布尔型的值只可以是常量 true 或者 false。
var ok bool = true
布尔型数据和整型数据不能进行相互转换;
声明的布尔型变量如不指定初始化值,默认是false。
var a bool //a is false
整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
整型:
Go语言内置了12种整数类型,如byte、int、int8、int16、int32(-2147483648 到 2147483647)、int64、uint、uint8、uint16、uint32、uint64、uintptr,这里特别注意不同类型的整型必须进行强制类型转换,其中byte相当于uint8。
var a int = 10
var b int64 = 20
b = a //错误
整型支持算术运算和位操作,算术表达式和位操作表达式的结果依旧是整型,如:
var a int = (2+3)*4
var b int = 1000>>3
浮点型:
Go语言内置了两种浮点数类型,为float32、float64
var b := 10.00 //浮点数字面量会被自动推断为float64类型
注意:计算机很难进行浮点数的精确表示和存储,不应该使用==或者!=进行比较操作,应该使用math标准库进行高精度科学计算。
复数类型:
Go语言内置的复数类型有两种,为complex64和complex128,其中前者是由两个float32构成的,后者是由两个float64构成的,复数在计算机里面使用两个浮点数表示,一个表示实部,一个表示虚部。
var valuel complex64 = 3.1 + 5i
//三个内置函数处理函数
var v = complex(2.2,4) //构造一个复数
a := real(v) //返回复数实部
b := image(v) //返回复数虚部
字符串就是一串固定长度的字符连接起来的字符序列,是一个不可改变的字节序列,创建后不能再次修改。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。
Go语言中,字符串(string类型)是 UTF-8 字符的序列(当字符为 ASCII 码表上的字符时则占用 1 个字节,其它字符根据需要占用 2-4 个字节,汉字是三个字节)。
字符串数据结构
字符串在 Go 语言中的接口其实非常简单,每一个字符串在运行时都会使用如下的 reflect.StringHeader 表示,其中包含指向字节数组的指针和数组的大小:
type StringHeader struct {
Data uintptr
Len int
}
与切片的结构体相比,字符串只少了一个表示容量的 Cap 字段,而正是因为切片在 Go 语言的运行时表示与字符串高度相似,所以我们经常会说字符串是一个只读的切片类型。
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
因为字符串作为只读的类型,我们并不会直接向字符串直接追加元素改变其本身的内存空间,所有在字符串上的写入操作都是通过拷贝实现的。
注意:
字符串尾部不包含NULL字符,这一点与C/C++不一样;
基于字符串创建的切片和原字符串指向相同的底层字符数组,一样不能修改,对字符串的切片操作返回的子串仍然是string,而非slice。
字符串和切片的转换:字符串可以转换为字节数组,也可以转换为Unicode的字节数组。如下:
a := "你好,世界!"
b := []byte(a)
c := []rune(a)
字符串修改的方法:
/*
修改字符串
注意:字符串是无法被修改的,只能复制原字符串,在复制的版本上修改,修改完成之后可以使用 string() 函数将其转为字符串即可
方法1:转换为[]byte()
方法2:转换为[]rune()
方法3:新字符串代替原字符串的子字符串,用strings包中的strings.Replace()
*/
func main() {
//方法1
s1 := "abcdefgabc"
s2 := []byte(s1)
s2[1] = 'B'
fmt.Println(string(s2)) //aBcdefgabc
//方法2
s3 := []rune(s1)
s3[1] = 'B'
fmt.Println(string(s3)) //aBcdefgabc
//方法3
new := "ABC"
old := "abc"
s4 := strings.Replace(s1, old, new, 2)
fmt.Println(s4) //ABCdefgABC
}
字符串遍历:
func main(){
strChinese :="你好"
for i:=0;i<len(strChinese) ;i++ {
fmt.Printf("%d:[%c]\n",i,strChinese[i])
}//fmt.Println(utf8.RuneCountInString(strChinese)) 输出长度
//会遍历每一个字节,一个汉字为3个字节,相当于遍历字节数组
for i,c:=range strChinese{
fmt.Printf("%d:[%c]\n",i,c)
}
//相当于遍历rune数组
}
将字符串转为一个整数:
package main
import "fmt"
func main() {
// 在声明字符串s的时候使用":="快速初始化s
s := "0123456789"
// 使用索引遍历字符串
for i := 0; i < len(s); i++ {
// 使用Printf格式化输出
fmt.Printf("%v ", s[i])
}
fmt.Println()
s1 := "392932"
n := 0
// 将s1转位int类型整数
for i := 0; i < len(s1); i++ {
n = n*10 + int(s1[i]&15)
}
fmt.Printf("%v", n)
}
字符串常用操作:
主要是分隔字符串,判断是否存在某个子串,是否存在某个前缀或者某个后缀,找到某个子串第一次出现的位置,拼接字符串等操作,可以使用 Sprintf 来拼接字符串等
package main
import (
"fmt"
"strings"
)
func main() {
// 1. len(str)获取字符串的长度
s := "Hello World!"
fmt.Println(len(s))
// 2. strings.Split()按照分隔符切分字符串, 函数的返回值是按照分隔符切分之后的字符串切片
s1 := "I am a student"
s2 := strings.Split(s1, " ")
for i := 0; i < len(s2); i++ {
fmt.Print(s2[i], " ")
}
fmt.Println()
// 3. Contains()判断字符串中是否包含子串substr
fmt.Println(strings.Contains(s1, "am"))
// 4. HasPrefix(),HasSuffix()判断是否包含前缀与后缀
fmt.Println(strings.HasPrefix("abbbcas", "abb"))
fmt.Println(strings.HasSuffix("abbbcas", "cas"))
// 5. strings.Index()返回子串第一次出现的位置, 不存在返回-1
// strings.LastIndex()返回子串最后一次出现的位置, 不存在返回-1
s3 := "abbabbabbab"
fmt.Println(strings.Index(s3, "abb"))
fmt.Println(strings.LastIndex(s3, "abb"))
// 6. join()对string切片的元素添加分隔符的操作, 返回的是添加分隔符之后的字符串
s4 := []string{"I", "am", "a", "student"}
fmt.Println(s4)
fmt.Println(strings.Join(s4, " "))
}
[]byte以及[]rune类型:
byte 是uint8类型的别名,uint8 类型,代表了 ASCII 码的一个字符。Go语言字符串的底层是byte[]字节数组,在默认的utf-8编码中,英文字符占8位,一个字节,而中文是3个字节。
Go语言支持 Unicode(UTF-8),因此字符同样称为 Unicode 代码点或者 runes,并在内存中使用 int32 来表示。
rune在Go内部是int32类型的别名,占用4个字节;
在文档中,一般使用格式 U+hhhh 来表示,其中 h 表示一个 16 进制数。
在书写 Unicode 字符时,需要在 16 进制数之前加上前缀\u或者\U。
如果需要使用到 4 字节,则使用\u前缀,如果需要使用到 8 个字节,则使用\U前缀。
Unicode函数:
判断是否为字母:unicode.IsLetter(ch)
判断是否为数字:unicode.IsDigit(ch)
判断是否为空白符号:unicode.IsSpace(ch)
注意:
package main
import (
"fmt"
)
func main() {
testStr := "this is a test!"
testStr1 := "这是一个测试"
fmt.Println("The length of str is: ",len(testStr))
fmt.Println("字符串长度为: ",len(testStr1))
}
The length of str is: 15
字符串长度为: 18
数据类型,点击查阅。
包括: 指针类型(Pointer)、数组类型、结构化类型(struct)、Channel 类型
函数类型 、切片类型 、接口类型(interface)、Map 类型
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。实质上,切片就是变长数组。
结构:
type SliceHeader struct {
Data uintptr //第一个是字符串指向的底层字节数组,
Len int //第二个是字符串的字节的长度
Cap int //切片指向的内存空间的最大容量(对应元素的个数不是字节数)
}
声明方式:
var name [] T
T : 表切片元素类型,可以是整型、浮点型、布尔型、切片、map 、函数等。
切片的元素使用“ [] ”进行访问,在方括号中提供切片的索引即可访问元素,索引的范围从 0开始,且不超过切片的最大容量。
代码如下:
a := make([]int , 3,3) //创建一个长度为3,容量为3的整型切片。
a[0] = 1 //为切片元素赋值。
其他方式:
var (
a []int // nil切片, 和 nil 相等, 一般用来表示一个不存在的切片
b = []int{} // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
c = []int{1, 2, 3} // 有3个元素的切片, len和cap都为3
d = c[:2] // 有2个元素的切片, len为2, cap为3
e = c[0:2:cap(c)] // 有2个元素的切片, len为2, cap为3
f = c[:0] // 有0个元素的切片, len为0, cap为3
g = make([]int, 3) // 有3个元素的切片, len和cap都为3
h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)
函数操作:
内置函数len()返回切片长度
内置函数cap()返回切片底层数组容量
内置函数append()对切片追加元素
内置函数copy()用于复制一个切片
package main
import (
"fmt"
)
func main() {
//创建切片
var number []int
printSlice(number)
//允许追加空切片
number = append(number,0)
printSlice(number)
//向切片添加一个元素
number = append(number,1)
printSlice(number)
//同时添加多个元素
number = append(number,2,3,4)
printSlice(number)
//创建的新的切片是之前的切片容量的两倍
number1 := make([]int,len(number),(cap(number))*2)
//拷贝number的内容到number1中
copy(number1,number)
printSlice(number1)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}
一个指针变量指向了一个值的内存地址。
类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:
var var_name *var-type
var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
当一个指针被定义后没有分配到任何变量时,它的值为 nil;nil 指针也称为空指针;nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
if(ptr == nil) /* ptr 是空指针 */
注意:
Go语言同样支持多级指针 **var-type;
结构体指针访问结构体字段仍然用“ . ”点操作符,Go语言没有“->”操作符
Go语言支持垃圾回收,所以禁止指针运算,因为会给垃圾回收带来很多不便。
函数中允许返回局部变量的地址,因为Go编译器使用“栈逃逸”机制将这种局部变量的空间分配在堆上,如下:
func sum(a , b int) *int{
sum := a + b
return &sum //允许,sum会被分配在heap上
}
其他:
Go 指针数组 你可以定义一个指针数组来存储地址
Go 指向指针的指针 Go 支持指向指针的指针
Go 向函数传递指针参数 通过引用或地址传参,在函数调用时可以改变其值
https://www.runoob.com/go/go-pointers.html
数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。
数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。
声明方式:
var arr [2]int //声明数组,元素默认值都为0
arr := [3]int{1,2,3} //指定长度和初始化字面量
arr := [...]int{1,2,3} //不指定长度,由后面的初始化列表数量来确定长度
arr := [3]int{1:1,2:3} //指定总长度,并通过索引值进行初始化,没有初始化元素时使用默认值
arr := [...]int{1:1,2:3} //不指定长度,通过索引值初始化,由最后的索引值确定长度
注意:
struct结构中的类型可以是任意类型;struct的存储空间是连续的,其中字段按照声明时的顺序存放(注意字段之间有对齐要求)
struct类型字面量:
struct {
FileName FieldType
FileName FieldType
FileName FieldType
}
自定义struct类型:
type TypeName struct{
FileName FieldType
FileName FieldType
FileName FieldType
}
struct初始化:
type Person struct{
Name string
Age int
}
type Student struct{
*Person
Number int
}
p := &Person{
Name : "xxx",
Age : 12,
}
s := Student{
Person : p,
Number : 110,
}
struct{}{} //匿名struct初始化
点击查看struct文章!
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
map的类型格式是:map[K]T,其中K可以是任意可以进行比较的类型,T是值类型
创建方式:
使用字面量创建,如下:
ma := map[string]int{"a":1,"b":2}
使用内置的make函数创建,如下:
make(map[K]T) //map的容量使用默认值
make(map[K]T,len) //map的容量使用给定的len值
ma := make(map[string]int,10)
操作方式:
访问格式:mapName[key]
删除格式:使用内置函数delete(mapName,key)
用range可以遍历map,但不保证顺序
mp := make(map[int]string)
mp[1] = "xxx"
mp[2] = "yyy"
delete (mp,1)
for k,v := range mp{
...
}
注意:
接口类型:
接口是一个编程规约,也是一组方法签名的集合。
接口内部存放的具体类型变量被称为接口指向的“实例”。
空接口:
由于空接口的方法集为空,所以任何类型都被认为实现了空接口,任意类型的实例都可以赋值或者传递给空接口,包括非命名类型的实例。
注意: 非命名类型由于不能定义自己的方法,所以方法集为空,因此其类型变量除了传递给空接口,不能传递给任何其他接口。
接口声明:
特点:
接口字面量类型声明:
interface {
MethodSignature1
MethodSignature2
}
使用type关键字声明:
type InterfaceName interface{
MethodSignature1
MethodSignature2
}
可以内嵌匿名接口字段,就是一个接口定义里面包括其他接口,如下:
type Reader interface{
Read(p []byte)(n int,err error)
}
type ReadFinallyer interface{
Reader
}
接口初始化:
没有初始化的接口变量,其默认值是nil
实例赋值接口
如果具体类型实例的方法集是某个接口的方法集的超集,则称该具体类型实现了接口,可以将该具体类型的实例直接赋值给接口类型的变量。
接口变量赋值给接口变量
已经初始化的接口类型变量a直接赋值给另一种接口变量b,要求b的方法集是a的方法集的子集,此时接口变量b绑定的具体实例是接口变量a绑定的具体实例的副本。
type Printer interface{
Print()
}
type s struct{}
func (s S) Print(){
....
}
func main(){
var i Printer
i = S{} //i = new(S)
i.Print()
}
接口的动态类型和静态类型
动态类型:接口绑定的具体实例的类型称为接口的动态类型,接口可以绑定不同类型的实例,所以接口的动态类型是随着其绑定的不同类型实例而发生变化的
类型查询:
空接口
Go语言的空接口有点像C语言中的void *,只不过void * 是指针,而Go语言的空接口内部封装了指针而已。
空接口组成(两个字段):实例类型、指向绑定实例的指针
用途:Go语言没有泛型,如果一个函数需要接收任意类型的参数,则参数类型可以使用空接口类型,这是弥补没有泛型的一种手段。
空接口是反射的基础,后续知识再补充。
注意:当空接口的两个字段都为nil时,空接口才为nil