左下角有文章目录

一、golang中的数据类型图

在这里插入图片描述

二、基本数据类型

1、整数类型

package main
import (
	"fmt"
	"unsafe"
)
var (
	a = 2
	b = 3
)
func main()  {
	//定义变量的多种方式
	var num1,num2,num3 int = 1,3,2
	num4,n,m :=  5,"chen",3.6
	fmt.Println(num3,num2,num1,num4,n,m)
	fmt.Println("----------------")
	fmt.Println("全局变量a和b:",a,b)
	//输出类型用printf("%T",变量)
	var num5 = 12
	fmt.Printf("num5的类型是:%T\n",num5)
	//变量占的字节数
	fmt.Println(unsafe.Sizeof(num5))
}

在这里插入图片描述

2、浮点类型

package main

import "fmt"

func main()  {
	//浮点类型
	//float32(4字节)、float64(8字节)
	var num1 float32 = 3.14
	var num2 float32 = 314E-2
	var num3 float32 = 0.314E+1
	fmt.Println(num1,num2,num3)
	var num4 float64 = 3.14
	fmt.Println(num4)
	//默认浮点数类型是float64,浮点数会有精度损失,推荐使用float
	num5 := 3.45
	fmt.Println(num5)
	fmt.Printf("%T",num5)
}

在这里插入图片描述

3、字符型

package main

import "fmt"

func main() {
	//字符类型使用byte表示,输出字符的时候会将对应的码值输出
	var c1 byte = 'a'
	fmt.Println(c1)
	//输出对应字符需要使用格式化输出“%c”
	fmt.Printf("%c", c1)
	//这里输出的类型是unit8
	//fmt.Printf("%T",c1)

	fmt.Println("---------------")
	//转义字符
	fmt.Println("换行:aaaaa\naaaaa")
	fmt.Println("向前挪一个字符:aaaaa\bbbbbb")
	fmt.Println("字表符以这个符号为分割线,每八位是一个字表符,这个空格大小取决于前面已有多少个字符:aaaaa\tbbbbb")
	fmt.Println("下面这个反斜杠r前面不换行的都清除:")
	fmt.Println("前面都清除:aaaaa\rbbbbb")
	fmt.Println("当想要输出双引号的时候可以在前面加上杠:\"双引号可以输出\"")
}

在这里插入图片描述

4、布尔类型

package main
import "fmt"
func main()  {
	var flag01 bool = true
	fmt.Println(flag01)

	var flag02 bool = false
	fmt.Println(flag02)

	var flag03 bool= 5<6
	fmt.Println(flag03)
}

在这里插入图片描述

5、字符串

package main

import "fmt"

func main()  {
	var string1 string = "你好,这是我创建的第一个string类型"
	fmt.Println(string1)
	//当字符串中有特殊类型字符时用``
	var string2 = `
		//反引号是使用
		func main()  {
			//浮点类型
			//float32(4字节)、float64(8字节)
			var num1 float32 = 3.14
			var num2 float32 = 314E-2
			var num3 float32 = 0.314E+1
			fmt.Println(num1,num2,num3)
			var num4 float64 = 3.14
			fmt.Println(num4)
			//默认浮点数类型是float64,浮点数会有精度损失,推荐使用float
			num5 := 3.45
			fmt.Println(num5)
			fmt.Printf("%T",num5)
		}
	`
	fmt.Println(string2)

	var string3 string= "字符串拼接:"+"abc"+"cde"
	string3 += "fg"
	fmt.Println(string3)

	var string4 string = "需要在多行进行拼接,加号必须放在最后:"+"aa"+
		"bb"+
		"cc"+
		"dd"
	fmt.Println(string4)
}

在这里插入图片描述

三、基本数据类型的转换

package main

import (
	"fmt"
	"strconv"
)

func main()  {
	fmt.Println("inr类型转换为float:")
	var num int = 100
	var numFloat float64 = float64(num)
	fmt.Println(numFloat)
	fmt.Printf("%T",numFloat)
	fmt.Println("--------------")
	fmt.Println("int类型之间的转换:")
	//int8的取值范围是-128~127,这里编译后会数值溢出,取得值会不准
	var num2 = 10
	var numInt8 int8 = int8(num2)+127
	fmt.Println(numInt8)
	//var num2Int8 int8 = int8(num2)+128//加的数值超过127,直接报异常
	//fmt.Println(num2Int8)

	fmt.Println("----int转换为string----")
	num3 := 10
	var str1 string = fmt.Sprintf("%d",num3)
	fmt.Printf("你的类型是%T,str1=%v\n",str1,str1)
	fmt.Println("----float转换为string----")
	num4 := 10.3
	var str2 string = fmt.Sprintf("%f",num4)
	fmt.Printf("你的类型是%T,str1=%v\n",str2,str2)
	fmt.Println("----bool转换为string----")
	var boolType bool
	var str3 string = fmt.Sprintf("%t",boolType)
	fmt.Printf("你的类型是%T,str1=%v\n",str3,str3)
	fmt.Println("----byte转换为string----")
	var byteType byte = 'a'
	var str4 string = fmt.Sprintf("%c",byteType)
	fmt.Printf("你的类型是%T,str1=%v\n",str4,str4)


	fmt.Println("--------使用方法将基本数据类型进行转换--------")
	fmt.Println(strconv.FormatInt(int64(num3),10))
	fmt.Println(strconv.FormatFloat(num4,'f',9,64))//f表示格式,9表示保留小数位9位,64表示float64
	fmt.Println(strconv.FormatBool(boolType))
}

在这里插入图片描述

package main

import (
	"fmt"
	"strconv"
)

func main()  {
	//string类型转换为其他基本数据类型的时候,如果是不合理的值,会转换为基本数据类型的默认值
	//String转换为bool
	//b是返回的bool类型,转换的值如果是true就会返回true,如果不是都会返回false
	var str1 string = "true"
	var b bool
	b, _ = strconv.ParseBool(str1)
	fmt.Println(b)

	//string类型转换为int64类型
	//10代表进制数,64指代int64
	var str2 string = "23"
	var num int64
	num , _ = strconv.ParseInt(str2,10,64)
	fmt.Println(num)

	//string类型转换为float64/float32
	var s2 string = "19"
	var float float64
	float,_= strconv.ParseFloat(s2,64)
	fmt.Println(float)
}

在这里插入图片描述

四、复杂数据类型

1、指针

package main

import "fmt"

func main() {
	var i int = 1
	fmt.Println(&i)//输出地址
	//定义一个指针变量,实际指针变量存的就是内存空间的地址
	var num *int = &i
	fmt.Println(&num)
	//获取指针变量的值在前面加“*”
	fmt.Println(*num,*&i)
	fmt.Println("----------")
	//注意:指针指向的数据类型必须匹配
	//指针指向的值也可以重新指定
	var num2 int = 2
	var point *int = &num2
	fmt.Println(point)//输出的是指针,也就是地址
	fmt.Println(&point)//能不能输出指针的指针,地址的地址,可以
	fmt.Println(*point)//输出地址指向的值
	//改变地址指向的值,上面的赋值也跟着改变
	*point = 3
	fmt.Println(num2)//num2变为3
	//值改变后地址一样吗?一样
	fmt.Println(point)
	fmt.Println(&point)
}

2、结构体

(1)if

package main
import "fmt"
func main() {
	var i int
	fmt.Scanln(&i)
	if i<14 {
		fmt.Printf("只有%d",i)
	}else {
		fmt.Println("dddd")
	}

	if i := 13;i<17{
		fmt.Println(i)
	}
}

(2)switch

package main
import "fmt"

func main() {
	var score float64
	fmt.Scanln(&score)
	switch score {
	case 10:
		fmt.Println("sssssss")
		fallthrough//会将下面case执行,称为switch穿透
	case 12:
		fmt.Println("sss")
	default:
		fmt.Println("---------")
	}
}

(3)for

package main
import "fmt"
func main() {
	var sum int = 0
	for i:= 0; i<2;i++{
		sum += i
	}
	fmt.Println(sum)
}

for range

//使用for range,python有类似的用法
package main
import "fmt"
func main() {
	var str string = "chen"
	for i,value:=range str{
		//fmt.Println(i,value)
		fmt.Printf("%d,%c\n",i,value)
	}
}

(4)goto:跳转至某一行,一般与if一起使用

package main
import "fmt"
func main() {
	fmt.Println("2222222222")
	fmt.Println("2222222222")
	fmt.Println("2222222222")
	goto a		//指定执行到哪一行,用“XXX:”指定
	fmt.Println("666")
	a :
		fmt.Println("2222222222")
}

3、函数

(1)函数的定义

package main
import "fmt"

//函数的定义
func sum(i int,n int) int{
	theSum := i+n
	return theSum
}
//不指定函数的参数数值,任意数值
func test(args...int){
	for i:=0;i<len(args);i++ {
		fmt.Println(args[i])
	}
}
//形参不能改变实参的值
func change(num int)  {
	num = 4
}
//可以改变地址中的值
func changeByAddress(num *int){
	*num = 2
}
//自己定义数据类型、函数类型
type myInt int
type myFunc func(int)
func functionIsCan(num1 myInt,num2 myInt,testFunc myFunc)  {
	
}

func main() {
	fmt.Println(sum(1,2))
	fmt.Println("-----------------")
	test(1,2,4,3,54,3)
	fmt.Println("-----------------")
	num := 3
	change(num)
	fmt.Println(num)
	fmt.Println("-----------------")
	changeByAddress(&num)
	fmt.Println(num)
	fmt.Printf("%T",sum(2,3))
}

(2)匿名函数(函数只被调用一次)的定义

package main

import "fmt"

//方法或类型第一个字母大写代表外部可访问,这里将匿名行数定义为全局匿名函数
var OverallSituationFunction = func (num3 int,num4 int) int {
	return num4+num3
}

func main() {
	//1:调用的时候直接传参
	result := func (num int,n int) int{
		return num+n
	}(10,20)		//后面直接传参
	fmt.Println(result)

	//2:定义一个匿名函数,函数也是一种数据类型,所以可以定义一个变量来接收
	result2 := func(num int,num2 int) int{
		return num+num2
	}
	fmt.Println(result2(2,3))
}

(3)闭包

package main
import "fmt"

//闭包
func getSum() func (int) int{
	var sum int  = 0
	return func (num int) int{
		sum = sum + num
		return sum
	}
}

func main() {
	f := getSum()
	fmt.Println(f(1))
	fmt.Println(f(1))
	fmt.Println(f(2))

	fmt.Printf("%T\n",getSum())
	fmt.Printf("%T",f)
}

在这里插入图片描述

(4)defer关键字

package main
import "fmt"

func main() {
	fmt.Println("------执行main函数-----")
	fmt.Println(deferKeyword())
}
//使用defer时,会将defer压入栈中,先执行下面的,执行完毕之后再从 栈中取出
//使用场景,在需要关闭某个资源的时候,可以直接写下这个语句进行执行之后关闭
func deferKeyword() int{
	fmt.Println("-------开始执行-------")
	defer fmt.Println(23)
	defer fmt.Println(24)
	fmt.Println("-----------")
	var i int = 1
	fmt.Println("自定义函数",i)
	return i
}

在这里插入图片描述

4、字符串函数

package main

import (
	"fmt"
	"strconv"
	"strings"
)

//字符串函数的使用
func main() {
	//len函数的使用
	str := "golang你好"
	fmt.Println(len(str))
	//使用len进行输出
	for i:=0;i<len(str);i++{
		fmt.Printf("%c\n",str[i])
	}
	fmt.Println("------使用range-----")
	//使用range
	for i,value  := range str{
		fmt.Printf("%d %c\n",i,value)
	}
	//使用切片
	fmt.Println("-----使用切片------")
	r:=[]rune(str)
	for i:=0;i<len(r);i++ {
		fmt.Printf("%c\n",r[i])
	}


	//字符串转为整数
	num,_ := strconv.Atoi("666")		//返回整数或者报错信息
	fmt.Println(num)

	//整数转为字符串
	var str1 string = strconv.Itoa(64)
	fmt.Println(str1)

	//统计一个字符串中有多少个子串
	fmt.Println(strings.Count("golangandjavaga","ga"))
	//不区分大小写进行string比较
	fmt.Println(strings.EqualFold("go","GO"))
	//区分大小写
	fmt.Println("go"=="Go")
	//子串在字符串中第一次出现的索引值
	fmt.Println(strings.Index("golandandjava","ay"))
	//字符串的替换
	fmt.Println(strings.Replace("golangandjava","g","o",2))
	//按照指定的标识符拆分
	fmt.Println(strings.Split("string-s-ni","-"))
	//把字符串的字符按照字母大小写转换
	fmt.Println(strings.ToLower("GO"))
	fmt.Println(strings.ToUpper("go"))
	//字符串两边的空格去掉
	fmt.Println(strings.TrimSpace("     hello  "))
	//字符串两边指定的字符去掉
	fmt.Println(strings.Trim("string","s"))
	fmt.Println(strings.TrimLeft("strings","s"))
	fmt.Println(strings.TrimRight("strings","s"))

	//判断字符串是否已指定字符开头
	fmt.Println(strings.HasPrefix("http://www.baidu.com","http"))
	fmt.Println(strings.HasSuffix("http://www.baidu.com","com"))
}

5、new函数

package main
import "fmt"
//new函数,创建基本类型的地址,也就是创建的值是指针
func main() {
	typeInt := new(int)
	fmt.Println(typeInt)
	fmt.Println(*typeInt)
	fmt.Println(&typeInt)
	fmt.Printf("%T",typeInt)
}

6、异常处理

package main

import (
	"errors"
	"fmt"
)

func main() {
	test()
	fmt.Println("--------")
	var err error = test2()
	if err != nil {
		fmt.Println(err)
		panic(any(err))//这里参数类型是any,为什么还要强制转换类型
	}
}
//捕获异常
func test() {
	defer func() {
		a:= recover()
		fmt.Println(a)
	}()
	num1 := 1
	num2 := 0
	num := num1/num2
	//num := 1/0	//直接写的异常会报错,异常也无法捕获
	fmt.Println(num)
}
//自定义异常
func test2() error{
	num1 := 1
	num2 := 0
	if num2 == 0{
		return errors.New("出错了~")
	}
	num := num1/num2
	fmt.Println(num)
	return nil
}

7、数组

(1)一维数组

package main

import "fmt"
//数组的定义
func main() {
	var a[5] int
	a[0] = 34
	a[1] = 43
	a[2] = 44
	a[3] = 2
	a[4] = 2
	fmt.Println(a)
	fmt.Printf("%p\n",&a)	//数组的地址指向是第一个地址
	fmt.Printf("%p\n",&a[0])
	fmt.Printf("%p\n",&a[1])
	fmt.Printf("%p\n",&a[2])
	fmt.Printf("%p\n",&a[3])
	fmt.Printf("%p\n",&a[4])
	//数组初始化
	var arr1 [3]int = [3]int{1, 2, 3}
	fmt.Println(arr1)
	var arr2 =[3] int{1,2,3}
	fmt.Println(arr2)
	var arr3 = [...] int {1,2,3,4}
	fmt.Println(arr3)
	var arr4 = [...]int{2:54,3:32,4:43}
	fmt.Println(arr4[2])
	fmt.Println(arr4)
	//数组形参改变的值不会影响实参
	var arr5[1] int
	arr5[0] = 2
	arr(arr5)
	fmt.Println(arr5)//2

}
func arr(a [1]int) {
	a[0] = 8
}

(2)二维数组

package main
import "fmt"

func main() {
	//定义二维数组
	var arr1 [2][2] int
	fmt.Println(arr1)

	var arr2 [2][3] int = [2][3] int {{2,3,4},{3,3,4}}
	fmt.Println(arr2)
}

8、切片slice

(1)切片基于数组的定义

package main
import "fmt"	

func main() {
	//切面是建立在数组之上
	var intArr [6]int = [6]int{2,3,4,5,6,7}
	//从1开始,2结束(不包含2)
	slice1 := intArr[1:3]
	fmt.Println(slice1)
	fmt.Println(len(slice1))
	fmt.Println(cap(slice1))//切片容量,大约是len的2倍左右
	fmt.Println(cap(intArr))
	fmt.Printf("%T",cap(intArr))
}

(2)通过make定义切片、通过定义数组的方式定义切片

package main
import "fmt"

func main() {
	//通过make定义切片
	slice := make([]int,2,30)
	fmt.Println(slice)
	fmt.Println(len(slice))
	fmt.Println(cap(slice))
	slice[0] = 66
	slice[1] = 56
	fmt.Println(slice)
	//通过定义数组的方式定义切片
	slice2 := []int{1,2,3,4}
	fmt.Println(slice2)
	fmt.Println(len(slice2))
	fmt.Println(cap(slice2))
}

(3)切片的遍历

package main
import "fmt"

func main() {
	slice := []int {1,3,4,5}
	//正常的for
	for i := 0;i<len(slice);i++{
		fmt.Println(slice[i])
	}
	fmt.Println("--------------")
	//通过for:range
	for i,value := range slice {
		fmt.Println(i,value)
	}
}

(4)切片的新增

package main
import "fmt"

func main() {
	//切片追加的是新的地址
	slice := []int {1,2,3,4}
	fmt.Printf("切片的地址:%d\n",&slice)//可以看出切片指向的是数组
	fmt.Println("-------------")
	fmt.Println(slice)
	fmt.Println(&slice[0])
	slice = append(slice,3 )	//新增后数组的地址指向发生变化
	fmt.Println(slice)
	fmt.Println(&slice[0])	
}

9、map

(1)定义map的三种方式

package main
import "fmt"

func main() {
	//定义map变量
	var mapType1 map[int]string
	//初始化map变量,初始化后正式分配空间
	mapType1 = make(map[int]string,10)
	mapType1[1] = "ddd"
	mapType1[98] = "ijj"
	mapType1[43] = "add"
	fmt.Println(mapType1)	//输出:map[1:ddd 43:add 98:ijj],证明存放的数据是无序的

	//定义map2
	var mapType2  = make(map[int]string)
	mapType2[2] = "099"
	mapType2[3] = "34"
	fmt.Println(mapType2)

	//定义方式3
	mapType3 := make(map[int]string)
	mapType3[2] = "3443"
	fmt.Println(mapType3)
}

(2)map的相关操作

package main

import "fmt"
//map的相关操作
func main()  {
	mapType1 := make(map[int]string)
	mapType1[666] = "中国"
	mapType1[777] = "jij"
	println(len(mapType1))
	delete(mapType1,777)	//删除
	println(len(mapType1))
	fmt.Println(mapType1)
	//重新指向新的map地址,达到清空变量
	mapType1 = make(map[int]string)
	fmt.Println(mapType1)
	//遍历map
	mapType1[666] = "中国"
	mapType1[777] = "jij"
	for i,value := range mapType1 {
		fmt.Println(i,value)
	}
	//定义嵌套map
	var mapType2  = make(map[int] map[int]string)
	mapType2[33] = make(map[int]string,3)
	mapType2[33][1] = "班级1"
	mapType2[33][2] = "班级2"
	mapType2[33][3] = "班级3"
	fmt.Println(mapType2)
}

10、结构体

(1)结构体的定义

package main

import "fmt"
//定义Teacher结构体
type Teacher struct {
	Name string
	age int
	sex string
}

type te Teacher //起别名

func main()  {
	t :=  Teacher{
		Name: "chen",
		age:  12,
		sex:  "男",
	}
	fmt.Println(t)
	fmt.Println(&t.age)
	t.age = 16
	fmt.Println(t)
	fmt.Println(t.age)
	fmt.Println(&t.age)
	fmt.Println("-------")
	//通过new方法指向地址
	var t2 *Teacher = new(Teacher)
	//通过指向地址直接赋值
	//var t2 *Techer = &Techer{}
	(*t2).age = 12
	t2.Name = "chen"
	fmt.Println(t2)
}

(2)结构体绑定方法

package main
import (
	"fmt"
)
//结构体绑定方法
type person struct{
	name string
	age int
}
//结构体绑定方法,在golang中方法与函数是不一样的
func (p person) test(){
	p.age = 13
	fmt.Println(p.age)
}
//String方法,在调用输出结构体的时候,会将此方法输出,与java的toString()方法类似,这里的String首字母必须大写
func (p person) String() string  {
	str:=fmt.Sprintf("调用了string方法:name=%v,age=%v",p.name,p.age)
	return str
}
func main() {
	p := person{
		name: "chen",
		age:  12,
	}
	//调用绑定的方法
	p.test()
	//值传递:值不会改变实际中的值,引用传递:改变地址中的值
	//值传递,绑定的方法中改变了值,不会改变实际结构体中的值
	fmt.Println(p.age)
	fmt.Println(p)

	t := Teacher{Name:"ji",age:12,sex:"n"}
	fmt.Println(t)
}

(3)结构体参数赋值的方式

package main
import "fmt"

type animal struct {
	name string
	age int
}

func (animal animal) String() string {
	str:=fmt.Sprintf("执行string方法:name的值为%v,age为%v",animal.name,animal.age)
	return str
}

func main() {
	//构造题传递值
	//方式1:通过名称指定值
	animal1 := animal{
		name: "chen",
		age:  12,
	}
	fmt.Println(animal1)
	//方式2:按照构造体的类型顺序进行传值,与java构造器写法类似
	var animal2 animal = animal{"chen",12}
	fmt.Println(animal2)
	//方式3:通过new一个内存空间进行赋值,赋值后输出的为地址的值,需要加*输出实际的值
	var animal3 = new(animal)
	animal3.age = 12
	animal3.name = "shi"
	fmt.Println(*animal3)
	//方式4:通过结构体指针进行赋值
	var animal4 *animal  = &animal{"chen",12}
	fmt.Println(*animal4)
}

(4)跨包创建结构体实例

结构体首字母必须大写,大写其他包才能使用,然后导入包进行创建,当创建的结构体首字母不为大写,而又想创建该实例,可以通过“工厂模式”创建一个新的结构体,通过新的结构体指向旧的,利用新的结构体来创建。

11、面向对象

(1)继承

package main
import "fmt"

//继承
type theAnimal struct {
	name string
	age int
}

func (theAnimal theAnimal) String() string {
	str:=fmt.Sprintf("这是动物类")
	return str
}
type cat struct {
	//继承动物类
	theAnimal
}

func (cat cat) String() string{
	str := fmt.Sprintln("这个猫类")
	return str
}

func main() {
	cat := cat{theAnimal{
		name: "chen",
		age:  1,
	}}
	fmt.Println(cat)
	fmt.Println(cat.age)
}

12、文件操作

(1)打开文件和关闭文件

open, err := os.Open("C:/Users/Administrator/Desktop")
fmt.Printf("文件:%v,错误:%v\n",open,err)
//关闭文件
open.Close()

(2)一次性读取文件

一般用于小文件

//一次读取文件
file, _ := ioutil.ReadFile("C:/Users/Administrator/Desktop/ee.txt")
fmt.Println(string(file))

(3)缓存区读取文件

//缓冲读取文件
//打开文件
f,err:= os.Open("C:/Users/Administrator/Desktop/ee.txt")
if err != nil{		//打开失败
	fmt.Println("文件打开失败")
}
//函数执行完毕关闭文件
defer f.Close()
//创建一个读取器
reader := bufio.NewReader(f)
for {
	readString, err := reader.ReadString('\n')
	if err == io.EOF{
		break
	}
	fmt.Println(readString)
}
fmt.Println("文件读取完成")

(4)缓冲区写入文件

package main
import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	file, err := os.OpenFile("C:/Users/Administrator/Desktop/ee.txt", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil{
		fmt.Println("打开文件失败")
		return
	}
	defer file.Close()
	//调用带缓存的“写",数据会先写入缓存中
	writer := bufio.NewWriter(file)
	for i:=0; i<5;i++  {
		writer.WriteString("中国\n")
	}
	//调用Flush()方法,将缓存数据写入文件中
	writer.Flush()
	查看文件权限
	//s := os.FileMode(0666).String()
	//fmt.Println(s)
}

13、协程

(1)协程的定义

package main
import (
	"fmt"
	"strconv"
	"time"
)

func test()  {
	for i := 1; i<=10; i++ {
		fmt.Println("test函数" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main() {
	//定义协程
	go test()
	//main是主协程,当主协程跑完时,从协程也会跟着停止
	for i:=1; i<5; i++ {
		fmt.Println("main函数",strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

(2)定义多个协程

package main
import (
	"fmt"
	"time"
)
//定义多个协程
func main() {
	//匿名函数+外部变量 = 闭包
	for i:=1;i<=5;i++ {
		go func(n int) {
			fmt.Println(n)
		}(i)
	}
	time.Sleep(time.Second)
}

(3)使用WaitGroup控制协程

package main
import (
	"fmt"
	"time"
)
//定义多个协程
func main() {
	//匿名函数+外部变量 = 闭包
	for i:=1;i<=5;i++ {
		go func(n int) {
			fmt.Println(n)
		}(i)
	}
	time.Sleep(time.Second)
}

(4)互斥锁多个协程控制一个数据

不加互斥锁当协程数量多的时候,可能会造成数据混乱

package main
import (
	"fmt"
	"sync"
)
//多个协程操纵一个数据
var totalNum int
var wg sync.WaitGroup	//互斥锁,适用于读写次数不确定的情况
var lock sync.Mutex
func add()  {
	defer wg.Done()
	for i:=0; i<100000;i++ {
		lock.Lock()
		totalNum = totalNum + 1
		lock.Unlock()
	}
}

func sub()  {
	defer wg.Done()
	for i:=0; i<100000;i++ {
		lock.Lock()
		totalNum = totalNum - 1
		lock.Unlock()
	}
}

func main() {
	wg.Add(2)
	go add()
	go sub()
	wg.Wait()
	fmt.Println(totalNum)
}

(5)defer+recover()捕获协程中的异常

package main

import (
	"fmt"
	"time"
)

//协程中出现错误,用defer+recover()捕获异常
func add(num int)  {
	for i:= 0;i<10;i++{
		num += i
	}
}
func div(num int)  {
	defer func(){
		err := recover()
		if err != nil{
			fmt.Println("出错了",err)
		}
	}()
	var num1 int = 1
	var num2 int = 0
	num = num1/num2
}

func main() {
	go add(0)
	go div(0)

	time.Sleep(time.Second)
}

14、管道

(1)管道的定义

package main

import "fmt"
//管道是采用数据结构中的队列--先进先出
//具有线程安全
//管道也有基本数据类型的定义
func main() {
	//管道的定义,在定义变量的基础上加上chan
	var intChan chan int
	//与数组一样,在使用之前需要使用make进行初始化和设置初识容量
	intChan = make(chan int,3)
	//添加
	intChan<-20
	num := 21
	intChan<-num
	fmt.Println(len(intChan))
	fmt.Println(cap(intChan))
}

(2)管道与协程协同工作

下面代码定义的管道容量是10,但是设置了两个协程,边读边写,只要管道没有一次性读入超过容量数的数值,就不会报阻塞异常

package main

import (
	"fmt"
	"sync"
	"time"
)
//创建写的方法
func write2(intChan chan int)  {
	for i:=0; i<15;i++  {
		intChan <- i
		fmt.Printf("写入数据%v成功\n",i)
		time.Sleep(time.Second)
	}
	defer close(intChan)
	defer wg.Done()
}
//创建读的方法
func read(intChan chan int)  {
	for v := range intChan{
		fmt.Printf("读出数据%v成功\n",v)
		time.Sleep(time.Second)
	}
	defer wg.Done()
}

var wg sync.WaitGroup

func main() {
	var intChan chan int	//定义int类型的管道
	intChan = make(chan int,10)		//初始化和设置容量
	wg.Add(2)		//计数器,初始值为2
	go write2(intChan)
	go read(intChan)
	wg.Wait()		//设置为阻塞状态,当WaitGroup为0时释放
}

(3)创建只读、只写管道

package main
import "fmt"

func main() {
	//定义只写管道
	var intChan chan<- int
	intChan = make(chan int,10)
	intChan <- 10
	close(intChan)
	//num := <-intChan	//读出会报错
	for i := range intChan {
		fmt.Println(i)
	//报错:cannot range over intChan (variable of type chan<- int) (receive from send-only channel)
	}
	//只读管道
	var onlyReadchan <-chan int
	//onlyReadchan<-12	//Invalid operation: onlyReadchan<-12 (send to receive-only type <-chan int)
	if onlyReadchan != nil{
		num := <-onlyReadchan
		fmt.Println(num)
	}
}

(4)使用select选取管道

用法与switch基本一致,上面传下来的是什么值,就会执行什么值,会优先选先传下来的,也就是这个代码不管怎么运行,都会输入10

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup
//select选择管道
func main() {
	wg.Add(2)
	var intChan chan int
	intChan = make(chan int ,1)
	go func(){
		time.Sleep(time.Second)
		intChan <- 10
		defer close(intChan)
		defer wg.Done()
	}()

	var stringChan chan string
	stringChan = make(chan string ,1)
	go func() {
		defer wg.Done()
		time.Sleep(time.Second*2)
		stringChan <- "20"
		close(stringChan)
	}()

	select {
		case v:= <-intChan:
			fmt.Println(v)
		case v:= <-stringChan:
			fmt.Println(v)
		//default:		//若添加了这个,则每次都会执行这个,上面管道是设置了阻塞时间的
		//	fmt.Println("管道都还在阻塞状态")
	}
	wg.Wait()
}

15、golang基于TCP创建客户端,向服务端发送数据

客户端需要先肯定是需要连接ip和端口号,连接成功后才能发送消息,而服务端,则必须时刻监听着客户端要连接的ip和端口号,这样在客户端发送消息或者重新建立连接的时候,服务端才能获取到客户端的数据

(1)Client代码

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

func main() {
	dial, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println("网络连接失败",err)
		return
	}
	fmt.Println("连接成功:",dial)

	//终端输入信息
	reader := bufio.NewReader(os.Stdin) //标准输入
	readString, err := reader.ReadString('\n') //读入数据
	if err != nil{
		fmt.Println("读入输入数据失败",err)
		return
	}
	//发送数据
	write, err := dial.Write([]byte (readString))
	if err != nil {
		fmt.Println("发送客户端输入数据失败")
		return
	}
	fmt.Println("发送客户端数据成功",write)
}

(2)Server代码

package main

import (
	"fmt"
	"net"
)

func main() {
	fmt.Println("服务端启动了...")
	listen, err := net.Listen("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println("监听失败",err)
		return
	}
	for{
		accept, err := listen.Accept()
		if err != nil{
			fmt.Println("等待客户端连接失败")
		}else {
			fmt.Printf("等待客户机连接accept=%v,收到客户端信息:%v\n",accept,accept.RemoteAddr().String())

		}
		//创建一个协程来获取一个客户端的数据
		go process(accept)
	}
}

func process(accept net.Conn)  {
	//关闭连接
	defer accept.Close()
	//获取数据
	buf := make([]byte,1024)	//创建分片
	read, err := accept.Read(buf)	//读取数据,返回成功个数或错误
	if err != nil {
		fmt.Println("读取失败")
		return
	}
	fmt.Println(string(buf[0:read]))
}

通过for循环来控制服务端保持开启状态,并且处于监听状态,当客户端重新连接或发送数据,服务端都可监听到。使用协程来控制一个客户端的数据读取,这样子可监听多个客户端

16、反射

(1)通过反射获取基本数据类型的类型和数值

package main

import (
	"fmt"
	"reflect"
)

func testReflex(i interface{}){
	//获取数据类型
	reType := reflect.TypeOf(i)
	fmt.Println(reType)
	//获取变量类别
	kind := reType.Kind()
	fmt.Println(kind)
	//获取变量值
	reValue := reflect.ValueOf(i)
	fmt.Println(reValue)
	//改变变量的值
	reValue.Elem().SetFloat(12)

}

func main() {
	num := 3.4
	testReflex(&num)
	fmt.Println(num)
}