安装:
单独运行:
go run main.go
编译:
go build main.go
./main
区分大小写
引入未使用的变量和包,会编译不过
gofmt -w main.go
官方document:https://golang.org/doc/
官方手册:Go 语言之旅
web端的运行版:https://play.golang.org/
go语言中文网:首页 - Go语言中文网 - Golang中文社区
中文库:Go语言标准库文档中文版 | Go语言中文网 | Golang中文社区 | Golang中国
使用:包名.函数名
变量:
//第一种
//变量 变量名 变量类型
var i int
//第二种,类型推导
var i = 10.1
//第三种,用:省略var
i := 102.3
//一次赋值多个变量
//第一种
var n1,n2,n3 int
//第二种
//按顺序对应
var n1,n2,n3 = 1,2,3
//第三种
n1,n2,n3 := 1,2,3
//第四种
var(
n1 = 1
n2 = 2
n3 = 3
)
在函数外声明的就是全局变量
数据类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名
// 表示一个 Unicode 码点
float32 float64
complex64 complex128
查看变量字节大小和数据类型
fmt.Printf("n1的字节长度 %d",unsafe.Sizeof(n1))
fmt.Printf("n1的类型 %T",n1)
浮点数可能有精度损失
默认是float64
golang没有专门的字符类型,没有char,一般用byte,
格式化输出:
var c1 byte = 'a'
fmt.Printf("n1的类型 %c",c1)
大于byte的话,那就要用int或者更大来存
传统字符串由字符组成,golang的字符串是由字节组成
go语言使用的字符使用utf-8:比如"国"在go中存进去的是22269
utf-8转换:ip地址查询 ip查询 查ip 公网ip地址归属地查询 网站ip查询 同ip网站查询 iP反查域名 iP查域名 同ip域名
bool只能取true或者false
string字符串不可更改具体字符,反引号``原样输出
类型转换需要显示转换,原来的类型不会变化,只是将转换后的类型赋值给左边
//整形转string
//str = fmt.Sprintf("%参数",表达式)
var str string
str = fmt.Sprintf("%d",i)
fmt.Println("str: ",str)
//整形转string
str = strconv.Itoa(123)
fmt.Println("str = ",str)
//整形转string
//使用strconv函数,需要引入strconv包
//第一个参数要求int64,第二个参数是转换成几进制
//输出结果str: 1100100
str = strconv.FormatInt(int64(i),2)
fmt.Println("str: ", str)
//浮点转字符串
//第一个参数要求int64
//第二个参数是格式,详细看函数手册。
//第三个参数是控制精度
//第四个参数是表示float64
str = strconv.FormatFloat(float64(12.3),'f',10,64)
fmt.Println("str: ", str)
//布尔类型转字符串
str = strconv.FormatBool(true)
fmt.Println("str: ", str)
string转基本数据类型
//string转布尔类型
var b bool
str = "true"
//ParseBool返回两个值,但是用_忽略掉第二个返回值
b,_ = strconv.ParseBool(str)
fmt.Printf("b类型:%T ,值:%v \n", b,b)
//string转整形
//结果用n1来接,结果是int64
var n1 int64
str = "1239400"
//第一参数是字符串
//第二个是转换的进制
//第三个是转换的数值能不能比int64大,大的话溢出,报错
n1,_ = strconv.ParseInt(str,10,64)
fmt.Printf("n1类型:%T ,值:%v ", n1,n1)
//string转浮点
var f1 float64
str = "1233.99209"
//第一个参数是字符串,第二个参数是期望float范围,超过则返回错误
f1,_ = strconv.ParseFloat(str,64)
fmt.Printf("f1类型:%T ,值:%v \n", f1,f1)
//返回错误类型
var f1 float64
str = "12312313213231231231231231231231231312313131313123122222222222222222222222222222222222222222222222222222222222222222222223.99209"
var e1 error
//第一个参数是字符串,第二个参数是期望float范围,超过则返回错误
f1,e1 = strconv.ParseFloat(str,32)
fmt.Printf("f1类型:%T ,值:%v \n", f1,f1)
fmt.Printf("e1类型:%T ,值:%v \n", e1,e1)
string转基本数据类型转换时,确保string类型能有效转换。转换不成功的话,会返回0或者false
指针
var i int =10
获取地址&i
指针变量存的是地址 var ptr *int = &i
解指针,取内容 *ptr 相当于i
指针格式:*数据类型
值类型,基本数据类型,int,float,bool,string,数组,结构体struct。变量直接存储值,在栈上分配
引用类型,指针,slice切片,map,管道chan,interface,变量存地址,在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间成为一个垃圾,由GC回收
标识符命名规则
25个系统保留关键字
36个预定义标志符:基本数据类型和系统内嵌函数
包名,保持package与目录文件夹名字保持一致
变量名、函数名、常量名,采用驼峰法,stuName,goodPrice,xxxYyyyZzzz
变量名、函数名、常量名首字母大写,可以被其他包调用,如果首字母小写,则只能在本包调用。首字母大写就是公有的,首字母小写就是私有的
运算符
算术运算符
如果运算的数是整数,那么结果也是整数
%的使用,a%b = a-a/b*b
++和--只能独立使用
a = i++,错误
关系运算符
逻辑运算符
&&与 ||或 !非
位运算符
不支持三元运算符
输入Scanln
var s1 int
fmt.Scanln(&s1)
fmt.Println("s1", s1)
控制格式
var s2 string
var s3 int
fmt.Scanf("%s %d",&s2,&s3)
fmt.Printf("%v 的数字是 %d\n", s2,s3)
进制及其转换
位运算
原码、补码、反码
正数的原码、补码、反码都一样
负数的反码=原码的符号位不变,其他位取反
负数的补码=反码+1
0的原码、补码、反码都一样
计算机运算时都是以补码方式运行
流程控制
顺序控制
分支控制
格式:
单分支语句
if 条件表达式 {
执行代码块1
}else{
执行代码块2
}
多分支语句
if 条件表达式 {
执行代码块1
}else if{
执行代码块2
}else{
执行代码块3
}
switch语句,不需要加break
switch key {
case "a":
fmt.Printf("aaaaa")
case "b":
fmt.Printf("bbbbb")
default:
fmt.Printf("default")
}
switch穿透fallthrought,默认只穿透一层
支持type switch
循环控制
格式:
for
如果有中文字符,按照这种遍历方法会出错,乱码
因为这种遍历是一个一个字节遍历的,但是中文汉字utf8占3个字节
用切片解决
for i:=1;i<=10;i++{
fmt.Println("circulation")
}
str = "as阿斯顿"
str2 := []rune(str)
for i:=0;i
for-range
按照字符遍历
str = "asdfghjkl"
for i,val :=range str{
fmt.Printf("str[%d] = %c\n",i,val)
}
go没有while和dowhile,要靠for实现
生成随机数
包“time" "math/rand"
种子:rand.Seed(time.Now().Unix())
产生随机数:rand.Intn(100)+1,产生1-100的数
break语句
可以用break跳出指定标签
continue
goto
函数
语法
举例:
func add(n1 float64,n2 float64)float64{
return n1+n2
}
包的本质就是创建不同的文件夹,来存放程序文件
函数名要首字母大写才能被跨包使用,专业名叫该包名可导出
首先是添加项目路径,在vim ~/.zprofile(mac用zsh)中添加export GOPATH=~/Desktop/gostudy
那么包会在gostudy下面的src文件夹中寻找。因此我们引用时候只需引用go_code/project01/cal,不需要写src/go_code/proj.......前面的src
注意细节
根据包名去区分函数,不是按照go文件区分
比如引用一个函数,先要import包,再包名.函数名
取别名
在引用包名时写成,ccccc "go_code/project01/cal",那就直接用ccccc就行了,当然cal包也失效了
生成可执行文件,做成main包
go build -o mai go_code/project01/main
调用
函数入参,默认以值传递,也可以引用传递
func cite(n *int) {
*n++
}
func main() {
num := 10
fmt.Printf("%d\n", num)
cite(&num)
fmt.Printf("%d\n", num)
}
Go不支持传统的函数重载,报重复定义,用另外形式实现函数重载
函数可以是一种数据类型,函数也可以作为参数入参,并且调用
用type取别名,但是转换时候还是要显示转换
函数别名
使用 _ 标识符,忽略返回值
函数支持可变参数
args... 要放在形参列表的最后
len(args) 获取参数长度
func sum(n1 float64, args... float64)float64{
sum := n1
for i:=0;i
init函数
在执行main函数前执行,完成初始化工作
变量定义->init函数->main函数
引入包时,即执行包的init函数
顺序如下:
匿名函数
没有名字的函数
求匿名函数结果
res2 := func (n1 int,n2 int)int{
return n1+n2
}(10,20)
fmt.Printf("%d\n", res2)
重复利用匿名函数,将匿名函数赋给变量
f123 := func (n1 int,n2 int)int{
return n1+n2
}
fmt.Printf("%d\n", f123(20,20))
全局匿名函数,将匿名函数赋给全局变量
闭包
闭包是一个函数与其相关的引用环境组合的一个整体
AddUpper()是一个函数,返回类型是func (int) int
func AddUpper() func (int) int {
var n = 10
return func (x int) int{
n = n + x
return n
}
}
func main(){
f := AddUpper()
fmt.Println(f(1))
fmt.Println(f(2))
fmt.Println(f(3))
}
匿名函数引用到函数外的变量,因此这个匿名函数和n就形成了整体,构成闭包
闭包相当于是一个类,而函数是操作,变量是字段
因为变量n只初始化一次,当我们反复调用f函数时,因此每调用一次就进行累加。
闭包的关键,分析出返回的函数和使用哪些变量,
func makeSuffix(suffix string) func (string) string{
return func (name string) string{
if !strings.HasSuffix(name,suffix) {
return name+suffix
}
return name
}
}
func main(){
f2 := makeSuffix(".jpg")
fmt.Println(f2("qewa"))
fmt.Println(f2("asd.jpg"))
fmt.Println("Hello, World!")
}
传统的话,每次都需要传入后缀名。但是用闭包,只需要传一次后缀名
函数中defer
目的:函数执行完后,可以及时释放资源
当执行到defer时,暂时不执行,会将defer后面的语句压入独立的栈(defer栈)
入栈时候,会将defer语句入栈,值也会拷贝一份压入
当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
让给函数释放时自动释放,defer ,关闭文件夹,释放链接这些场合好用
函数参数传递方式:值传递和引用传递(传递地址,效率高)
变量作用域
当全局和局部变量重复时,编译器采用就近原则
注意全局变量赋值方式,第一种才能成功,第二种不行。错误原因是Name = "tom"赋值语句不能在函数外
字符串函数
时间函数time
内置函数
错误处理机制
recover()内置函数,可以捕获到异常
defer func(){
err := recover()
if err != nil {
fmt.Println("err: ",err)
}
}()
errors.New()
panic(err)
数组
存放多个同一类型数据,数组是值传递
var hens[6] float64
hens[0] = 3.0
hens[1] = 2.1
hens[2] = 5.8
数组地址可以通过数组名来获取,&hens
数组的第一个元素的地址,就是数组的首地址,&hens 和&hens[0]一样,取地址格式用%p
数组初始化方式:
...是规定的,var arr3 = [...]int{1,2,3}
var arr4 = [...]int{1:800,0:900,2:222},根据下标指定顺序
类型推导arr5 := [...]int{1:800,0:900,2:222}
遍历方式
for index,value := range arr5 {
fmt.Println(index," : ",value)
}
数组长度固定,不能动态变化
数组默认情况下是值传递,因此会进行值拷贝
可以用引用传递,函数test(arr *[3]int),函数内(*arr)[0]使用,调用时test(&arr)
生成随机数,时间种子rand.Seed(time.Now().UnixNano()),rand.Intn(100)
切片slice
切片是数组的一个引用,因此切片是引用类型,在传递时,遵循引用传递的机制。
长度可以变化,定义:var a [] int,底层是一个结构体
slice := arr[1:3],从数组下标1开始,到下标为3但是不包含,就是只有两个元素
len(slice),获取长度。cap(slice),获取容量
使用切片的三种方式
切片遍历和数组遍历差不多
切片注意事项
slice := arr[0:len(arr)],取数组全部元素,和 slice:=arr[]是一样的
切片可以再切, slice1 := slice2[1,2]
append内置函数扩容,对切片动态追加,
slice1 := arr1[ : ]
fmt.Println(slice1)
slice1 = append(slice1,1,2,3)
fmt.Println(slice1)
slice1 = append(slice1,slice1...)
切片拷贝,copy(slice1,arr)
string和slice
string底层是一个byte数组,按字节处理,因此string也可以进行切片处理
string 也是一个slice
如果想要修改字符串,可以先将string转成[]byte或者[]rune,修改,重新转成string
str123 := "hello"
fmt.Println(str123)
arr123 := []byte(str123)
arr123[0] = 'q'
str123 = string(arr123)
fmt.Println(str123)
但是[]byte不能修改含有中文的,因此最好用[]rune
str456 := "热气球"
fmt.Println(str456)
arr456 := []rune(str456)
arr456[0] = '冷'
str456 = string(arr456)
fmt.Println(str456)
例子:
二维数组
初始化
var arr [2][3]int = [2][3]int { {1,2,3},{4,5,6}}
二维数组遍历方式
for 或者 for range
map
key-value结构,相当于集合
var a map[int]string
数组声明后会分配内存,但是map声明后不会分配内存,初始化需要make分配内存后才能赋值和使用
a = make(map[int]string,10),10的意思是可以容纳10对map的意思
golang中,map无序
map使用方式
方式二三不受空间大小限制,推荐方式二
var a map[string]string
a = make(map[string]string,10)
b := make(map[string]string)
var c map[string]string = map[string]string{
"111" : "qwe",
"222" : "erw",
}
d := map[string]string{
"111" : "qwe",
"222" : "erw",
}
两层map
map增删改查curd
增加和修改,a["111"] = "222",不存在111这个key的话那就增加,存在的话那就覆盖
删除,内置函数delete(map,"key"),存在的话那就删除,不存在就忽略。golang删除所有key,只能遍历或者再make一次
查询,两个返回值,存在的话findres1为true,不存在的话findres2为false
val1, findres1 := d["111"]
val2, findres2 := d["222"]
fmt.Println(val1,findres1)
fmt.Println(val2,findres2)
map遍历,不能使用for循环,需要使用for-range
map长度,len(map)
map切片,切片可以理解成动态数组,[]int,int的数量可以增加,[]map,map的数量可以增加
下面的代码不能动态增肌
monsters := make([]map[string]string,1,4)
if(monsters[0] == nil){
monsters[0] = make(map[string]string,2)
monsters[0]["name"] = "aaa"
monsters[0]["sex"] = "male"
}
fmt.Println(monsters)
切片使用append函数,动态增加
monsters := make([]map[string]string,1)
if(monsters[0] == nil){
monsters[0] = make(map[string]string,2)
monsters[0]["name"] = "aaa"
monsters[0]["sex"] = "male"
}
fmt.Println(monsters)
newmonster := make(map[string]string,2)
newmonster["name"] = "ccc"
newmonster["sex"] = "male"
monsters = append(monsters,newmonster)
fmt.Println(monsters)
map排序
//对map就行排序
//1、先将map的key放入到切片中
//2、对切片排序,sort
//3、遍历切片,然后按照key来输出map的值
map1 := make(map[int]int,4)
map1[3] = 10
map1[2] = 80
map1[8] = 8
map1[4] = 90
fmt.Println(map1)
var keys []int
for k,_ := range map1{
keys = append(keys, k)
}
sort.Ints(keys)
for _,k := range keys{
fmt.Printf("map1[%v] = %v\n", k,map1[k])
}
map注意事项
map是引用类型,引用传递,修改会直接修改原来的map
map会动态扩容
map的value通常使用结构体
student := make(map[string]stu)
stu1 := stu{"tom",18,"北京"}
stu2 := stu{"mary",28,"上海"}
student["no1"] = stu1
student["no2"] = stu2
fmt.Println(student)
for k,v := range student {
fmt.Printf("学生的编号:%v\t", k)
fmt.Printf("学生的姓名:%v\t", v.name)
fmt.Printf("学生的年龄:%v\t", v.age)
fmt.Printf("学生的地址:%v\t", v.address)
fmt.Printf("\n")
}
go没有类的概念,用的是结构体实现面向对象编程OOP概念,但是没有方法重载、构造析构、隐藏this指针,有封装继承多态的特性,实现方式和传统OOP语言不一样。通过接口interface关联,耦合性低,面向接口编程
结构体变量在内存的布局
声明结构体
概念:field=结构体字段=属性
type 结构体名称 struct{
field1 type
field2 type
}
举例:
type stu struct{
name string
age int
address string
}
如果结构体名为Stu,就是首字母大写,结构体可以被其他包使用。
属性可以是指针,切片,map
切片使用前要make,make([]int,10)
map使用前要make,make(map[int]int)
结构体变量实例化
//方式一
var p1 Person
p1.Name = "aaa"
p1.Age = 1
fmt.Println(p1)
//方式二
p2 := Person{}
p2.Name = "bbb"
p2.Age = 2
fmt.Println(p2)
//方式三
var p3 *Person = new (Person)
(*p3).Name = "ccc"
(*p3).Age = 3
fmt.Println(*p3)
//go里面也可以是直接赋值,go设计者为了程序员使用方便,
//底层会对指针优化,会加上取值运算
p3.Name = "ccchhh"
p3.Age = 31
fmt.Println(*p3)
//方式四
//赋值一个地址
var p4 *Person = &Person{}
p4.Name = "ddd"
p4.Age = 4
fmt.Println(*p4)
结构体struct内存分配机制
结构体中的字段是连续分布的,
转换时候需要具有相同字段的名字、类型、数量
结构体进行type重新定义,相当于取别名,golang认为是新的数据类型,但是可以相互强转
通过tag实现大小写一致,序列化和反序列化,json.Marshal(monster)使用了反射
type Monster struct{
Name string `json:"name"`
Age int `json:"age"`
Skill string `json:"skill"`
}
monster := Monster{"麦",18,"qwer"}
jsonstr, err := json.Marshal(monster)
if err != nil {
fmt.Println("json出错")
}
fmt.Println(string(jsonstr))
方法
数据类型绑定特有的方法
不能用其他类型调用
type Person struct{
Name string
Age int
}
func (p Person) test(){
fmt.Println(p.Name)
}
func main() {
p5 := Person{"aaa",18}
p5.test()
}
方法内部调用的是传参,值拷贝,方法内修改不会改变对象变量和值
方法和函数的调用机制一样,但是方法调用时,变量本身也会作为一个参数传递到方法。如果变量是值类型,则值拷贝。如果变量是引用类型,则进行地址拷贝
方法的声明与定义
声明:
func(person *Person)test()float64{
return (*person).Age
等价于return person.Age
}
调用:
(&p).test()等价于p.test()
方法和函数的区别
工厂模式
由于golang没有构造函数,往往使用工厂模式解决
需求:结构体或者变量名的首字母是小写,但是我们又希望在外部可以引用。
工厂模式实现跨包创建结构体实例。工厂模式返回的是一个zhi ehn e
定义
调用:
返回的是一个结构体指针
如果字段名首字母是小写,那就写一个结构体方法,绑定,返回首字母是小写的字段名
抽象,模版
封装,
继承。通过嵌入匿名结构体实现继承特性
继承语法
嵌套匿名结构体时,可以在创建结构体变量时,直接指定各个匿名结构体字段的值
也可以是匿名指针
接口interface
实现Usb接口,就是指实现了Usb接口声明的所有方法
定义和声明:
type Usb interface{
Start()
Stop()
}
type Phone struct{
}
func (phone Phone) Start(){
fmt.Println("手机开始工作")
}
func (phone Phone) Stop(){
fmt.Println("手机结束工作")
}
type Camera struct{
}
func (camera Camera) Start(){
fmt.Println("相机开始工作")
}
func (camera Camera) Stop(){
fmt.Println("相机结束工作")
}
type Computer struct{
}
//实现Usb接口,就是指实现了Usb接口声明的所有方法
func (computer Computer)Working(usb Usb){
usb.Start()
usb.Stop()
}
调用:
func main(){
//构建结构体变量
computer := Computer{}
phone := Phone{}
camera := Camera{}
//调用phone,实现多态
computer.Working(phone)
computer.Working(camera)
}
golang中接口不能有变量,只能有方法声明,也不需要接口本身去实现这个方法。需要交给具体的自定义类型实现方法,而且是要自定义类型去实现所有方法。
接口本身不能实例化,但是可以指向一个实现了该接口的自定义类型
自定义类型可以实现多个接口
接口之间也可以继承,比如A继承B、C,实现A的话也要实现B、C的接口
接口类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
空接口interface{}没有任何方法,所有类型都实现了空接口
绑定了指针类型的话,需要放入指针类型
实现一个学生结构体的排序
定义学生结构体,实现sort接口方法
type Student struct{
Name string
Age int
Score float64
}
type StudentSlice []Student
func (ss StudentSlice) Len() int{
return len(ss)
}
func (ss StudentSlice) Less(i, j int) bool{
return ss[i].Age < ss[j].Age
}
func (ss StudentSlice) Swap(i, j int){
ss[i] , ss[j] = ss[j] , ss[i]
}
调用
import(
"fmt"
"sort"
"math/rand"
)
func main{
var sss StudentSlice
for i:=0;i<10;i++{
ss := Student{
Name : fmt.Sprintf("学生 %d",rand.Intn(100)),
Age : rand.Intn(100),
Score : rand.Float64(),
}
sss = append(sss,ss)
}
for _,v := range sss{
fmt.Printf("学生的姓名:%v\t", v.Name)
fmt.Printf("学生的年龄:%v\t", v.Age)
fmt.Printf("学生的成绩:%v\t", v.Score)
fmt.Printf("\n")
}
sort.Sort(sss)
fmt.Println("-----------------------------")
for _,v := range sss{
fmt.Printf("学生的姓名:%v\t", v.Name)
fmt.Printf("学生的年龄:%v\t", v.Age)
fmt.Printf("学生的成绩:%v\t", v.Score)
fmt.Printf("\n")
}
}
不破坏继承的关系基础上,对结构体进行功能扩展。接口和继承是一种解藕的方式。
A结构体继承B结构体,那么A结构体就自动继承B结构体的字段和方法,并且可以直接使用
当A结构体需要扩展功能,同时不希望去破坏继承关系,则可以是实现某个接口即可。实现接口是对继承机制的补充
继承目的是解决代码复用性和可维护性
接口目的是设计,设计各种规范方法,让其它自定义类型实现
接口是对继承的一个补充。
接口更加灵活,继承是满足is-a的关系,接口是满足like-a的关系
接口体现多态poly的两种形式:多态参数和多态数组
在多态数组中,有的结构体具有另外结构体所没有的方法,即有的结构体有特有的方法,我们要在接口调用处区分类型,防止结构体之间的方法混用。引出类型断言
以下问题:将point赋值给空接口,空接口赋值给point类型,会报错。需要写成b = a.(Point)
类型断言用于接口转具体类型时使用
带检查的类型断言,需要写成b,flag = a.(Point)
也可以把if语句写在一起,
增加类型断言,可以区分结构体的接口实现
增加类型断言,判断传入参数的类型。x.(type)
文件操作
输入流、输出流
os包里面有一个结构体File,封装了所有文件相关操作,os.File
文件打开关闭
file , err := os.Open("/Users/xiquanmai/Desktop/gostudy/src/go_code/project01/main/abc.txt")
if err != nil {
fmt.Println("打开文件失败,err = ",err)
}
fmt.Printf("file =%v ",file)
defer file.Close()
读取文件,带缓冲区读取,适合大文件
reader := bufio.NewReader(file)
for{
str , err =reader.ReadString('\n')
if err ==io.EOF{
break
}
fmt.Print(str)
}
读取文件,一次读取,适合小文件。不需要显示打开和关闭文件,因为都在ReadFile里面实现了
content , err123 := ioutil.ReadFile("/Users/xiquanmai/Desktop/gostudy/src/go_code/project01/main/abc.txt")
if err123 != nil {
fmt.Println("ioutil.ReadFile读取文件失败,err = ",err123)
}
fmt.Println(string(content))
写文件
OpenFile的第二个参数是文件打开模式,第三个参数是权限控制rwx421
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky // t: sticky
ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
ModePerm FileMode = 0777 // Unix permission bits
)
示例程序:注意要写Flush()函数
filewrite := "/Users/xiquanmai/Desktop/gostudy/src/go_code/project01/main/abc.txt"
filefd, errwrite := os.OpenFile(filewrite, os.O_WRONLY | os.O_CREATE,0666)
if errwrite != nil {
fmt.Println("打开文件失败,errwrite = ",errwrite)
return
}
defer filefd.Close()
strwrite := "hellowrold\n"
writer := bufio.NewWriter(filefd)
for i:=0;i<5;i++{
writer.WriteString(strwrite)
}
//flush,缓冲区写入磁盘
writer.Flush()
一次写入
ioutil.WriteFile()
文件是否存在os.Stat()
拷贝文件
io包里面的func Copy(dst Writer, src Reader) (written int64, err error)
可以拷贝大文件,因为源代码中先读一部分再写一部分
重写复制文件函数
func CopyFile(dstFileName string, srcFileName string) (written int64, err error){
srcFile,err := os.Open(srcFileName)
if(err != nil){
fmt.Printf("open file err=%v\n", err)
}
defer srcFile.Close()
reader := bufio.NewReader(srcFile)
dstFile, err :=os.OpenFile(dstFileName,os.O_WRONLY | os.O_CREATE,0666)
if(err != nil){
fmt.Printf("open file err=%v\n", err)
return
}
defer dstFile.Close()
writer := bufio.NewWriter(dstFile)
return io.Copy(writer,reader)
}
调用
dstFileName := "./cpoy111.jpeg"
srcFileName := "./pictest.jpeg"
_,err111 := CopyFile(dstFileName,srcFileName)
if err111 == nil{
fmt.Println("复制图片成功")
}else{
fmt.Println("复制图片失败")
}
为了兼容中文字符,可以将str转成 []rune
str = []rune(str)
命令行参数基本使用,os.Args,得到的是一个切片,获得所有命令行输入参数值,但是需要按照顺序,使用下标调用即可
fmt.Println(os.Args[1])
flag包,提供命令行参数flag.StringVar()、flag.IntVar()、flag.Parse()解析,顺序可以调换
json字符串,
web中b/s,tcp中c/s 的传输
json键值对,保存数据。js中一切皆对象
json访问以下网站JSON在线解析及格式化验证 - JSON.cnJson中文网致力于在中国推广Json,并提供相关的Json解析、验证、格式化、压缩、编辑器以及Json与XML相互转换等服务。https://www.json.cn/json/jsononline.html序列化:将key-value结构的数据类型(结构体、map、切片)序列化成json字符串操作
stu123 := Student{
Name : "amai",
Age : 20,
Score : 90.0,
}
datajson, err := json.Marshal(&stu123)
if err != nil{
fmt.Printf("序列化错误 err = %v\n",err)
}
fmt.Printf("序列化结果%v\n",string(datajson))
var ajson map[string]interface{}
ajson = make(map[string]interface{})
ajson["name"] = "asd"
ajson["address"] = 123
ares, err := json.Marshal(ajson)
if err != nil{
fmt.Printf("序列化错误 err = %v\n",err)
}
fmt.Printf("序列化结果%v\n",string(ares))
//切片map
var aslice []map[string]interface{}
var m1 map[string]interface{}
m1 = make(map[string]interface{})
m1["name"] = "iuujs"
m1["address"] = 9882
aslice = append(aslice,m1)
var m2 map[string]interface{}
m2 = make(map[string]interface{})
m2["name"] = "iid"
m2["age"] = 1000
aslice = append(aslice,m2)
sliceres, err := json.Marshal(aslice)
if err != nil{
fmt.Printf("序列化错误 err = %v\n",err)
}
fmt.Printf("序列化结果%v\n",string(sliceres))
反序列化:将json字符串反序列化成key-value结构的数据类型(结构体、map、切片)操作
确保反序列化后的数据类型和原来的序列化前的数据类型一致
如果json字符串是通过程序获取的,则不需要对"转义处理。
json.Unmarshal([]byte(str666), &Stu555)
//反序列化
str666 := "{\"Name\":\"amai\",\"Age\":20,\"Score\":90}"
var Stu555 Student
err1234 := json.Unmarshal([]byte(str666), &Stu555)
if err1234 != nil {
fmt.Printf("反序列化错误 err1234 = %v\n",err1234)
}
fmt.Printf("Stu555: %+v\n", Stu555)
单元测试
测试框架testing,test命令实现单元测试和性能测试
go test -v启动test框架
cal_test.go文件,注意cal下划线后面一定要带test,函数TestAddUpper1(t *testing.T)的Test后要不能接a-z字母
package main
import (
_ "fmt"
"testing"
)
//测试用例
func TestAddUpper1(t *testing.T) {
res := AddUpper1(10)
if res != 55 {
t.Fatalf("AddUpper1(10)错误,期望值:%v,实际值:%v\n",55,res)
}
// tests := []struct {
// name string
// }{
// // TODO: Add test cases.
//
// }
// for _, tt := range tests {
// t.Run(tt.name, func(t *testing.T) {
// AddUpper1()
// })
// }
}
cal.go文件,插入需要测试的函数即可
package main
//被测函数
func AddUpper1(n int)int{
res := 0
for i := 0;i <= n;i++{
res += i
}
return res
}
goroutine协程和channel管道
进程和线程关系
并行(空间上,多线程在多核上)并发 (时间上,多线程在单核上)
协程(逻辑态)和主线程(物理级)
go协程特点:
如果主线程退出了,则即使协程没有执行完毕,协程也会退出
MPG模式,M是操作系统的主线程,P是协程执行需要的上下文,G协程
go设置运行cpu数量,go1.8之前需要设置,1.8之后不需要
runtime包
cpunum := runtime.NumCPU()
fmt.Println("cpunum: ",cpunum)
//设置cpu数目
//runtime.GOMAXPROCS(cpunum)
资源竞争问题
goroutine协程使用时会出现并发/并行问题,因此协程间需要通讯。
查看程序是否有竞争关系,编译时增加一个参数-race,
比如 go build -race main.go
解决方法:
lock.Lock(),lock.Unlock()
管道channel
声明:var 变量名 chan 数据类型
是引用类型,因此需要make
定义管道var intchan chan int
管道初始化intchan = make(chan int,3)
往管道注入数据intchan <- 20
长度len(intchan),容量cap(intchan)
从管道读取数据 num2 := <-intchan
任意数据类型
注意空接口需要类型断言
close(channel)
遍历,使用for-range方式遍历,遍历前需要关闭管道
//遍历管道
close(intchan)
for v := range intchan{
fmt.Println("v=: ",v)
}
读管道数据v,ok<-chan,如果读不到数据,ok为false
读写操作
func main(){
var intchan1 chan int
var exitchan chan bool
intchan1 = make(chan int, 50)
exitchan = make(chan bool,1)
go WriteData(intchan1)
go ReadData(intchan1,exitchan)
for{
_, ok := <-exitchan
if !ok {
break
}
}
}
func WriteData(intchan chan int){
for i := 1; i < 10; i++ {
intchan <- i
fmt.Println("WriteData写入数据=: ",i)
time.Sleep(time.Second/10.0)
}
close(intchan)
}
func ReadData(intchan chan int, exitchan chan bool){
for{
v, ok := <-intchan
if !ok {
break
}
fmt.Println("ReadData读到数据=: ",v)
}
exitchan <- true
close(exitchan)
}
管道阻塞机制
系统会分析有没有读进程,没有的话就会阻塞,报错。读写的速度快慢没所谓。
当前时间time.Now().Unix()
管道可以声明为只读或者只写。有效防止误操作
默认管道是双向的,var chan1 chan int
声明为只写,var chan1 chan<- int
声明为只读,var chan1 <-chan int
传统的方法在遍历管道时,如果不关闭管道,会阻塞而导致deadlock
但是不好确定什么时候关闭管道。使用select可以解决管道阻塞问题
反射
应用:
反射可以在运行时动态获取变量的各种信息,比如变量的类型type,类别kind
通过反射,可以修改变 量的值,引用传递,调用关联的方法
引入reflect包 ,
reflect.TypeOf(变量名),获取变量类型,返回reflect.Type类型,
reflect.ValueOf(变量名),获取变量值,返回reflect.Value类型,reflect.Value是一个结构体类型
变量、空接口类型interface{}和reflec.Value三个相互转换
var Student Stu
直接把Student赋值给函数func
将空接口类型interface{}转成reflec.Value,rVal := reflec.ValueOf(b)
将reflec.Value转成空接口类型interface{},iVal := rVal.Interface()
将空接口类型interface{}转成原来的变量类型,使用类型断言,v := iVal.(Stu)
反射的本质是运行时确定
func main(){
var num998 int = 100
testreflect(num998)
}
func testreflect(b interface{}){
rType := reflect.TypeOf(b)
fmt.Println("rType: ",rType)
rVal := reflect.ValueOf(b)
n1 := 20 + rVal.Int()
fmt.Println("n1: ",n1)
iV := rVal.Interface()
num3 := iV.(int)
fmt.Println("num3: ",num3)
}
reflect.kind本质是一个常量
常量在定义时必须初始化,const tax int = 100,常量只修饰bool、int、string这几个基本数据类型
Type和Kind有可能相同也有可能不同
如果传入的是指针类型,需要增加rVal.Elem().SetInt(20),可以实现函数改变即改变外面的值
反射最佳实践
使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
遍历结构体的字段
Type中Field(i int) StructField,返回的是StructField
type StructField struct {
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
调用结构体方法
Method按照函数名的acsii大小排序,call(切片) ,返回的结果是切片
如果传的是地址,那就只需要增加.Elem()获取指针即可
调用时传递进去的是地址:testreflect(& stu)
下面是修改字段的代码
适配器函数,有点像重载函数
网络编程
tcp socket编程,底层基于tcp/ip
b/s结构的http编程,基于http协议,底层基于 tcp socket实现,属于go web开发
服务端server
客户端client
net包,用于网络连接的包
233333333333定位结尾