106【GoLand-基础2】

137 阅读33分钟

Golang基础二

Golang基础复习

1.Go-常用命令

直接再终端输入 go help 即可显示所有的go命令以及相应命令功能简介,主要有下面这些。

  1. build: 编译包和依赖,生成.exe go build xxx.go -> ./hello.exe 💥
  2. clean:移除对象文件
  3. doc:显示包或者符号的文档
  4. env:打印go的环境信息
  5. bug:启动错误报告
  6. fix : 运行go tool fix
  7. fmt: 运行gofmt进行格式化
  8. generate: 从processing source生成go文件
  9. get: 下载并安装依赖 go get xxxx 💥
  10. install 编译并安装包和依赖 go list
  11. list: 列出包
  12. run: 编译并运行go程序 💥
  13. test: 运行测试 go run xxx.go
  14. tool: 运行go提供的工具
  15. version: 显示go的版本
  16. vet: 运行go tool vet

Go语言的所有包全在这里搜索: pkg.go.dev/
在这里插入图片描述

(1).添加依赖-(配置代理)

go env -w GO111MODULE=on  
go env -w GOPROXY=https://goproxy.cn  #一定要添加代理,要不然
go get xxxx

实战演练:

PS C:\Environment\GoWorks\src\demo> go get github.com/go-sql-driver/mysql
go: added github.com/go-sql-driver/mysql v1.7.0

添加完毕之后会在 go.mod中新增
在这里插入图片描述

2.创建一个项目的步骤

  1. 创建项目
  2. 初始化项目
go mod init 项目名
  1. 创建包
    在这里插入图片描述
  2. 创建模块
    创建一个包,然后下面放入一些.go文件
    在这里插入图片描述
  3. 相互调用

test包下hello.go文件

方法名开头大写就是公有方法。小写就是私有方法

package test

// 方法名为小写的话,那么就是私有的
func hello() string {
	return "hello"
}

//  方法名为大写的话,那么就是公用

func Hello() string {
	return "Hello"
}

主项目下创建一个main.go文件。通过包名进行调用方法

package main

import "demo/test"  //导入包

func main() {
	// 注意我们这里用的是 包名. 进行调用的,因为Go语言中没有类的概念,
	println(test.Hello()) //调用共有的,私有的调用不了
}

在这里插入图片描述


3.golang标识符、关键字、命名规则

(1).标识符

标识符的英文是: identifer.通俗的讲:就是给变量、常量、函数、方法、结构体、数组、切片、接口起名字。

标识符的组成

  1. 标识符只能由数字、字母和下划线组成。
  2. 只能以字母和下划线开头
  3. 标识符区分大小写

(2).关键字

25个关键字
在这里插入图片描述
在这里插入图片描述

(3).命名规则

  1. 包名称:

保持package的名字和目录一致,尽量能够见名识意。包名应该小写单词,不要使用下划线或则混合大小写。

package demo
  1. 文件命名:

见名识意应该为小写单词,使用下划线分割每一个单词

user_mapper.go
  1. 结构体命名

采用驼峰式命名,首字母根据公有私有访问控制大写或者小写

type student struct{}  //私有结构体
type Student struct{}  //公有结构体
  1. 接口命名

采用驼峰式命名,首字母根据公有私有访问控制大写或者小写。单个函数的结构名以 “er” 作为后缀。

type Reader interface{
	Read(p []byte)
}
  1. 变量命名

采用驼峰式命名,首字母根据公有私有访问控制大写或者小写。但遇到特有名字的时候,要遵循以下的规则:

如果变量为私有,且特有名词为首个单词,则使用小写,如appService若变量类型为bool类型,则名称应为Hash,is,Can或Allow开头。

var isExist bool
var hasConfict bool
var canMange bool
var allowGitHook bool
  1. 单元测试

单元测试文件命名规范为 example_test.go。测试用例额的函数名必须以Test开头。例如: TestExample。

4.变量

  1. 变量的定义
var identify type
---
var(
	idetify type
	idetify type
)
--- 语法糖,这里只能在函数的内部我们才能使用,函数外部不能使用!!!
identy:=value
---
  1. 变量的初始化
var identify type=value
---
var(
	identify type=value
)
---
// 类型会对其进行自动寻找,可以不用可以设置变量的类型的
var identify1,identify2,identifyN =value1,value2,value3
---
// 函数返回多个值时
identify1,identify2=function3()
---
// 匿名变量
_,identify=function3()

5.常量

  1. 常量的定义且赋值
const identify type=value

常量一旦定义之后,我们就不能对其进行修改的操作了。
---
const(
	identify type=value
)
  1. iota

iota比较特殊,可以被认为是一个可被编译器修改的常量,它默认开始的值是0,每调用一次加1.遇到const关键字的时候被重置为0.

1. 连续会一直增加
const(
	a1=iota  //0
	a2=iota  //1
	a3=iota  //2
)-> 0 1 2

2.使用匿名变量跳过某些值
const(
	a1=iota  //0
	_     
	a2=iota  //2
)-> 0 2
2. 连续的时候被打断
3. const(
	a1=iota  //0
	a2=100  //100 1
	a3=iota  //2
)->0 100 2

6.数据类型

字符串、数字型、布尔型、数组、切片、接口、函数、管道、结构体。

(1).字符串类型

Go语言中的字符串是被双引号括起来的值。被反引号`引起来的字符串用来创建多行的消息,HTML以及正则表达式.

%T 查看类型   %c 查看字符   %v 万能取值 %s 取字符串

1. 字符串-双引号
var identify string="value"
var identify=""
identify:=""

2. 字符串-反引号
var identify string=`
		line1
		line2
`
3.字符串的拼接可以使用+号进行拼接 
4.可以使用printf进行格式化进行拼接
5.使用strings.Join([]string{identify1,identify},",")
6.也可以使用bytes.Buffer进行字符串链接 (效率较高)
	var buffer bytes.Buffer
	buffer.WriteString("hello")
	buffer.WriteString(",")
	buffer.WriteString("jsxs")
	s := buffer.String()
7. 转义字符
\n ->换行  \t ->4个空格 \\ ->网络路径 
8.字符串切片操作
var identify="xxxxx" 
| 获取->m-n的字符(包头不包尾) identify[m:n]
| 获取->m- 到最后一个的字符 identify[m:]
| 获取-> -n 从开始到n的字符 identify[:n]	
9.获取字符串的长度 len(str)
10.字符串进行切割 strings.Split(str,"切割字符")->输出的是一个数组
11.是否包含字符串 strings.Contains(str,"xxx") -》bool
12.strings.ToLower(str) ->全部小写 strings.ToUpper(str) ->全部大写
13.strings.HahsPrefix(str,"xx")->是否以xx开头 string.HashSuffix("xxx")->是否以xx结尾
14. 查看某个字符串所在的索引位置 strings.Index(str,"xx")

(2).指针类型->引用类型

Go语言中的函数传参都是值拷贝,当我们想要修改某一个变量的时候,我么可以创建一个指向该变量的地址的指针变量传递数据使用的指针,而无需拷贝数据

类型指针不能进行偏移和运算。

Go语言中的指针操作非常简单,只需记住两个符号: &(取地址)和 *(根据地址取值)

指针地址和指针类型

每个变量在运行时都有一个地址,这个地址代表变量在内存中的位置。Go语言中使用 & 字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、float、bool、string、array、struct) 都有对应的指针类型,如" *int 、 *int64、 *string等

var identify *type=&idenify

指针地址和指针类型

一个指针指向了一个值得内存地址。(也就是我们声明了一个指针之后,可以像变量赋值一样,把一个值的内存放入到指针当中)

类似于变量和常量,在使用指针前我们需要声明指针。

1.指针的定义语法  
var var_name *var_type

package main

import "fmt"

func main() {
	var ip *int
	fmt.Printf("%T\n", ip)   //*int
	fmt.Printf("%v\n", ip)   //<nil>
	var num int = 10         //创建一个变量,也就是分配了一个地址
	fmt.Printf("%T\n", &num) //查看取地址的类型
	ip = &num                // ip指向num的地址
	fmt.Printf("%v", *ip)  //获取值
}
  1. 指向数组的指针

需要借助数组进行遍历取数组的地址

1.定义语法
var ptr [n]*int;
2. 指针数组的取地址需要通过遍历的方式进行遍历赋值。
package main

import "fmt"

func main() {
	var ip [4]*int
	fmt.Printf("%T\n", ip)
	fmt.Printf("%v\n", ip)
	num := [4]int{1, 2, 3}
	fmt.Printf("%T\n", &num)
	for i := 0; i < len(num); i++ {
		ip[i] = &num[i] // 数组指针的赋值
		fmt.Printf("%v\t", *ip[i])
	}
}

(3).数组类型

数组是相同数据类型的一组数据的集合,数组一旦定义长度就不能修改,数组可以通过下标(或者索引)来访问元素。

1.数组的定义
var identify [n]type=[n]type{value}
2.假如说定义的数组长度没有超过定义的长度,那么就补空
3.数组的初始化,用"..."可以不用设置固定长度,会自动根据我们添加的值进行分配空间
var identify=[...]int{xxx}
4.可以指定索引赋值  arr := [...]int{索引:value,索引:value}
arr := [...]int{0: 1, 2: 2, 5: 3}
	for _, i2 := range arr {
		println(i2)
	}
-> 1 0 2 0 0 3	
5.数组的值可以被修改,不像字符串那样不可以被修改。
6.获取数组的长度 len(identify)
7.数组的遍历: 普通for 增强for
-----------

(4).切片类型 -> 引用类型

  1. 切片的定义和初始化

切片同属的讲:就是一个动态数组,增加了自动扩容的功能。切片是一个拥有相同类型元素的可变长度的序列。

1.切片的定义: 声明一个数组只要不添加长度即可
(1). var identify []type=[]type{}
(2). identify []type
(3). identify:=[]type{}
2.切片是引用类型,可以使用make函数来创建切片。
var slice []type=make([]type,len)
可以简写为:
slice:=make([]type,len)
make([]T,len,capacity) ->这里的len是数组的长度并且是切片的初始长度
3.获取长度  len(str) csp(str)
-------------------------------------
4.切片的初始化->和数组的初始化一样
identify:=[]type{xxxx}
5.切片的初始化使用数组->可以先设置一个数组然后切片切取
s:=[3]int{1,2,3} -> slice:=s[:]
6.切片的遍历 和数组的遍历一样 for 和 增强for

切片是一个动态数组,可以使用append()函数添加元素,go语言中并没有删除元素的专用方法,我们可以使用切片本身的特性来删除元素,由于,切片是引用类型,通过赋值的方式,会修改原有的内容,go提供copy()函数来拷贝切片

  1. 切片的CRUD

make() 用于分配地址

1.添加元素 
append(数组,添加的元素)
(1).添加到元素的尾部
	var str = []int{}
	ints := append(str, 10, 20, 30) //进行添加的操作
(2).添加到指定元素的后面
---- 整形指定位置插入
func insertByIndex(index int, value int, slice []int) []int {
	var str2 = make([]int, len(slice))
	copy(str2, slice)                   //把ints拷贝给str2
	str3 := []int{}                     //定义一个空切片
	str3 = append(slice[:index], value) //先获取头部
	str3 = append(str3, str2[index:]...)
	return str3
}
----------
2.删除元素
(1).利用空切片进行删除的操作  ints->是一个数组,删除索引为1的值
	var slice []int = []int{}
	intss := append(slice, ints[:1]...)
	intss = append(intss, ints[2:]...)
	fmt.Printf("%d", intss)
(2).直接追加的方式删除  A=append(a[:index],a[index+1]...)
	ints = append(ints[:1], ints[2:]...)
	fmt.Printf("%d", intss)
3.修改 -> 

直接 slice[index]=?

4.查找
(1).通过下标进行寻找  -> slice[index]
(2).通过值进行查找索引  ->遍历
----------------------------
5.	copy()函数
(1).如果是直接赋值的话,那么内存地址一样,修改s2的时候同一个地址的s1也会被修改
直接赋值: ---->s1:=s2
(2).如果利用copy()函数的话,那么内存地址不一样。修改s2那么s1不会改动
基本步骤: 
 先分配给s1地址->  var s2=make([]int,4) //切片类型,四个空间
 开始拷贝 copy(s1,s2) -> 意思就是把s2拷贝给s1

(5).函数类型 ✅

函数在go语言中属于 一级公民 ,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型,这些构成了函数的签名

1 Go语言中函数的特性

  1. go语言中有3中函数: 普通函数匿名函数方法(定义在struct上的函数)
  2. Go语言中不允许函数重载,也就是说不允许函数同名
  3. Go语言中的函数不能嵌套函数但可以嵌套匿名函数
  4. 函数可以作为参数传递给另一个函数
  5. 函数的返回值可以是一个函数
  6. 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数
  7. 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再讲副本传递给函数
  8. 函数参数可以没有名称
  1. Go语言函数的定义
1. 定义一个普通函数
 func function_name([parameter list] type) [return_type]{
	函数体
}
  1. 函数的返回值

函数可以有0个或则多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。

  1. return 关键字中指定了参数时,返回值可以不用名称。如果 return 省略参数,则返回值部分必须带名称
  2. 当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
  3. 即使返回值重名了,return中也可以强制指定其他返回值的名称,也就是说return的优先级更高。
  4. 命名的返回值时预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量不能重复定义
  5. return 中可以有表达式,但不能出现赋值表达式,这和其他语言可能有所不同。列如 return a+b是正确的,但return c=a+b是错误的。
1. 没有返回值
func test1(){}
2. 有参数且返回值有名字
func test(a int) (sum int){ sum=1+a return sum}
3.有参数但返回值没有名字
func test(a int) (int){sum:=1+a return sum}
4有参数且有多个返回值
func test()(sum int,sum2 int){return sum,sum2}
5.覆盖命名返回值
func test()(a int){return sum} ->sum会覆盖a
6.如果返回值不想要,可以用匿名变量进行接受
  1. Go函数的参数
  1. go函数可以有0个或多个参数,且参数需要指定数据类型
  2. 声明函数时的参数列表叫做形参,调用时传递的参数叫做实参
  3. go语言的函数是通过值传递进行传递的,意味着传递给函数的参数是一个拷贝的副本,形参改变实参不会变化。
  4. go语言的函数可以使用可变长参数,有时候并不能呢确定参数的个数。
1. 可变参数 ->可变参数只能放在最后且只有一个
	func test(args...int){  
	函数体
}
  1. 函数类型和函数变量

可以使用type 关键字来定义一个函数类型

1.定义一个函数类型的f1,f1是一个类型并不是一个变量名
type f1 func(int,int) int
2.相同类型的函数赋值给函数变量
package main

import "fmt"

func test(a int) int {
	return a
}
func main() {

	type f1 func(int) int // 这里的就是定义一个f1这样的类型
	var ff f1  //定义f1类型的变量ff
	ff = test  // 把test变量赋值给ff变量
	fmt.Printf("%v", ff(2)) //开始调用
}
---------------------**********-------------------------

6.高阶函数

go语言的函数,可以作为函数的参数,传递给另外一个函数,可以作为另外一个函数的返回值返回,高阶函数就是参数类型带函数参数的函数

1.go语言函数作为参数
package main

func test(a int) int {
	return a
}
func test2(b int, f func(int) int) int {
	return b + f(b)
}
func main() {
	println(test2(1, test))
}
-------------------

7.匿名函数

go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现以一下简单功能的调用。所谓匿名的函数就是: 没有名称的函数
语法格式如下:

1.匿名函数的调用和执行
func(){函数体}()
-------
	// 直接执行
	func() {
		println("您好,我是小明")
	}()
2.匿名函数设置变量名 max,只有匿名函数才能设置变量名。	
	// 给匿名设置一个变量名
	max := func(a int, b int) {
		if a > b {
			fmt.Printf("%v", a)
		} else {
			fmt.Printf("%v", b)
		}
	}
	max(1, 2)
  1. 闭包

闭包可以理解成: 定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部链接起来的桥梁。或者说是函数和其引用环境的组合体
闭包指的是一个函数和与其相关的引用环境组合而成的实体,简单说,闭包+函数+引用环境

闭包: 函数内部定义一个函数,且外层函数的返回值类型是内层函数的函数类型。特性: 内层函数可以调用外层变量。外层变量一直在内层函数的作用域里面

外层变量的作用域扩展至内层函数中。前提是函数没有再次被从新分配空间

package main

import "fmt"

func test(a, b int) func(c, d int) int {
	sum1 := a + b //这里是闭包,它只要函数没有再次被定义,那么他的作用域就被内层函数所包含
	sum := func(c, d int) int {
		return sum1 + c + d
	}
	return sum //返回值是内层函数类型
}
func main() {
	f := test(1, 2) //赋值变量类型
	fmt.Printf("%T\n", f)
	fmt.Printf("%v", f(1, 2))

}

在这里插入图片描述

  1. 递归

函数内部调用函数自身的函数称为递归函数。
递归函数的最重要的三点:

  1. 递归是自己调用自己
  2. 必须先定义一个函数的退出条件,没有推出条件,递归将称为死循环
  3. go语言递归函数很可能会产生一大堆的栈溢出问题,也很可能会出现占空间内存溢出问题。
package main

func test(a int) int {
	if a == 1 {   //直接返回这个值,不会再向下执行了。
		return 1
	}
	return a + test(a-1)
}
func main() {
	println(test(5))
}
  1. defer 语句

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,即延迟处理的语句按defer定义的逆序进行执行,也就是说: 先被defer的居于最后执行,最后defer的语句最先执行。

defer特性:

  1. 关键字defer用于注册延迟调用
  2. 这些调用直到return前才被执行,因此,可以用来做资源清理
  3. 多个defer语句,按先进后出的方式执行。
  4. defer语句中变量,在defer声明就决定了。
1.如何定义一个defer语句
defer 语句[可以是输出语句/特殊函数]
2.如果有多个defer会按照先定义后执行的顺序进行处理
3.defer,只是进行了延迟的处理,但里面的参数在声明的时候就已经配置好了
  1. init函数

Go语言有一个特殊的函数 init 函数,先于main 函数执行,实现包级别的一些初始化操作

init函数的特点

  1. init先于main函数 自动执行,不能被其他函数所调用
  2. init函数没有输入参数、返回值
  3. 每个包可以有多个init函数
  4. 包的每个源文件也可以有多个init函数,这点比较特殊
  5. 同一个包的init执行顺序,Go没有明确定义,编程时要注意程序不要依赖这个执行顺序
  6. 不同包的init函数按照包导入的依赖关系决定执行顺序

GO初始化顺序
初始化顺序: 变量初始化->init()->main()

1. init函数的定义
func init() { //没有参数、没有返回值 不能被调用
	函数体
}
2.go语言的执行顺序 | 变量初始化->init()->main()
package main

func init() { //没有参数、没有返回值
	println("init........") //  ->2
}

var value int = test() //变量的初始化  ->1

func test() int {
	println("初始化数据....")
	return 100
}
func main() {
	println("main....") //  ->3
}
3.在一个包中可以定义多个init()函数,执行顺序是从上到下

(6).布尔类型

var identify bool=flag
identify:=flag

(7).数字类型

Go语言支持整数和浮点型数字,并且原生支持复数,其中位的运算采用补码。

1.整数类型

有符号(xxx)、无符号(uxxx)。

  %d ->十进制取值
  %b->二进制取值 
  %o->八进制取值 
  %x/%X->十六进制

2.浮点型

%f ->取值
%.nf%->保留n位小数 

(8).map类型 -> 引用类型

map是一种 key:value 键值对的数据结构容器。map内部实现是哈希表(hash)。map最重要的一定是通过key来快速检索数据,key类似于索引,指向数据的值。map是引用类型的。

map的定义与初始化

可以使用内建函数make, 也可以使用map关键字来定义map

1. 使用map来定义
var 变量名称 map[key的数据类型]value的数据类型
2. 使用make函数
变量名称:=make(map[key的数据类型]value的数据类型)
(1).两种初始化方式以及两种定义方式
	var m1 map[string]string           //  利用map定义一个map  初始化为空
	m2 := make(map[string]string)      //利用make定义一个map 初始化为空
	var m3 = map[string]string{"name": "李明", "age": "18", "high": "1.75"} //定义并初始化数据
	var m4 = map[string]string{"age": "18"}  //定义并初始化
*******************************
3.通过key值进行查找数据  
	变量名[key]
fmt.Printf("%v", m1["name"])
4.判断某一个key是否存在💥
	v,ok:=变量名[key]  v是value ok是布尔类型
v, ok := m1["name"]

map的遍历

1. 可以只获取key
2. 可以获取key和value
3. 可以只获取value
for key,value:=range 变量名{}
-----
	m1 = map[string]string{"name": "李明", "age": "18", "high": "1.75"} //初始化数据
	for k := range m1 {
		fmt.Printf("%v\t", k)
	}
	for k, v := range m1 {
		fmt.Printf("%v %v ", k, v)
	}
	for _, v := range m1 {
		fmt.Printf("%v\t", v)
	}

(9).结构体类型

  1. 类型定义和类型别名
1.  类型定义语法
type NewType Type  -》等同于Type

------------测试使用
package main

func main() {
	// 类型定义->实际上就是说 MyInt 等同于 int 
	type MyInt int
	// i 为MyInt类型的变量,实际上等同于int类型的变量
	var i MyInt 
	i=100
	println(i)
}
-------------
2. 类型别名定义
type NewType=Type   ->就是起一个别名

-------测试
package main

func main() {
	// 类型别名定义->实际上就是说 MyInt 等同于 int
	type MyInt=int
	// i 为MyInt类型的变量,实际上等同于int类型的变量
	var i MyInt
	i = 100
	println(i)
}
  1. 结构体的定义

go语言没有面向对象的概念,结构体的定义与类型定义类似,只不过多了一个struct关键字。

1. 结构体的定义
type 结构体类型名称 struct{
	成员定义;
	成员定义;
	成员定义;
}
-----------------测试
package main

import "fmt"

type Student struct {
	name    string
	age     int
	sex     int
	teacher string
}

func main() {
	var s1 Student 
	s1.name = "李明"
	s1.age = 20
	s1.sex = 0
	s1.teacher = "王先生"
	fmt.Printf("%T\n", s1)
	fmt.Printf("%v\n", s1)
}
-----------------测试
2.匿名结构体定义:
var 结构体名字 struct{
	xxxx
	xxxx
}
-----------------测试
package main

import "fmt"

func main() {
	var Student struct {
		name    string
		age     int
		sex     int
		teacher string
	}
	Student.age = 20
	Student.name = "jsxs"
	Student.sex = 1
	Student.teacher = "丽丽"
	fmt.Printf("%v\n", Student)
}
-----------------测试
  1. 结构体的初始化
1.第一种: 以键值对的方式进行初始化
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	var jsxs Person //类型定义
	//  1.第一种定义方式 -> 使用键值对
	jsxs = Person{ //进行初始化
		id:    1,
		name:  "heh",
		age:   0,
		email: "22123121", //最后一个也要添加逗号
	}
2.第二种:按照顺序的方式进行初始化。顺序一定要保持一致
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	var jsxs Person	
	jsxs = Person{1, "dsds", 0, "dssdsd"}
3.第三种:利用语法糖方式
jsxs:=Person{
	xx:xx
}
  1. 结构体指针

结构体指针和普通的变量指针相同.

1.普通方法创建结构体指针
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	jsxs := Person{1, "dsds", 0, "dssdsd"}
	var p1 *Person // 定义一个结构体指针
	p1 = &jsxs     //指针指向地址
	fmt.Printf("%v\n", jsxs)
	fmt.Printf("%v\n", *p1)
2. new创建结构体指针  new(结构体名字)
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	jsxs := Person{1, "dsds", 0, "dssdsd"}
	var p1 = new(Person)  ///---》利用new
	//var p1 *Person // 定义一个结构体指针
	p1 = &jsxs //指针指向地址
	fmt.Printf("%v\n", jsxs)
	fmt.Printf("%v\n", *p1)	
3.结构体指针的访问-》  指针变量名.属性/ (*指针变量名).属性
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	jsxs := Person{1, "dsds", 0, "dssdsd"}
	var p1 = new(Person)
	//var p1 *Person // 定义一个结构体指针
	p1 = &jsxs //指针指向地址
	fmt.Printf("%v\n", jsxs)
	fmt.Printf("%v\n", *p1)
	fmt.Printf("%v", p1.id)  //与下面的结果一致
	fmt.Printf("%v", (*p1).id) //与上面的结果一致 (*)可以省略	

4.结构体作为函数参数

go结构体可以像普通变量一样,作为函数的参数,这里分为两种情况

  1. 直接传递结构体,这是一个副本(拷贝),在函数内部不会改变外面结构体内容
  2. 传递结构体指针,这时在函数内部,能够改变外部结构体内容
1. 值传递: 形参改变实参不变
package main

import "fmt"

type Person struct {
	id    int
	name  string
	age   int
	email string
}

func test(p1 Person) {
	p1.id = 1
	p1.name = "sdsds"
	p1.age = 20
	p1.email = "sdsd"
	fmt.Printf("%v\t", p1)
}
func main() {
	person := Person{2, "SDDF", 0, "GFDG"}
	test(person) //值传递
	fmt.Printf("%v\t", person)
}
--------------
2.指针传递: 形参改变实参也改变
package main

import "fmt"

type Person struct {
	id    int
	name  string
	age   int
	email string
}

func test(p1 *Person) { //指针接受
	p1.id = 1
	p1.name = "sdsds"
	p1.age = 20
	p1.email = "sdsd"
	fmt.Printf("%v\t", p1)
}
func main() {
	person := Person{2, "SDDF", 0, "GFDG"}
	p1 := new(Person) // 创建一个指针
	p1 = &person
	test(p1) //传递一个地址
	fmt.Printf("%v\t", *p1)
}
  1. 结构体的嵌套
package main

import "fmt"

type Person struct {
	name string
	age  int
	pie  Pie  //人类拥有宠物
}

type Pie struct {
	name string  //姓名
	sort string  //种类
}

func main() {
	p := Person{
		name: "jsxs",
		age:  20,
		pie: Pie{ //初始化结构体
			name: "小黄",
			sort: "金毛",
		},
	}
	fmt.Printf("%v", p)
}

(10).方法 ✔™✅

Go语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对象里面有类方法概念。我们也可以声明一些方法,属于某一个结构体。

Go语言方法的语法

Go中的方法是一种特殊的函数,定义struct之上(与struct关联,绑定),被称为struct的接收者(receive)通俗的讲: "方法就是有接收者的函数"

  1. 方法的定义
1.方法的定义 -> 一个方法和一个函数非常相似,多了一个接受类型

type 结构体名 struct{}

func (接受该方法的结构体 结构体名) 方法名称(参数列表) 返回值类型 {}
func (接受该方法的结构体 *结构体名) 方法名称(参数列表) 返回值类型 {}

***********************************************************
值传递 
package main

import "fmt"

type Person struct { // 1.定义一个结构体
	name string
}

func (per Person) eat() { //2. 给结构体Person添加一个eat()的方法
	fmt.Printf("%v,eat....", per.name)
}

func (per Person) sleep() { //3. 给结构体Person添加一个sleep()的方法
	fmt.Printf("%v,sleep....", per.name)
}
func (per Person) sum(a, b int) int {
	return a + b
}
func main() {
	person := Person{ // 4.初始化结构体
		name: "李明",
	}
	person.eat() // 5.结构体本身调用自己的属性
	person.sleep()
	println(person.sum(1, 2))
}
***********************************************************
  1. 方法接收者类型

结构体实列,有值类型指针类型,那么方法的接受者是结构体,那么有值类型和指针类型。区别就是接收者是否赋值结构体副本。值类型拷贝,指针类型不拷贝

指针传递
package main

import "fmt"

type Person struct { // 1.定义一个结构体
	name string
}

func (per *Person) test() {  //2.添加一个指针
	per.name = "sssss"
	fmt.Printf("%v", per.name)
}
func main() {
	person := Person{ // 4.初始化结构体
		name: "李明",
	}
	p := new(Person)
	p = &person
	fmt.Printf("%v", person)
	p.test()  // 通过指针进行调用
	fmt.Printf("%v", person)
	//println(person)
}

(11).接口类型

接口像是一个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。Go语言的接口是一种新的类型定义,他把所有的具有共性的方法定义在一起任何具有其他类型只要实现了这些方法就是实现了这个接口

1.接口的初始化

1.定义接口
type interface_name interface{
	方法名 [返回值]
}
2. 定义结构体
 type struct_name struct{
	成员变量
}
3.实现接口的方法
func(结构体变量 结构体变量名) 方法名() [返回值类型]{
方法实现
}
4.声明接口有哪个结构体来实现

在接口中定义,若干个空方法。这些方法都具有通用性。
假如说一个结构体要实现一个接口,那么他要实现结构体的所有方法;否则会报错

package main

import "fmt"

// 定义一个接口存在两个方法

type USB interface {
	read()  //定义一个读的方法
	write() //定义一个写的方法
}

// 定义一个结构体

type Computer struct {
	name string
}

// 给结构体添加一个方法-》实现接口的方法

func (c Computer) read() {
	fmt.Printf("%v\n,read......", c.name)
}

// 给结构体添加一个方法-》实现接口的方法
func (c Computer) write() {
	fmt.Printf("%v\n,write......", c.name)
}
func main() {
	var usb USB  // 定义一个接口变量
	computer := Computer{
		name: "HP",
	}
	usb = computer  //声明这个接口将有computer这个结构体来实现
	usb.read() //接口调用
	usb.write()
}
  1. 接口值类型接收者和指针类型接收者

值接受就是一个拷贝,指针接受就是非拷贝

1.值传递
******************************************************
package main

import "fmt"

// 定义一个接口存在两个方法

type USB interface {
	read()  //定义一个读的方法
	write() //定义一个写的方法
}

// 定义一个结构体

type Computer struct {
	name string
}

// 给结构体添加一个方法

func (c Computer) read() {
	c.name = "sds"   // 在里面惊进行修改 ------- 值操作
	fmt.Printf("%v,reading......\n", c.name)
}

// 给结构体添加一个方法
func (c Computer) write() {
	c.name = "sds"   // 在里面惊进行修改 ------- 值操作
	fmt.Printf("%v,write......\n", c.name)
}

func main() {
	var usb USB // 定义一个接口变量
	computer := Computer{
		name: "HP",
	}
	usb = computer //声明这个接口将有computer这个结构体来实现
	usb.read()     //接口调用
	usb.write()
	fmt.Printf("%v", computer.name)
}
******************************************************
2.指针传递
******************************************************
package main

import "fmt"

// 定义一个接口存在两个方法

type USB interface {
	read()  //定义一个读的方法
	write() //定义一个写的方法
}

// 定义一个结构体

type Computer struct {
	name string
}

// 给结构体添加一个方法

func (c *Computer) read() {  // 指针传递 -----
	c.name = "sds" // 在里面惊进行修改 ------- 值操作
	fmt.Printf("%v,reading......\n", c.name)
}

// 给结构体添加一个方法
func (c *Computer) write() {  // 指针传递 -----
	c.name = "sds" // 在里面惊进行修改 ------- 值操作
	fmt.Printf("%v,write......\n", c.name)
}

func main() {
	var usb USB // 定义一个接口变量
	computer := Computer{
		name: "HP",
	}
	usb = &computer //声明这个接口将有computer这个结构体来实现  ---指针传递
	usb.read()      //接口调用
	usb.write()
	fmt.Printf("%v", computer.name)

}
  1. 接口和类型(结构体)的关系
  1. 一类型可以实现多个接口
  2. 多个类型可以实现同一个接口 (多态)
1..一个类型可以实现多个接口: 有一个player接口可以播放音乐,有一个vido接口可以播放视频,一个Phome可以同时实现这俩接口,边听儿歌变看视频。
******************************************************
package main

import (
	"fmt"
)

type Music interface {
	audio()
}
type Video interface {
	TV()
}

type Phone struct {
	name string
}

func (p Phone) audio() {
	fmt.Printf("%v,正在听音乐...", p.name)
}

func (p Phone) TV() {
	fmt.Printf("%v,正在看视频...", p.name)
}
func main() {
	var music Music
	var tv Video
	phone := Phone{
		name: "iqoo",
	}
	music = phone //实体类赋值接口
	tv = phone
	music.audio()
	tv.TV()
}
******************************************************
2.多个类型可以实现同一个接口 (多态)
package main

import (
	"fmt"
)

type Music interface {
	audio()
}

type Phone struct {
	name string
}

func (p Phone) audio() {
	fmt.Printf("%v,正在听音乐...", p.name)
}

type Computer struct {
	name string
}

func (c Computer) audio() {
	fmt.Printf("%v,正在听音乐...", c.name)
}

func main() {
	var music Music
	phone := Phone{
		name: "iqoo",
	}
	music = phone //实体类赋值接口
	music.audio()
	computer := Computer{
		name: "HP",
	}
	music = computer //实体类赋值接口
	music.audio()
}
  1. 接口嵌套

接口可以通过嵌套,创建新的接口。列如: 飞鱼,既可以飞,又可以游泳。我们创建一个飞Fly接口,创建一个游泳接口,飞鱼接口有这两个接口组成

package main

import "fmt"

type Fly interface {
	flying()
}
type Swim interface {
	swimming()
}
type FlyFish interface {
	Fly
	Swim
}

type Fish struct {
	name string
}

func (f Fish) flying() {  //实现飞的方法
	fmt.Printf("%v,正在飞.....", f.name)
}
func (f Fish) swimming() { //实现游泳的方法
	fmt.Printf("%v,正在游泳.....", f.name)
}
func main() {
	var fish FlyFish //定义接口
	f := Fish{
		name: "飞鱼",
	}
	fish = f  //对接口进行赋值
	fish.swimming()
	fish.flying()
}
  1. 通过接口实现OCP设计原则

而面向对象的可复用设计的第一块基石,便是所谓的 开-闭(Open-Closed Principle) 原则。虽然,go不是面向对象的语言,但也是可以模拟实现这个原则。对扩展是开放的,对修改是关闭的

在本次测试中: 我们初设两个实现方法cat和dog,猫和狗此时并没有率属于人类结构体下,我们可以实现对人类结构体进行扩展的操作。

package main

type Pet interface {
	eat()
	sleep()
}
type Dog struct {
}
type Cat struct {
}

//Dog实现接口

func (d Dog) eat() {
	println("Dog在eat....")
}
func (d Dog) sleep() {
	println("Dog在sleep....")
}

// Cat实现接口

func (c Cat) eat() {
	println("Cat在eat....")
}
func (c Cat) sleep() {
	println("Cat在sleep....")
}

type Person struct {
}

// pet既可以传递Dog也可以传递Cat

func (p Person) care(pet Pet) {  //接口接受
	pet.eat()
	pet.sleep()
}
func main() {

	dog := Dog{}
	cat := Cat{}
	person := Person{}
	person.care(dog)   //我们这里传递实体
	person.care(cat)  //我们这里传递实体
}
  1. 模拟OOP的属性和方法

Go没有面向对象的概念,也没有封装的概念。但是可以通过结构体 struct函数绑定来实现OOP的属性和方法特性。接收者receiver方法。通俗的讲: 就是属性和方法分开定义

列如: 想要定义一个Person类,有name和age属性,有 eat/sleep/work方法

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func (p Person) play() {
	fmt.Printf("%v\n正在玩游戏...", p.name)
}
func (p Person) Listener() {
	fmt.Printf("%v\n正在听歌...", p.name)
}
func main() {
	person := Person{
		name: "吉士先生",
		age:  20,
	}
	person.play()
	person.Listener()
}

7.Go继承

本质上没有oop的概念,也没有继承的概念,但是可以通过 结构体嵌套 实现这个特性。

内嵌结构体的话,我们可以省略调用内嵌结构体的变量名

package main

import "fmt"

type Animal struct {
	name string
}

func (a Animal) eat() {
	println("正在吃...")
}
func (a Animal) sleep() {
	fmt.Printf("%v正在睡...", a.name)
}

type Cat struct {

	//animal Animal //内嵌可以理解成继承的关系
	Animal  //内嵌结构体我们可以直接省略掉变量名
}

func main() {
	cat := Cat{
		Animal{  //因为上面省略了,所以我们这里也可以省略变量名  animal:=Animal{}
			name: "花花",
		},
	}
	cat.sleep() // 直接调用,因为省掉变量名了
	//cat.animal.sleep()  //这里是不省略
}
  1. 构造函数

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

利用函数来模拟构造函数...

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func NewPerson(name string, age int) (*Person, error) {
	if name == "" {
		return nil, fmt.Errorf("name 不能为空")
	}
	if age < 0 {
		return nil, fmt.Errorf("age 不能为负")
	}
	return &Person{
		name: name,
		age:  age,
	}, nil
}
func main() {
	person, err := NewPerson("李明", 20)
	fmt.Printf("%v", *person)
	fmt.Printf("%v", err)
}

7.输出函数

格式化输出

%v 万能变量  %d 十进制整数 %f 浮点型 %s 字符串 %c 字符 %T 数据类型
%#v 详细万能变量  %p 指针 %t 布尔 &取地址
fmt.printf("%v",identify)  ->格式化输出

换行输出

fmt.println()

不换行输出

fmt.print()

8.关系运算符和逻辑运算符和位运算符

  1. 关系运算符
1. ++和-- 不能放到表达式里面。只能先输出再调用。
++ -- + - * / += -= /=
> = < >= <=

2.逻辑运算符

&&  一个为假全为假
||  一个为真全为真
!  真为假 假为真

3.位运算符

& : 一个为假全为假
| : 一个为真全为真
^ : 不同为0,相同为1
<<  左移  : 左边非0的保留,右边添加0

>> 右移   : 右边非0也不保留,

9.Go语言的流程控制

1.顺序

从上->倒下
  1. 条件判断
1. if语句判断执行流程
if 布尔表达式 {
}else if 布尔表达式{
} else{
}
(1).在Go语言中,表达式不需要用括号括起来。
(2).左括号{}必须存在ifelse的同一行
(3).在if之后,条件语句之前,可以添加变量初始化语句,使用分割进行分割。
eg:
if idenfied:=value;idenfied>20{}
----------------------
2. switch进行条件判断默认是不穿透的。
3. Go语言switch语句,可以同时匹配多个条件,中间用逗号分割,有其中一个匹配成功即可。
switch idenfied{
case 1,2,3,4,5: println("工作日")
default: println("休息日")
}
4. Go语言的switch语句的case可以放置:判断表达式
5. fallthroug 设置穿透,Go语言默认是不穿透的。break打断穿透。0
  1. 循环

GO语言中只有for没有while{},do while{}

1. for 初始条件;条件表达式;结束语句{
 循环体
}
2. for表达式不需要加括号
3. 初始条件我们可以在外部定义。
4. 永真循环,就是我们的条件都不要。类似于while循环
for{} 或者 for ; ;{}
-----------
1. for index,value:=range 数组/字符串/map/chan{函数体}
2. 如果不需要的话,可以用匿名变量进行占位置
for i,v:=range identify{
}

10.Break与Continue与goto与标签💥

  1. Break
1. 单独在select中使用break和不使用break没有啥区别
2. 单独在表达式switch语句,并且没有fallthrough,使用break和不适用没有啥区别
3. 单独在表达式switch语句中,并且有fallthrough,使用break能够终止穿透
4. 带标签的break,可以跳出多层select/switch作用域,让break更加零花,不需要使用控制变量一层层的跳出循环,没有带break的只能跳出当前语句块。

(1).可以终止for循环
(2).可以打破switchfallthrough穿透
(3).可以跳出标签
package main

func main() {
	
	/**
	*	正常情况下,我们需要俩个break才能跳出双层循环。如果我们添加了标签就会直接跳转到外面,无需俩break
	 */
MY_LABEL:  //标签
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i == 2 {
				break MY_LABEL  //标签
			}
			println("你好")
		}
		println(i)
	}

	println("end....")
}
  1. continue

continue只能在循环中,在go中只能用for循环中,它可以终止本次循环,进行下一次循环。在continue语句后添加标签时,表示开始标签对应的循环。

1.for循环中使用continue,会跳过此次循环,但不会终止后面的循环

3.goto

goto语句通过标签进行代码间的无条件跳转,goto语句可以在快速跳转循环、避免重复退出上有一定的帮助.Go语言中使用goto语句能够简化一些代码的实现过程。比如双层嵌套的for循环要退出时。

1. goto 标签名 与 break 标签名的区别
(1).break的标签名只能放在for循环上面,不能放在其他位置,goto的标签名可以随便放,不必只放在for寻黄傻瓜
(2).break的标签必须先定义再使用,goto可以先定义再使用也可以先使用再定义

goto 进行跳转

package main

func main() {

	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i == 2 {
				goto first_range //标签的使用
			}
			println("你好")
		}
		println(i)
	}
	println("end....") //这里是不会打印的,直接跳转
first_range: //标签的定义
	println("end2....")
}

在这里插入图片描述

  1. 标签的定义和使用
  1. break调用标签的话,必须使用在for循环之上。必须先定义后使用
  2. goto调用标签的话,可以先使用再调用,不必须在for循环下。位置随便放
  3. goto标签,如果goto跳转之后会直接跳转到标签所在的位置
  4. break标签,会退出循环。
1. 定义

		标签名: 

2. 使用
		break 标签名
		goto 标签名