# 什么是 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 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
- 首先要 new 一个 big.Int
- 再通过 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 里面取余 :
Golang\C++\PHP 里面取余:
# 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 变量:= 初值 ; 停止判断 ;执行一次 变量的变换 {
执行语句
# 实现
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) | |
} |
# 数组和切片
# 数组
# 特点:
- 内存连续的数据集合 var arr [5] int --- arr [0] ... arr [4]
- 数组取值下标从 0 开始 var arr [5] int --- arr [0] ... arr [4]
- 最为函数参数(1 值传递 2 引用传递)
- 长度固定,顺序固定
- 使用 index 访问数字中的值
- 长度可以用 len 确定
- 声明数组的时候,未被赋值元素的值对应类型的零值
# 使用复合字面值初始化数组
复合字面值:(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] | |
*/ |
# 切片
指向数组的窗口
# 特点:
- 切片低层是数据
- 动态增加(数组是固定大小)
- 函数参数属于引用传递
- 索引不能是负数
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 关键字
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 同样不能混用
# 通过方法添加行为
- 在 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 是什么
错误,严重错误 导致程序崩溃
- map 忘记初始化
- 并发安全 map
- 网络数据库连接
- 常用数组越界的
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 数据延迟处理
- close free
- 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 个内核空间线程上运用,优势利用多核,上下文切换非常快,调度复杂性慢

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 端口