• Golang 快速上手 (3)



    结构体

    Golang 没有直接提供面向对象的概念,但是可以使用Go语法来实现面向对象编程的一些特性,例如:继承、多态、等特性。


    定义一个结构体

    type Animal struct {
    	Name string
    	Age  int
    }
    
    • 1
    • 2
    • 3
    • 4

    初始化结构体

    默认初始化

    	var dog Animal //定义一个结构体
    
    • 1

    使用键值对初始化

    animal := Animal{
    		Name: "animal",
    		Age:  19,
    	}
    
    • 1
    • 2
    • 3
    • 4

    列表初始化

    必须初始化结构体的所有字段。
    初始值的填充顺序必须与字段在结构体中的声明顺序一致。
    该方式不能和键值初始化方式混用。

    	cat := Animal{
    		"cat",
    		12,
    	}
    
    • 1
    • 2
    • 3
    • 4

    部分初始化

    	cat := Animal{
    		Name: "cat",
    	}
    
    • 1
    • 2
    • 3


    结构体的指针

    new 关键字创建结构体指针

    	pa := new(Animal)
    	fmt.Printf("pa: %T\n", pa) //pa: *main.Animal
    
    • 1
    • 2

    访问结构体成员

    指针和对象都可以通过逗号访问结构体对象的成员,如果指针通过逗号访问成员编译器会默认解引用,(*pa),name 等价于 pa.name

    	pa := new(Animal)
    	pa.Name = "fish"
    	pa.Age = 3
    
    	(*pa).Name = "dog"
    	(*pa).Age = 4
    	fmt.Printf("pa: %v\n", *pa) //pa: {dog 4}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    结构体的访问权限

    结构体名称首字母大写,那么在其他包内才可以定义Person对象,小写则无法访问。

    并且,结构体的属性首字母大写则其访问权限为public的,若为小写,则为private

    type Person struct {
    	Name string
    	Age  int
    }
    
    func main() {
    	p1 := Person{
    		Name: "marry",
    		Age:  30,
    	}
    
    	fmt.Printf("p1.Name: %v\n", p1.Name)
    	fmt.Printf("p1.Age: %v\n", p1.Age)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    编译器报错

    type Person struct {
    	name string
    	age  int
    }
    
    func main() {
    	p1 := Person{
    		name: "marry",
    		age:  30,
    	}
    	//p1.Name undefined (type Person has no field or method Name, but does have name)
    	fmt.Printf("p1.Name: %v\n", p1.Name)
    	fmt.Printf("p1.Age: %v\n", p1.Age)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    方法首字母大写才能在其他包内访问

    
    type Person struct {
    	name string
    	age  int
    }
    
    func (p Person) Run() {
    	fmt.Println("Person Run")
    }
    
    func main() {
    	p1 := Person{
    		name: "marry",
    		age:  30,
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    对象的方法

    Golang 没有直接提供面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性。


    定义结构体方法

    Golang 中的方法,是一种特殊的函数,定义于struct之上(与struct关联、绑定),被称为struct的接受者(receiver)。

    type Animal struct {
    	Name string
    	Age  int
    }
    
    func (a Animal) Eat() {
    	fmt.Println("Animal Eat")
    }
    
    func (a Animal) Sleep() {
    	fmt.Println("Animal Sleep")
    }
    
    func main() {
    
    	a := Animal{
    		Name: "dog",
    		Age:  10,
    	}
    
    	a.Eat()
    	a.Sleep()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    方法接收者类型

    //传递指针才能改变其值
    func (per *Animal) Set1(age_ int, name_ string) {
    	per.age = age_
    	per.name = name_
    }
    
    //值传递 内部修改不会影响外部
    func (per Animal) Set2() (int, string) {
    	per.age = age_
    	per.name = name_
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    方法注意点

    • 方法的receiver type可以是其他类型,如:type定义的类型别名、slice、 map、 channel、 func类型等

    • struct 结合它的方法就等价于面向对象中的类。只不过struct可以和它的方法分开,,并非一定要属于同一个文件,但必须属于同一个包。

    • 方法有两种接收类型: (T Type) 和(T *Type) , (T Type)是值传递,(T *Type)传递指针,内部修改会影响实参。

    • 方法就是函数,,Go中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一。

    • 如果receiver是一个指针类型, 则会自动解除引用。

    • 方法和type是分开的,意味着实例的行为(behavior)和数据存储(field)是分开的,但是它们通过receiver建立关联关系。



    接口

    go语言的接口,是一种新的类型定义,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
    语法格式和方法非常类似。

    定义一个接口

    type Displayer interface {
    	DisplayAudio(string)
    	Displayvideo(string)
    }
    
    • 1
    • 2
    • 3
    • 4

    实现接口

    type Displayer interface {
    	DisplayAudio()
    	DisplayVideo()
    }
     
    type MP4 struct {
    }
    
    func (mp4 MP4) DisplayAudio() {
    	fmt.Println("DisplayAudio")
    }
    
    func (mp4 MP4) DisplayVideo() {
    	fmt.Println("DisplayVideo")
    }
    
    func main() {
    
    	var player Displayer = MP4{}
    
    	player.DisplayAudio()
    	player.DisplayVideo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    要注意,实现接口必须实现接口中的所有方法,不然无法将结构体赋值给接口类型变量


    值类型receiver 指针类型receiver

    和方法传参一样,必须传递指针才能在内部修改其值。。



    接口和struct

    一个结构体可以实现多个接口
    多个结构体实现同一个接口(实现多态)


    一个结构体实现多个接口

    type Displayer interface {
    	DisplayAudio()
    	DisplayVideo()
    }
    
    type PlayGamer interface {
    	PlayGame()
    }
    
    type MP4 struct {
    	Volume int
    }
    
    func (mp4 MP4) DisplayAudio() {
    	fmt.Println("DisplayAudio")
    }
    
    func (mp4 MP4) DisplayVideo() {
    	fmt.Println("DisplayVideo")
    }
    
    func (mp4 MP4) PlayGame() {
    	fmt.Println("PlayGame")
    }
    
    func main() {
    
    	var player1 Displayer = MP4{
    		Volume: 10,
    	}
    	var player2 PlayGamer = MP4{
    		Volume: 10,
    	}
    
    	player1.DisplayAudio()
    	player1.DisplayVideo()
    
    	player2.PlayGame()
    }
    
    • 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

    实现多态

    多个结构体实现同一个接口 (多态)

    package main
    
    import "fmt"
    
    type Per interface {
    	eat()
    	sleep()
    }
    
    type Dog struct {
    }
    
    type Cat struct {
    }
    
    func (d Dog) eat() {
    	fmt.Println("Dog eat")
    }
    
    func (d Dog) sleep() {
    	fmt.Println("Dog eat")
    }
    
    func (d Cat) eat() {
    	fmt.Println("Cat eat")
    }
    
    func (d Cat) sleep() {
    	fmt.Println("Cat eat")
    }
    
    
    type Person struct {
    }
    
    func (p Person) care(per Per) {
    	per.eat()
    	per.sleep()
    }
    
    //其实就是多态的使用
    //OCP 开放扩展 关闭修改
    func main() {
    
    	dog := Dog{}
    	cat := Cat{}
    
    	person := Person{}
    	person.care(dog)
    	person.care(cat)
    }
    
    • 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

    简单地实现一个多态的案例,传递Dog对象调用的就是Dog类的方法,传递Cat对象调用Cat类的方法。



    继承

    定义一个父类

    type Animal struct {
    	Name string
    	Age  int
    }
    
    func (a Animal) Sleep() {
    	fmt.Println("Animal Sleep")
    }
    
    func (a Animal) Eat() {
    	fmt.Println("Animal Eat")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    子类可以重定义父类的方法

    type Dog struct {
    	Animal
    }
    func (d Dog) Eat() {
    	fmt.Println("Dog Eat")
    }
    
    func main() {
    		d1 := Dog{
    		Animal: Animal{
    			Name: "Dog",
    			Age:  3,
    		},
    	}
    	d1.Eat() //Dog Eat
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16


    Golang 构造函数

    golang没有构造函数的概念,但可以使用函数来模拟构造函数的的功能。

    type Student struct {
    	Name string
    	Age  int
    }
    
    func NewStudent(name string, age int) (*Student, error) {
    
    	if age > 99 || age < 0 {
    		return nil, fmt.Errorf("age input error")
    	}
    
    	return &Student{Name: name, Age: age}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13


    Golang 包

    在这里插入图片描述

    包可以区分命令空间(一个文件夹中不能有两个同名文件),也可以更好的管理项目。go中创建一个包,一般是创建一个文件夹, 在该文件夹里面的go文件中,使用package关键字声明包名称,通常,文件夹名称和包名称相同。并且同一个文件下面只有一个包

    包的注意点:

    一个文件夹下只能有一个package

    • import后面的其实是GOPATH开始的相对目录路径,包括最后一段。但由于一个目录下只能有一个package,所以import 一个路径就等于是import了这个路径下的包。

    需要注意,这里指的是“直接包含”的go文件。 如果有子目录,那么子目录的父目录是完全两个包。
    比如实现了一个计算器package,名叫calc,位于calc目录下。但又想给别人一个使用范例,于是在calc下可以建个example子目录(calc/example/) ,这个子目录里有个example.go (calc/example/example.go) 。此时,example.go可以是main包,里面还可以有个main函数。

    • 一个package的文件不能在多个文件夹下

    如果多个文件夹下有重名的package,它们其实是彼此无关的package。
    如果一个go文件需要同时使用不同目录下的同名package,需要在import这些目录时为每个目录指定一个package的别名。|


    包的导入方式

    匿名导入

    包一但导入就必须使用其内部函数,如未使用编译器报错,但如果有一个需求,需要调用 test_lib 的init函数,又不用调用test_lib内的函数就可以用 _ 匿名导入包。

    import (
    	"fmt"
    	_ "test_lib"
    )
    
    • 1
    • 2
    • 3
    • 4

    重命名包:

    如果包的名称太长,可以对其重命名使用。

    import (
    	"fmt"
    	my_lib "test_lib"
    )
    
    func main() {
    	my_lib.API()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    展开包:

    test_lib 包名前加逗号,那么调用test_lib 内的函数可以不加上包名,就像函数定义在当前包一样使用。

    import (
    	"fmt"
    	. "test_lib"
    )
    func main() {
    	API() // test_lib.API() 直接调用
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    不推荐使用,如果两个包都使用逗号展开包,如果两个包内存在同名函数就无法区分。


    包管理工具 module

    go modules 是golang 1.11新加的特性,用来管理模块中包的依赖关系。

    常用指令

    初始化模块

    go mod init <项目模块名称>
    
    • 1

    依赖关系处理,根据go.mod文件

    go mod tidy
    
    • 1

    将依赖包复制到项目下的vendor目录。
    如果无法科学上网,可以使用这个命令,随后使用go build -mod=vendor编译

    go mod vendor
    
    • 1

    显示依赖关系

    go list -m all
    
    • 1

    显示详细依赖关系

    go list -m -json all
    
    • 1

    下载依赖 ([path@version] 不是必写的)

    go mod download [path@version]
    
    • 1

    在这里插入图片描述


  • 相关阅读:
    HiveSql调优系列之Hive严格模式,如何合理使用Hive严格模式
    java汽车租赁超时罚款系统springboot+vue-前后端分离-e36ht
    MySQL数据库——DQL操作——基本查询
    POJ 1222 EXTENDED LIGHTS OUT 反转+点灯游戏
    MIT6.S081Lab2:system calls
    C# 文本绘制
    java计算机毕业设计springboot+vue城市轨道交通线路查询系统
    Alexa Fluor 远红色染料|DBCO-AF647|AF-647-DBCO
    C语言微博用户管理系统
    vue2中的props属性
  • 原文地址:https://blog.csdn.net/juggte/article/details/125382130