左下角有文章目录
一、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)
}