golang入门day3 (pointer + interface + type)

指针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 结构体, 因此 是两份内存)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值