结构体
创建结构体变量和访问结构体字段
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) }
结构体在内存中的体现
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) }
字段
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) }
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) }
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) }
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)) }
匿名结构体
//匿名结构体 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) }
结构体匿名字段
//结构体匿名字段 //同一类型的字段只有一个 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) }
方法
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) }
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) }
接受者为指针结构体和结构体的区别
//接受者是指针结构体和普通结构体的区别 //如果方法的接受者不是指针,实际只是获取一个拷贝,而不能真正改变接受者(结构体)中原来的数据 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) }
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) }
练习
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,"~") }
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 }
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(),从形式上石传入值类型,但本质上仍是地址拷贝 }
应用案例
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()) }
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) }
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() } }
创建结构体实例时,指定字段的值
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) }
工厂模式
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) }
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, } }
面向对象三大特性
面向对象-抽象
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") }
面向对象-封装
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 }
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()) }
继承
基础版
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() }
改进版
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() }
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() }
组合
type D struct { a A } var d D d.a.Name="jack"
例
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) }
类型作为字段
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字段指定名字 */
接口
初识
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) }
结构体本身不创建实例,但可以指向一个实现了该接口的自定义类型变量
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() }
一个自定义类型可以实现多个接口
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() }
接口中不能有变量
实现接口时,继承的接口也必须实现
内置方法排序
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) }
接口实现多态
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) }
鸭子类型
如果一个动物,走起来像鸭子,叫起来像鸭子,那么它就是鸭子
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()) }
类型断言
由于接口是一般类型,不知具体类型,就需要使用类型断言
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) }
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) }
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("继续执行。。。") // }
最佳实践:结构体断言,执行方法
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() } }
类型断言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) }
类型断言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) }
记账案例面向过程
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(¬e) //将这个收入情况,拼接到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(¬e) 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("你退出家庭记账软件的使用...") }
记账案例面向对象
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 } } }
main/main.go
package main import ( "go_code/familyaccount/utils" "fmt" ) func main() { fmt.Println("这个是面向对象的方式完成~~") utils.NewFamilyAccount().MainMenu() //思路非常清晰 }
客户管理系统
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 }
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/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() }
反射
反射的基本介绍
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)) }
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) }
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) }
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) }
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) }
6.
7.
8.
9.
。。。。