Go 语言学习-4
Go 反射
反射的应用场景
json tag
匿名函数 函数赋给变量 适配器函数 使用反射完成
自己创建框架
package. reflect
反射可以在运行时动态获取变量的各种信息 比如变量的类型类别
如果结构体变量 还可以获取到结构体本身的信息 包括结构体字段 方法 。
通过反射 可以修改变量的值 可以调用关联的方法
使用反射需要import (“reflect”)
反射:
变量 接口interface{} 和reflect.Value 是可以相互转换的 。
interface{} --> reflect.Value rVal:= reflect.VauleOf(b)
reflect.Value–> interface{} rVal.interface()
interface{}–> 变量。 类型断言
reflect.Value 类型和 int 等基本类型不一致 反射获得的数据不可以做算术运算 +2
func reflectTest(b interface{}) {
rtype := reflect.TypeOf(b)
rValue := reflect.ValueOf(b)
r := rValue.Int() + 2
fmt.Println(rtype, r)
ifv:=rValue.Interface()
num2 := ifv.(int)
fmt.Println(num2,"num2")
}
对结构体
type Student struct {
Name string
Age int
}
func reflectTestStruct(b interface{}) {
rtype := reflect.TypeOf(b)
rValue := reflect.ValueOf(b)
fmt.Printf("rval=%v type=%T\n", rValue, rValue)
ifv := rValue.Interface()
num2 := ifv.(Student)
fmt.Println(rtype, num2.Name)
}
反射的本质是运行时 虽然在运行时可以确定 某个值确实是该类型 但是在编译阶段无法确定 。
所以要取值需要通过断言 。
反射的注意事项 和细节说明
- reflect.Value.Kind 获取变量的类别 返回的是一个常量 Type 类型的具体分类
- Type 是类型 Kind 是类别 二者可能相同也可能不同 基本数据类型相同 结构体不同。区分一个Type 是包名+结构体名称
- 通过反射让变量在interface{} 和Reflect.Value 之间相互转换
- 使用反射获取变量的值要求类型匹配。否则会报panic
- 通过反射来修改变量 注意当使用Setxxx方法来设置需要通过对应的指针类型来完成 这样才能改变传入变量的值 同时需要使用到reflect.Value.Elem()
- reflect.Value.Elem() :
两点需要注意:
一是 reflect.ValueOf() 传入指针
二是 需要Elem 修改 变量。
反射最佳 实践-1
1.遍历结构体字段 调用结构体方法 并获取结构体标签值
Method Call 。
Type 的Field 返回时StructField 可以得到结构体的标签。tag
不能用ValueOf取得标签。
反射调用方法 会排序 比较函数名ASCII码。
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
Gender string
}
func testReflectJson(b interface{}) {
typ := reflect.TypeOf(b)
val := reflect.ValueOf(b)
kd := val.Kind()
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
num := val.NumField()
fmt.Printf("num=%v\n", num)
for i := 0; i < num; i++ {
fmt.Printf("field %d 值为%v\n", i, val.Field(i))
tagVal := typ.Field(i).Tag.Get("json")
if tagVal != "" { //没有标签不显示
fmt.Printf("field %d:tag为%v\n", i, tagVal)
}
}
numMe := val.NumMethod()
fmt.Printf("一共有%v个方法\n", numMe)
val.Method(1).Call(nil)
//传入的 是切片
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
//返回的也是切片
res := val.Method(0).Call(params)
fmt.Println("res=", res[0].Int())
}
func (stu Student) Print() {
fmt.Println("--------start--------")
fmt.Println(stu)
fmt.Println("--------end--------")
}
func (stu Student) GetSum(nn1 int, nn2 int) int {
return nn1 + nn2
}
func (stu Student) Set(name string, age int) {
stu.Age = age
stu.Name = name
}
func main() {
//var num int = 100
//reflectTest(num)
var stu = Student{Name: "zhangqii", Age: 1002, Gender: "nana"}
//reflectTestStruct(&stu)
fmt.Println(stu)
testReflectJson(stu)
}
反射最佳 实践-2 *******
SGG—291
常量
定义时需要初始化 const 常量定义时必须要赋值 不能修改
常量只能 是修饰bool 数值类型 int float string 类型 只能是基本数据类型 。
其他语言 常量一般要大写 但Go中没有必要要求
依然通过 首字母的大小写来控制变量的范围
const(
a=1
b=2)
const(
a=itoa
b
c)
// 0,1,2,
网络编程
TCP +Redis
1> Socket 编程 2> HTTP编程
网络编程基础知识
五层
应用层
传输层
网络层
链路层
物理层 网卡
tracert www.baidu,com windows 下 好多路由器不支持,故无法返回。
端口
端口分类
1-1024 固定端口
有名端口 即被某些程序固定使用 一般程序员不用
22 SSH 远程登录协议 23 telnet 使用 21 ftp 使用 25 smtp服务使用 80 iis使用
7 echo 服务
1025-65535 是动态端口 程序员可以使用。
0 是保留端口
netstat -an 查看本机有哪些端口在监听
netstat -anb 来查看监听端口的pid 再结合任务管理器关闭不安全的端口
系统要尽可能保证纯净版 有插件 会有恶意插件可能
TCP编程
服务监听
client.go
package main
import (
"bufio"
"fmt"
"net"
"os"
"strings"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8888")
if err != nil {
fmt.Println("client dial fail", err)
return
}
fmt.Println("conn success", conn)
// 客户端发送单行数据
//os.Stdin
//bufio.NewReader(io.Reader(conn))
reader := bufio.NewReader(os.Stdin)
for {
line, err := reader.ReadString('\n')
if err != nil {
fmt.Println("read string err=", err)
}
line = strings.Trim(line, "\r\n")
if line == "exit" {
fmt.Println("客户端退出")
break
}
// 把 \r\n 加上 可以换行。
_, err = conn.Write([]byte(line + "\r\n"))
if err != nil {
fmt.Println("conn Write err=", err)
}
}
//fmt.Printf("客户端发送了%d字节数据并退出", n)
}
server.go
package main
import (
"fmt"
"io"
"net"
)
/**
TCP 编程 服务器监听
*/
func main() {
fmt.Println("服务器开始监听")
//127.0.0.1 只支持IPV4 0.0.0.0 支持IPV4 和IPV6
listen, error := net.Listen("tcp", "0.0.0.0:8888")
if error != nil {
fmt.Println("listen err=", error)
}
defer listen.Close()
// 循环等待客户端连接
for {
fmt.Println("等待客户端连接")
conn, err := listen.Accept()
if err != nil {
fmt.Println("accpet err=", err)
return
} else {
fmt.Println("accept success", conn.RemoteAddr().String())
}
//准备一个协程为客户端服务
go process(conn)
}
fmt.Printf("listen=%v", listen)
}
//循环接受客户端发送的数据
func process(conn net.Conn) {
//关闭conn
defer conn.Close()
for {
buf := make([]byte, 12)
// 1.等待 客户端通过conn 发送信息 如果 客户端没有Write 那么 就一直等待在哪里。
fmt.Println("服务器等待客户端发送信息")
n, err := conn.Read(buf)
if err == io.EOF {
//fmt.Println("服务器 read 出错", err)
fmt.Println("客户端退出 ", err)
return
}
// 3 显示客户端发送的内容到服务器的终端 注意 string(buf[:n]) 直接用buf 后面的数据不清楚。 会出现奇怪的数据
fmt.Println(string(buf[:n]))
}
}
经典项目 海量用户即时通讯系统
用户注册
用户登陆
显示在线的用户列表
群聊 广播
点对点聊天
离线留言
Redis
Redis 基本介绍
Redis String 最大 512M
基本数据结构
Go 链接到Redis 。
telnet : ctrl+ ] 退出
Redis 连接池
init 函数中初始化连接池
func init() {
pool = &redis.Pool{MaxIdle: 8, MaxActive: 0, IdleTimeout: 100, Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "localhost:6379")
},
}
}
从 pool 中取出连接时 保证 连接池是开启状态。