Go学习总结笔记(一)

go version 1.13

基础语法

1. new(T)make(T,args) 的区别?

new(T) 返回对应T的指针类型,即*T,指针指向的数据其值为零值,make(T,args) 只能初始化 slice,map,chan这三种类型,T的初始化值也为零值,但是返回类型是T类型的引用,而不是指针类型。

2. slice 使用append()函数时,参数为slice和相同类型的元素时,有啥不同?

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)

3. 变量的声明方式有哪几种?

有四种。

方式一: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

4. 常量如何声明?

常量的声明要使用关键字const,常量在声明的时候必须赋值,方式有两种。

方式一:const 常量名 = 常量值。

    const s1 = "Happy"
    const i = 1234

方式二:参考代码片断。

    const(
        s2 = "编程学习"
        i2 = 2020
    )

5. Go语言中,哪些变量类型可以直接比较,哪些满足前提条件下可比较,哪些不能比较?

布尔类型(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。

6.通过指针变量p访问成员变量的方式?

// 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)
}

7.类型别名与自定义的区别

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

8.nil可以给哪些类型的变量赋值?

nil 只能给 chanslice,map,interface,func和指针类型赋值。注意:error本质上属于interface类型,参考源代码定义,如下:

type error interface {
    Error() string
}

9.说说Go中的init()函数?

  • init()是包的初始化函数,用于程序在执行对应包的代码前做一些初始化工作;

  • 每个包可以有多个init()函数;

  • 每个包的同一个源码文件也可以有多个init()函数;

  • 同一个源码文件的init()函数执行顺序没有明确定义;

  • init()函数不能手工调用,只能在引入包时自动执行;

  • 不同包的init()函数调用关系可参考下图:

    image-20200525170544881

10.一个接口如果被*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())
}

你可能感兴趣的:(Go学习总结笔记(一))