美文网首页
go学习笔记(一)

go学习笔记(一)

作者: 明明就_c565 | 来源:发表于2018-11-27 11:48 被阅读0次

    变量

    var num int;var num int = 1,var a,b int;var a,b int = 1,2;var a = 1;_,b=1,2;

    1,:=这个符号直接取代了var和type,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用var方式来定义全局变量。

    2,_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。

    3,Go对于已声明但未使用的变量会在编译阶段报错

    常量

    const Pi=3.1415926;const i =10000;const MaxThread=10;const prefix ="astaxie_"

    所谓常量,也就是在程序编译阶段就确定下来的值,而程序在运行时无法改变该值。在Go程序中,常量可定义为数值、布尔值或字符串等类型

    数值

    整数类型有无符号和带符号两种。Go同时支持int和uint,这两种类型的长度相同,但具体长度取决于不同编译器的实现。Go里面也有直接定义好位数的类型:rune, int8, int16, int32, int64和byte, uint8, uint16, uint32, uint64。其中rune是int32的别称,byte是uint8的别称。

    注:这些类型的变量之间不允许互相赋值或操作,不然会在编译时引起编译器报错。另外,尽管int的长度是32 bit, 但int 与 int32并不可以互用。浮点数的类型有float32和float64两种(没有float类型),默认是float64。

    字符串

    Go中的字符串都是采用UTF-8字符集编码。字符串是用一对双引号("")或反引号(` `)括起来定义,它的类型是string。

    注:在Go中字符串是不可变的,若一定要修改,可以将字符串 s 转换为 []byte 类型,再转换回 string 类型,Go中可以使用+操作符来连接两个字符串,字符串虽不能更改,但可进行切片操作,如果要声明一个多行的字符串怎么办?可以通过`来声明。

    array、slice、map

    array就是数组,var arr [n]type,长度也是数组类型的一部分,因此[3]int与[4]int是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的slice类型了。

    “动态数组”,在Go里面这种数据结构叫slice,slice并不是真正意义上的动态数组,而是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。slice可以从一个数组或一个已经存在的slice中再次声明。slice通过array[i:j]来获取,其中i是数组的开始位置,j是结束位置,但不包含array[j],它的长度是j-i,类似于python的切片或者c++的vector。

    从概念上面来说slice像一个结构体,这个结构体包含了三个元素:

    一个指针,指向数组中slice指定的开始位置。

    长度,即slice的长度。

    最大长度,也就是slice开始位置到数组的最后位置的长度。

    对于slice有几个有用的内置函数:

    len 获取slice的长度

    cap 获取slice的最大容量

    append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice

    copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数

    注:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap-len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。

    注意:slice和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。slice是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值。

    map类似python中的字典。声明举例:var numbersmap [string]int,numbers =make(map[string]int)。

    使用map过程中需要注意的几点:

    map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取

    map的长度是不固定的,也就是和slice一样,也是一种引用类型

    内置的len函数同样适用于map,返回map拥有的key的数量

    map的值可以很方便的修改,通过numbers["one"]=11可以很容易的把key为one的字典值改为11

    map和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制

    map的初始化可以通过key:val的方式初始化值,同时map内置有判断是否存在key的方式

    通过delete删除map的元素:

    map也是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应的改变

    make、new操作

    make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配。

    注:new返回指针。make返回初始化后的(非零)值。make 只能为 slice、map或 channel 类型分配内存并初始化,同时返回一个有初始值的 slice、map 或 channel 类型引用,不是指针。内建函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的指针。

    流程控制

    Go里面if条件判断语句中不需要括号,Go的if还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,比如if x=1;x>0{执行体}。

    goto类似c语言中的goto,

    for,在循环里面有两个关键操作break和continue ,break操作是跳出当前循环,continue是跳过本次循环。当嵌套过深的时候,break可以配合标签使用,即跳转至标签所指定的位置,break和continue还可以跟着标号,用来跳到多重循环中的外层循环。

    switch,类似与c的switch,Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch,但是可以使用fallthrough强制执行后面的case代码。

    函数

    注:函数可以返回多个值。Go函数支持变参,如 func myfunc(arg...int) {}。

    传值,当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。

    传指针使得多个函数能操作同一个对象。传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。Go语言中channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)

    defer,Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。

    函数作为值、类型。在Go中函数也是一种变量,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型。函数当做值和类型在我们写一些通用接口的时候非常有用。

    Panic和Recover

    Panic是一个内建函数,可以中断原有的控制流程,进入一个panic状态中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数defer会正常执行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函数返回,此时程序退出。panic可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。

    Recover是一个内建的函数,可以让进入panic状态的goroutine恢复过来。recover仅在延迟函数defer中有效。在正常的执行过程中,调用recover会返回nil,并且没有其它任何效果。如果当前的goroutine陷入panic状态,调用recover可以捕获到panic的输入值,并且恢复正常的执行。

    main函数和init函数

    Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数。

    Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

    程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过程:

    import这个命令用来导入包文件,类似于include,

    import . "fmt" 点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名

    import f “fmt” 别名 fmt.Printf()->f.Printf()

    import _ “fmt” _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。

    struct类型

    和C语言类似

    定义:

    type person struct {

            name strin

            gage int

    }

    1.按照顺序提供初始化值

    P := person{"Tom", 25}

    2.通过field:value的方式初始化,这样可以任意顺序

    P := person{age:24, name:"Tom"}

    3.当然也可以通过new函数分配一个指针,此处P的类型为*person

    P := new(person)

    匿名字段:Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段。当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。通过匿名访问和修改字段相当的有用,但是不仅仅是struct字段哦,所有的内置类型(int string等)和自定义类型都是可以作为匿名字段的。

    注:结构中匿名字段和结构本身字段重名,Go里面很简单的解决了这个问题,最外层的优先访问

    面向对象

    通俗的说就是给struct绑定了函数,函数接收者可以时值copy,也可以是指针,

    method的语法如下:

    func (r ReceiverType) funcName(parameters) (results)

    在使用method的时候重要注意几点

    虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样

    method里面可以访问接收者的字段

    调用method通过.访问,就像struct里面访问字段一样

    method 重写,继承。

    举例

    package main

    import "fmt"

    type Human struct {

        name  string

        age  int

        phone string // Human类型拥有的字段

    }

    type Student struct {

        Human

        school string

        phone  string

    }

    type Employee struct {

        Human      // 匿名字段Human

        speciality string

        phone      string // 雇员的phone字段

    }

    func (h Human) say() {

        fmt.Println(h.name, h.age, h.phone)

    }+++

    func (h Employee) say() {

        fmt.Println(h.name, h.age, h.phone, h.speciality, h.Human.phone)

    }

    func main() {

        Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}

        Tom := Student{Human{"Tom", 18, "123-456-189"}, "yichuan", "666"}

        Tom.say()

        Bob.say()

        fmt.Println(Tom.phone, Tom.Human.phone)

    注:以上笔记基本来自

    https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/preface.md

    作为业余爱好仅仅记录了一些注意点

    相关文章

      网友评论

          本文标题:go学习笔记(一)

          本文链接:https://www.haomeiwen.com/subject/bbdlqqtx.html