1、不支持继承 重载 ,比方C++Java的接口,接口的改动会影响整个实现改接口的类行为的改动,Go 设计者觉得这一特点也许根本没用.
2、必不论什么函数定义必须花括号跟在函数声明后面而不能换行 如 func funca(a int){},在Go语言中 函数也是一种类型 能够被推导 使用支持匿名函数 和闭包。
函数的返回值支持多重返回类似Python , 假设不赋值 整数类型默认 0 浮点数类型默认0.0 error类型 默认是nil
3、不用的包一定不要引入,这是Go的原则,就如Python的严格制表符对其一样 ,和 unused 变量都会被 编译器所报错
4、package做为引入包用。
5、import "a" 调用包内部的函数 a.xx()
6、CGo是Go语言的一个特性,能够方便高速的在Go中调用C 相比Java JNI简单easy实现
7、go通过goroutine 进行协程优化 提高并发性能。 动态线程调整。
8、6g和6l是64位版本号的Go编译器和链接器,相应的32位版本号工具为8g和8l。
Go还有另外一个 GCC版本号的编译器。名为 gccgo。
9、注意多个go文件能够用同一个包名字,假设要为 Go生成可运行文件 那么必须 进行 package main
main函数的声明
func main()
{
args := os.Args
if args == nil || len(args) < 2 {
return
}
10、Linux 为了可以构建这个project, 须要先把这个project的根文件夹加入到环境变量GOPATH中。 如果calcproj 文件夹位于~/goyard下,则应编辑~/.bashrc文件。并加入以下这行代码:
export GOPATH=~/goyard/calcproj
然后运行下面命令应用该设置: $ source ~/.bashrc ,GOPATH和PATH环境变量一样。也能够接受多个路径。而且路径和路径之间用冒号切割。
设置完GOPATH后,如今我们開始构建project。如果我们希望把生成的可运行文件放到 calcproj/bin文件夹.
11、GoRoot go安装路径 ....上面的时候project路径
12、GDB调试
不用设置什么编译选项, Go语言编译的二进制程序直接支持GDB调试, 比方之前用go build calc编译出来的可执行文件calc。就能够直接用下面命令以调试模式执行:
$ gdb calc
由于GDB的标准使用方法与Go没有特别关联,这里就不具体展开了,有兴趣的读者能够自行查
看相应的文档。须要注意的是,Go编译器生成的调试信息格式为DWARFv3,仅仅要版本号高于7.1
的GDB应该都支持它。
13、Go语言会被称为“更好的C语言”
14、每一行不须要加分号自己主动加入
15 、加入了map[string] int
16、加入了类型推导 var a int =1 等价 a:=1
var v1 int = 10 // 正确的使用方式1
var v2 = 10 // 正确的使用方式2,编译器能够自己主动推导出v2的类型
v3 := 10 // 正确的使用方式3,编译器能够自己主动推导出v3的类型
出如今:=左側的变量不应该是已经被声明过的。否则会导致编译错误,比方以下这个
写法:
var i int
i := 2
会导致类似例如以下的编译错误:
no new variables on left side of :=
17、支持多重赋值,i, j = j, i 两个值能够如此简单的进行交换 而不许引入外部变量
18、
我们在使用传统的强类型语言编程时,常常会出现这样的情况。即在调用函数时为了获取一个
值,却由于该函数返回多个值而不得不定义一堆无用的变量。在Go中这样的情况能够通过结合使
用多重返回和匿名变量来避免这样的丑陋的写法。让代码看起来更加优雅。
如果GetName()函数的定义例如以下,它返回3个值,分别为firstName、lastName和
nickName:
func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"
}
若仅仅想获得nickName,则函数调用语句能够用例如以下方式编写: _, _, nickName := GetName()
这样的使用方法能够让代码很清晰,基本上屏蔽掉了可能混淆代码阅读者视线的内容。从而大幅
减少沟通的复杂度和代码维护的难度
19、
在Go语言中,常量是指编译期间就已知且不可改变的值。常量能够是数值类型(包含整型、
浮点型和复数类型) 、布尔类型、字符串类型等。
所谓字面常量(literal) ,是指程序中硬编码的常量,如:
-12
3.14159265358979323846 // 浮点类型的常量
3.2+12i // 复数类型的常量
true // 布尔类型的常量
"foo" // 字符串常量
在其它语言中。常量通常有特定的类型,比方12在C语言中会觉得是一个int类型的常量。
假设要指定一个值为12的long类型常量,须要写成-12l,这有点违反人们的直观感觉。Go语言
的字面常量更接近我们自然语言中的常量概念,它是无类型的。仅仅要这个常量在对应类型的值域
范围内,就能够作为该类型的常量,比方上面的常量-12,它能够赋值给int、uint、int32、
int64、float32、float64、complex64、complex128等类型的变量。
20、常量的定义
通过constkeyword,你能够给字面常量指定一个友好的名字:
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0。常量的多重赋值
const a, b, c = 3, 4, "foo"
// a = 3, b = 4, c = "foo", 无类型整型和字符串常量
Go的常量定义能够限定常量类型,但没必要的。假设定义常量时没有指定类型,那么它
常量定义的右值也能够是一个在编译期运算的常量表达式,比方
const mask = 1 << 3
因为常量的赋值是一个编译期行为。 所以右值不能出现不论什么须要执行期才干得出结果的表达
式,比方试图以例如以下方式定义常量就会导致错误
const Home = os.GetEnv("HOME")
原因非常easy。os.GetEnv()仅仅有在执行期才干知道返回结果。在编译期并不能确定。所以
无法作为常量定义的右值。
21、
Go语言提前定义了这些常量:true、false和iota。
iota比較特殊。能够被觉得是一个可被编译器改动的常量,在每个constkeyword出现时被
重置为0。然后在下一个const出现之前。每出现一次iota。其所代表的数字会自己主动增1。
从下面的样例能够基本理解iota的使用方法:
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
const (
a = 1 << iota // a == 1 (iota在每一个const开头被重设为0)
b = 1 << iota // b == 2
c = 1 << iota // c == 4
)
const (
u = iota * 42 // u == 0
v float64 = iota * 42 // v == 42.0
w = iota * 42 // w == 84
)
const x = iota // x == 0 (由于iota又被重设为0了)
const y = iota // y == 0 (同上)
假设两个const的赋值语句的表达式是一样的,那么能够省略后一个赋值表达式。
因此,上
面的前两个const语句可简写为:
const ( // iota被重设为0
c0 = iota // c0 == 0
c1 // c1 == 1
c2 // c2 == 2
)
const (
a = 1 <<iota // a == 1 (iota在每一个const开头被重设为0)
b // b == 2
c // c == 4
)
22、 关于枚举 全部符号 以大写开头在包外是可见的 小写仅仅能在包内部使用
枚举指一系列相关的常量,比方以下关于一个星期中每天的定义。通过上一节的样例。我们
看到能够用在const后跟一对圆括号的方式定义一组常量, 这样的定义法在Go语言中通经常使用于定义
枚举值。Go语言并不支持众多其它语言明白支持的enumkeyword。
以下是一个常规的枚举表示法,当中定义了一系列整型常量:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)
同Go语言的其它符号(symbol)一样。以大写字母开头的常量在包外可见。
以上样例中numberOfDays为包内私有,其它符号则可被其它包訪问。
23、go中的布尔类型 仅仅能用 false true != ==不能进行强制转换
Go语言中的布尔类型与其它语言基本一致,keyword也为bool,可赋值为提前定义的true和
false演示样例代码例如以下:
var v1 bool
v1 = true
v2 := (1 == 2) // v2也会被推导为bool类型
布尔类型不能接受其它类型的赋值,不支持自己主动或强制的类型转换。下面的演示样例是一些错误
的使用方法,会导致编译错误:
var b bool
b = 1 // 编译错误
b = bool(1) // 编译错误
下面的使用方法才是正确的:
var b bool
b = (1!=0) // 编译正确
fmt.Println("Result:", b) // 打印结果为Result: true
24、Go语言支持下面的几种比較运算符:>、<、==、>=、<=和!=。这一点与大多数其它语言相 同。与C语言全然一致。
if i!=j { } //必须带大括号
i,j:=1,2
if i==j{
fmt.Println("i==j");
}else {
fmt.Println("i!=j");
}
25、两个不同类型的值不能比較 比方 int8 int16,仅仅能强制转换 然后再做比較
var a int8
var b int16
a,b=1,2
if int16(a)==b{
fmt.Printf("a==b")
}
26、尽管两个 int8 int16不能直接比較 可是 不论什么整数类型都能和字面常量整数进行比較 ,可是不能和字符串字面常量进行比較
var a int16
if a==1{
fmt.Printf("a!="a"");
}
27、Go语言中的位运算 注意 C语言中的~ 取反。 而Go中变成了^
表 2-2
运 算 含 义 样
例
x << y 左移 124
<< 2 // 结果为496
x >> y 右移 124
>> 2 // 结果为31
x ^ y 异或 124
^ 2 // 结果为126
x & y 与
124 & 2 // 结果为0
x | y 或 124
| 2 // 结果为126
^x 取反 ^2
// 结果为-3
Go语言的大多数位运算符与C语言都比較类似,除了取反在C语言中是~x。而在Go语言中 是^x
28、关于浮点数的操作 浮点数 自己主动推导 是float64即C语言中的double 不能直接和fload32转换 要进行强制转换
由于浮点数的比較精度
浮点型用于表示包括小数点的数据。比方1.234就是一个浮点型数据。Go语言中的浮点类型
採用IEEE-754标准的表达方式。
1. 浮点数表示
Go语言定义了两个类型float32和float64。当中float32等价于C语言的float类型,
float64等价于C语言的double类型。
在Go语言里,定义一个浮点数变量的代码例如以下:
var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 假设不加小数点。fvalue2会被推导为整型而不是浮点型
对于以上样例中类型被自己主动推导的fvalue2。须要注意的是其类型将被自己主动设为float64。
而无论赋给它的数字是否是用32位长度表示的。因此。对于以上的样例,以下的赋值将导致编译
错误:
fvalue1 = fvalue2
对于以上样例中类型被自己主动推导的fvalue2,须要注意的是其类型将被自己主动设为float64,
而无论赋给它的数字是否是用32位长度表示的。因此。对于以上的样例,以下的赋值将导致编译
错误:
fvalue1 = fvalue2
而必须使用这种强制类型转换:
fvalue1 = float32(fvalue2)
29、自己定义精准的 浮点数比較 ,因为浮点数不是一种精确的表达方式 所以比較精度 可能不准确
由于浮点数不是一种精确的表达方式。 所以像整型那样直接用==来推断两个浮点数是否相等
以下是一种推荐的替代方案:
import "math"
// p为用户自己定义的比較精度。比方0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
30、 Go语言中的复数类型 , real 取出 实部 imag取出虚部 虚部为0 的复数 为纯虚数。 某一类数字能够表示成这样的复数类型
var v1 complex64
v1 = 2.5+15i
v2 := 2.5+15i
v3 :=complex(2.5,15)
fmt.Println(v1)
fmt.Println(v2)
fmt.Println(v3)
fmt.Println("real:",real(v1))
fmt.Println("real:",imag(v1))
复数实际上由两个实数(在计算机中用浮点数表示)构成。一个表示实部(real),一个表示虚部(imag)。
对于什么是复数能够參考:http://baike.baidu.com/view/10078.htm
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real) ,一个表示
虚部(imag) 。假设了解了数学上的复数是怎么回事。那么Go语言的复数就很easy理解了。
1. 复数表示
复数表示的示比例如以下:
var value1 complex64
// 由2个float32构成的复数类型
value1 = 3.2 + 12i
value2 := 3.2 + 12i
// value2是complex128类型
value3 := complex(3.2, 12)
// value3结果同 value2
2. 实部与虚部
对于一个复数z = complex(x, y),就能够通过Go语言内置函数real(z)获得该复数的实
部。也就是x,通过imag(z)获得该复数的虚部,也就是y。
很多其它关于复数的函数。请查阅math/cmplx标准库的文档。
31、Golang中的字符串操作
go中的字符串声明之后仅仅能获取 字符不能改动字符 可是能够改动整个字符串
var str string
str = "abc"
str += "d"
str="1111"
fmt.Println(str)
我们能够获取单个字符可是不能改动单个字符
var str string
str = "abc"
str += "d"
ch:=str[0]
fmt.Printf("A:%c",ch) //println不能格式化
32、关于Go的编码处理 仅仅支持 Unicode UTF-8格式
import strings 这个包中包括了处理string类型的全部工具函数函数
Go编译器支持UTF-8的源码文件格式。这意味着源码中的字符串能够包括非ANSI的字
符。比方“Hello world. 你好,世界! ”能够出如今Go代码中。但须要注意的是,假设你的Go代
码须要包括非ANSI字符。保存源文件时请注意编码格式必须选择UTF-8。特别是在Windows下一
般编辑器都默认存为本地编码,比方中国地区可能是GBK编码而不是UTF-8,假设没注意这点在
编译和执行时就会出现一些意料之外的情况。
字符串的编码转换是处理文本文档(比方TXT、XML、HTML等)很常见的需求。只是可
惜的是Go语言仅支持UTF-8和Unicode编码。对于其它编码。Go语言标准库并没有内置的编码转
换支持。只是,所幸的是我们能够非常easy基于iconv库用Cgo包装一个。这里有一个开源项目:
https://github.com/xushiwei/go-iconv
33、在Go中字符串的单个字符就是 byte类型也就是 uint8
一个字符串的长度len返回的默认是int类型也就是平台相关类型,我们在做对应的操作的收 要么自己主动推导 要么进行强制转换
var str string
str = "abcdefghijklmn"
var length int8=int8(len(str))
for i:=0 ;i<int(length) ;i++{
fmt.Printf("%c",str[i])
}
34、关于遍历Unicode字符,每一个 unicode的字符类型是 rune
每一个中文字符在UTF-8中占3个字节,而不是1个字节。
还有一种是以Unicode字符遍历:
str := "Hello,世界"
for i, ch := range str {
fmt.Println(i, ch)//ch的类型为rune
}
输出结果为:
0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028
以Unicode字符方式遍历时,每一个字符的类型是rune(早期的Go语言用int类型表示Unicode
測试代码
package main
import "fmt"
func main() {
var strUnicode string = "hello,世界"
for i,ch := range strUnicode{
fmt.Println(i,ch)
}
}
35、关于 Go语言中支持的两种字符类型 一种是 byte 实际上是uint8的别名 ,还有一种是unicode类型的字符 keyword为 rune
在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名) ,代表UTF-8字符串的单个字节的值。还有一个是rune,代表单个Unicode字符。
关于rune相关的操作,可查阅Go标准库的unicode包。另外unicode/utf8包也提供了 UTF8和Unicode之间的转换。
出于简化语言的考虑。Go语言的多数API都如果字符串为UTF-8编码。虽然Unicode字符在标 准库中有支持,但实际上较少使用。
36、遍历unicode字符的还有一种是 能够用变量占位符去掉不想要的数据
package main
import "fmt"
func main() {
var strUnicode string = "hello,世界"
for _,ch := range strUnicode{
fmt.Printf("%c
",ch)
}
}
37、关于Go语言的指针操作
package main
import "fmt"
func main() {
var inta int8=3 ;
var pinta*int8=&inta ;
fmt.Printf("%d",*pinta);
}
38、
关于数组的遍历 range 遍历能够选择忽略 索引
package main
import "fmt"
func main() {
byteArr:=[5]byte{1,2,3,4,5}
for _,val:=range byteArr {
fmt.Println(val)
}
}
//////////////////////////////////各种数组的声明/////////////////////////////////
[32]byte // 长度为32的数组,每一个元素为一个字节
[2*N] struct { x, y int32 } // 复杂类型数组
[1000]*float64
// 指针数组 [3][5]int
// 二维数组 [2][2][2]float64
// 等同于[2]([2]([2]float64))
/////关于二维数组的初始化以及声明..................................
package main
import "fmt"
func main() {
td:=[2][5]int{{1,2,3,4,5},{5,4,3,2,1}}
for _,val:=range td{
for _,vall:=range val{
fmt.Println(vall)
}
}
}
39、关于Go的数组 是一个值类型,在做为參数传递 或者 做为函数返回的时候 都是 数组的副本,所以不能通过传递 数组參数在函数内部 进行改动 。、
package main
import "fmt"
func modify(arr[5]int){
arr[1]=1
fmt.Printf("arr[1]=%d
",arr[1])
}
func main() {
td:=[5]int{1,2,3,4,5}
modify(td)
for _,val:=range td{
fmt.Println(val)
}
}
//////Go Web
40、Go语言中的数组切片 能够从一个已存在的数组创建 也能够直接手动创建一个数组切片
一个指向原生数组的指针;
数组切片中的元素个数。
数组切片已分配的存储空间。
从底层实现的角度来看。数组切片实际上仍然使用数组来管理元素,因此它们之间的关系让
C++程序猿们非常easy联想起STL中std::vector和数组的关系。基于数组。数组切片加入了一系
列管理功能,能够随时动态扩充存放空间,而且能够被任意传递而不会导致所管理的元素被反复
基于数组创建切片
package main
import "fmt"
func modify(arr[]int){
arr[1]=1
fmt.Printf("arr[1]=%d
",arr[1])
}
func main() {
td:=[]int{1,2,3,4,5}
//基于数组创建切片
slice:=td[:3] // td[:] td[begin:end] 都能够创建数组切片 还能够创建一个比数组还大的切片
for _,val:=range slice{
fmt.Println(val)
}
}
41、主动创建数组切片 操作数组 全部的方法 都是适用于数组切片 ,合理利用切片能极大提高内存操作的速度
数组切片支持内建的cap()函数和len()函数
从数组切片创建数组切片的时候仅仅要不超过模板切片的大小那么创建是没问题的,否则会报出数组切片越界的错误。
td:=[]int{1,2,3,4,5} //这样创建出来的实际上是数组切片
数组切片做为參数传递给函数是能够被改动值的 ,攻克了Go中数组属于值类型 结果函数传递參数的时候值被复制
package main
import "fmt"
func modify(arr[]int){
arr[1]=1
fmt.Printf("arr[1]=%d
",arr[1])
}
func main() {
td:=[]int{1,2,3,4,5}
//基于数组创建切片
slice:=td[:3] //从切片创建切片 都能够
modify(slice)
for _,val:=range slice{
fmt.Println(val)
}
}
从输出结果我们发现了 数组切片能够作为參数传递到函数中而且被函数所改动
并不是一定要事先准备一个数组才干创建数组切片。Go语言提供的内置函数make()能够用于
灵活地创建数组切片。以下的样例示范了直接创建数组切片的各种方法。
创建一个初始元素个数为5的数组切片,元素初始值为0:
mySlice1 := make([]int, 5)
创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:
mySlice2 := make([]int, 5, 10)
直接创建并初始化包括5个元素的数组切片:
mySlice3 := []int{1, 2, 3, 4, 5}
当然,其实还会有一个匿名数组被创建出来,仅仅是不须要我们来担心而已。
数组切片支持Go语言内置的cap()函数和len()函数,代码清单2-2简单示范了这两个内置
函数的使用方法。能够看出,cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是
数组切片中当前所存储的元素个数
动态创建 一个 初始化五个0 而且 内存储空间初始化20的 数组切片
td1:=make([]int,5,20)
fmt.Println(cap(td1))
fmt.Println(len(td1))
//为数组切片动态添加元素
append(td1,1,2,3,4,56,6,7)
//为数组元素加入数组切片
append(td1,td2...) //一定要加... 附加数组切片的时候
须要注意的是,我们在第二个參数mySlice2后面加了三个点,即一个省略号,假设没有这个省
略号的话,会有编译错误,由于按append()的语义,从第二个參数起的全部參数都是待附加的
元素。由于mySlice中的元素类型为int,所以直接传递mySlice2是行不通的。加上省略号相
当于把mySlice2包括的全部元素打散后传入
42. 数组切片支持内容复制
数组切片支持Go语言的还有一个内置函数copy(),用于将内容从一个数组切片拷贝到还有一个
数组切片。假设增加的两个数组切片不一样大,就会按当中较小的那个数组切片的元素个数进行
复制。以下的演示样例展示了copy()函数的行为:
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 仅仅会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 仅仅会复制slice2的3个元素到slice1的前3个位置
old:=[]int{1,2,3,4,5,6}
newSlice:=[]int{1,3,3}
copy(old,newSlice)
fmt.Println(old)
43、在map中使用复杂数据类型
我们能够使用Go语言内置的函数make()来创建一个新map。以下的这个样例创建了一个键
类型为string、值类型为PersonInfo的map:
myMap = make(map[string] PersonInfo)
也能够选择是否在创建时指定该map的初始存储能力。以下的样例创建了一个初始存储能力
为100的map:
myMap = make(map[string] PersonInfo, 100)
关于存储能力的说明。能够參见2.3.6节中的内容。
创建并初始化map的代码例如以下:
myMap = map[string] PersonInfo{
"1234":
PersonInfo{"1", "Jack", "Room 101,..."},
}
package main
import "fmt"
type Info struct{
name string
age int8
}
func main() {
var infoMap map[string] Info
infoMap=make(map[string] Info)
infoMap["s1"]= Info{"ydw",11}
infoMap["s2"]=Info{"xxx",22}
fmt.Println(infoMap)
/////假设sone 没有查找到那么返回值应该是nil 实际上我们仅仅须要推断 ok是否是 true or false 就可以推断元素是否查找到
sone,ok:=infoMap["s1"]
if ok {
fmt.Println("s1 student info exists!",sone.name,":",sone.age)
}else{
fmt.Println("s1 student info not exists!")
}
}
/////删除一个map用
delete(map,"key")
Go语言提供了一个内置函数delete(),用于删除容器内的元素。以下我们简介一下如
何用delete()函数删除map内的元素:
delete(myMap, "1234")
上面的代码将从myMap中删除键为“1234”的键值对。假设“1234”这个键不存在,那么这个调
用将什么都不发生,也不会有什么副作用。可是假设传入的map变量的值是nil,该调用将导致
程序抛出异常(panic)
44、对于函数的返回值的限制
func returnFunc(num int) int{
if num > 0 {
return 100 //错误 返回值不能写在if...else之中结构之中
}
return 100
}
45、switch case default使用方法
switch i {
case 0:
fmt.Printf("0")
case 1:
fmt.Printf("1")
case 2:
fallthrough
case 3:
fmt.Printf("3")
case 4, 5, 6:
fmt.Printf("4, 5, 6")
default:
fmt.Printf("Default")
}
46、Go语言的goto 和break Label更加的灵活处理 循环和跳转
47、自己定义复杂数据类型
type Info struct{
name string
age int8
}
48、关于函数返回 一个值和函数返回多个值
package main
import "fmt"
func ret1() int{
return 1
}
func ret2()(a int,b int){
a,b=1,2
return
}
func main() {
a,b:=ret2()
fmt.Println(ret1(),a,b)
}
48、Go的大写和小写规则
Go牢记这种规则:小写字母开头的函数仅仅在本包内可见。大写字母开头的函数才
能被其它包使用。
这个规则也适用于类型和变量的可见性。
49、函数的不定參数 实际上是一种"语法糖"
package main
import "fmt"
func show(args ...int){
for _,val:= range args{
fmt.Println(val)
}
}
func main() {
show(1,2,3)
}
50、不定參数的传递 不定參数还能够传递给其它不定參数的 函数 而且能够打乱传递
...
unc myfunc(args ...int) {
// 按原样传递
myfunc3(args...)
// 传递片段。实际上随意的int slice都能够传进去
myfunc3(args[1:]...)
}
51、// ... 传递随意类型的參数
package main
import "fmt"
func Printfx(args ...interface{}) {
for _,val:= range args{
fmt.Println(val)
}
}
func main() {
Printfx(1,2,3,"adsd","sdaddf")
}
52、Go的不定參数语法糖
Go的不定參数语法糖会把 參数构成一个数组切片 当然你直接传递 切片数组是不能够的,由于他的參数就是1,2,3,4,5,66,7, 可是我们能够通过...的方式打乱
数组 或者数组切片
...仅仅能够打乱数组切片,常规数组是个值类型你是无法操作的
package main
import "fmt"
func show(args ...int){
for _,val:= range args{
fmt.Println(val)
}
}
// ...
func Printfx(args ...interface{}) {
for _,val:= range args{
fmt.Println(val)
}
}
func main() {
Printfx(1,2,3,"adsd","sdaddf")
slice:=[]int{5,4,3,2,6,7,8}
show(slice...)
}
53、通过传递不定參数获取 随意类型不定參数的类型
package main
import "fmt"
func checkType(args...interface{}){
for _,val:=range args{
switch val.(type){
case int :
fmt.Println("Type is int!")
case string:
fmt.Println("Type is string!")
default:
fmt.Println("Type is unknow!")
}
}
}
func main() {
checkType(1,2,3,"aaaa",int64(22))
}
54、使用匿名函数和闭包 Go的匿名函数实际上就是闭包
///定义匿名函数 而且调用
funAdd:=func(a,b int)int{
return a+b
}
r:=funAdd(11,22)
fmt.Println("a+b=",r)
////定义 +调用匿名函数 一起
r=func(a,b int)int{
return a-b
}(11,2)
fmt.Println("a+b=",r)
////Go闭包 通过函数创建匿名函数 而且返回函数
package main
import "fmt"
func createFunc()(func(aa,bb,cc int) int){
return func(aa,bb,cc int)int{
return aa+bb+cc
}
}
func main() {
add:=createFunc()
addNum:=add(1,2,3)
fmt.Println("addNum:",addNum)
}
package main
import (
"fmt"
)
///////函数的闭包定义 直接调用 .......闭包内部使用的代码块外部的变量 仅仅要代码块没有释放那么变量不会被释放的
func main() {
var j int = 5
a := func()(func()) {
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d
", i, j)
}
}()
a()
j *= 2
a()
}
55、函数多返回值
func ret()(int,int){
return 1,2
}
a,b:=ret()
fmt.Println("a,b=",a,b)
56、对于结构类型空的值是nil
57、定义结构体一定要加 type,type和C/C++的 typedef 类似也能够
起别名
package main
import "fmt"
type Data struct{
name string
age int
}
func main() {
data:=Data{"a",1}
fmt.Println(data)
}
///////////////////给结构体起个别名
package main
import "fmt"
type Data struct{
name string
age int
}
type DData Data
func main() {
data:=DData{"a",1}
fmt.Println(data)
}
58、Go的defer和资源释放 相关问题 defer语句是依照 先进后出的原则,也就是说最后一个defer将会被先运行。
defer字面的意思是延迟运行,也就是说会在不须要的时候自己主动运行
而Go语言使用defer
keyword简简单单地攻克了资源何时释放的问题,比方下面的样例:
func CopyFile(dst, src string) (w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
defer srcFile.Close()
dstFile, err := os.Create(dstName)
if err != nil {
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
即使当中的Copy()函数抛出异常,Go仍然会保证dstFile和srcFile会被正常关闭。
假设认为一句话干不完清理的工作。也能够使用在defer后加一个匿名函数的做法:
defer func() {
// 做你复杂的清理工作
} ()
另外,一个函数中能够存在多个defer语句,因此须要注意的是,defer语句的调用是遵照
先进后出的原则,即最后一个defer语句将最先被运行。
仅仅只是,当你须要为defer语句究竟哪
个先运行这样的细节而烦恼的时候。说明你的代码架构可能须要调整一下了
59、panic()和recover()
////panic场景1
package main
import "fmt"
func main() {
defer func(){
fmt.Println("hello,defer go")
}()
panic(11111)
}
////recover场景2
package main
import "fmt"
func main() {
defer func(){
fmt.Println("hello,defer go")
}()
panic(11111)
}
Go语言引入了两个内置函数panic()和recover()以报告和处理执行时错误和程序中的错
误场景:
func panic(interface{})
func recover() interface{}
当在一个函数运行过程中调用panic()函数时。正常的函数运行流程将马上终止。但函数中
之前使用deferkeyword延迟运行的语句将正常展开运行,之后该函数将返回到调用函数,并导致
逐层向上运行panic流程,直至所属的goroutine中全部正在运行的函数被终止。错误信息将被报
告。包含在调用panic()函数时传入的參数。这个过程称为错误处理流程。
从panic()的參数类型interface{}我们能够得知,该函数接收随意类型的数据。比方整
型、字符串、对象等。调用方法非常easy,以下为几个样例:
panic(404)
panic("network broken")
panic(Error("file not exists"))
recover()函数用于终止错误处理流程。普通情况下,recover()应该在一个使用defer
keyword的函数中运行以有效截取错误处理流程。假设没有在发生异常的goroutine中明白调用恢复
过程(使用recoverkeyword) 。会导致该goroutine所属的进程打印异常信息后直接退出。
我们对于foo()函数的运行要么心里没底感觉可能会触发错误处理,或者自己在当中明白加
入了按特定条件触发错误处理的语句,那么能够用例如以下方式在调用代码中截取recover():
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime error caught: %v", r)
}
}()
foo()
不管foo()中是否触发了错误处理流程。该匿名defer函数都将在函数退出时得到运行。假
如foo()中触发了错误处理流程。recover()函数运行将使得该错误处理过程终止。假设错误处
理流程被触发时,程序传给panic函数的參数不为nil。则该函数还会打印具体的错误信息。
60、panic()函数和recover()函数的调用详细差别在哪里
在panic()開始错误处理流程,传入的类型是interface{}随意类型 , 假设我们在defer 函数中调用
recover()函数那么会打断错误处理流程。 panic的调用会终止正常的程序流程
recover()函数用于终止错误处理流程。普通情况下,recover()应该在一个使用defer
keyword的函数中运行以有效截取错误处理流程。假设没有在发生异常的goroutine中明白调用恢复
过程(使用recoverkeyword) ,会导致该goroutine所属的进程打印异常信息后直接退出。
package main
import "fmt"
func main() {
//通过闭包定义 defer匿名函数 而且直接调用
defer func(){
//recover 结束当前错误处理过程 而且返回 panic的參数,并不结束其它goroutine的运行.......
if r:=recover() ;r!=nil{
fmt.Println("recover() called!")
}
fmt.Println("hello,defer go")
}()
panic("hello,go")
}
61、关于const 和iota的使用
package main
import "fmt"
var str string="aaa"
const(
A=iota
B
C
)
func main() {
fmt.Println(B)
defer func(){
if r:=recover() ;r!=nil{
fmt.Println("recover() called!")
fmt.Println(r)
}
fmt.Println("hello,defer go")
}()
panic("hello,go")
}
62、高速排序算法与数组切片的使用
package main
import "fmt"
import "math/rand"
/////冒泡排序Go实现 时间复杂富 O(n)=n~n^2
func bubbledSort(values []int){
var flags bool =true
for i:=0 ;i<len(values);i++{
flags=true
for j:=0;j<len(values)-i-1;j++{
if values[j]>values[j+1] {
values[j],values[j+1]=values[j+1],values[j]
flags=false
}
}
if flags {
break
}
}
}
///////高速排序Go实现
func quickSort(values []int,left,right int){
temp := values[left]
p := left
i, j := left, right
for i <= j {
for j >= p && values[j] >= temp {
j--
}
if j >= p {
values[p] = values[j]
p = j
}
if values[i] <= temp && i <= p {
i++
}
if i <= p {
values[p] = values[i]
p = i
}
}
values[p] = temp
if p - left > 1 {
quickSort(values, left, p - 1)
}
if right - p > 1 {
quickSort(values, p + 1, right)
}
}
func main() {
//创建初始化0个元素 容量1000的 切片 假设用索引直接訪问切片会越界的 容量必须大于等于 初始化元素个数
//val[1]=11
val:=make([]int,0,1000)
for i:=0;i<1000;i++{
val=append(val,rand.Intn(1000))
}
fmt.Println("冒泡排序前:",val)
bubbledSort(val)
fmt.Println("冒泡排序后:",val)
}
63、Go中的import和package 等等的关系
//import仅仅是引入的目录而已,,,能够使用相对路径或者绝对路径
//他会把目录中的全部.go文件引入 ,,,,,,
///package xx 实际上是 在外不用调用用的 比方xx.A() 并非给import使用 package内部的小写所有是私有 大写所有是公有
//外部包不能和主模块放到一起会编译只是的
import "./bubble"
版权声明:本文博主原创文章,博客,未经同意不得转载。