Go学习笔记(一)

  • 是一门编译型语言,运行效率高,开发高效,部署简单;语言层面支持并发,易于利用多核实现并发;内置runtime(作用:性能监控,GC等);简单易学,丰富的标准库,强大的网络库;内置强大的工具(gofmt),跨平台编译,内嵌C支持。
  • 应用:服务器编程,分布式系统 ,网络编程,云平台(docker)
  • 命令行工具:
    1、go build:用于编译源码文件、代码包、依赖包;
    2、go run:可以编译并运行Go源码文件;
    3、go get:命令主要用动态获取远程代码包。
Go保留的关键字只有25个
Go中36个预定标识符(包括基本数据类型和系统内嵌函数)
// 单行注释
/* 多行注释 */
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    "fmt"
    // "time"  // 导入的包要使用,否则会产生编译错误
)

// 定义全局常量
const NAME string = "global const var"

// 定义全局变量
var tmp string = "global var"

// 一般类型声明
type countInt int

// 结构体的声明
type Learn struct {

}

// 声明接口
type Ilearn interface {

}

// 函数定义
func learnGo() {
    fmt.Println("learn go!")
}

// 主函数
func main() {
    fmt.Println("Hello World!!!")
    fmt.Println(NAME)
    fmt.Println(tmp)
    learnGo()
}
  • package是基本的分发单位和工程管理中依赖关系的体现。
  • 每个Go语言源代码文件的开头都拥有一个package声明,表示源码文件所属代码包;要生成Go语言可执行程序,必须要有main的package包,且必须在该包下有main()函数
  • 同一个路径下只能存在一个package,一个package可以拆分成多个源文件组成。
  • import语句可以导入源代码文件所依赖的package包
  • 导入的包要使用,否则会产生编译错误!
  • 若导入的包中依赖其他包(包B),会首先导入包B,然后初始化包B中的常量和变量,最后若包B中有init()函数,会自动执行init()函数,所有包导入完成后才会对main中的常量和变量进行初始化,然后执行main中的init()函数(如果存在),最后才执行main函数。
  • 若有一个包被导入多次,则该包只会被导入一次。
  • 函数名首字母要大写
  • 转义字符:\r表示回车,从当前行的最前面开始输出,覆盖掉旧的内容
  • 格式化go源代码文件:执行命令:gofmt -w 文件名.go;或者先按组合键shift+tab将代码整体移动到最左边,再选择某些代码块按tab键适当缩进即可。
  • 行长约定:一行最长不超过80个字符,超过则用逗号隔开并换行展示!

错误记录

  • 作为一个go的初学者,首先必须了解一个go项目的目录结构,例如我的一个测试目录如下:
workspace 
         |--bin

         |--pkg

         |--src
               |---learn
                        |----learn.go
               |---show
                        |----show.go
         |--main.go
  • bin目录存放编译后的可执行文件pkg目录存放编译后的包文件(名称以.a为后缀的文件)src目录存放项目源文件
  • 一般地,bin和pkg目录可以不创建,go命令会自动创建(如 go install),只需要创建src目录即可。
  • 注意:某个.go文件import导入包的过程是先去$GOROOT下的src目录找对应的包,找到则导入;若找不到,则去存放Go的项目路径$GOPATH下的src目录找对应的包,找到则导入;若还找不到,则报错!
  • 刚开始还不知道go项目的目录结构时,我在main.go文件中导入learn和show包是这样的:
import (
    "fmt"
    "src/learn"
    "src/show"
)

结果报如下错误,细细观察,通过查找和对比,可以发现上面讲的原理;

import包名的查找顺序

于是,把main.go文件中import包的代码改一下(去掉"src"):

import (
    "fmt"
    "learn"
    "show"
)

再次键入执行命令:go run main.go,结果就成功执行了!

执行结果

learn.go的代码如下:

package learn

import (
    "fmt"
    "show"
)

func init() {
    show.Show()
    fmt.Println("learn init...")
}

func Learn() {
    fmt.Println("learn 学习...")
}

show.go的代码如下:

package show

import (
    "fmt"
)

func init() {   
    fmt.Println("show init...")
}

func Show() {
    fmt.Println("show 展示...")
}

main.go的代码如下:

// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    "fmt"
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)
// 函数定义
func learnGo() {
    fmt.Println("learn go!")
}
// init函数
func init() {
    fmt.Println("main init...")
}
// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}
  • 键入go env可以查看go的配置:例如我的环境配置如下:
go的环境配置
  • import别名、._
    1、import别名:
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    zzz "fmt"  // 包"fmt"的别名定义为zzz
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)

// 函数定义
func learnGo() {
    zzz.Println("learn go!")
}

// init函数
func init() {
    zzz.Println("main init...")
}

// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}

2、点(.)操作的含义是:点(.)表示的包导入后,调用该包中函数时可以省略前缀包名;

// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"  // 使用 . 操作,调用包"fmt"中的函数时,省略包名.
    // "time"  // 导入的包要使用,否则会产生编译错误
    "learn"
    "show"
)

// 函数定义
func learnGo() {
    Println("learn go!")  // 省略包名fmt.
}

// init函数
func init() {
    Println("main init...")
}

// 主函数
func main() {
    show.Show()
    learn.Learn()
    learnGo()
}

3、下划线(_)操作的含义是:导入该包,但不导入整个包,而是执行该包的init函数,因此无法通过包名来调用包中的其他函数。使用下划线(_)操作往往是为了注册包里的引擎,让外部可以方便地使用。

  • 代码包初始化函数即无参数声明和结果声明的init函数。
  • init函数可以被声明在任何文件中,且可以有多个。
(_)测试1
执行产生错误

以上报错说明无法调用learn.go文件中的Learn()函数,我们将main()函数中的代码learn.Learn()注释掉,再用命令行执行main.go可以发现执行了learn.go中的init()函数,说明_操作只调用init()函数,当然在执行init()函数之前,会先初始化learn.go文件中的常量和变量。

  • init函数的执行时机:单一代码包内,对所有全局变量进行求值,然后执行所有init函数,同一个代码包中多个init函数的执行顺序是不确定。

Go语言的基本数据类型

1、布尔型:只可以是常量 true 或者 false,不能是其他类型。一个简单的例子:var b bool = true。
2、整数类型:整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
1.uint8:无符号 8 位整型 (0 到 255)
2.uint16:无符号 16 位整型 (0 到 65535)
3.uint32:无符号 32 位整型 (0 到 4294967295)
4.uint64:无符号 64 位整型 (0 到 18446744073709551615)
5.int8:有符号 8 位整型 (-128 到 127)
6.int16:有符号 16 位整型 (-32768 到 32767)
7.int32:有符号 32 位整型 (-2147483648 到 2147483647)
8.int64:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
3、浮点数类型
1.float32:IEEE-754 32位浮点型数
2.float64:IEEE-754 64位浮点型数
3.complex64:32 位实数和虚数
4.complex128:64 位实数和虚数
4、字符串类型string,编码统一为"UTF-8"。
5、其他数字类型
1.byte:类似 uint8,无符号类型。
2.rune:类似 int32,表示一个Unicode码。
3.uint:32 或 64 位,其取决于操作系统的位数
4.int:与 uint 一样大小,其取决于操作系统的位数
5.uintptr:无符号整型,用于存放一个指针

  • 查看某个变量所占的字节数:unsafe.Sizeof(变量),导入import "unsafe"包!

  • 派生类型
    1、指针类型(Pointer)
    2、数组类型
    3、结构化类型(struct)
    4、Channel类型(chan)
    5、函数类型(func)
    6、切片类型(slice)
    7、接口类型(interface)
    8、Map类型(map)

  • 类型零值和类型别名
    1、类型零值不是空值,而是某个变量被声明后的默认值,一般情况下,值类型的默认值为0,布尔类型默认值为false,string默认值为空字符串。
    2、类型别名:type 别名 = 数据类型,例如:type D = int
    3、类型声明:type 别名 数据类型,例如:type d int
    4、区别:类型声明将d定义为一个新的数据类型,该类型拥有和int一样的特性,但是两者是不同类型不能进行算术等运算!而类型别名则将D定义为int整型的一个别名,使用D和int相同。二者可以当作同一种运算。别名只在源码中存在,编译完成后,不会有别名类型
  • 查看某个变量的类型:reflect.TypeOf(变量名),导入import "reflect"包。

变量与常量

  • 单个变量的声明和赋值
    1、变量的声明格式:var <变量名称> [变量类型]
    2、变量的赋值格式:<变量名称> = <值,表达式,函数等>
    3、声明和赋值同时进行:var <变量名称> [变量类型] = <值,表达式,函数等>
    4、分组声明格式:
var(
  a int
  b float32
  name string
)

5、同一行声明多个变量和赋值:var a, b, c int = 1, 2, 3 或者a, b := 1, 2。注意::=是声明并赋值;系统会自动推断类型;不需要var关键字;只能使用在局部变量。
6、全局变量必须使用var关键字,局部变量则可以省略。
7、特殊变量下划线_:相当于一个垃圾桶,把值赋值给下划线
,相当于把值丢进一个垃圾桶,以后都不再使用。
8、Go语言中不存在隐式转换,类型转换必须是显式转换。类型转换只能发生在两种兼容类型之间;类型转换格式:<变量名称> [:]= <目标类型>(<需要转换的变量>)

  • 变量的可见性规则
    1、大写字母开头的变量可导出的,也就是其他包可以读取的,是公共变量
    2、小写字母开头的变量就是不可导出的,是私有变量。即导入的某个包,只能使用该包中的公有变量,不能使用私有变量。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
    "reflect"
)

var(
    a uint
    b float32
    c bool
    d string = "你好"
)

func main() {
    var v1, v2, v3 = 4, 5, 1.222  // 可以声明每个变量的类型,省略的话--->系统自动推断类型
    Println(reflect.TypeOf(v3))  // float64
    Println(v1, v2, v3)  // 4 5 1.222
    Println(a, b, c, d) // 0 0 false 你好

    c1, c2, c3 := 33, "string", 1.222 // 简写方式,系统自动推断类型,只能用在函数体内
    Println(reflect.TypeOf(c2)) // string
    Println(c1, c2, c3) // 33 string 1.222

    // 下划线的使用
    // var a1, _, a2 = 9, 8, 7
    // Println(a1, _, a2) // 报错:
    // # command-line-arguments
    // .\main.go:31:9: cannot use _ as value

    // 类型转换测试: 只能用在相同类型之间进行转换
    var h float64 = 2.6555555
    k := int64(h)  // 括号不能省
    Println(k) // 2,直接向下取整
    Println(reflect.TypeOf(k))  // int64
}
  • 常量定义从形式上分为显式和隐式:
    1、显式:const identifier [type] = value
    2、隐式:const identifier = value (通常叫无类型常量)
  • 常量可以使用内置表达式定义:例如:len()unsafe.Sizeof()
    3、常量范围目前只支持布尔型,数字型(整数型、浮点型和复数)和字符串型。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
    "reflect"
)

const(
    name string = "cat" // 显式定义
    age = 12 // 隐式定义
)

const a, b, c = 1, 2, "我是中文"

const lenc = len(c)  // 一个中文占三个字节,长度为12

func main() {
    Println(name, age)  // cat 12
    Println(a, b, c) // 1 2 我是中文
    Println(reflect.TypeOf(c)) // string
    Println(lenc) // 12
}
  • 特殊常量iota的使用
    1、iota在const关键字出现时将被重置为0;
    2、const中每新增一行常量声明将使iota计数一次。
    3、iota常见用法:
    1.跳值使用法;
    2.插队使用法;
    3.表达式隐式使用法;
    4.单行使用法。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样

// 导入依赖包
import (
    . "fmt"
)

// iota在关键字出现时被重置为0
const a = iota // 0
const b = iota // 0

const(
    c = iota
    m = 2014 // 插入使用法,iota依然计数
    d = iota
    _  // 跳值使用法,下划线单独占用一行,iota依然计数
    e = iota
)

const(
    b1 = iota * 3
    b2 // 自动使用向上第一个非空表达式,此时每增加一行常量const,iota计数加1,即b2 = 1 * 2 
    b3 // b3 = 2 * 3 = 6
)

const(
    c1, c2 = iota + 1, iota * 3
    c3, c4  // 沿用上面第一个非空的表达式,注意格式要一样,且const新增一行,iota计数器才会加1
    c5 = iota // 否则就得赋予值

)

func main() {
    Println(a, b) // 0 0
    Println(c, d, e) // 0 2 4
    Println(b1, b2, b3) // 0 3 6
    Println(c1, c2, c3, c4, c5) // 1 0 2 3 2
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容