golang学习笔记之一 - 基础知识与概念

in 学习笔记 with 0 comment 访问: 1,232 次

ch1. 基础知识学习笔记

var arr2 = [5]float32{1.0, 2.2, 3.4}    // 数组个数必须小于等于[]中设置的长度
var arr3 = [...]int{1, 2, 3, 4}         // 个数不确定可以用[...]代替,是一样的
// 定义结构体
/*
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}
*/

// 实例演示
type Books struct {
    title string
    author string
    price int
    bookId int
}

func struceTest()  {
    struct1 := Books{"水浒", "施耐庵", 199, 3}
    struct2 := Books{title: "西游记", author: "吴承恩", price: 199, bookId: 4}
    fmt.Println(struct1.title, struct1)
    fmt.Println(struct2.title, struct2)

    var struct3 Books
    struct3.title = "三国演义"
    struct3.author = "罗贯中"
    fmt.Println(struct3)
}

/* 输出
水浒 {水浒 施耐庵 199 3}
西游记 {西游记 吴承恩 199 4}
{三国演义 罗贯中 0 0}
*/
func foreach2()  {
    var arr1 = []string{"q", "w", "e", "r"}
    var arr2 = []int{'q', 'w', 'e', 'r'}
    fmt.Println(arr1, arr2)

    for key, val := range arr1 {
        fmt.Println(key, val)
    }
}

func foreach3()  {
    var map1 = map[string]string{"a":"apple", "b":"banana", "c":"car"}

    for key, val := range map1 {
        fmt.Println(key, val)
    }
}

/* 输出
[q w e r] [113 119 101 114]
0 q
1 w
2 e
3 r
a apple
b banana
c car
*/
/* 声明变量,默认 map 是 nil */
var map_variable map[key_data_type]value_data_type

/* 使用 make 函数 */
map_variable := make(map[key_data_type]value_data_type)

// 实例
map1 = map[string]int{"a":11, "b":22, "c":33}

fmt.Println(map1["a"])    // 11
map1["d"] = 444           // 动态赋值
fmt.Println(map1)         // map[a:11 b:22 c:33 d:444]

value1, isset := map1["d"]
fmt.Println(value1,"|", isset)    // 444 | true

// 删除元素
delete(map1, "d")
value1, isset = map1["d"]
fmt.Println(value1,"|", isset)    //  | false
package main

import "fmt"

type People interface {
    name() string
    age() int
}
type Man struct {

}
type Woman struct {

}

func (man Man) name() string {
    return "亚当"
}

func (man Man) age() int {
    return 22
}

func (woman Woman) name() string {
    return "夏娃"
}
func (woman Woman) age() int {
    return 18
}

func main()  {
    var people People
    people = new(Woman)
    fmt.Println(people.name(), people.age())

    people = new(Man)
    fmt.Println(people.name(), people.age())
}

/* 输出
夏娃 18
亚当 22
*/
// 实现 error 接口类型
func Sqrt(f int) (int, error)  {
    if f < 0 {
        return 0, errors.New("something wrong")
    } else {
        return f, nil
    }
}

// 调用
func errTest()  {
    result, err := Sqrt(-44)
    if err != nil {
        fmt.Println(result, "是负数,err:", err)
    } else {
        fmt.Println(result, "正数没错,err:", err)
    }
}

// 输出
// 0 是负数,err: something wrong
func say(string1 string)  {
    for i := 0; i < 5; i++ {
        time.Sleep(time.Second)
        fmt.Println(string1)
    }
}

// 不使用并发,阻塞模式
func noGoroutine()  {
    say("hello111")
    say("world222")
    // 输出:5个hello111之后5个world222 共用时10s
}

// 使用并发
func goroutine()  {
    go say("hello333")
    say("world444")
    // 同时输出,共用时5s,因为是两个不同的 goroutine
    // 当主协程结束时,子协程也是会被终止掉。所以有时候最后如果只打开了这一个函数,丢包是正常现象
}

func main()  {
    //noGoroutine()
    goroutine()
}
func sum(s []int, ch chan int)  {
    var sum = 0
    for _, value := range s {
        sum += value
    }
    ch <- sum // 把 sum 发送到通道 ch
}

func main()  {
    channelTest1()
    channelTest2()
}

// 通道
func channelTest1()  {
    var s1 = []int{1, 2, 3, 4, 5}
    var s2 = []int{1, 2, 3, 4, 5, 6}

    ch1 := make(chan int, 2)
    //ch2 := make(chan int)

    go sum(s1, ch1)    // 如果不是goroutine模式,必须设置缓冲通道!!!
    go sum(s2, ch1)

    res1 := <- ch1  // 从通道 ch1 中接收
    res2 := <- ch1  // 从通道 ch2 中接收
    fmt.Println(res1, res2)
}

// 带有缓冲区的通道
func channelTest2()  {
    ch := make(chan int, 3)

    ch <- 11
    ch <- 22
    ch <- 33

    fmt.Println(<-ch, <-ch, <-ch)
    // 输出 11 22 33
}

ch2. 基础概念理解

slice的默认开始位置是0,arr[:n]等价于arr[0:n]
slice的第二个序列默认是数组的长度,arr[n:]等价于arr[n:len(arr)]
如果从一个数组里面直接获取slice,可以这样arr[:],因为默认第一个序列是0,第二个是数组的长度,即等价于arr[0:len(arr)]

    var map1 = map[string]string{"name":"shy"}
    map1["first"] = "zhen"
    map1["last"] = "huaixiu"
    fmt.Println(map1, map1["last"])     // map[name:shy first:zhen last:huaixiu] huaixiu

    // 判断“name”键是否存在 return bool
    _, ok := map1["name"]
    fmt.Println(ok)                    // true
 内建函数`make(T, args)`与`new(T)`有着不同的功能,make只能创建slice、map和channel,并且返回一个有初始值(非零)的`T`类型,而不是`*T`。
 new返回指针,make返回初始化后的(非零)值。

ch3. 语言基础

func main()  {
    x, y := sumTest(4, 8)
    fmt.Println(x, y)
}

func sumTest(a int, b int) (c, d int)  {
    return a + b, a * b
}

/*  可以在函数体中命名返回值,这样返回的时候不用带上变量名,可以直接return
func sumTest(a, b int) (add, multiply int)  {
    add = a + b
    multiply = a * b
    return
}
*/
func main()  {
    myFunc(2, 4)
}

func myFunc(arg ...int)  {
    fmt.Println("arg:", arg)    // arg: [2 4]  这里arg会自动转换为一个int类型的slice
}
 func main() {
     x := 3
     fmt.Println(x)       // 3

     x1 := add1(&x)       // 传递引用(内存地址)
     fmt.Println(x1, x)   // 4 4
 }

 func add1(a *int) int { // 指针,指向内存地址
     *a = *a + 1
     return *a
 }
 func deferTest() {
     defer fmt.Println("defer1")    // 最后逆序输出defer语句
     defer fmt.Println("defer2")    // 最后逆序输出defer语句

     fmt.Println("1")
     for i := 0; i < 3; i++ {
         fmt.Println(i)
     }

     fmt.Println("end")
 }

 // 输出
      1
      0
      1
      2
      end
      defer2
      defer1
  // 定义一个函数类型
 type someFunc func(int) bool

 // 满足上面条件的具体函数,被当值传递的函数
 func isBigThan10(int2 int) bool {
     if int2 > 10 {
         return true
     }
     return false
 }

 // 函数主体,将定义的函数类型传递进来.
 func numTest(slice1 []int, func1 someFunc)  {
     for _, value := range slice1 {
         if func1(value) {
             fmt.Println(value)
         }
     }
 }

 // 实现
 func main()  {
     var slice = []int{3, 54, 2, 55, 23, 5}
     numTest(slice, isBigThan10)
 }
func main()  {
    defer errProcess()
    f()

}

func errProcess() {
    fmt.Println("errProcess start")
    if err := recover(); err != nil {
        fmt.Println(err)
    }
    fmt.Println("errProcess end")
}

// 当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执行,然后F返回到调用它的地方
func f()  {
    defer fmt.Println("e")  // 会输出
    fmt.Println("a")         // 会输出
    defer fmt.Println("h")  // 会输出
    panic("some thing err")  // 会输出

    fmt.Println("b")         // 不会输出
    fmt.Println("c")         // 不会输出
}

/* 输出
a
h
e
errProcess start
some thing err
errProcess end
*/

Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数。

Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。

即 : main包先执行 import -> 被import包的import -> 被import包的 init()函数 -> main包的init()函数 -> main包的main()函数。如果中间被import包又import了别的包,同样适用此流程,直到最底层包的init()函数执行完毕,才返回上一层。

 package main

 import "fmt"

 func init()  {
     fmt.Println("init11")
 }

 func init()  {
     fmt.Println("init22")
 }

 func main()  {
     fmt.Println("main00")
 }

 /* 输出
 init11
 init22
 main00
 */
赞赏支持
Responses