Go学习
环境:
操作系统:mac
安装
- 去官网地址下载mac版本的安装包,双击按照提示安装就行了
- 打开terminal,输入
go version
,验证是否安装成功 - 输入
cd /usr/local/go
查看go的安装目录
语法
变量声明方式
package main
import(
"fmt"
)
//声明全局变量,方法一、方法二、方法三是可以的
var gA int = 100
var gB = 200
//方法四只能在方法体内声明
// gC := 300 这里会报错
func main() {
//方法一:声明一个变量,默认值0
var a int
fmt.Println("a = ", a)
fmt.Printf("type of a = %T\n", a)
//方法二:声明一个变量,初始化一个值
var b int =100
fmt.Println("b = ", b)
fmt.Printf("type of b = %T\n", b)
var bb string = "bbb"
fmt.Printf("bb = %s, type of bb = %T\n", bb, bb)
//方法三:在初始化时候,可以省去数据类型,通过值匹配当前变量的数据类型
var c =100.0
fmt.Println("c = ", c)
fmt.Printf("type of c = %T\n", c)
var cc = "ccc"
fmt.Printf("cc = %s, type of cc = %T\n", cc, cc)
//方法四:常用方法,省去var关键字,直接自动匹配
d := 3.14
fmt.Println("d = ", d)
fmt.Printf("type of d = %T\n", d)
var dd = "ddd"
fmt.Printf("dd = %s, type of dd = %T\n", dd, dd)
fmt.Println("--全局变量--")
fmt.Println("gA = ", gA, "gB = ",gB)
fmt.Println("--多变量声明--")
//多变量声明
var xx, yy int =100, 200
fmt.Println("xx = ", xx, "yy = ",yy)
//多行变量声明
var (
zz int = 300
uu bool = false
)
fmt.Println("zz = ", zz, "uu = ",uu)
}
const和iota声明枚举
可以在const()中添加一个关键字iota,每行的iota都会累加1,第一行的iota默认为0
package main
import(
"fmt"
)
//const定义枚举类型
const (
//可以在const()中添加一个关键字iota,每行的iota都会累加1,第一行的iota默认为0
BEIJING = iota*10 //iota =0 ,BEIJING = 0*10
SHANGHAI //iota =1, SHANGHAI = 1*10
SHENZHEN //iota=2, SHENZHEN = 2*10
)
const (
a, b = iota+1, iota+2 //iota=0, a=0+1=1, b=0+2=2
c, d //iota=1, c=1+1=2, d=1+2=3
e, f //iota=2, e=2+1=3, f=2+2=4
g, h = iota*2, iota*3 //iota=3, g=3*2=6, h=3*3=9
i, k //iota=4, i=4*2=8, k=4*3=12
)
func main(){
//const定义常量,只读
const lengths int = 10
// lengths = 100 //常量不允许修改
fmt.Println("length = ", lengths)
fmt.Println("BEIJING = ", BEIJING)
fmt.Println("SHANGHAI = ", SHANGHAI)
fmt.Println("SHENZHEN = ", SHENZHEN)
fmt.Println("----------------")
fmt.Println("a=",a," b=",b)
fmt.Println("c=",c," d=",d)
fmt.Println("e=",e," f=",f)
fmt.Println("g=",g," h=",h)
fmt.Println("i=",i," k=",k)
//iota只能和const()一起使用,iota只有在const进行累加效果
// var a int = iota 报错
}
函数
多返回值
go中函数能够返回多个返回值
package main
import(
"fmt"
)
func main(){
c := foo("1", 2)
fmt.Println("c = ", c)
m, n := foo2("123", 11)
fmt.Println("m =",m," n=",n)
r1, r2 := foo3("1",1)
fmt.Println("r1 = ", r1, " r2=", r2)
r3, r4 := foo4("1",1)
fmt.Println("r3 = ", r3, " r4=", r4)
}
//定义一个函数入参是string类型的a,int类型的b
//返回值是int类型
func foo(a string, b int) int {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
c := 100
return c
}
//定义一个函数入参是string类型的a,int类型的b
//返多个返回值,匿名的
func foo2(a string, b int) (int, int) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
return 123,456
}
//定义一个函数入参是string类型的a,int类型的b
//返多个返回值,有形参名
func foo3(a string, b int) (r1 int, r2 string) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
//给有名称的返回值变量赋值,作用域在整个函数{}包裹的区域
r1 = 100
r2 = "200"
return r1,r2
}
//定义一个函数入参是string类型的a,int类型的b
//返多个相同类型返回值,有形参名
func foo4(a string, b int) (r3 , r4 int) {
fmt.Println("a = ", a)
fmt.Println("b = ", b)
//给有名称的返回值变量赋值,作用域在整个函数{}包裹的区域
r3 = 1000
r4 = 2000
return r3,r4
}
init函数和import导包
如图:
匿名导包和别名导包
import (
// f "fmt" //别名导包
// . "fmt" //将fmt包中的全部方法导入到当前包的作用域中,不需要在fmt.Println这样调用,直接Println调用 ----不推荐
_ "fmt" //匿名导包,无法使用当前包的方法,但是会执行包的init方法
)
指针
定义:指针是一种数据类型,用于表示数据的内存地址
注意:
声明指针类型只需要在类型前加一个*,不管什么指针类型(*int,*string,*float),初始值都是nil
取变量name的首地址,也就是指针的值,需要用&name表示
package main
import (
"fmt"
)
func main(){
var a int = 1
var b int = 20
//值传递
swapValue(a, b)
fmt.Println("swap value: a = ", a, " ,b = ",b)
//指针传递, &a代表a值的地址, &b代表b值的地址
swapPointer(&a, &b)
fmt.Println("swap pointer: a = ", a, " ,b = ",b)
//一级指针
var x float64 = 1.1
var y * float64 = &x
//申明一个二级指针
var pp **float64
pp = &y
// &x = y
fmt.Println("&x = ", &x, "y = ", y)
//**pp和x值一样
fmt.Println("pp = ", **pp)
}
func swapValue(a int, b int){
var tmp int
tmp = a
b = a
a = tmp
}
//a *int a代表一个int类型的指针
//b *int b代表一个int类型的指针
func swapPointer(a *int, b *int){
var tmp int
//*a 代表取出a指针指向的值
tmp = *a
//*b 代表取出b执政指向的值
*a = *b
*b = tmp
}
defer
defer用于资源的释放,会在函数返回之前进行调用,如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。
package main
import ("fmt")
func main(){
defer_test()
}
//defer函数作用是在函数返回值前调用
//调用顺序类似栈,后入先出,defer 函数3->defer 函数2->defer 函数1
func defer_test(){
fmt.Println("defer_test")
defer fmt.Println("defer 函数1")
defer fmt.Println("defer 函数2")
defer fmt.Println("defer 函数3")
}
注意:
defer是在return之前被执行的,需要注意的是return XXX这个语句不是原子语句,所以会执行XXX在执行defer有点反直觉。详细代码参考如下:
package main import ("fmt") func main(){ returnAndDefer() } func defer_function() int{ fmt.Println("defer func") return 0; } func return_function() int{ fmt.Println("return func") return 0; } //这里打印的结果是: //return func //defer func func returnAndDefer() int{ defer defer_function() return return_function() }
数组和动态数组
静态数组
传递值默认是值传递,即用一个拷贝值传入,拷贝的变化不会引起原数组变化。如果想要引用传递,需要借助数组指针来实现。
package main
import "fmt"
func main() {
//固定长度的数组,初始值是类型的初始值,int是0,string是''
var intarr [10]int
fmt.Printf("intarr type is %T\n", intarr)
//初始化的固定数组
intarr2 := [10]int{1,2,3,4}
fmt.Printf("intarr2 type is %T\n", intarr2)
//fori方式遍历数组
//for i:=0; i<10; i++
for i:=0; i<len(intarr); i++ {
fmt.Println(intarr[i])
}
//range方式遍历数组
for index, value := range intarr2{
fmt.Println("index = ", index, "value = ",value)
}
fmt.Println("---普通数组---")
printArray2(intarr2)
for index, value := range intarr2{
fmt.Println("index = ", index, "value = ",value)
}
fmt.Println("---指针数组---")
printArray(&intarr2)
for index, value := range intarr2{
fmt.Println("index = ", index, "value = ",value)
}
}
//传递一个数组指针,可以同步改变数组内外的值
func printArray(myArr *[10]int){
for index,value := range myArr{
fmt.Println("index = ", index, "value = ",value)
}
myArr[0]=111
}
func printArray2(myArr [10]int){
for index,value := range myArr{
fmt.Println("index = ", index, "value = ",value)
}
myArr[0]=111
}
动态数组(slice 切片)
默认就是引用传递
package main
import "fmt"
func main() {
//[]中没有值就是一个动态数组,也就是slice-切片
myArr := []int{1,2,3,4}
fmt.Printf("myArr type is %T\n", myArr)
prinArray(myArr)
for _,value := range myArr{
fmt.Println("value = ", value)
}
}
func prinArray(a []int){
//引用传递
//_表示匿名的变量,可以无需使用也不会报错
for _, value := range a{
fmt.Println("value = ", value)
}
//这里值变化,原来传入的数组值变化,这就是引用传递的用处
a[0] = 100
}
切片
声明切片的4种方式
package main
import "fmt"
func main() {
//1.声明slice1是一个切片,并初始化,默认值是1,2,3, len是3
slice1 := []int{1, 2, 3}
//打印结果是:len = 3, slice = [1 2 3]
fmt.Printf("len = %d, slice = %v \n", len(slice1), slice1)
//2.声明sclice2是一个切片,但是没有给slice2分配空间
var sclice2 []int
//给slice2初始化空间
sclice2 = make([]int, 3)
sclice2[0] = 100
fmt.Printf("len = %d, slice = %v \n", len(sclice2), sclice2)
//3.声明slice3是一个切片,同时给其分配空间,3个空间,初始化值是0
var slice3 []int = make([]int, 3)
fmt.Printf("len = %d, slice = %v \n", len(slice3), slice3)
//4.通过:=推导出slice4是一个切片
slice4 := make([]int, 3)
fmt.Printf("len = %d, slice = %v \n", len(slice4), slice4)
//判断一个slice是否为0
var slice6 []int
if slice6 == nil {
fmt.Println("slice 是一个空切片")
} else {
fmt.Println("slice 不是空")
}
}
切片的追加
切片扩容机制:
向一个容量满的slice追加一个元素,会动态开辟一个原来cap容量的空间
package main
import "fmt"
func main() {
//定义一个整数类型切片,长度是3,容量是5
var numbers = make([]int, 3, 5)
//打印结果:len = 3, cap = 5, slice = [0 0 0]
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
//追加一个元素
numbers = append(numbers, 1)
//打印结果:len = 4, cap = 5, slice = [0 0 0 1]
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
//追加一个元素
numbers = append(numbers, 1)
//打印结果:len = 5, cap = 5, slice = [0 0 0 1 1]
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
//切片扩容机制:向一个容量满的slice追加一个元素,会动态开辟一个原来cap容量的空间
numbers = append(numbers, 1)
//打印结果:len = 6, cap = 10, slice = [0 0 0 1 1 1]
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
//不设置cap容量,cap默认和len相同
var numbers2 = make([]int, 3)
fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers2), cap(numbers2), numbers2)
}
切片截取
package main
import "fmt"
func main() {
//定义一个整数类型切片,长度是3,容量是5
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("numbers == ", numbers)
//打印子切片索引从1(包含)到4(不包含4 )
fmt.Println("numbers[1:4] == ", numbers[1:4])
//默认下限为0
fmt.Println("numbers[:3] == ", numbers[:3])
//默认上限为len(numbers)
fmt.Println("numbers[0:] == ", numbers[0:])
//这里number1是numbers的一个切片,但是底层还是指向同一个数组
//所以修改number1的值,也会同步修改numbers的值
number1 := numbers[0:5]
number1[0] = 100
fmt.Println(numbers)
fmt.Println(number1)
//为了修改一个切片,不影响另外一个,可以使用copy进行一个深拷贝
number2 := make([]int, 4)
copy(number2, numbers)
number2[0] = 101
fmt.Println(numbers)
fmt.Println(number2)
}
map
map的3种定义方式
package main
import "fmt"
func main() {
//第一种声明方式
//声明myMap是一个map类型,key是string类型,value是string类型
var myMap map[string]string
if myMap == nil {
fmt.Println("myMap是一个空map")
}
//在使用map前,需要先使用make给map分配空间
myMap = make(map[string]string, 10)
myMap["one"] = "Java"
myMap["two"] = "C++"
myMap["tree"] = "Go"
fmt.Println(myMap)
//第二种声明方式
myMap2 := make(map[int]string)
myMap2[0] = "Java"
myMap2[1] = "C++"
myMap2[2] = "Go"
fmt.Println(myMap2)
//第三种声明方式
myMap3 := map[string]string{
"one": "c#",
"two": "kotlin",
"tree": "c",
}
fmt.Println(myMap3)
}
map的使用
package main
import "fmt"
func printMap(myMap map[string]string) {
// 遍历
for key, value := range myMap {
fmt.Printf("key = %v, value = %v\n", key, value)
}
}
func changeMap(myMap map[string]string) {
//myMap是一个引用传递,在内部修改之后,会影响外面map
myMap["England"] = "London"
}
func main() {
cityMap := make(map[string]string)
//添加key-value
cityMap["China"] = "beijing"
cityMap["Japan"] = "Tokyo"
cityMap["USA"] = "NewYork"
printMap(cityMap)
//删除
delete(cityMap, "China")
//修改
cityMap["USA"] = "DC"
changeMap(cityMap)
fmt.Println("---")
printMap(cityMap)
}
结构体
基本概念
package main
import "fmt"
// 申明一个新的数据类型,myint是int的一个别名
type myint int
// 定义一个结构体
type Book struct {
title string
author string
}
func printBook(book Book) {
//传递book的一个副本
book.author = "123"
}
func changeBooke2(book *Book) {
//指针传递
book.author = "123"
}
func main() {
var a myint = 10
fmt.Println(a)
//使用一个结构体
var book1 Book
book1.title = "golang"
book1.author = "ada"
fmt.Printf("%v\n", book1)
//值传递
printBook(book1)
fmt.Printf("%v\n", book1)
//指针传递,参数是book1的地址
changeBooke2(&book1)
fmt.Printf("%v\n", book1)
}
类
封装
类名、属性名、方法名如果首字母大写表示对外(其他包)可以访问,否则只能在本包访问
package main
import "fmt"
// 定义一个结构体
//
// 如果类名首字母大写,表示其他包也能访问
type Hero struct {
//如果属性名大写,表示该属性对外能够访问
Name string
Ad int
Level int
}
/*
// (this Hero) -> 表示当前方法绑定到Hero结构
func (this Hero) getName()string{
return this.Name
}
func (this Hero) SetName(name string) {
//当前this是调用该方法的对象的一个拷贝
this.Name = name
}
func (this Hero) show(){
fmt.Println("Name = ", this.Name, ",Ad = ",this.Ad, ",Level = ", this.Level)
}
*/
// (this Hero) -> 表示当前方法绑定到Hero结构
func (this *Hero) getName() string {
return this.Name
}
// 如果方法首字母大写,表示方法对外能够访问
func (this *Hero) SetName(name string) {
this.Name = name
}
func (this *Hero) show() {
fmt.Println("Name = ", this.Name, ",Ad = ", this.Ad, ",Level = ", this.Level)
}
func main() {
//创建一个Hero对象
hero := Hero{Name: "zhangshan", Ad: 11, Level: 12}
hero.show()
hero.SetName("zzzz")
hero.show()
}
继承
子类继承父类,只需要将父类的类型名写入到子类结构体种,如下:
type SuperMan struct { Human //SuperMan继承了Human类 level int }
package main
import "fmt"
// 父类
type Human struct {
name string
sex string
}
func (this *Human) Eat() {
fmt.Println("Human Eat()...")
}
func (this *Human) Walk() {
fmt.Println("Human Walk()...")
}
// =============
// 子类
type SuperMan struct {
Human //SuperMan继承了Human类
level int
}
// 重写父类(Human)方法
func (this *SuperMan) Eat() {
fmt.Println("SuperMan Eat()...")
}
// 子类自己的新方法
func (this *SuperMan) Fly() {
fmt.Println("SuperMan Fly()...")
}
func main() {
//创建一个Human对象
human := Human{name: "ll", sex: "男"}
human.Eat()
human.Walk()
fmt.Println("=====")
//创建一个子类,方式一
// s := SuperMan{ Human{name: "ll", sex: "男"}, 2}
//方式二
var s SuperMan
s.Human = human
s.level = 1
//调用父类方法
s.Walk()
//调用子类重写的方法
s.Eat()
//调用子类自己写的方法
s.Fly()
}
多态
父类的变量(指针)指向(调用)子类的具体数据变量
package main
import "fmt"
// 本质是一个指针
type Animal interface {
Sleep()
}
// 定一个实现Animal接口的类
type Cat struct {
}
func (this *Cat) Sleep() {
fmt.Println("cat is sleep")
}
// 定义第二个实现Animal接口的类
type Dog struct {
}
func (this *Dog) Sleep() {
fmt.Println("dog is sleep")
}
// 这就是多态现象
func main() {
var animal Animal
animal = &Cat{}
//cat is sleep
animal.Sleep()
animal = &Dog{}
//cat is sleep
animal.Sleep()
}
接口
interface {}空接口,我们常用的数据结构:int、string、float等都实现了空接口,所以interface{}就能引用任意的数据类型,也称之为万能类型
package main
import "fmt"
// arg 使用interface{},表示是一个万能类型,可以传入任何值
func myFunc(arg interface{}) {
fmt.Println("my func is called")
fmt.Println(arg)
//arg如何区分底层引用的数据类型?
//
//go给interface{}提供了“类型断言”的机制
value, ok := arg.(string)
if !ok {
fmt.Println("arg is not srirng type")
} else {
fmt.Println("arg is srirng type, value = ", value)
fmt.Printf("value type is %T\n", value)
}
}
type zz struct {
a string
}
func main() {
myFunc(123) //123
myFunc("adv") //adv
myFunc(3.13) //3.13
z := zz{a: "测试"}
myFunc(z) //{测试}
}
反射
变量结构
一个变量内置结构被称为pair,pair包含type和value。type中又分为两类static type(int、string…)和concrete type(interface所指向的具体的数据类型,系统能够看的见的数据类型),并且一个type要么是static,要么是concrete,不会同时存在
package main
import "fmt"
type Reader interface {
ReadBook()
}
type Writer interface {
WriteBook()
}
// Book结构体,即实现Reader接口,又实现了Write接口
type Book struct {
}
func (this *Book) ReadBook() {
fmt.Println("read a book")
}
func (this *Book) WriteBook() {
fmt.Println("write a book")
}
func main() {
var a string
//a内部有个pair<statictype:string, value:"abc">
a = "abc"
//定义一个万能类型,pair<type:, value:>
var allType interface{}
//这是interfalce指向的数据结构就是a,因此pair<type:string, value:"abc">
allType = a
value, _ := allType.(string)
fmt.Println(value)
//-------
fmt.Println("-------")
//b:pair<type:Book, value:book{}地址>
b := &Book{}
//r:pair<type:, value:>
var r Reader
//r:pair<type:Book, value:book{}地址>
r = b
//这时候r.ReadBook相当于调用Book的ReadBook方法
r.ReadBook()
//r:pair<type:, value:>
var w Writer
//w:pair<type:Book, value:book{}地址>
w = r.(Writer) //此处为什么断言会成功?因为r,w具体的type是一致的
w.WriteBook()
}
reflect包
反射可以根据数据推测出你的数据类型,比较重要的两个方法
- func Value(i interface{}) Value{…}:输入任意数据类型,返回当前数据类型的value值,接口为空返回0
- func TypeOf(i interface{}) Type{…}:输入任意数据类型,返回当前数据类型的Type,接口为空返回nil
package main
import (
"fmt"
"reflect"
)
func relectNum(arg interface{}) {
//使用反射查看变量的类型和值
fmt.Printf("type = %v, value = %v \n", reflect.TypeOf(arg), reflect.ValueOf(arg))
}
type User struct {
Id int
Name string
Age int
}
func (this User) Call() {
fmt.Println("user is called...")
fmt.Printf("%v\n", this)
}
func FindFileAndMethod(input interface{}) {
//获取input的type
inputType := reflect.TypeOf(input)
fmt.Println("inputType type is ", inputType.Name())
//获取input的value
inputValue := reflect.ValueOf(input)
fmt.Println("inputType value is ", inputValue)
//通过type获取里面的字段
//1.获取interface的reflect.Type,通过Type得到NumFeild(属性数量),进行遍历
//2.得到每个field,数据类型
//3.通过field又个有一个Interface()方法得到对应的value
for i := 0; i < inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
fmt.Printf("%s:%v = %v \n", field.Name, field.Type, value)
}
//通过type获取里面的方法(只能是可以访问的即大写字母开头的),调用
for i := 0; i < inputType.NumMethod(); i++ {
m := inputType.Method(i)
fmt.Printf("%s:%v\n", m.Name, m.Type)
}
}
func main() {
//基本类型的反射
var num float64 = 1.234
relectNum(num)
//------
//通过user对象解析User类信息
fmt.Println("----")
user := User{1, "zhu", 12}
FindFileAndMethod(user)
}
结构体标签
package main
import (
"fmt"
"reflect"
)
// 主要用于字段说明
type resume struct {
Name string `info:"name" doc:"名称"`
Sex string `info:"sex"`
}
func findTag(str interface{}) {
//获取结构体全部元素
t := reflect.TypeOf(str).Elem()
for i := 0; i < t.NumField(); i++ {
//解析标签
tagInfo := t.Field(i).Tag.Get("info")
tagDoc := t.Field(i).Tag.Get("doc")
fmt.Println("info: ", tagInfo, "doc: ", tagDoc)
}
}
func main() {
var re resume
findTag(&re)
}
结构体标签在json中的应用
package main
import (
"encoding/json"
"fmt"
)
type Movie struct {
Title string `json:title`
Year int `json:year`
Price float32 `json:price`
Actors []string `json:actors`
}
func main() {
movie := Movie{"戏剧之王", 2000, 10, []string{"周星驰", "张柏芝"}}
//将结构体转成json格式字符串 结构体---》json
jsonStr, err := json.Marshal(movie)
if err != nil {
fmt.Println("json marshal err")
return
}
fmt.Printf("jsonString = %s\n", jsonStr)
//将json字符串转成结构体 json---》结构体
newMovie := Movie{}
err = json.Unmarshal(jsonStr, &newMovie)
if err != nil {
fmt.Printf("json unmarshal err")
return
}
fmt.Printf("newMovie = %v\n", newMovie)
}