下载地址:https://studygolang.com/dl (Go语言中文网)
安装好后,开启cmd,输入go version 回车,会显示go的版本信息
小demo一个,创建一个hello.go文件
package main
import “fmt”
func main() {
fmt.Printf(“hello,world!\n”);
}
运行go run hello.go
输出结果: hello,world!
Go 语言的基础组成有以下几个部分:
包声明
引入包
函数
变量
语句 & 表达式
注释
1、 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
2、 import “fmt” 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
3、func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)
4、 /…/ 是注释,在程序执行时将被忽略。
5、fmt.Println(…) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print(“hello, world\n”) 可以得到相同的结果。
6、当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )
注意
需要注意的是 { 不能单独放在一行
分隔符:在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。
如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。
标识符:一个标识符实际上就是一个或是多个字母(AZ和az)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
关键字:
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
空格
Go 语言中变量的声明必须使用空格隔开 var age int;
数据类型
1、布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true
2、数字类型整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码
3、字符串类型:字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本
4、派生类型:(a) 指针类型(Pointer)
(b) 数组类型
© 结构化类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface)
(h) Map 类型
uint8
无符号 8 位整型 (0 到 255)
uint16
无符号 16 位整型 (0 到 65535)
uint32
无符号 32 位整型 (0 到 4294967295)
uint64
无符号 64 位整型 (0 到 18446744073709551615)
int8
有符号 8 位整型 (-128 到 127)
int16
有符号 16 位整型 (-32768 到 32767)
int32
有符号 32 位整型 (-2147483648 到 2147483647)
int64
有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
float32
IEEE-754 32位浮点型数
float64
IEEE-754 64位浮点型数
complex64
32 位实数和虚数
complex128
64 位实数和虚数
byte
类似 uint8
rune
类似 int32
uint
32 或 64 位
int
与 uint 一样大小
uintptr
无符号整型,用于存放一个指针
对于数字类型,无需定义int及float32、float64,系统会自动识别。例如:
package main
import “fmt”
func main() {
var a = 1.5
var b = 3
fmt.Println(a,b)
}
输出 1.5 3
变量
声明变量的一般形式是使用 var 关键字:var identifier type
第一种,指定变量类型,声明后若不赋值,使用默认值
var v_name v_type
v_name = value
第二种,根据值自行判定变量类型。
var v_name = value
第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误
v_name := value
例子:
package main
import “fmt”
var a = “菜鸟教程”
var b string = “ruunable”
var c bool
func main() {
fmt.Println(a,b,c)
}
输出:菜鸟教程 ruunable false
值类型和引用类型:所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝
可以通过 &i 来获取变量 i 的内存地址
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置
注意事项:如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
例子:
package main
import “fmt”
func main() {
_,num,strs := numbers()
fmt.Println(num,strs)
}
func numbers() (int,int,string){
a,b,c := 1,2,“str”
return a,b,c
}
输出:2 str
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:const identifier [type] = value
例子:
package main
import “fmt”
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, “str”
area = LENGTH * WIDTH
fmt.Printf("面积为: %d", area)go
println()
println(a,b,c)
}
输出:面积为 : 50
1 false str
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过
package main
import “unsafe”
const (
a = “abc”
b = len(a)
c = unsafe.Sizeof(a)
)
func main() {
println(a,b,c)
}
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次
运算符
假定 A 值为 10,B 值为 20。
- 相加 A + B 输出结果 30
- 相减 A - B 输出结果 -10
- 相乘 A * B 输出结果 200
/ 相除 B / A 输出结果 2
% 求余 B % A 输出结果 0
++ 自增 A++ 输出结果 11
– 自减 A-- 输出结果 9
== 检查两个值是否相等,如果相等返回 True 否则返回 False。 (A == B) 为 False
!= 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 (A != B) 为 True
检查左边值是否大于右边值,如果是返回 True 否则返回 False。 (A > B) 为 False
< 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 (A < B) 为 True
= 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 (A >= B) 为 False
<= 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 (A <= B) 为 True
假定 A 值为 True,B 值为 False
&& 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 (A && B) 为 False
|| 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。(A || B) 为 True
! 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 !(A && B) 为 True
假定 A 为60,B 为13
& 按位与运算符"&“是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 (A & B) 结果为 12, 二进制为 0000 1100
| 按位或运算符”|“是双目运算符。 其功能是参与运算的两数各对应的二进位相或 (A | B) 结果为 61, 二进制为 0011 1101
^ 按位异或运算符”^“是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (A ^ B) 结果为 49, 二进制为 0011 0001
<< 左移运算符”<<“是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。 A << 2 结果为 240 ,二进制为 1111 0000
右移运算符">>“是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数。 A >> 2 结果为 15 ,二进制为 0000 1111
= 简单的赋值运算符,将一个表达式的值赋给一个左值 C = A + B 将 A + B 表达式结果赋值给 C
+= 相加后再赋值 C += A 等于 C = C + A
-= 相减后再赋值 C -= A 等于 C = C - A
*= 相乘后再赋值 C *= A 等于 C = C * A
/= 相除后再赋值 C /= A 等于 C = C / A
%= 求余后再赋值 C %= A 等于 C = C % A
<<= 左移后赋值 C <<= 2 等于 C = C << 2
= 右移后赋值 C >>= 2 等于 C = C >> 2
&= 按位与后赋值 C &= 2 等于 C = C & 2
^= 按位异或后赋值 C ^= 2 等于 C = C ^ 2
|= 按位或后赋值 C |= 2 等于 C = C | 2
列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低
7 ^ !
6 * / % << >> & &^
5 + - | ^
4 == != < <= >= >
3 <-
2 &&
1 ||
条件语句
if 语句 if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
if…else 语句 if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。
if 嵌套语句 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。
switch 语句 switch 语句用于基于不同条件执行不同动作。
select 语句 select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
循环语句
for 循环 重复执行语句块
循环嵌套 在 for 循环中嵌套一个或多个 for 循环
break 语句 经常用于中断当前 for 循环或跳出 switch 语句
continue 语句 跳过当前循环的剩余语句,然后继续进行下一轮循环。
goto 语句 将控制转移到被标记的语句
package main
import “fmt”
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <=i; j++ {
fmt.Printf("%d*%d=%d \t", i, j, i * j)
}
fmt.Println("")
}
}
语言函数
Go 语言最少有个 main() 函数。
函数声明告诉了编译器函数的名称,返回类型,和参数
语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数定义解析:
func:函数由 func 开始声明
function_name:函数名称,函数名和参数列表一起构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数体:函数定义的代码集合。
变量作用域
Go 语言中变量可以在三个地方声明:
函数内定义的变量称为局部变量
函数外定义的变量称为全局变量
函数定义中的变量称为形式参数
全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑
默认值为:
int 0
float32 0
pointer nil
数组
数组声明需要指定元素类型及元素个数,语法格式如下:
var variable_name [SIZE] variable_type
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
数组元素可以通过索引(位置)来读取
var salary float32 = balance[9]
指针
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址
package main
import “fmt”
func main() {
var a int = 10
fmt.Printf(“变量的地址: %x\n”, &a)
}
输出:c000012098
指针声明格式如下:var var_name var-type
var-type 为指针类型,var_name 为指针变量名, 号用于指定变量是作为一个指针。以下是有效的指针声明:
var ip int / 指向整型*/
var fp float32 / 指向浮点型 */
package main
import “fmt”
func main() {
var a int = 20
var ip *int
ip = &a
fmt.Printf(“a 变量的地址是:%x\n”, &a)
fmt.Printf(“ip 变量存储的指针地址: %x\n”, ip)
fmt.Printf("*p 变量的值:%d\n", *ip)
}
a 变量的地址是: c000012098
ip 变量储存的指针地址: c000012098
*ip 变量的值: 20
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针,一个指针变量通常缩写为 ptr
if(ptr != nil) /* ptr 不是空指针 /
if(ptr == nil) / ptr 是空指针 */
结构体
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:
Title :标题
Author : 作者
Subject:学科
ID:书籍ID
结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体有中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
type struct_variable_type struct {
member definition;
member definition;
…
member definition;
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
variable_name := structure_variable_type {value1, value2…valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2…, keyn: valuen}
例子:
package main
import “fmt”
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
fmt.Println(Books{“Go 语言”, “www.runoob.com”, “Go 语言教程”, 6495407})
fmt.Println(Books{title: “Go 语言”, author: “www.runoob.com”, subject: “Go 语言教程”, book_id: 6495407})
fmt.Println(Books{title: “Go 语言”, author: “www.runoob.com”})
}
输出:{Go 语言 www.runoob.com Go 语言教程 6495407}
{Go 语言 www.runoob.com Go 语言教程 6495407}
{Go 语言 www.runoob.com 0}
如果要访问结构体成员,需要使用点号 . 操作符,格式为:
结构体.成员名
例子:
package main
import “fmt”
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books
var Book2 Books
Book1.title = "Go语言"
Book1.author = "www.runoob.com"
Book1.subject = "Go 语言教程"
Book1.book_id = 6495407
Book2.title = "Python 教程"
Book2.author = "www.runoob.com"
Book2.subject = "Python 语言教程"
Book2.book_id = 6495700
printBook(Book1)
printBook(Book2)
}
func printBook(book Books) {
fmt.Printf( “Book title : %s\n”, book.title);
fmt.Printf( “Book author : %s\n”, book.author);
fmt.Printf( “Book subject : %s\n”, book.subject);
fmt.Printf( “Book book_id : %d\n”, book.book_id);
}
输出:Book title : Go 语言
Book author : www.runoob.com
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : www.runoob.com
Book subject : Python 语言教程
Book book_id : 6495700
结构体指针:var struct_pointer *Books
指针变量可以存储结构体变量的地址 struct_pointer = &Book1;
使用结构体指针访问结构体成员,使用 “.” 操作符:struct_pointer.title;
例子:
package main
import “fmt”
type Books struct {
title string
author string
subject string
book_id int
}
func main() {
var Book1 Books
var Book2 Books
Book1.title = “Go 语言”
Book1.author = “www.runoob.com”
Book1.subject = “Go 语言教程”
Book1.book_id = 6495407
Book2.title = “Python 教程”
Book2.author = “www.runoob.com”
Book2.subject = “Python 语言教程”
Book2.book_id = 6495700
printBook(&Book1)
printBook(&Book2)
}
func printBook( book *Books ) {
fmt.Printf( “Book title : %s\n”, book.title);
fmt.Printf( “Book author : %s\n”, book.author);
fmt.Printf( “Book subject : %s\n”, book.subject);
fmt.Printf( “Book book_id : %d\n”, book.book_id);
}
输出:
Book title : Go 语言
Book author : www.runoob.com
Book subject : Go 语言教程
Book book_id : 6495407
Book title : Python 教程
Book author : www.runoob.com
Book subject : Python 语言教程
Book book_id : 6495700
切片
切片不需要说明长度,或使用make()函数来创建切片:
var slice1 []type = make([]type, len)
len 是数组的长度并且也是切片的初始长度
切片初始化
s :=[] int {1,2,3 }
直接初始化切片,[]表示是切片类型,{1,2,3}初始化值依次是1,2,3.其cap=len=3
s := arr[startIndex:endIndex]
将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片
s := arr[startIndex:]
缺省endIndex时将表示一直到arr的最后一个元素
s := arr[:endIndex]
缺省startIndex时将表示从arr的第一个元素开始
s :=make([]int,len,cap)
通过内置函数make()初始化切片s,[]int 标识为其元素类型为int的切片
len() 方法获取长度
cap() 可以测量切片最长可以达到多少
例子:
package main
import “fmt”
func main() {
var numbers = make([]int, 3, 5)
printSlice(numbers)
}
func printSlice(x []int) {
fmt.Printf(“len=%d cap=%d slice=%v\n”, len(x), cap(x), x)
}
输出: len=3 cap=5 slice=[0 0 0]
一个切片在未初始化之前默认为 nil,长度为 0
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来
例子:
package main
import “fmt”
func main() {
var numbers []int
printSlice(numbers)
numbers = append(numbers, 0)
printSlice(numbers)
numbers = append(numbers,1)
printSlice(numbers)
numbers = append(numbers, 2,3,4)
printSlice(numbers)
numbers1 := make([]int, len(numbers), (cap(numbers)) * 2)
copy(numbers1, numbers)
printSlice(numbers)
}
func printSlice(x []int) {
fmt.Printf(“len=%d cap=%d slice=%v\n”, len(x), cap(x), x)
}
输出:
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
范围
range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对的 key 值。
例子:
package main
import “fmt”
func main() {
nums := []int{2,3,4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println(“sum:”,sum)
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
kvs := map[string]string{"a":"apple", "b":"banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n",k,v)
}
for i, c := range "go" {
fmt.Println(i, c)
}
}
输出:
sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111
Map集合
var map_variable map[key_data_type]value_data_type
或
map_variable := make(map[key_data_type]value_data_type)
如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对
例子:
package main
import “fmt”
func main() {
var countryCapitalMap map[string]string
countryCapitalMap = make(map[string]string)
countryCapitalMap["France"] = "Paris"
countryCapitalMap["Italy"] = "罗马"
countryCapitalMap["Japan"] = "东京"
countryCapitalMap["China"] = "南京"
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap[country])
}
capital, ok := countryCapitalMap["美国"]
if (ok) {
fmt.Println("美国的首都是", capital)
} else {
fmt.Println("美国的首都没有存储")
}
}
输出:
France 首都是 Paris
Italy 首都是 罗马
Japan 首都是 东京
China 首都是 南京
美国的首都没有存储
delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:
package main
import “fmt”
func main() {
countryCapitalMap := map[string]string{“France”:“Paris”, “Italy”:“Rome”, “Japan”:“Tokyo”}
fmt.Println(“原始地图”)
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap[country])
}
delete(countryCapitalMap, "France")
fmt.Println("法国条目被删除")
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap[country])
}
}
输出:
原始地图
France 首都是 Paris
Italy 首都是 Rome
Japan 首都是 Tokyo
法国条目被删除
删除元素后地图
Italy 首都是 Rome
Japan 首都是 Tokyo
类型转换
类型转换用于将一种数据类型的变量转换为另外一种类型的变量
type_name(expression)
type_name 为类型,expression 为表达式
例子:
package main
import “fmt”
func main() {
var sum int = 11
var count int = 3
var mean float32
mean = float32(sum)/float32(count)
fmt.Printf("mean 的值为:%f\n", mean)
}
输出:3.666667
接口
它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
/* 定义接口 */
type interface_name interface {
method_name1 [return_type]
method_name2 [return_type]
method_name3 [return_type]
…
method_namen [return_type]
}
/* 定义结构体 /
type struct_name struct {
/ variables */
}
/* 实现接口方法 /
func (struct_name_variable struct_name) method_name1() [return_type] {
/ 方法实现 /
}
…
func (struct_name_variable struct_name) method_namen() [return_type] {
/ 方法实现*/
}
例子:
package main
import (
“fmt”
)
type Phone interface {
call()
}
type NokiaPhone struct {
}
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 = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
输出:
I am Nokia, I can call you!
I am iPhone, I can call you!
错误处理
error类型是一个接口类型,这是它的定义:
type error interface {
Error() string
}
例子:
package main
import (
“fmt”
)
// 定义一个 DivideError 结构
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)
}
}
输出:
100/10 = 10
errorMsg is:
Cannot proceed, the divider is zero.
dividee: 100
divider: 0
并发
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:go 函数名( 参数列表 )
例子:
package main
import (
“fmt”
“time”
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say(“world”)
say(“hello”)
}
输出:
hello
world
hello
world
world
hello
hello
world
world
hello
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据并把值赋给 v
通道就可以使用 close() 函数来关闭。
项目结构:
── bin # 可执行的二进制文件,由 go install 或 go build 生成
├── pkg
│ └── … # object文件,包含了编译好的二进制代码,debug信息与源程序信息
└── src # 源代码文件
├── gitee.com
│ └── schwarzeni
│ └── …
└── github.com
└── schwarzeni
└── …
defer代码块会在函数调用链表中增加一个函数调用。这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用。因此,defer通常用来释放函数内部变量。
nil远比java中的null要难以理解和掌握
1.普通的 struct(非指针类型)的对象不能赋值为 nil,也不能和 nil 进行判等(==),即如下代码,不能判断 *s == nil(编译错误),也不能写:var s Student = nil
但是struct的指针对象可以赋值为 nil 或与 nil 进行判等。不过即使 *Student 类型的s3 == nil,依然可以输出s3的类型:*Student
var s3 *Student = nil
2.接口对象和接口对象的指针都可以赋值为 nil ,或者与 nil 判等(==)。此处所说的接口可以是 interface{},也可以是自定义接口如上述代码中 IStudent. 使用 new 创建一个 *interface{} 类型的s2之后,该指针对象s2 !=nil ,但是该指针对象所指向的内容 *s2 == nil
3.将一个指针对象赋值为 nil ,然后将该指针对象赋值给一个接口(当然,该指针类型必须实现了这个接口),此时该接口对象将不为 nil
使用json.Marshal()函数可以对一组数据进行JSON格式的编码
可以使用json.Unmarshal()函数将JSON格式的文本解码为Go里面预期的数据结构