指针pointer
pointer 存储了一个变量内存的地址
package main
import "fmt"
func main(){
a:=1
var p1 *int = &a
println(p1)
var p2 *int //nil 空指针
fmt.Println(p2)
println(p2)
}
指针的赋值 , 指针指向的内容 也可以用 C语言方式进行改变
同样 *int **int ***int …
数组指针: 指向数组的指针
指针数组: 存放指针的数组
函数指针: 指向函数的指针 ,函数名就是函数指针
指针函数: 返回指针的函数 eg:
func fun1()* [4]int //返回一个数组指针
结构体:聚合类型
Go 语言抛弃了 class 和 继承 , 只保留了strict 和 组合
创建 :type name struct{ };
p2 := name{ };
pr := Person{ name :”老王“ , age: 40, sex: ‘woman’ }
结构体 :值类型的数据:深拷贝
怎么浅拷贝呢? 可以用指针进行赋值就可以了
new 函数 :开辟内存空间 ,返回值是一个指针
new() 在 go语言中是 函数 ,专门创建内存,返回指针的 函数
make 、new 区别
make 用于 内建类型(slice , map, channel)的内存分配 , new用于各种内存的分配;
new(T) 函数 分配了T类型的 零值进行内存填充, 并返回其地址
而 内建函数make(T,args) 创建一个 make(T,args)返回一个非零的T类型, 而不是 *T类型。
从本质上讲: 导致 make 和 new两个函数 结果不同的原因 是因为创建之后的初始化
p2 := new(int)
p2 是一个指针 指向 0
匿名结构体: 相当于内部类(意义不大, 没必要用)
结构体中的成员 也称为字段
如果在 struct 中 采用 匿名字段 ,其实是用类型名进行字段命名的 ,因此含有匿名字段的 结构体, 不能含有相同类型的字段。
结构体嵌套 要注意: 赋值的时候 注意 结构体是值类型(深拷贝)
那么 我们应该看需要定义 类型指针 / 类型 对象, 防止没必要的内存创建/ 无意修改
Go 语言可以模拟的 OOP思想,但Go语言不是 面向对象的语言
GO语言 :没有 面向对象的 三大特性,, 因为 Go语言不含 class 对象 , 但是我们可以用 struct 进行模拟
一 : 匿名结构体 模拟实现 继承
不定义成定义成 匿名结构体 不就成组合了吗?我感觉这样,到时候不就成一个对象了吗?对吧? 暂时先这样理解试试吧 ? 但是好像跟C中组合不太一样
package main
type Person struct{
name string
age int
}
type Student struct{
Person // 匿名结构体 模拟实现 继承
school string
}
func main(){
s1 := new(Student)
s1.name = "海绵宝宝"
s1.age = 18
s1.school = "北京大学"
println(s1.name," ", s1.age," ", s1.school)
s2 := Student{Person{"派大星",17}, "XPU"}
println(s2.name," ",s2.age," ", s2.school)//' ' 不能表示空格代表32ASCII
s3 := Student{Person:Person{name:"擎天柱",age:14}, school:"地球"}
println(s3.name," ",s3.age," ", s3.school)
s4 := Student{Person{"大黄蜂",13}, "泰斯坦"}
println(s4.name," ",s4.age," ",s4.school)
}
匿名对象的 字段 称为提升字段 , 可以通过对象直接访问,并修改
方法 vs 函数
方法: 包含了调用者的 函数称为方法:简单说 包含对象的函数称为方法
方法 和 函数的 定义非常相似: 方法 func关键字后、方法名前,加上 接收器的类型 ,方法必须通过接受者来调用,谁调用谁就是接收者, 方法的接收者可以在方法的内部 进行访问
为什要有方法啊??
方法和函数的意义不一样 :
1 方法 必须有调用者,是某个类别的功能; 函数 只是一段代码 ,随时可以调用
2 方法名可以相同, 只要接收者(调用者)不同就可以, 而 函数名不能冲突
3 方法可以模拟实现 多态(真不愧一个爹搞出来的)
Go 模拟 method 继承
如果一个 匿名字段(匿名结构体)实现了一个method方法,那么包含这个匿名字段的结构体也可以调用该匿名字段的方法
同时 外层struct的方法,匿名字段/内层字段 struct 是可以对其进行重写的(C++叫重定义, JAVA 叫重写)
内层字段 类似于 C++的内部类 ,内部字段可以访问外层函数/方法
方法 = 调用者 + 函数
interface 接口
在面向对象世界中: 接口定义对象的行为 , 表示对象能做什么,实现行为的细节是针对对象的 (感觉像多态)
在Go 语言中: 接口 interface 是一组方法签名,类型为接口中的所有方法提供定义 就是在 实现接口, 与OOP思想很相似。 接口指定了 类型应该具有的方法,而类型 决定了如何去实现这些方法。
总之:
Go语言 接口:定义了具有共性的一组方法,任何其他类型只要实现了这些方法 就是实现了这个接口。
接口定义了一组方法: 如果某个对象实现了这个接口的所有方法,那么此对象就实现了该接口。
来看看 具体实现: 真的是多态的变通啊哈哈哈 ~
来看我实现的代码
package main
//USB 接口
type USB interface{
start()// 接口定义一组具有共性的方法
end()
}
// 鼠标
type Mouse struct {
name string
}
//U盘
type FlashDisk struct {
name string
}
//Mouse 的实现接口
func (m Mouse)start(){
println("鼠标就绪,可以使用,点点点")
}
func (m Mouse)end(){
println("鼠标结束工作 ,退出啦")
}
//U盘 实现接口
func(f FlashDisk)start(){
println("闪迪准备就绪,可以开始传输数据了...")
}
func(f FlashDisk)end(){
println("传输数据结束,U盘拔出")
}
//test测试函数 : 普通函数,只进行测试(完美转发)
func testInterfae(usb USB){
usb.start()
usb.end()
}
func main(){
m1 := Mouse{"鼠标"}
println(m1.name)
m1.start()
m1.end()
f1 :=FlashDisk{"闪迪"}
println(f1.name)
f1.start()
f1.end()
//testInterfae(m1)
//testInterfae(f1)
}
原理就是这么个原理: 但我写的代码的耦合性太高了 ,比如: 名字应该通过对象传递,而非直接在方法内定义。
需要注意的是 : 1 由接口实现类型对象赋值 得到的接口对象,不能访问原有的“一些”字段。
2 接口可以向上访问, 但是 不能向下访问(e g: 接口 不可以访问对象的name 等 ,但 对象却可以访问接口的方法)
接口的类型
接口的语法:
1 如果一个函数 接收接口类型作为参数,那么可以接收该接口任意实现类型的对象作为参数,甚至可以将不同实现类对象放在接口数组里。
2 定义一个对象为接口类型对象, 那么可以用任意实现类的对象给其赋值
鸭子类型
长的像鸭子, 会游泳 ,会嘎嘎叫 ~
针对动态语言来说 ,是一种类型推断策略,更关注对象如何被使用。、,并不是类型本身。
虽然Go 语言是弱类型语言,采用非侵入式的方式实现接口。(非侵入式就是 : 接口的 声明 和 实现 分离)
,提高代码的服用,减少代码的耦合。 、
空接口: 可以当作泛型使用,非常方便
还可以配合 map 使用 map[srting] interface{}
package main
import "fmt"
type A interface {
}
type Cat struct{
name string
}
type Dog struct{
name string
}
//空接口 接收任意类型数据
func test1(a A){
fmt.Println(a)
//println(a)可能会打印成地址
}
func test2(a interface{}){
fmt.Println(a)
}
func main(){
var c1 A = Cat{"小老虎"}
c := Cat{"小猫咪"}
d := Dog{"王二狗"}
var A = 4
var B = 3.14
test1(c1)
test1(c)
test1(d)
test1(A)
test1(B)
test2(c1)
test2(c)
test2(d)
test2(3.14)
test2(6)
m1 := make(map[string]interface{})
m1["name"] ="隔壁老王"
m1["age"] =18
m1["sex"] ="man"
fmt.Println(m1)
}
接口允许继承 ,更允许多继承
接口的继承 用嵌套来模拟实现
嵌套的接口 想使用 必须实现所有 被嵌套的接口内的方法
接口的断言
Go语言的类型断言 是对接口实行的操作
package main
import (
"fmt"
"math"
)
type Circle struct{
path float64
}
type Square struct{
path int
}
type AA interface {
Area()
CLong()
}
func (s Square)Area(){
fmt.Println("Square Area is:",s.path * s.path)
}
func (s Square)CLong(){
fmt.Println("Square Long is:",s.path * 4)
}
func (c Circle)Area(){
fmt.Println("Circle Area is:",c.path * c.path * math.Pi)
}
func (c Circle)CLong(){
fmt.Println("Circle long is:",c.path * 2 * math.Pi)
}
func getAssert(a AA){
if ret,ok :=a.(Circle); ok{
fmt.Println("it is Circle, path is",ret.path)
}else if ret, ok := a.(Square);ok{
fmt.Println("it is Square, path is ",ret.path)
}else if ret,ok :=a.(*Circle); ok{
fmt.Println("it is Circle, path is",(*ret).path)
}else if ret,ok :=a.(*Square); ok{
fmt.Println("it is Square, path is ",ret.path)
}else{
fmt.Println("i don't know what type a is")
}
}
func testAssert2(a AA){
switch ret := a.(type){ //a.(type) 只能用在 switch 语句中
case Circle:
fmt.Println("Circle",ret.path)
case Square:
fmt.Println("Square",ret.path)
case * Circle:
fmt.Println("Circle",ret.path)
case * Square:
fmt.Println("Square",ret.path)
default:
fmt.Println("i don't what type it is")
}
}
func testAssert(a AA){
getAssert(a)
a.Area()
a.CLong()
}
func main(){
c := Circle{4}
s := Square{4}
d := &Circle{3}
e := &Square{3}
testAssert2(e)
testAssert(d)
testAssert(c)
testAssert(s)
testAssert2(e)
testAssert2(d)
testAssert2(c)
testAssert2(s)
}
type
type 可以自定义一种类型 , 比如将 int 定义为 myint
但在语法上,myint类型 和int类型 不相同 ,一次不能相互赋值。 但可以前值类型转换后进行赋值。
给类型区别名 : type newname = typename
例如 type myInt = int 那么 myInt 就是 int 的别名
来看一个 别名的不同表现吧 :
package main
import "fmt"
type people struct{
name string
}
type person = people
type puiple struct {
person
people
}
func(p person) show1(){
fmt.Println(p.name)
}
func (p people) show2(){
fmt.Println(p.name)
}
func testShow(a puiple){
fmt.Println(a.people.name)
fmt.Println(a.person.name)
}
func main(){
var s puiple
s.person.name ="小明"
s.people.name ="明明"
println(s.person.name)
println(s.people.name)
// testShow(s)
}
/*
type myInt int
type INT = int
func main(){
var a int =4
var a1 myInt = 5
// a = a1// 不能隐式类型转换 ,但是可以强制类型转换
a = int (a1)
println(a)
var cc INT = 10
fmt.Printf("%T\n",cc)
}
*/
虽然 person是people 的别名, 但看起来好像是给一个对象开了两份空间来进行存储的 , 我们来进行验证一下
果然: 是两份内存(其实也很好理解: people/person 结构体中都单独又一个 name字段 , 而puiple结构体,含一个person结构体, 也含有一个 people 结构体, 因此 是两份内存)