Go语言学习篇07

Go语言学习篇07

反射

  • 序列化和反序列化使用tag标签,利用的是反射机制
  • 使用反射机制,编制适配器,桥连接

在这里插入图片描述

反射的基本介绍

1)反射可以在运行时动态的获取变量的各种信息,比如变量的类型、类别

2)如果是结构体变量,还可以获取到结构体本身的信息(比如:结构体字段、方法)

3)通过反射,可以修改变量的值,可以调用关联的方法

4)使用反射,需要import “reflect”

package reflect

import "reflect"

reflect包实现了运行时反射,允许程序操作任意类型的对象。典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。Zero接受一个Type类型参数并返回一个代表该类型零值的Value类型值。

reflect示意图

在这里插入图片描述

reflect的相关函数和转换

1)reflect.TypeOf(变量名), 获取变量的类型,返回reflect.Type类型

2)reflect.ValueOf(变量名),获取变量值,返回reflect.Value类型(结构体)

案例

  • 变量、空接口、reflect.value之间的相互转换

在这里插入图片描述

在这里插入图片描述

reflect快速入门案例

基本数据类型、interface、reflect.Value的相互转化

在这里插入图片描述

基本数据类型的转化

package main

import (
	"fmt"
	"reflect"
)

// 基本数据类型、interface{}、 reflect.Value的相互转换
func test01(x interface{
    
    })  {
    
    
	// 通过反射获取 type、kind、value
	// 1、先获取到 reflect.Type
	rType := reflect.TypeOf(x)
	fmt.Println("reflectType:", rType)

	// 获取到 reflect.value
	rValue := reflect.ValueOf(x)
	fmt.Println("reflectValue:", rValue)
	fmt.Println("reflectValue type is:", reflect.TypeOf(rValue))

	// 获取真正的值
	n := 99 + rValue.Int()
	fmt.Println("n:", n)

	// 下面将 reflect.Value 转成 interface{}
	iV := rValue.Interface()
	fmt.Println("interface{}类型iV:", iV)
	// 将 interface{} 转化成 int基本类型
	a := iV.(int)
	fmt.Println("int数据类型a:", a)
}

func main() {
    
    
	var num int = 100
	test01(num)
}

结果

reflectType: int
reflectValue: 100
reflectValue type is: reflect.Value
n: 199
interface{
    
    }类型iV: 100
int数据类型a: 100

结构体数据类型

代码

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
    
    
	Name string
	Age int
}

func (this *Student) Study() {
    
    
	fmt.Printf("%v学习中...\n", this.Name)
}

func ReflectStruct(i interface{
    
    }) {
    
    
	rType := reflect.TypeOf(i)
	fmt.Println("rType:", rType)

	rValue := reflect.ValueOf(i)
	fmt.Println("rValue:", rValue)

	kind01 := rValue.Kind()
	kind02 := rType.Kind()
	fmt.Printf("类别kind01:%v,kind02:%v\n", kind01, kind02)

	iV := rValue.Interface()
	fmt.Printf("Interface方法后是%T类型iV:%v\n", iV, iV)

	stu, ok := iV.(Student)
	if ok {
    
    
		fmt.Println("Student结构体类型stu:", stu)
	}
}

func main() {
    
    
	var student Student = Student{
    
    
		Name: "Carter",
		Age:  18,
	}
	ReflectStruct(student)
}

结果

rType: main.Student
rValue: {
    
    Carter 18}
类别kind01:struct,kind02:struct
Interface方法后是main.Student类型iV:{
    
    Carter 18}
Student结构体类型stu: {
    
    Carter 18}

反射的注意事项

1)reflect.Value.Kind,获取变量的类别,返回的是一个常量

2)Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的

​ 比如:var num int = 10 num的type是int kind也是int

​ 比如:var stu Student stu的 type是 包名.Studentkind是struct

3)使用反射的方式获取变量的值(并返回对应的类型),要求数据类型匹配,

​ 比如 var x int = 10 那么应该使用reflect.ValueOf(x).Int(),而不能使用其他的,否则会报panic

reflect 修改变量

func (Value) Elem

func (v Value) Elem() Value

Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。

代码

package main

import (
	"fmt"
	"reflect"
)

func reflectValue(i interface{
    
    }) {
    
    
	rValue := reflect.ValueOf(i)
	fmt.Printf("rValue kind is %v\n", rValue.Kind())
    // 注意使用Elem()方法
	rValue.Elem().SetInt(64)
}

func main() {
    
    
	var num int = 20
	reflectValue(&num)
	fmt.Println("num:", num)
}

结果

rValue kind is ptr
num: 64

reflect 基础练习题

1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值

基础练习

package main

import (
	"fmt"
	"reflect"
)

func reflectValue(i interface{
    
    }) {
    
    
	rValue := reflect.ValueOf(i)
	fmt.Printf("rValue kind is %v\n", rValue.Kind())
	fmt.Printf("rValue type is %v\n", reflect.TypeOf(i))
	rValInterface := rValue.Interface()
	fmt.Println("rValInterface:", rValInterface)
	value, ok := rValInterface.(float64)
	if ok {
    
    
		fmt.Printf("value=%v\n", value)
	}
}

func main() {
    
    
	var num float64 = 20.1
	reflectValue(num)

	var str string = "tom"
	// 必须传入地址
	rValue := reflect.ValueOf(&str)
	rValue.Elem().SetString("java")
	fmt.Println(str)
}

结果

rValue kind is float64
rValue type is float64
rValInterface: 20.1
value=20.1
java

升级练习题

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
    
    
	Name string `json:"name"`
	Age int	`json:"age"`
	Gender string
}

func (stu Student) Study() {
    
    
	fmt.Println("学习中...")
}

func (stu Student) GetSum(x int, y int) int {
    
    
	return x + y
}

func (stu Student) GetSub(x int, y int) int {
    
    
	return x - y
}
func (stu Student) getSub(x int, y int) int {
    
    
	return x - y
}

func (stu *Student) String() string {
    
    
	return fmt.Sprintf("name: %v sex: %v gender: %v", stu.Name, stu.Age, stu.Gender)
}

func reflectStu(stu interface{
    
    }) {
    
    
	rType := reflect.TypeOf(stu)
	fmt.Println("rType:", rType)

	rValue := reflect.ValueOf(stu)
	fmt.Println("rValue:", rValue)

	// 获取类别
	kd := rValue.Kind()
	if kd != reflect.Struct {
    
    
		fmt.Println("参数不是结构体!")
		return
	}

	//获取字段数目 【注意:字段首字母小写也可以获取】
	nums := rType.NumField()
	fmt.Println("nums:", nums)
	numFields := rValue.NumField()
	fmt.Println("字段个数:", numFields)

	// 获取字段对应的值和tag标签
	for i := 0; i < nums; i++ {
    
    
		tagVal := rType.Field(i).Tag.Get("json")
		if tagVal != "" {
    
    
			fmt.Print(tagVal,":")
		}
		fmt.Println(rValue.Field(i))
	}
	// 获取方法数目【注意:方法首字母必须是大写的才能获取】
	numMethods := rValue.NumMethod()
	fmt.Println("方法个数:", numMethods)
	numMethods1 := rType.NumMethod()
	fmt.Println("方法个数:", numMethods1)

	// 通过反射执行方法
	rValue.MethodByName("Study").Call(nil)

	fmt.Println("获取所有的方法名...")
	var i int = 0

	fmt.Println(rType.Method(i).Index)
	fmt.Println(rType.Method(i).Name)
	fmt.Println(rType.Method(i).Type)
	fmt.Println()

	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(20))
	//返回一个reflect.Value切片
	sum := rValue.Method(1).Call(params)
	for _, value := range sum {
    
    
		sum , ok := value.Interface().(int)
		if !ok {
    
    
			break
		}
		fmt.Println("sum:", sum)
	}
}

func main() {
    
    
	var stu Student = Student{
    
    
		Name : "Carter",
		Age : 18,
		Gender : "男",
	}
	reflectStu(stu)

	var stu2 *Student = &Student{
    
    
		Name:"lsx",
		Age:18,
		Gender:"男",
	}
	fmt.Printf("修改前: %v\n", stu2)
	rValue := reflect.ValueOf(stu2)
	rValue = rValue.Elem()
	rValue.FieldByName("Name").SetString("廖述幸")
	fmt.Printf("修改后: %v\n", stu2)
}

结果

rType: main.Student
rValue: {
    
    Carter 18}
nums: 3
字段个数: 3
name:Carter
age:18
男
方法个数: 3
方法个数: 3
学习中...
获取所有的方法名...
0
GetSub
func(main.Student, int, int) int

sum: 30
修改前: name: lsx sex: 18 gender: 男
修改后: name: 廖述幸 sex: 18 gender:

常量

常量的介绍

  • 常量使用const修改
  • 常量在定义的时候必须初始化
  • 常量不能修改
  • 常量还能用bool、数值类型(int、float系列)、string类型
  • 语法:const identifier [type] = value
  • 举例说明,看看下面的写法是否正确【右侧的值是否固定】

const name = “tom”

const tax float64 = 0.8

const a int error

const b = 9/3

const c = getValue() error

在这里插入图片描述

常量的注意事项

1)Go语言中,常量可以不大写

2)Go语言中,常量仍有访问范围机制

面试题 1

package main

import "fmt"

const (
	a = iota	//默认从0开始,后面的如果都没赋值,就在0的基础上+1
	b
	c
	d = 99
	e
	f
)

func main() {
    
    
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
	fmt.Println(e)
	fmt.Println(f)
}

结果

0
1
2
99
99
99

面试题 2

代码

package main

import "fmt"

const (
	a = iota
	b = iota
	c, d = iota, iota
)

func main() {
    
    
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
}

结果

0
1
2
2	//没有改变

TCP 编程

TCP 基本介绍

  • 应用案例:
  • CS结构
    • QQ、微信
    • A->Server->B
  • B/S【浏览器】

TCP socket

基于TCP/IP,如:QQ

B/S http

web编程

有三本书:网络底层的书本,硬件必学

在这里插入图片描述

TCP/IP QQ通信原理图

在这里插入图片描述

Bash 命令追踪路由包

trace [追踪]

rt [router简写]

  • 请求超时不是没通过,而是它不给你返回通过的包数据
cmd > tracert www.baidu.com
C:\Users\海角天涯S>tracert www.baidu.com

通过最多 30 个跃点跟踪
到 www.a.shifen.com [110.242.68.4] 的路由:

  1     5 ms     5 ms     4 ms  100.88.96.1
  2     6 ms     5 ms     6 ms  61.187.3.1
  3     5 ms     *       10 ms  61.137.12.237
  4     *        *        *     请求超时。
  5    37 ms    39 ms    41 ms  202.97.98.5
  6     *        *        *     请求超时。
  7    44 ms    41 ms    35 ms  219.158.44.125
  8     *        *        *     请求超时。
  9     *        *        *     请求超时。
 10    95 ms    42 ms    55 ms  110.242.66.178
 11     *        *        *     请求超时。
 12     *        *        *     请求超时。
 13     *        *        *     请求超时。
 14     *        *        *     请求超时。
 15     *        *        *     请求超时。
 16   244 ms   239 ms   236 ms  110.242.68.4

跟踪完成。

IPV4 和IPV6

最早只有IPV4(32位)、IPV6(128位)! 2的128次方台电脑,一台电脑一个IP

端口

在这里插入图片描述

在这里插入图片描述

端口分类

在这里插入图片描述

TCP Socket编程快速入门

  • 服务器端的处理流程

    • 监听端口 8888
    • 接收客户端的tcp连接,建立客户端和服务器端的链接
    • 创建goroutine,处理该链接的请求(通常客户端会通过连接发送请求包)
  • 客户端的处理流程

    • 建立与服务端的链接
    • 发送请求数据,就收服务器端返回的结果数据
    • 关闭链接

聊天系统

思路

在这里插入图片描述

服务器

package main

import (
	"fmt"
	"net"
)

const (
	host = "localhost"
	port = "8888"
	addr = host + ":" + port
)

func process(conn net.Conn) {
    
    
	defer conn.Close()
	// 循环接收客户端发送的数据
	for {
    
    
		// 创建一个new切片
		buf := make([]byte, 1024)
		// 1、等待客户端通过conn发送信息
		// 2、如果客户端没有write([]byte),那么协程就阻塞在这里
		n, error := conn.Read(buf)
		if error != nil {
    
    
			fmt.Printf("%v已退出...\n", conn.RemoteAddr().String())
			return
		}
		// 客户端早在尾部追加了\n回车符
		fmt.Printf("接收到%v的数据:%v", conn.RemoteAddr().String(), string(buf[:n]))
	}
}

func main() {
    
    
	// 1、tcp协议
	// 2、监听8888端口
	listen, error := net.Listen("tcp", addr)
	if error != nil {
    
    
		fmt.Println("Server监听失败,", error)
		return
	}

	// 3、延时等待
	defer listen.Close()

	// 4、等待客户端连接
	for true {
    
    
		fmt.Println("等待客户端连接")
		conn , error := listen.Accept()
		if error != nil {
    
    
			fmt.Println("客户端连接错误,", error)
		} else {
    
    
			fmt.Printf("%v成功连接\n", conn.RemoteAddr().String())
		}
		// 4.1、这里启动一个协程,为客户端服务
		go process(conn)
	}
}

客户端

package main

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

func main() {
    
    
	var host string
	var port string
	flag.StringVar(&host, "h", "localhost", "获取host,默认是175.0.211.19")
	flag.StringVar(&port, "p", "8888", "获取ip地址,默认为8888")
	flag.Parse()

	var address = fmt.Sprintf("%v:%v", host, port)
	conn, connError := net.Dial("tcp", address)
	if connError != nil {
    
    
		fmt.Println("连接服务器错误,", connError)
		return
	}

	defer conn.Close()
	fmt.Println("连接服务器成功,", conn.RemoteAddr().String())

	// 客户端发送单行数据 【终端标准输入,Socket的本质是文件】
	// 磁盘满了,无法进行Socket传输-->网络不通
	reader := bufio.NewReader(os.Stdin)

	for {
    
    
		// 从终端读取一行用户的输入
		fmt.Print("请输入内容:")
		data, readerError := reader.ReadString('\n')
		if readerError != nil {
    
    
			fmt.Println("读取终端输入错误,error:", readerError)
		}

		// 如果用户输入exit,就退出程序
		if strings.TrimSpace(data) == "exit" {
    
    
			fmt.Println("客户端退出OK!")
			return
		}
		// 再将data 发送给Server
		_, error := conn.Write([]byte(data))
		if error != nil {
    
    
			fmt.Println("发送给Server错误,", error)
		}
	}
}

Redis

Redis 基本使用

说明:Redis安装默认16个数据库,默认使用0号数据库

  • 添加 key-value [set]

  • 查看当前Redis所有的 key [keys *]

  • 获取 key 对应的值 [get key]

  • 切换Redis数据库 [select index]

  • 如何查看当前数据库的 key-value 数量 [dbsize]

  • 清空当前数据库的 key-value 和 清空所有数据库的 key-value [flushdb flushall]

  • SET

    • mset key1 val1 key2 val2 [设置多个key]
    • mget key1 key2
    • setex key 10 value10s超时
  • GET

  • DEL

Redis hash(哈希)

Redis hash 是一个键值对集合,key不能重复,无序

用于存储结构体

type User struct {
    
    
    Name string
    Gender string
    age ing
}
  • hset user name “tom” 存
    • hget user name 取
  • hset user sex “男”
    • hget user sex
  • hset user age 18
    • hget user age

hgetall

  • hgetall user

hmset

  • hmset user name “tom” sex “男” age 18

hlen

  • hlen user

hexists key field

hexists user name

Redis List(链表)

list:链表,有序,管道

lpush

left push [左边插入]

lPush name lsx carter tom

rpush

right push [右边插入]

lrange

LRange name 0 -1 [倒数第一个]

lpop

left pop [取出list左边数据并移走]

rpop

right pop [取出list右边数据并移走]

实际应用

  • 最近浏览的10个文件

在这里插入图片描述

Redis Set(集合)

set:无序,不重复,HashTable的数据结构

举例:存放电子邮件

  • 添加

sadd

sadd email [email protected]

  • 取出所有值

smembers

smembers email

  • 判断是否存在

sismember

sismember email [email protected]

  • 删除

srem

srem email [email protected]

Golang操作Redis

1、安装第三方开源Redis库

操作Redis的API

1)使用第三方的Redis库:redis库

2)使用Redis前,先安装第三方库,在GOPATH路径下安装指令:

D:\Work\Goland\Go>go get github.com/garyburd/redigo/redis

3)安装后:

src下出现github.com目录

d:\Work\Goland\Go\src\github.com>dir
 驱动器 D 中的卷是 Data
 卷的序列号是 2C37-C14D

 d:\Work\Goland\Go\src\github.com 的目录

2020/12/19  18:16    <DIR>          .
2020/12/19  18:16    <DIR>          ..
2020/12/19  18:16    <DIR>          garyburd
               0 个文件              0 字节
               3 个目录 821,155,868,672 可用字节

d:\Work\Goland\Go\src\github.com>

特别说明

​ 确保已经安装并配置Git

D:\Work\Goland>git version
git version 2.27.0.windows.1

2、Go操作Redis

package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

func main() {
    
    
	// 1、Redis数据库连接
	conn, error := redis.Dial("tcp", "localhost:6379")
	if error != nil {
    
    
		fmt.Println("连接Redis数据库失败,", error)
		return
	}
	fmt.Println("连接Redis数据库成功!")
	defer conn.Close()

	// 2、Redis数据库存值
	_, err := conn.Do("Set", "key", "廖述幸")
	if err != nil {
    
    
		fmt.Println("操作Redis数据库失败!")
		return
	}
	fmt.Println("操作Redis数据库成功!")

	// 3、Redis数据库取值
	value ,getError:= redis.String(conn.Do("Get", "key"))
	if getError != nil {
    
    
		fmt.Println("从Redis中取值失败!")
		return
	}
	fmt.Println("取出的值:", value)
}

3、批处理

package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

func main() {
    
    
	// 1、Redis数据库连接
	conn, error := redis.Dial("tcp", "localhost:6379")
	if error != nil {
    
    
		fmt.Println("连接Redis数据库失败,", error)
		return
	}
	fmt.Println("连接Redis数据库成功!")
	defer conn.Close()

	// 2、Redis数据库存值
	_, err := conn.Do("HMSet", "student", "name", "廖述幸", "age", 18, "sex", "男")
	if err != nil {
    
    
		fmt.Println("操作Redis数据库失败!")
		return
	}
	fmt.Println("操作Redis数据库成功!")

	// 3、Redis数据库取值
	value ,getError:= redis.StringMap(conn.Do("HGetAll", "student"))
	if getError != nil {
    
    
		fmt.Println("从Redis中取值失败!")
		return
	}
	fmt.Println("取出的值:", value)
	name := value["name"]
	fmt.Println(name)
}

结果

连接Redis数据库成功!
操作Redis数据库成功!
取出的值: map[age:18 name:廖述幸 sex:]
廖述幸

4、设置有效时间

_, error := conn.Do("expire", "name", "10")

Redis 连接池

1)打开一个Redis连接,关闭(一次性)

2)连接池,先创建一个池的Redis连接,用就拿,不用就放回去,也不关闭

节省的连接时间

代码

package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

var pool *redis.Pool

func init() {
    
    
	pool = &redis.Pool{
    
    
		// 最大空闲连接数
		MaxIdle:         8,
		// 和数据库最大连接数, 0表示没有限制
		MaxActive:       0,
		// 最大空闲时间
		IdleTimeout:     100,
		Dial: func() (conn redis.Conn, err error) {
    
    
			return redis.Dial("tcp", "localhost:6379")
		},
	}
}

func main() {
    
    
	// 从pool池中取出一个连接
	conn := pool.Get()
	// 延迟关闭
	defer conn.Close()
	// 存值
	_, setError := conn.Do("Set", "name", "Carter-廖述幸")
	if setError != nil {
    
    
		fmt.Println("存值失败,", setError)
		return
	}
	// 取值

	data, getError := redis.String(conn.Do("Get", "name"))
	if getError != nil {
    
    
		fmt.Println("取值失败,", getError)
		return
	}
	fmt.Println(data)
}

结果

Carter-廖述幸

:", value)
name := value[“name”]
fmt.Println(name)
}


**结果**

~~~go
连接Redis数据库成功!
操作Redis数据库成功!
取出的值: map[age:18 name:廖述幸 sex:男]
廖述幸

4、设置有效时间

_, error := conn.Do("expire", "name", "10")

Redis 连接池

1)打开一个Redis连接,关闭(一次性)

2)连接池,先创建一个池的Redis连接,用就拿,不用就放回去,也不关闭

节省的连接时间

代码

package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

var pool *redis.Pool

func init() {
    
    
	pool = &redis.Pool{
    
    
		// 最大空闲连接数
		MaxIdle:         8,
		// 和数据库最大连接数, 0表示没有限制
		MaxActive:       0,
		// 最大空闲时间
		IdleTimeout:     100,
		Dial: func() (conn redis.Conn, err error) {
    
    
			return redis.Dial("tcp", "localhost:6379")
		},
	}
}

func main() {
    
    
	// 从pool池中取出一个连接
	conn := pool.Get()
	// 延迟关闭
	defer conn.Close()
	// 存值
	_, setError := conn.Do("Set", "name", "Carter-廖述幸")
	if setError != nil {
    
    
		fmt.Println("存值失败,", setError)
		return
	}
	// 取值

	data, getError := redis.String(conn.Do("Get", "name"))
	if getError != nil {
    
    
		fmt.Println("取值失败,", getError)
		return
	}
	fmt.Println(data)
}

结果

Carter-廖述幸

猜你喜欢

转载自blog.csdn.net/IT_Carter/article/details/111874778