go version 1.13
new(T)
和 make(T,args)
的区别?new(T) 返回对应T的指针类型,即*T,指针指向的数据其值为零值,make(T,args) 只能初始化 slice,map,chan这三种类型,T的初始化值也为零值,但是返回类型是T类型的引用,而不是指针类型。
slice追加slice时,需要在被追加的slice后面加...
,追加元素时而不需要,具体可看下面代码片断。
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2...)
fmt.Println(s1)
s1 = append(s1, 6, 7)
fmt.Println(s1)
有四种。
方式一:var 变量名 变量类型,这种方式变量值为零值,如下所示:
var s string
var i int
var m map[string]string
fmt.Printf("s:%T,%q,i:%T,%v,m:%T,%v",s,s,i,i,m,m)
代码输出结果:s:string,"",i:int,0,m:map[string]string,map[]
方式二: var 变量名 变量类型 = 表达式,表达式指定变量的初始值,如下所示:
var s1 string = "Hello,王二狗"
var i1 int = 520
var s2 []string = []string{"学习","go"}
fmt.Printf("s1:%T,%q,i1:%T,%v,s2:%T,%v\n",s1,s1,i1,i1,s2,s2)
代码输出结果:s1:string,"Hello,王二狗",i1:int,520,s2:[]string,[学习 go]
方式三(同时声明多个变量):var 变量名1,变量名2 = 值1,值2,如下所示:
var s3,i2 = "Happy",521
fmt.Printf("s3:%T,%q,i2:%T,%d\n",s3,s3,i2,i2)
代码输出结果:s3:string,"Happy",i2:int,521
方式四:变量名 := 值,注意,这种短变量声明方式只能使用在函数内部,如下所示:
s4 := "技术人的自我修养"
i3 := 12345
fmt.Printf("s4:%T,%q,i3:%T,%d\n",s4,s4,i3,i3)
代码输出结果:s4:string,"技术人的自我修养",i3:int,12345
常量的声明要使用关键字const
,常量在声明的时候必须赋值,方式有两种。
方式一:const 常量名 = 常量值。
const s1 = "Happy"
const i = 1234
方式二:参考代码片断。
const(
s2 = "编程学习"
i2 = 2020
)
布尔类型(boolean)、整型(integer)、浮点数(float)、复数(complex)、字符串(string)、指针类型(pointer)、通道(channel)、接口类型(interface)可以直接进行比较。
结构体(struct)、数组(array),如果元素都是能直接比较类型,(即元素类型属于前一类能直接比较的),则对应的结构体和数据也可比较。
切片(slice)、map和函数类型(func)不能进行比较,除非是与nil
进行比较。
注意:接口类型虽可直接比较,但如果实现接口的对应类型不能直接进行比较的话,也会引发panic
导致程序异常退出,如下代码所示:
// T 定义的一个演示接口
type T interface {
hello()
}
// PersonA 演示用结构体
type PersonA struct {
m map[string]int
}
// PersonB 演示用结构体
type PersonB struct {
}
func (p *PersonA) hello() {
fmt.Println("hello PersonA")
}
func (p *PersonB) hello() {
fmt.Println("hello PersonB")
}
func main() {
var p1 PersonA
var p2 PersonB
fmt.Println(p1 == p2)
}
运行以上代码会提示:invalid operation: p1 == p2 (mismatched types PersonA and PersonB)
Boolean values are comparable. Two boolean values are equal if they are either both true or both false.
Integer values are comparable and ordered, in the usual way.
Floating-point values are comparable and ordered, as defined by the IEEE-754 standard.
Complex values are comparable. Two complex values u and v are equal if both real(u) == real(v) and imag(u) == imag(v).
String values are comparable and ordered, lexically byte-wise.
Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
Channel values are comparable. Two channel values are equal if they were created by the same call to make or if both have value nil.
Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.
A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.
Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.
Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil.
Comparison of pointer, channel, and interface values to nil is also allowed and follows from the general rules above.
以上英文片断出自:https://golang.org/ref/spec#Comparison_operators。
// p.name , (*p).name 或 *&p.name
type person struct {
name string
}
func main() {
p1 := person{name: "王二狗"}
p := &p1
fmt.Printf("%s,%s,%s\n", p.name, (*p).name, *&p.name)
}
Go中可用type
关键字自定义类型,类型别名相当于给类型T
取一个别名,别名指向的类型本质上还是属于T
,二者的语法区别如下所示:
// MyInt 自定义类型:MyInt,属于一个新类型,它具有int的特性
type MyInt int
// MyInt2 类型别名:给int类型取一个别名
type MyInt2 = int
func main() {
var i int = 1234
// 编译不通过,因为Go是强类型语言,不能直接把int类型的变量赋给MyInt类型,可通过强转赋值
//var i1 MyInt = i
var i1 MyInt = MyInt(i)
var i2 MyInt2 = i
fmt.Printf("i1 type:%T,i1=%d,i2 type:%T,i2=%d\n", i1, i1, i2, i2)
}
// 输出结果:i1 type:main.MyInt,i1=1234,i2 type:int,i2=1234
nil
可以给哪些类型的变量赋值?nil 只能给 chan
,slice
,map
,interface
,func
和指针类型赋值。注意:error
本质上属于interface
类型,参考源代码定义,如下:
type error interface {
Error() string
}
init()
是包的初始化函数,用于程序在执行对应包的代码前做一些初始化工作;
每个包可以有多个init()
函数;
每个包的同一个源码文件也可以有多个init()
函数;
同一个源码文件的init()
函数执行顺序没有明确定义;
init()
函数不能手工调用,只能在引入包时自动执行;
不同包的init()
函数调用关系可参考下图:
*T
类型的变量实现,T
可以赋值给该接口类型吗?不能,参考以下代码:
// Walker 接口
type Walker interface {
Walk() string
}
// Dog 狗
type Dog struct{}
// Walk 小狗跑
func (d *Dog) Walk() string {
return "小狗奔跑"
}
func main() {
var walker Walker
wangcai := &Dog{}
fugui := Dog{}
walker = wangcai
fmt.Println("旺柴", wangcai.Walk())
fmt.Println("接口", walker.Walk())
// walker = fugui 编译不通过,因为实现Walker的是 *Dog类型
walker = &fugui
// Dog 和 *Dog 都可以调用 Walk()函数
fmt.Println("富贵", fugui.Walk())
fmt.Println("接口", walker.Walk())
}