1.认识Golang代码特性
Go
package main //1.包含main函数的文件就是一个main包--当前程序的包名
// import "fmt"
// import "time"
import(
"fmt"
"time"
)//3.同时包含多个包 4.强制代码风格:函数的 { 一定和函数名在同一行,否则会出现语法错误
//main函数
func main() {
fmt.Println("hello Go!")
// fmt.Println("hello Go!"); 2.加不加分号都可以,建议不加分号
time.Sleep(1 * time.Second)
}
2.Golang变量声明的四种方式
Go
package main
import (
"fmt"
)
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 = "abcd"
fmt.Printf("bb = %s,type of bb = %T\n",bb,bb)
//方法三:在初始化时可以省去数据类型,通过值自动匹配当前的变量的数据类型
var c = 100
fmt.Println("c =",c)
fmt.Printf("type of c = %T\n",c)
var cc = "abcd"
fmt.Printf("cc = %s,type of cc = %T\n",cc,cc)
//方法四:(常用的方法)省去var的关键字,直接:=自动匹配;在声明的变量是全局变量时,方法四是不可用的会报错
e := 100
fmt.Println("e =",e)
fmt.Printf("type of e = %T\n",e)
ee := "abcd"
fmt.Printf("ee = %s,type of ee = %T\n",ee,ee)
eee := 3.14
fmt.Printf("eee = %s,type of eee = %T\n",eee,eee)
//声明多个变量
var xx,yy int = 100,200 //相同数据类型
fmt.Println("xx =",xx," yy =",yy)
var mm,nn = 100,"abcd" //不同数据类型
fmt.Println("mm =",mm," nn =",nn)
//多行的多变量声明
var (
vv int = 100
jj bool = true
)
fmt.Println("vv =",vv," jj =",jj)
}
3.常量与iota
Go
package main
//const 来定义枚举类型
const (
//可以在const()添加一个关键字 iota,每行的iota都会累加1,第一行的iota的默认值是0
BEIJING = iota //iota = 0
SHANGHAI //iota = 1
SHENZHEN //iota = 2
)
//iota只能出现在const的括号中
const (
a,b = iota+1,iota+2// 0+1,0+2
c,d // 1+1,1+2
e,f // 2+1,2+2
g,h = iota*2,iota*3// 3*2,3*3
i,k // 4*2,4*3
)
func main()
{
//常量-具有只读属性
const length int = 10
fmt.Println("length = ",length)
length = 100 //报错-常量不可更改
}
4.Golang多返回值的三种写法
Go
package main
import "fmt"
func fool(a string,b int) int {
fmt.Println("a = ",a)
fmt.Println("b = ",b)
c := 100
return c
}
//1.返回多个返回值-匿名
func foo2(a string,b int) (int,int) {
fmt.Println("a = ",a)
fmt.Println("b = ",b)
return 666,777
}
//2.返回多个返回值-有形参名称
func foo3(a string,b int) (r1 int,r2 int) {
fmt.Println("------------foo3-----------")
fmt.Println("a = ",a)
fmt.Println("b = ",b)
r1 = 1000
r2 = 2000
return r1,r2
//return; 方法3--也可以返回r1,r2
}
func main() {
c := fool("abc",555)
fmt.Println("c = ",c)
ret1,ret2 := foo2("bfr",666)
fmt.Println("ret1 = ",ret1,"ret2 = ",ret2)
ret3,ret4 := foo3("grx",777)
fmt.Println("ret3 = ",ret3,"ret4 = ",ret4)
}
5.init函数与import导包

递归式导包直到最后一个包没有依赖包->加载该包全局的常量变量->执行init函数
"主程序开始之前,加载一些配置文件、加载一些数据库的内容、基本环境变量的初始化"
Go
package main
import(
"GolangStudy/Golang_5/lib1"
"GolangStudy/Golang_5/lib2"
)
func main(){
lib1.LibTest()
lib2.LibTest()
}
Go
package lib1
import "fmt"
//API接口
func LibTest() {
fmt.Println("Lib1Test.....")
}
//init初始化函数
func init() {
fmt.Println("Lib1init.....")
}
Go
package lib2
import "fmt"
//API接口--大写首字母表示对外开放该接口
func LibTest() {
fmt.Println("Lib2Test.....")
}
//init初始化函数
func init() {
fmt.Println("Lib2init.....")
}
运行结果:
三种导包的方式:
Go
//三种导包的方式
import(
_ "GolangStudy/Golang_5/lib1" //_表示匿名,可以在不使用该包但是可以调用它的init函数-无法使用当钱包的方法
//mylib "GolangStudy/Golang_5/lib2" //给包起别名mylib
. "GolangStudy/Golang_5/lib2" // . 表示将该包直接导入main包直接使用
)
func main(){
//lib1.LibTest()
//lib2.LibTest()
//mylib.LibTest()
LibTest()//同名方法容易导致歧义
}
6.Golang中指针的使用
Go
package main
import "fmt"
func swap(pa *int, pb *int) {
var tem int;
tem = *pa
*pa = *pb
*pb = tem
}
func main() {
var a int = 10
var b int = 20
swap(&a,&b)
fmt.Println("a = ",a,"b = ",b)
var p *int
p = &a
fmt.Println("p = ",p)
var pp **int = &p
fmt.Println("pp = ",pp)
}
7.Golang中的defer语句
Go
package main
import "fmt"
func main(){
//写入defer关键字--类似C++中的析构函数--出它作用域时会调用
defer fmt.Println("main end1")//先入栈
defer fmt.Println("main end2")//再入栈
fmt.Println("main run1")
fmt.Println("main run2")
}
输出结果:
defer和return的执行顺序
Go
func deferFunc() int{
fmt.Println("defer func called...")
return 0
}
func returnFunc() int{
fmt.Println("return func called...")
return 0
}
func rad() int {
defer deferFunc()
return returnFunc()
}
func main() {
rad()
}
输出结果: return比defer先执行
8.Golang中的数组
---静态数组
静态数组传参是值拷贝传参,并且只能接收特定格式的数组确定元素类型与元素个数
Go
package main
import "fmt"
//值拷贝传参,将实参数组拷贝给形参
func printArray(myArray [10]int) { //只能接收1、2即int[10]数据类型
for index,value:=range myArray {
fmt.println(index," ",value)
}
}
func printArray(myArray [4]int) { 只能接收3即int[4]数据类型
for index,value:=range myArray {
fmt.println(index," ",value)
}
}
func main(){
//固定长度的数组
var myArray1 [10]int
myArray2 := [10]int{1,2,3,4}
myArray3 := [4]int{1,2,3,4}
//for i:= 0; i<10; i++
for i:=0; i<len(myArray1); i++{
fmt.println(myArray1[i])
}
for index,value:=range myArray1 {
fmt.println(index," ",value)
}
//查看数组的数据类型
fmt.printf("myArray1 types = %T\n",myArray1)
fmt.printf("myArray2 types = %T\n",myArray2)
fmt.printf("myArray3 types = %T\n",myArray3)
}
---动态数组(slice切片)
Go
package main
import "fmt"
//动态数组具有传参上的优势
//传递的是当前数组的指针首地址,引用传递
func printArray(myArray []int) {
// _ 表示匿名的变量
for _, value := range myArray {//不关心下标,但必须用两个接收
fmt.Println("value = ",value)
}
myArray[0] = 100
}
func main() {
myArray := []int{1,2,3,4} //动态数组,切片 slice
fmt.Printf("myArray type is %T\n",myArray)//输出:myArray type is []int
printArray(myArray)
for _, value := range myArray {
fmt.Println("value = ",value)
}
}
---四种声明一个切片的方式
Go
func main () {
//声明slice是一个切片,并且初始化,默认值是1,2,3;长度len是3
slice := []int{1,2,3}
//声明slice1是一个切片,但没有给它分配空间
var slice1 []int //此时长度就是0
slice1 = make([]int,3)//通过make给slice1分配空间,默认值是0
//声明slice2是一个切片,同时通过make给slice2分配空间,默认值是0
var slice2 []int = make([]int,3)
//声明slice3是一个切片,同时分配空间,通过:=推导出slice是一个切片
slice := make([]int,3)
fmt.Printf("len = %d,slice = %v\n",len(slice1),slice1)
//判断一个slice是否为0
if slice == nil {
fmt.printf("slice是一个空切片")
}else{
fmt.printf("slice不是一个空切片")
}
}
---切片容量的追加
Go
func main () {
var numbers = make([]int,3,5)//开辟五个空间只初始化3个为0
fmt.Printf("len = %d,cap = %d,numbers = %v\n",len(numbers),cap(numbers),numbers)
//向numbers切片追加一个1
numbers = append(numbers,1)
fmt.Printf("len = %d,cap = %d,numbers = %v\n",len(numbers),cap(numbers),numbers)
numbers = append(numbers,2)
fmt.Printf("len = %d,cap = %d,numbers = %v\n",len(numbers),cap(numbers),numbers)
numbers = append(numbers,3)//会进行2倍扩容
fmt.Printf("len = %d,cap = %d,numbers = %v\n",len(numbers),cap(numbers),numbers)
var numbers2 = make([]int,3)//开辟三个空间并全部初始化为0
fmt.Printf("len = %d,cap = %d,numbers2 = %v\n",len(numbers2),cap(numbers2),numbers2)
numbers2 = append(numbers2,1)//会进行2倍扩容
fmt.Printf("len = %d,cap = %d,numbers2 = %v\n",len(numbers2),cap(numbers2),numbers2)
}
输出结果:
---切片的截取
Go
func main () {
s := []int{1,2,3} //len=3,cap=3
//[0,2)
s1 := s[0:2] //取1和2,前面省略表示从第0个开始取,后面省略表示取到最后一个
fmt.Println(s1)
s1[0] = 100
fmt.Println(s1) //s1此时也指向s的第一个,相当于浅拷贝
fmt.Println(s)
//copy深拷贝,将底层数组的slice一起进行拷贝
s2 := make([]int,2) //s2 = [0,0,0]
copy(s2,s1)
fmt.Println(s2) //可以先将s切片成s1,再进行深拷贝给s2,就可以分离
}
9.Golang中基于哈希的Map
---Map的三种声明定义方式
Go
func main() {
//第一种声明方式
var myMap1 map[string]string //仅仅是声明,没有实际的空间,并不能被使用
if myMap1 == nil {
fmt.Println("myMap1是一个空map")
}
myMap1 = make(map[string]string,10) //用make给Map开辟十个键值对的空间
myMap1["one"] = "java"
myMap1["two"] = "C++"
myMap1["three"] = "python"
//...当插入十个之后也会二倍扩容
fmt.Println(myMap1)//打印出来是无序的
//第二种声明方式
myMap2 := make(map[int]string)
myMap2[1] = "java"
myMap2[2] = "C++"
myMap2[3] = "python"
fmt.Println(myMap2)
//第三种声明方式
myMap3 := map[int]string {
4 : "java",
5 : "C++",
6 : "python",
}
fmt.Println(myMap3)
}
---Map的基本使用
Go
func printMap(cityMap map[string]string) {
for key,value := range cityMap {
fmt.Printf("key = %s ",key)
fmt.Printf("value = %s\n",value)
}
cityMap["one"] = "golang"
}
//指向当前map内存结构体地址的指针cityMap
func main() {
cityMap := make(map[string]string,3)
//添加
cityMap["one"] = "java"
cityMap["two"] = "C++"
cityMap["three"] = "python"
//删除
delete(cityMap,"three")
//修改
cityMap["two"] = "php"
//传参
printMap(cityMap)
//遍历
for key,value := range cityMap {
fmt.Printf("key = %s ",key)
fmt.Printf("value = %s\n",value)
}
}
//将一个map赋值给另一个时只能完成指针之间的浅拷贝,只能通过make开辟一个Map进行遍历赋值
10.struct(类)的定义和使用
---struct类的定义
Go
//声明一种行的数据类型,是Int的一个别名
type myint int
type Book struct {
title string
auth string
}
func changeBook(book Book) {
//传递book的一个副本
book.auth = "666"
}
func changeBook2(book *Book) {
//传递指向Book结构体的指针
book.auth = "777"
}
func main() {
// var a myint = 10
// fmt.Printf("type = %T",a)
var book1 Book
book1.title = "Golang"
book1.auth = "zhang3"
fmt.Printf("book1 = %v",book1)
}
---类的封装和方法的调用
Go
//go语言中的类实际上是通过结构体来绑定方法
package main
//方法名、类名如果首字母大写,其它包也可以访问
type Hero struct {
Name string
Ad int
Level int
}
//当前括号表示该方法绑定到了Hero结构体,this表示调用对象,谁调用该方法this就指哪个对象
func (this Hero) GetName() string{
return this.Name
}
//this是当前调用对象的一个副本
func (this Hero) SetName(newName string) {
this.Name = newName
}
//此时this就是指向当前对象的指针,就可以修改了
func (this *Hero) SetName(newName string) {
this.Name = newName
}
func main() {
//创建一个对象
hero := Hero{Name: "zhang3",Ad: 100,Level: 1}
}
---继承的基本语法
Go
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 {//Go中实际是以组合的形式实现继承
Human //Superman继承了Human类的所有方法
level int
}
//重写(覆盖)父类方法
func (this *SuperMan) Eat() {
fmt.Println("SuperMan Eat().....")
}
//子类的新方法
func (this *SuperMan) Fly() {
fmt.Println("SuperMan Fly().....")
}
func main() {
h := Human{"zhang3","female"}
h.Eat()
h.Walk
s := SuperMan{Human{"li4","female"},88}
// var s SuperMan
// s.name = "li4"//...
s.Walk() //子类调用父类的方法
s.Eat() //子类调用子类重写的方法
s.Fly() //子类调用子类的新方法
}
---interface实现多态
Go
package main
//面向对象的多态:定义一个接口完成多态的现象;接口定义抽象方法,子类去继承实现重写这个方法
//interface本质上是一个指针,指向当前类型包含的函数列表
type AnimalIF interface {
Sleep()
GetColor() string //获取动物的颜色
GetType() string //获取动物的类型
}
//具体的值--要继承AnimalIF interface只需要将他的三个方法实现,不需要显式继承
type Cat struct {
color string //猫的颜色
}
func (this *Cat) Sleep() {
fmt.Println("cat is sleep")
}
func (this *Cat) GetColor() string {
return this.color
}
func (this *Cat) GetType() string {
return "cat"
}
//具体的值--狗
type Dog struct {
color string
}
func (this *Dog) Sleep() {
fmt.Println("Dog is sleep")
}
func (this *Dog) GetColor() string {
return this.color
}
func (this *Dog) GetType() string {
return "Dog"
}
func showAnimal(animal AnimalIF)//调用该函数,形参为父类指针,实参为子类对象实现多态
{
animal.Sleep()
}
func main() {
// var animal AnimalIF //接口的数据类型,相当于定义了一个父类指针
// animal = &Cat("green") //让父类的指针指向一个子类对象
// animal.Sleep() //指向子类对象的父类指针调用子类方法实现多态
cat := Cat("green")
dog := Dog("yellow")
showAnimal()
}
---interface{}空接口万能类型
Go
//interface{} 空接口,万能类型-》可以引用任意数据类型
package main
import "fmt"
func myFunc(arg interface{}) { //可以传任意类型的实参
fmt.Println("myFunc is called..")
fmt.Println(arg)
//类型断言
value,ok := arg.(string) //value是数据值,ok是error
if !ok {
fmt.Println("arg is not a string")
}else{
fmt.Println("arg is a string")
}
fmt.Println(value)
}
type Book struct {
auth string
}
func main() {
book := Book{"Golang"}
myFunc(book)
myFunc("abcd")
myFunc(888)
myFunc(23.45)
}
11.Golang中反射的概念
---接口type interface { }的类型
Golang中一个变量包含一个pair即type和value对。
type分为static type(在编译时就确定并且不会改变)和concrete type(在运行时确定并且会改变)。
Go
var i int // static type 为 int;
var i interface{} // pair()
i = 18 // concrete type 为 int
i = "Go编程时光" // concrete type 变为 string
Go
type Reader interface {
ReadBook()
}
type Writer interface {
WriteBook()
}
//具体类型
type Book struct {
}
func (this *Book) ReadBook() {
fmt.Println("Read a book.")
}
func (this *Book) WriteBook() {
fmt.Println("Write a book.")
}
func main() {
b := &Book{} //type:*Book value:0xc000012028
var r Reader //concrete type:nil,static type:Reader value:nil
r = b //concrete type:Book*,static type Reader value:0xc000012028
r.ReadBook() //Reader - Book* 多态
var w Writer //concrete type:nil,static type:Writer value:nil
w = r.(Writer)//类型断言,检查r是否实现了Writer,并将Book*给w
w.WriteBook()
}
---reflect.ValueOf 和 reflect.TypeOf
Go
package main
import "fmt"
import "reflect"
type User struct {
Id int//一个filed
Name string//第二个filed
Age int //第三个filed
}
func (this *User) Call() {
fmt.Println("User is called ...")
fmt.Println("%v\n",this)
}
func main() {
user := User{1,"bfr",18}
DoF(user)
}
func DoF(input interface{}) {
//获取input的type
inputType := reflect.TypeOf(input)
fmt.Println("inputType :",inputType.Name())//?直接打印出当前类型的名称?
//获取input的value
inputValue := reflect.ValueOf(input)
fmt.Println("inputValue :",inputValue)
//通过type获取里面重要字段
//通过inputType可以获取NumFiled,进行遍历,就能得到每个filed
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)//ID,int,1
}
//通过Type获取里面的方法,调用
for i:=0; i<inputType.NumMethod(); i++ { //遍历User有多少个方法
m := inputType.Method(i)
fmt.Printf("%s: %v\n",m.Name,m.Type)
}
}

12.结构体标签
Go
package main
import "fmt"
import "reflect"
//在不同包中有不同的解释说明
type resume struct {
Name string `info:"name" doc:"我的名字"`
Sex string `info:"sex"`
}
func findTag(str interface{}) {
t := reflect.TypeOf(str).Elem()//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," dac:",tagdoc)
}
}
func main() {
var res resume
findTag(&res)
}
Go
//结构体标签-key已知,value未知
type Movie struct {
Title string `json:"title"` //在json中显示的字段
Year int `json:"year"`
Price int `json:"price"`
Actors []string `json:"actors"`
}
func main() {
movie := Movie{"喜剧之王",2000,10,[]string{"xingye","zbz"}}
//编码的过程 将结构体->json
jsonStr,err := json.Marshal(movie)//Marshal可以将结构体转化为json格式
if err != nil {
fmt.Println("json marshal error",err)
}else{
fmt.Printf("josnStr = %s\n",jsonStr)
}
//解码的过程 json->结构体
myMovie := Movie{}
err = json.Unmarshal(jsonStr,&myMovie)//将json字符串解析给myMovie结构体
if err != nil {
fmt.Println("json unmarshal error",err)
return
}
}
13.Golang中的协程:goroutine
---协程的演变发展
---单进程的操作系统只能顺序执行任务,一旦某个任务阻塞,其它任务都不能处理。
---多进程/多线程解决了,CPU调度器进行轮询调度切换进程/线程 ,当某个时间片到了就会切换。但是进程/线程间切换具有成本,一旦数量过大,CPU的++使用效率++ 就会大大降低。进程占4GB左右,线程占用4MB左右,也有++高内存占用++ 的弊端。
---将一个线程拆分,一半在用户层面供用户调用(协程:co-routine),一半在内核层供CPU调度。
---N : 1,如果有一个协程阻塞,那它的下一个协程就会受到影响。操作系统感知不到用户级协程的存在,无法将阻塞的协程与线程分离,线程资源被完全占用
---M : N,利用多核,一个CPU绑定多个线程;解决了某个协程阻塞影响其它协程的问题;通过优化写成调度器优化效率,CPU不做调度。
---Golang对协程的处理
co-routine -> goroutine;将占用内存优化到KB单位(可以大量生产),(可以灵活调度)。
---通过优化调度器实现灵活调度
Go
package main
import "fmt"
import "time"
func newTask() {
i:=0
for {
i++
fmt.Printf("new Goroutine : i = %d\n",i)
time.Sleep(1 * time.Second)
}
}
func main() {
//创建一个go程 去执行newTask()流程
go newTask()
time.Sleep(10*time.Second)
fmt.Println("main goroutine exit")
// i:=0
// for { //死循环main
// i++
// fmt.Printf("main goruntine: i = %d\n",i)
// time.Sleep(1*time.Second)
// }
}
Go
package main
import "fmt"
import "time"
import "runtime"
func main() {
//调匿名无参goroutine
go func() {//匿名方法,直接用Go承载一个形参为空返回值为空的一个函数
defer fmt.Println("A.defer")
func() {//仅仅是函数定义
defer fmt.Println("B.defer")
//return
runtime.Goexit()
fmt.Println("B")
}()
fmt.Println("A")
}()//调用
//调匿名有参goroutine
go func(a int,b int) bool { //并不是阻塞操作,是一个异步操作,不能拿到返回值
fmt.Println("a = ",a," b = ",b)
return true
}(10,20)
time.Sleep(1*time.Second)
}
14.channel-go语言中协程间通信的机制
---构成死锁:
1.无缓存channel、只写不读或只读不写、导致主线程阻塞。
2.有缓存channel、已满只写不读或为空只读不写、导致主线程阻塞。
Go
package main
import "fmt"
//import "time"
func main() {
c := make(chan int,3)//带有缓冲的channel
fmt.Println("len(c) = ",len(c)," cap(c) = ",cap(c))
go func() {
defer fmt.Println("子go程结束")
for i:=0;i<6;i++{
c <- i
fmt.Println("running...:","i = ",i," len(c) = ",len(c)," cap(c) = ",cap(c))
}
}()
//time.Sleep(2 * time.Second)
// for i:=0;i<2;i++ {
// num := <-c //从c中接收数据,并赋值给num
// fmt.Println("num =",num)
// }
fmt.Println("main 结束")
}
---close关闭channel
关闭channel后,无法向channel再发送数据(再发会引发 panic 错误后导致接收立即返回零值)
关闭channel后,仍旧具有缓存,等读端读完了才会返回。
简单var声明一个channel数据类型,没有进行make,称为nil channel。无论收发都会阻塞。
Go
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i:=0;i<5;i++ {
c <- i
}
//close可以关闭一个channel
close(c)
}()
for {
//ok=true表示channel没有关闭
if data,ok := <-c; ok { //原子性操作:确保通道接收与状态检查在同一个代码块中完成
fmt.Println(data)
}else{
break
}
}
fmt.Println("Main Finished..")
}
---channel 和 range
尝试从c中读数据,range会阻塞等待这个结果;c中有数据range就会返回并进入本轮for循环,没有数据就会阻塞
Go
//可以使用range来迭代不断操作channel
for data := range c {
fmt.Println(data)
}
---channel 和 select
单流体下的一个go只能监视一个channel状态,阻塞监听,select可以解决一个go监听多个channel状态,如果某个channel可读或可写它就会立刻返回。一般会循环进行select监控多个channel。
Go
package main
import "fmt"
func fibo(c,quit chan int){
x,y := 1,1 //1,1->1; 1,2->1;
for{
select{
case c <- x: //只要func可读这边就可写
//如果c可写,则该case就会进来
t := x
x = y
y = t+y
case <-quit: //只要quit被写入这边就可读就退出
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 6; i++{
fmt.Println(<-c)//读c
}
quit <- 0 //循环结束退出
}()
//main go
fibo(c,quit)
}