声明:本文为b站大佬刘丹冰Aceld 的8小时转职Golang工程师 视频学习笔记,如有侵权,联系我速删
大佬的传送门:https://www.bilibili.com/video/BV1gf4y1r79E/?spm_id_from=333.999.0.0&vd_source=95a9bb0b0759f8d391968411c1a6f007 
在b站发现了大佬的宝藏视频,在此记录下学习过程。我在这里着重学习记录的是go语言的特性,也就是go与其他后端语言有区别的地方。并且在文章的末尾附上我使用go语言开发的即时通信系统的github地址。
一、目录 1.初识go语言
2.变量与常量
3.函数的返回值与defer
4.golang中的数组
5.golang中的map
6.golang面向对象基础
7.golang中的反射
8.goroutine和channel
二、初识go语言 1 2 3 4 5 6 7 8 9 10 11 package  main import  ( 	"fmt"  	"time"  ) func  main ()   { 	fmt.Println("Hello,go!!!" ) 	time.Sleep(2  * time.Second) } 
运行结果: 
知识点: 
1.程序的第一行是包名,使用package 关键字
2.使用import 关键字进行导包
3.go语言中函数声明行末必须是左花括号
三、变量与常量 1.声明变量的四种方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package  mainimport  "fmt" func  main ()   {	 	var  a int  	fmt.Println("a =" , a) 	 	var  b string  = "字符串"  	fmt.Println("b =" , b) 	 	var  c = 100.00  	fmt.Printf("c is %T\n" , c) 	 	 	d := 60  	fmt.Printf("the type of c is %T\n" , d) } 
运行结果: 
知识点: 
(1) 我们通常使用fmt包中的Println函数来输出一行内容,相当于python中的print函数,其中的逗号表示使用空格连接,会自动在末尾加上换行符。
(2) fmt包中的Printf函数为格式化输出,不会自动加换行符,需要手动添加。其中的格式化参数:
%T 打印变量的类型.%v 以默认的方式打印变量的值.例如在结构体中{jack {12345 6789}}%+v 带字段名称.例如在结构体中{name:jack phone:{mobile:12345 office:6789}%t 打印true或false…… (3) 我们通常在函数体内使用冒等(:=)的写法来初始化变量,并给它赋值
2.声明多个变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package  mainimport  "fmt" func  main ()   {	var  m, n int  = 100 , 200  	var  j, k = 300 , "abc"  	c, v := 1.5 , true  	fmt.Println(m, n, j, k, c, v) 	var  ( 		z bool  = true  		x int   = 1  	) 	fmt.Println(z, x) } 
运行结果: 
3.const与iota 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package  mainimport  "fmt" const (	 	MONDAY = iota +1   	TUESDAY  		 	WEDNESDAY  	 ) const (	a,b = iota +1 ,iota +2   	c,d					 	e,f					 	g,h = iota *2  , iota *3   	m,n ) func  main ()   {	 	const  length int  = 10  	fmt.Println(MONDAY,TUESDAY,WEDNESDAY) 	fmt.Println(a,b,c,d,e,f,g,h,m,n) } 
运行结果: 
知识点: 
(1) 常量的定义格式和变量的声明语法类似
(2) iota是常量计数器,在第一行为0,之后每新增一行常量声明将使iota计数一次
四、函数的返回值与defer 1.函数多返回值的几种写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package  mainimport  "fmt" func  foo1 (a string , b string )   int  {	c := 11111  	fmt.Println("进入了函数1,并输入两个参数:" , a, b) 	return  c } func  foo2 (a string , b string )   (int , string ) {	c := 2222222  	d := "222222"  	fmt.Println("进入了函数2,并输入两个参数:" , a, b) 	return  c, d } func  foo3 (a string , b string )   (r1 int , r2 string ) {	fmt.Println("进入了函数3,并输入两个参数:" , a, b) 	 	r1 = 333  	r2 = "333333333"  	return  } func  foo4 (a string , b string )   (r1, r2 int ) {	fmt.Println("进入了函数4,并输入两个参数:" , a, b) 	 	r1 = 444  	r2 = 444444  	return  } func  main ()   {	fmt.Println(foo1("1" , "1" )) 	ret1, ret2 := foo2("2" , "2" ) 	fmt.Println(ret1, ret2) 	ret3, ret4 := foo3("3" , "3" ) 	fmt.Println(ret3, ret4) 	ret5, ret6 := foo4("4" , "4" ) 	fmt.Println(ret5, ret6) } 
运行结果: 
知识点: 
(1) 函数声明语法: func 函数名(形参 形参数据类型,……)(返回值参数 返回值类型,…){
(2) 大多数 a int,b int,c int这种形式都可以简写为a, b, c int
(3) 函数名首字母小写表示只能在当前文件内调用,像我们常用的fmt.Println()首字母大写,可以在其他文件内调用
2.defer语句调用流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package  mainimport  "fmt" func  deferAndReturn ()   int  {	defer  fmt.Println("func: 调用了defer" ) 	return  returnFunc() } func  returnFunc ()   int  {	fmt.Println("func: 调用了return" ) 	return  1  } func  main ()   {	 	defer  fmt.Println("main: main end1" ) 	defer  fmt.Println("main: main end2" ) 	fmt.Println("main: main go 1" ) 	fmt.Println("main: main go 2" ) 	deferAndReturn() } 
运行结果: 
知识点: 
(1) 使用defer后会将当前语句入栈 ,在当前函数结束之前依次出栈 
(2) 注意defer是在return之后执行的
五、golang中的数组 1.静态数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package  mainimport  "fmt" func  printArray (myArray [10]int )  {	for  index,value := range  myArray{ 		fmt.Println(index,value) 	} } func  main ()   {	 	var  myarray1 [10 ]int  	myarray2 := [10 ]int {1 ,2 ,3 ,4 } 	for  i:=0 ; i<len (myarray1); i++ { 		fmt.Println(myarray1[i]) 	} 	printArray(myarray2) } 
运行结果: 
知识点: 
(1) 声明数组后,其中元素默认值为0
(2) 函数的形参和传递实参的长度一定要对应
2.动态数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  mainimport  "fmt" func  printArray (myArray []int )   {	 	 	for  _, value := range  myArray { 		fmt.Println(value) 	} } func  main ()   {	 	 	slice1 := []int {1 , 2 , 3 } 	printArray(slice1) 	fmt.Printf("len=%d,slice=%v\n" , len (slice1), slice1) 	 	var  slice2 []int  	 	slice2 = make ([]int , 3 ) } 
运行结果: 
3.动态数组的容量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package  mainimport  "fmt" func  main ()   {	var  numbers = make ([]int , 3 , 5 ) 	 	fmt.Printf("len=%d,cap=%d,slice=%v\n" , len (numbers), cap (numbers), numbers) 	 	numbers = append (numbers, 1 ) 	fmt.Printf("len=%d,cap=%d,slice=%v\n" , len (numbers), cap (numbers), numbers) 	 	 	numbers = append (numbers, 2 ) 	numbers = append (numbers, 3 ) 	fmt.Printf("len=%d,cap=%d,slice=%v\n" , len (numbers), cap (numbers), numbers) } 
运行结果: 
知识点: 
(1) 使用make为数组开辟空间时,可以指定数组的cap,即数组的最大容量
(2) 当向slice添加的元素超过cap时,会自动扩充一个原始cap的大小
六、golang中的map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package  mainimport  "fmt" func  main ()   {	 	var  myMap1 map [string ]string  	 	myMap1 = make (map [string ]string , 10 ) 	myMap1["one" ] = "java"  	myMap1["two" ] = "c++"  	fmt.Println(myMap1) 	 	myMap2 := map [string ]string { 		"a" : "aa" , 		"b" : "bb" ,  	} 	fmt.Println(myMap2) } 
运行结果: 
七、golang面向对象基础 1.struct的使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package  mainimport  "fmt" type  myint int type  Book struct  {	title string  	auth  string  } func  printBook (book Book)   {	 	fmt.Printf("%v\n" , book) } func  changeBook (book *Book)   {	 	book.title = "已经被改变了"  } func  main ()   {	var  book1 Book 	book1.title = "Golang"  	book1.auth = "lisi"  	 	fmt.Printf("%v\n" , book1) 	 	changeBook(&book1) 	fmt.Printf("%v\n" , book1) } 
运行结果: 
2.创建一个对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package  mainimport  "fmt" type  Hero struct  {	Name  string  	Ad    int  	Level int  } func  (this *Hero)   Show() {	fmt.Println("Name =" , this.Name) 	fmt.Println("Ad =" , this.Ad) 	fmt.Println("Level =" , this.Level) } func  (this *Hero)   GetName() string  {	return  this.Name } func  (this *Hero)   SetName(newName string ) {	this.Name = newName } func  main ()   {	 	hero := Hero{Name: "zhangsan" , Ad: 100 , Level: 2 } 	 	fmt.Println(hero.GetName()) 	hero.SetName("已经改变" ) 	hero.Show() } 
运行结果: 
知识点: 
(1) 注意成员方法的写法,要在func和函数名之间加上**(变量名 *类名)**,变量名通常用this
3.类的继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package  mainimport  "fmt" type  Human struct  {	name string  	sex  string  } func  (this *Human)   Eat() {	fmt.Println("开饭了" ) } func  (this *Human)   Walk() {	fmt.Println("走起来了" ) } type  SuperMan struct  {	Human  	level int  } func  (this *SuperMan)   Eat() {	fmt.Println("超人开饭了" ) } func  (this *SuperMan)   Fly() {	fmt.Println("超人飞起来了" ) } func  main ()   {	human := Human{"zhangsan" , "female" } 	human.Eat() 	 	 	var  s SuperMan 	s.name = "lisi"  	s.sex = "male"  	s.level = 99  	s.Walk() 	s.Eat() 	s.Fly() } 
运行结果: 
4.创建一个接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package  mainimport  "fmt" type  AnimalIF interface {	Sleep() 	GetColor() string  	GetType() string  } type  Cat struct {	color string  } func  (this *Cat)   Sleep(){	fmt.Println("猫在睡觉" ) } 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("狗在睡觉" ) } func  (this *Dog)   GetColor() string  {	return  this.color } func  (this *Dog)   GetType() string  {	return  "dog"  } func  showAnimal (animal AnimalIF)  {	animal.Sleep() 	fmt.Println("color = " ,animal.GetColor()) 	fmt.Println("type = " ,animal.GetType()) } func  main ()   {	 	cat := Cat{"Green" } 	dog := Dog{"Yellow" } 	showAnimal(&cat) 	showAnimal(&dog) } 
运行结果: 
知识点: 
(1) 接口使用interface关键字
(2) 当一个类实现了接口的所有方法后,就相当于是实现了这个接口
5.interface{}万能数据类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package  mainimport  "fmt" func  myFunc (arg interface {})   {	fmt.Println(arg) 	 	 	value, ok := arg.(string ) 	if  !ok { 		fmt.Println("arg is not string" ) 	} else  { 		fmt.Println("arg is string,value =" , value) 		fmt.Printf("arg type is %T\n" , value) 	} } type  Book struct  {	auth string  } func  main ()   {	book := Book{"Golang" } 	myFunc(book) 	myFunc("1234" ) } 
运行结果: 
知识点: 
(1) 感觉interface{}类似于java中的Object
八、golang中的反射 1.变量的内置pair结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  mainimport  "fmt" func  main ()   {	var  a string  	 	a = "abcd"  	 	var  allType interface {} 	allType = a 	str, _ := allType.(string ) 	fmt.Println(str) } 
运行结果: 
知识点: 
(1) 每个变量都内置一个键值对(type, value)
(2) 变量不管给谁赋值,变量的pair都不会改变(pair连续传递 )
一些例子: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  mainimport  (	"fmt"  	"io"  	"os"  ) func  main ()   {	 	tmp, err := os.OpenFile("D:\\Temp\\tmp.txt" , os.O_RDWR, 0 ) 	if  err != nil  { 		fmt.Println("error" , err) 		return  	} 	 	var  r io.Reader 	 	r = tmp 	 	var  w io.Writer 	 	w = r.(io.Writer) 	w.Write([]byte ("Hello world!!\n" )) } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package  mainimport  "fmt" type  Reader interface  {	ReadBook() } type  Writer interface  {	WriteBook() } type  Book struct {}func  (this *Book)   ReadBook() {	fmt.Println("Read..." ) } func  (this *Book)   WriteBook() {	fmt.Println("Write..." ) } func  main ()   {	 	b := &Book{} 	 	var  r Reader 	 	r = b 	r.ReadBook() 	var  w Writer 	 	w = r.(Writer) 	w.WriteBook() } 
2.使用reflect获取变量的value和type 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package  mainimport  (	"fmt"  	"reflect"  ) func  reflectNum (arg interface {})   {	fmt.Println("type: " , reflect.TypeOf(arg)) 	fmt.Println("value: " , reflect.ValueOf(arg)) } func  main ()   {	var  num float64  = 1.2345  	reflectNum(num) } 
运行结果: 
3.使用reflect获取类对象的字段和方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package  mainimport  (	"fmt"  	"reflect"  ) type  User struct  {	Id   int  	Name string  	Age  int  } func  (this User)   Call() {	fmt.Println("user is called" ) 	fmt.Printf("%v\n" , this) } func  main ()   {	user := User{1 , "zhangsan" , 8 } 	DoFileAndMethod(user) } func  DoFileAndMethod (input interface {})   {	 	inputType := reflect.TypeOf(input) 	fmt.Println("inputType: " , inputType.Name()) 	 	inputValue := reflect.ValueOf(input) 	fmt.Println("inputValue: " , inputValue) 	 	 	 	 	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) 	} 	 	for  i := 0 ; i < inputType.NumMethod(); i++ { 		m := inputType.Method(i) 		fmt.Printf("%s: %v\n" , m.Name, m.Type) 	} } 
运行结果: 
九、goroutine和channel 1.创建一个goroutine 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package  mainimport  (	"fmt"  	"time"  ) func  newTask ()   {	i := 0  	for  { 		i++ 		fmt.Printf("new goroutine : i = %d\n" , i) 		time.Sleep(1  * time.Second) 	} } func  main ()   {	 	go  newTask() 	i := 0  	for  { 		i++ 		fmt.Printf("main goroutine : i = %d\n" , i) 		time.Sleep(1  * time.Second) 	} } 
运行结果: 
知识点: 
(1) 使用go 关键字可以开启一个go程
(2) 可以使用go加匿名函数来创建一个go程,详情见下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package  mainimport  (	"fmt"  	"time"  	 ) func  main ()   {	 	go  func (a int , b int )   bool { 		fmt.Println("a =" ,a,"b = " ,b) 		return  true  	}(10 ,20 ) 	 	for { 		time.Sleep(1  * time.Second) 	} } 
注意:使用go+func创建go程后要在最后使用括号调用函数
2.创建一个channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package  mainimport  "fmt" func  main ()   {	 	c := make (chan  int ) 	go  func ()   { 		defer  fmt.Println("go1结束" ) 		fmt.Println("go1正在运行...." ) 		c <- 666   	}() 	num := <-c  	fmt.Println("num = " , num) 	fmt.Println("main gorountine结束" ) } 
运行结果: 
知识点: 
(1) 使用**make(chan int)**声明并初始化一个int类型的channel
(2) 在channel中读数据: num := <-c  ,写数据:c <- 666 
3.带缓冲的channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package  mainimport  (	"fmt"  	"time"  ) func  main ()   {	 	c := make (chan  int , 3 ) 	 	fmt.Println("len(c) = " , len (c), "cap(c)" , cap (c)) 	go  func ()   { 		defer  fmt.Println("子go程结束" ) 		for  i := 0 ; i < 5 ; i++ { 			c <- i 			fmt.Println("子go程正在运行 len(c) = " , len (c), "cap(c)" , cap (c)) 		} 	}() 	time.Sleep(2  * time.Second) 	for  i := 0 ; i < 3 ; i++ { 		num := <-c 		fmt.Println("num =" , num) 	} 	fmt.Println("main结束" ) } 
运行结果: 
知识点: 
(1) 当子go程向channel写了3个元素之后,由于主go程的Sleep,并没有人读channel,导致子go程阻塞。随后,主go程睡醒,读取三个元素然后结束,就导致了子go程的defer语句没有输出
(2) 当channel满了之后,再想写入就会阻塞当前go程
4.使用close关闭channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package  mainimport  (	"fmt"  ) func  main ()   {	c := make (chan  int ) 	go  func ()   { 		for  i := 0 ; i < 5 ; i++ { 			c <- i 		} 		 		close (c) 	}() 	for  { 		 		if  data, ok := <-c; ok { 			fmt.Println(data) 		} else  { 			break  		} 	} 	fmt.Println("main结束" ) } 
运行结果: 
知识点: 
(1) channel关闭就不可以写了,但是可以接着读
5.使用range来不断迭代操作channel 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package  mainimport  (	"fmt"  ) func  main ()   {	c := make (chan  int ) 	go  func ()   { 		for  i := 0 ; i < 5 ; i++ { 			c <- i 		} 		 		close (c) 	}() 	 	 	 	 	 	 	 	 	 	for  data := range  c { 		fmt.Println(data) 	} 	fmt.Println("main结束" ) } 
6.使用select 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package  mainimport  (	"fmt"  ) func  fabonacii (c, quit chan  int )   {	x, y := 1 , 1  	for  { 		select  { 		case  c <- x: 			 			t := y 			y, x = x+y, t 		case  <-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) 		} 		quit <- 0  	}() 	fabonacii(c, quit) } 
运行结果: 
十、即时通信系统 跟着老师的课程,使用go语言写了一个即时通信系统的小例子,放在了我的github上
项目地址:https://github.com/feiweiliang/-golang- 
实现的功能:
更改用户名 公聊模式 私聊模式 查看当前在线用户 超时强踢 项目截图:
未来打算学完gin之后,改成web界面版的即时通信系统