基本数据类型
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:
|
数字类型
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
其他数字类型
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
Go语言的使用
Hello World
package main
import "fmt"
func main(){
fmt.Println("Hello World!")
}
变量
包中定义的 方法、常量、变量以及结构体的名称,若首字母大写,则表示public,小写则表示private。
变量的声明有两种方式:var 变量名 类型
和 变量名 := 表达式
//变量声明 var :=
//声明变量时 类型可省略 自动根据相应值来初始化
//封装:声明在方法外部的变量叫全局变量 内部的叫局部变量
//var可用于声明全局变量和局部变量 var a int = 1 ==> var a = 1
//:= 只能用于声明局部变量 a := 1
//变量赋值
a, b := 1, 2
fmt.Println("a:", a, " b:", b)
// & 取变量地址 * 通过指针访问目标对象
var p = [2]*int{&a, &b}
fmt.Println("*p[0]:", *p[0], " *p[1]:", *p[1])
var arr = [2]int{a, b}
pf := &arr
fmt.Println(pf)
逻辑语句
go语言中的逻辑判断语句是使用 if
。
循环语句只有for
,根据设置不同表达式,也可达到while
效果。
go语言也拥有swith判断。
这些关键字的条件都不需要加括号 ()
//if条件不用() 左{ 必须与条件、条件语句或else在同一行
if a >= 1 {
fmt.Println(a)
}
//在表达式中定义变量时 会隐藏原有变量 表达式中新定义的变量只会在该语句块中有效
//这里新定义的变量c 只在这个if代码块中有效
if c := 3; c > 2 {
fmt.Print("c:", c)
}
//fmt.Println(c) 该语句会报错:变量c并没有定义
// 在这个if代码块中初始化的a已存在 但已被隐藏 这里是初始化的a是新的变量
if a := 0; a == 0 {
fmt.Print("a:", a)
a = 2
}
//这里打印出的a是原值1 表示if代码块中对a的修改对外无效
fmt.Println(a)
//for循环 也不需要()
//for init; condition; post {} ==> for(init;condition;post){}
//for condition{} ==> while(condition){}
// for {} ==> for(;;){}
for i := 0; i < 10; i++ {
fmt.Print(i)
}
fmt.Println()
//switch 选择
//每个case的最后默认带了break 即默认情况下 匹配成功后 直接跳出选择 不继续执行
switch a { // a:1
case 0:
fmt.Println("a:0")
case 1:
fmt.Println("a:1")
//可使用 fallthrough 关键字 继续执行
fallthrough
//会继续执行case 2
case 2:
fmt.Println("a:2")
case 3:
//虽然使用fallthrough关键字后会继续执行case 2
//不过case 2 也默认带了break 所以并不会继续执行case 3
fmt.Println("a:3")
}
//标签 label: 必须被使用 若没有被使用则会编译错误
//Label 只在声明它的函数中有效。只要函数中声明了 Label ,那它在该函数的整个作用域都有效。
//Label 的标识符和其他标识符具有不同的命名空间,所以不会与变量标识符等发生冲突
goto a
a:
{
fmt.Println("label a")
}
数组
在go中数组也是一种类型。数组、切片以及字符串的底层都是字节数组,它们是不同的类型。
//数组 var 数组名 [数组长度]元素类型
var array1 [5]int //int为元素类型 并不是数组类型 该数组类型为 [5]int
array1[1] = 1
array1[2] = 2
//数组初始化中的变量机制与if类似
fmt.Println(array1)
//数组的长度可以不设置
var array2 = [...]int{0, 1, 2, 0, 0}
fmt.Println(array2)
// 可使用 index:value 设置对应下标的值
var array3 = [5]int{1: 1, 2: 2}
fmt.Println(array3)
// 此处该数组位置 0 和 3 并没有设置对应值 为默认值0
var array4 = [...]int{1: 1, 2: 2, 4: 0}
fmt.Println(array4)
// 可以使用new创建数组 new([数组长度]数组类型)
// 使用new创建数组 返回的是指向该数组的指针
var array5 = *new([5]int)
array5[1] = 1
array5[2] = 2
fmt.Println(array5)
// 此时array1 array2 array3 array4的类型都是[5]int 并且里面的值都一样
fmt.Println(array1 == array2) //true
fmt.Println(array2 == array3) //true
fmt.Println(array3 == array4) //true
fmt.Println(array4 == array5) //true
// == 比较的是值是否相等 并不是比较地址
a1 := &array1
a2 := &array2
a3 := &array3
a4 := &array4
a5 := &array5
fmt.Println(a1 == a2) //false
fmt.Println(a2 == a3) //false
fmt.Println(a3 == a4) //false
fmt.Println(a4 == a5) //false
// 若修改array4中的某个值 其与array1的值就不再相等了
array5[2] = 1
fmt.Println(array1 == array5) //false
//所有值类型变量在赋值或作为参数传递的时候将产生一次复制操作
//此处将array1赋值给arr1 表示将array1的值赋值给arr1
arr1 := array1
// 对arr1进行修改
arr1[1] = 5
arr1[2] = 4
fmt.Println(arr1)
// 修改arr1并不会修改array1
fmt.Println(array1)
// len(数组名) 返回数组中元素个数 cap返回数组容量
fmt.Println(len(array1))
fmt.Println(cap(array1))
切片
//切片slice
/*
创建格式:
slice := array[0 : 5]
slice := array[ : ]
切片名 := 数组名[起始下标 : 结束下标] 起始下标和结束下标可以省略
slice := []int{1,2,3,4,5}
切片名 := []元素类型{初始元素}
slice := make([]T, len,cap)
切片名 := make([]元素类型,元素个数,预留空间) 预留空间可省略 默认与len相同
特点:
其本身并不是数组,它指向底层的数组
作为变长数组的替代方案,可以关联原数组的部分或全部
属于引用类型
可以直接创建或从底层数组获取生成
可使用len()和cap()
如果多个slice指向同一个数组 某一个slice对值得改变也会引起其它slice的值改变
*/
//slice1 指向array1数组的第2 - 4位 1 2 0
//疑问:为什么指向第2 - 4位长度为3 容量为4
slice1 := array1[1:4]
//slice2 指向array1数组的所有元素 0 1 2 0 0
slice2 := array1[:]
//输出 1 2 0
fmt.Print("slice1:")
fmt.Println(slice1)
//slice1和slice2都指向array1 slice1的改变也会导致原数组array1以及指向该数组的slice2的改变
slice1[0] = 5
//append 在slice尾部追加元素
//若追加后的最终长度没有超过原slice的容量 则返回原始slice
//若追加后的最终长度超过了原slice的容量,则将重新分配数组并拷贝原始数据
fmt.Println(len(slice1)) //3
fmt.Println(cap(slice1)) //4
fmt.Println(cap(slice2)) //5
//此处slice1容量为4 长度为3 追加一个元素后没有超过容量 返回slice1
slice4 := append(slice1, 3)
//并且slice1的追加也造成了slice2以及原数组的数据改变
fmt.Print("slice1:")
fmt.Println(slice1)
//输出 0 5 2 0 3
fmt.Print("slice2:")
fmt.Println(slice2)
fmt.Print("slice4:") // 5 2 0 3
fmt.Println(slice4)
fmt.Print("array1:") // 0 5 2 0 3
fmt.Println(array1)
fmt.Println("***********append追加超过原数组容量************")
//此时slice1追加元素3 4 变为 5 2 0 3 4超过了它的容量4
//slice5将会重新分配数组并拷贝数据
//这次append操作并不会引发其它相关slice及原数组的改变
slice5 := append(slice1, 3, 4)
//输出 0 5 2 0 3
fmt.Print("slice2:")
fmt.Println(slice2)
fmt.Print("slice5:") // 5 2 0 3 4
fmt.Println(slice5)
fmt.Print("array1:") // 0 5 2 0 3
fmt.Println(array1)
fmt.Println("************make************")
//使用make可创建slice 初始值为元素类型默认值
//slice := make([]T, len,cap)
//切片名 := make([]元素类型,元素个数,预留空间) 预留空间可省略 默认与len相同
slice3 := make([]int, 5, 10)
fmt.Println(len(slice3)) //5
fmt.Println(cap(slice3)) //10
fmt.Println(&slice3[0])
//使用 ... 可将一个slice中的所有元素追加到另一个slice中 append(切片1,切片2...)
slice3 = append(slice3, slice1...)
fmt.Print("slice3:") // 0 0 0 0 0
fmt.Println(slice3)
// 对slice3进行append操作后 并没有超过slice3的容量 所以地址并没有改变
fmt.Println(&slice3[0])
fmt.Println("*********append超出容量************")
slice9 := make([]int, 5, 5)
fmt.Println(&slice9[0])
slice9 = append(slice9, slice1...)
//由于进行append操作后 元素数量已超出slice9的容量 所以重新分配了数组 地址改变
fmt.Println(&slice9[0])
//拷贝 切片2 := 切片1[起始位置 : 结束位置] 起始位置和结束位置可省略 [:] 表示全部拷贝
slice8 := slice3[2:7]
fmt.Print("slice8:") // 0 0 0 0 0 5 2 0
fmt.Println(slice8)
map
//map key-value
/*
存储数据为key-value形式
key的类型必须支持==或!=的比较运算 不可以是函数 map 或slice
map查找比线性搜索快很多
当数据超出容量时会自动扩容(尽量提供一个合理的初始值)
使用len()可获取元素个数
键值对不存在时自动添加
每个map都必须进行单独的初始化操作
*/
fmt.Println("*************map****************")
//声明方式: var 名称 map[键类型] 值类型
var map1 map[string]int
//map声明后需要初始化创建 使用make进行创建
//var 名称 = make([键类型] 值类型,容量) 容量可省略
map1 = make(map[string]int)
map1["hu"] = 22
map1["li"] = 23
fmt.Println("size:", len(map1))
//通过key访问value 变量 = map名称[键]
//若该key不存在 则返回默认值
value1 := map1["hu"]
fmt.Println("hu:", value1)
//map查找
//判断某个key是否存在 value,OK := map[key] value用于接收获取到的值,OK表示该值是否存在
//若key存在 将该key对应的value赋值给value OK赋值为true
//若key不存在 value为默认值0 OK为false
value2, exist1 := map1["li"]
value3, exist2 := map1["wang"]
fmt.Println("li:", value2, " exist1:", exist1)
fmt.Println("wang:", value3, " exist2:", exist2)
//map的删除
delete(map1, "li")
fmt.Println("size:", len(map1))
//遍历map
for key, value := range map1 {
fmt.Println("key:", key, " value:", value)
}
函数
/*
函数 function
不支持嵌套,重载和默认参数
特性:无需声明原型、不定长度变参、多返回值、命名返回值参数、匿名函数、闭包
定义函数使用关键字 func 且左大括号不能另起一行
函数也可以作为一种类型使用
func 函数名(参数列表) 返回值{
return
}
*/
//不定长参数用 ... 表示 必须放在所有参数之后
func function1(a string, b ...int) {
fmt.Println(a, ":", b)
}
//闭包 在一个函数中声明一个匿名函数并返回该函数
func function2() func() int {
i := 1
return func() int {
i++
return i
}
}
func function3() func() func() int {
i := 1
return func() func() int {
return func() int {
i++
return i
}
}
}
func function4() {
//函数在执行到defer函数时 会将遇到的defer函数放入一个栈中
//在函数执行完毕或抛出异常时 会从该栈中取出defer函数并执行(先进后执行)
defer func() {
fmt.Println("第一个defer函数")
}()
defer func() {
fmt.Println("第二个defer函数")
//若异常在抛出后被捕获过 则后续无法再次捕获到该异常
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() {
fmt.Println("第三个defer函数")
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() {
fmt.Println("第四个defer函数")
//可使用recover函数捕获函数执行中所抛出的异常
if err := recover(); err != nil {
fmt.Println(err)
}
panic("defer函数中抛出异常")
}()
fmt.Println("执行function4")
panic("抛出异常")
//unreachable code 抛出异常后 程序不再往下执行 开始去执行defer函数
//defer func(){}()
}
func main(){
function1("function1", 1, 2, 3, 4, 5)
//返回的是一个函数 使用时需加 ()
i := function2()
//该函数是将其内部的i值进行累加并返回
fmt.Println("i:", i(), i(), i()) //2 3 4
//重新定义了j函数其内部的i值为初始值1
j := function2()
fmt.Println("j:", j(), j(), j()) //2 3 4
//j函数对其内部i值得修改 并不会影响i函数中的i
fmt.Println("i:", i(), i(), i()) //5 6 7
result1 := function3()
result2 := result1()
fmt.Println(result2(), result2(), result2())
result3 := result1()
fmt.Println(result3(), result3(), result3())
function4()
}
自定义类型
func main(){
//自定义类型 type 名称 类型
//自定义类型是单独的一种类型
var t1 data = 1
var t2 data = 2
//虽然其基础类型是int 但却不能够与int型变量直接进行赋值及运算
//var i int = t1 + t2
t3 := t1 + t2
fmt.Println(t1, "+", t2, "=", t3)
t3.show()
d := demo{
name: "hu",
age: 22,
}
fmt.Println(d.name, d.age)
var result4 int = d.talk()
fmt.Println(result4)
t4 := data2{1, "data2"}
fmt.Println(t4.data, t4.name)
t4.show()
d1 := demo2{demo{"li", 23}, "男"}
fmt.Println(d1.name, d1.age, d1.sex)
d1.talk()
}
/自定义结构体类型
type demo struct {
name string
age int
}
//继承:Go中继承方式采用的是匿名组合的方式
//在demo2中包含了demo匿名字段 那么demo2就拥有了demo的属性和方法
type demo2 struct {
demo
sex string
}
type data int
type data2 struct {
data
name string
}
//方法 method 格式: func (变量名 自定义类型) 方法名(参数) 返回值 {}
//方法是作用在自定义类型的值上的一类特殊函数
//通常自定义类型的值会被传递给该函数。
//理解为给某个自定义类型添加方法
func (data) show() {
fmt.Println("data的show方法")
}
func (d demo) talk() int {
fmt.Println("你好!我是", d.name)
return d.age
}
接口
/*
接口 type 接口名称 interface{
method1(参数列表) 返回值列表
...
}
interface {} 可以承接任何类型的变量
Interface类型可以定义一组(多个)方法(不是变量),本质上是一种类型
interface是为实现多态功能
多态是指代码可以根据类型的具体实现采取不同行为的能力
如果一个类型实现了某个接口(实现该接口的所有方法 称为实现接口)
所有使用这个接口的地方 都可以支持这种类型的值
*/
type shower interface {
show()
}
func main(){
//shower中只有一个show方法 data中刚好有一个show方法
//则data就算是实现了shower接口
//可使用shower来承接data变量
var interface1 shower = t3
interface1.show()
}
字符串拼接
//字符串拼接
string1 := "hello"
string2 := "world"
string3 := "hu"
//使用strings包拼接
string4 := strings.Join([]string{string1, string2, string3}, ",")
//使用 + 号拼接
string5 := string1 + string2 + string3
//使用fmt包拼接
string6 := fmt.Sprintf("%s,%s,%s,",string1,string2,string3)
fmt.Println(string4)
fmt.Println(string5)
fmt.Println(string6)
类型转换
//基本数据类型转换 类型名(变量)
t5 := int(t3)
fmt.Println(t5)
//指针类型转换需要用到unsafe包 (*数据类型)(unsafe.Pointer(变量))
var i1 int = 10
var i2 *int = &i1
var i3 *int64 = (*int64)(unsafe.Pointer(i2))
//var i4 *int64 = (*int64)(i2) 指针类型不能像基本数据类型那样转换
fmt.Println("i3:", *i3)
断言
//断言
//interface{} 类型可以承接任何类型的变量
var i5 interface{} = "hu"
//可以使用断言来判断该变量的具体类型
switch i5.(type) {
case int:
fmt.Println("int")
case string:
fmt.Println("string")
}