1.定义结构体
输入:
type Student struct {
Name string
Age int
Addr string
}
func main() {
//结构体定义1
var stu1 Student
fmt.Println("stu1: ", stu1)
//结构体定义2
var stu2 = Student{}
fmt.Println("stu2: ", stu2)
//结构体定义3
var stu3 *Student = new(Student) //x为指针,指针指向的为Student
//var y *Student = &Student{}
(*stu3).Name = "xx"
stu2.Age = 18 //这样也没问题Go语言编译其底层对x.Age进行了处理(*x).Age
fmt.Println("stu3: ", *stu3)
//结构体定义4
var stu4 *Student = &Student{}
fmt.Println("stu4: ", *stu4)
}
输出:
stu1: { 0 }
stu2: { 0 }
stu3: {xx 0 }
stu4: { 0 }
2.结构体之间的相互转化1
输入:
type Student struct {
Name string
}
type Persion struct {
Name string
}
func main() {
var s Student
s.Name = "cc"
var p Persion
p.Name = "xx"
s = Student(p)
fmt.Println("输出转化后的s的值:", s)
p = Persion(s) //引用传递
fmt.Println("输出转化后的p的值:", p)
}
输出:
输出转化后的s的值: {xx}
输出转化后的p的值: {xx}
3.结构体之间的相互转化2
输入:
type Class struct {
Name string
}
type Cla Class
func main() {
var c1 Class
c1.Name = "a"
var c2 Cla
fmt.Println("c1.Name=", c1.Name)
fmt.Println("c2.Name=", c2.Name)
c2 = Cla(c1)
fmt.Println("转换后c1.Name=", c1.Name)
fmt.Println("转换后c2.Name=", c2.Name)
}
输出:
c1.Name= a
c2.Name=
转换后c1.Name= a
转换后c2.Name= a
4.方法与函数的区别
①方法需要绑定数据类型,函数不需要
type Student struct {
Name string
}
// 方法
func (s Student) method1() {
fmt.Println(s.Name)
}
// 函数
func function1(s Student) {
fmt.Println(s.Name)
}
②调用方式不同
函数的调用:函数名(实参列表)
方法的调用:变量.方法名
func main() {
var s Student
s.Name = "xx"
//函数的调用
function1(s)
//方法的调用
s.method1()
}
③对于函数,传入参数类型严格指定,对于方法可以将值当作指针传入
type Student struct {
Name string
}
// 方法1
func (s Student) method1() {
fmt.Println(s.Name)
}
// 方法2
func (s *Student) method2() {
fmt.Println(s.Name)
}
// 函数1
func function1(s Student) {
fmt.Println(s.Name)
}
// 函数2
func function2(s *Student) {
fmt.Println(s.Name)
}
func main() {
var s Student
s.Name = "xx"
//函数的调用
function1(s)
//function1(&s) 这样传会报错
//function2(&s) 正确方式
//方法的调用
s.method1()
(&s).method1() //不会报错,虽然是指针类型调用,但还是值传递
s.method2() //不会报错,虽然是值类型调用,但还是引用传递
}
5.方法的调用
输入:
type Animal struct {
Name string
Food string
}
func (a Animal) Eat() {
//结构体传入的对象为值传递
fmt.Printf("Animal %v Eat %v ", a.Name, a.Food)
}
func main() {
var a Animal
a.Name = "Cat"
a.Food = "Fish"
a.Eat()
}
输出:
Animal Cat Eat Fish
6.通过指针在方法中实现引用传递
输入:
type Animal struct {
Name string
}
//& 运算符用于获取变量的地址,而 * 运算符用于通过指针访问该地址处的值
func (a *Animal) Eat() {
(*a).Name = "Dog"
fmt.Println((*a).Name)
}
func main() {
var a Animal
a.Name = "Cat"
(&a).Eat()
fmt.Println(a.Name)
}
输出:
Dog
Dog
代码精简:
输入:
type Animal struct {
Name string
}
func (a *Animal) Eat() {
a.Name = "Dog"
fmt.Println(a.Name)
}
func main() {
var a Animal
a.Name = "Cat"
a.Eat()
fmt.Println(a.Name)
}
输出:
Dog
Dog
7.方法使用注意事项
(1)方法的访问控制规则:
方法名 首字母小写 只能在本包中使用,方法名首字母大写,可以在本包和其他包访问!
(2)如果一个类型实现了String()方法,那么fmt.Println默认会调用这个变量的String()方法
输入:
type Student struct {
Name string
Age int
}
func (s Student) String() string {
sprintf := fmt.Sprintf("Student Name: %v ,Student Age: %v ", s.Name, s.Age)
return sprintf
}
func main() {
var stu Student
stu.Name = "xx"
stu.Age = 10
fmt.Println(stu)
}
输出:
Student Name: xx ,Student Age: 10
8.跨包创建结构体实例
package model
type Student struct {
Name string
Age int
}
package main
import (
"fmt"
"project/0625/model"
)
func main() {
stu := model.Student{
Name: "xx",
Age: 18,
}
fmt.Println(stu)
}
注意:同方法访问规则类似
结构体名 首字母小写 只能在本包中使用,结构体名首字母大写,可以在本包和其他包访问!
9.解决首字母小写,跨包访问问题(类似JAVA中的构造方法)
输入:
package model
type Student struct {
Name string
Age int
}
func CreateStudent(n string, a int) *Student {
return &Student{n, a}
}
package main
import (
"fmt"
"project/0625/model"
)
func main() {
stu := model.CreateStudent("xx", 18)
fmt.Println(stu)
fmt.Println(*stu)
}
输出:
&{xx 18}
{xx 18}
10.封装
输入:
package model
import "fmt"
type Student struct {
Name string
age int
}
func NewStudent(name string, age int) *Student {
return &Student{
Name: name,
age: age,
}
}
func (stu *Student) SetAge(age int) {
if age > 0 && age < 100 {
stu.age = age
} else {
fmt.Println("你输入的age有误")
}
}
func (stu *Student) GetAge() int {
return stu.age
}
package main
import (
"fmt"
"project/0626/model"
)
func main() {
stu := model.NewStudent("xx", 18)
fmt.Println(*stu)
stu2 := model.Student{Name: "cc"}
stu2.SetAge(18)
fmt.Println(stu2.GetAge())
fmt.Println(stu2)
}
输出:
{xx 18}
18
{cc 18}
11.继承
输入:
package main
import "fmt"
// 定义父结构体
type Animal struct {
Age int
Weight float32
}
// 定义父结构体对应的方法
func (a *Animal) Eat() {
fmt.Println("我是父结构体对应的方法")
}
// 定义子结构体
type Dog struct {
Animal //继承父结构体
Breed string //品种
}
// 定义子结构体对应的方法
func (d *Dog) Move() {
fmt.Printf("我的品种是%v ,我今年%v岁了", d.Breed, d.Age)
}
func main() {
dog := &Dog{
Animal: Animal{
Age: 20,
Weight: 10.99,
},
Breed: "中华田园犬",
}
dog.Animal.Eat()
dog.Move()
}
输出:
我是父结构体对应的方法
我的品种是中华田园犬 ,我今年20岁了
注意:结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,都可以使用。
// 定义父结构体
type Animal struct {
Age int
weight float32
}
// 定义父结构体对应的方法
func (a *Animal) eat() {
fmt.Println("我是父结构体对应的方法")
}
12.匿名结构体字段、方法访问支持简化
//其他代码同上
dog.Animal.Eat() //结构体支持简化,等同于dog.Eat()
13.就近访问原则
当结构体与匿名结构体有相同的字段、方法时,访问这些字段、方法采取就近原则
输入:
package main
import "fmt"
// 定义父结构体
type Animal struct {
Age int
Weight float32
}
// 定义父结构体对应的方法
func (a *Animal) Eat() {
fmt.Println("我是父结构体对应的方法")
}
// 定义子结构体
type Dog struct {
Animal //继承父结构体
Age int
Weight float32
Breed string //品种
}
func (dog *Dog) Eat() {
fmt.Println("我是Dog结构体对应的方法")
}
// 定义子结构体对应的方法
func (d *Dog) Move() {
fmt.Printf("我的品种是%v ,我今年%v岁了", d.Breed, d.Age)
}
func main() {
dog := &Dog{
Animal: Animal{
Age: 20,
Weight: 10.99,
},
Age: 999,
Weight: 999.99,
Breed: "中华田园犬",
}
fmt.Println(dog.Age) //就近原则 Age: 999,
dog.Eat()
}
输出:
999
我是Dog结构体对应的方法
14.如果需要访问指定的字段或方法时,选择指定的结构体类型
15.Go支持多继承
一个结构体中有多个匿名结构体
输入:
package main
import "fmt"
type Cat struct {
Name string
}
type Dog struct {
Age int
}
type Animal struct {
Cat
Dog
}
func main() {
animal := Animal{
Cat{
Name: "xx",
},
Dog{
Age: 20,
},
}
fmt.Println(animal.Name)
fmt.Println(animal.Age)
}
输出:
xx
20
16.结构体中的字段可以是基本数据类型
输入:
package main
import "fmt"
type Student struct {
Name string
int
}
type Person struct {
Name string
stu Student
}
/*
*
与
type Person struct {
Name string
Student
}
的区别:这种属于继承
而组合模式中只是三个字段
*/
func main() {
person := Person{
Name: "xx",
stu: Student{
Name: "cc",
int: 1,
},
}
fmt.Println(person.stu.Name)
fmt.Println(person)
}
输出:
cc
{xx {cc 1}}
17.接口引入
输入:
package main
import "fmt"
type GetName interface {
getName()
}
type Panda struct {
name string
}
func (p Panda) getName() {
fmt.Println("panda")
}
type Monkey struct {
name string
}
func (m Monkey) getName() {
fmt.Println("monkey")
}
func name(n GetName) {
n.getName()
}
func main() {
panda := new(Panda)
name(panda)
monkey := new(Monkey)
name(monkey)
}
输出:
panda
monkey
(1)接口中可以定义一组方法,但不需要实现,不需要方法体。并且接口中不能包含任何变量。到某个自定义类型要使用的时候(实现接口的时候),再根据具体情况把这些方法具体实现出来。
(2)实现接口要实现所有的方法才是实现。
(3) Golang中的接口,不需要显式的实现接口。Golang中没有implement关键字。(Golang中实现接口是基于方法的,不是基于接口的)
例如:
A接口a,b方法
B接口a,b方法
C结构体实现了a,b方法,那么C实现了A接口,也可以说实现了B接口﹐(只要实现全部方法即可,和实际接口耦合性很低)
(4)接口目的是为了定义规范,具体由别人来实现即可。
18.接口相关注意事项
输入:
package main
import "fmt"
type GetName interface {
getName()
}
type Panda struct {
name string
}
func (p Panda) getName() {
fmt.Println("panda")
}
type Monkey struct {
name string
}
func (m Monkey) getName() {
fmt.Println("monkey")
}
func name(n GetName) {
n.getName()
}
func main() {
panda := new(Panda)
name(panda)
monkey := new(Monkey)
name(monkey)
//不支持直接通过接口创建实例
var g GetName
fmt.Println(g) //输出: <nil>
g = panda //将实现该接口的实例赋值后,可调用接口中的方法
g.getName()
}
输出:
panda
monkey
<nil>
panda
19.一个结构体可以实现多个接口
输入:
package main
import "fmt"
type AgetPro interface {
getAPro()
}
type BgetPro interface {
getBPro()
}
type ProName struct {
Name string
}
func (p *ProName) getAPro() {
fmt.Println("getAPro() " + p.Name)
}
func (p *ProName) getBPro() {
fmt.Println("getBPro() " + p.Name)
}
func main() {
proName := ProName{"xC"}
var apro AgetPro = &proName
var bpro BgetPro = &proName
apro.getAPro()
bpro.getBPro()
}
输出:
getAPro() xC
getBPro() xC
20.多态参数与多态数组
输入:
package main
import "fmt"
type SayName interface {
SayGoodsName()
}
type Goods1 struct {
Name string
}
type Goods2 struct {
Name string
}
func (g Goods1) SayGoodsName() {
fmt.Println(g.Name)
}
func (g Goods2) SayGoodsName() {
fmt.Println(g.Name)
}
func say(s SayName) {
s.SayGoodsName()
}
func main() {
//多态参数
var g1 = Goods1{"xigua"}
say(g1)
var g2 = Goods1{"pingguo"}
say(g2)
//多态数组
var arr [2]SayName
arr[0] = Goods1{"xiangjiao"}
arr[1] = Goods2{"caomei"}
fmt.Println(arr)
}
输出:
xigua
pingguo
[{xiangjiao} {caomei}]
21.断言
一般用于单个结构体的判断,多个结构体的话自我感觉还是用Type Switch
输入:
package main
import "fmt"
type EatFood interface {
FoodName()
}
type Food1 struct {
Name string
}
type Food2 struct {
Name string
}
func (f Food1) FoodName() {
fmt.Println("Food1的名字为:", f.Name)
}
func (f Food1) ClearFood() {
fmt.Println(f.Name, "已经洗过了")
}
func (f Food2) FoodName() {
fmt.Println("Food2的名字为:", f.Name)
}
func eat(e EatFood) {
e.FoodName()
//断言
//判断传过来的e是否为Food1类型,如果是则将其定义赋值给food1
food1, flag := e.(Food1)
if flag {
food1.ClearFood()
}
}
func main() {
food1 := Food1{"黄瓜"}
eat(food1)
food2 := Food2{"番茄"}
eat(food2)
}
输出:
Food1的名字为: 黄瓜
黄瓜 已经洗过了
Food2的名字为: 番茄
22.Type Switch
输入:
package main
import "fmt"
type EatFood interface {
FoodName()
}
type Food1 struct {
Name string
}
type Food2 struct {
Name string
}
func (f Food1) FoodName() {
fmt.Println("Food1的名字为:", f.Name)
}
func (f Food1) ClearFood() {
fmt.Println(f.Name, "已经洗过了")
}
func (f Food2) FoodName() {
fmt.Println("Food2的名字为:", f.Name)
}
func (f Food2) CutName() {
fmt.Println(f.Name, "已经切过了")
}
func eat(e EatFood) {
e.FoodName()
//Type Switch
switch e.(type) {
case Food1:
food1 := e.(Food1)
food1.ClearFood()
case Food2:
food2 := e.(Food2)
food2.CutName()
default:
fmt.Println(" wtf ?")
}
}
func main() {
food1 := Food1{"黄瓜"}
eat(food1)
food2 := Food2{"番茄"}
eat(food2)
}
输出:
Food1的名字为: 黄瓜
黄瓜 已经洗过了
Food2的名字为: 番茄
番茄 已经切过了