判断和流程控制
https://www.jianshu.com/p/77be2afa225d
- if的使用,布尔判断条件不加括号;而且初始化的时候可以添加一个申明的初始值
if num == 3 {
fmt.Println("index 3 :", i)
}else{
fmt.Println(num)
}
- switch 的使用:默认每个case自带break,如果想继续判断需要手动添加 fallthrough;有2种写法(直接switch 或者 switch i),适用不同的业务场景;case的值可以是整数,字符串等类型
//这种场景适用 条件值是固定的业务场景
i := 5
switch i{
case 1:
fmt.Println("i is equal to 1")
case 2:
fmt.Println("i is equal to 2")
case 3,4,5,6:
fmt.Println("i is equal to 3,4,5 or 6")
fallthrough
default:
fmt.Println("others")
}
//这种场景适用 条件值不固定,为某一区间的业务场景
switch {
case i < 0:
fmt.Println("小于零")
case i > 0:
fmt.Println("大于零")
default:
fmt.Println("等于零")
}
- for循环:go的for循环有如下三种形式。条件不需要括号。for循环 数组和map的时候可以结合range一起用
for init; condition; post{} //和C的for一样
for condition{} //和while一样
for{} //结合break,continue
nums := []int{2, 3, 4}
for i, num := range nums {
if num == 3 {
fmt.Println("index 3 :", i)
}else{
fmt.Println(num)
}
}
Golang的函数特征
- Go的函数支持多返回值,这方便编程,java实现多返回值只能封装在实体对象或者Hashmap中,典型的使用场景就是分页查询,既需要知道count,也需要当页的数据
func main(){
result1,result2 := swap("hello", "kitty")
}
func swap(x, y string) (string, string) {
return y, x
}
Go的函数写法和其它语言不一样,先申明变量的名字,再申明变量的类型。设计者觉得这样才符合正常人类的思维方式
Go函数和通过方法名的大小写来实现对外权限的控制
Go函数不支持重载
Go语言的函数的参数支持不指定数量和类型
func main(){
result := sum(3,5,7,9)
fmt.Println("结果为:", result)
}
func sum(aregs ...int) int {
s := 0
for _, number := range aregs{
s += number
}
return s
}
Go对象的申明
- 申明一个对象,不需要再写冗余的get、set方法、机械式的构造函数、toString方法了,自带灵活的构造函数
- 支持三种类型的构造函数
- 另外通过(r *rect)来添加类的方法
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books
Book1.title = "Go 语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
fmt.Printf( "Book title : %s\n", book1.title);
var Book2 Books=Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
fmt.Println(Book2)
fmt.Println(Book2.title)
Book4 :=Books{
title:"testJson",
subject:"testSubject",
}
}
type rect struct {
width int
height int
}
func (r *rect) area() int {
return r.width * r.height
}
func main() {
r := rect{width: 10, height: 5}
fmt.Println("area: ", r.area())
}
Go对象的继承和多态
- Go语言通过把目标对象当作自身成员变量的方式,间接的实现继承
func main(){
stu := Student{"chain"}
fmt.Println(stu)
aStu := AStudent{Student{"chain"}, 23}
fmt.Println(aStu)
}
type Student struct{
name string
}
type AStudent struct{
Student
Age int
}
- Go语言通过实现接口的方式,来实现多态,需要用到指针 &,不能指定为非 Phone接口的实现类,否则会报错
Go语言简洁归简洁,有时候过于简洁并不方便,比如不能知道这个类的接口是哪个接口
定义一个Phone的接口,它的实现类必须实现call()方法
type Phone interface {
call()
}
定义一个普通类NokiaPhone
type NokiaPhone struct {
}
实现call()的方法
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("I am IPhone, I can call you!")
}
func main() {
var phone Phone
phone = &NokiaPhone{}
phone.call()
phone= &IPhone{}
phone.call()
}
Go中指针的使用 & 、 *
https://www.cnblogs.com/grimm/p/5623860.html
&符号的意思是对变量取地址,如:变量a的地址是&a
符号的意思是对指针取值,如:&a,就是a变量所在地址的值,当然也就是a的值了
和 & 可以互相抵消,同时注意,&可以抵消掉,但&是不可以抵消的
a和&a是一样的,都是a的值,值为1 (因为*&互相抵消掉了)
var a int = 1
var b *int = &a
fmt.Println("a = ",a) //1
fmt.Println("&a = ",&a)//0xc00000a098 a的内存地址
fmt.Println("*&a = ",*&a)//1 *和&抵消掉了,还是1
fmt.Println("b = ",b) //0xc00000a098 b是a的指针,和 &a的值一样
fmt.Println("&b = ",&b) //0xc000006028 指针的指针
fmt.Println("*b = ",*b) //1 b指针指向的值
defer的用法
http://developer.51cto.com/art/201306/400489.htm
defer语句延迟执行一个函数,该函数被推迟到当包含它的程序返回时(包含它的函数 执行了return语句/运行到函数结尾自动返回/对应的goroutine panic/当前线程 runtime.Goexit())执行。
按照栈的形式后进先出的原则依次执行每个defer注册的函数。通常用来进行资源释放,错误的处理,清理数据等。
- defer是后进先出
//打印的顺序 main method 、method2、 method1,因为类似于栈模型,后进先出
func main() {
defer doSomething("method 1")
defer doSomething("method 2")
fmt.Println("i am main method")
}
func doSomething(things string){
fmt.Println(" ia am doing somthing",things)
}
- defer虽然是在当前方法执行后执行,但是defer的参数就会被实时解析。下面打印 1 0
func main() {
i := 0
defer fmt.Println(i) //此时是0
i++
defer fmt.Println(i) //此时是1
}
- 经典的使用场景:简化资源的释放,用起来有类似finally的效果,特别适合多分支返回语句的情况,可以明显简化代码
func main() {
fmt.Println(judgeNumber(-1))
fmt.Println(judgeNumber(0))
fmt.Println(judgeNumber(1))
}
func judgeNumber(num int) string{
var lockName ="numlock"
getLock(lockName)
defer unlock(lockName)
switch {
case num<0 :
return "nagetive"
case num==0 :
return "Zero"
default:
return "good number"
}
}
func getLock(lockname string){
fmt.Println("i got the lock of ",lockname)
}
func unlock(lockname string){
fmt.Println("i release the lock of ",lockname)
}
- defer是Go语言中 异常处理的一部分,和panic()、recover()一起使用
错误和异常处理机制
https://www.jianshu.com/p/f30da01eea97
- Golang中引入error接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含error,可逐层返回,直到被处理。
- Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。
异常和错误的正确使用方式:
- 当错误的类型只有1种的时候,直接用bool,而不是用error的实现类,因为bool更简洁
//当hostType为 virtual_machine 或者bare_metal时校验通过,否则校验失败
func (self *AgentContext) IsValidHostType(hostType string) bool {
return hostType == "virtual_machine" || hostType == "bare_metal"
}
一般error的返回值放在返回值的最后一个
规范使用errors.New的使用,放在常量文件里面,标准化输出,而不是每次用的时候,自己定义输出内容
util.go 里面定义这样的常量
var ERR_NOT_EXIST = errors.New("file is not exist")
其它地方直接使用
err := createResource1()
if err != nil {
return ERR_CREATE_RESOURCE1_FAILED
}
经典的异常和错误的使用方式
- 手动抛出异常:
if f < 0 {
return 0, errors.New("math: square root of negative number")
}
- 手动实现一个特定类型的异常类(类似于java的自定义Exception),需要实现Error接口的Error()方法
// 定义一个 DivideZeroError 结构,分母为0的专门的异常处理类
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当被除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}
- 通过defer实现一个未知可能类型的异常,类似于java的 try catch finally的用法,联合 panic() recover()一起使用
func main(){
defer func(){
if err := recover(); err != nil{ //捕捉异常并处理
fmt.Println("err: ", err)
}}()
if num, err := delive(20, -5); err == nil{
fmt.Printf("%f / %f = %f\n", 20.0, -5.0, num)
}else{
fmt.Println(err)
}
if num, err := delive(20, 0); err == nil{
fmt.Printf("%f / %f = %f\n", 20.0, 0.0, num)
}else{
fmt.Println(err)
}
fmt.Println("Test")
}
func delive(numA, numB float32) (float32, error){
if numB < 0{
return 0, errors.New("被除数不能为负数")
}else if numB == 0{
panic("被除数不能为0") //抛出异常
}else{
return numA/numB, nil
}
}
Go的数据类型和转换
- int 默认是和CPU的位数一样的,在64位的操作系统上,默认int就是64位,相当于java的long型数据
当然 int支持: int8 、int16、 int32、int64 - uint:无符号位的整形,就是没有符号位了,比如 int8 的范围是-127 到128,但是 uint8的范围是 0- 255
- 类型转换
var test string="120"
//把test字符串转换为10进制的32位结果的数字
result,err:=strconv.ParseInt(test,10,32)
if err!=nil{
fmt.Println(result)
}
count :=2000
resultResult:= strconv.Itoa(count)
import原理
https://blog.csdn.net/zhangzhebjut/article/details/25564457
- 按照import的先后顺序挨个引入依赖类,并且先执行依赖类的init方法,再执行自己的init方法,最后执行main方法
- 现在的1.11的版本,import还是加 相对路径吧;或者把工程路径放在 GOPATH/src下面。
- 路径前面加 . 的话,则可用直接引用,省略引用包名前缀
- import的话,还支持别名操作,和 _ 初始化假引用
- package下小写开头的方法对外不可见,类似于java中的private
import (
. "fmt"
"./learn1"
)
import( f “fmt” )
Server版helloWord
设置访问端口为 8888
- 设置不接受参数的网络请求
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintln(writer,"<h1>hello kitty </h1>")
})
http.ListenAndServe(":8888",nil)
}
- 接收URL参数, 利用Fprintf使用格式化输出, %s为占位符,取的是 request中 参数name的值
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer,"<h1>hello kitty %s</h1>",request.FormValue("name"))
})
- 并发型输出HelloWorld,1秒内开5000个微线程输出
func main() {
for i :=0;i<5000;i++{
go printHelloWorld(i)
}
time.Sleep(time.Second)
}
func printHelloWorld(i int){
fmt.Printf("hello kitty ,%d \n",i)
}