在学这门语言之前,根据Java我整理了几个方向
目录
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字符类型
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的教程