go 面向对象

结构体

创建结构体变量和访问结构体字段

package main

import "fmt"

//创建结构体变量和访问结构体字段
type Person struct {
    Name string
    Age int
}


func main(){
    //方式一
    var p1 Person
    p1.Name="牛魔王"
    p1.Age=24
    fmt.Println(p1)
    //方式二
p2:=Person{"mary",20}
p2.Name="tome"
p2.Age=18
fmt.Println(p2)
//方式三
var p3 *Person=new (Person)
//因为p3是指针,因此赋值方式
    (*p3).Name="smith"//也可以这样写p3.Name="smith2"
    //fmt.Println(*p3)
    p3.Age=100
    fmt.Println(*p3)
    //方式四
    var person *Person=&Person{}//结构体指针
    //person.Age=10和(*person).Age=88效果一样,因为
    (*person).Name="scott"
    person.Name="scott~~~"
    (*person).Age=88
    person.Age=10
//go编译器底层对person.Name做了转化(*person).Name

    fmt.Println(*person)


}
View Code

结构体在内存中的体现

0.

package main

import "fmt"

//struct类型的内存分配机制
type Person struct{
    Name string
    Age int
}


func main(){
var p1 Person

p1.Age=10
p1.Name="小明"
var p2 *Person=&p1
fmt.Println((*p2).Age)
fmt.Println(p2.Age)
p2.Name="tom~~"
fmt.Printf("p2.Name=%v p1.Name=%v
",p2.Name,p1.Name)
fmt.Printf("p2.Name=%v p1.Name=%v
",(*p2).Name,p1.Name)

fmt.Printf("p1的地址%p
",&p1)
fmt.Printf("p2的地址%p p2的值%
",&p2,p2)
}
View Code

字段

1.在内存中的地址是连续的

package main

import "fmt"

//结构体所有字段在内存中是连续的
type Point struct {
    x int
    y int

}

//
type Rect struct {
    leftUp,rightDown Point


}
type Rect2 struct {
    leftUp,rightDown *Point
}

func main(){
    r1:=Rect{Point{1,2},Point{3,4}}
    //r1的四个int,在内存中是连续分布的
    //打印地址
    fmt.Printf("r1.leftUp.x地址=%p
r1.leftUp.y地址=%p
r1.rightDown.x地址=%p
r1.rightDown.y地址%p
",
        &r1.leftUp.x,&r1.leftUp.y,&r1.rightDown.x,&r1.rightDown.y)

    //r2有两个*Point 这两个*Point类型的本身地址也是连续的
    //但他们指向的地址不一定是连续的
    r2:=Rect2{&Point{10,20},&Point{30,40}}
    //打印本身地址
    fmt.Printf("r2.leftUp本身地址=%p r2.rightDown 本身地址=%p 
",
        &r2.leftUp,&r2.rightDown)
    //打印指向地址
    //他们指向的地址不一定是连续的
    fmt.Printf("r2.leftUp指向地址=%p r2.rightDown指向地址=%p
",
        r2.leftUp,r2.rightDown)
}
View Code

2.结构体是单独定义的类型,与其他类型进行转换时需要有完全相同的字段(名字,个数,类型)

package main

import "fmt"

//结构体是单独定义的类型,与其他类型进行转换时需要有完全相同的字段(名字,个数,类型)
type A struct {
    Num int
}
type B struct {
    Num int
}

func main(){
    var a A
    var b B
    a=A(b)//可以转换
    fmt.Println(a,b)
}
View Code

3.结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是相互可以强转

package main

import "fmt"

//结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是相互可以强转
type Student struct {
    Name string
    Age  int
}

//定义结构体
type Stu Student

type integer int

func main() {
    var stu1 Student
    var stu2 Stu
    stu2 = Stu(stu1)
    fmt.Println(stu1, stu2)

    var i integer=10
    var j int=20
    j=int(i)
    fmt.Println(i,j)
}
View Code

4.struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和饭序列化

package main

import (
    "encoding/json"
    "fmt"
)
//struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和饭序列化
type Monster struct {
    Name string `json:"name"`//`json:"name"`就是struct tag
    Age int `json:"age"`
    Skill string `json:"skill"`

}
func main(){
    //1.创建一个Moster变量
monster:=Monster{"牛魔王",500,"芭蕉扇"}
//2.将Master变量序列化为json格式字串
//json.Marsha1 函数中使用反射
jsonStr,err:=json.Marshal(monster)
if err!=nil{
    fmt.Println("json 处理错误",err)
}
//
fmt.Println("jsonStr",string(jsonStr))

}
View Code

 匿名结构体

//匿名结构体
func main() {
    //匿名函数
    res := func(a, b float64) float64 {
        return math.Pow(a, b)
    }(2, 3)
    fmt.Println(res)
    //匿名结构体
    addr := struct {
        province, city string
    }{"陕西省", "西安市"}
    fmt.Println(addr)
    cat := struct {
        name, color string
        age         int8
    }{
        name:  "绒毛",
        color: "黑白",
        age:   1,
    }
    fmt.Println(cat)
}
View Code

结构体匿名字段

//结构体匿名字段
//同一类型的字段只有一个
type User struct {
    string
    byte
    int8
    float64
}

func main() {
    //实例化结构体
    user := User{
        "Steven",
        'm',
        35,
        177.5,
    }
    fmt.Println(user)
    //数据依次打印
    fmt.Printf("姓名:%s
",user.string)
    fmt.Printf("年龄:%d
",user.int8)
    fmt.Printf("身高:%.2f
",user.float64)
    fmt.Printf("性别:%c
",user.byte)
}
View Code

方法

1.结构体是值类型,遵循值类型的传递机制

a

//结构体类型是值类型,如果改变结构体的变量,可以通过结构体的指针来实现


type Circle struct {
    radius float64
}
func (c Circle) area() float64{
    return 3.14*c.radius*c.radius
}
func (c *Circle) area2() float64{
    (*c).radius=10//等价于c.radius=10
    return 3.14*c.radius*c.radius
}
func main(){
    var c Circle
    c.radius=4.0
    res:=c.area()
    fmt.Println("面积是",res)
    //修改结构体的字段

    res2:=c.area2()
    fmt.Println(res2)

}
View Code

b

2.使用结构体指针改变结构体变量

3.自定义struct可以有方法,int,float32也可以有方法

package main

import "fmt"

//自定义struct可以有方法,int,float32也可以有方法
type integer int

func (i integer) print(){
fmt.Println("i=",i)
}

func (i *integer) change(){
    *i+=1
}


func main(){
var i integer=10
    i.print()
i.change()
fmt.Println("i=",i)
}
View Code

 接受者为指针结构体和结构体的区别

//接受者是指针结构体和普通结构体的区别
//如果方法的接受者不是指针,实际只是获取一个拷贝,而不能真正改变接受者(结构体)中原来的数据

type Rectangle struct {
    width, height float64
}

//func
func (r Rectangle) setValue() {
    fmt.Printf("setValue方法中r的地址:%p
", &r)
    r.height = 10
}
func (r *Rectangle) setValue2() {
    fmt.Printf("setValue方法中r的地址:%p
", &r)
    r.height = 20
}

func main() {
    r1 := Rectangle{5, 8}
    r2 := r1
    //打印对象的内存地址
    fmt.Printf("r1的地址:%p
", &r1)
    fmt.Printf("r1的地址:%p
", &r2)
    r1.setValue()
    fmt.Println("r1.height=", r1.height)
    fmt.Println("r2.height=", r2.height)
    r1.setValue2()
    fmt.Println("r1.height=", r1.height)
    fmt.Println("r2.height=", r2.height)

}
View Code

4.访问范围控制规则,方法名首字母小写,只能本包访问

5.如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变凉的String()进行输出

package main

import "fmt"
//如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变凉的String()进行输出
type student struct{
    Name string
    Age int
}

func (stu *student) String() string{
    str:=fmt.Sprintf("Name=[%v] Age=[%v]",stu.Name,stu.Age)
    fmt.Println("
string() 我被调用了
")
return str
}

func main(){
    stu:=student{
        Name:"tom",
        Age:20,
    }

    fmt.Println(&stu)
}
View Code

练习

1.打印矩形

package main

import "fmt"

type MethodUtils struct {


}

func (mu MethodUtils) Print(){
    for i:=1;i<=10;i++{
        for j:=1;j<=8;j++{
            fmt.Print("*")
        }
        fmt.Println()
    }
}

func (mu MethodUtils) Print2(m int,n int){
    for i:=1;i<=m;i++{
        for j:=1;j<=n;j++{
            fmt.Print("*")
        }
        fmt.Println()
    }
}

//计算矩形面积
func (mu MethodUtils) area(len float64,width float64) float64{
    return len*width
}

//判断一个数是奇数还是偶数
func (mu *MethodUtils) JudgeNum(num int){
    if num%2==0{
        fmt.Println(num,"是偶数")
    }else{
        fmt.Println(num,"是奇数")
    }
}
//根据行列,字符打印
func (mu *MethodUtils) Print3(n int,m int,key string){
    for i:=1;i<=n;i++{
        for j:=1;j<=m;j++{
            fmt.Print(key)
        }
        fmt.Println()
    }
}
func main(){
    var mu MethodUtils
    mu.Print()
    mu.Print2(5,5)
    fmt.Println(mu.area(4,3))
    mu.JudgeNum(4)
    mu.Print3(3,4,"~")
}
View Code

2.计算

package main

import "fmt"

type Cacuator struct {
    Num1 float64
    Num2 float64
}

func (calcuator *Cacuator) getSum() float64 {
    return calcuator.Num1 + calcuator.Num2
}

func (calcuator *Cacuator) getSub() float64 {
    return calcuator.Num1 - calcuator.Num2
}

//方式二
func (calcuator *Cacuator) getRes(operator byte) float64 {
    res := 0.0
    switch operator {
    case '+':
        res = calcuator.Num1 - calcuator.Num2
    case '-':
        res = calcuator.Num1 - calcuator.Num2
    case '*':
        res = calcuator.Num1 * calcuator.Num2
    case '/':
        res = calcuator.Num1 / calcuator.Num2
    default:
        fmt.Println("运算符输入错误")
    }
    return res
}
View Code

3.值拷贝和地址拷贝

package main

import "fmt"

type Person struct {
    Name string
}
//决定值拷贝还是地址拷贝看这个方法和哪个类型绑定
func test01(p Person) {
    fmt.Println(p.Name)
}

func test02(p *Person) {
    fmt.Println(p.Name)
}

//对于方法,接受者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
func (p Person) test03() {
    p.Name = "jack"
    fmt.Println("test03()=", p.Name)
}
func (p *Person) test04() {
    p.Name = "mary"
    fmt.Println("test03()=", p.Name)
}

func main() {
    p := Person{"tom"}
    test01(p)
    //对于普通函数,接受者为值类型时,不能将指针类型的数据直接传递,反之亦然
    test02(&p)

    p.test03()
    fmt.Println("main() p.name=", p.Name)
    (&p).test03() //从形式上传入地址,但本质上仍是值拷贝
    fmt.Println("main() p.name=",p.Name)

    (&p).test04()
    fmt.Println("main() p.name=",p.Name)
    p.test04()//等价于(&p).test04(),从形式上石传入值类型,但本质上仍是地址拷贝
}
View Code

应用案例

1.

package main

import (
    "fmt"
)

type Student struct {
    name string
    gender string
    age int
    id int
    score float64
}

func (student *Student) say() string{
    infoStr:=fmt.Sprintf("student 的信息name=[%v],gender=[%v],age=[%v],id=[%v],score=[%v]",
        student.name,student.gender,student.age,student.id,student.score)
    return infoStr
}


func main(){
var stu=Student{
    name:"tom",
    gender:"male",
    age:18,
    id:1000,
    score:99.98,
}
fmt.Println(stu.say())
}
View Code

2.计算面积

package main

import "fmt"

type Box struct {
    len    float64
    width  float64
    height float64
}

func (box *Box) getVolumn() float64 {
    return box.len * box.width * box.height
}

func main(){
    var box Box
    box.len=1.1
    box.width=2.0
    box.height=3.0
    volumn:=box.getVolumn()
    fmt.Printf("体积为=%2.f",volumn)
}
View Code

3.门票

package main

import "fmt"

//景区门票案例
//根据年龄判断价额
type Visitor struct{
    Name string
    AGe int
}

func (visitor *Visitor) showPrice(){
    if visitor.AGe>=90||visitor.AGe<=8{
        fmt.Println("考虑到安全,就不要玩了")
        return
    }

    if visitor.AGe>18{
        fmt.Printf("游客的名字为%v 年龄为%v 收费为20元
",visitor.Name,visitor.AGe)
    }else{
        fmt.Printf("游客的名字为%v 年龄为%v 免费
",visitor.Name,visitor.AGe)
    }
}

func main(){
var v Visitor
for{
    fmt.Println("请输入你的名字")
    fmt.Scanln(&v.Name)
    if v.Name=="n"{
        fmt.Println("退出程序")
    }
    fmt.Println("请输入你的年龄")
    fmt.Scanln(&v.AGe)
    v.showPrice()
}

}
View Code

创建结构体实例时,指定字段的值

package main

import "fmt"

//创建结构体实例时,指定字段的值

type Stu struct{
    Name string
    Age int
}

func main(){
    //实例化时赋初始值
    var stu1=Stu{"小明",19}
    stu2:=Stu{"小强",20}
    //下面这种方法可以不考虑顺序
    var stu3=Stu{
        Name: "jack",
        Age:  20,
    }
    stu4:=Stu{
        Age:30,
        Name:"mary",
    }
    fmt.Println(stu1,stu2,stu3,stu4)

    //方式二
    var stu5 *Stu=&Stu{"小王",20}
    stu6:=&Stu{"小王",39}
    var stu7=&Stu{
        Name:"小东",
        Age:22,

    }
    stu8:=&Stu{
        Age:15,
        Name:"rose",
    }

    fmt.Println(*stu5,*stu6,*stu7,*stu8)
}
View Code

工厂模式

mian/main.go

package main

import (
    "fmt"
    "oop/10.5.3/model"
)
//引入首字母小写的字段和方法
func main(){
    var stu=model.NewStudent("tom",88.8)
    fmt.Println(*stu)
    fmt.Println("name=",stu.Name,"score=",stu.Score)
}
View Code

model/student.go

package model

type student struct{
    Name string
    Score float64
}
func NewStudent(n string,s float64) *student{
    return &student{
        Name:n,
        Score:s,
    }
}
View Code

 面向对象三大特性

 面向对象-抽象

package main

import "fmt"

type Account struct{
    AccountNo string
    Pwd string
    Balance float64
}


//存款
func (account *Account) Deposite(money float64,pwd string){
    if pwd!=account.Pwd{
        fmt.Println("你输入的密码不正确")
        return
    }

    if money<=0{
        fmt.Println("你输入的金额不正确")
        return
    }
    account.Balance+=money
    fmt.Println("存款成功")
}

//取款
func (account *Account) WidthDraw(money float64,pwd string){
    if pwd!=account.Pwd{
        fmt.Println("你输入的密码不正确")
        return
    }
    if money<=0||money>account.Balance{
        fmt.Println("金额不正确")
        return
    }
    account.Balance-=money
    fmt.Println("取款成功")
}
//查询余额
func (account *Account) Query(pwd string){
    if pwd!=account.Pwd{
        fmt.Println("密码不正确")
        return
    }
    fmt.Printf("你的账号为%v,余额为%v",account.AccountNo,account.Balance)
}


func main(){
    account:=Account{
        AccountNo:"gs111",
        Pwd:"666666",
        Balance:100.0,

    }

    account.Query("666666")
account.Deposite(200.0,"666666")
}
View Code

面向对象-封装

 model/person.go

package model



import "fmt"

type person struct {
    Name string
    age int
    sal float64
}
//工厂模式,相当于构造函数
func NewPerson(name string) *person{
    return &person{
        Name:name,
    }
}

func (p *person) SetAge(age int){
    if age>0&&age<150{
        p.age=age
    }else{
        fmt.Println("年龄范围不正确")
    }
}
func (p *person) GetAge() int{
    return p.age
}
func (p *person) SetSal(sal float64){
    if sal>=3000&&sal<=30000{
        p.sal=sal
    }else{
        fmt.Println("范围不正确")
    }
}

func (p *person) GetSal() float64{
    return p.sal
}
View Code

main/main.go

package main

import (
    "fmt"
    "oop/11.3.6/model"
)

func main(){
    p:=model.NewPerson("smith")
    p.SetAge(18)
    p.SetSal(5000)
    fmt.Println(p)
    fmt.Println(p.Name,"age=",p.GetAge(),"sal=",p.GetSal())
}
View Code

 继承

基础版

package main

import "fmt"

//继承
type Pupil struct{
    Name string
    Age int
    Score int
}
//显示成绩
func (p *Pupil) ShowInfo(){
    fmt.Printf("学生名=%v 年龄=%v 成绩=%v
",p.Name,p.Age,p.Score)
}

func (p *Pupil) SetScore(score int){
    p.Score=score
}

func (p *Pupil) testig(){
    fmt.Println("考试中...")
}


type Graduate struct {
    Name string
    Age int
    Score int
}
func (p *Graduate) ShowInfo(){
    fmt.Printf("学生名=%v,年龄=%v,成绩=%v",p.Name,p.Age,p.Score)
}

func (p *Graduate) SetScore(score int){
    p.Score=score
}

func (p *Graduate) testing(){
    fmt.Println("大学生正在考试中。。")
}


func main(){
    var pupil=&Pupil{
        Name:  "tom",
        Age:   10,
    }
    pupil.testig()
    pupil.SetScore(90)
    pupil.ShowInfo()
}
View Code

改进版

package main

import (
    "fmt"
)

//改进版
type Student struct {
    Name string
    Age int
    Score int
}
func (stu *Student) ShowInfo(){
    fmt.Printf("学生名=%v,年龄=%v,成绩=%v
",stu.Name,stu.Age,stu.Score)
}
func (stu *Student) SetScore(score int){
    stu.Score=score
}

type Pupil struct {
    Student//潜入了Student匿名结构体
}
func (p *Pupil) testing(){
    fmt.Println("小学生考试中。。。")
}

type Graduate struct {
    Student
}

func (p *Graduate) testing(){
    fmt.Println("大学生正在考试中。。。")
}


func main(){
    pupil:=&Pupil{}
    pupil.Student.Name="tom~"
    pupil.Student.Age=8
    pupil.testing()
    pupil.Student.SetScore(70)
    pupil.Student.ShowInfo()


    graduate:=&Graduate{}
    graduate.Student.Name="mary~"
    graduate.Student.Age=28
    graduate.testing()
    graduate.Student.SetScore(90)
    graduate.Student.ShowInfo()

}
View Code

a

package main

import (
    "fmt"
)

type A struct {
    Name string
    age int
}

func (a *A) SayOk(){
    fmt.Println("A SayOk",a.Name)
}

func (a *A) hello(){
    fmt.Println("A hello",a.Name)
}

type B struct{
    A
}
func main(){
    var b B
    b.A.Name="tom"
    b.A.age=19
    b.A.hello()
    //也可以如下简化
    b.Name="smith"
    b.age=20
    b.SayOk()
    b.hello()
}
View Code

组合

type D struct {
  a   A
}

var d D
d.a.Name="jack"
View Code

 例

package main

import "fmt"

type Goods struct{
    Name string
    Price float64

}

type Brand struct{
    Name string
    Address string

}
type TV struct {
    Goods
    Brand

}

type TV2 struct {
    *Goods
    *Brand
}

func main(){
    tv:=TV{Goods{"电视机001",5000.99},Brand{"海尔","山东"}}
tv2:=TV{
    Goods: Goods{
        Price:5000.99,
        Name:"电视机002",
    },
    Brand: Brand{
            Name:"夏普",
            Address:"北京",
    },
}

fmt.Println("tv",tv)
fmt.Println("tv2",tv2)
tv3:=TV2{&Goods{"电视机003",7000.99},&Brand{"创维","河南"}}

tv4:=TV2{&Goods{Name:"电视机004",Price:9000.88},&Brand{Name:"长虹",Address:"四川"}}
fmt.Println("tv3",*tv3.Goods,*tv3.Brand)
fmt.Println("tv4",*tv4.Goods,*tv4.Brand)


}
View Code

 类型作为字段

package main

import "fmt"

type Monster struct {
    Name string
    Age int
}

type E struct{
    Monster
    int
    n int
}

func main(){
    var e E
    e.Name="狐狸精"
    e.Age=300
    e.int=20
    e.n=40
    fmt.Println("e=",e)
}
/*
1.一个结构体同类型的匿名字段只能有一个。
2.如果需要多个,则必须给int字段指定名字

*/
View Code

 接口

初识

package main

import "fmt"

//接口

type Usb interface{
    Start()
    Stop()
}

type Phone struct{

}

func (p Phone) Start(){
    fmt.Println("手机开始工作了")
}

func (p Phone) Stop(){
    fmt.Println("手机停止工作了")
}

type Camera struct {

}

func (c Camera) Start(){
    fmt.Println("相机开始工作。。。")
}

func (c Camera) Stop(){
    fmt.Println("相机停止工作。。。")
}

type Computer struct{

}

func (c Computer) Working(usb Usb){
    usb.Start()
    usb.Stop()
}

func main(){
    computer:=Computer{}
    phone:=Phone{}
    camera:=Camera{}
    computer.Working(phone)
    computer.Working(camera)
}
View Code

结构体本身不创建实例,但可以指向一个实现了该接口的自定义类型变量

package main

import "fmt"

//结构体本身不创建实例,但可以指向一个实现了该接口的自定义类型变量
type AInterface interface{
    Say()
}
type Stu struct{
    Name string
}
func (stu Stu) Say(){
    fmt.Println("stu say()")
}


func main(){
    var stu Stu//结构体变量实现了Say(),实现了AInterface
    var a AInterface=stu
    a.Say()
}
View Code

 一个自定义类型可以实现多个接口

package main

import "fmt"

//一个自定义类型可以实现多个接口
type Ainterface interface{
    Say()
}
type BInterface interface {
    Hello()
}

type Monster struct{

}
func (m Monster) Hello(){
    fmt.Println("Monster Hello()~~")
}
func (m Monster) Say(){
    fmt.Println("Monster Say()~~")
}
func main(){
    var monster Monster
    var a2 Ainterface=monster
    var b2 BInterface=monster
    a2.Say()
    b2.Hello()
}
View Code

接口中不能有变量

实现接口时,继承的接口也必须实现

内置方法排序

package main

import (
    "fmt"
    "math/rand"
    "sort"
)

//最佳实践
type Hero struct{
    Name string
    Age int

}
type HeroSlice []Hero

func (hs HeroSlice) Len() int{
    return len(hs)
}

func (hs HeroSlice) Less(i,j int)bool{
    return hs[i].Age<hs[j].Age
}

func (hs HeroSlice) Swap(i,j int){
    hs[i],hs[j]=hs[j],hs[i]
}
type Student struct{
    Name string
    Age int
    Score float64
}

func main(){
    var intSlice=[]int{0,-1,10,7,90}
    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),
        }
        heroes=append(heroes,hero)
    }
    for _,v:=range heroes{
        fmt.Println(v)
    }

    //调用sort
    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)
}
View Code

 接口实现多态

package main

import "fmt"

//接口体现多态的两种形式,多态参数,多态数组
type Usb interface{
    Start()
    Stop()
}
type Phone struct {
    name string
}

func (p Phone) Start(){
    fmt.Println("手机开始工作。。。")
}

func (p Phone) Stop(){
    fmt.Println("手机停止工作")
}

type Camera struct {
    name string
}

func (c Camera) Start(){
    fmt.Println("相机开始工作。。。")
}

func (c Camera) Stop(){
    fmt.Println("相机停止工作...")
}

func main(){
    var usbArr[3]Usb
    usbArr[0]=Phone{"vivo"}
    usbArr[1]=Phone{"小米"}
    usbArr[2]=Phone{"尼康"}
    fmt.Println(usbArr)
}
View Code

鸭子类型

如果一个动物,走起来像鸭子,叫起来像鸭子,那么它就是鸭子

package main

import "fmt"

//鸭子类型
//定义接口类型
type ISayHello interface {
    SayHello() string
}

//
type Duck struct {
    name string
}
type Person struct {
    name string
}

func (d Duck) SayHello() string {
    return d.name + "叫 ga ga ga"
}
func (p Person) SayHello() string {
    return p.name + "说 Hello"
}

func main() {
    //定义实现接口的对象
    duck := Duck{"yaya"}
    person := Person{"Steven"}
    fmt.Println(duck.SayHello())
    fmt.Println(person.SayHello())
    fmt.Println("-------")
    //定义接口类型的变量
    var i ISayHello
    i = duck
    fmt.Printf("%T,%v,%p 
", i, i, &i)
    fmt.Println(i.SayHello())
    i = person
    fmt.Printf("%T,%v,%p
", i, i, &i)
    fmt.Println(i.SayHello())
}
View Code

类型断言

由于接口是一般类型,不知具体类型,就需要使用类型断言

1

package main

import "fmt"

//解决将接口变量赋值给自定义类型的变量
//类型断言
type Point struct {
    x int
    y int
}

func main() {
    var a interface{}
    var point Point = Point{1, 2}
    a = point //实现接口
    var b Point
    b = a.(Point)//类型断言
    fmt.Println(b)
}
View Code

2.

package main

import "fmt"

func main(){
    var x interface{}
    var b2 float32=1.1
    x=b2
    y:=x.(float32)
    fmt.Printf("y的类型是%T,值=%v",y,y)
}
View Code

3.类型断言,带检测

package main

import "fmt"

func main(){
    var x interface{}
    var b2 float32=2.1
    x=b2

    if y,ok:=x.(float32);ok{
        fmt.Println("convert success")
    fmt.Printf("type=%T,val=%v",y,y)
    }else{
        fmt.Println("convert fail")
    }
    fmt.Println("继续执行。。。")



    //
}
View Code

最佳实践:结构体断言,执行方法

package main

import (
    "fmt"
)

//类型断言最佳实践1
type Usb interface {
    Start()
    Stop()
}

type Phone struct {
    name string
}

func (p Phone) Start() {
    fmt.Println("手机开始工作。。。")
}

func (p Phone) Stop() {
    fmt.Println("手机停止。。")
}
func (p Phone) Call() {
    fmt.Println("手机 在打电话")
}

type Camera struct {
    name string
}

func (p Camera) Start() {
    fmt.Println("相机开始工作。。。")
}

func (p Camera) Stop() {
    fmt.Println("相机停止。。")
}

type Computer struct {
}

func (computer Computer) Working(usb Usb) {
    usb.Start()
    //类型断言,注意体会
    if phone, ok := usb.(Phone); ok {
        phone.Call()
    }
    usb.Stop()
}

func main() {
    var usbArr [3]Usb
    usbArr[0] = Phone{"vivo"}
    usbArr[1] = Phone{"小米"}
    usbArr[2] = Camera{"尼康"}
    //phone 有一个特殊方法,如果是手机需要调用
    var computer Computer
    for _, v := range usbArr {
        computer.Working(v)
        fmt.Println()
    }
}
View Code

 类型断言2:

 判断输入的参数是什么类型

package main

import "fmt"

//类型断言最佳实践2
//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}){
    for index,x:=range items{
        switch x.(type){
        case bool:
            fmt.Printf("第%v个参数是bool类型,值是%v
",index,x)
        case float32:
            fmt.Printf("第%v个参数是float32类型,值是%v
",index,x)
        case float64:
            fmt.Printf("第%v个参数是float64类型,值是%v
",index,x)
        case int,int32,int64:
            fmt.Printf("第%v个参数是int,int32,int64类型,值是%v
",index,x)
        case string:
            fmt.Printf("第%v个参数是string类型,值是%v
",index,x)
        default:
            fmt.Printf("第%v个参数是类型 不确定,值是%v
",index,x)
        }
    }
}


func main(){
    var n1 float32=1.1
    var n2 float64=2.3
    var n3 int32=30
    var name string="tom"
    address:="北京"
    n4:=300
    TypeJudge(n1,n2,n3,name,address,n4)
}
View Code

类型断言3

package main

import "fmt"


type Student struct {

}
//类型断言最佳实践2
//编写一个函数,可以判断输入的参数是什么类型
func TypeJudge(items... interface{}){
    for index,x:=range items{
        switch x.(type){
        case bool:
            fmt.Printf("第%v个参数是bool类型,值是%v
",index,x)
        case float32:
            fmt.Printf("第%v个参数是float32类型,值是%v
",index,x)
        case float64:
            fmt.Printf("第%v个参数是float64类型,值是%v
",index,x)
        case int,int32,int64:
            fmt.Printf("第%v个参数是int,int32,int64类型,值是%v
",index,x)
        case string:
            fmt.Printf("第%v个参数是string类型,值是%v
",index,x)
        case Student:
            fmt.Printf("第%v个参数是Student类型,值是%v
",index,x)
        case *Student:
            fmt.Printf("第%v个参数是*Student类型,值是%v
",index,x)
        default:
            fmt.Printf("第%v个参数是类型 不确定,值是%v
",index,x)
        }
    }
}


func main(){
    var n1 float32=1.1
    var n2 float64=2.3
    var n3 int32=30
    var name string="tom"
    address:="北京"
    n4:=300
    var s1 Student
    var s2 *Student
    TypeJudge(n1,n2,n3,name,address,n4,s1,s2)
}
View Code

记账案例面向过程

package main
import (
    "fmt"
)
func main() {
    //声明一个变量,保存接收用户输入的选项
    key := ""
    //声明一个变量,控制是否退出for
    loop := true

    //定义账户的余额 []
    balance := 10000.0
    //每次收支的金额
    money := 0.0
    //每次收支的说明
    note := ""
    //定义个变量,记录是否有收支的行为
    flag := false
    //收支的详情使用字符串来记录
    //当有收支时,只需要对details 进行拼接处理即可
    details := "收支	账户金额	收支金额	说    明"
    //显示这个主菜单
    for {
        fmt.Println("
-----------------家庭收支记账软件-----------------")
        fmt.Println("                  1 收支明细")
        fmt.Println("                  2 登记收入")
        fmt.Println("                  3 登记支出")
        fmt.Println("                  4 退出软件")
        fmt.Print("请选择(1-4):")
        fmt.Scanln(&key)

        switch key {
            case "1":
                fmt.Println("-----------------当前收支明细记录-----------------")
                if flag {
                    fmt.Println(details)
                } else {
                    fmt.Println("当前没有收支明细... 来一笔吧!")
                }
                
            case "2":
                fmt.Println("本次收入金额:")
                fmt.Scanln(&money)
                balance += money // 修改账户余额
                fmt.Println("本次收入说明:")
                fmt.Scanln(&note)
                //将这个收入情况,拼接到details变量
                //收入    11000           1000            有人发红包
                details += fmt.Sprintf("
收入	%v	%v	%v", balance, money, note)
                flag = true

            case "3":
                fmt.Println("本次支出金额:")
                fmt.Scanln(&money)
                //这里需要做一个必要的判断
                if money > balance {
                    fmt.Println("余额的金额不足")
                    break
                }
                balance -= money
                fmt.Println("本次支出说明:")
                fmt.Scanln(&note)
                details += fmt.Sprintf("
支出	%v	%v	%v", balance, money, note)
                flag = true
            case "4":
                fmt.Println("你确定要退出吗? y/n")
                choice := ""
                for {

                    fmt.Scanln(&choice)
                    if choice == "y" || choice == "n" {
                        break
                    }
                    fmt.Println("你的输入有误,请重新输入 y/n")
                }

                if choice == "y" {
                    loop = false
                }
            default :
                fmt.Println("请输入正确的选项..")        
        }

        if !loop {
            break 
        }
    }
    fmt.Println("你退出家庭记账软件的使用...")
}
View Code

记账案例面向对象

utils/familyAccount.go

package utils
import (
    "fmt"
)

type FamilyAccount struct {
    //声明必须的字段.

    //声明一个字段,保存接收用户输入的选项
    key  string
    //声明一个字段,控制是否退出for
    loop bool
    //定义账户的余额 []
    balance float64
    //每次收支的金额
    money float64
    //每次收支的说明
    note string
    //定义个字段,记录是否有收支的行为
    flag bool
    //收支的详情使用字符串来记录
    //当有收支时,只需要对details 进行拼接处理即可
    details string
}

//编写要给工厂模式的构造方法,返回一个*FamilyAccount实例
func NewFamilyAccount() *FamilyAccount { 

    return &FamilyAccount{
        key : "",
        loop : true,
        balance : 10000.0,
        money : 0.0,
        note : "",
        flag : false,
        details : "收支	账户金额	收支金额	说    明",
    }

} 

//将显示明细写成一个方法
func (this *FamilyAccount) showDetails() {
    fmt.Println("-----------------当前收支明细记录-----------------")
    if this.flag {
        fmt.Println(this.details)
    } else {
        fmt.Println("当前没有收支明细... 来一笔吧!")
    }
} 

//将登记收入写成一个方法,和*FamilyAccount绑定
func (this *FamilyAccount) income() {
    
    fmt.Println("本次收入金额:")
    fmt.Scanln(&this.money)
    this.balance += this.money // 修改账户余额
    fmt.Println("本次收入说明:")
    fmt.Scanln(&this.note)
    //将这个收入情况,拼接到details变量
    //收入    11000           1000            有人发红包
    this.details += fmt.Sprintf("
收入	%v	%v	%v", this.balance, this.money, this.note)
    this.flag = true
}

//将登记支出写成一个方法,和*FamilyAccount绑定
func (this *FamilyAccount) pay() {
    fmt.Println("本次支出金额:")
    fmt.Scanln(&this.money)
    //这里需要做一个必要的判断
    if this.money > this.balance {
        fmt.Println("余额的金额不足")
        //break
    }
    this.balance -= this.money
    fmt.Println("本次支出说明:")
    fmt.Scanln(&this.note)
    this.details += fmt.Sprintf("
支出	%v	%v	%v", this.balance, this.money, this.note)
    this.flag = true
}

//将退出系统写成一个方法,和*FamilyAccount绑定
func (this *FamilyAccount) exit() {

    fmt.Println("你确定要退出吗? y/n")
    choice := ""
    for {

        fmt.Scanln(&choice)
        if choice == "y" || choice == "n" {
            break
        }
        fmt.Println("你的输入有误,请重新输入 y/n")
    }

    if choice == "y" {
        this.loop = false
    }
}


//给该结构体绑定相应的方法
//显示主菜单
func (this *FamilyAccount) MainMenu() {

    for {
        fmt.Println("
-----------------家庭收支记账软件-----------------")
        fmt.Println("                  1 收支明细")
        fmt.Println("                  2 登记收入")
        fmt.Println("                  3 登记支出")
        fmt.Println("                  4 退出软件")
        fmt.Print("请选择(1-4):")
        fmt.Scanln(&this.key)
        switch this.key {
            case "1":
                this.showDetails()
            case "2":
                this.income()
            case "3":
                this.pay()
            case "4":
                this.exit()
            default :
                fmt.Println("请输入正确的选项..")        
        }

        if !this.loop {
            break 
        }

    }
}
View Code

main/main.go

package main
import (
    "go_code/familyaccount/utils"
    "fmt"
)
func main() {

    fmt.Println("这个是面向对象的方式完成~~")
    utils.NewFamilyAccount().MainMenu() //思路非常清晰
}
View Code

客户管理系统

model/customer.go

package model

import (
    "fmt"
)

type Customer struct {
    Id int
    Name string
    Gender string
    Age int
    Phone string
    Email string
}

//获取一个Customer
func NewCustomer(id int, name string, gender string, age int, 
    phone string, email string) *Customer {
    return &Customer{
        Id : id,
        Name : name,
        Gender : gender,
        Age : age,
        Phone : phone,
        Email : email,
    }
}

//获取一个Customer, 不提供id
func NewCustomer2(name string, gender string, age int, 
    phone string, email string) *Customer {
    return &Customer{
        Name : name,
        Gender : gender,
        Age : age,
        Phone : phone,
        Email : email,
    }
}

//返回Customer的信息
func (this *Customer) GetInfo() string {

    info := fmt.Sprintf("%v	%v	%v	%v	%v	%v", this.Id, this.Name, this.Gender, 
        this.Age, this.Phone, this.Email)
    return info
}
View Code

service/customerService.go

package service

import (
    _ "fmt"
    "go_code/customer/model"
)

type CustomerService struct {
    //定义一个客户切片,可以存放客户信息
    customers []*model.Customer
    //定义客户的实际个数
    customerNum int
}

//先创建一个Customer对象,放到 CustomerService的Customers切片中
//作为测试数据
func NewCustomerService() *CustomerService {

    customerService := &CustomerService{}

    //customerService.customers = make([]model.Customer, 1)
    customerService.customerNum = 1
    customer := model.NewCustomer(1, "张三", "",
        10, "999", "zs@sohu.com")
    customerService.customers = append(customerService.customers, customer)

    return customerService
}

//返回客户的信息数组

func (this *CustomerService) List()  []*model.Customer {
    return this.customers
}

//完成添加客户的功能
func (this *CustomerService) Add(customer *model.Customer) bool {

    this.customerNum++
    //这时我们可以这个customer一个id
    customer.Id = this.customerNum
    this.customers = append(this.customers, customer)
    
    return true
    
}


//删除一个客户
func (this *CustomerService) Delete(id int) bool {

    //先根据id去得到该id的客户对应元素下标
    index := this.FindById(id)
    
    if index == -1 {
        return false
    }
    //找到,删除切片对应的index的元素
    this.customers = append(this.customers[:index], 
        this.customers[index+1:]...)
    //this.customerNum--
    return true
    
}

//先根据id去得到该id的客户对应元素下标
//如果找到就返回对应的下标,如果找不到,我们返回-1
func (this *CustomerService) FindById(id int) int {
    index := -1
    for i := 0; i < this.customerNum; i++ {
        if this.customers[i].Id == id {
            index = i
            break
        }
    }
    return index
}
View Code

view/main.go

package main

//引入包
import (
    "fmt"
    "go_code/customer/service"
    "go_code/customer/model"
)

type CustomerView struct {

    //定义一个字段,控制菜单显示是否退出
    loop bool
    //定义一个字段,接收用户输入
    key string
    //定义一个CustomerService 字段,主要用于完成对客户信息的各种操作。
    customerService *service.CustomerService
}

//显示主菜单
//    -----------------客户信息管理软件-----------------
//
//    1 添 加 客 户
//    2 修 改 客 户
//    3 删 除 客 户
//    4 客 户 列 表
//    5 退           出
//
//    请选择(1-5):_

func (this *CustomerView) mainMenu() {

    for {

        fmt.Println("-----------------客户信息管理软件-----------------")
        fmt.Println();
        fmt.Println("                 1 添 加 客 户")
        fmt.Println("                 2 修 改 客 户")
        fmt.Println("                 3 删 除 客 户")
        fmt.Println("                 4 客 户 列 表")
        fmt.Println("                 5 退          出")
        fmt.Print("请选择(1-5):")
        fmt.Scanln(&this.key)

        switch (this.key) {
        case "1":
            this.add()
        case "2":
            //同学们自己加入
        case "3":
            this.delete()
        case "4":
            //调用方法显示客户信息
            this.list()
        case "5":
            this.loop = false
        default:
            fmt.Println("输入错误");
        }

        if !this.loop {
            break
        }

    }
}

//编写一个方法,可以显示客户信息
//    ---------------------------客户列表---------------------------
//    编号  姓名       性别    年龄   电话            邮箱
//     1    张三       男      30     010-56253825   abc@email.com
//     2    李四       女      23     010-56253825    lisi@ibm.com
//     3    王芳       女      26     010-56253825   wang@163.com
//    -------------------------客户列表完成-------------------------

func (this *CustomerView) list() {

    //调用 customerService 获取到 客户信息切片
    customerList := this.customerService.List()

    //显示
    fmt.Println("---------------------------客户列表---------------------------")
    fmt.Println("编号	姓名	性别	年龄	电话	邮箱")

    //遍历
    for i := 0; i < len(customerList); i++ {
        fmt.Println(customerList[i].GetInfo())
    }
    fmt.Println("---------------------------客户列表完成---------------------------")
    
}

//添加客户
//    ---------------------添加客户---------------------
//    姓名:张三
//    性别:男
//    年龄:30
//    电话:010-56253825
//    邮箱:zhang@abc.com
//    ---------------------添加完成---------------------

func (this *CustomerView) add() {

    fmt.Println("---------------------添加客户---------------------")
    fmt.Println("姓名:")
    name := ""
    fmt.Scanln(&name)
    fmt.Println("性别:")
    gender := ""
    fmt.Scanln(&gender)
    age := 0
    fmt.Println("年龄:")
    fmt.Scanln(&age)
    fmt.Println("电话:");
    phone := ""
    fmt.Scanln(&phone)
    fmt.Println("邮箱:");
    email := ""
    fmt.Scanln(&email)

    //根据用户输入,创建一个Customer对象
    customer := model.NewCustomer2(name, gender, age, phone, email)
    
    if(this.customerService.Add(customer)){
        fmt.Println("---------------------添加客户成功---------------------");
    }else{
        fmt.Println("---------------------添加客户失败---------------------");
    }
}

//删除
//    ---------------------删除客户---------------------
//    请选择待删除客户编号(-1退出):1
//    确认是否删除(Y/N):y
//    ---------------------删除完成---------------------

func (this *CustomerView) delete() {

    fmt.Println("---------------------删除客户---------------------")
    fmt.Println("请选择待删除客户编号(-1退出)")
    id := 0
    fmt.Scanln(&id)
    //如果用户输入-1
    if id == -1 {
        return
    }
    fmt.Println("确认是否删除(Y/N):")

    choice := ""
    fmt.Scanln(&choice) // 可以
    
    if choice == "Y" || choice == "y"  {
        
        if this.customerService.Delete(id) {
            fmt.Println("---------------------删除完成---------------------")
        } else {
            fmt.Println("---------------------删除失败,id不存在---------------------")
        }
    }
} 


func main() {

    customerView := CustomerView{
        loop : true,
    }
    customerView.customerService = service.NewCustomerService()
    customerView.mainMenu()
}
View Code

 反射

反射的基本介绍
1.反射可以在运行时,动态获取变量各种信息,比如变量的类型(type),类别(kind)
2.如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段,方法)
3.通过反射可以改变变量的值,可以调用关联的方法
4.使用反射需要import("reflect")

 1.反射的使用场景

package main

import (
    "encoding/json"
    "fmt"
)

//反射的使用场景
type Monster struct{
    Name string `json:"name"`
    Age int `json:"age"`
    Sal float64 `json:"sal"`
    Sex string `json:"sex"`
}

func main(){
    m:=Monster{
        Name: "玉兔精",
        Age:  20,
        Sal:  11663.12,
        Sex:  "female",
    }

    data,_:=json.Marshal(m)

    fmt.Println("json result:",string(data))
}
View Code

2.演示类型断言

package main

import (
    "fmt"
    "reflect"
)

/*
演示对基本数据类型,interface{},reflect.Value),进行反射的基本操作

*/

func reflectTest01(b interface{}){
    //通过反射获取传入的变量的type,kind,值
    //1.先获取到reflect.TypeOf
    rTyp:=reflect.TypeOf(b)
    fmt.Println("rType=",rTyp)
//kind为int
    //2.获取到refect.Value
    rVal:=reflect.ValueOf(b)

    n2:=2+rVal.Int()//转为基础类型int
    fmt.Println("n2=",n2)
    fmt.Printf("rVal=%v,rVal Type=%T
",rVal,rTyp)
    //下面我们将rVal转成interface{}
    iV:=rVal.Interface()
    //将interface{}通过断言转成需要的类型
    num2:=iV.(int)
    fmt.Printf("num2=%v  num2 类型=%T
",num2,num2)
}

//专门研究反射[对结构体的反射]
func reflectTest02(b interface{}){
    //通过反射传入的变量type,kind,值
    //先获取到reflect.Type
    rType:=reflect.TypeOf(b)
    fmt.Println("rType=",rType)
    //2.获取reflect.Value
    rVal:=reflect.ValueOf(b)
    //下面我们将rVal转成interface{}
    iV:=rVal.Interface()
    fmt.Printf("iV=%v iv type=%T
",iV,iV)
    //将interface{}通过断言转成需要的类型
    //这里,使用带检测的类型断言
    stu,ok:=iV.(Student)
    if ok{
        fmt.Printf("stu.Name=%v
",stu.Name)
    }
}
type Student struct {
    Name string
    Age int
}

func main(){
//var num int=100
//reflectTest01(num)
//2.定义一个student的实例
stu:=Student{
    Name:"tom",
    Age:20,
}
reflectTest02(stu)
}
View Code

3.改变传入变量的值

package main

import (
    "fmt"
    "reflect"
)

/*
改变传入变量的值


*/
func testInt(b interface{}){
    //获取reflect.ValueOf的值
    val:=reflect.ValueOf(b)
    fmt.Printf("val type=%T
",val)
    //设置值
    val.Elem().SetInt(110)
    fmt.Printf("val=%v
",val)
}

func main(){
    var num int=20
    testInt(&num)
    fmt.Println("num=",num)
}
View Code

4.指针修改变量和反射修改变量对比

package main

import (
    "fmt"
    "reflect"
)

/*
改变传入变量的值


*/
//方式一
func update1(str *string) {
//传入指针
    fs := reflect.ValueOf(str)
    //通过指针修改值
    fs.Elem().SetString("jack")
}
//方式二
func update2(str *string) {
    //等价于下面的代码
    *str = "rose"
}
func main() {
    var str string = "tom"
    update1(&str)
    fmt.Printf("update1()=%v
", str)
    update2(&str)
    fmt.Printf("update2()=%v
", str)
}
View Code

5.结构体使用反射获取方法,字段

package main

import (
    "fmt"
    "reflect"
)

/*
使用反射来遍历结构体字段,调整结构体的方法,并获取结构体标签的值

*/
//
type Monster struct {
    Name  string `json:"name"`
    Age   int   `json:"age"`
    Score float32 `json:"score"`
    Sex   string
}

//返回两个数的和
func (s Monster) GetSum(n1, n2 int) int {
    return n1 + n2
}

//接收四个值,给s赋值
func (s Monster) Set(name string, age int, score float32, sex string) {
    s.Name = name
    s.Age = age
    s.Score = score
    s.Sex = sex
}

//显示s值
func (s Monster) Print() {
    fmt.Println("---start---")
    fmt.Println(s)
    fmt.Println("---end---")
}

func TestStruct(a interface{}) {
    //获取reflect.Type类型
    typ := reflect.TypeOf(a)
    //获取reflect.Value
    val := reflect.ValueOf(a)
    //获取到a对应的类别
    kd := val.Kind()
    fmt.Printf("kind type=%T,kd=%v
",kd,kd)//结构类别
    fmt.Printf("a type=%T,a=%v
",a,a)//具体定义类型
    fmt.Printf("%v
",reflect.Struct)
    //如果传入的不是struct,就退出
    if kd != reflect.Struct {
        fmt.Println("expect struct")
        return
    }
    //获取该结构体有几个字段
    num := val.NumField()
    fmt.Printf("struct has %d fields
", num) //4
    //遍历结构体所有字段
    for i := 0; i < num; i++ {
        fmt.Printf("Field %d:值为=%v
", i,val.Field(i))
        //获取到结构体的标签,注意前面通过reflect.Type来获取tag标签的值
        tagVal := typ.Field(i).Tag.Get("json")
        //如果该字段于tag标签就显示,否则就不显示
        if tagVal != "" {
            fmt.Printf("Field %d:tag为=%v
", i, tagVal)
        }
    }
    //获取该结构体有多少个方法
    numOfMethod := val.NumMethod()
    fmt.Printf("struct has %d methods
", numOfMethod)
    //var params []reflect.Value
    //方法的排序默认是按照函数名的排序(ASCII码)
    val.Method(1).Call(nil) //获取到第三个方法调用它
    //调用结构体的第一个方法 Method(0)
    var params []reflect.Value //声明了 []reflect.Value
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    res := val.Method(0).Call(params) //传入的参数是 []reflect.Value 返回[] reflect.Value
    fmt.Println("res=", res[0].Int()) //返回结果是[]reflect.Value

}

func main() {
    var a Monster = Monster{
        Name:  "黄鼠狼精",
        Age:   400,
        Score: 30.8,
    }
    TestStruct(a)
}
View Code

6.

 7.

8.

9.

。。。。

原文地址:https://www.cnblogs.com/huay/p/12145323.html