# 什么是 GO 语言

  • Go 语言是一门编译语言
    • 编译器需要先编译一遍代码
    • 编译成一个可执行文件,编译过程当中,编译器捕获一些错误
  • 但不是所有的编程语言都使用这种方式
    • Python Ruby 等都是使用解释器,随着程序运行去 进行翻译,在调试过程中进行捕获错误

# 格式化打印

可以使用 Printf 控制打印的输出结果

与 Print 和 Println 不同,Printf 的第一个参数必须是字符串

  • 这个字符串里面包含了像 % v 这样的格式化动词,他的值由第二个参数的值代替
  • 如果制定多个格式化动词,他们的值由后边的参数值按其顺序进行替换
package main
import (
    "fmt"
)
func main(){
    fmt.Printf("My Weight on the surface of Mars is %v lbs" , 149*0.5)
    fmt.Printf(" and I would be %v years old.\n", 41* 365/687)
    fmt.Printf("My weight on the surface of %v is %v lbs.\n","Earth",149.0)
}
/*
My Weight on the surface of Mars is 74.5 lbs and I would be 21 years old.
My weight on the surface of Earth is 149 lbs.
*/

# 使用 Printf 对齐文本

  • 在格式化动词里指定宽度,就可以对其文本
    • 例如 %4v 就是向左填充到足够 4 个宽度
  • 正数,向左填充空格
  • 负数,向右填充空格
package main
import (
    "fmt"
)
func main(){
    fmt.Printf("%-15v $%4v\n","SpaceX",94)
    fmt.Printf("%-15v $%4v\n","Virgin Galactic",100)
}
/*
SpaceX          $  94
Virgin Galactic $ 100
*/

# 常量和变量

  • const 用来声明 常量
    • 常量的值不可以改变
  • var 用来声明 变量
    • 想要使用变量首先需要 声明
package main
import (
    "fmt"
)
func main(){
    const lightSpeed = 2999792
    var distance = 560000000
    fmt.Println(distance / lightSpeed, "seconds")
    distance = 400000000000000
    fmt.Println(distance / lightSpeed, "seconds")
}
/*
186 seconds
133342578 seconds
*/
  • 同时申明多个变量

  • package main
    import (
        "fmt"
    )
    func main(){
        var(
            hs = 999
            zcr = 1111
        )
        fmt.Println(hs,zcr)
        var av,lzq = 777,888
        fmt.Println(av,lzq)
        const zcr1,hs1 = 999999,1111111
        fmt.Println(zcr1,hs1)
    }
    /*
    999 1111
    777 888
    999999 1111111
    */

    # 自增运算符

    age , age 就是 age += 1

# 变量的作用域

在 GO 里面,作用域的范围是 {} 之间的部分

# 短声明

var count = 10 和 count:=10

短声明不可以用来声明 package 作用域的变量

# 作业

package main
import (
    "fmt"
    "math/rand"
)
func main(){
    for i:= 0 ; i < 10 ; i++{
        year := rand.Intn(2021) + 1
        month := rand.Intn(12) + 1
        daysInMonth := 31
        switch month{
            case 2:
                if (year%4 ==0 && year %100 != 0 )||(year %400 ==0){
                    daysInMonth = 29
                }else{
                    daysInMonth = 28
                }
            case 4 ,6, 9 ,11:
                daysInMonth = 30
        }
        day := rand.Intn(daysInMonth) + 1
        fmt.Printf("AD %4v:%2v:%2v  bbyyds\n", year , month , day)
    }
}

# 声明浮点型变量

days := 365.2425
var days = 365.2425
var days float64 = 365.2425

只要数字含有小数部分,她就是 float64

# 单精度浮点数类型:

GO 里面由有 2 种浮点数类型:

  • # 默认是 float 64

    • 64 位的浮点类型
    • 占用 8 字节内存
    • 某些编程语言把这种类型叫做 double (双精度)
  • # float 32

    • 占用 4 字节内存
    • 精度比 float 64 低
    • 叫做单精度类型

float 32 主要是为了牺牲精度来 节省内存 math 包里面函数操作都是 float64 类型

Printf 显示 小数位数 的需要结合 % f 来显示小数位数

%f
%.4.2f
%.3f

# 整数类型

Go 语言 10 种 整数类型:

  • 不可以存小数部分
  • 范围有限
  • 通常根据数值范围选取整数类型

5 种整数类型有符号

5 种整数类型无符号

int8,    -128-127 8-bit   
uint8,   0-255     
int16,   -32768-32767  16 -bit    
uint16,  0-65535
int32,  -2147483648-2147483647   32-bit
uint32, 0-4294967295
int64, -9223372036854775808 - 9223372036854775807  64-bit
uint64. 0- 18446744073709551615
int, int64
uint , uint64

Printf 里 % T 就是输出类型 % x 输出十六进制, %02x 表示 用 0 填充 最小宽度 为 2 %08b 二进制

# 作业题

package main
import (
    "fmt"
    "math/rand"
)
var Money [3]float32
func main(){
    Money[0] = 0.05
    Money[1] = 0.10
    Money[2] = 0.25
    var raised float32 = 0.00
    for{
        raised = raised + Money[rand.Intn(3)]
        fmt.Printf("Raised:$%.2f\n",raised)
        if raised >= 20{
            break
        }
    }
}

# 数太大了怎么办

可以使用 big 包

对于较大的整数(超过 10**18) :big.Int

对于任意精度的浮点类型:big.Float

对于分数 big.Rat

package main
import (
    "fmt"
    "math/big"
)
func main(){
    lightSpeed := big.NewInt(299792)
    secondsPerDay := big.NewInt(86400)
    distance := new(big.Int)
    distance.SetString("2222222222222222222222222222222222",10)
    fmt.Println("Deebato Galaxy is",distance,"km away.")
    seconds := new(big.Int)
    seconds.Div(distance,lightSpeed) // distance /lightSpeed
    days := new(big.Int)
    days.Div(seconds,secondsPerDay)
    d1 := new(big.Int)
    d1.Mul(days,distance)
    fmt.Println("That is",days,"days of travel at light speed")
    fmt.Println("d1 is:",d1)
}

一旦使用了 big.Int 等式里其他部分也要是 big.Int

  1. 首先要 new 一个 big.Int
  2. 再通过 SetString 函数 把数值 的字符串形式 和 几进制传递进行即可

缺点 速度慢。

# 较大数的常量

可以做字面值直接使用,不去定义

# 多语言文本

声明字符串:

peace := "peace"
var peace = "peace"
var peace string = "peace"
var blank string

字符串字面值 / 原始字符串字面值

  • 字符串的字面值可以包含转义字符 例如 \n

  • 如果想得到 \n 而不是换行可以使用 ` 去代替 “ ,原始字符串字面值

    fmt.Println("peace be upon you \nupon you be peace")
    fmt.Println(`strings can \n`)

任何整数类型都可以使用 % c 打印,但是 rune 意味着改数值表示了任意一个字符

# 字符

字符字面值使用 ' ' 括起来,例如 'A'

如果么指定字符类型 Go 推断他是 rune

package main
import (
    "fmt"
)
func main(){
    var grade rune = 'A'
    fmt.Println(grade)
}
/*
65
*/

byte 也可以 但是 他是 0-255

# string

可一个给某个变量赋予不同的 string 值,但是 string 本身不可变

# 类型转换

类型转换需要谨慎 整数溢出

把 rune,byte 转换位 string

package main
import (
    "fmt"
)
func main(){
    var pi rune = 960
    var aloha rune = 940
    var omega rune = 969
    var bang byte = 33
    fmt.Print(string(pi),string(aloha),string(omega),string(bang))
}

想把数值转换为 string, 它的值必须能转化为 code point

但是如果我想就是数值直接转换 就需要 用 strconv 包的 Itoa(Integer to ASCII) Atoi

package main
import (
    "fmt"
    "strconv"
)
func main(){
    var seconds int = 9999999
    str := "GreatLLLL " + strconv.Itoa(seconds) + " seconds"
    fmt.Println(str)
}

Sprintf 函数 功能与 Printf 相同 但是他会返回一个 string 但没有去 Printf

package main
import (
    "fmt"
)
func main(){
    countdown := 9
    strr := fmt.Sprintf("Launch in %v ",countdown)
    fmt.Println(strr)
}
/*
Launch in 9 
*/

# Bollean

Go 语言不能转换布尔类型

某些语言里面,1 和 0 当作 true 和 false Go 里面不可以

# 取余

发现 Golang 语言里面的取余是 与 python 不一样

举个例子:

  • Python\Ruby\Lua 里面取余 :

    17mod5=(4)5+(+3)mod5=3-17\mod5 = (-4) * 5 + (+3)\mod5 = 3

  • Golang\C++\PHP 里面取余:

    17mod5=(3)5+(2)mod5=2-17\mod 5 = (-3)*5 +(-2)\mod5 = -2

# Vigenere 加密的实现

package main
import (
    "fmt"
    "strings"
)
const UPPERTABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const LOWERTABLE = "abcdefghijklmnopqrstuvwxyz"
func Encrypt(plain string,key string)(string){
    var cipher string = ""
    var times = 0
    for i:=0 ; i < len(plain) ; i++ {
        p := plain[i]
        if strings.Contains(UPPERTABLE,string(p)) == true{
            p = p - 'A'
            k := key[times%len(key)] - 'A'
            cipher = cipher + string((p + k)%26 + 'A')
            times +=1
        }else if strings.Contains(LOWERTABLE,string(p)) == true{
            p = p - 'a'
            k := key[times%len(key)] - 'A'
            cipher = cipher + string((p + k)%26 + 'a')
            times += 1
        }else{
            cipher = cipher + string(p)
        }
    }
    return cipher
}
func Decrypt(cipher string,key string)(string){
    var plain string = ""
    var times = 0
    for i:=0 ; i < len(cipher) ; i++ {
        c := cipher[i]
        if strings.Contains(UPPERTABLE,string(c)) == true{
            c = c - 'A'
            k := key[times%len(key)] - 'A'
            plain = plain + string((26 + (int(c) - int(k)) % 26 )% 26 + 'A') // 取模得到的数不同主要是因为.. rune 和 int 取值上的问题   而 Go 语言中的模数是 带负号的  是少加了一个 模
            times += 1
        }else if strings.Contains(LOWERTABLE,string(c)) == true{
            c = c - 'a'
            k := key[times%len(key)] - 'A'
            plain = plain + string((26 + (int(c) - int(k)) % 26 )% 26 + 'a')
            times += 1
        }else{
            plain = plain + string(c)
        }
    }
    return plain
}
func main(){
    var flag = "Wow! You are so smart!Vigenere is just So So~.FLAG is moectf{V1g3nere_Just_S0_s0,G014ng_Sh1t!}"
    var key   = "GOLANG"
    var cipher string
    var Plains string
    cipher = Encrypt(flag,key)
    fmt.Println("Encrypted Cipher is:",cipher)
    Plains = Decrypt(cipher,key)
    fmt.Println("Decrypted Plains is:",Plains )
}

# Rand 库

package main
import (
    "fmt"
    "math/rand"
)
func main(){
    var num = rand.Intn(10) + 1
    fmt.Println(num)
    num = rand.Intn(10) + 1
    fmt.Println(num)
}

Intn 是 0~ 括号里的数 发现每次 rand 的都是相同的... 是不是因为每次种子是不变的

# 循环和分支

# Boolean 类型

某些语言会把 “” 当作 false 其他字符串当作 true

Go 语言不行,只有 true 是真的,只有 false 是假的

package main
import (
    "fmt"
    "strings"
)
func main(){
    fmt.Println("You find yourself in a dimly lit cavern.")
    var command = "walk outside"
    var exit = strings.Contains(command,"outside")
    fmt.Println("You leave the cave:", exit)
}
/*
You find yourself in a dimly lit cavern.
You leave the cave: true
*/

# if else 条件语句

package main
import (
    "fmt"
)
// 1 if else 条件语句
var iint int = 10001
func main(){
    fmt.Println("hello world")
    if iint == 100 {
        fmt.Println("iint == 100")
    }else if iint == 1001{
        fmt.Println("iint == 1001")
    }else{
        fmt.Println("iint == no")
    }
}

其中 花括号 需要和 if 或者 else 同行

# for 循环语句

package main
import (
    "fmt"
)
func main(){
    // 循环语句
    for i := 0; i < 10; i++  {
        fmt.Println("i = ", i )
    }
}
//i =  0
//i =  1
//i =  2
//i =  3
//i =  4
//i =  5
//i =  6
//i =  7
//i =  8
//i =  9
  • # for 关键字后面没跟条件,无限循环

    • break 跳出循环
  • # for 跟条件

for 变量:= 初值 ; 停止判断 ;执行一次 变量的变换 {

​ 执行语句

# 实现

package main
import (
    "fmt"
    "math/rand"
)
func main(){
    var left = 1
    var right = 123456
    var guess = 0
    var num = rand.Intn(right)+left
    guess = right + left
    var times = 0
    for {
        if guess == num{
            fmt.Println("Your guess is Right!", guess)
            break
        }else if guess > num{
            right = guess
            guess = rand.Intn(right-left) + left + 1
        }else{
            left = guess
            guess = rand.Intn(right-left) + left + 1
        }
        times++
    }
    fmt.Println(times)
}

# switch case 选择语句

package main
import (
    "fmt"
)
var IIF int = 10000
func main(){
    fmt.Println("hello world")
    //switch case
    switch IIF{
        case 10000:
            fmt.Println("IIF == 10000")
        case 100:
            fmt.Println("IIF == 100")
        default:
            fmt.Println("IIF == no")
    }
}

go 语言中 switch case 不需要写一个 break 他会自动去 break

# 测试题:

package main
import (
    "fmt"
    "math/rand"
)
var SPACELINE [3]string
var TRIPTYPE [2]string
func main(){
    SPACELINE[0] = "Virgin Galactic"
    SPACELINE[1] = "SpaceX"
    SPACELINE[2] = "Space Adventures"
    TRIPTYPE[0] = "Round-trip"
    TRIPTYPE[1] = "One-way"
    const distance = 62100000
    var speed int
    var days  int
    var spaceline string
    var triptype string
    var price int
    fmt.Printf("%-18v %5v  %-12v  %-5v\n","Spaceline","Days","Trip type","Price")
    fmt.Println("=============================================")
    for i:=0 ; i<10 ; i++{
        spaceline = SPACELINE[rand.Intn(3)]
        speed = rand.Intn(15)+16
        days = distance/(speed * 60 * 60 *24)
        price = rand.Intn(15) + 36
        triptype = TRIPTYPE[rand.Intn(2)]
        switch triptype{
            case "Round-trip":
                price = price * 2
            case "One-way":
                price = price * 1
        }
        fmt.Printf("%-18v %5v  %-12v  $%4v\n",spaceline,days,triptype,price)
    }
}

# 结构体

# 结构体变量

定义结构体的时候 小写都是包下私有变量,只在私有变量中使用

package main
import (
    "fmt"
)
// 结构体定义
type name_struct struct{
    ID int// 成员变量
    id int// 小写都是包下私有变量,只在私有变量中使用
}
// 定义结构体变量
var data name_struct
func main(){
    fmt.Println("hello world")
    //1. 赋值
    data.ID = 100
    data.id = 10
    fmt.Println("data:" , data)
    //2. 直接赋值
    data2 := name_struct{
        ID : 88,
        id : 66,
    }
    fmt.Println("data2:",data2)
}
//hello world
//data: {100 10}
//data2: {88 66}

# 结构体方法

package main
import (
    "fmt"
)
type name_struct struct{
    ID int
    id int
}
var data name_struct
// 定义结构体方法
func (this name_struct) Tes() {
    fmt.Println("Tes")
    return
}
func (this *name_struct) Tesptr(){
    fmt.Println("Tesptr")
    return
}
func main(){
    fmt.Println("hello world")
    data.ID = 100
    data.id = 10
    fmt.Println("data:",data)
    data.Tes()
    data2 := &name_struct{
        ID:22,
        id:11,
    }
    fmt.Println("data2:",data2)
    data2.Tesptr()
}
//hello world
//data: {100 10}
//Tes
//data2: &{22 11}
//Tesptr

# Map 定义

就像 python 里面的字典

package main
import (
    "fmt"
)
//map 定义
var map_name map[string]int
// 中括号里面是 key  外面是 value
//map 注意点: map 使用之前 需要初始化操作
func init(){
    map_name = make(map[string]int)
}//init 在变量初始化之后执行 在 main 函数之前
func main(){
    map_name["1111"] = 100
    fmt.Println("map_name: " ,map_name)
}

map 注意点: 1. map 使用之前 需要初始化操作 (make)
2. map 存储数据无序的 -- 低层是 hash
3. map 并发不安全的

package main
import (
    "fmt"
)
//map 定义
var map_name map[string]st_test
type st_test struct{
    id int
    name string
}
//map 注意点: 1.map 使用之前 需要初始化操作
//             2. map 存储数据无序的  -- 低层是 hash
//             3. map 并发不安全的
func init(){
    map_name = make(map[string]st_test)
}//init 在变量初始化之后执行 在 main 函数之前
func main(){
    data := st_test{
        id: 100,
        name: "sss",
    }
    map_name["1111"] = data
    data1 := st_test{
        id: 100,
        name: "sss",
    }
    map_name["222"] = data1
    fmt.Println("map_name: " ,map_name)
}

# 数组和切片

# 数组

# 特点:

  1. 内存连续的数据集合 var arr [5] int --- arr [0] ... arr [4]
  2. 数组取值下标从 0 开始 var arr [5] int --- arr [0] ... arr [4]
  3. 最为函数参数(1 值传递 2 引用传递)
  4. 长度固定,顺序固定
  5. 使用 index 访问数字中的值
  6. 长度可以用 len 确定
  7. 声明数组的时候,未被赋值元素的值对应类型的零值

# 使用复合字面值初始化数组

复合字面值:(composite literal) 是一种给复合类型初始化的紧凑语法

Go 的复合字面值语法允许我们只用一步就完成数组声明和数组初始化两部操作

s := [5]string {"avalon","AV","Avalon","AVALON","AVV"}

可以在复合字面值里面使用... 作为数组长度,这样 Go 编译器回味你算出数组元素数量

s := [...]string {"avalon","AV","Avalon","AVALON","AVV"}

# 二位数组定义

package main
import "fmt"
func main(){
    var board [8][8]string
    board[0][0] = "r"
    board[0][7] = "r"
    for column := range board[1]{
        board[1][column] = "p"
    }
    fmt.Print(board)
}

# 值传递 - 浅拷贝

package main
import (
    "fmt"
)
var arr_name [2]int
func main(){
    arr_name[0] = 1
    arr_name[1] = 10
    Arr_fun(arr_name)
    fmt.Println("arr_name:" , arr_name)
}
// 值传递 -- 浅拷贝  内存复制
func Arr_fun(arr_name [2]int){
    arr_name[1] = 100
    fmt.Println("arr_name:" ,arr_name)
}
/*
arr_name: [1 100]
arr_name: [1 10]
*/

# 引用传递 - 深拷贝

package main
import (
    "fmt"
)
var arr_name [2]int
func main(){
    arr_name[0] = 1
    arr_name[1] = 10
    Arr_fun(&arr_name)
    fmt.Println("arr_name:" , arr_name)
}
// 引用传递 -- 深拷贝 内存地址的复制
func Arr_fun(arr_name *[2]int){
    arr_name[1] = 100
    fmt.Println("arr_name:" ,arr_name)
}
/*
arr_name: &[1 100]
arr_name: [1 100]
*/

# 切片

指向数组的窗口

# 特点:

  1. 切片低层是数据
  2. 动态增加(数组是固定大小)
  3. 函数参数属于引用传递
  4. 索引不能是负数
package main
import (
    "fmt"
)
var slice_name []int
func main(){
    slice_name = make([]int, 10)
    slice_name = append(slice_name,100)
    fmt.Println("arr_name:" , slice_name)
}
/*
arr_name: [0 0 0 0 0 0 0 0 0 0 100]
*/
package main
import (
    "fmt"
)
var slice_name []int
func main(){
    //slice_name = make([]int, 10)
    slice_name = []int {1,2,3,4,5,6,7,8,9,10}
    slice_name = append(slice_name,100)
    fmt.Println("arr_name:" , slice_name)
}
/*
arr_name: [1 2 3 4 5 6 7 8 9 10 100]
*/

append 使得 数组长度增加 末尾加上一个数

package main
import (
    "fmt"
)
var slice_name []int
func main(){
    //slice_name = make([]int, 10)
    slice_name = []int {1,2,3,4,5,6,7,8,9,10}
    slice_name = append(slice_name,100)
    newslice := slice_name[1]
    fmt.Println("newslice" , newslice)
    newslice1 := slice_name[1:3]
    fmt.Println("newslice1" , newslice1)
    newslice2 := slice_name[0:5:5] // 容量  slice_name [n:m:f]  长度 len = m-n    容量 cap = f-n 最大取到的个数
    fmt.Println("newslice2" , newslice2)
}
/*
    newslice 2
    newslice1 [2 3]
    newslice2 [1 2 3 4 5]
 */

# 函数

# 定义

Func

函数:func 关键字

main 函数作为我们执行的入口

程序执行顺序:1.var 2. init 3. main

init 函数是可以有多个

package main
import (
    "fmt"
)
func init(){
    fmt.Println("init1")
}
func init(){
    fmt.Println("init2")
}
func init(){
    fmt.Println("init3")
}
func main(){
    fmt.Println("main")
}
/*
init1
init2
init3
main
*/

大写字母开头的函数、变量或者其他标识符都会被导出,对其他包可用,小写字母开头不行

# 函数实现

package main
import (
    "fmt"
)
func main(){
    a := 1
    fmt.Println(tests(a))
}
func tests(data int) (bool,int,int){
    return false,0,1
}
/*
false 0 1
*/

# 一等函数

在 Go 里面函数是头等的,他可以用在整数、字符串或者其他类型能用的地方:

  • 将函数附给变量

    package main
    import (
        "fmt"
        "math/rand"
    )
    type kelvin float64
    func fakeSensor() kelvin{
        return kelvin(rand.Intn(151)+150)
    }
    func main(){
        sensor := fakeSensor
        fmt.Println(sensor())
    }

    ​ 函数遇到 () 才会执行

  • 将函数作为参数传递给函数

  • 将函数作为函数的返回类型

# 声明函数类型

为函数声明类型有助于精简和明确调用者的代码

type sensor func() kelvin
func measure(samples int,s func() kelvin)// 可以改为
func measure(samples int,s sensor)

# 方法

# 声明新类型

  • 关键字 type 可以用来声明新类型

    type celsius float64
    var temp celsius = 20
  • 运算和 type 使用的相同

  • 为什么声明新类型:提高代码的可读性

  • 不同的类型无法混用。celsius 和 float64 同样不能混用

# 通过方法添加行为

Method

  • 在 C#,Java 里面,方法属于类
  • 在 Go 里面,提供了方法,但是没提供类和对象
  • 可以将方法与同包种声明的任何类型相关联,但不可以是 int64 float64 等预声明的类型相关联
  • 每个方法可以有多个参数,但只能有一个接收者
  • 在方法体种,接收者的行为和其他参数一样
  • 调用方法 变量。方法 ()
package main
import(
    "fmt"
)
type celsius float64
type kelvin float64
func kelvinToCelsius(k kelvin)celsius{
    return celsius(k - 273.15)
}
func (k kelvin) celsius() celsius{
    return celsius(k - 273.15)
}
func main(){
    var k kelvin
    k = 555
    fmt.Println(k.celsius())
    fmt.Println(kelvinToCelsius(k))
}
/*
281.85
281.85
*/

# 匿名函数

# 定义

匿名函数就是没有名字的函数, 在 Go 里也称做函数字面值

因为函数字面值需要保留外部作用域的变量引用,所以函数字面值都是闭包的

/*j := func (i,j int)(data int){
		data = i + j
		return data
}
//j 是我们的函数地址
x,y := func (i,j int)(m,n int){
    return j,i
}(1,9)
*/
package main
import (
    "fmt"
)
func main(){
    x,y := func(i,j string)(m,n string){
        fmt.Println("i:",i , " j:" , j)
        return j,i
    }("hs","yyds")
    fmt.Println("x:",x," y:",y)
    j := func(i,j string)(data string){
            data = i + j
            return data
    }
    fmt.Println("j:",j)
    fmt.Println("j:",j("hs","yyds"))
}
/*
i: hs  j: yyds
x: yyds  y: hs
j: 0x497b80
j: hsyyds
*/

# 闭包

# 定义

闭包:由于匿名函数封闭并包围作用域中的变量而得名的

外界访问不了

package main
import "fmt"
type kelvin float64
func main(){
    var k kelvin = 294.0
    sensor := func() kelvin{
        return k
    }
    fmt.Println(sensor())
    k++
    fmt.Println(sensor())
}
/*
294
295
*/

# 接口

# 概念

为了实现 go 语言中的 多态

# 例子

1 定义接口

2 接口使用 为了实现 go 语言中的 多态

package main
import (
    "fmt"
)
type I_name interface{
    Printflm() // 接口的函数
}
type A1 struct{
}
func (this *A1)Printflm(){
    fmt.Println("A1 Printflm:")
}
type B1 struct{
}
func (this *B1)Printflm(){
    fmt.Println("B1 Printflm:")
}
// 接口的实现函数 -- 多态的根本
func Ts(data I_name){
    data.Printflm()
}
func main(){
    data1 := A1{}
    Ts(&data1)
    data2 := B1{}
    Ts(&data2)
}
/*
A1 Printflm:
B1 Printflm:
*/

# 异常处理

# 定义

异常处理 panic recover

# Panic 是什么

错误,严重错误 导致程序崩溃

  1. map 忘记初始化
  2. 并发安全 map
  3. 网络数据库连接
  4. 常用数组越界的
package main
import (
    "fmt"
)
func Panic_func(){
    panic("123456")
}
func main(){
    fmt.Println("data:")
    Panic_func()
    fmt.Println("data1:")
}
/*
data:
panic: 123456
goroutine 1 [running]:
main.Panic_func (...)
        /home/deebato/Desktop/learning/golang 学习 /tmp.go:12
main.main ()
        /home/deebato/Desktop/learning/golang 学习 /tmp.go:18 +0x96
exit status 2
*/
# defer 数据延迟处理
  1. close free
  2. conn 数据库连接 defer close ---> panic error ---> close (conn)
package main
import (
    "fmt"
)
func Panic_func(){
    panic("123456")
}
func main(){
    defer func(){
        if err:=recover();err!= nil{
            strerr := fmt.Sprintf("%s",err)
            fmt.Println("strerr:",strerr)
        }
    }()
    fmt.Println("data:")
    Panic_func()
    fmt.Println("data1:")
}
/*
data:
strerr: 123456
*/

实现异常处理捕获并且没有停止程序

# 并发编程

# 定义

并发:与并行做对比

1. 并行   同样的时间去一起执行
2. 并发   不同的时间去执行   队列的形列
3.  go 关键字 实现并发   写
4. 全局变量(+锁)
5. channel 通道 初始化操作 (并发安全 低层处理)
  	1. 无缓冲通道: 阻塞  得等到有输入 才会去执行
  	2. 有缓冲通道: 可以往里面存东西

# 例子

package main
import (
    "fmt"
    "time"
)
func main(){
    fmt.Println("data:")
    go func(){
        fmt.Println("data2:")
    }()
    go func(){
        fmt.Println("data3:")
    }()
    time.Sleep(10000)
}
/*
data:
data3:
data2:
 */

time.Sleep 是为了让他 主进程结束后 持续一段时间 , 否则 并发进程可能没执行完就已经结束 就不会出现

channel 需要去初始化操作 并且可以使用这个通道去传输

package main
import (
    "fmt"
    "time"
)
var chal chan int
func main(){
    chal = make(chan int)//channel 需要初始化
    fmt.Println("data:")
    go func(){
        chal <- 100
        fmt.Println("data2:")
    }()
    go func(){
        fmt.Println("data3:",<-chal)
    }()
    time.Sleep(100000)
}
/*
data:
data2:
data3: 100
*/

# 协程调度

# 定义

go 调度器

线程模型:

  • N: 1 模型 :

    N 个用户空间线程再一个内核空间线程上运用,优势是上下文切换非常快,但是无法利用多核

  • 1: 1 模型:

    一个用户空间线程在一个内核空间线程上运用,优势 利用多核 ,但是每次调度都会上下文切换

  • M: N 模型:

    M 个用户空间线程在 N 个内核空间线程上运用,优势利用多核,上下文切换非常快,调度复杂性慢

![Untitled Diagram](/home/deebato/Desktop/learning/golang 学习 / Untitled Diagram.png)

1 个内核线程对应 1 个用户线程

1 个用户线程 M 可以调度 n 个 调度器 P

调度器: GOMAXPROCS(10) //number <cpu 的数量

默认用户线程 M 有 1w 个 SetMaxTHreads () 由这个函数控制 M P 并不对应

# HTTP 服务器建立

package main
import (
    "fmt"
    "net/http"
)
func init(){
}
type helloHandler struct{}
func (h *helloHandler)ServeHTTP(w http.ResponseWriter,r *http.Request){
    w.Write([]byte("ssssss"))
}
func main(){
    fmt.Println("HelloWorld")
    http.Handle("/",&helloHandler{})
    http.ListenAndServe(":12346",nil)
}

# TCP 服务器建立

package main
import (
    "fmt"
    "net"
)
func HandleConn(conn net.Conn) {
    defer conn.Close()
    for {
        // read from the connection
        //...
        // write to the connection
        //...
    }
}
func main(){
    listen, err := net.Listen("tcp",":8888")
    if err!= nil{
        fmt.Println("Listen ERROR:", err)
        return
    }
    for {
        conn , err := listen.Accept()
        if err!=nil{
            fmt.Println("Accept ERROR:",err)
            break
        }
        //start a new goroutine to handle the new connection
        go HandleConn(conn)
    }
}

listen 监听 tcp8888 端口

请我喝[茶]~( ̄▽ ̄)~*

De3B4to 微信支付

微信支付

De3B4to 支付宝

支付宝