在Go语言官网的常见问题解答一栏中,有这样一个问题:“Is Go an object-oriented language?(Go语言是否是一种面向对象语言?)”比较有意思的是,Go官方给出的回答是:“Yes and no.(既是也不是。)”
Go语言中不支持类,但它提供了结构体,Go语言中的结构体和接口实现了面向对象编程的特性。
通过使用结构体,我们可以自定义一系列由相同类型或不同类型的数据构成的数据集合,用来实现较复杂的数据结构。
结构体由一系列成员变量构成,这些成员变量对应着实体不同的属性。
GO语言中通过关键字type定义自定义类型,结构体定义需要使用type和struct关键字。
type 结构体名 struct {
成员变量1 类型1
成员变量2 类型2
成员变量3 类型3
...
}
【注意】
type Book struct {
title,author string
num,id int
}
一个结构体在定义完成后才能进行使用。结构体实例化时,会真正地分配内存。因此,必须在定义结构体并实例化后才能使用结构体的字段。
三种方式:
结构体本身是一种自定义数据类型,和其他基本数据类型的声明格式类似,可以通过var关键字进行实例化。
var 结构体实例 结构体类型
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
func main() {
var book1 Book
fmt.Println(book1)
}
【string和int类型变量的初始值为""和0】
new函数可对结构体进行实例化,实例化完成后会返回结构体的指针类型。
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
func main() {
book1 := new(Book)
fmt.Println(book1)
}
取地址实例化与使用new函数进行实例化类似,返回的是结构体指针类型。
结构体实例 := &结构体类型{}
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
func main() {
book1 := &Book{}
fmt.Println(book1)
}
结构体实例化完成后,可通过“.”来访问结构体的成员变量,对成员变量进行赋值或修改。
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
func main() {
book1 := &Book{}
book1.title = "Go语言"
book1.author = "Tom"
book1.num = 20
book1.id = 152368
fmt.Println("title:", book1.title)
fmt.Println("author:", book1.author)
fmt.Println("num", book1.num)
fmt.Println("id", book1.id)
}
Go语言中可以通过键值对格式和列表格式对结构体进行初始化。
结构体实例 := 结构体类型{
成员变量1:值1,
成员变量2:值2,
成员变量3:值3,
}
package main
import (
"fmt"
)
type Book struct {
title string
author string
num int
id int
}
func main() {
book1 := &Book{
title: "Go语言",
author: "Tom",
num: 20,
id: 152368,
}
fmt.Println("title:", book1.title)
fmt.Println("author:", book1.author)
fmt.Println("num", book1.num)
fmt.Println("id", book1.id)
}
结构体实例 := 结构体类型{
值1,
值2,
值3,
}
【注意】
Go语言中,一个方法就是一个包含了接收者的函数。对于结构体方法,接收者可以是结构体类型的值或是指针。
当接收者类型为指针时,可以通过该方法改变该接收者的成员变量值,即使你使用了非指针类型实例调用该函数,也可以改变实例对应的成员变量值。
package main
import "fmt"
type User struct {
Name string
Email string
}
func (u *User) ChanegeName() { //指针类型接受者
u.Name = "Tom"
}
func main() {
u := &User{"Peter", "Go@go.com"}
fmt.Println("Name:", u.Name, "Email:", u.Email)
u.ChanegeName()
fmt.Println("Name:", u.Name, "Email:", u.Email)
}
即使实例不是指针类型,修改也能生效。
当接收者不是一个指针时,该方法操作对应接收者值的副本,否则即使你使用了指针调用函数,也无法改变成员变量值。
func (u User) ChanegeName() { //值类型接受者
u.Name = "Tom"
}
Go语言的结构体内嵌是一种组合特性,使用结构体内嵌可构建一种面向对象编程思想中的继承关系。结构体实例化后,可直接访问内嵌结构体的所有成员变量和方法。
type 结构体名1 struct {
成员变量1 类型1
成员变量2 类型2
}
type 结构体名2 struct {
结构体名1
成员变量3 类型3
}
结构体内嵌的初始化和结构体初始化类似,可以使用键值对或“.”的方式来进行初始化。
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
type BookBorrow struct {
Book
borrowTime string
}
type BookNotBorrow struct {
Book
readTime string
}
func main() {
bookBorrow := &BookBorrow{
Book: Book{
"Go语言",
"Tom",
20,
152368,
},
borrowTime: "30",
}
fmt.Println(bookBorrow)
bookNotBorrow := &BookNotBorrow{}
bookNotBorrow.title = "Python"
bookNotBorrow.author = "Peter"
bookNotBorrow.num = 10
bookNotBorrow.id = 152369
bookNotBorrow.readTime = "50"
fmt.Println(bookNotBorrow)
}
使用匿名结构体来代替普通的结构体,以使代码的编写更加便利。
在定义匿名结构体时,无须type关键字,但是在初始化被嵌入的匿名结构体时,需要再次声明结构体才能赋予数据。
package main
import (
"fmt"
)
type BookBorrow struct {
Book struct { //内嵌匿名结构体
title string
author string
num int
id int
}
borrowTime string
}
func main() {
bookNotBorrow := &BookBorrow{
Book: struct { //声明类型
title string
author string
num int
id int
}{
"Go语言",
"Tom",
20,
152368,
},
borrowTime: "30",
}
fmt.Println(bookNotBorrow)
}
匿名结构体,顾名思义,即没有名字的结构体,与匿名函数类似。匿名结构体无须type关键字就可以直接使用,匿名结构体在创建的同时也要创建对象。
匿名结构体的初始化和使用更加简单,无须通过type关键字定义,且不用写出类型名称。
title: Go语言
结构体实例 := struct{
//匿名结构体定义
成员变量1 类型1
成员变量2 类型2
成员变量3 类型3
...
}{
//成员变量初始化(可选)
成员变量1:值1,
成员变量2:值2,
成员变量3:值3,
...
}
package main
import "fmt"
type Book struct {
title string
author string
num int
id int
}
func main() {
book1 := struct {
title string
author string
num int
id int
}{
title: "Go语言",
author: "Tom",
num: 20,
id: 152368,
}
fmt.Println("title:", book1.title)
fmt.Println("author:", book1.author)
fmt.Println("num:", book1.num)
fmt.Println("id:", book1.id)
}
匿名结构体一般可用于组织全局变量、构建数据模板和解析JSON等。
使用匿名结构体来临时存储经过解析后的JSON数据。
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := &struct {
Code int
Msg string
}{}
jsonData := `{"code":200,"msg":""}`
if err := json.Unmarshal([]byte(jsonData), data); err != nil {
fmt.Println(err)
}
fmt.Println("code", data.Code)
fmt.Println("msg:", data.Msg)
}
XML即可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。
在计算机中,标记指计算机所能理解的信息符号,通过此类标记,计算机之间可以处理包含各种信息的文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。
1998年,W3C就发布了XML 1.0规范,用来简化网络间的文档信息传输。
XML本质上是一种树形的数据格式,我们可以定义与之匹配的Go语言的struct类型,然后通过xml包对XML文件中的数据进行解析,存储到自定义的结构体中。
test.xml
<Persons>
<Person>
<Name>小丁Name>
<Age>22Age>
<Interests>
<Interest>看书Interest>
<Interest>打游戏Interest>
Interests>
Person>
<Person>
<Name>凤凤Name>
<Age>22Age>
<Interests>
<Interest>姜云升Interest>
<Interest>吃零食Interest>
Interests>
Person>
Persons>
解析xml
package main
import (
"encoding/xml"
"fmt"
"io/ioutil"
)
type Result struct {
Person []Person
}
type Person struct {
Name string
Age string
Interests Interests
}
type Interests struct {
Interest []string
}
func main() {
var res Result
content, err := ioutil.ReadFile("第9章结构体/知识拓展/test.xml")
if err != nil {
fmt.Println(err)
return
}
err = xml.Unmarshal(content, &res)
if err != nil {
fmt.Println(err)
}
fmt.Println("XML文件解析后内容为:")
fmt.Println(res)
}
Go语言中是利用反射机制将来自XML文件中的数据反射成对应的struct对象的,其中缺少的元素或空属性值将被解析为零值。