Golang數(shù)據(jù)類型之結(jié)構(gòu)體-上篇
目錄
1、結(jié)構(gòu)體介紹
2、結(jié)構(gòu)體的定義
3、構(gòu)造結(jié)構(gòu)體實(shí)例
4、聲明與初始化
5、屬性的訪問和修改

1、結(jié)構(gòu)體介紹

為什么要有結(jié)構(gòu)體?
數(shù)組只能保存同一種類型的數(shù)據(jù),當(dāng)需要記錄多種不同類型的數(shù)據(jù),并聚集在一起用來描述復(fù)雜的實(shí)體時(shí),怎么辦?
結(jié)構(gòu)體就是用于解決這個(gè)問題的,結(jié)構(gòu)體是由一系列具有相同類型或不同類型的數(shù)據(jù)構(gòu)成的數(shù)據(jù)集合,方便容量任意類型的數(shù)據(jù)
結(jié)構(gòu)體的目的就是把數(shù)據(jù)聚集在一起,以便能夠更加便捷的操作這些數(shù)據(jù)
結(jié)構(gòu)體是由一些列屬性組成的復(fù)合數(shù)據(jù)類型,每個(gè)屬性都具有名稱、類型和值,結(jié)構(gòu)體將屬性組合在一起進(jìn)行由程序進(jìn)行處理
結(jié)構(gòu)體和類的概念
go里面沒有類,go用一種特殊的方式,把結(jié)構(gòu)體本身看做一個(gè)類
一個(gè)成熟的類,具備成員變量和成員函數(shù),結(jié)構(gòu)體本身就有成員變量,再給他綁定上成員函數(shù),就可以了
結(jié)構(gòu)體單例綁定
如下,sayHello()用了指針的方式進(jìn)行綁定,相當(dāng)于給結(jié)構(gòu)體綁定了函數(shù),這個(gè)結(jié)構(gòu)體等價(jià)于對(duì)象
唯一的不同點(diǎn)就是如果使用*綁定函數(shù),那么這種對(duì)象就是單例的,引用的是同一個(gè)結(jié)構(gòu)體
type People struct {
name string
}
func (p People) toString() {
fmt.Println(p.name)
fmt.Printf("p的地址 %p \n", &p)
}
func (p *People) sayHello() {
fmt.Printf("Hello %v \n", p.name)
fmt.Printf("*P的地址 %p \n", p)
}
2、結(jié)構(gòu)體的定義
結(jié)構(gòu)體定義使用struct標(biāo)識(shí),需要指定其包含的屬性(名和類型),使用關(guān)鍵字type和struct來定義一個(gè)結(jié)構(gòu)體
結(jié)構(gòu)體的定義格式如下
type 類型名 struct {
字段1 類型1
字段2 類型2
//...
}
以上各個(gè)部分的說明如下
類型名:標(biāo)識(shí)自定義結(jié)構(gòu)體的名稱,在同一個(gè)包內(nèi)不能包含重復(fù)的類型名 struct{}:表示結(jié)構(gòu)體類型,type 類型名 struct{}可以被理解為將 struct{}結(jié)構(gòu)體定義為類型名的類型 字段 1、字段 2......:表示結(jié)構(gòu)體字段名。結(jié)構(gòu)體中的字段名必須唯一 類型 1、類型 2......:表示結(jié)構(gòu)體各個(gè)字段的類型,結(jié)構(gòu)體中的字段可以是任意類型:string、int、float;復(fù)合類型:map、slice、channel、struct
在定義結(jié)構(gòu)體時(shí)可以為結(jié)構(gòu)體指定結(jié)構(gòu)體名(命名結(jié)構(gòu)體),用于后續(xù)聲明結(jié)構(gòu)體變量使用
type struct_variable_type struct {
member definition
member definition
...
member definition
}
例如 用于描述一個(gè)人的特征
如果單獨(dú)使用變量描述應(yīng)該如何描述?
沒有結(jié)構(gòu)體
var (
name string
age int
gender string
weight uint
favoriteColor []string
)
使用結(jié)構(gòu)體
type Person struct {
Name string
Age int
Gender string
Weight uint
FavoriteColor []string
}
type User struct {
ID int
Name string
Birthday string
Addr string
Tel string
Remark string
}
如果某幾個(gè)字段類型相同,可以縮寫在同一行:
type Person struct {
Name, City string
Age int
}
可以看出結(jié)構(gòu)體就像一個(gè)容器,這個(gè)容器里面裝什么自己定義,這也是結(jié)構(gòu)體的特點(diǎn): 自定義化的程度很高,使用靈活
一旦定義了結(jié)構(gòu)體類型,則它就能用于變量的聲明,語法格式如下
variable_name := struct_variable_type {value1, value2,...}
或
variable_name := struct_variable_type {key1: value1, key2: value2,...}
例如,定義一個(gè)名為Book的圖書結(jié)構(gòu)體,并打印出結(jié)構(gòu)體的字段值的示例如下
package main
import "fmt"
type Book struct {
title string
author string
subject string
press string
}
func main() {
// 創(chuàng)建一個(gè)新的結(jié)構(gòu)體
fmt.Println(Book{"Go基礎(chǔ)", "ssgeek", "Go語言", "Go語言教程"})
// 也可以使用 key => value 格式
fmt.Println(Book{title: "Go基礎(chǔ)", author: "ssgeek", subject: "Go語言", press: "Go語言教程"})
// 忽略的字段為 0 或 空
fmt.Println(Book{title: "Go基礎(chǔ)", author: "ssgeek"})
}
3、構(gòu)造結(jié)構(gòu)體實(shí)例
定義了struct,就表示定義了一個(gè)數(shù)據(jù)結(jié)構(gòu),或者說數(shù)據(jù)類型,也或者說定義了一個(gè)類。總而言之,定義了struct,就具備了成員屬性,就可以作為一個(gè)抽象的模板,可以根據(jù)這個(gè)抽象模板生成具體的實(shí)例,也就是所謂的"對(duì)象", 也就是面向?qū)ο笾械?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(150, 84, 181);">Class---> Object, 如下圖

對(duì)應(yīng)的結(jié)構(gòu)體定義
type Car struct {
Color string // 顏色
Brand string // 品牌
Model string // 型號(hào)
}
var car Car // 初始化一個(gè)Car實(shí)例, car就代表是一輛具體的車(Object)
這里的car就是一個(gè)具體的Car實(shí)例,它根據(jù)抽象的模板Car構(gòu)造而出,具有具體的屬性Color、Brand和Model的值,雖然初始化時(shí)它的各個(gè)字段都是""值。換句話說,car是一個(gè)具體的車, 比如福特野馬
struct初始化時(shí),會(huì)做默認(rèn)的賦0初始化,會(huì)給它的每個(gè)字段根據(jù)它們的數(shù)據(jù)類型賦予對(duì)應(yīng)的0值。例如int類型是數(shù)值0,string類型是"",引用類型是nil等
var car Car
fmt.Printf("%+v", car) // {Color: Brand: Model:}
也可以在聲明的時(shí)候直接為成員變量賦值,通常把這個(gè)過程稱之為構(gòu)造結(jié)構(gòu)體的實(shí)例, 語法如下:
// 使用{k:v, k:v}這種方式來為結(jié)構(gòu)體的成員賦值
TypeName{filed1: value1, filed2: value2, ...}
// 為了書寫美觀, 通常都把每一個(gè)k:v 獨(dú)立寫在一行,比如:
TypeName{
file1: value1,
file2: value2,
...
}
一個(gè)具體的例子,比如要通過Car構(gòu)造一個(gè)具體的汽車: 福特野馬
// 為了書寫的簡潔,采用簡單聲明的方式, 就像 a := 10 <---> var a int; a = 10
//
var car Car
fmt.Printf("%+v\n", car)
car = Car{ // 當(dāng)然也可以不然聲明car的類型, 直接使用簡短聲明: car := Car{...}
Color: "yellow", // 黃色
Brand: "ford", // 福特
Model: "yema", //
}
fmt.Printf("%+v\n", car) // {Color:yellow Brand:ford Model:yema}
// 注意,上面最后一個(gè)逗號(hào)","不能省略,Go會(huì)報(bào)錯(cuò),這個(gè)逗號(hào)有助于去擴(kuò)展這個(gè)結(jié)構(gòu)
4、聲明與初始化
聲明結(jié)構(gòu)體變量只需要定義變量類型為結(jié)構(gòu)體名,變量中的每個(gè)屬性被初始化為對(duì)應(yīng)類型的零值
也可聲明結(jié)構(gòu)體指針變量,此時(shí)變量被初始化為nil
遵循所有類型聲明語法: var struct_name struct_type
使用結(jié)構(gòu)體創(chuàng)建的變量叫做對(duì)應(yīng)結(jié)構(gòu)體的實(shí)例或者對(duì)象
只聲明不初始化
比如下面初始化一個(gè)person的實(shí)例
// 只聲明
var person Person
var me User
fmt.Printf("%T\n", me) // main.User
可以看到聲明后的結(jié)構(gòu)體的所有屬性都是初始值
var不加等號(hào):初始化零值(對(duì)數(shù)值類型來說,零值是 0;對(duì)字符串來說,零值是空字符串;對(duì)布爾類型,零值是false)
var person Person
fmt.Printf("%+v\n", person)
// {Name: Age:0 Gender: Weight:0 FavoriteColor:[]}
var me3 User = User{}
fmt.Println(me3) // {0 0001-01-01 00:00:00 +0000 UTC }
聲明并初始化
使用字面量初始化結(jié)構(gòu)體值對(duì)象
var person Person = Person{
Name: "andy",
Age: 66,
Gender: "male",
Weight: 120,
FavoriteColor: []string{"red", "blue"},
}
fmt.Printf("%+v\n", person)
// {Name:andy Age:66 Gender:male Weight:120 FavoriteColor:[red blue]}
fmt.Printf("%p\n", &person.Name) // 0xc0000ce080 地址相鄰
fmt.Printf("%p\n", &person.Age) // 0xc0000ce090 地址相鄰
var me2 User = User{
1,
"geek",
time.Now().Format("2006-01-02"),
"北京市",
"15588888888",
"這是備注2",
}
fmt.Printf("%v\n", me2) // {1 geek 2021-08-10 北京市 155888888888 這是備注2}
可以在函數(shù)外部使用,可以用來聲明初始化全局變量
注意,上面最后一個(gè)逗號(hào)","不能省略,Go會(huì)報(bào)錯(cuò),這個(gè)逗號(hào)有助于去擴(kuò)展這個(gè)結(jié)構(gòu)體
使用短變量聲明
// 變量 := 結(jié)構(gòu)體{賦值}
func TestPerson32(t *testing.T) {
p3 := Person3{
Name: "ssgeek",
Age: 24,
}
fmt.Println(p3)
}
通過屬性名
// 通過屬性名指定,可以省略、無序
var me4 User = User{ID:1, Name: "geek", Birthday: time.Now().Format("2006-01-02"), Addr: "北京市", Tel: "15588888888"}
fmt.Println(me4)
使用 new 函數(shù)進(jìn)行初始化結(jié)構(gòu)體指針對(duì)象
var me5 *User = new(User)
fmt.Printf("%T, %#v, %#v\n", me5, me5, *me5)
使用指針初始化
var me6 *User
fmt.Printf("%T\n", me6) // *main.User
fmt.Printf("%#v\n", me6) // (*main.User)(nil)
var me7 = &User{ID: 2, Name: "geek"}
fmt.Printf("%T\n", me7) // *main.User
5、屬性的訪問和修改
通過結(jié)構(gòu)體對(duì)象名.屬性名的方式來訪問和修改對(duì)象的屬性值
可以通過結(jié)構(gòu)體指針對(duì)象的點(diǎn)操作直接對(duì)對(duì)象的屬性值進(jìn)行訪問和修改
// 語法:結(jié)構(gòu)體.成員名
fmt.Println((&person).Name) // andy
fmt.Println(person.Name) // andy
person.Age = 20
fmt.Println(person.Age) // 20
在golang中,訪問結(jié)構(gòu)體成員需要使用點(diǎn)號(hào)操作符,點(diǎn)號(hào)操作符也被稱為選擇器selector
func main() {
p1 := Person{
Name: "geek",
Age: 9000,
}
fmt.Println(p1.Name, p1.Age)
p1.Age += 1
fmt.Println(p1.Name, p1.Age)
}
See you ~
