天天看點

Golang 基礎知識(十一.struct結構體)

struct結構體

        • 1.定義
        • 2.匿名結構體
        • 3.鍵值對初始化
        • 4.構造函數
          • 4.1 (值)方法和接收者
          • 4.2 (指針)方法和接受者
          • 4.3 結構體的“繼承
          • 4.4 序列化與反序列化
          • 4.5總結(知識點綜合使用案例:學生資訊管理)

注意:根據記憶體對齊原則,結構體裡面的資料類型,應根據類型所占的大小,依次排序,從小到大,越小越小前,主要是優化記憶體讀取速度。(取值是有一個”框“的(32 :4,64,:8),根據框取值時候是一塊一塊的取,超出框的部分,取兩次,并删除多餘的部分再和前面取得資料合并,操作變多了,消耗就多)

type Part1 struct {
    a bool
    b int32
    c int8
    d int64
    e byte
}

type Part2 struct {
    e byte
    c int8
    a bool
    b int32
    d int64
}

func main() {
    part1 := Part1{}
    part2 := Part2{}

    fmt.Printf("part1 size: %d, align: %d\n", unsafe.Sizeof(part1), unsafe.Alignof(part1))
    fmt.Printf("part2 size: %d, align: %d\n", unsafe.Sizeof(part2), unsafe.Alignof(part2))
}
           

輸出結果:

part1 size: 32, align: 8
part2 size: 16, align: 8
           

1.定義

Go語言中的基礎資料類型可以表示一些事物的基本屬性,但是當我們想表達一個事物的全部或部分屬性時,這時候再用單一的基本資料類型明顯就無法滿足需求了,Go語言提供了一種自定義資料類型,可以封裝多個基本資料類型,這種資料類型叫結構體,英文名稱

struct

。 也就是我們可以通過

struct

來定義自己的類型了。

Go語言中通過

struct

來實作面向對象。

注意:

  • 類型名:辨別自定義結構體的名稱,在同一個包内不能重複。
  • 字段名:表示結構體字段名。結構體中的字段名必須唯一。
  • 字段類型:表示結構體字段的具體類型。

使用

type

struct

關鍵字來定義結構體,具體代碼格式如下:

type 類型名 struct {
    字段名 字段類型
    字段名 字段類型
    …
}
           

舉個例子:

type person struct {
	name string
	city string
	age  int8
}

func main() {
	var p1 person
	p1.name = "張三"
	p1.city = "北京"
	p1.age = 18
	fmt.Printf("p1=%v\n", p1)  //p1={張三 北京 18}
	fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"張三", city:"北京", age:18}
}
           

2.匿名結構體

注意:一般用于零時場景

var res struct{

​ name string

​ age int

}

res.name = “李四”

res.age = 19

fmt.Println(res)

3.鍵值對初始化

方式一:使用

&

對結構體進行取位址操作相當于對該結構體類型進行了一次

new

執行個體化操作

p3 := &person{}
fmt.Printf("%T\n", p3)     //*main.person
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}
p3.name = "七米"
p3.age = 30
p3.city = "成都"
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"七米", city:"成都", age:30}
           

方式二:

p5 := person{
	name: "小王子",
	city: "北京",
	age:  18,
}
           

方式三:對結構體指針進行鍵值對初始化

p6 := &person{
	name: "小王子",
	city: "北京",
	age:  18,
}
           

方式四:

p8 := &person{
	"沙河娜紮",
	"北京",
	28,
}
fmt.Printf("p8=%#v\n", p8) //p8=&main.person{name:"沙河娜紮", city:"北京", age:28}
           

使用這種格式初始化時,需要注意:

  1. 必須初始化結構體的所有字段。
  2. 初始值的填充順序必須與字段在結構體中的聲明順序一緻。
  3. 該方式不能和鍵值初始化方式混用。

4.構造函數

作用:不用每次都初始化

注意:

​ 構造函數都是以new開頭的;

​ 傳回資料主要分結構體和結構體指針;

​ 字段或者資料小的時候用結構體;

​ 結構體比較打得時候用結構體指針,減少記憶體開銷

傳回結構體:

type persion struct {

name string

age int

}

func newP1 (x string ,y int) persion{

return persion{

​ name : x,

​ age : y,

}

}

func main(){

p2 := newP1(“張三”,13)

fmt.Printf("%#v",p2)

fmt.Println(p2)

}

4.1 (值)方法和接收者

Go語言中的

方法(Method)

是一種作用于特定類型變量的函數。這種特定類型變量叫做

接收者(Receiver)

。接收者的概念就類似于其他語言中的

this

或者

self

方法的定義格式如下:

func (接收者變量 接收者類型) 方法名(參數清單) (傳回參數) {
    函數體
}
           

其中,

  • 接收者變量:接收者中的參數變量名在命名時,官方建議使用接收者類型名稱首字母的小寫,而不是

    self

    this

    之類的命名。例如,

    Person

    類型的接收者變量應該命名為

    p

    Connector

    類型的接收者變量應該命名為

    c

    等。
  • 接收者類型:接收者類型和參數類似,可以是指針類型和非指針類型。
  • 方法名、參數清單、傳回參數:具體格式與函數定義相同。

舉個例子:

//Person 結構體
type Person struct {
	name string
	age  int8
}

//NewPerson 構造函數
func NewPerson(name string, age int8) *Person {
	return &Person{
		name: name,
		age:  age,
	}
}

//Dream Person做夢的方法
func (p Person) Dream() {
	fmt.Printf("%s的夢想是學好Go語言!\n", p.name)
}

func main() {
	p1 := NewPerson("小王子", 25)
	p1.Dream()
}
           
4.2 (指針)方法和接受者

注意:

​ 1.需要修改接收者中的值

​ 2.接收者是拷貝代價比較大的大對象

​ 3.保證一緻性,如果有某個方法使用了指針接收者,那麼其他的方法也應該使用指針接收者。

type persion struct {

​ name string

​ age int

}

func newP1(name string ,age int) persion {

​ return persion{

​ name: name,

​ age: age,

​ }

}

func ( p *****persion ) brithday(){

p.age++

fmt.Printf("%s 今年是 %d 歲了",p.name,p.age)

}

func main(){

​ p2 := newP1(“張三”,18)

​ p2.brithday()

​ fmt.Printf(”%s 今天 %d 歲了“,p2.name,p.age) // 張三今天19 歲了

}

4.3 結構體的“繼承

注意:在一個結構體裡面調用其他結構體,就可以使用其他結構體的方法

//Animal 動物
type Animal struct {
	name string
}

func (a *Animal) move() {
	fmt.Printf("%s會動!\n", a.name)
}

//Dog 狗
type Dog struct {
	Feet    int8
	*Animal //通過嵌套匿名結構體實作繼承
}

func (d *Dog) wang() {
	fmt.Printf("%s會汪汪汪~\n", d.name)
}

func main() {
	d1 := &Dog{
		Feet: 4,
		Animal: &Animal{ //注意嵌套的是結構體指針
			name: "樂樂",
		},
	}
	d1.wang() //樂樂會汪汪汪~
	d1.move() //樂樂會動!
}
           
4.4 序列化與反序列化

import (

“fmt”

"encoding/json"

)

type Persion struct{

Name string

json:"name"

// N大寫是外部包可以引用

Age int

josn:"age"

}

func main (){

a := Persion{

​ Name:“張三”,

​ Age:18,

}

data , err := json.Marshal(a) // 序列化

if err != nil{

​ fmt.Println(“this is error”)

​ return

}

fmt.Println(“下面是json”)

fmt.Println(string(data))

var a1 Persion

res :=

{"name":"張三","Age":18}

err = json.Unmarshal([]byte(res),&a1) // 反序列化,&a1 是取位址,不然隻是拷貝一個空

if err != nil{

​ fmt.Println(“this is err122”)

}

fmt.Println(“下面是反序列化”)

fmt.Printf("%#v",a1)

}

4.5總結(知識點綜合使用案例:學生資訊管理)

package main

import (

“fmt”

“os”

)

// 定義學生

type student struct {

id int64

name string

}

type sMangger struct{

allStudent map[int64]student

}

// 注意:變量不能存儲資料,隻是定義資料類型

var smr sMangger // 申明變量存儲學生管理資訊

// 展示學生資訊

func (m sMangger) showInfo(){

// fmt.Println(m)

for _ , v := range m.allStudent{
	fmt.Printf("學号: %d , 姓名:%s\n", v.id , v.name)
}
           

}

func newStudent(id int64 , name string) student {

return student{

id:id,

name:name,

}

}

// 添加學生資訊

func (m sMangger) addInfo(id int64 , name string){

stu := newStudent(id,name)

m.allStudent[id] = stu

}

// 編輯

func (m sMangger) updateInfo(id int64){

data , ok := m.allStudent[id]

if !ok {

fmt.Println(“沒有這個玩意兒”)

return

}

fmt.Printf(“您要修改的學生資訊為:學号:%d 姓名:%s”, data.id , data.name)

fmt.Print(“請輸入您要修改的姓名”)

var xinName string

fmt.Scan(&xinName)

data.name = xinName // 指派

m.allStudent[id] = data // 更新

}

// 删除

func (m sMangger) deleteInfo(id int64){

data , ok := m.allStudent[id]

if !ok {

fmt.Println(“沒有這個人喲~~”)

return

}

fmt.Printf(“學生資訊為:學号;%d 姓名:%s”, data.id , data.name)

fmt.Println(“确定要删除嗎? 1:确定 0:取消”)

var xinNum int64

fmt.Scan(&xinNum)

delete(m.allStudent,id)

}

func main(){

var (
	num int64
	id int64
	name string
	isOk int64
)
smr = sMangger{
	allStudent: make(map[int64]student,100),
}

for {
	// 選擇項目
	fmt.Print(`
	我們提供一下集中選項(請輸入編号進行操作):
		1.檢視學生資訊;
		2.添加學生資訊;
		3.編輯學生資訊;
		4.删除學生資訊;
	請選擇:	`)

	// 接受使用者的輸入的選項服務
	fmt.Scan(&num)

	// 判斷執行要執行的動作
	switch num{
	case 1:
		fmt.Println("您選擇了1 檢視學生資訊")
		smr.showInfo()
	case 2:
		fmt.Println("您選擇了2 添加學生資訊")
		fmt.Print("請輸入學生學号 ")
		fmt.Scan(&id)
		fmt.Print("請輸入學生姓名 ")
		fmt.Scan(&name)
		fmt.Printf("您确定這是要添加的學生資訊嗎?學号: %d 姓名: %s ,1:确定 0:取消 ",id,name)
		fmt.Scan(&isOk)
		if isOk == 1 {
			// fmt.Println("ok")
			// 執行邏輯
			// add := 
			smr.addInfo(id,name)

		}else{
			fmt.Println("cannl")
			os.Exit(1)
		}
	case 3:
		fmt.Println("您選擇了3 編輯學生資訊")
		fmt.Print("請輸入學号:")
		fmt.Scan(&id)
		smr.updateInfo(id)
	case 4:
		fmt.Println("您選擇了4 删除學生資訊")
		fmt.Print("請輸入學号:")
		fmt.Scan(&id)
		smr.deleteInfo(id)
	default:
		fmt.Println("目前沒有此選項服務喲")
		fmt.Println("很高興為您服務")
		os.Exit(1)
	}
}
           

}