0%

struct的方法

Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct
定义:func (recevier type) methodName(参数列表)(返回值列表){}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type student struct {
name string
age int
score int
}

func (p *student) init(name string, age, score int) {
p.name = name
p.age = age
p.score = score
}

func (p student) getName() string {
return p.name
}

func (p student) print() {
fmt.Println("p is :", p)
}

func main() {
var stu student
stu.init("张三", 18, 61)
stu.print()
name := stu.getName()
fmt.Printf("stu name:%s\n", name)
}

struct的继承

如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type car struct {
brand string
model string
}

type train struct {
car
wheel int
}

func (p car) run() {
fmt.Printf("%s run....\n", p.brand)
}
func main() {
var tr train
tr.brand = "火车"
tr.model = "长"
tr.wheel = 50
tr.run()
}
------
火车 run....

如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type car struct {
brand string
model string
}

type audi struct {
c car
wheel int
}

func (p car) run() {
fmt.Printf("%s run....\n", p.brand)
}

func main() {
var ad audi
ad.c.brand = "奥迪"
ad.c.model = "suv"
ad.wheel = 4
ad.c.run()
}
------
奥迪 run....

struct的注意点

结构体是用户单独定义的类型,不能和其他类型进行强制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14

type student struct {
value int
}

type stu student

func main() {
var a student = student{value: 30}
var b stu = stu{}
//注意:这段代码的编译无法通过
a = b
}

struct中没有构造函数,一般可以使用工厂模式来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

type student struct {
value int
}

func CreateStudent(val int) *student {
return &student{
value: val,
}
}

func main() {
var a *student = CreateStudent(1)
fmt.Printf("a:%v\n", a)
}
------------
a:&{1}

注意:make只用来创建map、slice、channel new用来创建值类型

struct中的tag

在Go中,如果一个struct需要被序列化成json,则需要其标识符和内部字段的首字母大写,否则Go自带的encoding/json包无法对其访问
而首字母大写后,序列化出的json中每个字段的首字母也会大写,这时候就需要使用tag来标识

  • 未使用tag标识的代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import (
    "encoding/json"
    "fmt"
    )

    type Student struct {
    Name string
    Age int
    Score int
    }

    func test() {
    var s *Student = &Student{
    Name: "张三",
    Age: 18,
    Score: 61,
    }
    b, err := json.Marshal(s)
    if err != nil {
    fmt.Println(err)
    }
    fmt.Printf("%s\n", string(b))
    }

    func main() {
    test()
    }
    ---------------
    {"Name":"张三","Age":18,"Score":61}

    使用tag对struct的字段进行标识:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    type Student struct {
    //tag使用的是``反引号进行标识
    Name string `json:"name"`
    Age int `json:"age"`
    Score int `json:"score"`
    }

    func test() {
    var s *Student = &Student{
    Name: "张三",
    Age: 18,
    Score: 61,
    }
    b, err := json.Marshal(s)
    if err != nil {
    fmt.Println(err)
    }
    fmt.Printf("%s\n", string(b))
    }

    func main() {
    test()
    }
    -----------------
    {"name":"张三","age":18,"score":61}

    注意!tag使用``反引号进行标识!

struct中的匿名字段

结构体中的字段可以没有名字,即匿名字段,匿名字段可以通过.类型的方式进行访问和赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Car struct {
Name string
}

type Train struct {
Car
Name string
Start time.Time
}

func test1() {
var t Train
t.Car.Name = "车"
t.Name = "火车"
t.Start = time.Now()
fmt.Printf("%v\n", t)
}

func main() {
test1()
}
-----------------
{{车} 火车 2019-07-09 07:19:08.2539989 +0800 CST m=+0.002012401}

结构体的声明

结构体标识符和结构体内的字段,如果大写,则表示公共的,可以在其他包内访问,否则是私有的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
结构体的声明
type 标识符 struct{
field1 type
field2 type
}
*/
type student struct {
name string
age int
}

func test1() {
// 结构体定义的三种方式
var stu student
stu.name = "方式1"
stu.age = 18
fmt.Printf("stu[name:%s,age:%d]\n", stu.name, stu.age)

// 通过new关键字创建的结构体,返回的是地址,所以这里的stu1是指向地址的指针
stu1 := new(student)
(*stu1).name = "方式2"
(*stu1).age = 18
fmt.Printf("stu1[name:%s,age:%d]\n", (*stu1).name, (*stu1).age)

stu2 := &student{
name: "方式3",
age: 18,
}
fmt.Printf("stu2[name:%s,age:%d]\n", (*stu2).name, (*stu2).age)
}

func main() {
test1()
}

互斥锁

若不加互斥锁,下面这段程序中map[1]的数值可能为负数(多次执行结果可能不相同,但大多都为负数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var lock sync.Mutex

func consumer(m map[int]int) {
// 定义一个消费者,用来消费传入的map中的数值,直到数值小于或等于0
// 在没有使用互斥锁时,map中的数值可能会出现负数
// 读写锁适合在读写频率相差不多的情况下使用,其会影响性能
for i := 0; i < 100; i++ {
go func(m1 map[int]int) {
lock.Lock() //上锁
if m1[1] > 0 {
time.Sleep(time.Microsecond)
m1[1] = m1[1] - 1
}
lock.Unlock() //解锁
}(m)
}
}

func test1() {
m := make(map[int]int, 1)
m[1] = 10
consumer(m)
time.Sleep(3 * time.Second)
fmt.Printf("map:%v", m)
}

func main() {
test1()
}
---------------------
map:map[1:0]
阅读全文 »

map的声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func test1() {
fmt.Printf("map的声明与初始化\n")
// map key-value的数据结构 又叫字典
// 1.声明
var m map[string]string
var m1 map[string]int
var m2 map[string]map[string]string
// 声明是不会分配内存的,初始化需要使用make
m = make(map[string]string, 5)
m["s1"] = "1"

m1 = make(map[string]int, 5)
m1["s1"] = 1

// 注意 这里m2只初始化了key value同样需要初始化 否则会panic
m2 = make(map[string]map[string]string, 5)
m2["s1"] = make(map[string]string, 5)
m2["s1"]["y1"] = "s1-y1-1"
fmt.Printf("m:%v\n", m)
fmt.Printf("m1:%v\n", m1)
fmt.Printf("m2:%v\n", m2)

}

func main() {
test1()
}

--------map的声明与初始化--------
m:map[s1:1]
m1:map[s1:1]
m2:map[s1:map[y1:s1-y1-1]]

map的插入与更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func test2() {
fmt.Printf("-------map的插入和更新-------\n")
var m map[string]string = map[string]string{"s1": "hello world"}
fmt.Printf("m:%v\n", m)
m["s1"] = "hi"
fmt.Printf("update m:%v\n", m)
// 查找
if _, ok := m["s1"]; ok {
fmt.Printf("m[s1]:%v\n", m["s1"])
}
// 遍历map
for k, v := range m {
fmt.Printf("%s:%s\n", k, v)
}
// 删除map内的元素
delete(m, "s1")
fmt.Printf("len:%d\n", len(m))
}

func main() {
test2()
}

-------map的插入和更新-------
m:map[s1:hello world]
update m:map[s1:hi]
m[s1]:hi
s1:hi
len:0

map嵌套的初始化和遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
func modify(m map[string]map[string]string) {
fmt.Printf("%p\n", m)
// 判断m的key对应的map是否初始化
if _, ok := m["s1"]; !ok {
fmt.Println(ok)
m["s1"] = make(map[string]string, 5)
}
m["s1"]["y1"] = "s1-y1-1"
fmt.Printf("modify:m[s1][y1]:%v\n", m["s1"]["y1"])
}

func trans(m map[string]map[string]string) {
// 遍历map嵌套
for k, v := range m {
fmt.Printf("k:%s\n", k)
for k1, v1 := range v {
fmt.Printf("%s:[%s:%v]\n", k, k1, v1)
}
}
}

func test3() {
fmt.Printf("-------map嵌套的初始化-------\n")
// map是引用类型 在传递时传递的时地址的副本
m := make(map[string]map[string]string, 5)
fmt.Printf("%p\n", m)
modify(m)
// 这里也能读取到modify对m的初始化和赋值
fmt.Printf("test3:m[s1][y1]:%v\n", m["s1"]["y1"])

m["s1"]["y2"] = "s1-y2-2"
m["s1"]["y3"] = "s1-y3-3"
m["s2"] = make(map[string]string, 5)
m["s2"]["y1"] = "s2-y1-1"
m["s2"]["y2"] = "s2-y2-2"
m["s2"]["y3"] = "s2-y3-3"

trans(m)
}

func main() {
test3()
}

-------map嵌套的初始化-------
0xc00006a510
0xc00006a510
false
modify:m[s1][y1]:s1-y1-1
test3:m[s1][y1]:s1-y1-1
k:s1
s1:[y1:s1-y1-1]
s1:[y2:s1-y2-2]
s1:[y3:s1-y3-3]
k:s2
s2:[y1:s2-y1-1]
s2:[y2:s2-y2-2]
s2:[y3:s2-y3-3]

slice与map嵌套的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func test4() {
fmt.Printf("-------slice与map嵌套的初始化-------\n")
a := make([]map[string]int, 5)
if a[0] == nil {
a[0] = make(map[string]int, 5)
}
a[0]["s1"] = 1
a[0]["s2"] = 2
a[0]["s3"] = 3
fmt.Printf("%v\n", a)
}

func main() {
test3()
}

-------slice与map嵌套的初始化-------
[map[s1:1 s2:2 s3:3] map[] map[] map[] map[]]

map排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func test5() {
fmt.Printf("---------------map排序-----------------\n")
// Go的map是无序的 只在fmt.print打印时候有序
a := make(map[string]string, 5)

a["3"] = "hij"
a["1"] = "abc"
a["2"] = "def"
a["4"] = "opq"

for k, v := range a {
fmt.Printf("%s,%s\n", k, v)
}

// 把map的key取出来放入slice进行排序
slices := make([]string, 0, 5)
for k, _ := range a {
slices = append(slices, k)
}
fmt.Printf("排序前,key:%v\n", slices)
sort.Strings(slices)
fmt.Printf("排序后,key:%v\n", slices)

for _, v := range slices {
fmt.Printf("map[%s]:%s\n", v, a[v])
}
}

func main() {
test5()
}

---------------map排序-----------------
3,hij
1,abc
2,def
4,opq
排序前,key:[3 1 2 4]
排序后,key:[1 2 3 4]
map[1]:abc
map[2]:def
map[3]:hij
map[4]:opq

map反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func test6() {
fmt.Printf("---------------map反转-----------------\n")
a := make(map[int]string, 5)
b := make(map[string]int, 5)

a[1] = "a"
a[2] = "b"
a[3] = "c"
a[4] = "d"
a[5] = "e"

for k, v := range a {
b[v] = k
}
fmt.Printf("反转后:%v\n", b)
}

func main() {
test6()
}

---------------map反转-----------------
反转后:map[a:1 b:2 c:3 d:4 e:5]

数组与切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func test1() {
arr := [5]int{1, 2, 3, 4, 5}
// 切片是对数组的引用 因此切片是引用类型
slices := arr[1:3]
fmt.Println(slices) //[2 3]
// 切片的长度是指切片中元素的个数
fmt.Printf("slice的长度:%d\n", len(slices)) //slice的长度:2
// 切片的容量是指切片所指向的数组的元素到其最后一个元素的个数
fmt.Printf("slice的容量:%d\n", cap(slices)) //slice的容量:4

for _, v := range slices {
fmt.Println(v) //2 、3
}

// 切片的最大长度为其容量,修改切片的长度和容量一样,会显示出切片所指向数组的元素到其最后一个元素
slices = slices[:cap(slices)]
fmt.Printf("修改后:%v\n", slices) // 修改后:[2 3 4 5]

// 切片的地址是其所指向数组的第一个值的地址
fmt.Printf("slices的地址:%p,所指向数组值的地址:%p\n", slices, &arr[1])
//slices的地址:0xc00008e038,所指向数组值的地址:0xc00008e038
}

func main() {
test1()
}

阅读全文 »

数组的定义与初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func arrs1() {
// 数组的定义与初始化
var arr [5]int

var arr1 [5]int = [5]int{1, 2, 3, 4, 5}

var arr2 = [5]int{1, 2, 3, 4, 5}

var arr3 = [...]int{1, 2, 3, 4, 5}

var arr4 = [...]int{0: 1, 4: 3}

arr5 := make([]int, 5)

fmt.Println(arr)
fmt.Println(arr1)
fmt.Println(arr2)
fmt.Println(arr3)
fmt.Println(arr4)
fmt.Println(arr5)
}

func main() {
arrs1()
}
----------------------
[0 0 0 0 0]
[1 2 3 4 5]
[1 2 3 4 5]
[1 2 3 4 5]
[1 0 0 0 3]
[0 0 0 0 0]

阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func makeSuffixFunc(suffix string) func(string) string {
return func(str string) string {
if !strings.HasSuffix(str, suffix) {
return str + suffix
}
return str
}
}

func adder() func(int) int {
var x int = 0
return func(val int) int {
x += val
return x
}
}

func main() {
f := adder()
fmt.Println(f(1))
fmt.Println(f(100))
fmt.Println(f(1000))

f1 := makeSuffixFunc(".jpg")
fmt.Println(f1("123"))
f2 := makeSuffixFunc(".txt")
fmt.Println(f2("hello"))
}
-----------------------------------print:
1
101
1101
123.jpg
hello.txt
  • 闭包可以读取函数内部的变量,并且会让这个变量始终保存在内存中

使用闭包的注意点

  • 由于闭包会使函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则可能会造成性能问题,还可能会导致内存泄漏
  • 不要随便修改父函数中的内部变量!!!!!

Go的内置函数

  • close 主要用来关闭channle
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    func main() {
    // 定义一个channel 长度为10 也可以不定义长度 make(chan int)
    ch := make(chan int, 10)
    ch <- 1
    ch <- 2
    /*
    有关 channel 的关闭,你需要注意以下事项:
    关闭一个未初始化(nil) 的 channel 会产生 panic
    重复关闭同一个 channel 会产生 panic
    向一个已关闭的 channel 中发送消息会产生 panic
    从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为 false 的 ok-idiom,可以用它来判断 channel 是否关闭
    关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息
    */
    // 关闭了channel
    close(ch)
    for v := range ch {
    fmt.Println(v)
    }

    // 消息被读完 这里读到0值
    x, ok := <-ch
    fmt.Println(x, ok)
    }
阅读全文 »

SPU:标准化产品单元

SPU = Standard Product Unit (标准化产品单元),SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。

SKU:库存量单位

SKU=stock keeping unit(库存量单位) SKU即库存进出计量的单位(买家购买、商家进货、供应商备货、工厂生产都是依据SKU进行的),在服装、鞋类商品中使用最多最普遍。 例如纺织品中一个SKU通常表示:规格、颜色、款式。
KU是物理上不可分割的最小存货单元。也就是说一款商品,可以根据SKU来确定具体的货物存量。

阅读全文 »