title: (未完成)golang入门
tags: [go,入门]
categories: [后端,go后端]
date: 2022-03-31 15:09:42
前言:
对go语言挺感兴趣的,所以简单学习一下,这里是对go语言的初级入门
go语言中的占位符:
占位符 | 说明 | 举例 | 输出 |
%v | 相应值的默认格式。 | Printf(“%v”, name) | {xuehui} |
%+v | 打印结构体时,会添加字段名 | Printf(“%+v”, name) | {Name:xuehui} |
%#v | 相应值的Go语法表示 | Printf(“#v”, name) | main.Human{Name:“xuehui”} |
%T | 相应值的类型的Go语法表示 | Printf(“%T”, name) | main.Human |
%% | 字面上的百分号,并非值的占位符 | Printf(“%%”) | % |
%t | true 或 false | Printf(“%t”, true) | TRUE |
%b | 二进制表示 | Printf(“%b”, 5) | 101 |
%c | 相应Unicode码点所表示的字符 | Printf(“%c”, 0x4E2D) | 中 |
%d | 十进制表示 | Printf(“%d”, 0x12) | 18 |
%o | 八进制表示 | Printf(“%d”, 10) | 12 |
%q | 单引号围绕的字符字面值,由Go语法安全地转义 | Printf(“%q”, 0x4E2D) | ‘中’ |
%x | 十六进制表示,字母形式为小写 a-f | Printf(“%x”, 13) | d |
%X | 十六进制表示,字母形式为大写 A-F | Printf(“%x”, 13) | D |
%U | Unicode格式:U+1234,等同于 “U+%04X” | Printf(“%U”, 0x4E2D) | U+4E2D |
%b | 无小数部分的,指数为二的幂的科学计数法, 与 strconv.FormatFloat 的 ‘b’ 转换格式一致。例如 -123456p-78 | ||
%e | 科学计数法,例如 -1234.456e+78 | Printf(“%e”, 10.2) | 1.02E+01 |
%E | 科学计数法,例如 -1234.456E+78 | Printf(“%e”, 10.2) | 1.02E+01 |
%f | 有小数点而无指数,例如 123.456 | Printf(“%f”, 10.2) | 10.2 |
%g | 根据情况选择 %e 或 %f 以产生更紧凑的(无末尾的0)输出 | Printf(“%g”, 10.20) | 10.2 |
%G | 根据情况选择 %E 或 %f 以产生更紧凑的(无末尾的0)输出 | Printf(“%G”, 10.20+2i) | (10.2+2i) |
%s | 输出字符串表示(string类型或[]byte) | Printf(“%s”, []byte(“Go语言”)) | Go语言 |
%q | 双引号围绕的字符串,由Go语法安全地转义 | Printf(“%q”, “Go语言”) | “Go语言” |
%x | 十六进制,小写字母,每字节两个字符 | Printf(“%x”, “golang”) | 676f6c616e67 |
%X | 十六进制,大写字母,每字节两个字符 | Printf(“%X”, “golang”) | 676F6C616E67 |
%p | 十六进制表示,前缀 0x | Printf(“%p”, &name) | 0x4f57f0 |
+ | 总打印数值的正负号;对于%q(%+q)保证只输出ASCII编码的字符。 | Printf(“%+q”, “中文”) | “\u4e2d\u6587” |
- | 在右侧而非左侧填充空格(左对齐该区域) | ||
# | 备用格式:为八进制添加前导 0(%#o) 为十六进制添加前导 0x(%#x)或 0X(%#X),为 %p(%#p)去掉前导 0x; 如果可能的话,%q(%#q)会打印原始 (即反引号围绕的)字符串; 如果是可打印字符,%U(%#U)会写出该字符的 Unicode 编码形式(如字符 x 会被打印成 U+0078 ‘x’)。 | Printf(“%#U”, ‘中’) | U+4E2D |
’ ’ | (空格)为数值中省略的正负号留出空白(% d); 以十六进制(% x, % X)打印字符串或切片时,在字节之间用空格隔开 | ||
0 | 填充前导的0而非空格;对于数字,这会将填充移到正负号之后 |
1)变量:
变量练习:
package main
import "fmt"
func main(){
//1.变量的声明
var age int
//2.变量的赋值
age = 18
//3.变量的使用
fmt.Println("age = ",age);
//声明和赋值可以合成一句:
var age2 int = 19
fmt.Println("age2 = ",age2);
//不可以在赋值的时候给与不匹配的类型
var num int = 12.56
fmt.Println("num = ",num);
}
一次性声明多个变量:
package main
import "fmt"
//全局变量:定义在函数外的变量
var n7 = 100
var n8 = 9.7
//设计者认为上面的全局变量的写法太麻烦了,可以一次性声明:
var (
n9 = 500
n10 = "netty"
)
func main(){
//定义在{}中的变量叫:局部变量
//第一种:变量的使用方式:指定变量的类型,并且赋值,
var num int = 18
fmt.Println(num)
//第二种:指定变量的类型,但是不赋值,使用默认值
var num2 int
fmt.Println(num2)
//第三种:如果没有写变量的类型,那么根据=后面的值进行判定变量的类型 (自动类型推断)
var num3 = "tom"
fmt.Println(num3)
//第四种:省略var,注意 := 不能写为 =
sex := "男"
fmt.Println(sex)
fmt.Println("------------------------------------------------------------------")
//声明多个变量:
var n1,n2,n3 int
fmt.Println(n1)
fmt.Println(n2)
fmt.Println(n3)
var n4,name,n5 = 10,"jack",7.8
fmt.Println(n4)
fmt.Println(name)
fmt.Println(n5)
n6,height := 6.9,100.6
fmt.Println(n6)
fmt.Println(height)
fmt.Println(n7)
fmt.Println(n8)
fmt.Println(n9)
fmt.Println(n10)
}
进制的介绍:
十进制整数,如:99, -500, 0
八进制整数,要求以 0 开头,如:015
十六进制数,要求 0x 或 0X 开头,如:0x15
二进制:要求0b或者0B开头,如:0b11
整数类型:
整数类型介绍:简单的说,就是用于存放整数值的,比如10,-45,6712等等。
有符号整数类型:
PS:127怎么算出来的?01111111 -->二进制 —》转为十进制: 12^6 + 12^5 + 12^4 + 12^3 + 12^2 + 12^1 + 1*2^0 = 64 + 32 + 16 + 8 + 4 + 2 + 1= 127
PS:-128怎么算出来的?10000000 —>二进制 —>一看就是个负数
10000000 --》负数的二进制减1:01111111取反:10000000 —》得到一个正数 2^7 = 128加负号:-128
无符号整数类型:
表数范围的边界计算:
11111111= 2^7+127 = 128 + 127 = 25500000000 = 0
其他整数类型:
PS:Golang的整数类型,默认声明为int类型
Ps:变量占用的字节数:
package main
import (
"fmt"
"unsafe"
)
func main() {
var num1 int8 = 110
fmt.Println(num1)
var num2 uint8 = 130
fmt.Println(num2)
var num3 = 28
//printf函数是格式化,%T就是填类型
fmt.Printf("num3 的类型是: %T", num3)
fmt.Println(unsafe.Sizeof(num3))
}
整数类型的选择:
这么多整数类型,使用的时候该如何选择呢?
Golang程序中整型变量在使用时,遵守保小不保大的原则,
即:在保证程序正确运行下,尽量使用占用空间小的数据类型
浮点类型:
【1】浮点类型介绍:
简单的说,就是用于存放小数值的,比如3.14、0.28、-7.19等等。
【2】浮点类型种类:
PS:底层存储空间和操作系统无关
PS:浮点类型底层存储:符号位+指数位+尾数位,所以尾数位只是存了 一个大概,很可能会出现精度的损失。
package main
import "fmt"
func main(){
//定义浮点类型的数据:
var num1 float32 = 3.14
fmt.Println(num1)
//可以表示正浮点数,也可以表示负的浮点数
var num2 float32 = -3.14
fmt.Println(num2)
//浮点数可以用十进制表示形式,也可以用科学计数法表示形式 E 大写小写都可以的
var num3 float32 = 314E-2
fmt.Println(num3)
var num4 float32 = 314E+2
fmt.Println(num4)
var num5 float32 = 314e+2
fmt.Println(num5)
var num6 float64 = 314e+2
fmt.Println(num6)
//浮点数可能会有精度的损失,所以通常情况下,建议你使用:float64
var num7 float32 = 256.000000916
fmt.Println(num7)
var num8 float64 = 256.000000916
fmt.Println(num8)
//golang中默认的浮点类型为:float64
var num9 = 3.17
fmt.Printf("num9对应的默认的类型为:%T",num9)
}
go的字符:
【1】Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。
【2】Golang中字符使用UTF-8编码
【3】ASCII码表:
左面是不可见字符 右面是可见字符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j77Pr5XS-1648974341927)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331155403822.png)]
【4】查看UTF-8编码表:
http://www.mytju.com/classcode/tools/encode_utf8.asp
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CrFAQZJf-1648974341927)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331155436775.png)]
【5】代码实验:
package main
import "fmt"
func main(){
//定义字符类型的数据:
var c1 byte = 'a'
fmt.Println(c1)//97
var c2 byte = '6'
fmt.Println(c2)//54
var c3 byte = '('
fmt.Println(c3 + 20)//40
//字符类型,本质上就是一个整数,也可以直接参与运算,输出字符的时候,会将对应的码值做一个输出
//字母,数字,标点等字符,底层是按照ASCII进行存储。
var c4 int = '中'
fmt.Println(c4)
//汉字字符,底层对应的是Unicode码值
//对应的码值为20013,byte类型溢出,能存储的范围:可以用int
//总结:Golang的字符对应的使用的是UTF-8编码(Unicode是对应的字符集,UTF-8是Unicode的其中的一种编码方案)
var c5 byte = 'A'
//想显示对应的字符,必须采用格式化输出
fmt.Printf("c5对应的具体的字符为:%c",c5)
}
【6】转义字符:
转义字符:将后面的字母表示为特殊含义
package main
import "fmt"
func main(){
//练习转义字符:
//\n 换行
fmt.Println("aaa\nbbb")
//\b 退格
fmt.Println("aaa\bbbb")
//\r 光标回到本行的开头,后续输入就会替换原有的字符
fmt.Println("aaaaa\rbbb")
//\t 制表符
fmt.Println("aaaaaaaaaaaaa")
fmt.Println("aaaaa\tbbbbb")
fmt.Println("aaaaaaaa\tbbbbb")
//\"
fmt.Println("\"Golang\"")
}
bool类型:
【1】布尔类型也叫bool类型,bool类型数据只允许取值true和false
【2】布尔类型占1个字节。
【3】布尔类型适于逻辑运算,一般用于程序流程控制
【4】代码:
package main
import "fmt"
func main(){
//测试布尔类型的数值:
var flag01 bool = true
fmt.Println(flag01)
var flag02 bool = false
fmt.Println(flag02)
var flag03 bool = 5 < 9
fmt.Println(flag03)
}
string类型:
【1】介绍:
字符串就是一串固定长度的字符连接起来的字符序列。
【2】字符串的使用:
package main
import "fmt"
func main() {
//1.定义一个字符串:
var s1 string = "你好全面拥抱Golang"
fmt.Println(s1)
//2.字符串是不可变的:指的是字符串一旦定义好,其中的字符的值不能改变
var s2 string = "abc"
//s2 = "def"
//s2[0] = 't'
fmt.Println(s2)
//3.字符串的表示形式:
//(1)如果字符串中没有特殊字符,字符串的表示形式用双引号
//var s3 string = "asdfasdfasdf"
//(2)如果字符串中有特殊字符,字符串的表示形式用反引号 ``
var s4 string = `
左右同形,左小右大
上下同形,上小下大
左右等长,错位书写
左边小时要偏上
右边小时要下落
点下有竖,点竖直对
下有横竖横高竖低
下有撇点,撇高点低
package main
import "fmt"
func main(){
//测试布尔类型的数值:
var flag01 bool = true
fmt.Println(flag01)
var flag02 bool = false
fmt.Println(flag02)
var flag03 bool = 5 < 9
fmt.Println(flag03)
}`
fmt.Println(s4)
//4.字符串的拼接效果:
var s5 string = "abc" + "def"
s5 += "hijk"
fmt.Println(s5)
//当一个字符串过长的时候:注意:+保留在上一行的最后
var s6 string = "abc" + "def" + "abc" + "def" + "abc" + "def" + "abc" +
"def" + "abc" + "def" + "abc" + "def" + "abc" + "def" + "abc" + "def" +
"abc" + "def" + "abc" + "def" + "abc" + "def" + "abc" + "def" + "abc" +
"def" + "abc" + "def" + "abc" + "def" + "abc" + "def" + "abc" + "def" +
"abc" + "def" + "abc" + "def"
fmt.Println(s6)
}
基本数据类型的默认值:
【1】在Golang中数据类型都有一个默认值,当程序员没有赋值时,就会保留默认值(默认值又叫零值)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pr0PoR0U-1648974341929)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331161022555.png)]
基本类型之间的转换:
【1】Go在不同类型的变量之间赋值时需要显式转换,并且只有显式转换(强制转换)。
【2】语法:
表达式T(v)将值v转换为类型T
T : 就是数据类型
v : 就是需要转换的变量的一个值
package main
import "fmt"
func main(){
//进行类型转换:
var n1 int = 100
//var n2 float32 = n1 在这里自动转换不好使,比如显式转换
fmt.Println(n1)
//fmt.Println(n2)
var n2 float32 = float32(n1)
fmt.Println(n2)
//注意:n1的类型其实还是int类型,只是将n1的值100转为了float32而已,n1还是int的类型
fmt.Printf("%T",n1) //int
fmt.Println()
//将int64转为int8的时候,编译不会出错的,但是会数据的溢出
var n3 int64 = 888888
var n4 int8 = int8(n3)
fmt.Println(n4)//56
var n5 int32 = 12
var n6 int64 = int64(n5) + 30 //一定要匹配=左右的数据类型
fmt.Println(n5)
fmt.Println(n6)
var n7 int64 = 12
var n8 int8 = int8(n7) + 127 //编译通过,但是结果可能会溢出
//var n9 int8 = int8(n7) + 128 //编译不会通过
fmt.Println(n8)
//fmt.Println(n9)
基本数据类型转换成string
【1】基本数据类型和string的转换介绍在程序开发中,我们经常需要将基本数据类型转成string类型。或者将string类型转成基本数据类型。
【2】基本类型转string类型方式1:fmt.Sprintf(“%参数”,表达式) —》 重点练习这个,推荐方式方式2:使用strconv包的函数
【3】代码测试:
方式一:
fmt.Sprintf("%参数",参数)
package main
import "fmt"
func main() {
var n1 int = 19
var n2 float32 = 4.78
var n3 bool = false
var n4 byte = 'a'
var s1 string = fmt.Sprintf("%d", n1)
fmt.Printf("s1对应的类型是:%T ,s1 = %q \n", s1, s1)
var s2 string = fmt.Sprintf("%f", n2)
fmt.Printf("s2对应的类型是:%T ,s2 = %q \n", s2, s2)
var s3 string = fmt.Sprintf("%t", n3)
fmt.Printf("s3对应的类型是:%T ,s3 = %q \n", s3, s3)
var s4 string = fmt.Sprintf("%c", n4)
fmt.Printf("s4对应的类型是:%T ,s4 = %q \n", s4, s4)
var s5 string = fmt.Sprintf("%d", 12)
fmt.Printf("s4对应的类型是:%T ,s5 = %q \n", s5, s5)
}
方式二
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cm26HSBh-1648974341929)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331162349039.png)]
用了一下go包
package main
import (
"fmt"
"strconv"
)
func main() {
var n1 int = 18
var s1 string = strconv.FormatInt(int64(n1), 10) //参数:第一个参数必须转为int64类型 ,第二个参数指定字面值的进制形式为十进制
fmt.Printf("s1对应的类型是:%T ,s1 = %q \n", s1, s1)
var n2 float64 = 4.29
var s2 string = strconv.FormatFloat(n2, 'f', 9, 64)
//第二个参数:'f'(-ddd.dddd) 第三个参数:9 保留小数点后面9位 第四个参数:表示这个小数是float64类型
fmt.Printf("s2对应的类型是:%T ,s2 = %q \n", s2, s2)
var n3 bool = true
var s3 string = strconv.FormatBool(n3)
fmt.Printf("s3对应的类型是:%T ,s3 = %q \n", s3, s3)
}
string转换成基本数据类型
【1】string类型转基本类型方式:使用strconv包的函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3pppDoE-1648974341930)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331162624566.png)]
ParseBool的源码:
package strconv
// ParseBool returns the boolean value represented by the string.
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error.
func ParseBool(str string) (bool, error) {
switch str {
case "1", "t", "T", "true", "TRUE", "True":
return true, nil
case "0", "f", "F", "false", "FALSE", "False":
return false, nil
}
return false, syntaxError("ParseBool", str)
}
有返回error,可以用_
来忽略掉
package main
import (
"fmt"
"strconv"
)
func main() {
//string-->>>bool
var s1 string = "true"
var b bool
//ParseBool这个函数的返回值有两个:(value bool, err error)
//value就是我们得到的布尔类型的数据,err出现的错误
//我们只关注得到的布尔类型的数据,err可以用_直接忽略
b, _ = strconv.ParseBool(s1)
fmt.Printf("b的类型是:%T,b=%v \n", b, b)
//string--->>>int64
var s2 string = "19"
var num1 int64
num1, _ = strconv.ParseInt(s2, 10, 64)
fmt.Printf("num1的类型是:%T,num1=%v \n", num1, num1)
//string-->>>float32/float64
var s3 string = "3.14"
var f1 float64
f1, _ = strconv.ParseFloat(s3, 64)
fmt.Printf("f1的类型是:%T,f1=%v \n", f1, f1)
//注意:string向基本数据类型转换的时候,一定要确保string类型能够转成有效的数据类型,否则最后得到的结果就是按照对应类型的默认值输出
var s4 string = "golang"
var b1 bool
b1, _ = strconv.ParseBool(s4)
fmt.Printf("b1的类型是:%T,b1=%v \n", b1, b1)
var s5 string = "golang"
var num2 int64
num2, _ = strconv.ParseInt(s5, 10, 64)
fmt.Printf("num2的类型是:%T,num2=%v \n", num2, num2)
}
ps:前面都是主要讲得基本数据类型
复杂数据类型:
指针
【1】基本数据类型和内存:
package main
import(
"fmt"
)
func main(){
var age int = 18
//&符号+变量 就可以获取这个变量内存的地址
fmt.Println(&age) //0xc0000a2058
}
【2】指针变量:
package main
import(
"fmt"
)
func main(){
var age int = 18
//&符号+变量 就可以获取这个变量内存的地址
fmt.Println(&age) //0xc0000a2058
//定义一个指针变量:
//var代表要声明一个变量
//ptr 指针变量的名字
//ptr对应的类型是:*int 是一个指针类型 (可以理解为 指向int类型的指针)
//&age就是一个地址,是ptr变量的具体的值
var ptr *int = &age
fmt.Println(ptr)
fmt.Println("ptr本身这个存储空间的地址为:",&ptr)
//想获取ptr这个指针或者这个地址指向的那个数据:
fmt.Printf("ptr指向的数值为:%v",*ptr) //ptr指向的数值为:18
}
【3】可以通过指针改变指向值
func main(){
var num int = 10
fmt.Println(num)
var ptr *int = &num
*ptr = 20
fmt.Println(num)
}
10
20
【4】指针变量接收的一定是地址值
【5】指针变量的地址不可以不匹配
PS:*float32意味着这个指针指向的是float32类型的数据,但是&num对应的是int类型的不可以。
就是和c语言的指针一样的,我c也学得不是很好
标识符:
【1】标识符:读音 biao zhi fu
【2】什么是标识符? 变量,方法等,只要是起名字的地方,那个名字就是标识符 var age int = 19 var price float64 = 9.8
【3】标识符定义规则:
1.三个可以(组成部分):数字,字母,下划线_
2.四个不可以:不可以以数字开头,严格区分大小写,不能包含空格,不可以使用Go中的保留关键字
3.见名知意:增加可读性
4.下划线"_"本身在Go中是一个特殊的标识符,称为空标识符。可以代表任何其它的标识符,但是它对应的值会被忽略(比如:忽略某个返回值)。所以仅能被作为**占位符**使用,不能单独作为标识符使用。
就像刚刚用`_`来忽略error的返回值
5.可以用如下形式,但是不建议: var int int = 10 (int,float32,float64等不算是保留关键字,但是也尽量不要使用)
6.长度无限制,但是不建议太长 asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasfd
起名规则:
(1)包名:尽量保持package的名字和目录保持一致,尽量采取有意义的包名,简短,有意义,和标准库不要冲突
1.为什么之前在定义源文件的时候,一般我们都用package main 包 ?main包是一个程序的入口包,所以你main函数它所在的包建议定义为main包,如果不定义为main包,那么就不能得到可执行文件。
2.尽量保持package的名字和目录保持一致
3.和标准库不要冲突
(2)变量名、函数名、常量名 : 采用驼峰法。
就是单词按照大写区分开
var stuNameDetail string = 'lili'
(3)如果变量名、函数名、常量名首字母大写,则可以被其他的包访问;
如果首字母小写,则只能在本包中使用 (利用首字母大写小写完成权限控制)
注意:
import导入语句通常放在文件开头包声明语句的下面。
导入的包名需要使用双引号包裹起来。
包名是从GOPATH后开始计算的,使用/进行路径分隔。
要导包的程序:
package main
import (
"fmt"
"helloworld/test02/demo1"//这里就是导包路径
)
func main() {
fmt.Println(demo1.N3)
}
被导的包:
//package main
package demo1
var n1 = 100
var n2 = 1.89
var (
N3 = 699
n4 = "ladidol"
)
【1】关键字就是程序发明者规定的有特殊含义的单词,又叫保留字。
go语言中一共有25个关键字。
【2】预定义标识符:一共36个预定标识符,包含基础数据类型和系统内嵌函数
2)运算符
算术运算符
【1】算术运算符:+ ,-,*,/,%,++,–
【2】介绍:算术运算符是对数值类型的变量进行运算的,比如,加减乘除。
【3】代码展示:
package main
import (
"fmt"
)
func main() {
//+加号:
//1.正数 2.相加操作 3.字符串拼接
var n1 int = +10
fmt.Println(n1)
var n2 int = 4 + 7
fmt.Println(n2)
var s1 string = "abc" + "def"
fmt.Println(s1)
// /除号:
fmt.Println(10 + 123)
fmt.Println(n1 + n2)
fmt.Println(10 / 3) //两个int类型数据运算,结果一定为整数类型
fmt.Println(10.0 / 3) //浮点类型参与运算,结果为浮点类型
// % 取模 等价公式: a%b=a-a/b*b
fmt.Println(10 % 3) // 10%3= 10-10/3*3 = 1
fmt.Println(-10 % 3)
fmt.Println(10 % -3)
fmt.Println(-10 % -3)
//++自增操作:
var a int = 10
a++
fmt.Println(a)
a--
fmt.Println(a)
//++ 自增 加1操作,--自减,减1操作
//go语言里,++,--操作非常简单,只能单独使用,不能参与到运算中去
//go语言里,++,--只能在变量的后面,不能写在变量的前面 --a ++a 错误写法
}
赋值运算符
【1】赋值运算符:=,+=,-=,*=,/=,%=
关系运算符
【1】关系运算符:==,!=,>,<,> =,<= 关系运算符的结果都是bool型,也就是要么是true,要么是false
逻辑运算符
【1】逻辑运算符:&&(逻辑与/短路与),||(逻辑或/短路或),!(逻辑非)
没有|
or&
的运算符,现在还不知道怎么使用
等等
运算符优先级表
Go语言有几十种运算符,被分成十几个级别,有的运算符优先级不同,有的运算符优先级相同,请看下表。
一句话:为了提高优先级,可以加()
终端输入函数
【1】介绍:在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。
【2】API:
【3】代码练习:
package main
import "fmt"
func main(){
//实现功能:键盘录入学生的年龄,姓名,成绩,是否是VIP
//方式1:Scanln
var age int
// fmt.Println("请录入学生的年龄:")
//传入age的地址的目的:在Scanln函数中,对地址中的值进行改变的时候,实际外面的age被影响了
//fmt.Scanln(&age)//录入数据的时候,类型一定要匹配,因为底层会自动判定类型的
var name string
// fmt.Println("请录入学生的姓名:")
// fmt.Scanln(&name)
var score float32
// fmt.Println("请录入学生的成绩:")
// fmt.Scanln(&score)
var isVIP bool
// fmt.Println("请录入学生是否为VIP:")
// fmt.Scanln(&isVIP)
//将上述数据在控制台打印输出:
//fmt.Printf("学生的年龄为:%v,姓名为:%v,成绩为:%v,是否为VIP:%v",age,name,score,isVIP)
//方式2:Scanf
fmt.Println("请录入学生的年龄,姓名,成绩,是否是VIP,使用空格进行分隔")
fmt.Scanf("%d %s %f %t",&age,&name,&score,&isVIP)
//将上述数据在控制台打印输出:
fmt.Printf("学生的年龄为:%v,姓名为:%v,成绩为:%v,是否为VIP:%v",age,name,score,isVIP)
}
3)流程控制:
和C语言很类似,就是少了小括号:
if语句:
package main
import "fmt"
func main(){
var score int = 18
if score > 10 {
fmt.Println("aaa")
} else if score > 6{
fmt.Println("bbb")
}
}
switch语句:
go语言中的switch自动带有break
package main
import "fmt"
func main(){
//实现功能:根据给出的学生分数,判断学生的等级:
// >=90 -----A
// >=80 -----B
// >=70 -----C
// >=60 -----D
// <60 -----E
//给出一个学生分数:
var score int = 187
//根据分数判断等级:
//switch后面是一个表达式,这个表达式的结果依次跟case进行比较,满足结果的话就执行冒号后面的代码。
//default是用来“兜底”的一个分支,其它case分支都不走的情况下就会走default分支
//default分支可以放在任意位置上,不一定非要放在最后。
switch score/10 {
case 10 :
fmt.Println("您的等级为A级")
case 9 :
fmt.Println("您的等级为A级")
case 8 :
fmt.Println("您的等级为B级")
case 7 :
fmt.Println("您的等级为C级")
case 6 :
fmt.Println("您的等级为D级")
case 5 :
fmt.Println("您的等级为E级")
case 4 :
fmt.Println("您的等级为E级")
case 3 :
fmt.Println("您的等级为E级")
case 2 :
fmt.Println("您的等级为E级")
case 1 :
fmt.Println("您的等级为E级")
case 0 :
fmt.Println("您的等级为E级")
default:
fmt.Println("您的成绩有误")
}
}
循环结构:
原始for循环
for 初始表达式; 布尔表达式; 迭代因子 {
循环体;
}
正常格式:
package main
import "fmt"
func main() {
var sum int = 0
for i := 1; i <= 5; i++ {
sum += i
}
fmt.Println(sum)
}
格式灵活:
package main
import "fmt"
func main(){
i := 1 //变量的初始化
for i<=5 {//条件表达式。判断条件
fmt.Println("你好 Golang")//循环体
i++//迭代
}
}
如果进入死循环可以:
在控制台中结束死循环:ctrl+c
键值遍历for循环:
for 遍历者i脚标, i所代表的值 := range 被遍历的字符串 {
...
}
例子:
package main
import "fmt"
func main(){
//定义一个字符串:
var str string = "hello golang你好"
//方式1:普通for循环:按照字节进行遍历输出的 (暂时先不使用中文)
// for i := 0;i < len(str);i++ {//i:理解为字符串的下标
// fmt.Printf("%c \n",str[i])
// }
//方式2:for range
for i , value := range str {
fmt.Printf("索引为:%d,具体的值为:%c \n",i,value)
}
}
流程控制中的关键字:
break,continue,goto(依旧是不建议用),return
4)函数
基本函数:
func 函数名(形参列表)(返回值类型列表){
执行语句..
return + 返回值列表
}
(1)函数名:
遵循标识符命名规范:见名知意 addNum,驼峰命名addNum
首字母不能是数字
首字母大写该函数可以被本包文件和其它包文件使用(类似public)
首学母小写只能被本包文件使用,其它包文件不能使用(类似private)
(2)形参列表:
形参列表:个数:可以是一个参数,可以是n个参数,可以是0个参数
形式参数列表:作用:接收外来的数据
实际参数:实际传入的数据
(3)返回值类型列表:函数的返回值对应的类型应该写在这个列表中
返回0个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sIMk7O9U-1648974341938)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331215144622.png)]
返回1个:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nkazrMqv-1648974341939)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331215800943.png)]
返回多个:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oWA3IsGp-1648974341940)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331215407262.png)]
golang不支持重载
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值。
以值传递方式的数据类型,如果希望在函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果来看类似引用传递。
如下,直接传入地址,test函数在拿到地址后的对上面的值进行修改:
(4)在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数调用。
package main
import "fmt"
//定义一个函数:
func test(num int){
fmt.Println(num)
}
func main(){
//函数也是一种数据类型,可以赋值给一个变量
a := test//变量就是一个函数类型的变量
fmt.Printf("a的类型是:%T,test函数的类型是:%T \n",a,test)//a的类型是:func(int),test函数的类型是:func(int)
//通过该变量可以对函数调用
a(10) //等价于 test(10)
}
结果:
a的类型是:func(int),test函数的类型是:func(int)
10
(5)函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用(把函数本身当做一种数据类型)
package main
import "fmt"
//定义一个函数:
func test(num int){
fmt.Println(num)
}
//定义一个函数,把另一个函数作为形参:
func test02 (num1 int ,num2 float32, testFunc func(int)){
fmt.Println("-----test02")
}
func main(){
//函数也是一种数据类型,可以赋值给一个变量
a := test//变量就是一个函数类型的变量
fmt.Printf("a的类型是:%T,test函数的类型是:%T \n",a,test)//a的类型是:func(int),test函数的类型是:func(int)
//通过该变量可以对函数调用
a(10) //等价于 test(10)
//调用test02函数:
test02(10,3.19,test)
test02(10,3.19,a)
}
a的类型是:func(int),test函数的类型是:func(int)
10
-----test02
-----test02
一些对函数更神奇的操作;
匿名函数:
【1】Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数
【2】匿名函数使用方式:
(1)在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次(用的多)
package main
import "fmt"
func main() {
//定义匿名函数同时使用
result := func(num1 int, num2 int) int {
return num1 + num2
}(12, 5)
fmt.Println(result)//17
}
(2)将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数(用的少)
【3】如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了
package main
import "fmt"
var Func01 = func (num1 int,num2 int) int{
return num1 * num2
}
func main(){
//定义匿名函数:定义的同时调用
result := func (num1 int,num2 int) int{
return num1 + num2
}(10,20)
fmt.Println(result)
//将匿名函数赋给一个变量,这个变量实际就是函数类型的变量
//sub等价于匿名函数
sub := func (num1 int,num2 int) int{
return num1 - num2
}
//直接调用sub就是调用这个匿名函数了
result01 := sub(30,70)
fmt.Println(result01)
result02 := sub(30,70)
fmt.Println(result02)
result03 := Func01(3,4)
fmt.Println(result03)
}
闭包(有点复杂了):
【1】什么是闭包:闭包就是一个函数和与其相关的引用环境组合的一个整体
【2】案例展示:
package main
import "fmt"
//函数功能:求和
//函数的名字:getSum 参数为空
//getSum函数返回值为一个函数,这个函数的参数是一个int类型的参数,返回值也是int类型
func getSum() func (int) int {
var sum int = 0
return func (num int) int{
sum = sum + num
return sum
}
}
//闭包:返回的匿名函数+匿名函数以外的变量num
func main(){
f := getSum()
fmt.Println(f(1))//1
fmt.Println(f(2))//3
fmt.Println(f(3))//6
fmt.Println(f(4))//10
}
感受:匿名函数中引用的那个变量会一直保存在内存中,可以一直使用
【3】闭包的本质:
闭包本质依旧是一个匿名函数,只是这个函数引入外界的变量/参数
匿名函数+引用的变量/参数 = 闭包
【4】特点:
(1)返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数 ,因此这个匿名函数就和变量/参数形成一个整体,构成闭包。
(2)闭包中使用的变量/参数会一直保存在内存中,所以会一直使用—》意味着闭包不可滥用(对内存消耗大)
【5】不使用闭包可以吗?
package main
import "fmt"
//函数功能:求和
//函数的名字:getSum 参数为空
//getSum函数返回值为一个函数,这个函数的参数是一个int类型的参数,返回值也是int类型
func getSum() func (int) int {
var sum int = 0
return func (num int) int{
sum = sum + num
return sum
}
}
//闭包:返回的匿名函数+匿名函数以外的变量num
func main(){
f := getSum()
fmt.Println(f(1))//1
fmt.Println(f(2))//3
fmt.Println(f(3))//6
fmt.Println(f(4))//10
fmt.Println("----------------------")
fmt.Println(getSum01(0,1))//1
fmt.Println(getSum01(1,2))//3
fmt.Println(getSum01(3,3))//6
fmt.Println(getSum01(6,4))//10
}
func getSum01(sum int,num int) int{
sum = sum + num
return sum
}
//不使用闭包的时候:我想保留的值,不可以反复使用
//闭包应用场景:闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了
defer关键字(推迟):
【1】defer关键字的作用:
在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字
【2】案例展示:
【3】代码变动一下,再次看结果:
发现:遇到defer关键字,会将后面的代码语句压入栈中,也会将相关的值同时拷贝入栈中,不会随着函数后面的变化而变化。
【4】defer应用场景:比如你想关闭某个使用的资源,在使用的时候直接随手defer,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事
系统常用函数:
(1)字符串相关的函数:
【1】统计字符串的长度,按字节进行统计:
len(str) 使用内置函数也不用导包的,直接用就行
【2】字符串遍历:
(1)利用方式1:for-range键值循环:
(2)r:=[]rune(str), 进行数组遍历
【3】字符串转整数: n, err := strconv.Atoi("66")
package main
import (
"fmt"
"strconv"
)
func main() {
n, err := strconv.Atoi("66")
fmt.Println(n, err)
}
66 <nil>
【4】整数转字符串:str = strconv.Itoa(6887)
package main
import (
"fmt"
"strconv"
)
func main() {
var str = strconv.Itoa(6887)
fmt.Println(str)
fmt.Printf("这个str的类型是%T", str)
}
6887
这个str的类型是string
【5】查找子串是否在指定的字符串中: strings.Contains("javaandgolang", "go")
【6】统计一个字符串有几个指定的子串:strings.Count("javaandgolang","a")
【7】不区分大小写的字符串比较: strings.EqualFold("go" , "Go")
【8】返回子串在字符串第一次出现的索引值,如果没有返回-1:strings.lndex("javaandgolang" , "a")
【9】字符串的替换:strings.Replace("goandjavagogo", "go", "golang", n)
n可以指定你希望替换几个,如果n=-1表示全部替换,替换两个n就是2
【10】按照指定的某个字符,为分割标识,将一个学符串拆分成字符串数组strings.Split("go-python-java", "-")
【11】将字符串的字母进行大小写的转换:
strings.ToLower("Go")// go
strings.ToUpper"go")//Go
【12】将字符串左右两边的空格去掉:
strings.TrimSpace(" go and java ")
【13】将字符串左右两边指定的字符去掉:
strings.Trim("~golang~ ", " ~")
【14】将字符串左边指定的字符去掉:
strings.TrimLeft("~golang~", "~")
【15】将字符串右边指定的字符去掉:
strings.TrimRight("~golang~", "~")
【16】判断字符串是否以指定的字符串开头:
strings.HasPrefix("http://java.sun.com/jsp/jstl/fmt", "http")
【17】判断字符串是否以指定的字符串结束:
strings.HasSuffix("demo.png", ".png")
(2)日期时间相关函数:
【1】时间和日期的函数,需要到入time包,所以你获取当前时间,就要调用函数Now函数:
package main
import (
"fmt"
"time"
)
func main(){
//时间和日期的函数,需要到入time包,所以你获取当前时间,就要调用函数Now函数:
now := time.Now()
//Now()返回值是一个结构体,类型是:time.Time
fmt.Printf("%v ~~~ 对应的类型为:%T\n",now,now)
//2021-02-08 17:47:21.7600788 +0800 CST m=+0.005983901 ~~~ 对应的类型为:time.Time
//调用结构体中的方法:
fmt.Printf("年:%v \n",now.Year())
fmt.Printf("月:%v \n",now.Month())//月:February
fmt.Printf("月:%v \n",int(now.Month()))//月:2
fmt.Printf("日:%v \n",now.Day())
fmt.Printf("时:%v \n",now.Hour())
fmt.Printf("分:%v \n",now.Minute())
fmt.Printf("秒:%v \n",now.Second())
}
go你好优美呀!
【2】日期的格式化:
(1)将日期以年月日时分秒按照格式输出为字符串:
//Printf将字符串直接输出:
fmt.Printf("当前年月日: %d-%d-%d 时分秒:%d:%d:%d \n",now.Year(),now.Month(),
now.Day(),now.Hour(),now.Minute(),now.Second())
//Sprintf可以得到这个字符串,以便后续使用:
datestr := fmt.Sprintf("当前年月日: %d-%d-%d 时分秒:%d:%d:%d \n",now.Year(),now.Month(),
now.Day(),now.Hour(),now.Minute(),now.Second())
fmt.Println(datestr)
当前年月日: 2022-4-2 时分秒:11:46:46
当前年月日: 2022-4-2 时分秒:11:46:46
(2)按照指定格式:自动识别格式
//这个参数字符串的各个数字必须是固定的,必须这样写
datestr2 := now.Format("2006/01/02 15/04/05")
fmt.Println(datestr2)
//选择任意的组合都是可以的,根据需求自己选择就可以(自己任意组合)。
datestr3 := now.Format("2006 15:04")
fmt.Println(datestr3)
2022/04/02 11/47/52
2022 11:47
(3)内置函数:
【1】什么是内置函数/内建函数:
Golang设计者为了编程方便,提供了一些函数,这些函数不用导包可以直接使用,我们称为Go的内置函数/内建函数。
【2】内置函数存放位置:
在builtin包下,使用内置函数的,直接用就行
【3】常用函数:
(1)len函数:
统计字符串的长度,按字节进行统计
(2)new函数:分配内存,主要用来分配值类型(int系列, float系列, bool, string、数组和结构体struct)
(3)make函数:
分配内存,主要用来分配引用类型(指针、slice切片、map、管道chan、interface 等)
5)包的引入:
【1】使用包的原因:
(1)我们不可能把所有的函数放在同一个源文件中,可以分门别类的把函数放在不同的原文件中
(2)解决同名问题:两个人都想定义一个同名的函数,在同一个文件中是不可以定义相同名字的函数的。此时可以用包来区分
【2】案例展示包:项目的结构:
demo8代码:
package main
import (
"fmt"
"helloworld/test02/demo1" //这里就是导包路径
)
func main() {
fmt.Println(demo1.N3)
}
demo1代码:
//package main
package demo1
var n1 = 100
var n2 = 1.89
var (
N3 = 699
n4 = "ladidol"
)
1.package进行包的声明,建议:包的声明这个包和所在的文件夹同名
2.main包是程序的入口包,一般main函数会放在这个包下 main函数一定要放在main包下,否则不能编译执行
3.包名和文件夹的名字,可以不一样
4.一个目录下的同级文件归属一个包同级别的源文件的包的声明必须一致
5.包到底是什么:
(1)在程序层面,所有使用相同 package 包名 的源文件组成的代码模块
(2)在源文件层面就是一个文件夹
6.可以给包取别名,取别名后,原来的包名就不能使用了
6)初始化函数init():
类似于java中的构造函数
【1】init函数:初始化函数,可以用来进行一些初始化的操作
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。
【2】全局变量定义,init函数,main函数的执行流程?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQq651Cz-1648974341952)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331224122916.png)]
【3】多个源文件都有init函数的时候,如何执行:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AwldOh8i-1648974341953)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331224337791.png)]
执行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U8B9B2dE-1648974341954)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220331224352592.png)]
由此可知:
先对import包里面的东西进行初始化,再对main包进行操作;
7)错误处理
【1】错误处理/捕获机制:
go中追求代码优雅,引入机制:defer+recover机制处理错误
内置函数recover:
package main
import "fmt"
func main() {
test()
fmt.Println("已经执行上面的除法方法")
}
func test() {
defer func() {
err := recover()
if err != nil {
fmt.Println("错误已经捕获到了")
fmt.Println("err是:", err)
}
}()
num1 := 10
num2 := 0
result := num1 / num2
fmt.Println(result)
}
结构:
错误已经捕获到了
err是: runtime error: integer divide by zero
已经执行上面的除法方法
优点:提高程序健壮性
先简单介绍一下这个异常
【2】自定义错误:
需要调用errors包下的New函数:函数返回error类型
代码:
package main
import (
"errors"
"fmt"
)
func main() {
err := test()
if err != nil {
fmt.Println("自定义错误: ", err)
}
fmt.Println("上面的除法函数执行了...")
}
func test() (err error) {
num1 := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0!")
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
自定义错误: 除数不能为0!
上面的除法函数执行了...
【3】终止函数进行:
有一种情况:程序出现错误以后,后续代码就没有必要执行,想让程序中断,退出程序:借助:builtin包下内置函数:panic
package main
import (
"errors"
"fmt"
)
func main() {
err := test()
if err != nil {
fmt.Println("自定义错误: ", err)
panic(err)//终止程序进行
}
fmt.Println("上面的除法函数执行成功了...")
}
func test() (err error) {
num1 := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0!")
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
自定义错误: 除数不能为0!
panic: 除数不能为0!
8)数组
数组的引入:
var 数组名 [数组大小]数据类型
例子:
package main
import "fmt"
func main(){
//实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
//给出五个学生的成绩:--->数组存储:
//定义一个数组:
var scores [5]int
//将成绩存入数组:
scores[0] = 95
scores[1] = 91
scores[2] = 39
scores[3] = 60
scores[4] = 21
//求和:
//定义一个变量专门接收成绩的和:
sum := 0
for i := 0;i < len(scores);i++ {//i: 0,1,2,3,4
sum += scores[i]
}
//平均数:
avg := sum / len(scores)
//输出
fmt.Printf("成绩的总和为:%v,成绩的平均数为:%v",sum,avg)
}
地址分配和C语言差不多:
数组优点:访问/查询/读取 速度快
数组的遍历:
【1】普通for循环【2】键值循环(键值循环) for range结构是Go语言特有的一种的迭代结构,在许多情况下都非常有用,for range 可以遍历数组、切片、字符串、map 及通道,for range 语法上类似于其它语言中的 foreach 语句,一般形式为:
for key, val := range coll {
...
}
注意:
(1)coll就是你要的数组
(2)每次遍历得到的索引用key接收,每次遍历得到的索引位置上的值用val
(3)key、value的名字随便起名 k、v key、value
(4)key、value属于在这个循环中的局部变量
(5)你想忽略某个值:用_就可以了:
package main
import "fmt"
func main(){
//实现的功能:给出五个学生的成绩,求出成绩的总和,平均数:
//给出五个学生的成绩:--->数组存储:
//定义一个数组:
var scores [5]int
//将成绩存入数组:(循环 + 终端输入)
for i := 0; i < len(scores);i++ {//i:数组的下标
fmt.Printf("请录入第个%d学生的成绩",i + 1)
fmt.Scanln(&scores[i])
}
//展示一下班级的每个学生的成绩:(数组进行遍历)
//方式1:普通for循环:
for i := 0; i < len(scores);i++ {
fmt.Printf("第%d个学生的成绩为:%d\n",i+1,scores[i])
}
fmt.Println("-------------------------------")
//方式2:for-range循环
for key,value := range scores {
fmt.Printf("第%d个学生的成绩为:%d\n",key + 1,value)
}
}
数组常用的初始化方法:
package main
import "fmt"
func main(){
//第一种:类似于数组间的赋值
var arr1 [3]int = [3]int{3,6,9}
fmt.Println(arr1)
//第二种:自动识别的那种赋值
var arr2 = [3]int{1,4,7}
fmt.Println(arr2)
//第三种:这也是,我猜这是最常用的
var arr3 = [...]int{4,5,6,7}
fmt.Println(arr3)
//第四种:这令人感觉到sweet的
var arr4 = [...]int{2:66,0:33,1:99,3:88}
fmt.Println(arr4)
}
[3 6 9]
[1 4 7]
[4 5 6 7]
[33 99 66 88]
一些注意地方:
【1】长度属于类型的一部分 :
【2】Go中数组属值类型,在默认情况下是值传递,因此会进行值拷贝。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ykAByDT2-1648974341957)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220402215016806.png)]【3】如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)。
二维数组:
【1】二维数组的定义,并且有默认初始值:
var arr [2][3]int16
【2】二维数组内存:
【4】二维数组的初始化:
二维数组的遍历:
【1】普通for循环
【2】键值循环(for range)
go中println函数可以直接打印数组的值
代码:
package main
import "fmt"
func main(){
//定义二维数组:
var arr [3][3]int = [3][3]int{{1,4,7},{2,5,8},{3,6,9}}
fmt.Println(arr)//好美妙啊,
fmt.Println("------------------------")
//方式1:普通for循环:
for i := 0;i < len(arr);i++{
for j := 0;j < len(arr[i]);j++ {
fmt.Print(arr[i][j],"\t")
}
fmt.Println()
}
fmt.Println("------------------------")
//方式2:for range循环:
for key,value := range arr {
for k,v := range value {
fmt.Printf("arr[%v][%v]=%v\t",key,k,v)
}
fmt.Println()
}
}
[[1 4 7] [2 5 8] [3 6 9]]
------------------------
1 4 7
2 5 8
3 6 9
------------------------
arr[0][0]=1 arr[0][1]=4 arr[0][2]=7
arr[1][0]=2 arr[1][1]=5 arr[1][2]=8
arr[2][0]=3 arr[2][1]=6 arr[2][2]=9
9)切片
切片简介:
【1】切片(slice)是golang中一种特有的数据类型
【2】数组有特定的用处,但是却有一些呆板(数组长度固定不可变),所以在 Go 语言的代码里并不是特别常见。相对的切片却是随处可见的,切片是一种建立在数组类型之上的抽象,它构建在数组之上并且提供更强大的能力和便捷。
【3】切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个相关数组的动态窗口。
【4】代码:
切片的语法:
var 切片名 []类型 = 数组[beginindex,endindex]
或者下面
var slice = arr[0:end] ----> var slice = arr[:end]
var slice = arr[start:len(arr)] ----> var slice = arr[start:]
var slice = arr[0:len(arr)] ----> var slice = arr[:]
切片的运用:
动态变化的大小
切片的内存分析:
切片有3个字段的数据结构:一个是指向底层数组的指针,一个是切片的长度,一个是切片的容量。
是一个结构体
切片的遍历:
和数组差不多:
for key, val := range coll {
...
}
coll就是你的切片咯
package main
import "fmt"
func main(){
//定义切片:
slice := make([]int,4,20)
slice[0] = 66
slice[1] = 88
slice[2] = 99
slice[3] = 100
//方式1:普通for循环
for i := 0;i < len(slice);i++ {
fmt.Printf("slice[%v] = %v \t" ,i,slice[i])
}
fmt.Println("\n------------------------------")
//方式2:for-range循环:
for i,v := range slice {
fmt.Printf("下标:%v ,元素:%v\n" ,i,v)
}
}
切片的注意事项:
没啥注意事项,就把它当一个数组就行
切片可以动态增长
package main
import "fmt"
func main(){
//定义数组:
var intarr [6]int = [6]int{1,4,7,3,6,9}
//定义切片:
var slice []int = intarr[1:4] //4,7,3
fmt.Println(len(slice))
slice2 := append(slice,88,50)
fmt.Println(slice2) //[4 7 3 88 50]
fmt.Println(slice)
//底层原理:
//1.底层追加元素的时候对数组进行扩容,老数组扩容为新数组:
//2.创建一个新数组,将老数组中的4,7,3复制到新数组中,在新数组中追加88,50
//3.slice2 底层数组的指向 指向的是新数组
//4.往往我们在使用追加的时候其实想要做的效果给slice追加:
slice = append(slice,88,50)
fmt.Println(slice)
//5.底层的新数组 不能直接维护,需要通过切片间接维护操作。
}
可以通过append函数将切片追加给切片:
切片的拷贝:
package main
import "fmt"
func main(){
//定义切片:
var a []int = []int{1,4,7,3,6,9}
//再定义一个切片:
var b []int = make([]int,10)
//拷贝:
copy(b,a) //将a中对应数组中元素内容复制到b中对应的数组中
fmt.Println(b)
}
10)map类型:
map的简介:
基本语法:
1
var name map[int]string
name = make(map[int]string,10)
2
name := make(map[int]string)
3
name := map[int]string{
20095452 : "张三",
20098765 : "李四",
}
注意点:
PS:key、value的类型:bool、数字、string、指针、channel 、还可以是只包含前面几个类型的接口、结构体、数组
PS:key通常为int 、string类型,value通常为数字(整数、浮点数)、string、map、结构体
PS:key:slice、map、function不可以
一些小特点:
(1)map集合在使用前一定要make
(2)map的key-value是无序的
(3)key是不可以重复的,如果遇到重复,后一个value会替换前一个value
(4)value可以重复的
package main
import "fmt"
func main(){
//方式1:
//定义map变量:
var a map[int]string
//只声明map内存是没有分配空间
//必须通过make函数进行初始化,才会分配空间:
a = make(map[int]string,10) //map可以存放10个键值对
//将键值对存入map中:
a[20095452] = "张三"
a[20095387] = "李四"
//输出集合
fmt.Println(a)
//方式2:
b := make(map[int]string)
b[20095452] = "张三"
b[20095387] = "李四"
fmt.Println(b)
//方式3:
c := map[int]string{
20095452 : "张三",
20098765 : "李四",
}
c[20095387] = "王五"
fmt.Println(c)
}
map[20095387:李四 20095452:张三]
map[20095387:李四 20095452:张三]
map[20095387:王五 20095452:张三 20098765:李四]
map的一些操作
【1】增加和更新操作:
map[“key”]= value ——》 如果key还没有,就是增加,如果key存在就是修改。
【2】删除操作:
delete(map,“key”) , delete是一个内置函数,如果key存在,就删除该key-value,如果k的y不存在,不操作,但是也不会报错
【3】清空操作:
(1)如果我们要删除map的所有key ,没有一个专门的方法一次删除,可以遍历一下key,逐个删除
(2)或者map = make(…),make一个新的,让原来的成为垃圾,被gc回收
【4】查找操作:
value ,bool = map[key]
value为返回的value,bool为是否返回 ,要么true 要么false
package main
import "fmt"
func main(){
//定义map
b := make(map[int]string)
//增加:
b[20095452] = "张三"
b[20095387] = "李四"
//修改:
b[20095452] = "王五"
//删除:
delete(b,20095387)
delete(b,20089546)
fmt.Println(b)
//查找:
value,flag := b[200]
fmt.Println(value)
fmt.Println(flag)
}
【5】获取长度:len函数
【6】遍历:for-range
package main
import "fmt"
func main(){
//定义map
b := make(map[int]string)
//增加:
b[20095452] = "张三"
b[20095387] = "李四"
b[20098833] = "王五"
//获取长度:
fmt.Println(len(b))
//遍历:
for k,v := range b {
fmt.Printf("key为:%v value为%v \t",k,v)
}
fmt.Println("---------------------------")
//加深难度:
a := make(map[string]map[int]string)
//赋值:
a["班级1"] = make(map[int]string,3)
a["班级1"][20096677] = "露露"
a["班级1"][20098833] = "丽丽"
a["班级1"][20097722] = "菲菲"
a["班级2"] = make(map[int]string,3)
a["班级2"][20089911] = "小明"
a["班级2"][20085533] = "小龙"
a["班级2"][20087244] = "小飞"
for k1,v1:= range a {
fmt.Println(k1)
for k2,v2:= range v1{
fmt.Printf("学生学号为:%v 学生姓名为%v \t",k2,v2)
}
fmt.Println()
}
}
这个好屌啊
11)面向对象
面向对象的引入:
【1】Golang语言面向对象编程说明:
(1)Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
(2)Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Gelang是基于struct来实现OOP特性的。
(3)Golang面向对象编程非常简洁,去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等
(4)Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
【2】结构体的引入:
具体的对象:
一位老师:珊珊老师: 姓名:赵珊珊 年龄:31岁 性别 :女 …
package main
import "fmt"
//定义老师结构体,将老师中的各个属性 统一放入结构体中管理:
type Teacher struct{
//变量名字大写外界可以访问这个属性
Name string
Age int
School string
}
func main(){
//创建老师结构体的实例、对象、变量:
var t1 Teacher // var a int
fmt.Println(t1) //在未赋值时默认值:{ 0 }
t1.Name = "兴趣使然的小小"
t1.Age = 20
t1.School = "成都信息工程大学"
fmt.Println(t1)
fmt.Println(t1.Age + 10)
}
{ 0 }
{马士兵 45 清华大学}
55
结构体的实例创建方式:
【1】方式1:直接创建:
【2】方式2:
【3】方式3:返回的是结构体指针:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GVMlrrAj-1648974341964)(https://cdn.jsdelivr.net/gh/ladidol/figurebed@main/img/image-20220402223448562.png)]
【4】方式4:返回的是结构体指针:
结构体之间的转换:
【1】结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
package main
import "fmt"
type Student struct {
Age int
}
type Person struct {
Age int
}
func main() {
//var s Student = Student{10}
var p Person = Person{10}
s := Student(p)
fmt.Printf("%T", s)
fmt.Println()
fmt.Printf("%T", p)
}
main.Student
main.Person
emmm这个:
【2】结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
package main
import "fmt"
type Student struct {
Age int
}
type Stu Student
func main(){
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1)
fmt.Println(s2)
}
接下来的还要看这个视频传送门
v1:= range a {
fmt.Println(k1)
for k2,v2:= range v1{
fmt.Printf(“学生学号为:%v 学生姓名为%v \t”,k2,v2)
}
fmt.Println()
}
}
这个好屌啊
## 11)面向对象
### 面向对象的引入:
【1】Golang语言面向对象编程说明:
(1)Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
(2)Golang没有类(class),**Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位**,你可以理解Gelang是基于struct来实现OOP特性的。
(3)Golang面向对象编程非常简洁,**去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等**
(4)**Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样**,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
【2】结构体的引入:
具体的对象:
一位老师:珊珊老师: 姓名:赵珊珊 年龄:31岁 性别 :女 ......
```go
package main
import "fmt"
//定义老师结构体,将老师中的各个属性 统一放入结构体中管理:
type Teacher struct{
//变量名字大写外界可以访问这个属性
Name string
Age int
School string
}
func main(){
//创建老师结构体的实例、对象、变量:
var t1 Teacher // var a int
fmt.Println(t1) //在未赋值时默认值:{ 0 }
t1.Name = "兴趣使然的小小"
t1.Age = 20
t1.School = "成都信息工程大学"
fmt.Println(t1)
fmt.Println(t1.Age + 10)
}
{ 0 }
{马士兵 45 清华大学}
55
[外链图片转存中…(img-k7L1EabC-1648974341963)]
结构体的实例创建方式:
【1】方式1:直接创建:
[外链图片转存中…(img-M3Le4ILV-1648974341963)]
【2】方式2:
[外链图片转存中…(img-ZHvRbsz9-1648974341964)]
【3】方式3:返回的是结构体指针:
[外链图片转存中…(img-GVMlrrAj-1648974341964)]
【4】方式4:返回的是结构体指针:
[外链图片转存中…(img-hwckQCoP-1648974341965)]
结构体之间的转换:
【1】结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
package main
import "fmt"
type Student struct {
Age int
}
type Person struct {
Age int
}
func main() {
//var s Student = Student{10}
var p Person = Person{10}
s := Student(p)
fmt.Printf("%T", s)
fmt.Println()
fmt.Printf("%T", p)
}
main.Student
main.Person
emmm这个:
【2】结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
package main
import "fmt"
type Student struct {
Age int
}
type Stu Student
func main(){
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1)
fmt.Println(s2)
}
接下来的还要看这个视频传送门