Go
1.声明变量
1.指定变量类型 (variable)
var i int
如果未指定默认值 那么默认值为0
2.自动判断值类型
var i="string";
3.省略var 初始化声明
hello:="spark"
多变量声明
1.指定声明变量的类型 统一类型
var i,k,s,w int
2.声明不同的类型
var (
i int
str string
b bool
)
!!!在同一个作用域当中 不能对变量进行二次初始化 :变量名=v
2.Go语言常量
const关键词 常量声明语法
1.单个常量声明方法 显示声明 const 变量名 变量类型=value
const str string ="100"
2.隐示声明 可省略string 因为会自动推断类型
const str ="100"
const str,str1 int
3.枚举声明
const (
MAN=1
GIRL=0
)
4.特殊常量iota 在const关键词出现时被重置为0 每次出现一次+1可以把iota当作const索引
const (
MAN = iota //0
MAIN //1
_ //2
afer //3
)
3.运算符
//算术运算符
+ - * / ++ --
//关系运算符 跟Java中的一致
== != > < >= <=
//逻辑运算符
&& || !
4.for循环
//berak关键词 停止当前循环
//goto 跳转到指定的标签
//continue 跳过当前循环
func main() {
//var i="string";
for i:=1; i<=10 ;i++ {
// goto breakHader
if(i==1){
fmt.Println(i)
}else if(i==10){
fmt.Println("执行")
goto breakHader
}else{
if(i==5){
fmt.Println(i)
break;
}
}
}
breakHader:
fmt.Print("Message")
}
5.Go函数
//声明函数
func function_name(param list)(return paramList)
{
//函数体
}
//demo
func GetMaxNum(a ,b int)(int){
//声明一个返回变量
var result int
if(aa){
result=a
}
return result
}
//main函数
//noinspection ALL
func main() {
//var i="string";
var num=GetMaxNum(1,10);
fmt.Print(num)
}
//匿名函数 把一个函数作为方法的返回值
func function_name func()匿名函数返回值//int {
}
func getSequenes() func()int{
var i =0
return func() int{
i++
return i;
}
}
//noinspection ALL
func main() {
sequene := getSequenes()
fmt.Println(sequene())
fmt.Println(sequene())
//var i="string";
//var num=GetMaxNum(1,10);
//fmt.Print(num)
}
6.数组声明
[length]Type
[N]Type{value1, value2, ..., valueN}
[...]Type{value1, value2, ..., valueN} 初始化数组
var 数组名称[] type
//不指定数组长度
var 数组名称=[...]int{1,2,3,4,501}
-
语言指针
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。指针接受的是一个变量的内存地址 如果直接赋值会报错
访问指针中的值 采用 *指针名称
一个指针变量指向了一个值的内存地址。//输出变量地址 var n [10]int /* n 是一个长度为 10 的数组 */ n[0]=100 fmt.Printf("变量的地址: %x\n", &n[0] ) var a=10 //声明指针变量 var i *int //存储 &a的地址 i=&a fmt.Printf("a 变量的地址是: %x\n", &a ) /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %x\n", i ) /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %d\n", *i )
8.结构体定义
Go语言中的基础数据类型可以表示一些事务的基本熟悉,但是当我们想表达一个事务的全部或部分属性时,这时候使用单一基本数据类型就无法满足了。Go语言提供了一种自定义数据类型,struct这种类型称之为结构体。我们可以采用struct来定义自己的类型
//语法
type 类型名 struct{
filedName type
filedName type
//1.类型名 标识自定义结构体的名称,在同一个包内不能重复
//2.字段名 表示结构体字段名。结构体中的字段名必须唯一
//3.字段类型 表示结构体字段的具体类型
}
//比如 创建一个Person自定义类型,该类型拥有name和age2个字段
type Person struct{
name string
age int
}
//结构体实例化 结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型
var 结构体实例 结构体类型
//1.基本实例化
var person Person
person.name="zhangsan"
person.age=18
//2.匿名结构体
var user struct{name string;age int}
user.name="李四"
user.age=20
//3. 创建指针类型结构体
//采用new关键字对结构体进行实例化,得到的是结构体的地址
var p2=new(Person)
fmt.Println(p2)//&{ 0} 可以看出p2是一个结构指针
//4.取结构体的地址实例化 使用&对结构体进行取地址操作,相当于对该结构体类型进行了一次new实例化操作
var p2=&Person{}
p2.name="张三"
//p2.name="张三"其实在底层是(*p3).name="张三" 这是Go帮我们实现的语法糖
//5.使用键值对初始化 使用键值对对结构体进行初始化时,键对应结构体的字段,值对应字段的初始值
var p4=Person{name: "张三",age: 20}
fmt.Println(p4)//{张三 20}
//使用指针初始化
var p5=&Person{name: "张三",age: 21}
fmt.Println(p5)//&{张三 21}
//6使用值的列表进行初始化
var p6= Person{"张三", 12}
//初始化结构体的时候可以简写,也就是初始化的时候不写键位,直接写值
//使用该初始化操作的时候
//1.必须初始化结构的所有字段
//2.初始值的填充顺序必须与字段在结构体内的声明顺序一致
//3. 该方式不能和键值初始化方式混用
8.1 构造函数
//Go语言的结构体没有构造函数。所以我们可以自己实现。如果结构体比较复杂的话,值拷贝性能会比较大,所以构造函数返回结构的指针类型 func NewPerson(name string, age int) *Person { return &Person{name, age} }
8.2 方法和接收者
Go语言中的方法是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Reveiver)。接收者的概念类似于其他语言中的this或者self
func(接收者变量 接收者类型)方法名(参数列表)(返回参数){ 1.接收者变量:接收者中的参数变量命名时,官方建议使用接收者类型名的第一个小写字母。 2.接收者类型: 接收者类型和参数类似,可以是指针类型和非指针类型 3.方法名、参数列表、返回参数:具体格式与函数定义相同 } /Person 结构体 type Person struct { name string age int8 } //NewPerson 构造函数 func NewPerson(name string, age int8) *Person { return &Person{ name: name, age: age, } } //Dream Person做梦的方法 func (p Person) Dream() { fmt.Printf("%s的梦想是学好Go语言!\n", p.name) } func main() { p1 := NewPerson("测试", 25) p1.Dream() } //方法与函数的区别是 函数不属于任何类型,方法属于特定的类型 //方法的接收者类型 可以是指针 也可以不是指针 唯一的区别如下 //Dream Person做梦的方法 func (p Person) Dream() { p.name = "张三" } func (p *Person) Dreams() { p.name = "李四" } func main() { var p1 = new(Person) p1.name = "zs" p1.Dream() fmt.Println(p1) p1.Dreams() fmt.Println(p1) //输出结果 变量接收者是结构体变量的 无法改变接收者的值 // &{zs 0} //指针接收者可以修改接收者内部的值 //&{李四 0} }
什么时候使用指针类型?
- 需要修改接收者中的值
- 接收者是拷贝代价比较大的对象
- 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者
8.3 结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就成为匿名字段
type Person struct{ string int } func main{ p:=Person{"zk",12} //匿名字段默认采用类型名作为字段名,结构体要求字段必须唯一,因此一个结构体中同种类型的匿名字段只能有一个 }
8.4 嵌套结构体
一个结构体种可以嵌套包含另一个结构体或结构体指针
type Address struct{ Province string City string } type User struct{ Name string Address Address } func main(){ user1 := User{ Name: "张三", Address: Address{ Province: "浙江", City: "杭州", }, } fmt.Println(user1)//{张三 20 {浙江 杭州}} }
8.5 嵌套匿名结构体
type Address struct{ Province string City string createTime string } type User struct{ Name string Gender string Address //匿名结构体 Email } type Email struct{ Account string createTime string } func main() { user1 := User{ Name: "张三", Gender: "男", Address:Address{ Province: "浙江", City: "杭州", }, } fmt.Println(user1.City)//直接访问匿名结构体的字段名 fmt.Println(user1.Province) //对于匿名结构体内部可能存在相同的字段名,这个时候 应该为了避免歧义需要指定具体的内嵌结构体的字段 }
8.6 结构体的"继承"
Go语言中使用结构体也就可以实现其他变成语言中面向对象的继承
type Animal struct { name string } func (a *Animal) move() { fmt.Println(a.name + "移动") } type Dog struct { Feet int *Animal } func (d *Dog) wang() { fmt.Println(d.name + "WWWW") } func main() { //继承 dog := Dog{Feet: 12, Animal: &Animal{name: "旺财"}} dog.move() dog.wang() }
8.7 结构体字体地可见性
结构体中字段大写开头表示可以公开访问,小写表示私有(仅在定义当前结构体的包中可访问)
8.9 结构体与JSON序列化
JSON是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号分隔,然后紧接着值;多个键值之间使用英文,分隔
type Student struct { No int //学号 Name string //学生名称 } type Class struct { ClassName string //班级名称你 Students []*Student } //如果有嵌套结构体在内 我们打印外层的结构体可以正常输出 可视化值,但是嵌套中的值输出的都是一串地址 所以我们可以进行重写对应的结构体的String方法 自定义输出效果 func (c *Student) String() string { return "{Name:" + c.Name + " " + "No:" + strconv.Itoa(c.No) + "}" } func main() { class := &Class{ ClassName: "三年级二班", Students: make([]*Student, 0, 20)} for i := 0; i < 100; i++ { stu := &Student{ Name: fmt.Sprintf("stu%02d", i), No: i, } //追加 class.Students = append(class.Students, stu) } //序列化json data, _ := json.Marshal(class) fmt.Println(data) fmt.Printf("json:%s\n", data) //json.Marshal默认返回的是一个byte结构 //[123 34 67 108 97 115 115 78 97 109 101 34 58 34 228 184 137 229 185 180....] //json:{"ClassName":"三年级二班","Students":[{"No":0,"Name":"stu00"},{"No":1,"Name":"stu01"},{"No":2,"Name":"stu02"},{"No":3,"Name":"stu03"},{"No":4,"Name":"stu04"},{"No":5,"Name":"stu05"}]} //json字符串转换称json str := "{\"ClassName\":\"三年级二班\",\"Students\":[{\"No\":0,\"Name\":\"stu00\"},{\"No\":1,\"Name\":\"stu01\"},{\"No\":2,\"Name\":\"stu02\"},{\"No\":3,\"Name\":\"stu03\"},{\"No\":4,\"Name\":\"stu04\"},{\"No\":5,\"Name\":\"stu05\"},{\"No\":6,\"Name\":\"stu06\"}]}" //初始化Class c := &Class{} err := json.Unmarshal([]byte(str), c) if err == nil { fmt.Println(c) } }
8.10 结构体标签(Tag)
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来
Tag在机构提字段的后方定义,由一对反引号包裹起来,具体的格式如下:
`key:"value1" key2:"value2"`
结构体标签由一个或多个键值对组成。键与值使用冒号分割,值用双引号括起来。键值对之间使用一个空格分隔。
type Student struct { Name string `json:"name"` Id int //json序列化默认使用字段名作为key sex string //私有不能被json包访问 } func main() { stu := &Student{Name: "张三", Id: 1, sex: "男"} json, _ := json.Marshal(stu) fmt.Println(string(json))//{"name":"张三","Id":1} }
8.11 map有序输出
func main() { //创建一个int键值map数组 maps := make(map[int]string, 10) maps[1] = "A" maps[2] = "B" maps[3] = "C" maps[4] = "D" //创建一个存放maps的key值的数组 var sli []int //循环把所有的key放进map当中 for i, _ := range maps { sli = append(sli, i) } //排序 sort.Ints(sli) //循环输出 for i := 0; i < len(maps); i++ { fmt.Println(maps[sli[i]]) } }
9.切片
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
切片是一种引用类型
新获得的切片跟源切片底层对应的数组都是同一个,所以对其中一个切片元素的修改也会影响到另外一个
声明切片语法
make([]type, length, capacity)
make([]type, length)
[]type{}
[]type{value1, value2,..., valueN}
s[n] 切片s中索引位置为n的项
s[n:m] 从切片s的索引位置n到m-1处所获得的切片
s[n:] 从切片s的索引位置到len(s)-1处所获得的切片
s[:m] 从切片s的索引位置0到m-1处所获得的切片
s[:] 从切片s的索引位置0到len(s)-1处获得的切片
var identifier []type
切片初始化
s:=[]int{1,2,3}
/* 创建切片 长度为4 最长长度可以达到4*/
slice := make([]int, 2, 4)
append_slice := []int{1, 2}
fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
//追加2个元素 地址未改变
slice = append(slice, append_slice...)
fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
//继续追加 地址改变
slice = append(slice, 10, 20)
fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
slice = append(slice, 30, 40, 50)
fmt.Printf("slice addr:%p len:%d cap:%d slice:%v\n", slice, len(slice), cap(slice), slice)
slice addr:0xc000052140 len:2 cap:4 slice:[0 0]
slice addr:0xc000052140 len:4 cap:4 slice:[0 0 1 2]
slice addr:0xc00006c140 len:6 cap:8 slice:[0 0 1 2 10 20]
slice addr:0xc00008a000 len:9 cap:16 slice:[0 0 1 2 10 20 30 40 50]
10.创建Map集合
//声明map的方式
var hashMap=make(map[keyType]valueType);
var hashMap=make(map[string]string)
hashMap["Lucy"]="张三"
hashMap["Ad"]="Nike"
for k, v := range hashMap {
fmt.Println(k+"\t"+v)
}
//删除函数
delete(hashMap,"Lucy")
for k, v := range hashMap {
fmt.Println(k+"\t"+v)
}
11.类型转换
type_name(exp)
var name=12
var str=string(12)
//string转int
nb,_=strconv.Atio(str)
//string转 int 64
in64, err := strconv.ParseInt(string, 10, 64)
//int转换成string
string := strconv.Itoa(int)
//int64转换成 string
string := strconv.FormatInt(int64,10)
12.接口定义 实现
demo
//定义一个say的接口
type Say interface {
sayHelloWorld();
}
type sayHello struct {
}
type saySpeak struct {
}
//实现类
func (say sayHello) sayHelloWorld() {
fmt.Println("helloworld")
}
//实现类
func ( saySpeak saySpeak)sayHelloWorld(){
fmt.Println("Nice")
}
func main() {
var say Say
say=new(sayHello)
say.sayHelloWorld();
say=new(saySpeak);
say.sayHelloWorld();
}
13 异常情况
package main
import (
"fmt"
)
// 定义一个 DivideError 结构
type DivideError struct {
dividee int
divider int
}
// 实现 `error` 接口
func (de *DivideError) Error() string {
strFormat := `
Cannot proceed, the divider is zero.
dividee: %d
divider: 0
`
return fmt.Sprintf(strFormat, de.dividee)
}
// 定义 `int` 类型除法运算的函数
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
if varDivider == 0 {
dData := DivideError{
dividee: varDividee,
divider: varDivider,
}
errorMsg = dData.Error()
return
} else {
return varDividee / varDivider, ""
}
}
func main() {
// 正常情况
if result, errorMsg := Divide(100, 10); errorMsg == "" {
fmt.Println("100/10 = ", result)
}
// 当被除数为零的时候会返回错误信息
if _, errorMsg := Divide(100, 0); errorMsg != "" {
fmt.Println("errorMsg is: ", errorMsg)
}
}