1.1.1. 一、Golang 语言面向对象编程说明。
- Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。
- Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解 Golang 是基于 struct 来实现 OOP 特性的。
Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的方法重载、构造函数和析构函数、隐藏的 this 指针等等。
Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。
Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口 (interface)关联,耦合性低,也非常灵活。也就是说在 Golang 中面向接口编程是非常重要的特性。
1.1.2. 二、struct的定义和使用。
定义struct的语法:
type 结构体名 struct {
属性名1 类型
属性名2 类型
// 其他属性和方法
}
1、如果结构体的名字首字母大写,其他包可以使用这个结构体。
2、如果结构体内的属性名首字母大写,表示这个属性可以在其他包使用。
示例如下:
// 定义一个结构体,名字叫Person
type Person struct {
Name string
age int
Height float32
}
func main() {
// 声明一个Person类型变量
var p1 Person
// 没有赋值默认零值
fmt.Println(p1) // { 0 0}
// 属性赋值
p1.Name = "张三"
p1.age = 18
p1.Height = 1.9
fmt.Println(p1) //{张三 18 1.9}
//取出属性值
fmt.Println(p1.Name,p1.age,p1.Height) //张三 18 1.9
}
说明:
- 结构体的属性名,首字母大写,相当于其他语法的public。表示这个属性可以在其他包使用。
- 声明结构体变量时,结构体内的属性会被赋予默认值(零值)。如果是指针,slice,和 map 的零值都是 nil ,即还没有分配空间。指针,slice,和 map 使用时要先make(类似于new),否则报错。
1.1.3. 三、结构体是值类型。传递是值传递。
例子如下:p1是结构体,p2 := p1
//这里是值传递。相当于拷贝了一份。改变p2的属性值,并不能改变p1的属性值。
// 定义一个结构体,名字叫Person
type Person struct {
Name string
age int
}
func main() {
// 声明一个Person类型变量
var p1 = Person{"张三",18}
p2 := p1 //这里是值传递。相当于拷贝了一份。
p2.Name = "p2李四"
fmt.Println(p1.Name) // 张三
fmt.Println(p2.Name) // p2李四
}
1.1.4. 四、创建结构体变量的4种方式。
下面例子中的结构体Person:
type Person struct {
Name string
age int
}
1、直接声明:var p1 Person
func main() {
var p1 Person
p1.Name = "张三"
p1.age = 18
fmt.Println(p1)
}
2、用大括号。p3 := Person{}
func main() {
var (
p1 = Person{"张三",18} // 顺序赋值全部属性。类型为 Person
p2 = Person{Name:"李四"} //只赋值Name属性
p3 = Person{}// 不赋值任何属性
p4Point = &Person{"王5", 2} // 类型为Person指针 *Person
)
// {张三 18} {李四 0} { 0} &{王5 2}
fmt.Println(p1, p2, p3, p4Point)
}
3、声明结构体的指针。
var p1Point = &Person{"王5", 22}
var p2Point = new(Person)
func main() {
var p1Point = &Person{"王5", 22} // 类型为Person指针 *Person
var p2Point = new(Person) // 类型为Person指针 *Person
//给指针指向的Person赋值
(*p2Point).Name = "拉登" // 简化为:p2Point.Name = "拉登"
fmt.Println(p1Point) // &{王5 2}
fmt.Println(p2Point) // &{拉登 0}
//查询值
p1Age := (*p1Point).age // 简化为:p1Age := p1Point.age
fmt.Println(p1Age) //22
}
说明:
因为go语言的底层做了处理,所以上面的Person指针对属性的修改、查询可以简化如下:
(*p2Point).Name = "拉登" 简化为 p2Point.Name= "拉登"
p1Age := (*p1Point).age 简化为 p1Age := p1Point.age
1.1.5. 五、结构体的注意事项。
1、结构体的所有字段在内存中是连续的。
也就是结构体中属性变量本身的地址,是连续分配的。
2、结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的属性(名字、个数和类 型)。
也就是如果把一个结构体的实例强制转换为另一个类型的结构体,需要他们的属性完全相同(属性名字、个数和类 型)。否则编译报错,不能强制转换。
type A struct {
Name string
}
type B struct {
Name string
}
func main() {
var a = A{"张三"}
b := B(a) //强制转换为B类型
fmt.Println(b)// {张三}
}
3、结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转。
type Student struct {
Name string
}
type B Student //B类型是新的数据类型,只是和Student类型有相同的结构
func main() {
var stu = Student{"张三"}
//var b1 B = stu 编译报错,B类型是新的数据类型。不能直接赋值。
b := B(stu) //可以强制转换为B类型
fmt.Println(b)// {张三}
}
4、struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。
示例:在属性类型后面加个字符串。这个字符串就是tag。
type Student struct {
Name string `json:"ageTest"` //`json:"age"` 就是tag
Age int `json:"age"`
Sex string `json:"sex"`
}
func main() {
var stu = Student{"张三",20,"男"}
// json.Marshal() 函数使用反射读取tag,把stu转换为json。
str,err := json.Marshal(stu)
if err != nil {
fmt.Println("json转换错误")
}else {
////如果tag是这种格式 json:"age" ,后面的age就是对应的json字符串的属性名
// 转换json后: {"ageTest":"张三","age":20,"sex":"男"}
fmt.Println("转换json后:",string(str))
}
}