Go-重要的数据类型-数组-切片-map-结构体

重要的数据类型

指针

符号名称作用
&变量取地址符返回变量所在的地址
*指针变量取值符返回指针指地址存储的值
package main

import "fmt"

func main() {
	//&变量:获取变量的地址
	var x = 10
	fmt.Printf("赋值之前x的对应的地址:%p\n", &x)

	x = 100
	fmt.Printf("赋值之后x的对应的地址:%p\n", &x)
	var p *int
	p = &x
	//赋值之后x的对应的地址
	fmt.Println(p)
	//取的值是100
	fmt.Println(*p)
}
/*
赋值之前x的对应的地址:0xc0000140b8
赋值之后x的对应的地址:0xc0000140b8
*/

new函数是开辟一块内存空间的.

当我们只申明一个指针类型的变量时,默认是指向nil
var num *int  // 此时只是声明一个指针,并没有开辟内存空间,让num执行此空间,而是指向nil对象
fmt.Println(num)  // nil    nil对象是不能直接复制的 invalid memory address or nil pointer dereference
*num = 2   // 报错,

通过new函数开辟一个内存空间

temp := new(int)   // new开辟一个存储int值的空间,temp则是一个*int 的指针变量
num = temp         // 将num指向new开辟的这个地址

数组

  1. 声明数组&循环遍历

    var 数组名[元素数量]元素类型
    
    package main
    
    import "fmt"
    
    func main() {
    	var arr1 [2]int //  [0 0]
    	arr1[0] = 1     //  [1 0]
    	// 2.声明并赋值
    	var  arr2 =[2]int{1,2}  // [1 2]
    
    	// 3.省略长度赋值
    	var arr3 = [...]{1,2,3} // [1 2 3] 长度为3 编译器自己计算
    
    	// 4.索引赋值
    	var arr4 = [3]{0:1,2:3} // [1 0 3]
    	var names = [6]string{"张三", "李四", "王五", "赵六", "王霸", "嘿嘿"}
    	// 遍历数组
    	for i := 0; i < len(names); i++ {
    		fmt.Print(i, names[i])
    	}
    	// range循环
    	for u, v := range names {
    		fmt.Println(u, v)
    	}
    }
    
    

切片

切片的创建的俩种方式:

1、从数组或者切片上切取获得

2、直接声明切片:var name []Type

切片语法:

arr [start : end] 或者 slice [start : end]

切片特点:

1、左闭右开

2、去除的元素数量为:结位置-开始位置

3、去除袁术不包含结束位置对应的索引、切片最后一个元素使用slice[len(slice)]获取;

4、当缺省开始位置时、表示从连续区域开头到结束位置;当缺省结束位置是,表示从开始位置到整个连续区域末尾;俩者同事缺省时,与切片本身等效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wWMXtnZA-1669646040589)(C:\Users\lh\AppData\Roaming\Typora\typora-user-images\image-20221128222557825.png)]

切片的长度计算: 结束索引-起始索引
切片的容量计算: 数组的长度-起始值在数组中的索引

内置函数:
len() 计算容器类型中元素的个数
cap() 计算容器的容量

make函数

切片数据类型是引用类型,即声明未赋值时,不会开辟空间,指向nil对象,
make函数用来开辟内存空间并初始化切片底层引用的数组中各个元素的值.
语法:
make(切片类型,长度,容量)
原理参考3.1中的原理图

append函数

// append函数是向容器中追加元素的.
// 1. 追加一个或多个值
var Slice1 = make([]int,1,4)   //[0]
append(Slice1,1,2)               // [0 1 2]

// 2. 追加一个切片 通过...
var Slice2 = make([]int,1,4)
append(Slice2,[]int{1,2,3}...) // [0 1 2 3]

注意: []int... 与一个值或多个值方式不能同时使用.

// 3. 删除操作 删除的索引 append(slice[start:index],[index+1:]...)

var Slice3 = []int{1,2,3,4}   // 删除3这个值
SliceNew := append(Slice3[:2],Slice3[2+1:]...)

// 4. 插入操作
// 开头插入10值
var Slice4 = []int{1,2,3,4}
SliceNew2 := append([]int{10},Slice4...)

// 中间插入值66
var Slice5 = []int{1,2,3,4}
SliceNew3 = append(Slice5[:2],append([]int{66}, Slice[2:])...)
  • 扩容原理
扩容机制:
每次append操作会检查slice是否有足够的容量来容纳append值,如果足够,则在原来的
数组上追加值,并返回一个新slice,新的slice引用的还是原始的数组.如果容量不足,则会
在内存中新创建一个数组,将原始数组中的值,进行值拷贝到新数组中,再将新元素追加之后,
返回一个新slice,此时新的slice引用的是新创建的数组,与原始的数组无关,
扩容策略:
如果切片的容量小于 1024 个元素,于是扩容的时候就翻倍增加容量,
元素个数超过 1024 个元素,那么增长因子就变成 1.25 ,即每次增加原来容量的四分之一。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V73dRyOh-1669646040590)(C:\Users\lh\AppData\Roaming\Typora\typora-user-images\image-20221128222710193.png)]

切片(容器)遍历

// 方式1  for循环
var s1 = []int{1,2,3,4}
count := len(s1)
for i:=0;i<count;i++{
    fmt.Println(i,s1[i])  // i为索引  s1[i] 去对应索引位置的值
}

// 方式2 range方式 (推荐使用此方式)
for index,val := range s1{
    fmt.Println(index,val) // index为索引  val 索引对应位置的值.
}

map

map数据类型

map 是一种通过key获取value数据结构,底层通过数组进行存储

特点:

1. map类型是一种引用类型

1. map的key是无序的,且不能重复.

map的声明方式

声明语法:

map[key_type][val_type]

声明map时,需要指定key和value的类型.
// 方式1: 声明并赋值
var person1 = map[string][string]{"name":"dongdong","gender":"male"}
fmt.Println(person1)  // map[name:dongdong gender:male]

// 方式2: make方式
//错误:
var person2 map[string]string
person2["name"] = "dongdong"  // 或报错,和切片一样,map在声明未初始化时,存储的是一个nil
                              // 空对象,此时是没有存储空间来存储键值对
//make:
ver person2 = make(map[string][string])
person2["name"] = "dongdong"
fmt.Println(person2) // map[name:dongdong]

map的基本操作

// 语法: map[key]=value
var person1 = map[string][string]{"name":"dongdong","gender":"male"}
person1["hoppy"] = "reading"
fmt.Println(person1)  // map[name:dongdong hoppy:reading gender:male]
// 语法: delete(map,key)  使用内置函数delete
var person1 = map[string][string]{"name":"dongdong","gender":"male"}
delete(person1,"gender")
fmt.Println(person1)  //  map[name:dongdong]
// 语法: map[new_key]= value   改与增一样,当key存在是则为改,当key不存在时为增加.
var person1 = map[string][string]{"name":"dongdong","gender":"male"}
person1["name"] = "dongd"
fmt.Println(person1) // map[name:dongd gender:male]
// 语法: map[key]  通过key取对应的值.
var person1 = map[string][string]{"name":"dongdong","gender":"male"}
fmt.Println(person1["name"]) // dongdong

map的遍历

var person1 = map[string][string]{"name":"dongdong","gender":"male"}
for k,info := person1{
    fmt.Println(k,info)  // k为map的key,info为对应key的value
}
 = map[string][string]{"name":"dongdong","gender":"male"}
fmt.Println(person1["name"]) // dongdong

map的遍历

var person1 = map[string][string]{"name":"dongdong","gender":"male"}
for k,info := person1{
    fmt.Println(k,info)  // k为map的key,info为对应key的value
}

结构体

声明结构体

type 类型名 struct {
	字段1 字段1类型
	字段2 字段2类型
	...
}

Go语言结构体(Struct)从本质上讲师一种自定义的数据类型,只不过这种数据类型比较复杂,是由int,char,float等基本类型组成的,你可以认为结构体是一种聚合类型。

type Student struct {
	sid int 
	name string
	age int8
	course []string //选修课程
}

结构体的实例化方式

1、先声明在赋值
//声明
type Student struct {
	sid int 
	name string
	age int8
	course []string //选修课程
}
//实例化
var s Student
fmt.Println(s)
s.sid = 1001
s.name = "test"
fmt.Println(s)
**//使用make初始化,要不然直接使用会报错**
s.course = make([]string,3)
s.course[0] = "chinese"

在这里插入图片描述

2、声明并赋值

type Student struct {
	sid int 
	name string
	age int8
	course []string //选修课程
}
//直接对象赋值
var s = student{name:"test",sid: 1002}
fmt.Println(s)
//按顺序赋值
var s2 = Student{123,"test",19,[]string{"math"}}

3、实例化之&结构体

ackage main

import "fmt"

type Student struct {
    sid    int
    name   string
    age    int8
    course []string //  选秀课程
}

func CourseInit(stu Student) {
    stu.course = []string{"chinese", "math", "english"}
    fmt.Println(stu)
}

func CourseInit2(stu *Student) {
    (*stu).course = []string{"chinese", "math", "english"}
}

func main() {

    // 案例1
    s1 := Student{sid: 1001, name: "alvin", age: 32}
    s2 := s1 // 值拷贝
    fmt.Println(s2)
    s1.age = 100
    fmt.Println(s2.name)

    // 如果希望s3的值跟随s2保持一致怎么实现
    s3 := &s1 // var s4 *Student = &s2
    s1.age = 100
    fmt.Println((*s3).age)
    fmt.Println(s3.age)

    // 案例2
    var s4 = Student{sid: 1001, name: "alvin", age: 32}
    CourseInit(s4)
    fmt.Println("s报的课程:", s4.course)
    // 怎么能初始化成功呢?
    var s5 = &Student{sid: 1001, name: "alvin", age: 32}
    CourseInit2(s5)
    fmt.Println("s报的课程:", (*s5).course) // *s.course的写法是错误的
    fmt.Println("s报的课程:", s5.course)

}

在Go语言中,结构体指针的变量可以继续使用.,这是因为Go语言为了方便开发者访问结构体指针的成员变量可以像访问结构体的成员变量一样简单,使用了语法糖(Syntactic sugar)技术,将 instance.Name 形式转换为 (*instance).Name。

4、实例化之 new(结构体)

Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。使用 new 的格式如下:其中:

instance := new(T)

其中:
T 为类型,可以是结构体、整型、字符串等。
instance:T 类型被实例化后保存到 instance 变量中,instance的类型为 *T,属于指针。

s := new(Student)              // &Student{}
fmt.Println(reflect.TypeOf(s)) // *Student
fmt.Println(s)                 // *Student
s.name = "yuan"
fmt.Println((*s).name)
fmt.Println(s.name)

模拟构造函数

Go语言没有构造函数,但是我们可以使用结构体初始化的过程来模拟实现构造函数。

package main

import "fmt"

type Student struct {
    sid    int
    name   string
    age    int8
    course []string //  选秀课程
}

func NewStudent(sid int, name string, age int8, course []string) *Student {
    return &Student{
        sid:    sid,
        name:   name,
        age:    age,
        course: course,
    }
}

func main() {

    s := NewStudent(1001, "yuan", 32, nil)
    fmt.Println(s)

}

方法接收器

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。

方法的定义格式如下:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
          函数体
}

其中,

  • 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
  • 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
  • 方法名、参数列表、返回参数:具体格式与函数定义相同。
package main

import "fmt"

type Player struct {
    Name        string
    HealthPoint int
    Level       int
    NowPosition []int
    Prop        []string
}

func NewPlayer(name string, hp int, level int, np []int, prop []string) *Player {

    return &Player{
        name,
        hp,
        level,
        np,
        prop,
    }
}

func (p Player) attack() {
    fmt.Printf("%s发起攻击!\n", p.Name)
}
func (p *Player) attacked() {
    fmt.Printf("%s被攻击!\n", p.Name)
    p.HealthPoint -= 10
    fmt.Println(p.HealthPoint)
}

func (p *Player) buyProp(prop string) {
    p.Prop = append(p.Prop, prop)
    fmt.Printf("%s购买道具!\n", p.Name)
}

func main() {
    player := NewPlayer("yuan", 100, 100, nil, nil)
    player.attack()
    player.attacked()
    fmt.Println(player.HealthPoint)
    player.buyProp("魔法石")
    fmt.Println(player.Prop)
}
1、官方定义:Methods are not mixed with the data definition (the structs): they are orthogonal to types; representation(data) and behavior (methods) are independent

2、方法与函数的区别是,函数不属于任何类型,方法属于特定的类型

匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。

 	

package main

import "fmt"

type Person struct {
    string
    int
}

func main() {
    p1 := Person{
        "yuan",
        18,
    }
    fmt.Printf("%#v\n", p1)        //main.Person{string:"yuan", int:18}
    fmt.Println(p1.string, p1.int) //北京 18
}

结构体也可以作为匿名字段使用

package main

import "fmt"

type Addr struct {
    country  string
    province string
    city     string
}

type Person struct {
    name string
    age  int
    Addr
}

func main() {
    p1 := Person{
        "yuan",
        18,
        Addr{"中国", "广东省", "深圳"},
    }
    fmt.Printf("%#v\n", p1)      //main.Person{string:"北京", int:18}
    fmt.Println(p1.name, p1.age) // yuan 18
    fmt.Println(p1.Addr)
    fmt.Println(p1.Addr.country) // 中国
    fmt.Println(p1.city)         // 深圳
}

当结构体中有和匿名字段相同的字段时,采用外层优先访问原则

结构体的继承

package main

import "fmt"

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

func (a *Animal) eat() {
    fmt.Printf("%s is eating!\n", a.name)
}
func (a *Animal) sleep() {
    fmt.Printf("%s is sleeping!\n", a.name)
}

// Dog 类型
type Dog struct {
    Kind    string
    *Animal //通过嵌套匿名结构体实现继承
}

func (d *Dog) bark() {
    fmt.Printf("%s is barking ~\n", d.name)
}

// Cat 类型
type Cat struct {
    *Animal
}

func (c *Cat) climbTree() {
    fmt.Printf("%s is climb tree ~\n", c.name)
}

func main() {
    d1 := &Dog{
        Kind: "金毛",
        Animal: &Animal{ //注意嵌套的是结构体指针
            name: "旺财",
        },
    }
    d1.eat()
    d1.bark()

    c1 := &Cat{
        Animal: &Animal{
            name: "喵喵",
        },
    }
    c1.sleep()
    c1.climbTree()

}

序列化

序列化最重要的就是json序列化。

JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript
(w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON
成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在这里插入图片描述

结构体的json操作



package main

import (
    "encoding/json"
    "fmt"
)

type Addr struct {
    Province string
    City     string
}
type Stu struct {
    Name string `json:"name"` // 结构体的标签
    Age  int    `json:"-"`    // 表示不参与序列化
    Addr Addr
}

func main() {

    var stuMap = map[string]interface{}{"name": "yuan", "age": 32, "addr": "beijing"}
    var stuStruct = Stu{Name: "yuan", Age: 18, Addr: Addr{Province: "Hebei", City: "langFang"}}

    // 序列化
    jsonStuMap, _ := json.Marshal(stuMap)
    jsonStuStruct, _ := json.Marshal(stuStruct)

    fmt.Println(string(jsonStuMap))
    fmt.Println(string(jsonStuStruct))

    // 反序列化
    // var x  = make(map[int]string)
    var StuMap map[string]interface{}
    err := json.Unmarshal(jsonStuMap, &StuMap)
    if err != nil {
        return
    }
    fmt.Println("StuMap", StuMap, StuMap["name"])

    var StuStruct Stu
    err := json.Unmarshal(jsonStuStruct, &StuStruct)
    if err != nil {
        return
    }
    fmt.Println(StuStruct)
    fmt.Println(StuStruct.Name)
    fmt.Println(StuStruct.Addr.City)

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值