go 入门语法学习

在学这门语言之前,根据Java我整理了几个方向

 

目录

1、基本类型、string

1.1整数类型

1.2浮点类型

1.3字符类型

1.4布尔类型

1.5字符创类型

基本方法:strconv

指针:

关键字

2、运算

3、if、else switch

4、method方法、函数

5、数组、集合

6、面向对象

7、文件,流处理

8、线程、并发

9、多态、反射

10、网络

11、框架、第三方jar包

12、怎么集成中间件?


package main

import "fmt"

// 类型可以被遗漏
var v1 = 100
var v2 = 2.3

var (
	v3 = 1
	v4 = 2.5
	v5 = "str"
)
var x1, x2, x3 = 10, "str", 2.36

//error non-declaration statement outside function body
//n3, n4 := "xx", 325

func main() {
	var age int
	age = 18
	fmt.Println("年龄: ", age)

	var age1 int = 18
	fmt.Println(age1)

	var age2 int = 160
	fmt.Println(age2)
	n6, height := 6.9, 100.6

	fmt.Println(n6)
	fmt.Println(height)
	fmt.Println(v1)
	fmt.Println(x1)

	//must used
	n1, n2 := "xx", 325
	fmt.Println(n1)
	fmt.Println(n2)
}

年龄:  18
18          
160         
6.9         
100.6       
100         
10          
xx          
325         

1、基本类型、string

1.1整数类型

//基本数据类型 int8 int16 int32 int64
	fmt.Println("基本数据类型")
	//整数
	var num1 int8 = -128
	var num2 int8 = 127
	fmt.Println(num1, num2)

	//自然数 uint8 uint16 uint32 uint64
	var num3 uint8 = 0
	var num4 uint8 = 255
	fmt.Println("自然数", num3, num4)

	//等价int32
	var num5 rune = 32
	fmt.Println("rune", num5)
	var num6 int = 32
	fmt.Println("int", num6)

	//等价uint8
	var num7 byte = 255
	fmt.Println("rune", num7)

	fmt.Println("字节数:", unsafe.Sizeof(num6))


基本数据类型
-128 127    
自然数 0 255
rune 32     
int 32      
rune 255    
字节数: 8  

1.2浮点类型

var f1 float32 = 256.000000916
	var f2 float64 = 256.000000916
	fmt.Println(f1)
	fmt.Println("float32存在精度问题,建议使用float64", f2)

256                                               
float32存在精度问题,建议使用float64 256.000000916

1.3字符类型

查看字符编码(UTF-8)

var c1 byte = 'a'
	fmt.Println(c1)
	var c2 byte = '6'
	fmt.Println(c2)
	var c3 byte = '('
	fmt.Println(c3 + 20)
	var c4 int = '中'
	fmt.Println(c4)
	var c5 byte = 'A'
	fmt.Printf("c5对应的具体的字符为:%c", c5)


97
54
60
20013
c5对应的具体的字符为:A


//\n  换行
	fmt.Println("aaa\nbbb")
	//\b 退格
	fmt.Println("aaa\bbbb")
	//\r 光标回到本行的开头,后续输入就会替换原有的字符
	fmt.Println("aaaaa\rbbb")
	//\t 制表符
	fmt.Println("aaaaaaaaaaaaa")
	fmt.Println("aaaaa\tbbbbb")
	fmt.Println("aaaaaaaa\tbbbbb")
	//\"
	fmt.Println("\"Golang\"")

1.4布尔类型

var flag01 bool = true

1.5字符创类型

var str1 string = "123" + "yyy"
	str1 = str1 + "xxx"
	fmt.Println(str1)

对应方法:

len(str) 

 for-range 

 r:=[]rune(str)  

strconv.Atoi

strconv.Itoa

strings.Contains

strings.Count

strings.EqualFold

strings.lndex

strings.Replace

strings.Split

strings.ToLower

strings.ToUpper

strings.TrimSpace

strings.Trim

strings.TrimLeft

strings.TrimRight

strings.HasPrefix

strings.HasSuffix

基本方法:strconv

s1 := 123
	s2 := strconv.FormatInt(int64(s1), 10)
	fmt.Println(s1, s2)
s1 := 123
	s2 := strconv.FormatInt(int64(s1), 10)
	s3,_ := strconv.ParseInt(s2, 10, 64)
	fmt.Println(s1, s2, s3)

日期和时间

now := time.Now()
	fmt.Printf("%v ~~~ 对应的类型为:%T\n", now, now)
	datestr2 := now.Format("2006/01/02 15/04/05")
	fmt.Println(datestr2)
	datestr3 := now.Format("2006 15:04")
	fmt.Println(datestr3)

2023-04-01 04:16:44.1690965 +0800 CST m=+0.018000001 ~~~ 对应的类型为:time.Time
2023/04/01 04/16/44
2023 04:16

指针:

1.& 取内存地址
2.* 根据地址取值

//&符号+变量 就可以获取这个变量内存的地址
	fmt.Println(&s1) //0xc0000a61b0

    var ps1 *int = &s1
	fmt.Println("ps1值为:", ps1)
	fmt.Println("ps1本身这个存储空间的地址为:", &ps1)
	fmt.Println("ps1指向的数值为:", *ps1)
	fmt.Println("s1指向的数值为:", *&s1)

0xc0000a61b0
ps1值为: 0xc0000a61b0
ps1本身这个存储空间的地址为: 0xc0000ca020
ps1指向的数值为: 123
s1指向的数值为: 123

可以自定义数据类型

var m1 myInt = 35
	fmt.Println(m1)
}

type myInt int
type myfunc func(int, int)

关键字

defer : 延迟执行,在方法结束时执行

在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字

遇到defer关键字,会将后面的代码语句压入栈中,也会将相关的值同时拷贝入栈中,不会随着函数后面的变化而变化

2、运算

跟java的差不多

3、if、else switch for循环

if s1 > 10 {
		fmt.Println(s1 > 10)
	}
	if s4 := 20; s4 > 10 {
		fmt.Println(s4 > 20)
	}
	if s5 := 10; s5 < 10 {
		fmt.Println("s5 < 10 :", s5 < 10)
	} else if s5 == 10 {
		fmt.Println("s5 == 10 :", s5 == 10)
	} else {
		fmt.Println("s5 > 10 :", s5 > 10)
	}


true
false
s5 == 10 : true
var score int = 187

	switch score {
	case 187:
		fmt.Println(score)
	default:
		fmt.Println("default")

	}
var sum int = 0
	for i := 1; i <= 5; i++ {
		sum += i
	}
	fmt.Println(sum)

    var str string = "xsdasdadsa"
	for i, value := range str {
		fmt.Printf("索引为:%d,具体的值为:%c \n", i, value)
	}
var sum int = 0
	for i := 1; i <= 5; i++ {
		sum += i
	}
	fmt.Println(sum)

	var str string = "xsdasdadsa"
	for i, value := range str {
		fmt.Printf("索引为:%d,具体的值为:%c \n", i, value)
		if i == 1 {
			continue
		}
		if i == 3 {
			return
		}
		if i == 4 {
			goto label
		}
		if i == 2 {
			break
		}
	}

	fmt.Println(123)
label:
	fmt.Println(123)

4、函数: 基本类型在栈帧开辟空间

不支持重载,支持可变参数,基本数据类型和数组默认值拷贝,别的方法改动,不会改到原来的值

如果想改变值传递的话,传递地址即可: &引用

也可以嵌套函数,即进行函数传递

Go 语言也有 Public 和 Private 的概念,粒度是包。如果类型/接口/方法/函数/字段的首字母大写,则是 Public 的,对其他 package 可见,如果首字母小写,则是 Private 的,对其他 package 不可见

注意:go执行函数和java不一样,java是单线程的,go增加了协程的概念

	nu1, nu2 := 10, 10
	sum1, _ := cal(nu1, nu2)
	fmt.Println(sum1)
	fmt.Println(cal(nu1, nu2))
	fmt.Println(nu1, nu2)

}

func cal(n1 int, n2 int) (int, string) {
	n1 = 32
	n2 = 18
	return n1 + n2, ""
}

也可以嵌套函数,即进行函数传递

import (
	"fmt"
	"strconv"
	"unsafe"
)
var name = test()
func test() int {//执行1
	fmt.Println("init")
    return 10
}
func init()  {//执行2
	fmt.Println("init")
}

func main() {//3 
	fmt.Println("main")
}

匿名函数

result := func(nl1 int, nl2 int) int {
		return 10
	}(10, 20)
	fmt.Println(result)

	sub := func(nl1 int, nl2 int) int {
		return 10
	}
	result2 := sub(1, 2)
	fmt.Println(result2)
	result3 := sub(3, 2)
	fmt.Println(result3)

闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体

匿名函数中引用的那个变量会一直保存在内存中,可以一直使用

内置函数

new函数

        分配内存,主要用来分配值类型(int系列, float系列, bool, string、数组和结构体struct)

make函数:

        分配内存,主要用来分配引用类型(指针、slice切片、map、管道chan、interface 等)

错误处理

defer+recover机制处理错误

也可以自定义错误

5、数组、集合

和java差不多

增加了切片slice, 其实就是java里的截取数组,切片执行对应的原数组地址

map:

//定义map变量:
        var a map[int]string
        //只声明map内存是没有分配空间
        //必须通过make函数进行初始化,才会分配空间:
        a = make(map[int]string,10) //map可以存放10个键值对
        //将键值对存入map中:
        a[20095452] = "张三"
        a[20095387] = "李四"
        a[20097291] = "王五"
        a[20095387] = "朱六"
        a[20096699] = "张三"
        //输出集合
        fmt.Println(a)

        //创建map
        //方式1:
        //定义map变量:
        var a map[int]string
        //只声明map内存是没有分配空间
        //必须通过make函数进行初始化,才会分配空间:
        a = make(map[int]string,10) //map可以存放10个键值对
        //将键值对存入map中:
        a[20095452] = "张三"
        a[20095387] = "李四"
        //输出集合
        fmt.Println(a)
        //方式2:
        b := make(map[int]string)
        b[20095452] = "张三"
        b[20095387] = "李四"
        fmt.Println(b)
        //方式3:
        c := map[int]string{
                20095452 : "张三",
                20098765 : "李四",
        }
        c[20095387] = "王五"
        fmt.Println(c)

6、面向对象

package main

import "fmt"

type Teacher struct {
	name   string
	school string
}

type Student struct {
	name   string
	school string
}

func (t Teacher) test() {
	t.name = "test"
	fmt.Println(t.name)
}

//方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问
func (t *Teacher) test2() {
	(*t).name = "test1"
	fmt.Println(t.name)
	t.name = "test2"
	fmt.Println(t.name)
}

func main() {
	//实例、构造方法
	var t1 Teacher = Teacher{"name", "school"}
	t1.name = "我是老师"
	t1.school = "我教的是语文"

	fmt.Println(t1.name, t1.school)

	var s1 Student = Student{"name1", "school1"}
	fmt.Println(s1)

	//类型转换:属性名一致
	s1 = Student(t1)
	fmt.Println(s1)

	var s2 Student = Student{"name2", "school2"}
	fmt.Println(s2)
	s1 = Student(s2)
	fmt.Println(s1)

	t1.test()

	fmt.Println(t1.name) //test方法虽然修改了name值,但是main方法未变化,类型的值传递

	t1.test2()

	fmt.Println(t1.name) //test方法虽然修改了name值,但是main方法未变化,类型的值传递

	fmt.Printf("t1的地址:%p", &t1)
	fmt.Printf("t1的值:%v", t1)
	fmt.Printf("t1的类型:%T", t1)
	fmt.Println()
	fmt.Println(*&t1)

	var t2 Teacher = Teacher{school: "s", name: "sd"}   //不按顺序的构造方法
	var t3 *Teacher = &Teacher{school: "s", name: "sd"} //不按顺序的构造方法

	fmt.Println(t2)
	fmt.Println(*t3)
}

构造方法

属性

方法,封装

继承:提高代码复用、易扩展

Go 语言也有 Public 和 Private 的概念,粒度是包。如果类型/接口/方法/函数/字段的首字母大写,则是 Public 的,对其他 package 可见,如果首字母小写,则是 Private 的,对其他 package 不可见。

//定义动物结构体:
type Animal struct {
	Age    int
	Weight float32
}

//给Animal绑定方法:喊叫:
func (an *Animal) Shout() {
	fmt.Println("我可以大声喊叫")
}

给Animal绑定方法:自我展示:
func (an *Animal) ShowInfo() {
	fmt.Printf("动物的年龄是:%v,动物的体重是:%v", an.Age, an.Weight)
}

//定义结构体:Cat
type Cat struct {
	//为了复用性,体现继承思维,嵌入匿名结构体:——》将Animal中的字段和方法都达到复用
	Animal
}

//对Cat绑定特有的方法:
func (c *Cat) scratch() {
	fmt.Println("我是小猫,我可以挠人")
}
func main() {
	//创建Cat结构体示例:
	cat := &Cat{}
	cat.Animal.Age = 3
	cat.Animal.Weight = 10.6
	cat.Animal.Shout()
	cat.Animal.ShowInfo()
	cat.scratch()
}
package main

import (
	"fmt"
)

type A struct {
	a int
	b string
}
type B struct {
	c int
	d string
}
type C struct {
	A
	B
}

func main() {
	//多继承
	c := C{A{10, "aaa"}, B{20, "ccc"}}
	fmt.Println(c)
}

接口

package main

import "fmt"

//接口的定义:定义规则、定义规范,定义某种能力:
type SayHello interface {
	//声明没有实现的方法:
	sayHello()
}

//接口的实现:定义一个结构体:
//中国人:
type Chinese struct {
}

//实现接口的方法---》具体的实现:
func (person Chinese) sayHello() {
	fmt.Println("你好")
}

//接口的实现:定义一个结构体:
//美国人:
type American struct {
}

//实现接口的方法---》具体的实现:
func (person American) sayHello() {
	fmt.Println("hi")
}

//定义一个函数:专门用来各国人打招呼的函数,接收具备SayHello接口的能力的变量:
func greet(s SayHello) {
	s.sayHello()
}
func main() {
	//创建一个中国人:
	c := Chinese{}
	//创建一个美国人:
	a := American{}
	//美国人打招呼:
	greet(a)
	//中国人打招呼:
	greet(c)
}

多态

断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok := element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

类似jiava的  instanceof

7、文件,流处理:读取、写入、复制、删除

package main

import (
	"fmt"
	"os"
)

func main() {
	//打开文件:
	file, err := os.Open("d:/Test.txt")
	if err != nil { //出错
		fmt.Println("文件打开出错,对应错误为:", err)
	}
	//没有出错,输出文件:
	fmt.Printf("文件=%v", file)
	//.........一系列操作
	//关闭文件:
	err2 := file.Close()
	if err2 != nil {
		fmt.Println("关闭失败")
	}
}
package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	//备注:在下面的程序中不需要进行 Open\Close操作,因为文件的打开和关闭操作被封装在ReadFile函数内部了
	//读取文件:一次性读取
	content, err := ioutil.ReadFile("d:/Test.txt") //返回内容为:[]byte,err
	if err != nil {                                //读取有误
		fmt.Println("读取出错,错误为:", err)
	}
	//如果读取成功,将内容显示在终端即可:
	//fmt.Printf("%v",content)
	fmt.Printf("%v", string(content))
}
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	//打开文件:带缓冲区读取
	file, err := os.Open("d:/Test.txt")
	if err != nil { //打开失败
		fmt.Println("文件打开失败,err=", err)
	}
	//当函数退出时,让file关闭,防止内存泄露:
	defer file.Close()
	//创建一个流:
	reader := bufio.NewReader(file)
	//读取操作:
	for {
		str, err := reader.ReadString('\n') //读取到一个换行就结束
		//如果没有读取到文件结尾的话,就正常输出文件内容即可:
		fmt.Println(str)
		if err == io.EOF { //io.EOF 表示已经读取到文件的结尾
			break
		}

	}
	//结束:
	fmt.Println("文件读取成功,并且全部读取完毕")
}
package main

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

func main() {
	//写入文件操作:
	//打开文件:
	file, err := os.OpenFile("d:/Demo.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil { //文件打开失败
		fmt.Printf("打开文件失败", err)
		return
	}
	//及时将文件关闭:
	defer file.Close()
	//写入文件操作:---》IO流---》缓冲输出流(带缓冲区)
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString("你好")
	}
	//流带缓冲区,刷新数据--->真正写入文件中:
	writer.Flush()
	s := os.FileMode(0666).String()
	fmt.Println(s)
}
package main

import (
	"fmt"
	"io/ioutil"
)

//文件复制
func main() {
	//定义源文件:
	file1Path := "d:/Demo.txt"
	//定义目标文件:
	file2Path := "d:/Demo2.txt"
	//对文件进行读取:
	content, err := ioutil.ReadFile(file1Path)
	if err != nil {
		fmt.Println("读取有问题!")
		return
	}
	//写出文件:
	err = ioutil.WriteFile(file2Path, content, 0666)
	if err != nil {
		fmt.Println("写出失败!")
	}
}

8、线程、并发、锁、队列(管道)

协程:1) 如果主线程退出了,则协程即使还没有执行完毕,也会退出
2) 当然协程也可以在主线程没有退出前,就自己结束了,比如完成了自己的任务

3)可以启动多个协程

package main

import (
	"fmt"
	"strconv"
	"time"
)

func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("hello golang + " + strconv.Itoa(i))
		//阻塞一秒:
		time.Sleep(time.Second)
	}
}
func main() { //主线程
	go test() //开启一个协程
	for i := 1; i <= 10; i++ {
		fmt.Println("hello 协程 " + strconv.Itoa(i))
		//阻塞一秒:
		time.Sleep(time.Second)
	}
}

WaitGroup的作用:
WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。---》解决主线程在子协程结束后自动结束

类似于过滤器

互斥锁:

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)
}

读写锁:

package main

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

var wg sync.WaitGroup //只定义无需赋值
//加入读写锁:
var lock sync.RWMutex

func read() {
	defer wg.Done()
	lock.RLock() //如果只是读数据,那么这个锁不产生影响,但是读写同时发生的时候,就会有影响
	fmt.Println("开始读取数据")
	time.Sleep(time.Second)
	fmt.Println("读取数据成功")
	lock.RUnlock()
}
func write() {
	defer wg.Done()
	lock.Lock()
	fmt.Println("开始修改数据")
	time.Sleep(time.Second * 10)
	fmt.Println("修改数据成功")
	lock.Unlock()
}
func main() {
	wg.Add(6)
	//启动协程 ---> 场合:读多写少
	for i := 0; i < 5; i++ {
		go read()
	}
	go write()
	wg.Wait()
}

管道(channel)特质介绍:
(1)管道本质就是一个数据结构-队列
(2)数据是先进先出
(3)自身线程安全,多协程访问时,不需要加锁,channel本身就是线程安全的
(4)管道有类型的,一个string的管道只能存放string类型数据

package main

import (
	"fmt"
)

func main() {
	//定义管道 、 声明管道 ---> 定义一个int类型的管道
	var intChan chan int
	//通过make初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 3)
	//证明管道是引用类型:
	fmt.Printf("intChan的值:%v", intChan) // 0xc000112080
	//向管道存放数据:
	intChan <- 10
	num := 20
	intChan <- num
	intChan <- 40
	//注意:不能存放大于容量的数据:
	//intChan<- 80
	//在管道中读取数据:
	num1 := <-intChan
	num2 := <-intChan
	num3 := <-intChan
	fmt.Println(num1)
	fmt.Println(num2)
	fmt.Println(num3)
	//输出管道的长度:
	fmt.Printf("管道的实际长度:%v,管道的容量是:%v", len(intChan), cap(intChan))
	//注意:在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错:
	num4 := <-intChan
	fmt.Println(num4)
}
package main

import (
	"fmt"
)

func main() {
	//定义管道 、 声明管道
	var intChan chan int
	//通过make初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 3)
	//在管道中存放数据:
	intChan <- 10
	intChan <- 20
	//关闭管道:
	close(intChan)
	//再次写入数据:--->报错
	//intChan<- 30
	//当管道关闭后,读取数据是可以的:
	num := <-intChan
	fmt.Println(num)
}
package main

import (
	"fmt"
)

func main() {
	//定义管道 、 声明管道
	var intChan chan int
	//通过make初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan <- i
	}
	//在遍历前,如果没有关闭管道,就会出现deadlock的错误
	//所以我们在遍历前要进行管道的关闭
	close(intChan)
	//遍历:for-range
	for v := range intChan {
		fmt.Println("value = ", v)
	}
}
package main

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

var wg sync.WaitGroup //只定义无需赋值
//写:
func writeData(intChan chan int) {
	defer wg.Done()
	for i := 1; i <= 50; i++ {
		intChan <- i
		fmt.Println("写入的数据为:", i)
		time.Sleep(time.Second)
	}
	//管道关闭:
	close(intChan)
}

//读:
func readData(intChan chan int) {
	defer wg.Done()
	//遍历:
	for v := range intChan {
		fmt.Println("读取的数据为:", v)
		time.Sleep(time.Second)
	}
}
func main() { //主线程
	//写协程和读协程共同操作同一个管道-》定义管道:
	intChan := make(chan int, 50)
	wg.Add(2)
	//开启读和写的协程:
	go writeData(intChan)
	go readData(intChan)
	//主线程一直在阻塞,什么时候wg减为0了,就停止
	wg.Wait()
}
package main

import (
	"fmt"
)

func main() {
	//默认情况下,管道是双向的--》可读可写:
	//var intChan1 chan int
	//声明为只写:
	var intChan2 chan<- int // 管道具备<- 只写性质
	intChan2 = make(chan int, 3)
	intChan2 <- 20
	//num := <-intChan2 报错
	fmt.Println("intChan2:", intChan2)
	//声明为只读:
	var intChan3 <-chan int // 管道具备<- 只读性质
	if intChan3 != nil {
		num1 := <-intChan3
		fmt.Println("num1:", num1)
	}
	//intChan3<- 30 报错

}

阻塞:

【1】当管道只写入数据,没有读取,就会出现阻塞:

【2】写的快,读的慢(管道读写频率不一致),不会出现阻塞问题:

多路复用:

select功能:解决多个管道的选择问题,也可以叫做多路复用,可以从多个管道中随机公平地选择一个来执行
PS:case后面必须进行的是io操作,不能是等值,随机去选择一个io操作
PS:default防止select被阻塞住,加入default

package main

import (
	"fmt"
	"time"
)

func main() {
	//定义一个int管道:
	intChan := make(chan int, 1)
	go func() {
		time.Sleep(time.Second * 15)
		intChan <- 10
	}()
	//定义一个string管道:
	stringChan := make(chan string, 1)
	go func() {
		time.Sleep(time.Second * 12)
		stringChan <- "msbgolang"
	}()
	//fmt.Println(<-intChan)//本身取数据就是阻塞的
	select {
	case v := <-intChan:
		fmt.Println("intChan:", v)
	case v := <-stringChan:
		fmt.Println("stringChan:", v)
	default:
		fmt.Println("防止select被阻塞")
	}
}

【1】问题原因:多个协程工作,其中一个协程出现panic,导致程序崩溃
【2】解决办法:利用refer+recover捕获panic进行处理,即使协程出现问题,主线程仍然不受影响可以继续执行。

package main

import (
	"fmt"
	"time"
)

//输出数字:
func printNum() {
	for i := 1; i <= 10; i++ {
		fmt.Println(i)
	}
}

//做除法操作:
func devide() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("devide()出现错误:", err)
		}
	}()
	num1 := 10
	num2 := 0
	result := num1 / num2
	fmt.Println(result)
}
func main() {
	//启动两个协程:
	go printNum()
	go devide()
	time.Sleep(time.Second * 5)
}

9、多态、反射

【1】反射相关的函数
1) reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
2) reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息。 

package main

import (
	"fmt"
	"reflect"
)

//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	//1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	fmt.Println("reType:", reType)
	fmt.Printf("reType的具体类型是:%T", reType)
	//2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	fmt.Println("reValue:", reValue)
	fmt.Printf("reValue的具体类型是:%T", reValue)
	//num1 := 100
	//如果真想获取reValue的数值,要调用Int()方法:返回v持有的有符号整数
	num2 := 80 + reValue.Int()
	fmt.Println(num2)
	//reValue转成空接口:
	i2 := reValue.Interface()
	//类型断言:
	n := i2.(int)
	n2 := n + 30
	fmt.Println(n2)
}
func main() {
	//对基本数据类型进行反射:
	//定义一个基本数据类型:
	var num int = 100
	testReflect(num)
}
package main

import (
	"fmt"
	"reflect"
)

//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	//1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	fmt.Println("reType:", reType)
	fmt.Printf("reType的具体类型是:%T", reType)
	//2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	fmt.Println("reValue:", reValue)
	fmt.Printf("reValue的具体类型是:%T", reValue)
	//reValue转成空接口:
	i2 := reValue.Interface()
	//类型断言:
	n, flag := i2.(Student)
	if flag == true { //断言成功
		fmt.Printf("学生的名字是:%v,学生的年龄是:%v", n.Name, n.Age)
	}

}

//定义学生结构体:
type Student struct {
	Name string
	Age  int
}

func main() {
	//对结构体类型进行反射:
	//定义结构体具体的实例:
	stu := Student{
		Name: "丽丽",
		Age:  18,
	}
	testReflect(stu)
}

【1】获取变量的类别:两种方式:
(1)reflect.Type.Kind()
(2)reflect.Value.Kind()

Type和 Kind 的区别
Type是类型, Kind是类别,Type和Kind 可能是相同的,也可能是不同的.
比如:var num int = 10 num的Type是int , Kind也是int 
比如:var stu Studentstu的 Type是 pkg1.Student , Kind是struct

package main

import (
	"fmt"
	"reflect"
)

//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	//1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	//2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	//获取变量的类别:
	//(1)reType.Kind()
	k1 := reType.Kind()
	fmt.Println(k1)
	//(2)reValue.Kind()
	k2 := reValue.Kind()
	fmt.Println(k2)
	//获取变量的类型:
	//reValue转成空接口:
	i2 := reValue.Interface()
	//类型断言:
	n, flag := i2.(Student)
	if flag == true { //断言成功
		fmt.Printf("结构体的类型是:%T", n)
	}
}

//定义学生结构体:
type Student struct {
	Name string
	Age  int
}

func main() {
	//对结构体类型进行反射:
	//定义结构体具体的实例:
	stu := Student{
		Name: "丽丽",
		Age:  18,
	}
	testReflect(stu)
}


struct
struct
结构体的类型是:main.Student

通过反射修改变量

package main

import (
	"fmt"
	"reflect"
)

//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { //空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	reValue := reflect.ValueOf(i)
	//通过SetInt()来改变值:
	reValue.Elem().SetInt(40)

}
func main() {
	//对基本数据类型进行反射:
	//定义一个基本数据类型:
	var num int = 100
	testReflect(&num) //传入指针地址
	fmt.Println(num)
}

通过反射操作结构体的属性和方法

通过反射修改变量

10、网络:tcp、udp

package main

import (
	"fmt"
	"net" 
)

func main() {
	//打印:
	fmt.Println("客服端启动。。")
	//调用Dial函数:参数需要指定tcp协议,需要指定服务器端的IP+PORT
	conn, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil { //连接失败
		fmt.Println("客户端连接失败:err:", err)
		return
	}
	fmt.Println("连接成功,conn:", conn)
}
package main

import (
	"fmt"
	"net" //所需的网络编程全部都在net包下
)

func main() {
	//打印:
	fmt.Println("服务器端启动了。。")
	//进行监听:需要指定服务器端TCP协议,服务器端的IP+PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8888")
	if err != nil { //监听失败
		fmt.Println("监听失败,err:", err)
		return
	}
	//监听成功以后:
	//循环等待客户端的链接:
	for {
		conn, err2 := listen.Accept()
		if err2 != nil { //客户端的等待失败
			fmt.Println("客户端的等待失败,err2:", err2)
		} else {
			//连接成功:
			fmt.Printf("等待链接成功,con=%v ,接收到的客户端信息:%v \n", conn, conn.RemoteAddr().String())
		}
	}
}
package main

import (
	"bufio"
	"fmt"
	"net" //所需的网络编程全部都在net包下
	"os"
)

func main() {
	//打印:
	fmt.Println("客服端启动。。")
	//调用Dial函数:参数需要指定tcp协议,需要指定服务器端的IP+PORT
	conn, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil { //连接失败
		fmt.Println("客户端连接失败:err:", err)
		return
	}
	fmt.Println("连接成功,conn:", conn)
	//通过客户端发送单行数据,然后退出:
	reader := bufio.NewReader(os.Stdin) //os.Stdin代表终端标准输入
	//从终端读取一行用户输入的信息:
	str, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("终端输入失败,err:", err)
	}
	//将str数据发送给服务器:
	n, err := conn.Write([]byte(str))
	if err != nil {
		fmt.Println("连接失败,err:", err)
	}
	fmt.Printf("终端数据通过客户端发送成功,一共发送了%d字节的数据,并退出", n)
}
package main

import (
	"fmt"
	"net" //所需的网络编程全部都在net包下
)

func process(conn net.Conn) {
	//连接用完一定要关闭:
	defer conn.Close()
	for {
		//创建一个切片,准备:将读取的数据放入切片:
		buf := make([]byte, 1024)
		//从conn连接中读取数据:
		n, err := conn.Read(buf)
		if err != nil {
			return
		}
		//将读取内容在服务器端输出:
		fmt.Println(string(buf[0:n]))
	}
}
func main() {
	//打印:
	fmt.Println("服务器端启动了。。")
	//进行监听:需要指定服务器端TCP协议,服务器端的IP+PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8888")
	if err != nil { //监听失败
		fmt.Println("监听失败,err:", err)
		return
	}
	//监听成功以后:
	//循环等待客户端的链接:
	for {
		conn, err2 := listen.Accept()
		if err2 != nil { //客户端的等待失败
			fmt.Println("客户端的等待失败,err2:", err2)
		} else {
			//连接成功:
			fmt.Printf("等待链接成功,con=%v ,接收到的客户端信息:%v \n", conn, conn.RemoteAddr().String())
		}
		//准备一个协程,协程处理客户端服务请求:
		go process(conn) //不同的客户端的请求,连接conn不一样的
	}
}

11、框架、第三方jar包

设置代理:https://goproxy.cn,direct

go mod init projectname

Go Module == Maven

框架:gin、bee、Iris导入第三方包 : go get -u github.com/gin-gonic/gin

我会另外写一篇,gin的教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值