Golang是支持面向对象编程特性
。
传统的面向对象编程是怎么样?
拿Java作为例子,定义一个Person类,里面包含了这个Person的一些属性或特征。
public class Person(){
private String name;
private int age;
private String addr;
}
然后根据这个类来创建对象来进行使用。
既然Golang是支持面向对象编程特性,那是使用什么来实现OOP呢?
那就是结构体struct
。
先展示一个struct的样子。
//定义一个Dog的结构体,将Dog的各个字段或属性信息,放入Dog结构体进行管理
type Dog struct {
name string
age int
addr string
}
这时候就不难发现,struct和Java的class的结构是差不多的,那golang的结构体是怎么进行管理的?
//创建一个Dog的变量
var dogo Dog
dogo.name = "小黄"
dogo.age = 2
dogo.addr = "home"
fmt.Println("狗狗的个人简历如下:")
fmt.Println("name=", dogo.name)
fmt.Println("name=", dogo.age)
fmt.Println("name=", dogo.addr)
结合上面的描述,
- 结构体是自定义的数据类型,代表一类事物。(Dog)
- 结构体变量(实例)是具体的,实际的,代表一个具体变量。(dogo)
结束了对比,现在来正式介绍Golang的结构体(struct)
type 结构体名称 struct {
field1 type //字段属性
field2 type
...
}
具体例子上面有描述,这里就不赘述了。
字段是结构体的一个重要组成部分,一般是基本数据类型、数组
,也可以是引用类型
。
type Person struct{
name string
age int
scores [3]float64 //数组
ptr *int //指针
slice []int //切片
mapp map[string]string //map
}
//如果结构体的字段类型是:指针,slice和map的零值都是nil,表示还没有分配空间
//如果要使用,就要先make,才能使用。
func main(){
var per Person
//使用slice ,一定要先用make
per.slice = make([]int, 5)
per.slice[0] = 3
//使用map,一定要先用make
per.mapp = make(map[string]string)
per.mapp["key"] = "aaa"
fmt.Println(per)
}
不同结构体变量(实例)的字段是独立的,互不影响,一个实例的字段改变了,不影响另外一个实例。
type Dog struct {
name string
age int
}
func main(){
//不同实例的字段互不影响
var dog1 Dog
dog1.name = "小黄"
dog1.age = 1
dog2 := dog1 //结构体是值类型,默认是值拷贝
dog2.name = "小白"
fmt.Println("dog1=",dog1) //dog1 = {小黄 1},dog1的name没有受到影响
fmt.Println("dog2=",dog2) //dog2 = {小白 1}
}
在结构体声明后,就要去创建使用,对于结构体的创建使用主要有几个方式:
这里取Dog的结构体为例
type Dog struct {
name string
age int
}
//方式一:
//这个方式就是最基础的直接声明
type doge Dog
//方式二:
doge := Dog{"小黄", 2}
//方式三:
var doge *Dog = new(Dog)
(*doge).name = "aa" //因为doge是一个指针,所以标准的赋值方式
doge.name = "bb" //不过这种方式也支持,因为在底层有对doge.name = "bb"进行处理
(*doge).age = 2
doge.age = 3
fmt.Println(*doge) //返回结构体的指针
//方式四:
var doge *Dog = &Dog{}
(*doge).name = "aa"
doge.name = "bb"
(*doge).age = 2
doge.age = 3
fmt.Println(*doge) //返回结构体的指针
在一些场景下,结构体单单只是属性是不够的,比如像Person结构体除了要有一些属性(name,age),还需要有一些行为:可以说话、奔跑这些,所以这时候结构体就要用到方法。
golang中的方法是作用在指定的数据类型上的(和指定的数据类型绑定),所以自定义类型都可以有方法,不单单是struct。
type C struct{
num int
}
func (c C) test(){
fmt.Println(c.num)
}
这段例子表示:
1. func(c C) test(){} 表示C结构体有一个方法名叫做test()
2. (c C)表示test()方法是和C类型绑定的
具体的例子说明
type Dog struct {
name string
age int
}
func(d Dog) say(){
fmt.Println("say() name = ", d.name)
}
func main(){
var d Dog
d.name = "啊黄"
d.say() // 调用方法
}
上面的代码体现出来几个点:
- say方法和Dog类型绑定。
- say方法只能通过Dog类型的变量来调用,不能直接调用,也不能使用其他类型变量进行调用。
- func(d Dog) say(){}中的d表示哪个Dog变量调用。
方法也可以接收参数,比如
func (d Dog) say(n int){
for i := 1; i<= n; i++{
fmt.Println("第%d次叫",i)
}
}
//调用
d.say(2)
方法也可以返回参数,比如
func (d Dog) cout(n int) int {
res := 0
for i := 1; i<= n; i++ {
res += 1
}
return res
}
//调用
res := d.cout(5)
(1) 结构体类型是值类型,在方法调用中,要遵守值类型的传递机制,是值拷贝传递方式
。
(2) 若想在方法中修改结构体变量的值,可以通过结构体的指针来处理。
//为了提高效率,通常将方法和结构体的指针类型绑定
func (d *Dog) cout() int {
retrun 2
}
//调用
res := d.cout() //等同于res := (&d).cout() ,只是底层做了优化
(3) golang中的方法作用在指定的数据类型上的(也就是和指定的数据类型绑定),所以自定义类型都可以拥有方法,不单单是struct,还可以是int ,float64都可以拥有方法。
func (i integer) print() {
fmt.Println("i=", i)
}
func (i *integer) change(){
*i = *i + 1
}
func main(){
var i integer = 10
i.print()
i.change()
fmt.Println("i=", i)
}
(4) 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。
(5) 如果一个类型实现了String()
这个方法,那么fmt.Println默认会调用这个变量的String()进行输出。
type Student struct {
name string
age int
}
//给*Student实现方法String()
func (stu *Student) String() string {
str := fmt.Sprintf("name=[%v] age=[%v]", stu.name, stu.age)
return str
}
//定义一个Student变量
stu := Student{
name : "jam"
age : 10
}
//如果实现了*Student 类型的String方法,就会自动调用
fmt.Println(&stu)