对go语言挺感兴趣的,所以简单学习一下,这里是对go语言的初级入门
如果想要入门的话,也可以看看这个博主的,他的是和快速入门, 戳这里
golang的一些语法香的地方:极简单的部署方式:可直接编译成机器码、不依赖其他库、直接运行即可部署。
静态类型语言,编译的时候可以检查出大多数问题。
语言层面的并发:天生的基因支持、充分的利用多核
// Go 语言实现并发的代码
func goFunc(i int) {
fmt.Println("goroutine ", i, " ...")
}
func main() {
for i := 0; i < 1000; i++ {
go goFunc(i) // 开启一个并发协程
}
time.Sleep(time.Second)
}
对于是个的结果如下:
goroutine 9 ...
goroutine 5 ...
goroutine 0 ...
goroutine 3 ...
goroutine 2 ...
goroutine 4 ...
goroutine 8 ...
goroutine 7 ...
goroutine 6 ...
goroutine 1 ...
强大的标准库:runtime 系统调度机制、高效的 CG 垃圾回收、丰富的标准库
“大厂” 领军:Google、facebook、Tencent、Baidu、七牛、字节 …
不同语言的斐波那契数列算法 编译 + 运行 时间对比:
Golang 的应用场景1、云计算基础设施领域:
代表项目:docker、kubernetes、etcd、consul、cloud flare CDN、七牛云存储 等。
2、基础后端软件:
代表项目:tidb、influxdb、 cockroach 等。
3、微服务
代表项目:go-kit、 micro、 monzo bank 的 typhon、bilibili 等。
4、互联网基础设施
代表项目:以太坊、hyperledger 等
Golang 的不足1、包管理,大部分包都托管在 Github 上。
像我们熟悉的 maven、npm 等都有专门的包管理组织;
托管在 Github 上的代码容易被作者个人 *** 作影响到使用该项目的工程。
2、无泛化类型。
据说很快就会有了。
3、所有 Exception 都用 Error 来处理(有争议)。
4、对 C 的降级处理,并非无缝,没有 C 降级到 asm 那么完美。(序列化问题)
go语言中的占位符:占位符 | 说明 | 举例 | 输出 |
%v | 相应值的默认格式。 | Printf(“%v”, name) | {小小} |
%+v | 打印结构体时,会添加字段名 | Printf(“%+v”, name) | {Name:小小} |
%#v | 相应值的Go语法表示 | Printf(“#v”, name) | main.Human{Name:“小小”} |
%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而非空格;对于数字,这会将填充移到正负号之后 |
变量练习:
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码表:
左面是不可见字符 右面是可见字符
【4】查看UTF-8编码表:
http://www.mytju.com/classcode/tools/encode_utf8.asp
【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】介绍:
字符串就是一串固定长度的字符连接起来的字符序列。对于字符串 *** 作的 4 个包:bytes、strings、strconv、unicode
【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)
}
(*)字符串的常用包使用展示:
简介:
bytes 包 *** 作 []byte
。因为字符串是只读的,因此逐步构创建字符串会导致很多分配和复制,使用 bytes.Buffer
类型会更高。strings 包提供 切割、索引、前缀、查找、替换 等功能。strconv 包提供 布尔型、整型数、浮点数 和对应字符串的相互转换,还提供了双引号转义相关的转换。unicode 包提供了 IsDigit、IsLetter、IsUpper、IsLower 等类似功能,用于给字符分类。
如果 string 中包含汉字,要注意:
UTF-8 编码中,一个汉字需要 3 个字节,通过len()
获取的是字符串占据的字节数。
str1 := "hello 世界"
fmt.Println(len(str1)) // 12
如果想要得到字符串本身的长度,可以将 string 转为 rune 数组再计算:
str2 := "hello 世界"
fmt.Println(len([]rune(str2))) // 8
字符串遍历
byte 是 uint8 的别名
rune 是 int32 的别名,相当于 Go 里面的 char
如果包含汉字,以下遍历方式会出现乱码:
str := "你好世界!"
for i := 0; i < len(str); i++ {
fmt.Printf("%c", str[i])
}
// ä½ å¥½ä¸çï¼%
解决方案1:转成 rune 切片再遍历
str := "你好世界!"
newStr := []rune(str)
for i := 0; i < len(newStr); i++ {
fmt.Printf("%c", newStr[i])
}
// 你好世界!
解决方案2:使用 range 来遍历
range 按照字符遍历,前面的 for 按照字节遍历
str := "你好世界123"
for index, value := range str {
fmt.Printf("index = %d value = %c\n", index, value)
}
index = 0 value = 你
index = 3 value = 好
index = 6 value = 世
index = 9 value = 界
index = 12 value = 1
index = 13 value = 2
index = 14 value = 3
strings包(简单 *** 作)
字符串比较:使用 strings.Compare
比较两个字符串的字典序
strings.Compare("aaa", "bbb") // -1
strings.Compare("baa", "abb") // 1
strings.Compare("aaa", "aaa") // 0
查找函数:使用 strings.Index
查找字符串中子串的位置(第 1 个),不存在返回 -1
strings.Index("hello world", "o") // 4
类似的,使用 strings.LastIndex
查找字符串子串出现的最后一个位置,不存在返回 -1
strings.Index("hello world", "o") // 4
Count、Repeat:
使用 strings.Count
统计子串在整体中出现的次数:
strings.Count("abc abc abab abc", "abc") // 3
使用 strings.Repeat
将字符串重复指定次数:
strings.Repeat("abc", 3) // abcabcabc
Replace、Split、Join:
strings.Replace
实现字符串替换
str := "acaacccc"
// 局部替换 param3: 替换次数,< 0 则全部替换
strings.Replace(str, "a", "b", 2) // bcbacccc
strings.Replace(str, "a", "b", -1) // bcbbcccc
// 全部替换
strings.ReplaceAll(str, "a", "b") // bcbbcccc
strings.Split
实现字符串切割
str := "abc,bbc,bbd"
slice := strings.Split(str, ",")
fmt.Println(slice) // [abc bbc bbd]
strings.Join
实现字符串拼接
slice := []string{"aab", "aba", "baa"}
str := strings.Join(slice, ",")
fmt.Println(str // aab,aba,baa
go语言好美啊!
bytes 包(buffer)Buffer 是 bytes 包中定义的 type Buffer struct {...}
,Bufer 是一个变长的可读可写的缓冲区。buffer缓冲区好神奇!
创建缓冲器:bytes.NewBufferString
、bytes.NewBuffer
func main() {
buf1 := bytes.NewBufferString("hello")
buf2 := bytes.NewBuffer([]byte("hello"))
buf3 := bytes.NewBuffer([]byte{'h', 'e', 'l', 'l', 'o'})
fmt.Printf("%v,%v,%v\n", buf1, buf2, buf3)
fmt.Printf("%v,%v,%v\n", buf1.Bytes(), buf2.Bytes(), buf3.Bytes())
buf4 := bytes.NewBufferString("")
buf5 := bytes.NewBuffer([]byte{})
fmt.Println(buf4.Bytes(), buf5.Bytes())
}
hello,hello,hello
[104 101 108 108 111],[104 101 108 108 111],[104 101 108 108 111]
[] []
写入缓冲器:Write
、WriteString
、WriteByte
、WriteRune
、WriteTo
func main() {
buf := bytes.NewBufferString("a")
fmt.Printf("%v, %v\n", buf.String(), buf.Bytes())
// a, [97]
buf.Write([]byte("b")) // Write
buf.WriteString("c") // WriteString
buf.WriteByte('d') // WriteByte
buf.WriteRune('e') // WriteRune
fmt.Printf("%v, %v\n", buf.String(), buf.Bytes())
// abcde, [97 98 99 100 101]
}
缓冲区原理介绍:Go 字节缓冲区底层以字节切片做存储,切片存在长度 len 与容量 cap
缓冲区从长度 len 的位置开始写,当 len > cap 时,会自动扩容缓冲区从内置标记 off 位置开始读(off 始终记录读的起始位置)当 off == len 时,表明缓冲区已读完,读完就重置缓冲区 len = off = 0func main() {
byteSlice := make([]byte, 20)
byteSlice[0] = 1 // 将缓冲区第一个字节置1
byteBuffer := bytes.NewBuffer(byteSlice) // 创建20字节缓冲区 len = 20 off = 0
c, _ := byteBuffer.ReadByte() // off+=1
fmt.Printf("len:%d, c=%d\n", byteBuffer.Len(), c) // len = 20 off =1 打印c=1
byteBuffer.Reset() // len = 0 off = 0
fmt.Printf("len:%d\n", byteBuffer.Len()) // 打印len=0
byteBuffer.Write([]byte("hello byte buffer")) // 写缓冲区 len+=17
fmt.Printf("len:%d\n", byteBuffer.Len()) // 打印len=17
byteBuffer.Next(4) // 跳过4个字节 off+=4
c, _ = byteBuffer.ReadByte() // 读第5个字节 off+=1
fmt.Printf("第5个字节:%d\n", c) // 打印:111(对应字母o) len=17 off=5
byteBuffer.Truncate(3) // 将未字节数置为3 len=off+3=8 off=5
fmt.Printf("len:%d\n", byteBuffer.Len()) // 打印len=3为未读字节数 上面len=8是底层切片长度
byteBuffer.WriteByte(96) // len+=1=9 将y改成A
byteBuffer.Next(3) // len=9 off+=3=8
c, _ = byteBuffer.ReadByte() // off+=1=9 c=96
fmt.Printf("第9个字节:%d\n", c) // 打印:96
}
缓冲区:
func main() {
buf := &bytes.Buffer{}
// 写缓冲区
buf.WriteString("abc?def")
// 从缓冲区读(分隔符为 ?)
str, _ := buf.ReadString('?')
fmt.Println("str = ", str)
fmt.Println("buff = ", buf.String())
}
str = abc?
buff = def
缓冲区读数据:Read
、ReadByte
、ReadByes
、ReadString
、ReadRune
、ReadFrom
func main() {
log.SetFlags(log.Lshortfile)
buff := bytes.NewBufferString("123456789")
log.Println("buff = ", buff.String()) // buff = 123456789
//先创建一个四个字节的数组s
s := make([]byte, 4)
// 从缓冲区读取4个字节
n, _ := buff.Read(s)
log.Println("buff = ", buff.String()) // buff = 56789
log.Println("s = ", string(s)) // s = 1234
log.Println("n = ", n) // n = 4
// 从缓冲区读取4个字节
n, _ = buff.Read(s)
log.Println("buff = ", buff.String()) // buff = 9
log.Println("s = ", string(s)) // s = 5678
log.Println("n = ", n) // n = 4
// 再读四个的话,会发现缓冲区本来只有一个9了,锁一个数组s就第一个被重新写入
n, _ = buff.Read(s)
log.Println("buff = ", buff.String()) // buff =
log.Println("s = ", string(s)) // s = 9678
log.Println("n = ", n) // n = 1
buff.Reset()
buff.WriteString("abcdefg")
log.Println("buff = ", buff.String()) // buff = abcdefg
b, _ := buff.ReadByte()
log.Println("b = ", string(b)) // b = a
log.Println("buff = ", buff.String()) // buff = bcdefg
b, _ = buff.ReadByte()
log.Println("b = ", string(b)) // b = b
log.Println("buff = ", buff.String()) // buff = cdefg
bs, _ := buff.ReadBytes('e')
log.Println("bs = ", string(bs)) // bs = cde
log.Println("buff = ", buff.String()) // buff = fg
buff.Reset()
buff.WriteString("编译输出GO")
r, l, _ := buff.ReadRune()
log.Println("r = ", r, ", l = ", l, ", string(r) = ", string(r))
// r = 32534 , l = 3 , string(r) = 编
buff.Reset()
buff.WriteString("qwer")
str, _ := buff.ReadString('?')
log.Println("str = ", str) // str = qwer
log.Println("buff = ", buff.String()) // buff =
buff.WriteString("qwer")
str, _ = buff.ReadString('w')
log.Println("str = ", str) // str = qw
log.Println("buff = ", buff.String()) // buff = er
file, _ := os.Open("doc.go")
buff.Reset()
buff.ReadFrom(file)
log.Println("doc.go = ", buff.String()) // doc.go = 123
buff.Reset()
buff.WriteString("中国人")
cbyte := buff.Bytes()
log.Println("cbyte = ", cbyte) // cbyte = [228 184 173 229 155 189 228 186 186]
}
strconv 包(转换,包括类型转化)
字符串转 []byte:
sum := []byte("hello")
字符串 —> 整数:使用 strconv.Atoi
或 strconv.ParseInt
// 按照 10进制 转换,返回 int 类型
i, _ := strconv.Atoi("33234")
fmt.Printf("%T\n", i) // int
// param1:要转化的字符串
// param2:转换的进制,如 2,8,16,32
// param3:返回bit的大小(注意,字面量显示还是 int64)
i2, _ := strconv.ParseInt("33234", 10, 0)
fmt.Printf("%T\n", i2) // int64
字符串 —> 浮点数:使用 strconv.ParseFloat
// 参数类似 ParseInt
val, _ := strconv.ParseFloat("33.33", 32)
fmt.Printf("type: %T\n", val) // type: float64
val2, _ := strconv.ParseFloat("33.33", 64)
fmt.Printf("type: %T\n", val2) // type: float64
整数 —> 字符串:使用 strconv.Iota
或 strconv.FormatInt
num := 180
// 默认按照10进制转换
f1 := strconv.Itoa(num)
// param1: 要转换的数字(必须是int64类型)
// param2: 转换的进制
f2 := strconv.FormatInt(int64(num), 10)
浮点数 —> 整数:使用 strconv.FormatFloat
num := 23423134.323422
fmt.Println(strconv.FormatFloat(float64(num), 'f', -1, 64)) // 普通模式
fmt.Println(strconv.FormatFloat(float64(num), 'b', -1, 64)) // 二进制模式
fmt.Println(strconv.FormatFloat(float64(num), 'e', -1, 64)) // 科学记数法
fmt.Println(strconv.FormatFloat(float64(num), 'E', -1, 64)) // 同上,显示为E
fmt.Println(strconv.FormatFloat(float64(num), 'g', -1, 64)) // 指数大时用科学记数,否则普通模式
fmt.Println(strconv.FormatFloat(float64(num), 'G', -1, 64)) // 同上,显示为E
23423134.323422
6287599743057036p-28
2.3423134323422e+07
2.3423134323422E+07
2.3423134323422e+07
2.3423134323422E+07
字符串 和 bool 类型转换:
// string --> bool
flagBool, _ := strconv.ParseBool("true")
// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
// Any other value returns an error.
// bool --> string
flagStr := strconv.FormatBool(true)
unicode包(就是判断字符串的一些性质的)
/src/unicode/letter.go
:
// 判断字符 r 是否为大写格式
func IsUpper(r rune) bool
// 判断字符 r 是否为小写格式
func IsLower(r rune) bool
// 判断字符 r 是否为 Unicode 规定的 Title 字符
// 大部分字符的 Title 格式就是其大写格式
// 只有少数字符的 Title 格式是特殊字符
// 这里判断的就是特殊字符
func IsTitle(r rune) bool
// ToUpper 将字符 r 转换为大写格式
func ToUpper(r rune) rune
// ToLower 将字符 r 转换为小写格式
func ToLower(r rune) rune
// ToTitle 将字符 r 转换为 Title 格式
// 大部分字符的 Title 格式就是其大写格式
// 只有少数字符的 Title 格式是特殊字符
func ToTitle(r rune) rune
// To 将字符 r 转换为指定的格式
// _case 取值:UpperCase、LowerCase、TitleCase
func To(_case int, r rune) rune
1234567891011121314151617181920212223242526
/src/unicode/digit.go
:
// IsDigit 判断 r 是否为一个十进制的数字字符
func IsDigit(r rune) bool
12
/src/unicode/graphic.go
:
// IsNumber 判断 r 是否为一个数字字符 (类别 N)
func IsNumber(r rune) bool
// IsLetter 判断 r 是否为一个字母字符 (类别 L)
// 汉字也是一个字母字符
func IsLetter(r rune) bool
// IsSpace 判断 r 是否为一个空白字符
// 在 Latin-1 字符集中,空白字符为:\t, \n, \v, \f, \r,
// 空格, U+0085 (NEL), U+00A0 (NBSP)
// 其它空白字符的定义有“类别 Z”和“Pattern_White_Space 属性”
func IsSpace(r rune) bool
// IsControl 判断 r 是否为一个控制字符
// Unicode 类别 C 包含更多字符,比如代理字符
// 使用 Is(C, r) 来测试它们
func IsControl(r rune) bool
// IsGraphic 判断字符 r 是否为一个“图形字符”
// “图形字符”包括字母、标记、数字、标点、符号、空格
// 他们分别对应于 L、M、N、P、S、Zs 类别
// 这些类别是 RangeTable 类型,存储了相应类别的字符范围
func IsGraphic(r rune) bool
// IsPrint 判断字符 r 是否为 Go 所定义的“可打印字符”
// “可打印字符”包括字母、标记、数字、标点、符号和 ASCII 空格
// 他们分别对应于 L, M, N, P, S 类别和 ASCII 空格
// “可打印字符”和“图形字符”基本是相同的,不同之处在于
// “可打印字符”只包含 Zs 类别中的 ASCII 空格(U+0020)
func IsPrint(r rune) bool
// IsPunct 判断 r 是否为一个标点字符 (类别 P)
func IsPunct(r rune) bool
// IsSymbol 判断 r 是否为一个符号字符
func IsSymbol(r rune) bool
// IsMark 判断 r 是否为一个 mark 字符 (类别 M)
func IsMark(r rune) bool
// IsOneOf 判断 r 是否在 set 范围内
func IsOneOf(set []*RangeTable, r rune) bool
基本数据类型的默认值:
【1】在Golang中数据类型都有一个默认值,当程序员没有赋值时,就会保留默认值(默认值又叫零值)。
基本类型之间的转换:【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)
}
方式二
用了一下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包的函数
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("您的成绩有误")
}
}
用switch来做的一个很神奇的类型判断:
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
循环结构:
原始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个:
返回1个:
返回多个:
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
}
例二:
func a() func() int {
i := 0
b := func() int {
i++
fmt.Println(i)
return i
}
return b
}
func main() {
//这里的c就是返回的一个,函数
c := a()
//这里的i一直在变而已
c() // 1
c() // 2
c() // 3
a() // 返回的是一个函数的地址,所以无输出
}
感受:匿名函数中引用的那个变量会一直保存在内存中,可以一直使用
【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
【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")
【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 等)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yFcviw49-1649144080914)(http://www.kaotop.com/file/tupian/20220518/image-20220404193512823.png)]
制作报的时候,项目路径如下:
$GOPATH/GolangStudy/5-init/
├── lib1/
│ └── lib1.go
├── lib2/
│ └── lib2.go
└── main.go
【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函数的执行流程?
【3】多个源文件都有init函数的时候,如何执行:
执行结果:
由此可知:
先对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中数组属值类型,在默认情况下是值传递,因此会进行值拷贝。
【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()
}
}
这个好屌啊
map和slice都是指针指向,就像引用类型一样
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 }
{兴趣使然的小小 20 成都信息工程大学}
30
结构体的实例创建方式:
注意结构体传的是值,所以一般都是要传指针进去;
【1】方式1:直接创建:
【2】方式2:
【3】方式3:返回的是结构体指针:
【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)
}
接下来的还要看这个视频传送门
面向对象编程 type利用 type 可以声明某个类型的别名(理解为声明一种新的数据类型)
type myint int
func main() {
var a myint = 10
fmt.Println("a = ", a)
fmt.Printf("type of a = %T\n", a)
}
a = 10
type of a = main.myint
方法
方法:包含了接受者的函数,接受者可以是命名类型或结构体类型的值或者指针。
方法和普通函数的区别:
对于普通函数,参数为值类型时,不能将指针类型的数据直接传递,反之亦然。对于方法,接收者为值类型时,可以直接用指针类型的变量调用方法(反过来也可以)。// 1.普通函数
// 接收值类型参数的函数
func valueIntTest(a int) int {
return a + 10
}
// 接收指针类型参数的函数
func pointerIntTest(a *int) int {
return *a + 10
}
func structTestValue() {
a := 2
fmt.Println("valueIntTest:", valueIntTest(a))
// 函数的参数为值类型,则不能直接将指针作为参数传递
// fmt.Println("valueIntTest:", valueIntTest(&a))
// compile error: cannot use &a (type *int) as type int in function argument
b := 5
fmt.Println("pointerIntTest:", pointerIntTest(&b))
// 同样,当函数的参数为指针类型时,也不能直接将值类型作为参数传递
// fmt.Println("pointerIntTest:", pointerIntTest(b))
// compile error:cannot use b (type int) as type *int in function argument
}
对象的方法: golang中,就好似吧类的属性和方法给分开来写了
// 2.方法
type PersonD struct {
id int
name string
}
//接收者为值类型
func (p PersonD) valueShowName() {
fmt.Println(p.name)
}
//接收者为指针类型
func (p *PersonD) pointShowName() {
fmt.Println(p.name)
}
func structTestFunc() {
// 与普通函数不同,接收者为指针类型和值类型的方法,指针类型和值类型的变量均可相互调用
// 值类型调用方法
personValue := PersonD{101, "hello world"}
personValue.valueShowName()
personValue.pointShowName()
// 指针类型调用方法
personPointer := &PersonD{102, "hello golang"}
personPointer.valueShowName()
personPointer.pointShowName()
}
一道 struct 与指针面试题:
type student struct {
name string
age int
}
func main() {
m := make(map[string]*student)
stus := []student{
{name: "aaa", age: 18},
{name: "bbb", age: 23},
{name: "ccc", age: 28},
}
for _, stu := range stus {
m[stu.name] = &stu
}
for k, v := range m {
fmt.Println(k, "=>", v.name)
}
}
aaa => ccc
bbb => ccc
ccc => ccc
一般一个包就是一个类
封装Golang 中,类名、属性名、⽅法名 首字⺟大写 表示对外(其他包)可以访问,否则只能够在本包内访问。
// 如果类名首字母大写,表示其他包也能够访问
type Hero struct {
// 如果类的属性首字母大写, 表示该属性是对外能够访问的,否则的话只能够类的内部访问
Name string
Ad int
level int // 只能本包访问
}
func (h *Hero) Show() {//类似toString方法
fmt.Println("Name = ", h.Name)
fmt.Println("Ad = ", h.Ad)
fmt.Println("Level = ", h.level)
fmt.Println("---------")
}
func (h *Hero) GetName() string {//get方法
return h.Name
}
// 不用指针则传递的是副本,无法赋值
func (h *Hero) SetName(newName string) {//set方法
h.Name = newName
}
func main() {
hero := Hero{Name: "zhang3", Ad: 100}
hero.Show()
hero.SetName("li4")
hero.Show()
}
Name = zhang3
Ad = 100
Level = 0
---------
Name = li4
Ad = 100
Level = 0
---------
继承
Golang 通过匿名字段实现继承的效果:
// 父类
type Human struct {
name string
sex string
}
func (h *Human) Eat() {
fmt.Println("Human.Eat()...")
}
func (h *Human) Walk() {
fmt.Println("Human.Walk()...")
}
// 子类
type SuperMan struct {
Human // SuperMan类继承了Human类的方法
level int
}
// 重定义父类的方法Eat()
func (s *SuperMan) Eat() {
fmt.Println("SuperMan.Eat()...")
}
// 子类的新方法
func (s *SuperMan) Fly() {
fmt.Println("SuperMan.Fly()...")
}
func main() {
// 定义一个子类对象
// s := SuperMan{Human{"li4", "female"}, 88}
var s SuperMan
s.name = "li4"
s.sex = "male"
s.level = 88
s.Walk() // 父类的方法
s.Eat() // 子类的方法
s.Fly() // 子类的方法
}
Human.Walk()...
SuperMan.Eat()...
SuperMan.Fly()...
多态:
总结Go 中接口相关文章:理解Duck Typing(鸭子模型)
如何理解Golang中的接口? - 波罗学的回答 - 知乎
回答结束,做个简单总结。理解 Go 接口要记住一点,接口是一组方法的集合,这句话非常重要,理解了这句话,再去理解 Go 的其他知识,比如类型、多态、空接口、反射、类型检查与断言等就会容易很多。
Golang 中多态的基本要素:
有一个父类(有接口)// 本质是一个指针
type AnimalIF interface {
Sleep()
GetColor() string // 获取动物的颜色
GetType() string // 获取动物的种类
}
有子类(实现了父类的全部接口)
// 具体的类
type Cat struct {
color string // 猫的颜色
}
func (c *Cat) Sleep() {
fmt.Println("Cat is Sleep")
}
func (c *Cat) GetColor() string {
return c.color
}
func (c *Cat) GetType() string {
return "Cat"
}
父类类型的变量(指针)指向(引用)子类的具体数据变量
// 接口的数据类型,父类指针
var animal AnimalIF
animal = &Cat{"Green"}
animal.Sleep() // 调用的就是Cat的Sleep()方法, 多态
不同接收者实现接口
type Mover interface {
move()
}
type dog struct {
name string
}
值类型接收者实现接口:可以同时接收 值类型 和 指针类型。
Go 语言中有对指针类型变量求值的语法糖,dog 指针
dog2
内部会自动求值*dog2
// 可以同时接收 值类型 和 指针类型
func (d dog) move() {
fmt.Println(d.name, "is moving")
}
func main() {
var m Mover
var dog1 = dog{"dog1"}
m = dog1 // 可以接收值类型
m.move()
var dog2 = &dog{"dog2"}
m = dog2 // 可以接收指针类型
m.move()
}
指针类型接收者实现接口:只能接收指针类型。
// 只能接收指针类型
func (d *dog) move() {
fmt.Println(d.name, "is moving")
}
func main() {
var m Mover
// 无法接收指针类型
// var dog1 = dog{"dog1"}
// m = dog1
//m.move()
var dog2 = &dog{"dog2"}
m = dog2
m.move()
}
一道面试题:以下代码能否通过编译?
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "sb" {
talk = "你是个大帅比"
} else {
talk = "您好"
}
return
}
func main() {
var peo People = Student{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
不能。修改 var peo People = Student{}
为 var peo People = &Student{}
即可。
这里新发现了一个语法糖: 就是方法中的func (stu *Student) Speak(think string) (talk string) {
func (stu *Student) Speak(think string) (talk string) {
if think == "sb" {
talk = "你是个大帅比"
} else {
talk = "您好"
}
return
}
talk可以在第一行就进行声明了,因为它就是当返回值来着
多态就可以引出一个,类似泛型的东西:就是下面的通用万能类型
通用万能类型interface{}
表示空接口,可以用它引用任意类型的数据类型。
// interface{}是万能数据类型
func myFunc(arg interface{}) {
fmt.Println(arg)
}
type Book struct {
auth string
}
func main() {
book := Book{"Golang"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
断言:
Golang 给 interface{}
提供类型断言机制,用来区分此时引用的类型:
注意断言这个 *** 作会有两个返回值
package main
import "fmt"
// interface{}是万能数据类型
func myFunc(arg interface{}) {
// 类型断言
value, ok := arg.(string)
if !ok {
fmt.Println("arg is not string type")
} else {
fmt.Println("arg is string type, value = ", value)
fmt.Printf("value type is %T\n", value)
}
}
type Book struct {
auth string
}
func main() {
book := Book{"Golang"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
arg is not string type
arg is not string type
arg is string type, value = abc
value type is string
arg is not string type
一个接口的值(简称接口值)是由一个 具体类型 和 具体类型的值 两部分组成的。
这两部分分别称为接口的动态类型和动态值。
var w io.Writer//默认为空
w = os.Stdout//这里指的是引用了其他的,就不为空了
w = new(bytes.Buffer)//这里也是,就不为空了
w = nil
为了更好理解:
package main
import (
"fmt"
"io"
)
func show() {
fmt.Println("hello")
}
func main() {
var buf io.Writer = nil
// buf = new(bytes.Buffer)
interfaceParam(buf)
}
func interfaceParam(bufItf io.Writer) {
if bufItf != nil {
fmt.Println("吃鲸!")
} else {
fmt.Println("预期结果!")
}
// fmt.Println(reflect.ValueOf(bufItf).Kind())
// fmt.Println(reflect.ValueOf(bufItf).Type())
}
switch判断多个断言:
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
golang基础篇完结
over interface {
move()
}
type dog struct {
name string
}
**值类型接收者实现接口**:可以同时接收 值类型 和 指针类型。
> Go 语言中有对指针类型变量求值的语法糖,dog 指针 `dog2` 内部会自动求值 `*dog2`
```go
// 可以同时接收 值类型 和 指针类型
func (d dog) move() {
fmt.Println(d.name, "is moving")
}
func main() {
var m Mover
var dog1 = dog{"dog1"}
m = dog1 // 可以接收值类型
m.move()
var dog2 = &dog{"dog2"}
m = dog2 // 可以接收指针类型
m.move()
}
指针类型接收者实现接口:只能接收指针类型。
// 只能接收指针类型
func (d *dog) move() {
fmt.Println(d.name, "is moving")
}
func main() {
var m Mover
// 无法接收指针类型
// var dog1 = dog{"dog1"}
// m = dog1
//m.move()
var dog2 = &dog{"dog2"}
m = dog2
m.move()
}
一道面试题:以下代码能否通过编译?
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "sb" {
talk = "你是个大帅比"
} else {
talk = "您好"
}
return
}
func main() {
var peo People = Student{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
不能。修改 var peo People = Student{}
为 var peo People = &Student{}
即可。
这里新发现了一个语法糖: 就是方法中的func (stu *Student) Speak(think string) (talk string) {
func (stu *Student) Speak(think string) (talk string) {
if think == "sb" {
talk = "你是个大帅比"
} else {
talk = "您好"
}
return
}
talk可以在第一行就进行声明了,因为它就是当返回值来着
多态就可以引出一个,类似泛型的东西:就是下面的通用万能类型
通用万能类型interface{}
表示空接口,可以用它引用任意类型的数据类型。
// interface{}是万能数据类型
func myFunc(arg interface{}) {
fmt.Println(arg)
}
type Book struct {
auth string
}
func main() {
book := Book{"Golang"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
断言:
Golang 给 interface{}
提供类型断言机制,用来区分此时引用的类型:
注意断言这个 *** 作会有两个返回值
package main
import "fmt"
// interface{}是万能数据类型
func myFunc(arg interface{}) {
// 类型断言
value, ok := arg.(string)
if !ok {
fmt.Println("arg is not string type")
} else {
fmt.Println("arg is string type, value = ", value)
fmt.Printf("value type is %T\n", value)
}
}
type Book struct {
auth string
}
func main() {
book := Book{"Golang"}
myFunc(book)
myFunc(100)
myFunc("abc")
myFunc(3.14)
}
arg is not string type
arg is not string type
arg is string type, value = abc
value type is string
arg is not string type
一个接口的值(简称接口值)是由一个 具体类型 和 具体类型的值 两部分组成的。
这两部分分别称为接口的动态类型和动态值。
var w io.Writer//默认为空
w = os.Stdout//这里指的是引用了其他的,就不为空了
w = new(bytes.Buffer)//这里也是,就不为空了
w = nil
为了更好理解:
package main
import (
"fmt"
"io"
)
func show() {
fmt.Println("hello")
}
func main() {
var buf io.Writer = nil
// buf = new(bytes.Buffer)
interfaceParam(buf)
}
func interfaceParam(bufItf io.Writer) {
if bufItf != nil {
fmt.Println("吃鲸!")
} else {
fmt.Println("预期结果!")
}
// fmt.Println(reflect.ValueOf(bufItf).Kind())
// fmt.Println(reflect.ValueOf(bufItf).Type())
}
[外链图片转存中…(img-JuBqQvMF-1649144080937)]
switch判断多个断言:
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
golang基础篇完结
欢迎点赞关注哦!
也欢迎到访我的博客!小小的博客传送门!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)