20200326 指针结构体方法与接口
昨日回顾
// swtich 数组切片 map
// 1 switch (作为if else的另一种尝试)
/*
swtich 变量 {
case 1,5:
代码
case 2,6:
代码
defult:
代码
}
swtich {
case 条件:
代码
case 条件:
代码
Fallthrough
defult:
代码
}
*/
//2 数组
-内存中连续的存储空间,数组大小是固定的
-var a [8]int=[8]int{1,2}
-var a [8]int=[8]int{1:99}
-a[2]
-数组是值类型
-数组长度 len
-数组的遍历(两种)
-for i,v:=range a{
---i:索引
---v:值
}
-多维数组
-var a [2][3]int=[2][3]int{{1,2,3},{4,5,6}}
-a[0][1]
//3 切片:本身不存储数据,对底层数组的引用
-基于底层数组创建
var a [8]int=[8]int{1,2}
b:=a[:]
b:=make([]int,3,5)
b:=[]int{1,2}
-长度和容量
-使用 b[100] //程序执行,如果越界,就报错
-引用类型
-追加 append
-循环 range
-多维切片
-切片的数据结构 a[4]
{
指向数组的指针
长度
容量、
}
//4 map:key value存储
-定义 var a map[key的类型]value的类型
-var a map[key的类型][]map[int]string
-var a map[key的类型]对象
-a[key]
-可能为空值,不会报错
-判断key是否存在
if _,ok:=a[999];!ok{
fmt.Println("不存在")
}
-map的长度 len
-循环map
for k,v:=range a{
k:key
v:value
}
-map是引用类型 (空值为nil,当参数传递会修改原来的值)
-相等性(只能跟nil比较)
//补充:自动化运维
-资产收集20台服务器
-项目日志:echars
-批量执行命令,批量安装软件,代码自动发布,自动下线
go语言进阶
一. 指针 &*
指针是一种存储 变量内存地址 的变量
- 取一个变量的地址: & 取地址符号
- 如果在类型前面加*,表示指向这个类型的指针
- 在指针变量前加*,表示解引用(反解),通过地址拿到值
1. 指针的定义
func main(){
var b=156
// 把b内存地址赋值给a这个指针
// 取一个变量的地址: & 取地址符号
// 如果在类型前面加*,表示指向这个类型的指针
var a *int = &b
// var c *string =&b int类型不能赋值给字符串类型的指针
// a :=&b
fmt.Println(a)
}
2.指针的零值
var b int
fmt.Println(b) // nil
3. 指针的解引用
拿到地址指向的值
var b = 156
a:=&b
fmt.Println(a)
// 指针a指向的值: 解引用(反解)
fmt.Println(*a)
4. 向该函数传递指针参数
func(){
var b = 126
fmt.Println(b) //156
test(&b)
fmt.Println(*b) //166
}
func test(b *int){
//解引用,然后操作 b是一个指针
*b++ //指针传过来地址,修改了原来的数据
fmt.Println(*b) //166
}
5. 不要向函数传递数组的指针,而应该使用切片
- 切片较数组内存占用小
- 传递类型不会因数组大小而更改
func main() {
var a[3]int = [3]int{3,4,5}
//写一个函数,传入数组,改掉原来的数组
test(&a) // 传入变量的地址
// 向函数传递切片 (推荐使用切片)
test1(a[:])
}
//*[3]int 类型的指针
func test(x *[3]int){
//x传入的是指针
//x[1]=100 // 支持指针自动操作,不会报错,内部自动处理
(*x)[1]=100 // 先解引用,再改值
fmt.Println(x) //x是一个指向数组的指针,地址 : 打印出 &[3 100 5]
}
func test1(x []int){
x[0] = 100
fmt.Println(x)
}
6. go 不支持指针运算
//var b =156
//a := &b
////不允许指针运算
//a++
7. 指向指针的指针
var b int=156
var a *int=&b
var c **int
c = &a
fmt.Println(c)
var d ***int
d = &c
fmt.Println(d)
8. 数组指针与指针数组
- 数组指针: 指向数组的指针
- 指针数组: 数组里面放指针
//数组指针
var a[3]int =[3]int{1,2,3}
var b *[3]int = &a
fmt.Println(b)
//指针数组
var x,y,z=12,13,14
var b [3]*int = [3]*int{&x,&y,&z}
fmt.Println(b)
二. 结构体 type struct
go语言中的结构体,类似于面向对象语言中的类
- go中的面向对象: 继承,封装,多态
- 一系列属性的集合
1.结构体定义
定义一个结构体
type 结构体名字 struct {
属性1 类型
属性2 类型
// 大小写表示公有与私有
type Person struct {
name string
age int
sex int
}
2.结构体的使用
func main(){
p :=Person{} //相当于实例化得到有一个对象
var p Person =Person{} //结构体的类型就是 Person
fmt.Println(p)
fmt.Println(p.name)
}
type Person struct {
name string
age int
sex int
}
3. 结构体的零值
var p Person// 值类型,零值是属性的零值
p.name="jack"
fmt.Println(p)
4. 定义并初始化(传入属性)
var p Person=Person{}
按位置传
var p Person=Person{"jack",15,1}
按关键字传(位置可以乱,有几个值就要传几个值)
var p Person=Person{name:"jack",age:15,sex:2}
fmt.Println(p)
fmt.Println(p.name)
5. 创建匿名结构体
没有名字,也没有type关键字,定义在函数体内部
只使用一次,把一堆属性放到一个变量中
a := struct {
name string
age int
sex int
}{age:18}
fmt.Println(a.age)
6. 结构体的指针
var p Person=Person{sex:1,age:12}
var p1 *Person=&Person{sex:1,age:12} //指针
var p1 *Person // 结构体空值是nil
fmt.Println(p1)
7. 匿名字段
- 字段没有名字
- 做变量提升,提升字段:面向对象中的继承
// 按位置
var p Person2=Person2{"lqz",18}
//按关键字
var p Person2=Person2{int:18,string:"jack"}
fmt.Println(p)
//匿名字段
type Person2 struct {
string
int
sex int // 匿名与有名可以套用
}
8.嵌套结构体(结构体中套结构体)
// 按位置
var p Person3=Person3{"jack",12,Hobby{1,"蓝蓝"}}
//按关键字
var p Person3=Person3{name:"abc",hobby:Hobby{}}
fmt.Println(p)
//把hobby的id设置2,name设置为篮球
p.hobby.name="篮球"
p.hobby.id = 2
fmt.Println(p)
//结构体嵌套
type Hobby struct {
id int
name string
}
type Person3 struct {
name string
age int
hobby Hobby
}
9.字段提升(只把不重复的字段提升)
// 按位置
var p Person4=Person4{"jack"}
//按关键字(面向对象的继承)
var p Person4=Person4{name:"jack",Hobby2:Hobby2{}}
//Hobby中的字段被提升了(可以直接.)
p.hobby_id=2
p.hobby_name="chenglong"
p.Hobby2.hobby_id=3
// go中通过匿名字段和结构体嵌套实现面向对象的继承
--------------------------------
// 字段提升
type Hobby2 struct {
hobby_id int
hobby_name string
}
type Person4 struct {
name string
sex int
age int
Hobby2
}
10. 导出结构体(大小写)和字段(大小写)
11.结构体相等性
- 结构体是值类型。
- 如果它的每一个字段都是可比较的,则该结构体也是可比较的。
- 如果两个结构体变量的对应字段相等,则这两个变量也是相等的
- 如果结构体包含不可比较的字段,则结构体变量也不可比较。
三. 方法 fnuc (t type)
python: 什么是方法(绑定给对象,类,自动传值),什么是函数
go: 方法是绑定给结构体的: 结构体只有属性 + 方法 = 类
1. 方法的定义
func 关键字和方法名之间 加入了一个特殊的接收器类型
func (t type) 方法名(){}
2. 使用方法
p :=Person4{"jack",28,1}
//自动传值
p.printName()
// 有了函数,为什么还要方法
效果一样,但是调用方式,以及函数必须传值等
//定义一个结构体
type Person4 struct {
name string
age int
sex int
}
//给结构体绑定方法
func (p Person4)printName(){
fmt.Println(p.name)
}
//普通函数
func printName(p Person4) {
fmt.Println(p.name)
}
3. 指针接收器与值接收器
值接收器,不会修改原来的
指针接收器才会修改原来的
p:=Person4{"jack",18,1}
p.changeName("engo") //值接收器
//(&p).changeName("engo") //值接收器,也可以
p.changeAge(1111) // 指针接收器
//(&p).changeAge(1111) // 指针接收器,没有区别
fmt.Println(p)
// 不管是值类型接收器,还是指针类型接收器,都可以用值和指针来调用
//3. 指针接收器与值接收器
func (p Person4)changeName(name string) {
p.name=name
fmt.Println(p)
}
//指针类型接收器的修改年龄
func (p *Person4)changeAge(age int) {
//(*p).age=age //解引用(正统的修改)
p.age=age // 可以直接修改
fmt.Println(p)
}
4. 那么什么时候使用指针接收器,什么时候使用值接收器
指针接收器也可以被使用在如下场景:当拷贝一个结构体的代价过于昂贵时
5. 匿名字段的方法
p :=Person5{}
//匿名字段的方法也会提升
p.hobbyName="篮球"
p.printName() // 继承.子类没有printName方法,就会调用父类的
//指定调用父类的printName方法(面向对象的super())
p.Hobby1.printName()
//匿名字段的方法
type Hobby1 struct {
id int
hobbyName string
}
type Person5 struct {
name string
age int
sex int
Hobby1
}
//Hobby1 的绑定方法
func (h Hobby1)printName(){
fmt.Println(h.hobbyName)
}
//Person5 的绑定方法
func (p Person5)printName(){
fmt.Println(p.name)
}
6. 在方法中使用值接收器 与 在函数中使用值参数
- 方法: 指针和值都可以来调用
- 函数 只能传值
7. 在方法中使用指针接收器 与 在函数中指针参数
- 方法: 指针和值都可以来调用
- 函数 只能传递指针
8.在非结构体上的方法
//像给int类型绑定个add方法可以吗?
//var i int =10
//i.add() // 不行
// 但是可以给传统数据类型重命名,然后绑定方法
var i Myint=10
i.add()
i.add()
i.add()
fmt.Println(i)
type Myint int //把int类型重命名为Myint
func (i *Myint)add(){
(*i)=(*i)+1 // 指针类型 解引用
}
四. 接口 type 名字 interface
interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。
简单的说:
- interface是方法的集合
- interface是一种类型,并且是指针类型
- interface的更重要的作用在于多态实现
1. 定义
定义一个对象的行为,一系列方法的集合
规范了子类应该有哪些行为,类似abc模块
django中: 父类写了一个发布方法,没有d代码.只有一个raise, 子类继承,但是没有重写该方法,一调用就报错
type 接口名称 interface {
method1 (参数列表) 返回值列表
method2 (参数列表) 返回值列表
...
}
2. 简单使用
- 接口的使用不仅仅针对结构体,自定义类型、变量等等都可以实现接口。
- 如果一个接口没有任何方法,我们称为空接口,由于空接口没有方法,所以任何类型都实现了空接口。
- 要实现一个接口,必须实现该接口里面的所有方法。
package main
import "fmt"
//1.定义了一个鸭子接口(run speak方法)
type DuckInterface interface {
run()
//run(a int)int // 有参数,有返回值
speak()
}
//写一个唐老鸭结构体,实现该接口
type TDuck struct {
name string
age int
wife string
}
//实现接口(只要结构体绑定了接口中的所有方法,就叫做结构体实现了该接口)
func (t TDuck)run() { //传参的话,接口也必须定义
fmt.Println("我是唐老鸭",t.name,"我会走路")
}
func (t TDuck)speak() {
fmt.Println("我是唐老鸭",t.name,"我会说话")
}
//写一个肉鸭结构体
type RDuck struct {
name string
age int
}
func (t RDuck)run() { //传参的话,接口也必须定义
fmt.Println("我是肉鸭",t.name,"我会走路")
}
func (t RDuck)speak() {
fmt.Println("我是肉鸭",t.name,"我会说话")
}
func main() {
//实例化
var t TDuck=TDuck{"嘤嘤怪",17,"刘亦菲"}
var r RDuck=RDuck{"怪五",18}
//唐老鸭的run
t.run()
//肉的run
r.run()
//定义一个鸭子接口类型(肉鸭和唐老鸭都实现了鸭子接口,所以都可以把对象赋值给鸭子接口)
var i1 DuckInterface
i1 = TDuck{"嘤嘤怪",17,"刘亦菲"}
var i2 DuckInterface
i2 = RDuck{"怪五",18}
//分别实现run方法
i1.run()
i2.run()
}