go笔记
时间和日期函数
- 时间和日期相关函数,需要导入time包
- 获取当前时间 now := time.Now()
- 如何获取到其他的日期信息 now.Format()
- fmt.Printf(’%d-%d-%d %d:%d:%d’, now.Year(),now.Month(),…)
- 格式化日期时间的第二种方式
fmt.Printf(now.Format(“2006-01-02 15:04:05”))
fmt.Printf(now.Format(“2006-01-02”))
fmt.Printf(now.Format(“15:04:05”)) - 时间的常量
const(
Nanosecond Duration =1 //纳秒
Microsecond = 1000 * Mi
Mill
常量的作用:在程序中可用于获取指定时间单位的时间,比如想得到100毫秒
7. time.Sleep()
8. 获取当前Unix时间戳和unixnano时间戳 (可以获取随机的数字)
9. fmt.Printf(’%v’,now.Unix(),now.UnixNano())
golang设计者为了编程方便,提供了一些函数,这些函数可以直接使用—内置函数
- len
- new 用来分配内存,主要用来分配值类型,比如 int,float32,struct 返回的是指针
- make 用来分配内存,主要用来分配引用类型 比如channel map slice
golang错误处理机制
1) 在默认情况下,当发生错误后(panic),程序就会推出(崩溃)
2)当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行,还可以在捕获到错误后,给管理员一个提示 (邮件,短信)
3)
错误处理机制
- 1)go语言追求简洁优雅,所以go语言不支持传统的try…catch …finally 这种处理
- 2)go中引入的处理方式为defer panic recover
-
- go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。
错误处理的好处
进行错误处理后,程序不容易挂掉,如果加入预警代码,就可以让程序更加健壮。
自定义错误
go程序中支持自定义错误,使用errors.New和panic内置函数
1) error.New(“错误说明”) 会返回一个error类型的值,表示一个错误
2)panic 内置函数,接受一个interface{}类型的值(也就是任何值了)作为参数,可以接受error类型的变量,输出错误信息,并退出程序。、
数组和切片
数组可以存放多个同一类型数据,数组也是一种数据类型,在Go中数组是值类型。
go语言的数组地址是连续的
数组的各个元素的地址间隔是依据数组的类型决定
访问数组的元素
数组名[下标]
四种初始化数组的方式
- var newsArray01 [3]int = [3]int {1,2,3}
- var numsArray02 = [3]int{2,3,3}
- var numsArray03 = […]int{6,7,8}
- var names = [3]string{1:“tom”,0:“jack”,3:“marry”}
数组的遍历
方一:常规遍历
方二:for-range 结构遍历
for index,value := range array01{
…
}
- 1) 第一个返回值index是数组的下标
- 2)第二个value是在该下标位置的值
- 3)他们都是仅在for循环内部可见的局部变量
- 4)遍历数组元素的时候,如果不想使用下标index,可以直接把index标为下划线_
- 5)index 和value的名称不是固定的,即程序员可以自行制定,一般命名为index和value
数组使用注意事项和细节
- 数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
- var arr []int 这时arr 就是一个slice切片
- 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用
- 数组创建后,如果没有赋值,有默认值
数组类型数组,默认值为0
字符串数组, 默认值为""
bool数组,默认值为false - 使用数组的步骤,1.声明数组并开辟空间 2.给数组各个元素赋值 3 使用数组
- 数组的下标是从0开始的。
- 数组下标必须在指定范围内使用,否则报panic,数组越界
比如 var arr [5]int 则有效下标为0-40 - go数组属值类型,在默认情况下是值传递,因此会进行值拷贝。数组间不会相互影响。
- 如想在其他函数中,去修改原来的数组,可以使用引用传递(指针方式)
10.长度是数组类型的一部分,在传递函数参数时需要考虑数组的长度。
切片 :动态的数组,保存不确定的数组
切片的基本介绍
- 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
- 切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度len(slice)都一样
- 切片的长度是可以变化的,切片是一个可以动态变化的数组
- 切片定义的语法:
var 切片名 []类型
切片的容量是可以动态变化的
切片容量一般是长度的2倍
切片在内存中的形式–在内存中如何布局
- 切片的地址 长度 容量
从底层来说就是一个数据结构,struct 结构体
切片使用
方一:直接使用一个数组,然后用切片
方二:通过make来切片
基本语法:var 切片名 []type = make([],len,[cap])
- 通过make方式创建切片可以指定切片的大小和容量
- 如果没有给切片的各个元素赋值,那么就会使用默认值(int ,float=>0,string=>’’ bool=>false)
- 通过make方式创建的切片对应的数组是有make底层维护,对外不可见,即只能通过slice去访问各个元素。
方三:定义一个切片,直接就指定具体数组,使用原理类似make的方式。
方一和方二的区别
方一:直接引用数组,这个数组是先存在的,程序员可见
方二:通过make来创建切片,make也会创建一个数组,是有切片在底层进行维护程序员不可见。
切片遍历
- for循环常规方式遍历
- for-range结构遍历切片
切片注意事项和细节说明
切片初始化时 var slice = arr[startIndex:endIndex
说明: 从arr数组下标为startIndex,取到下标为endIndex的元素(不含arr[endIndex])
切片初始化,仍然不能越界,范围在[0-len(arr)]之间,但是可以动态增长
- 1)var slice = arr[0:end] 可以简写 var slice = arr[:end]
-
- var slice = arr[start:len(arr)] 可以简写var slice = arr[start:]
- 3)var slice = arr[0:len(arr)] 可以简写var slice =arr[:]
cap是一个内置函数,用于统计切片的容量,即最大可以存放多少个元素
切片定义完成后,还不能使用,因为本省是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用
切片可以继续切片
用append函数,可以对切片进行动态追加
切片的拷贝操作,切片使用copy内置函数完成拷贝。
string 和slice
- string底层是一个byte数组,因此string也可以进行切片处理。
- string 和切片在内存的形式
- string是不可变的,也就是说不能通过str[0] = ‘z’ 方式来修改字符串
- 如果需要修改字符串,可以先将string->[]byte/或者[]rune->修改->重写转成string
排序和查找
- 内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。
(包括交换式排序法,选择式排序法和插入式排序法) - 外部排序法 数据量过大,无法全部加载到内存中,需要借助外部存储进行排序,
(合并排序法和直接合并排序法)
交换式排序法:
- 冒泡排序法
- 快速排序法
MAP
map是key-value数据结构,又称为字段或者关联数组,类似其他编程语言的的集合
var map变量名 map[keytype]valuetype
key 可以是什么类型
go中map的key可以是很多类型,比如bool,数字,string,指针,chanel ,还可以包含前面几个类型的接口,结构体,数组,
通常为int string
注意 slice,map还有function不可以作为key,因为这几个没法用==来判断
valuetype的类型和key基本一行,
通常是数字(整数,浮点数)string struct
生命是不会分配内存的,初始化需要make,分配内存后才能赋值和使用
map总结的几点
- map在使用前一定要make
- map的key是不能重复的,如果重复了,则以最后这个key-value为准
- map的value是可以相同的
- map的key-value是无序的。
map 使用细节
- map是引用类型,遵守引用类型传递的机制,再一个函数接受map,修改后,会直接修改原来的map
- map的容量达到后,再想map增加元素,会自动扩容,并不会发生panic,也就是说map能动态的增长键值对(key-value)
- map的value也经常使用struct类型,更适合管理复杂的数据
go面向对象编程说明
- 1)go支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言,所以我们说golang支持面向对象编程特性是比较准确的。
-
- go中没有类class,go语言的结构体和其他编程语言的类有同等地位,go是基于struct来实现oop特性的。
- 3)go面向对象编程非常简洁,去掉了传统OOP语言的继承,方法重载,构造函数和析构函数,隐藏的this指针等
- 4)go仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其他OOP语言不一样,比如继承,go中没有extend关键字,继承是通过匿名字段来实现的。
- 5)go面向对象很优雅,oop本身就是语言类型系统(type system)的一部分,通过接口关联,耦合性低,非常灵活,go中面向接口编程
结构体注意细节
1.结构体的所有字段在内存中是连续的
指针本身的地址还是连续的,但是指针指向的地址不一定时连续的。
2.结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字,个数和类型)
3.结构体进行type重新定义(相当于取别名)golang认为是新的数据类型,但是相互间可以强转
4.struct的每个字段上,可以写一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化
struct方法
go中的方法是作用在指定的数据类型上的(和指定的数据类型绑定)。因此自定义类型,都可以有方法,而不仅仅是struct.
方法的声明
func (recevier type)methodName (参数列表)(返回值列表){
方法体
return 返回值
}
- 参数列表 表示方法输入
- recevier type 表示这个方法和type这个类型进行绑定,或者说该方法作用域type类型
- recevier type type可以是结构体,也可以其他的自定义类型
- receiver:就是type类型的一个变量(实例),比如Person结构体的一个变量(实例)
- 参数列表:表示方法输入
- 返回列表:表示返回的值,可有多个
- 方法主体:表示为了实现某一功能代码块
- return 语句不是必须的。
方法的注意事项和细节讨论 重要
- 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方法
- 如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理。
- go中的方法作用在指定的数据类型上,即(和指定的数据类型绑定)。因此自定义类型,都可以有方法,而不仅仅是struct,比如int,float32 等都可以有方法。
- 方法的访问范围控制规则,和函数一样,方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问
- 如果一个变量实现了string()这个方法,那么fmt.Println()默认会调用这个变量的string()进行输出
方法和函数区别
-
调用方式不一样
函数的调用方式 : 函数名(实参列表)
方法的调用方式:变量名.方法名(实参列表) -
对于普通函数,接受者为值类型时,不能讲指针类型的数据直接传递,反之亦然。
-
对于方法(比如struct方法)接受者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。
总结:
- 不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和那个类型绑定。
- 如果是和值类型,比如(P,Person)则是值拷贝,如果和指针类型,比如是(p *Person)则是地址拷贝。
面向对象编程应用实例
步骤
- 声明(定义)结构体,确定结构体名。
- 编写结构体的字段
- 编写结构体的方法
面向对象编程-抽象
面向对象三大特性:封装 继承 多态
go中没有特别强调封装,
继承可以解决代码复用,当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。
其他的结构体不需要重新定义这些属性和方法,只需要嵌套一个Student匿名结构体即可。
也就是说,在go中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。
嵌入一个匿名结构体实现继承,
继承的深入讨论、
- 1)结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段,方法,都可以使用
- 2)匿名结构体字段访问可以简化
- 3)当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。
- 4)结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有相同的字段和方法)在访问时,就必须明确指定匿名结构体名字,否则编译器报错。
- 5)如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段和方法时,必须带上结构体的名字。
- 6)嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。
多重继承
- 如一个struct嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。
- 为了保证代码的间接性,建议不要使用多重继承
接口 interface 减少耦合 松耦合 高类聚
go中 多态特性主要是通过接口来体现的
基本介绍
- interfaceleixi8ng可以定义一组方法,但是这些不需要实现,并且interface不能包含任何变量,到某个自定义类型(比如结构体Phone)要使用的时候,再根据具体情况把这些方法写出来。
基本语法
type 接口名 interface{
method1 (参数列表)返回值列表
method2 (参数列表) 返回值列表
}
func (t 自定义类型) method(参数列表) 返回值列表{
//方法实现
}
说明
- 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法,接口体现了程序设计的多态和高内聚低耦合的思想。
- go中的接口,不需要显示的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口,go中没有implement这样的关键字。
接口的注意事项和细节
- 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义的变量(实例)
- 接口中的所有方法都没有方法体,即都是没有实现的方法
- 在go中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口
- 一个自定义类型只能实现了某个接口,才能将该自定义类型的实例(变量)赋给接口的类型
- 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
- 一个自定义类型可以实现多个接口
- go接口中不能有任何变量
- 一个接口(比如A接口)可以继承多个别的接口(比如 B接口,C接口)这时如果要实现A接口,也必须将B,C接口的方法也全部实现。
- interface类型默认是一个指针(引用类型)如果没有对interface初始化就使用,那么会输出nil
- 空接口interface{}没有任何方法,所以所有类型都实现了空接口
go基于函数(方法)实现。
接口和继承的关系
- 当A结构体继承了B结构体,那么A结构就自动的继承了B结构体的字段和方法,并且可以直接使用
- 当A结构体需要扩展功能,同时不希望去破坏继承关系。
实现接口可以看做是对继承的一种补充
1.接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它自定义类型去实现这些方法
2. 接口比继承更加灵活
接口比继承更加灵活,继承是满足is-a的关系,而接口只需要满足like-a的关系。
3. 接口在一定程度上实现代码解耦
面向对象的多态
变量(实例)具有多种形态,
go语言中,多态是通过接口实现的。可以按照统一的接口来调用不同的实现,这时接口变量就呈现不同的形态。
golang 在创建结构体实例(变量)时,可以直接指定字段的值。
创建结构体变量时指定字段值方式
- 方一:
var stu1 Student = Student{“tome”,10}
stu2 := Student{“tom”,10} - 方二
var stu *Student = &Student{“smith”,20}
var stu2 *Student = &Student{}
go 工厂模式
golang的结构没有构造函数,,通常可以使用工厂模式来解决这个问题。
使用工厂模式实现跨包创建结构体实例(变量)的案例
如果model包的结构体变量首字母大写,引入后,直接使用,没有问题。
如果model包的结构体变量首字母小写,引入后,不能直接使用,可以工厂模式解决。
文件的基本介绍
文件是数据源(保存数据的地方)
文件在程序中是以流的形式来操作的,
打开文件的方法 Open 关闭文件的路径Close
os 包 Variable
os.Args是一个string的切片,用来存储所有的命令行参数。
go中的json
json (javascript object notation )是一种轻量级的数据交换格式,易于人阅读和编写,同事也易于机器解析和生成。
类型断言,由于接口是一般类型,不知道具体类型,
如果要转成具体类型,就需要使用类型断言。