标识符&关键字
标识符
关键字
变量
var 变量名 变量类型 or var (变量名1 变量类型 \n 变量名2 变量类型)
变量声明未被使用,则会编译不过
- 变量声明
- 变量批量声明
- 局部变量
- 匿名变量
常量
const
- 批量声明
iota
常量计数器
数组
数组声明
var 数组名 [数组长度]数据类型例子:var al [3]bool //[false,false,false]var a12 [行数量][列数量] int
数组初始化
不够的数量用相应的类型数据填充
a1 = [3]bool{true, true}a1 = [...]bool{true,true,true} //自动判断数组长度a1 = [5]{0:1,4:2}//特定数组长度设置值a12 = [3]int{[2]int{1,2},[2]int{3,4},[2]int{5,6},}
数组的遍历
a1 := [3]string{"胡", "加", "加"}for i := 0; i < len(a1); i++ {fmt.Println(a1[i])}
a1 := [3]string{"胡", "加", "加"}for i, v := range a1 {fmt.Println(i, v)}for _, v := range a1 {fmt.Println(i, v)}
切片(slice)
拥有相同类型数据可变长度的数据序列 ,长度可变,引用类型,判断slice是空的,用len(slice)==0判断,不用nil;
定义
var 数组名 []数据类型例子: var al []int
初始化
a1 := []int{1,2,3}// 由数组创建切片a2 :=[5]int{1,2,3,4,5}a3 :=a1[1:4]// 由切片创建切片a4 :=a3[0:2]fmt.Println(a2, a3, a4) => 输出 [1 2 3 4 5] [2 3 4] [2 3]
切片容量&长度
make 函数创建切片
make([]数据类型,长度,容量)
a1 := make([]int,5,10)fmt.Println(len(a1), cap(a1))
append 切片追加元素
当容量 不够时,底层会进行扩容处理,扩容策略会根据当前值大小,数据类型等做出相应的扩容处理!!!!
a1 := make([]int, 2, 2)fmt.Println(len(a1), cap(a1), a1)a1 = append(a1, 3)fmt.Println(len(a1), cap(a1), a1)a1 = append(a1, 4, 5)fmt.Println(len(a1), cap(a1), a1)a2 := make([]int ,1,2)a1 = append(a1,a2...)
扩容策略源码见 /src/runtime/slice.go growslice方法。go版本 “go1.15.6 windows/amd64”
newcap := old.capdoublecap := newcap + newcapif cap > doublecap {newcap = cap} else {if old.len < 1024 {newcap = doublecap} else {// Check 0 < newcap to detect overflow// and prevent an infinite loop.for 0 < newcap && newcap < cap {newcap += newcap / 4}// Set newcap to the requested cap when// the newcap calculation overflowed.if newcap <= 0 {newcap = cap}}}
复制切片copy
copy 复制,值赋值
copy(目标切片,源切片)
指针
&=》 取地址
*=》 根据地址取值
var a1 *int // =>指针指向nil*a1 = 100 // 会报错的var a2 = new(int) 开辟一个空间
map
无序的key-value数据结构。引用类型。类似js中的object
声明 名字 map[key类型] value类型var mapname map[string] int ;初始化 使用makemapname =make(map[string]int,size) // 估算map容量,避免动态扩容// 设置值mapname["key"] = 2// 获取值value ,isbe := mapname["key2"]//map遍历for key, value := range mapname {fmt.Printf(key, v)}// map 删除
函数
函数的定义
func1 functionname(参数1 参数类型1,参数2 参数类型2)(返回值1 返回类型1){return 返回类型1的数据}func1 functionname(参数1 参数类型1,参数2 参数类型2) 返回类型1{return 返回类型1的数据}func functionname(){}
defer
延迟操作,延迟后面的语句
defer fmt.Printf("start 1 => ")defer fmt.Printf("start 2 => ")defer fmt.Printf("start 3 => ")fmt.Printf("start 4 => ")
结果 start 4 => start 3 => start 2 => start 1 =>
defer执行契机:
如果延迟执行内部有需要执行的条件,需要先满足执行条件
func() {a := 1b := 2defer callbac("1", a, callbac("10", a, b))a = 0defer callbac("2", a, callbac("20", a, b))b = 1}()run ==>10 1 2 320 0 2 22 0 2 21 1 3 4
函数作为参数
函数可以作为一种类型,可以作为参数传递
func f3(x func() int) {result := x()fmt.Printf("%T\n", result)}
函数可以作为一个返回值
func f3(x func() int) func() {result := x()return result}func f3(x func() int) func(int ,int ) int {result := x()return result}
实际上也还是函数的样子。参数的变量,变量类型。 返回值,返回类型。
匿名函数
没有名字的函数,一般写在函数内,作为代码块。或者立即执行此函数
f1 := func() {fmt.Println("这是匿名函数")}f1()func() {fmt.Println("这是匿名函数立即执行")}()
闭包
和js 类似,将变量的”生命”延长了,本来变量随着函数的生命周期的结束而结束,但是闭包将需要使用的变量生命延长至使用处。这个不需要过度的研究闭包。本身这个名字就很怪,非常怪,这概念在js中也是令人头痛。
内置函数
| close | 主要关闭channel |
|---|---|
| len | string,array,slice,map,channel 长度 |
| new | 分配值类型 内存,比如int,struct |
| make | 分配引用类型内存,chan.map,slice |
| append | 追加元素到array,slice |
| panic& recover | 错误处理 |
panic&recover
panic 在程序的任何地方都可能出现会导致程序崩溃,recover 在defer 中才能使用,用来补救函数.
在某些资源被打开之后,程序panic .这是补救就是,在defer 下释放资源。func test() {defer func() {err := recover()fmt.Println(err)println("兜底关闭了文件")}()println("打开了文件")panic("程序崩溃了")}
defer 补救在可能出现错误之前定义
结构体
类型别名
type 类型别名 = 类型 // 类型别名,在程序编译的时候是原型类型
自定义类型
type 自定义类型 类型 // 自定义类型,编译之后是一定义类型
定义声明
type 结构体名字 struct{属性名字1 属性类型1属性名字2 属性类型2}
匿名结构体
var dd struct {属性名字1 属性类型1属性名字2 属性类型2}
结构体指针
type person struct{name stringage int}per := person{name :"名字",age:1}per:= new(person)
内存分布
结构体中数据的内存是连续的,即初始化时分配了连续的内存
仿造构造函数
type person struct {name stringage int}func newperson(name string, age int) person {return person{name: name,age: age,}}// 或者func newperson(name string, age int) *person {return &person{name: name,age: age,}}
仿造“对象”的方法
限定方法的时候对象
func (per person) hold(){fmt.Printf("人类对象实例",per.name) // 此时per和外部的不是一个东西}func (per *person) hold(){per.age ++;fmt.Printf("人类对象实例",per.name) // 传外部指针}var pers = new(person)pers.hold()
此操作可以给自定义类型进行扩展
type myInt intfunc (myint myInt)hello(){fmt.Println("这是自定义类型")}func main(){test := myInt(100)test.hello()}
结构体匿名字段
字段类型当作 key
type person struct{stringint}
结构体嵌套
结构体组成了结构体
type address struct {city string}type person struct {name stringage intaddress address}//type person struct { 匿名嵌套结构体// name string// age int// address//}func main (){per :=person{name:"名字",age:1,address:{city :"城市"}}fmt.Println(per.name,per.address.city);}
仿造“对象”继承
利用嵌套结构体,将父级属性嵌入.
json于结构体的转换
结构体中属性名需要大写才可以被其他包识别
序列化 && 反序列化
per := person{Name: "名字", Age: 1,}// 序列化p, err := json.Marshal(per)if err != nil {fmt.Println("序列化错误")return}fmt.Println("序列化结果", string(p))str := `{"Name":"理想","age":18}`var paf personjson.Unmarshal([]byte(str), &paf)fmt.Printf("反序列化结果%#v\n", paf)
接口
interface, 一种类型,但是不能被实例化,他的作用呢?可以说是一些方法定义的集合体。任何实现了这些方法一个或者多个的数据类型都是这个接口的“实现”。这意味着可以通过一个统一的渠道来执行这些方法!!!
package
- 一个文件夹下面只能有一个包
- 一个程序有且只有一个main 方法,入口方法
- 引入包,以$GOPATH/src 为起始路径
- 包中标识符(变量名/接口/方法/结构体)首字母小写,表示私有,其他包不能使用.
给引入的包设置别名
import(myself "com.yourself.code.project.module")
匿名导入.导入了但是没有使用其中方法.(会执行init),比如数据库操作
impoert _ "com.yourself.code.project.module"

文件操作
读取文件
filrobj, err := os.Open("./main.go") // 打开文件if err != nil {fmt.Println("open file error : %v", err)return}defer filrobj.Close()for {var tmp [128]bytefiledata, err := filrobj.Read(tmp[:]) //从文件对象拿数据if err != nil {fmt.Println("read file error,errr:%v", err)return}fmt.Println("读取了字节数", filedata)fmt.Println(string(tmp[:filedata]))if filedata < 128 { // 代表数据读取完毕return}}
使用包ioutil读取文件
ret, err := ioutil.ReadFile("./main.go")if err != nil {fmt.Println("读取文件错误%v", err)return}fmt.Println(string(ret))
写入文件
file, err := os.OpenFile("./main.go", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)if err != nil {fmt.Println("写文件打开错误%v", err)}defer file.Close()file.WriteString("//测试") == >实际上是对Write 的封装file.Write([]byte("//测试"))
使用bufio 写文件
file, err := os.OpenFile("./res.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)if err != nil {fmt.Println("写文件打开错误%v", err)}defer file.Close()// file.WriteString("//测试")// 使用bufiowr := bufio.NewWriter(file)wr.WriteString("使用bufio 写数据到缓冲,不过记得将缓冲数据刷新到文件内")wr.Flush()
获取用户控制台输入
var s stringreader := bufio.NewReader(os.Stdin)s, _ = reader.ReadString('\n') // 回车终止fmt.Println("用户输入的数据库%s", s)
反射
json与结构体的转换应用了反射,GO 是强类型的语言,基本上在编译阶段就能确定程序的类型,但是有的情况是在编译时不知道的,这时候使用反射,在程序运行的时候再去获取变量类型.比如orm库.配置文件等等
reflect.TypeOf
func reflectType(x interface{}) {v := reflect.TypeOf(x)fmt.Printf("类型:%v\n", v)}func main() {var a float32 = 3.14reflectType(a) // == > 类型:float32}
类型种类(kind)&&名称(Name)
type Dog struct {}func reflectType(x interface{}) {v := reflect.TypeOf(x)fmt.Printf("type:%v kind :%v ", v.Name(), v.Kind())}func main() {var dog = Dog{}reflectType(dog)}
reflect.ValueOf
值的种类
func reflectValue(x interface{}) {v := reflect.ValueOf(x)k := v.Kind()switch k {case reflect.Int64:fmt.Printf("type is int64 ,value is %d\n ", int64(v.Int()))case reflect.Float32:fmt.Printf("type is float32 ,value is %f\n ", float32(v.Float()))//=>type is float32 ,value is 3.140000}}func main() {var a float32 = 3.14reflectValue(a)}
反射设置值
函数传值在函数或方法体内是操作值拷贝.所以基本传指针.否则通过反射修改值会引发panic.通过Elem 获取指针的值进行修改操作
// 指针传递func reflectSetValue(x interface{}) {v := reflect.ValueOf(x)if v.Elem().Kind() == reflect.Float32 {v.Elem().SetFloat(3.222)}}// 非指针传递func reflectSetValuePinc(x interface{}) {v := reflect.ValueOf(x)if v.Elem().Kind() == reflect.Float32 {v.Elem().SetFloat(3.111)}}func main() {var a float32 = 3.14reflectSetValue(&a)// reflectSetValuePinc(a)fmt.Print("修改之后的值是", a)// var dog = Dog{}// reflectType(&dog)}
IsValid&& IsNil
IsValid 判定返回值是否有效,IsNil 判定指针是否为空
func IsNilIsValid() {var a *intfmt.Println("指针空", reflect.ValueOf(a).IsNil())fmt.Println("nil is Valid", reflect.ValueOf(nil).IsValid())b := struct{}{}fmt.Println("结构体中是否存在某个属性", reflect.ValueOf(b).FieldByName("属性名字在").IsValid())fmt.Println("结构体中是否存在某个方法", reflect.ValueOf(b).MethodByName("方法名字").IsValid())mapobj := map[string]int{}fmt.Println("map中不存在的key", reflect.ValueOf(mapobj).MapIndex(reflect.ValueOf("搞钱")).IsValid())}//指针空 true//nil is Valid false//结构体中是否存在某个属性 false//结构体中是否存在某个方法 false//map中不存在的key false
结构体反射
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_identifiersPkgPath stringType 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 是否是匿名字段}
exapmle
type student struct {Name string `json:"name" yml:"servename"`Age int `json:"age"`}func readstructField() {stu := student{Name: "护甲问",Age: 28,}typ := reflect.TypeOf(stu)for i := 0; i < typ.NumField(); i++ {field := typ.Field(i)fmt.Printf("name:%s index:%d type:%v tag:%v \n", field.Name, field.Index, field.Type, field.Tag.Get("yml"))}if scoreField, ok := typ.FieldByName("Name"); ok {fmt.Printf("name:%s index:%d type:%v tag:%v \n", scoreField.Name, scoreField.Index, scoreField.Type, scoreField.Tag.Get("yml"))}}name:Name index:[0] type:string tag:servenamename:Age index:[1] type:int tag:name:Name index:[0] type:string tag:servename
缺点
- 反射类型错误,在运行时才会出错,意味着错误延后性.
- 性能低下
- 代码缺少联系性,增加阅读难度
