Go 语言经过自定义方式造成新的类型,结构体是类型中带有成员的符合类型,Go语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各类属性。前端
结构体成员是由一系列成员变量构成,这些成员变量称为"字段"mysql
字段特征以下:
1 字段必须有本身的名称和类型
2 字段名必须惟一
3 字段的类型通常是基本数据类型,数组,也能够是引用类型,甚至能够是字段所在的结构体的类型。程序员
Go 语言中不但结构体能够拥有本身的方法,且每种自定义类型均可以拥有本身的方法 golang
Go语言使用关键字type 能够定义各类类型,包括将各类基本类型定义为自定义类型,天然也能够定义结构体 sql
type 类型名称 struct { 字段1 字段1类型 字段2 字段2类型 }
类型名: 同一个包中不能重复,标识自定义结构体名称数据库
struct{}: 标识其类型是结构体类型编程
字段: 表示字段名称,在该结构体中必须惟一 json
字段类型:该字段的类型,能够是多种类型数组
结构体定义只是一种内存布局的描述,只有当结构体实例化后,才会真正的分配内存,所以必须在定义结构体并进行实例化后才能使用。安全
实例化:根据结构体定义的格式建立一份与格式一致的区域
结构体实例和实例之间内存是独立的,由于其值类型
package main import "fmt" type A struct { //定义一个结构体,其中包含三个字段 X int Y string Z int } func main() { a := A{X: 10, Y: "234", Z: 10} b := A{10, "123", 100} c := A{} c.X = 100 c.Y = "789" c.Z = 1000 fmt.Println(a) fmt.Println(b) fmt.Println(c) }
结果以下
package main import ( "fmt" ) func PrintMsgType(msg *struct { //此处定义入参类型为如此结构体 id int data string }) { fmt.Printf("其类型为:%T", msg) // 此处打印其对应的类型 } func main() { msg := struct { // 此处使用匿名指针 id int data string }{ // 此处调用该匿名指针,直接进行赋值操做 10, "12234", } PrintMsgType(&msg) }
结果以下
结构体自己是一种类型,其也可经过var 方式声明并进行实例化,基本以下
package main import "fmt" type A struct { //定义一个结构体,其中包含三个字段 X int Y string Z int } func main() { var a A // 实例化结构体 a.X = 10 //结构体赋值,及其成员访问是经过"." 点号来访问的,其赋值方式和普通变量相同 a.Y = "abcd" a.Z = 10 fmt.Println(a) fmt.Printf("结构体实例地址:%p\n", &a) // 取出实例地址 fmt.Println("第一个参数地址:", &a.X) fmt.Println(&a.Y) fmt.Println(&a.Z) }
结果以下
结构体注意事项和使用细节
1 结构体的全部字段在内存中都是连续的
2 结构体是用户单独定义的类型,和其余类型进行转换时须要有彻底相同的字段(名称,个数和类型一一对应)
package main import "fmt" type A struct { X int //定义整形数据 Y string // 定义字符串类型数据 Z float64 //定义浮点类型数据 W []int //定义切片 M map[string]string //定义map N [3]string //定义数组 P *int // 定义指针 } func main() { var a A a.X = 10 a.Y = "abcd" a.Z = 10.0000 a.W = []int{1, 2, 3, 4} a.M = make(map[string]string) a.M["a"] = "abcd" a.M["b"] = "1234" a.N = [3]string{"1", "2", "3"} a.P = &a.X a.W = append(a.W, 1, 3, 4, 6, 7, ) fmt.Println(a) }
结果以下
Go语言中,也可以使用new关键字对类型进行实例化,结构体在实例化后会造成指针类型的结构体
package main import "fmt" type A struct { //定义一个结构体,其中包含三个字段 X int Y string Z int } func main() { a := new(A) //此处返回为一个指针类型,其须要使用*a 来进行取值,其类型为*A a.X = 10 a.Y = "123" a.Z = 20 fmt.Printf("a的类型为%T,a的值为%v\n", a, *a) b := new(int) // 其类型为*int fmt.Printf("b的类型为%T,b的值为%v\n", b, *b) }
结果以下
package main import "fmt" type A struct { X int //定义整形数据 Y string // 定义字符串类型数据 } func main() { var a A a = A{X: 10, Y: "abcd"} fmt.Printf("a 的类型为:%T", a) fmt.Println(a) var b *A = new(A) //使用值传递进行处理 fmt.Printf("b 的类型为:%T", b) (*b).X = 100 //此处应该使用值方式访问,其* 表示经过指针获取其值 (*b).Y = "1234" fmt.Println(*b) b.X = 200 // 此处也修改了,其实际上应该是(*b).X ,其是Go语言底层对其进行了优化和配置 b.Y = "mysql" fmt.Println(*b) var c = &A{}// 取地址操做,其可视为对该类型进行一次new的实例化操做。 fmt.Printf("c 的类型为:%T", c) c.X = 300 c.Y = "abcd1234" fmt.Println(*c) }
结果以下
说明:
其中b和c 方式返回的是结构体指针,其标准的访问字段形式是(*x.field).Go语言为了进行简化,支持结构体指针.字段名,更加符合程序员的使用习惯,Go的底层进行了相关的优化操做
某种状况下,须要调用某些结构体,但又不想对外暴露,此时即可使用工厂模式来解决此种状况
目录结构以下
test01.go中的配置
package test type student struct { Name string Score float64 } func NewStudent(n string, s float64) *student { //此处用于返回一个student的指针类型数据,其被封装在对应的函数中 return &student{ Name: n, Score: s, } }
main.go 调用以下
package main import ( "fmt" "gocode/project01/test" ) func main() { t1 := test.NewStudent("zhangsan", 100.00) fmt.Println(*t1) }
结果以下
结构体进行type 从新定义(至关于取别名),golang任务是新的数据类型,可是相互之间可强制转换
package main import "fmt" type A struct { X int //定义整形数据 } type B A // 从新定义 func main() { var a A var b B a.X = 10 b = B(a) // 强制转换 fmt.Println(a, b) }
结果以下
问题:
Json 要处理数据,其必须调用对应的Mashal方法,若结构体的各个字段都是小写,则其不能被调用,如果大写,则可能会致使返回给前端为大写,不符合规范,此时可以使用tag 进行处理
package main import ( "encoding/json" "fmt" ) type A struct { X int //定义整形数据 } func main() { var a A a.X = 10 data, err := json.Marshal(a) if err != nil { fmt.Println(err) } else { fmt.Printf("%c", data) //其默认返回的Json数据为大写 } }
结果以下
经过tag 进行处理
package main import ( "encoding/json" "fmt" ) type A struct { X int `json:"x"` //定义整形数据,定义tag,其中不能有空格 } func main() { var a A a.X = 10 data, err := json.Marshal(a) if err != nil { fmt.Println(err) } else { fmt.Printf("%c", data) //其默认返回的Json数据为大写 } }
结果以下
Go语言中的方法是一种做用于特定类型变量的函数,这种特定变量叫作接收器
若是将特定类型理解为结构体或"类"时,接收器的概念就相似于this 或 self
Go语言中,接收器的类型能够是任何类型,而不只仅是结构体,任何类型均可拥有方法
面向对象语言中,类拥有的方法通常被理解为类能够作的事情,在Go语言中方法给概念和其余一致,只是Go语言创建的"接收器"强调方法做用对象是接收器,也就是类实例,而函数是没有做用对象的
Go语言的类型或结构体的类型没有构造函数的功能,结构体的初始化过程可使用函数封装实现
每一个类能够添加构造函数,多个构造函数使用函数重载实现
构造函数通常与类名同名,且没有返回值
构造函数有一个静态构造函数,通常用这个特性来调用父类的构造函数
对于C++来讲,还有默认构造函数,拷贝构造函数等
在某些状况下,咱们须要定义方法,及数据能够干什么操做,所以,自定义的不只仅是类型,还能够是方法
说明:方法的调用和传参机制和函数基本同样,不同的地方是方法调用,会将调用方法的变量,当作实参传递给方法
func (receiver type) methodName(参数列表) (返回值列表) { 方法体 return 返回值 }
其中receiver和type统称为接收器。其类型有指针类型和非指针类型两种。通常的,非指针类型都有返回值的。 1 type: 表示这个方法和这个type类型进行绑定,或者说方法用于type类型,其type能够是结构体,也能够是其余自定义类型 2 receive: 就是type 类型的一个变量(实例),其称为接收器变量,其在命名时,官方建议使用接收器类型名的第一个小写字母,而不是self,this之类的名称。 3 methodName:表示该结构体绑定的方法名称 4 参数列表: 表示方法的输入 5 返回值列表,其可为多个 6 方法主体:表示为了实现某一功能的代码块 7 return: 返回值,非必须
package main import "fmt" type Bag struct { items []int } func (b Bag) Insert(n int) { // 非指针类型 b.items = append(b.items, n) } func (b *Bag) Test(n int) { // 指针类型 (*b).items = append((*b).items, n) } func main() { bag := Bag{} bag.Insert(10) // 此处调用方法 bag.Insert(20) fmt.Println(bag) bag1 := &Bag{} // 此处也可以使用new(Bag) 进行建立 bag1.Test(30) // 此处调用方法 bag1.Test(40) bag1.Test(40) fmt.Println(*bag1) }
结果以下
在计算机中,小对象因为值复制时很快,所以适合非指针接收器,大对象由于复制性能较低,适用于指针接收器,在接收器和参数之间传递时不进行复制,只进行指针传递
具体的结构体的方法其第一个字段只有结构体
package main import ( "fmt" ) type A struct { X int `json:"x"` //定义整形数据,定义tag,其中不能有空格 } func (a A) Test() { //定义方法,其中a 表示A的形参,后面的Test表示值 a.X += a.X fmt.Println(a.X) } func (a A) Test1(n1 int) int { //此中可在对应方法中传值,更加具体的描述其方法的灵活性 return a.X + n1 } func main() { a := A{10} a.Test() res := a.Test1(100) fmt.Println(res) }
结果以下
1 结构体类型是值类型,在方法调用过程当中,遵循值传递机制,是值拷贝传递方式
2 若程序员但愿在访问中修改结构体变量的值,可经过结构体指针方式来完成
3 golang中方法做用在指定的数据类型上,及和数据类型绑定,只要是数据类型,都可绑定方法,而不必定是struct
4 方法的访问访问控制规则和函数同样,方法首字母小写,只能在本包中使用,方法首字母大写,可在本包和其余包中访问使用
5 若是一个变量实现了string()这个方法,则fmt.Println() 会默认调用这个变量的string() 进行输出
函数的调用方式 函数名(实参列表) 方法的调用方式 变量.方法名(实参列表)
对于普通函数,接受者为值类型,不能将指针类型的数据直接传递,反之亦然,对于方法,接受者为值类型,可以使用指针类型的变量调用方法,反之也能够
package main import "fmt" type User struct { Name string `json:"name"` //定义整形数据,定义tag,其中不能有空格 Age int `json:"age"` } func (user *User) Test() { fmt.Printf("user 的类型为:%T,其对应的值为:%v\n", user, *user) } func (user User) Test1() { fmt.Printf("user 的类型为:%T,其对应的值为:%v\n", user, user) } func main() { user := User{"golang", 13} user.Test() user.Test1() }
结果以下
无论是任何形式,真正决定是值拷贝仍是地址包括的,是看方法是哪一种类型的绑定,如果值类型绑定(user User) 指针类型绑定(user *User),则是指针拷贝。
1 golang 也支持面向对象编程(OOP),但和传统的面向对象编程有区别,其并非纯粹的面向对象语言,因此说golang支持面向对象编程特性是比较准确的
2 golang没有class,golang语言结构体(struct)和其余语言的class有同等的地位,可理解为使用struct实现了OOP特性
3 golang面向对象很是简洁,去掉了传统的继承,重载,构造和析构,隐藏了this和self等
4 golang仍然有面向对象编程的继承,封装和多态,只是其实现方式和其余OOP语言不一样,如继承golang没有extends关键字,继承法是经过匿名字段来实现的
5 golang面向对象很优雅,OOP自己就是语言系统的一部分,经过接口interface实现,耦合性第,也很是灵活
golang仍然有面向对象编程的继承,封装和多态,只是实现方式和其余COP语言不一样
封装就是把抽象出的字段和堆字段的操做封装在一块儿,数据被保护在内部,程序的其余包只能经过被受权的操做,才能实现对字段的操做
1 将结构体字段的首字母大写
2 给结构体所在的包提供一个工厂模式的函数,首字母大写,相似于一个构造函数
3 提供一个首字母大写的Set方法,用于对属性判断和赋值
4 提供一个Get方法,用于获取属性的值
1 隐藏实现细节
2 可对数据进行验证,保证安全合理
1 对结构体中的方法进行封装
2 经过方法,包实现封装
类型内嵌和结构体内嵌
结构体容许其成员字段在声明时没有字段名而只有类型,这种形式被称为类型内嵌或者匿名字段类型内嵌
写法以下
package main import "fmt" type Data struct { int //定义匿名字段 float32 bool } func main() { ins := &Data{ 10, 3.14, true, } fmt.Println(*ins) ins1 := Data{ 10, 20.0, false, } fmt.Println(ins1) ins2 := new(Data) ins2.float32 = 30.20 // 类型内嵌其实也有本身的字段名,只是字段名就是其类型自己,结构体要求字段名必须惟一,所以结构体中同种类型的匿名字段只能有一个 ins2.int = 10 ins2.bool = false fmt.Println(*ins2) }
结果以下
继承可解决代码复用的问题
当多个结构体存在相同的属性和方法时,能够从这些结构体中抽象出基础结构体,该结构体中定义这些相同的属性和方法,其余的结构体不须要从新定义该属性和方法,只须要嵌套基本结构体便可,也就是说,在golang中,若是一个struct嵌套了另外一个匿名结构体,那么这个结构体能够直接访问匿名结构体中的字段和方法,从而实现了继承的特性
代码以下
package main import "fmt" type Person struct { //老师和学生都是人类 Name string Age int } type Teacher struct { //老师具备对应的职业 Person Occupation string } type Student struct { //学生具备对应的座位号 Person Seat_Number map[int]int } func main() { student := Student{} student.Name = "小旺" student.Age = 12 student.Seat_Number = make(map[int]int) student.Seat_Number[10] = 20 teacher := Teacher{} teacher.Name = "王老师" teacher.Age = 30 teacher.Occupation = "teacher" fmt.Println(student) fmt.Println(teacher) }
结果以下
1 结构体可使用嵌套匿名结构体复用字段和方法
2 当结构体和匿名结构体具备相同的字段或方法时,则采用就近原则,及个该实例最接近的结构体的属性或方法就是其返回的结果的条件
package main import "fmt" type Person struct { //老师和学生都是人类 Name string Age int } type Teacher struct { //老师具备对应的职业 Person Occupation string } type Student struct { //学生具备对应的座位号 Person Seat_Number map[int]int } func (p Person) Get() { fmt.Printf("名字为:%s的年龄为:%d\n", p.Name, p.Age) } func (s Student) Get() { fmt.Printf("此方法只针对学生,名字为:%s的年龄为:%d\n", s.Name, s.Age) } func main() { student := Student{} student.Name = "小旺" student.Age = 12 student.Seat_Number = make(map[int]int) student.Seat_Number[10] = 20 teacher := Teacher{} teacher.Name = "王老师" teacher.Age = 30 teacher.Occupation = "teacher" fmt.Println(student) fmt.Println(teacher) student.Get() teacher.Get() }
结果以下
3 结构体嵌套入两个匿名结构体时,若两个匿名结构体具备相同的字段和方法,则在访问时,就必须指明匿名结构体名称,不然会出现编译错误
package main import ( "fmt" ) type Person struct { Name string Age int } type Teacher struct { Name string Age int } type Student struct { //此结构体继承了上面两个结构体 Person Teacher } func (p Person) Get() { fmt.Printf("名字为:%s的年龄为:%d\n", p.Name, p.Age) } func (s Teacher) Get() { fmt.Printf("此方法只针对学生,名字为:%s的年龄为:%d\n", s.Name, s.Age) } func main() { student := Student{} student.Teacher.Name = "小王" student.Teacher.Age = 20 student.Person.Get() student.Teacher.Get() }
结果以下
4 若是一个struct嵌套了一个有名称的结构体,此称为组合,若组合关联,那么在访问结构体字段或方法时,就必须有结构体的名称
5 嵌套名结构体后,也能够建立结构体变量时,直接指定各个匿名结构体字段的值
变量(实例)具备多种形态,面向对象的第三大特征,在Go语言中,多态特征是经过接口实现的,能够按照统一的接口来调用不一样的实现,这时接口就会呈现不一样的形态
interface 类型能够定义一组方法,可是这些不须要实现,而且interface不能包含任何变量,到某个自定义类型的时候要使用,根据具体状况将这些方法写出来,接口自己调用方法和实现均须要遵照一种协议,你们按照统一的方法来命名参数类型和数量来协调逻辑处理的过程
Go语言中使用组合实现独享特性的描述,对象的内部使用结构体内嵌组合对象应该具备的特性,对外经过接口暴露能使用的特性。
接口是双方约定的一种合做协议,是一种类型,也是一种抽象结构,不会暴露全部含数据的格式,类型及结构
type 接口名称 interface { method1 (参数列表) 返回值列表 method2 (参数列表) 返回值列表 } func (t 自定义类型) method1(参数列表) 返回值列表 { 方法体 return 返回数据 }
接口名称: 使用type将接口定义为自定义的类型名,Go语言的接口命名时,通常在单词后面加上er,如写操做叫作writer,关闭叫作closer等
method1: 当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法能够被接口所在的包(package)以外的代码访问
参数列表,返回值列表:参数列表和返回值列表中的参数变量可被忽略
1 接口中全部的方法都没方法体,及接口的方法都是没实现的方法,接口体现了程序设计的多态和高内聚低耦合的思想
2 golang中的接口,不须要显示实现,只要一个变量,含有接口类型中的全部方法,那么这个变量就实现了这个接口,所以,golang中没有implement这样的关键字
接口定义后,须要实现调用接口,调用方能正确编译并经过使用接口,接口的实现须要遵循两条规则才能让接口可用。
接口的方法与实现接口的类型方法一致,及在类型中添加与接口签名一致的方法就能够实现该方法,签名包括方法中的名称、参数列表、返回值列表,其实现接口中方法的名称,参数列表,返回参数列表中的任意一项和接口要实现的方法不一致,那么就扣的这个方法就不能被实现。
package main import "fmt" type DataWriter interface { WriteData(data interface{}) error } type file struct { } func (d *file) WriteData(data interface{}) error { fmt.Println("WriteData:", data) return nil } func main() { f := new(file) //实例化file var write DataWriter // 声明一个接口,用于读取数据 write = f //将接口赋值f,也就是file类型,及关联接口和实例,虽然其变量类型不一致,但writer是一个接口,且f已经彻底实现了DataWriter()的全部方法,所以赋值成功 write.WriteData("data") }
结果以下
当一个接口有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用
Go 语言实现的接口是隐式的,无需让实现接口的类型写出实现了那些接口,这种设计被称为非侵入式设计。
package main import ( "fmt" ) type Test interface { Start() Stop() } type A struct { X string Y int } type B struct { A } type C struct { } func (a A) Start() { fmt.Println(a.X) } func (a A) Stop() { fmt.Println(a.Y) } func (b B) Start() { fmt.Println(b.X) } func (b B) Stop() { fmt.Println(b.Y) } func (c C) Usb(usb Test) { usb.Start() usb.Stop() } func main() { a := A{"golang", 20} b := B{} b.X = "goland" b.Y = 10 c := C{} //接口调用对应实例a c.Usb(a) fmt.Println("-----------------------------") c.Usb(b) }
结果以下
package main import ( "fmt" ) type DataWriter interface { WriteData(data interface{}) error } type DataRead interface { ReadData(data interface{}) error } type file struct { } // 定义实现该方法的结构体 func (d *file) WriteData(data interface{}) error { fmt.Println("WriteData:", data) return nil } func (d *file) ReadData(data interface{}) error { fmt.Println("READ DATA", data) return nil } func main() { f := new(file) //实例化file var write DataWriter // 声明一个接口,用于读取数据 write = f //将接口赋值f,也就是file类型,及关联接口和实例,虽然其变量类型不一致,但writer是一个接口,且f已经彻底实现了DataWriter()的全部方法,所以赋值成功 write.WriteData("write data") var read DataRead // read = f read.ReadData("read data") }
上述中file类型实现了DdataWriter 和 DataRead 的两个接口,这两个接口之间没联系。
一个接口的方法,不必定须要由一个类型彻底实现,接口的方法能够经过在类型中嵌套其余类型或结构体实现,也就是说,使用者并不关系某个接口的方法是经过一个类型彻底实现的,仍是经过多个结构嵌套到一个结构体中拼凑起来共同实现的。
package main import ( "fmt" ) type DataWriter interface { WriteData(data interface{}) error ReadData(data interface{}) error } type fileread struct { } type file struct { fileread //此类型包含此类型 } // 定义实现该方法的结构体 func (d *file) WriteData(data interface{}) error { fmt.Println("WriteData:", data) return nil } func (d *fileread) ReadData(data interface{}) error { // 此类型实现了此方法 fmt.Println("READ DATA", data) return nil } func main() { f := new(file) //实例化file var write DataWriter // 声明一个接口,用于读取数据 write = f //将接口赋值f,也就是file类型,及关联接口和实例,虽然其变量类型不一致,但writer是一个接口,且f已经彻底实现了DataWriter()的全部方法,所以赋值成功 write.WriteData("write data") write.ReadData("read data") }
结果以下
在Go语言中,不只结构体体和结构体体之间能够嵌套,接口和接口之间也能够经过创造出新的接口
接口与接口嵌套组合而造成了新接口,只要接口的全部方法都被实现,则这个接口中全部嵌套接口的方法都可以被调用
package main import ( "fmt" ) type Data interface { DataWriter DataRead } type DataWriter interface { WriteData(data interface{}) error } type DataRead interface { ReadData(data interface{}) error } type file struct { } // 定义实现该方法的结构体 func (d *file) WriteData(data interface{}) error { fmt.Println("WriteData:", data) return nil } func (d *file) ReadData(data interface{}) error { // 此类型实现了此方法 fmt.Println("READ DATA", data) return nil } func main() { f := new(file) //实例化file var data Data // 声明一个接口,用于读取数据 data = f //将接口赋值f,也就是file类型,及关联接口和实例,虽然其变量类型不一致,但writer是一个接口,且f已经彻底实现了DataWriter()的全部方法,所以赋值成功 data.WriteData("write data") data.ReadData("read data") }
结果以下
1 接口自己不能建立实例,可是能够指向一个实现了接口的自定义类型的变量
2 接口中全部的方法都没有方法体,及没实现该方法
3 在golang中,一个自定义类型须要将某个接口的全部方法倒都实现,咱们才说这个自定义接口实现了该方法
4 一个自定义类型只有实现了某个接口,才能将该自定义类型赋值给接口类型
5 只要是自定义类型,均可以实现接口,而不只仅是结构体
6 一个自定义类型能够实现多个接口
7 golang接口中不能有任何变量
8 一个接口能够继承多个个别接口,这时若是要实现接口A,则必须实现接口B和接口C
9 interface 类型默认是一个指针,若是没有对interface初始化就使用,则会出现输出nil
10 空接口interface{} 没有实现任何方法,因此全部类型都实现了空接口
package main import ( "fmt" "math/rand" "sort" "time" ) func Test(n int) []int { var l1 []int for i := 1; i <= n; i++ { rand.NewSource(time.Now().UnixNano()) num := rand.Intn(500) l1 = append(l1, num) } return l1 } func main() { var l1 []int l1 = Test(10) fmt.Println("排序前的值:", l1) sort.Ints(l1) fmt.Println("排序后的值:", l1) }
结果以下
官网得知,欲使用Sort接口,需实现三个方法
Len() 获取数据长度,返回为int 类型数据
Less() 元素比大小,经过i,j 进行比较返回bool
Swap() 元素交换,经过i,j 交换来处理元素
具体代码实现以下
package main import ( "fmt" "math/rand" "sort" "time" ) // 此处用于构造元数据 type Base struct { Name string Age int } type SliceBase []Base //此方法需实现对应的interface规定的功能,其才能调用具体的interface sort.Sort()接口 // 此函数用于返回列表,其列表中的元素是上述struct中的元素 func CreateSlice(n int, base SliceBase) []Base { for i := 1; i <= n; i++ { rand.NewSource(time.Now().UnixNano()) num := rand.Intn(100) value := Base{"goland" + string(num), num} base = append(base, value) } return base } func (t SliceBase) Len() int { return len(t) //此处返回对应类型长度便可 } func (t SliceBase) Less(i, j int) bool { if t[i].Age > t[j].Age { //此处表示降序排列,且此处为age的比较 return true } else { return false } } func (t SliceBase) Swap(i, j int) { t[i], t[j] = t[j], t[i] //此处标识交换 } func main() { var l1 SliceBase l1 = CreateSlice(10, l1) fmt.Println("排序前的值:", l1) sort.Sort(l1) fmt.Println("排序后的值:", l1) }
结果以下
Go 语言使用接口端来将接口转换成另外一个接口,也能够将接口转换成另一种类型。
t:=i.(T)
i 表明接口变量
T 表明转换的目标类型
t 表明转换后变量若是i 没有彻底实现T接口的方法,此语句将会致使触发宕机,所以可经过 t,ok:=i.(T),这种写法下,若是发生接口未实现,则将会把ok置为false,t置为T类型的值为0,ok的含义是i接口是否实现T类型的结果。
应用场景:因为接口是通常类型,不知道具体类型,若是要转换成具体类型,就须要断言
package main import "fmt" // 此处用于构造元数据 func main() { var a interface{} var f float64 = 10.00 a = f if y, ok := a.(float64); ok { fmt.Println(y) } else { fmt.Println("失败") } }
结果以下
package main import "fmt" // 此处用于构造元数据 type Base struct { Name string Age int } func main() { var a interface{} base := Base{"golang", 30} a = base var b Base b = a.(Base) //此处若不使用断言,则会报错 fmt.Println(b) }
在进行断言时,须要确保类型匹配,不然会报错
可进行处理,不报panic
package main import ( "fmt" ) type DataWriter interface { WriteData(data interface{}) error } type DatawRead interface { ReadData(data interface{}) error } type readwritefile struct { } // 定义实现该方法的结构体 func (d *readwritefile) WriteData(data interface{}) error { fmt.Println("WriteData:", data) return nil } func (d *readwritefile) ReadData(data interface{}) error { // 此类型实现了此方法 fmt.Println("READ DATA", data) return nil } type readfile struct { } func (d *readfile) ReadData(data interface{}) error { // 此类型实现了此方法 fmt.Println("read dta:", data) return nil } func main() { //建立实例 file := map[string]interface{}{ "readfile": new(readfile), "readwritefile": new(readwritefile), } //遍历映射 for name, obj := range file { fmt.Println("-------------------") rw, ok := obj.(DataWriter) if ok { rw.WriteData("write data") fmt.Println(name) } r, ok := obj.(DatawRead) if ok { r.ReadData("read data") fmt.Println(name) } } }
结果以下
空接口是接口类型的特殊形式,空接口没有任何方法,所以任何类型都无需实现空接口,从实现的角度看,任何值都知足这个接口的需求,所以空接口类型可保存任何值,也能够从空接口中取出原值。
空接口的内部实现了保存对象的类型和指针,使用空接口保存一个数据的过程比直接使用数据对应类型的变量保存稍慢。
package main import "fmt" func main() { var str interface{} str = 1 fmt.Println(str) str = "hello" fmt.Println(str) str = false fmt.Println(str) }
结果以下
package main import "fmt" func main() { var a int = 1 var i interface{} = a fmt.Println(i) var b int = i fmt.Println(b) }
结果以下
编译器返回接口,不能将i 变量视为int类型赋值给b。
修改结果以下
package main import "fmt" func main() { var a int = 1 var i interface{} = a fmt.Println(i) var b int = i.(int) fmt.Println(b) }
结果以下
空接口在保存不一样的值后,能够和其余变量值同样使用"==" 进行比较操做,空接口比较有如下几种特性
1 类型不一样的空接口之间的比较结果不一样
package main import "fmt" func main() { var a interface{} = 100 var b interface{} = "abc" fmt.Println(a == b) }
结果以下
2 不能比较空接口中的动态值
package main import "fmt" func main() { var c interface{} = []int{10} var d interface{} = []int{20} fmt.Println(c == d) }
结果以下
1 结构体实现继承过程当中不能选择性继承,而当基类结构体须要其余扩展功能不被子类继承时,则须要使用接口来补充
2 接口比继承更灵活 接口必定程度上实现了代码的解耦
1 继承的价值在于解决代码中的复用性和可维护性问题
2 接口的价值在于设计好各类规范,让其余自定义类型实现这些方法接口提现的多态特性
1 多态参数
2 多态数组
文件,数据源,其主要做用就是保存数据,其就是数据源
文件在程序中以流的方式操做的
流:数据在数据源和程序之间经历的路径
输入流:从数据源到程序的路径,读取文件
输出流:从程序到数据源的路径,写文件
func Open(name string) (file *File, err error)
Open打开一个文件用于读取。若是操做成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具备O_RDONLY模式。若是出错,错误底层类型是*PathError。
func OpenFile(name string, flag int, perm FileMode) (*File, error) { testlog.Open(name) f, err := openFileNolog(name, flag, perm) if err != nil { return nil, err } f.appendMode = flag&O_APPEND != 0 return f, nil }
name :表示文件路径
flag:表示文件打开模式
const ( O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件 O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件 O_RDWR int = syscall.O_RDWR // 读写模式打开文件 O_APPEND int = syscall.O_APPEND // 写操做时将数据附加到文件尾部 O_CREATE int = syscall.O_CREAT // 若是不存在将建立一个新文件 O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在 O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O O_TRUNC int = syscall.O_TRUNC // 若是可能,打开时清空文件 )
perm FileMode:解析结果以下
const ( // 单字符是被String方法用于格式化的属性缩写。 ModeDir FileMode = 1 << (32 - 1 - iota) // d: 目录 ModeAppend // a: 只能写入,且只能写入到末尾 ModeExclusive // l: 用于执行 ModeTemporary // T: 临时文件(非备份文件) ModeSymlink // L: 符号连接(不是快捷方式文件) ModeDevice // D: 设备 ModeNamedPipe // p: 命名管道(FIFO) ModeSocket // S: Unix域socket ModeSetuid // u: 表示文件具备其建立者用户id权限 ModeSetgid // g: 表示文件具备其建立者组id的权限 ModeCharDevice // c: 字符设备,需已设置ModeDevice ModeSticky // t: 只有root/建立者能删除/移动文件 // 覆盖全部类型位(用于经过&获取类型位),对普通文件,全部这些位都不该被设置 ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice ModePerm FileMode = 0777 // 覆盖全部Unix权限位(用于经过&获取类型位) )
func ReadFile(filename string) ([]byte, error) { f, err := os.Open(filename) if err != nil { return nil, err } defer f.Close()
package main import ( "fmt" "io/ioutil" ) func main() { str := "D:\\go\\project\\src\\gocode\\project01\\test\\test01.go" l1, err := ioutil.ReadFile(str) if err != nil { fmt.Println(err) } else { fmt.Printf("%s", l1) //此处输出类型为字符串 } }
结果和上述相同
package main import ( "bufio" "fmt" "io" "os" ) func main() { str := "D:\\go\\project\\src\\gocode\\project01\\test\\test01.go" file, err := os.Open(str) //此处获取到一个file句柄,一个err错误,如有错误返回,则此处不为nil if err != nil { fmt.Println(err) } else { reader := bufio.NewReader(file) // 此处用于生成一个缓冲,默认是4096,其读取数据选择部分读取 for { str, err := reader.ReadString('\n') // 此处是经过\n进行分割的 if err == io.EOF { // 此处如果io.EOF,则代表其进入文件底部 break } fmt.Println(str) } } defer file.Close() }
结果以下
package main import ( "fmt" "os" ) func main() { str := "D:\\go\\project\\src\\gocode\\project01\\test\\test02.txt" file, err := os.OpenFile(str, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640) if err != nil { fmt.Println(err) } else { _, err := file.WriteString( `package test type student struct { Name string Score float64 } func NewStudent(n string, s float64) *student { return &student{ Name: n, Score: s, } }`) if err == nil { fmt.Println("数据写入成功") } else { fmt.Println("数据写入失败") } } defer file.Close() }
结果以下
package main import ( "bufio" "fmt" "os" ) func main() { str := "D:\\go\\project\\src\\gocode\\project01\\test\\test02.txt" file, err := os.OpenFile(str, os.O_CREATE|os.O_WRONLY, 0777) if err != nil { fmt.Println(err) return } else { writer := bufio.NewWriter(file) for i := 'a'; i <= 'z'; i++ { _, err := writer.WriteString("mysql" + string(i) + "\n") if err != nil { fmt.Println(err) } } err := writer.Flush() if err != nil { fmt.Println(err) } else { fmt.Println("刷新成功") } } }
结果以下
package main import ( "fmt" "io/ioutil" ) func main() { str1 := "D:\\go\\project\\src\\gocode\\project01\\test\\test02.txt" str2 := "D:\\go\\project\\src\\gocode\\project01\\test\\test03.txt" data, err := ioutil.ReadFile(str1) //此处使用读文件,而后再写文件的方式完成对应操做 if err != nil { fmt.Println("read file error", err) return } err = ioutil.WriteFile(str2, data, 0666) if err != nil { fmt.Println(err) } else { fmt.Println("文件写入成功") } }
结果以下
func Copy(dst Writer, src Reader) (written int64, err error)
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。
对成功的调用,返回值err为nil而非EOF,由于Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。若是src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;不然若是dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。
package main import ( "fmt" "io" "os" ) func CopyTest(srcfilepath, desfilepath string) (n int64, err error) { srcfile, err := os.Open(srcfilepath) if err != nil { fmt.Println(err) return } defer srcfile.Close() desfile, err := os.Create(desfilepath) if err != nil { fmt.Println(err) return } defer desfile.Close() return io.Copy(desfile, srcfile) } func main() { srcfilepath := "D:\\go\\project\\src\\gocode\\project01\\test\\test02.txt" desfilepath := "D:\\go\\project\\src\\gocode\\project01\\test\\test04.txt" n, err := CopyTest(srcfilepath, desfilepath) if err != nil { fmt.Println(err) } else { fmt.Println("成功", n) } }
结果以下
结果以下
golang判断文件或文件夹是否存在的方法是使用os.Stat() 函数返回错误进行判断
1 若返回错误为nil,代表文件或文件夹存在
2 若返回类型使用os.IsNotExist() 判断为true,则代表文件或文件夹不存在
3 若返回错误为其余类型,则不肯定是否存在
func PathExists(path string) (bool, error) { _, err := os.Stat(path) //此处返回错误为nil,则表示存在文件或文件夹 if err == nil { return true, nil } if os.IsNotExist(err) { //若此处返回使用此判断为true,则表示为不存在 return false, nil } return false, nil }
os.Args 是一个string的切片,用来存储全部的命令行参数
package main import ( "fmt" "os" ) func main() { param1 := os.Args[1] // 此处表示获取命令行的第一个参数 param2 := os.Args[2] // 此处表示获取命令行的第二个参数 fmt.Printf("第一个参数为:%s,第二个参数为:%s", param1, param2) }
flag包用来解析命令行参数
说明: 前面的方式比较原生,对解析参数不方便,特别是带有指定参数形式的命令行
如 mysql.exe -p 5000 -u root -proot -h localhost ,对于此种形式,Go语言提供了flag包,能够方便解析命令行参数,并且其顺序书写也能够随意。
相关代码以下
package main import ( "flag" "fmt" ) var ( user string password string port int hostip string db string ) func main() { flag.StringVar(&user, "u", "", "用户名,默认为空") flag.StringVar(&password, "p", "", "密码,默认为空") flag.IntVar(&port, "P", 3306, "端口,默认为3306") flag.StringVar(&hostip, "h", "localhost", "远端IP地址,默认为localhost") flag.StringVar(&db, "db", "test", "数据库,默认为test") flag.Parse() // 此处表示方法转换,必须存在 fmt.Printf("user=%v,password=%v,port=%v,hostip=%v,db=%v\n", user, password, port, hostip, db) }
结果以下
package main import ( "encoding/json" "fmt" ) type A struct { Name string `json:"name"` Age int `json:"age"` Sal float32 `json:"sal"` } // 结构体反序列化操做 func SerA() { a := A{ Name: "golang", Age: 13, Sal: 30.00, } data, err := json.Marshal(a) if err != nil { fmt.Println(err) } else { fmt.Printf("%s\n", data) } } //Map 序列化 func SerM() { m := make(map[string]interface{}) m["name"] = "golang" m["age"] = 40 m["sal"] = 100.0 data, err := json.Marshal(m) if err != nil { fmt.Println(err) return } else { fmt.Printf("%s\n", data) } } //切片序列化 func SerS() { var l1 []map[string]interface{} m := make(map[string]interface{}) m["name"] = "golang" m["age"] = 40 m["sal"] = 100.0 m1 := make(map[string]interface{}) m1["name"] = "goland" m1["age"] = 10 m1["sal"] = 200.0 l1 = append(l1, m) l1 = append(l1, m1) data, err := json.Marshal(l1) if err != nil { fmt.Println(err) return } else { fmt.Printf("%s\n", data) } } func main() { SerA() SerM() SerS() }
结果以下
将Json 字符串反序列化成对应的数据类型
package main import ( "encoding/json" "fmt" ) type A struct { Name string `json:"name"` Age int `json:"age"` Sal float32 `json:"sal"` } // 结构体反序列化操做 func SerA() []byte { a := A{ Name: "golang", Age: 13, Sal: 30.00, } data, err := json.Marshal(a) if err != nil { fmt.Println(err) } else { } return data } //Map 序列化 func SerM() []byte { m := make(map[string]interface{}) m["name"] = "golang" m["age"] = 40 m["sal"] = 100.0 data, err := json.Marshal(m) if err != nil { fmt.Println(err) } else { } return data } func DesA(by []byte) A { a := A{} err := json.Unmarshal(by, &a) if err != nil { fmt.Println(err) } else { } return a } func DesM(by []byte) map[string]interface{} { m := make(map[string]interface{}) err := json.Unmarshal(by, &m) if err != nil { fmt.Println(err) } else { } return m } func main() { b := SerA() b1 := DesA(b) fmt.Println(b1) m := SerM() m1 := DesM(m) fmt.Println(m1) }
结果以下
1 在反序列化一个json字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致 2 若是json字符串是经过程序获取的,则不须要对其进行转义和处理