一、 golang面向对象介绍
1、golang也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。
2、golang没有类(class),golang语言的结合体(struct)和其它编程语言的类有同等的地位。
3、golang面向对象编程,去掉了传统语言面向对象的继承、方法重载、结构函数和析构函数、隐藏的this指针等。
4、goalng仍然有面向对象编程的继承,封装和多态的特性,。
5、golang面向接口编程非常重要
二、结构体创建注意事项
1、字段申明语法同变量
2、字段的类型可以为:基本类型、数组或应用类型
3、在创建一个结构体变量后,如果没有给字段赋值,都对应改类型的默认值(如bool为false,int为0,string为"")
4、不同结构体变量的字段是独立互不影响的,一个结构体变量字段的更改不会影响另一个结构体的值类型。
三、创建struct语法
type 结构体名称 struct{
field type
}
创建例子:
type stru struct{ a1 string a2 int a3 [5]float64 a4 []int //切片 a5 *int //指针 a6 map[string]string //map }
使用例子:
package main import "fmt" func main(){ type ss struct{ Name string Age int } //1、直接申明 var ss1 ss ss1.Name = "张三" ss1.Age = 20 fmt.Println(ss1) //2、使用{} var ss2 ss = ss{"李四",22} fmt.Println(ss2) //3、& var ss3 *ss = new(ss) (*ss3).Name = "王五" (*ss3).Age = 30 fmt.Println(ss3,*ss3) //4、{} var ss4 *ss = &ss{} ss4.Name = "韩梅梅" ss4.Age = 66 fmt.Println(ss4,*ss4) } ###结果### {张三 20} {李四 22} &{王五 30} {王五 30} &{韩梅梅 66} {韩梅梅 66}
四、结构体使用注意事项
1、结构体的所有字段在内存中是连续的
例子:
package main import "fmt" type Point struct { x int y int } type Test1 struct { s1,s2 Point } func main(){ a1 := Test1{Point{1,2},Point{3,4}} //打印地址 fmt.Printf("%p,%p,%p,%p",&a1.s1.x,&a1.s1.y,&a1.s2.x,&a1.s2.y) } ##结果## 0xc4200141e0,0xc4200141e8,0xc4200141f0,0xc4200141f8
2、结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型
例子:
package main import "fmt" type A struct { x int } type B struct { x int } func main(){ var a A var b B b.x = 2 a = A(b) //可以转换,但是结构体的字段和类型必须完全一样 fmt.Println(a,b) } ###结果### {2} {2}
3、结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
例子:
package main import "fmt" func main(){ type A struct { x int } type B A var s1 A var s2 B s2 = B(s1) //强转 fmt.Println(s1,s2) } ##结果## {0} {0}
4、struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化
序列化的使用场景:
例子:
package main import ( "fmt" "encoding/json" ) type A struct { Name string `json:"name"` //`json:"name"`就是struct的tag Age int `json:"age"` } func main(){ s1 := A{"张三",20} //将s1变量序列化为json格式字符串 jsonStr,err := json.Marshal(s1) if err != nil { fmt.Println("json错误处理",err) } fmt.Println(string(jsonStr)) } ###结果### {"name":"张三","age":20}
五、结构体的方法
方法的声明和调用
type A struct {
Num int
}
func (a A) test() {
fmt.Println(a.Num)
}
语法的说明
1) func (a A) test() {} 表示 A 结构体有一方法,方法名为 test
2) (a A) 体现 test 方法是和 A 类型绑定的
例子:
package main import "fmt" type A struct { Name string } //test方法只能公告A类型的变量类调用,不能直接调用,也不能使用其它类型变量来调用 func (a A) test(){ //将test方法绑定到A类型中,a的名字是随意指定的 fmt.Println("test()",a.Name) } func (b A) add(n1,n2 int) int { return n1 + n2 } func main(){ var x A x.Name = "zhang" x.test() fmt.Println(x.add(1,2)) } ####结果#### test() zhang 3
六、golang面向对象的三大特性
1、封装
封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只有通过被授权的操作(方法),才能对字段进行操作封装的好处
1、隐藏实现细节
2、提可以对数据进行验证,保证安全合理
2、封装的实现步骤
1) 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似 private)
2) 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
3) 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值
func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
//加入数据验证的业务逻辑
var.字段 = 参数
}
4) 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值func (var 结构体类型名) GetXxx() {return var.age
例子:
[root@localhostgo_test]#cat src/fz/fz.go package fz import "fmt" type T1 struct { Name string age int //开头是小写,其它包不能直接访问 } func Ss(name string) *T1 { return &T1{ Name:name, } } func (a *T1) Getage() int { fmt.Println(a.age) return a.age } [root@localhostgo_test]#cat class6.go package main import ( "fmt" "fz" ) func main(){ x := fz.Ss("root") fmt.Println(x) x.Getage() } [root@localhostgo_test]#go run class6.go &{root 0} 0
2、继承
继承可以解决代码复用,让我们的编程更加靠近人类思维。
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法
在 Golang 中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性
继承给编程带来的便利
1) 代码的复用性提高了
2) 代码的扩展性和维护性提
嵌套匿名结构体的基本语法
type Goods struct {
Name string
Price int
}
type Book struct {
Goods //这里就是嵌套匿名结构体 Goods
Writer string
}
例子:
package main import "fmt" type A struct { name string age int } func (p *A) Run() { fmt.Println("Run...") } type B struct { A b1 int } type C struct { A } func (p *C) Crun() { fmt.Println(p.name,p.age) } func main(){ var q B q.name = "qq" q.age = 3 q.b1 = 20 q.Run() fmt.Println(q) fmt.Println("==========") var w C w.name = "ww" w.age = 222 w.Run() fmt.Println(&w) } ####结果###### Run... {{qq 3} 20} ========== Run... &{{ww 222}}
3、多态(多态基于接口interface)
介绍:
变量(实例)具有多种形态。面向对象的第三大特征,在 Go 语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态
接口体现多态的两种形式
1、在下面interface的 Usb 接口案例,Usb usb ,即可以接收手机变量,又可以接收相机变量,就体现了Usb接口多态
2、多态数组
例子:给 Usb 数组中,存放 Phone 结构体 和 Camera 结构体变量
package main import ( "fmt" ) //声明/定义一个接口 type Usb interface { //声明了两个没有实现的方法 Start() Stop() } type Phone struct { name string } //让 Phone 实现 Usb 接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作。。。") } func (p Phone) Stop() { fmt.Println("手机停止工作。。。") } type Camera struct { name string } //让 Camera 实现 Usb 接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作。。。") } func (c Camera) Stop() { fmt.Println("相机停止工作。。。") } func main() { //定义一个 Usb 接口数组,可以存放 Phone 和 Camera 的结构体变量 //这里就体现出多态数组 var usbArr [3]Usb usbArr[0] = Phone{"vivo"} usbArr[1] = Phone{"小米"} usbArr[2] = Camera{"尼康"} fmt.Println(usbArr) } #####结果####### [{vivo} {小米} {尼康}]
七、接口interface
简介
1、interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。到某个自定义类型要使用的时候,在根据具体情况把这些方法写出来实现
2、 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低偶合的思想。
3、 Golang 中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang 中没有 implement 这样的关键字
基本语法
例子:
package main import ( "fmt" ) //声明/定义一个接口 type Usb interface { //声明了两个没有实现的方法 Start() Stop() } type Phone struct { } //让 Phone 实现 Usb 接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作。。。") } func (p Phone) Stop() { fmt.Println("手机停止工作。。。") } type Camera struct { } //让 Camera 实现 Usb 接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作。。。") } func (c Camera) Stop() { fmt.Println("相机停止工作。。。") } //计算机 type Computer struct { } //编写一个方法 Working 方法,接收一个 Usb 接口类型变量 //只要是实现了 Usb 接口 (所谓实现 Usb 接口,就是指实现了 Usb 接口声明所有方法) func (c Computer) Working(usb Usb) { //usb 变量会根据传入的实参,来判断到底是 Phone,还是 Camera //通过 usb 接口变量来调用 Start 和 Stop 方法 usb.Start() usb.Stop() } func main() { //测试 //先创建结构体变量 computer := Computer{} phone := Phone{} camera := Camera{} //关键点 computer.Working(phone) computer.Working(camera) } #####结果#### 手机开始工作。。。 手机停止工作。。。 相机开始工作。。。 相机停止工作。。。
使用接口注意事项
1、接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
2、接口中所有的方法都没有方法体,即都是没有实现的方法。
3、在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4、一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
5、只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
6、一个自定义类型可以实现多个接口
7、Golang 接口中不能有任何变量
8、一个接口(比如 A 接口)可以继承多个别的接口(比如 B,C 接口),这时如果要实现 A 接口,也必须将 B,C 接口的方法也全部实现。
9、interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil
10、空接口 interface{} 没有任何方法,所以所有类型都实现了空接口, 即我们可以把任何一个变量赋给空接口
接口最佳实践例子:
package main import ( "fmt" "sort" "math/rand" ) //1.声明 Hero 结构体 type Hero struct{ Name string Age int } //2.声明一个 Hero 结构体切片类型 type HeroSlice []Hero //3.实现 Interface 接口 func (hs HeroSlice) Len() int { return len(hs) } //Less 方法就是决定你使用什么标准进行排序 //1. 按 Hero 的年龄从小到大排序!! func (hs HeroSlice) Less(i, j int) bool { return hs[i].Age < hs[j].Age //修改成对 Name 排序 //return hs[i].Name < hs[j].Name } func (hs HeroSlice) Swap(i, j int) { //交换 hs[i], hs[j] = hs[j], hs[i] } //1.声明 Student 结构体 type Student struct{ Name string Age int Score float64 } //将 Student 的切片,安 Score 从大到小排序!! func main() { //先定义一个数组/切片 var intSlice = []int{0, -1, 10, 7, 90} //要求对 intSlice 切片进行排序 //1. 冒泡排序... sort.Ints(intSlice) fmt.Println(intSlice) //测试看看我们是否可以对结构体切片进行排序 var heroes HeroSlice for i := 0; i < 10 ; i++ { hero := Hero{ Name : fmt.Sprintf("英雄|%d", rand.Intn(100)), Age : rand.Intn(100), } //将 hero append 到 heroes 切片 heroes = append(heroes, hero) } //看看排序前的顺序 for _ , v := range heroes { fmt.Println(v) } sort.Sort(heroes) fmt.Println("-----------排序后------------") //看看排序后的顺序 for _ , v := range heroes { fmt.Println(v) } i := 10 j := 20 i, j = j, i fmt.Println("i=", i, "j=", j) // i=20 j = 10 } ######运行结果###### [-1 0 7 10 90] {英雄|81 87} {英雄|47 59} {英雄|81 18} {英雄|25 40} {英雄|56 0} {英雄|94 11} {英雄|62 89} {英雄|28 74} {英雄|11 45} {英雄|37 6} -----------排序后------------ {英雄|56 0} {英雄|37 6} {英雄|94 11} {英雄|81 18} {英雄|25 40} {英雄|11 45} {英雄|47 59} {英雄|28 74} {英雄|81 87} {英雄|62 89} i= 20 j= 10