入门
Go
语言入门
入门
一个简单的hello world
程序
1 | package main |
package
关键字用于声明这个文件源文件属于哪个包, 一个文件夹下面直接包含的文件只能归属一个包,同一个包的文件不能在多个文件夹下. 文件夹的名称可以与包名不同
import
关键字, 用法import [importname] "/path/to/package"
, 用于导入包, importname
可以省略, 省略后, 默认值为引入包的包名.
go语言依赖包查找机制
- 在
Go
支持Go Modules
之后,编译时编译器会从工作目录(当前所在目录)开始并逐级向上查找是否具有go.mod
文件。- 如果有,
go.mod
文件中声明的module
名称就视作go.mod
所在的路径,然后以指定的main
包为依赖入口,所有以go.mod
中声明的module
名称开头的导入路径都以go.mod
所在的路径为相对路径进行包的查找导入。所有需要导入的路径中如果在go.mod
中指定了版本,则从GOMODCACHE
下取得相应版本进行导入,如果没有被指定则从$GOPATH/src/
或$GOROOT/src/
中进行查找导入。GOMODCACHE
, 它默认为$GOPATH/pkg/mod
- 如果没有,所有依赖均从
$GOPATH/src/
或$GOROOT/src/
中进行查找导入。
- 如果有,
- 编译时,不在乎源文件在哪(只要指定入口,依赖路径便可依次拿到),而是在乎工作目录在哪(从工作目录开始逐级向上查找,是否可以找到
go.mod
)
参考链接: https://www.obooks.net/post-671.html
go环境变量
1 | 查看环境变量 |
Go
的命令
1 | 编译, 并运行, 运行完成后, 可执行文件会被删除 |
Go
语言的命令可能和网上的说明差异很大, 准确的描述, 只能看go help
命令行参数
os.Args
是一个slice
, 其中os.Args[0]
是go
程序本身的名字
1 | package main |
go
语言中变量的声明有4种方式
1 | 由于string可以省略, 很少使用这种方式 |
go
语言中++
和--
是语句, 不是表达式, 不支持j = i++
, 但在java
中, ++
和--
是语句, 支持j = i++;
go语言中循环
1 | while循环 |
go
中数组时支持打印的, java
中数组如果不使用工具类, 打印的是hash
值
查找重复的行
go
中map
的键不能为不能比较的类型, 例如slice
, 值可以是任何类型. java
中键值只要是引用类型就行
go
中打印结构体的办法
使用fmt.Printf
- %v占位符是不会打印结构体字段名称的,字段之间以空格隔开;
- %+v占位符会打印字段名称,字段之间也是以空格隔开;
- %#v占位符则会打印结构体类型和字段名称,字段之间以逗号分隔
指针类型, 只会打印地址, 如果需要打印值, 就需要实现String
方法, 跟java
一样, 但这个方法只对%v
和%+v
有效, 如果想%#v
打印值, 需要实现GoString
使用json
1 | package main |
go从标准输入和文件读入
1 | // 从标准流读入 |
函数和包级别的变量可以任意顺序声明, 并不影响其调用. 这个和java
一样
GIF
动画
当我们import
了一个包路径包含了多个单词的package
时, 比如image/color
, 通常我们只需要用最后那个单词表示这个包就可以.
常量是指在程序编译后运行时始终不会发生变化的值. 目前常量声明的值必须是一个数字值, 字符串或者一个固定的boolean
值.
[]color.Color{...}
和gif.GIF{...}
是复合声明的一种写法. 这里前者生成的是一个slice
切片, 后者生成的是一个struct
结构体.
struct
是一组值或者叫字段的集合, 不同的类型集合在一个struct
可以让我们以一个统一的单元进行处理, struct
内部的变量可以以一个点.
来进行访问.
并发获得多个URL
goroutine
是一种函数的并发执行方式, 而channel
是用来在goroutine
之前进行参数传递.
main
函数本身也运行在一个goroutine
中, 而go function
则表示创建一个新的goroutine
, 并在新的goroutine
中执行这个函数.
当一个goroutine
尝试在一个channel
上做send
或者receive
操作时, 这个goroutine
会堵塞在调用处, 直到另一个goroutine
往这个channel
里写入, 或者接收值, 这样两个goroutine
才会继续执行channel
操作之后的逻辑.
Web
服务
Go
允许一个简单的语句(如一个局部变量声明)写在if
条件的前面, 这在错误处理的时候特别有用, 这样可以缩小err
变量的作用域.
本章要点
switch
语句: Go
语言并不需要显式地在每一个case
后写break
, 语言默认执行完case
后的逻辑语句会自动退出. 如果需要贯穿, 需要自己显式地写上一个fallthrough
语句来覆盖默认行为.
label
语句: 允许break
和continue
跳过任意层的循环, 调到label
处.
命名类型: 类型声明使得我们可以很方便地给一个特殊类型一个名字
1 | type Point struct { |
指针: Go
语言提供了指针. 指针是一种直接存储了变量内存地址的数据类型. 指针是可见的内存地址, &
操作符可以返回一个变量的内存地址, 并且*
操作符可以获取指针指向的变量内容, 但是在Go
语言中没有指针运算, 也就是不能像c
语言里可以对指针进行加或减操作.
方法和接口: 方法是和命名类型关联的一类函数. 接口是一种抽象类型, 这种类型可以让我们以同样的方式来处理不同的固有类型, 不用关心它们的具体实现.
多行注释: /* ... */
.
常用的函数
1 | // 可以打印以空格间隔的一个或多个值, 并在最后添加一个换行符, 从而输出一整行 |
Panic异常
1 | // 手动抛出异常 |
Recover捕获异常
1 | // go中捕获异常异常只能在defer语句中使用 |
方法
方法声明
1 | // go语言中方法和结构体绑定的方式是在方法名称声明前写明类型, 在java中类有作用域, 在类的括号中, 就是类的方法 |
基于指针对象的方法
1 | // go语言中如果方法想改变结构体的值, 声明的接收器就必须是结构体的指针 |
Nil
也是一个合法的接收器类型
1 | // go语言中nil可以用于调用方法, 返回什么, 由方法决定, 在java和很多其他语言中, 都会报空指针异常. go中这样弄, 抛空指针的panic就由方法实现者来实现了 |
通过嵌入结构体来扩展类型
1 | // go语言中内嵌结构的方法, 是通过委托的方式, 让外部结构体也有这些方法的 |
方法值和表达式
1 | // go语言中new函数的用法, 返回对应类型的指针 |
Bit数组
1 | // go语言中对于字符串拼接, 也有高效的类, bytes.Buffer, 相当于java中StringBuilder |
类型开关
在go语言中, 接口有两种使用方式
- 像
java
中的接口那样, 用于隐藏属性, 重点在于方法, 表示行为 - 像
java
中的Object
那样, 不是为了隐藏属性, 而是为了暴露, 这种方式接口几乎没有任何方法, 操作它们的函函数通常会使用类型开关的, 这种行为可以被称为可辨识联合
1 | // go中的类型开关, go中swith好像与顺序有关 |
Channels
go语言中, make可以用于创建slice
, map
, channel
1 | ch := make(chan int) |
channel
是引用类型, 两个相同类型的channel
可以用==
比较. 如果两个channel
引用的是相通的对象, 那么比较的结果为真
不带缓存的Channels
不带缓存的Channel对导致堵塞发送者和接收者goroutine
go语言中没有volatile
, 但可以用channel
的收发来保证执行顺序和可见性
基于无缓存Channels
的发送和接收操作将导致两个goroutine
做一次同步操作. 当通过一个无缓存Channels
发送数据时, 接收者受到数据发生在唤醒发送者goroutine
之前
串联的Channels(Pipeline)
go语言中channel
不是资源, 不是一定要像文件那样, 必须关闭的. go会自动回收不用的channel
, 其实只有当需要告诉接收者发送完毕了, 才需要关闭channel
.
同时重复关闭channel
会报panic
, 关闭一个nil
值的channel
也会导致panic
异常
1 | // Squarer |
反射
为什么需要反射
注意事项
- 空silce和值为nil的slice是等效的, 但map不是这样