Go基础篇

黄靠谱 · · 630 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

判断和流程控制

https://www.jianshu.com/p/77be2afa225d

  1. if的使用,布尔判断条件不加括号;而且初始化的时候可以添加一个申明的初始值
 if num == 3 {
            fmt.Println("index 3 :", i)
 }else{
     fmt.Println(num)
 }

  1. 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("等于零")
    }
  1. 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的函数特征

  1. Go的函数支持多返回值,这方便编程,java实现多返回值只能封装在实体对象或者Hashmap中,典型的使用场景就是分页查询,既需要知道count,也需要当页的数据
func main(){
 result1,result2 := swap("hello", "kitty")
}

func swap(x, y string) (string, string) {
   return y, x
}
  1. Go的函数写法和其它语言不一样,先申明变量的名字,再申明变量的类型。设计者觉得这样才符合正常人类的思维方式

  2. Go函数和通过方法名的大小写来实现对外权限的控制

  3. Go函数不支持重载

  4. 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对象的申明

  1. 申明一个对象,不需要再写冗余的get、set方法、机械式的构造函数、toString方法了,自带灵活的构造函数
  2. 支持三种类型的构造函数
  3. 另外通过(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对象的继承和多态

  1. 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
}
  1. 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注册的函数。通常用来进行资源释放,错误的处理,清理数据等。

  1. 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)
}
  1. defer虽然是在当前方法执行后执行,但是defer的参数就会被实时解析。下面打印 1 0
func main() {
    i := 0
    defer fmt.Println(i) //此时是0
    i++
    defer fmt.Println(i) //此时是1
}
  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)
}
  1. defer是Go语言中 异常处理的一部分,和panic()、recover()一起使用

错误和异常处理机制

https://www.jianshu.com/p/f30da01eea97

  • Golang中引入error接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含error,可逐层返回,直到被处理。
  • Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。

异常和错误的正确使用方式:

  1. 当错误的类型只有1种的时候,直接用bool,而不是用error的实现类,因为bool更简洁
//当hostType为 virtual_machine 或者bare_metal时校验通过,否则校验失败
func (self *AgentContext) IsValidHostType(hostType string) bool {
    return hostType == "virtual_machine" || hostType == "bare_metal"
}
  1. 一般error的返回值放在返回值的最后一个

  2. 规范使用errors.New的使用,放在常量文件里面,标准化输出,而不是每次用的时候,自己定义输出内容

util.go 里面定义这样的常量
var ERR_NOT_EXIST = errors.New("file is not exist")

其它地方直接使用
    err := createResource1()
    if err != nil {
        return ERR_CREATE_RESOURCE1_FAILED
    }

经典的异常和错误的使用方式

  1. 手动抛出异常:
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
  1. 手动实现一个特定类型的异常类(类似于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)
    }

}
  1. 通过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的数据类型和转换

  1. int 默认是和CPU的位数一样的,在64位的操作系统上,默认int就是64位,相当于java的long型数据
    当然 int支持: int8 、int16、 int32、int64
  2. uint:无符号位的整形,就是没有符号位了,比如 int8 的范围是-127 到128,但是 uint8的范围是 0- 255
  3. 类型转换
    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

  1. 按照import的先后顺序挨个引入依赖类,并且先执行依赖类的init方法,再执行自己的init方法,最后执行main方法
  2. 现在的1.11的版本,import还是加 相对路径吧;或者把工程路径放在 GOPATH/src下面。
  3. 路径前面加 . 的话,则可用直接引用,省略引用包名前缀
  4. import的话,还支持别名操作,和 _ 初始化假引用
  5. package下小写开头的方法对外不可见,类似于java中的private
import (
    . "fmt"
    "./learn1"
    )

 import( f “fmt” )

Server版helloWord

设置访问端口为 8888

  1. 设置不接受参数的网络请求
func main() {
    http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
        fmt.Fprintln(writer,"<h1>hello kitty </h1>")
    })
    http.ListenAndServe(":8888",nil)
}
  1. 接收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"))
    })
  1. 并发型输出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)
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:黄靠谱

查看原文:Go基础篇

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

630 次点击  
加入收藏 微博
上一篇:ubuntu安装go
下一篇:2018-11-15
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传