二、面向对象编程
1、类和方法
1.1、简单实例
func main() {
//第一种调用方式a.Less(b)
var a Integer = 1
println("a是否大于b:", a.Less(2))
//第二种调用方式Less(a,b)
println("a是否大于b:", Less(2, 1))
}
type Integer int //定义一个Integer类型
/*
定义比较函数:第一种写法
*/
func (a Integer) Less(b Integer) bool {
return a < b
}
/*
定义比较函数:第二种写法
*/
func Less(a Integer, b Integer) bool {
return a < b
}
1.2、指针
1、Go语言中的面向对象最为直观,也无需支付额外的成本。如果要求对象必须以指针传递,这有时会是个额外成本,因为对象有时很小(比如4字节),用指针传递并不划算。
只有在你需要修改对象的时候,才必须用指针。它不是Go语言的约束,而是一种自然约束。举个例子
func main() {
/*
函数两种区别:
打印结果如下:
LessOne:a= 3
a=: 1
LessOne:a= 4
a=: 4
总结:
加*表示指针,指向对象a可以修改a的值
不加*星号,方法内a修改,外部不修改
*/
//第一种
var a Integer = 1
a.LessOne(2)
println("a=:", a) //输出a=: 1
//第二种
a.LessTwo(3)
println("a=:", a) //输出a=: 4
}
type Integer int //定义一个Integer类型
/*
定义相加函数:第一种写法
*/
func (a Integer) LessOne(b Integer) {
a += b
println("LessOne:a=", a)
}
/*
定义相加函数:第二种写法
*/
func (a *Integer) LessTwo(b Integer) {
*a += b
println("LessOne:a=", *a)
}
2、引用类型
如果b的修改不会影响a的值,那么此类型属于值类型。如果会影响a的值,那么此类型是引用类型。
func main() {
/**
数组类型赋值b=a是将内容完全复制,如果想表达引用使用指针。
例:修改b,a不会改变 修改c,a会改变
*/
var a = [3]int{1, 2, 3}
var b = a
b[1]++
fmt.Println(a, b) //输出:[1 2 3] [1 3 3]
var c = &a
c[1]++
fmt.Println(a, *c) //输出:[1 3 3] [1 3 3]
}
1.3、结构体定义和初始化
- 结构体=类+方法
- 在Go语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如bool类型的零值为false,int类型的零值为0,string类型的零值为空字符串。
- 在Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命名,表示“构造函数”:
func main() {
//初始化
u1 := new(User) //输出:&{ }
u2 := User{} //输出:{ }
u3 := User{"binshao", "18", "男"} //输出:{binshao 18 男}
u4 := User{age: "18", name: "liming"} //输出:{liming 18 }
//使用构造函数初始化
u5 := NewUser("zhangsan", "12", "女") //输出:&{zhangsan 12 女}
fmt.Println(u1)
fmt.Println(u2)
fmt.Println(u3)
fmt.Println(u4)
fmt.Println(u5)
}
type User struct { //自定义类
name string
age string
sex string
}
func (u *User) getIntroduce() string { //定义类中的方法
return "名字:" + u.name + " 年龄:" + u.age + " 性别:" + u.sex
}
func NewUser(name, age, sex string) *User { //定义类的构造函数
return &User{name, age, sex}
}
1.4、可见性
大写字母开头,表示可以被其他包访问到,小写字母只能本包访问。
例如:User类型的成员变量就全部被导出了,可以被所有其他引用了User所在包的代码访问到。
成员方法的可访问性遵循同样的规则,User中getUser()方法只能在该类型的包内使用
type User struct {
Name string
Sex string
}
func (u *User) getUser() string {
return "姓名:" + u.Name + " 性别:" + u.Sex
}
注意:Go语言中符号的可访问性是包一级的而不是类型一级的。在上面的例子中,尽管getUser是User的内部方法,但同一个包中的其他类型也都可以访问到它。这样的可访问性控制很粗旷,很特别,但是非常实用。如果Go语言符号的可访问性是类型一级的,少不了还要加上friend这样的关键字,以表示两个类是朋友关系,可以访问彼此的私有成员。
2、接口
2.1、接口定义与实现
在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口
/*
例如:
IUser、IGetName、IGetSex、IGetAge中的方法被User全部实现
*/
func main() {
/*
尽管User类并没有从这些接口继承,甚至不知道这些接口的存在,但是User类实现了
这些接口,可以进行赋值,如下:
*/
var user1 IUser = new(User)
var user2 IGetName = new(User)
var user3 IGetAge = new(User)
var user4 IGetSex = new(User)
fmt.Println(user1.getName())
fmt.Println(user2.getName())
fmt.Println(user3.getAge())
fmt.Println(user4.getSex())
}
type User struct {
Name string
age int
sex string
}
func (u *User) getName() (name string, err error) {
return u.Name, nil
}
func (u *User) getAge() (age int, err error) {
return u.age, nil
}
func (u *User) getSex() (sex string, err error) {
return u.sex, nil
}
type IUser interface {
getName() (name string, err error)
getAge() (age int, err error)
getSex() (sex string, err error)
}
type IGetName interface {
getName() (name string, err error)
}
type IGetAge interface {
getAge() (age int, err error)
}
type IGetSex interface {
getSex() (sex string, err error)
}
2.2、接口赋值
接口赋值
1、将对象实例赋值给接口;
2、将一个接口赋值给另一个接口。
func main() {
user := User{"wang", 19, "男"}
/*
对象赋值接口
结论:实例赋值接口是使用"&"
*/
var user1 IUser = user //报错
var user2 IUser = &user //成功
/*
接口赋值接口
结论:一个接口赋值给另一个接口时,必须实现另一个接口的所有方法
*/
var user3 IUser = &user //成功
var user4 IGetName = user3 //成功
var user5 IUser = user4 //报错
var user6 IGetAge = user4 //报错
}
2.3、接口类型
在Go语言中,还可以更加直截了当地询问接口指向的对象实例的类型,例如
func main() {
var v interface{} = "字符串"
switch v.(type) {
case int:
println("int类型")
case string:
println("string类型")
default:
println("类型缺省")
}
}