24.Go中的反射与Java中的反射对比
整体概述:反射是一个通用的概念,是指在程序运行期间获取到变量或者对象,结构体的元信息,比如类型信息,并且能够取出其中变量的值,调用对应的方法。 首先我们先来回顾一下Java语言用到反射的场景有哪些? 1.比如说我们的方法参数不能确定是什么类型,是Object类型,我们就可以通过反射在运行期间获取其真实的类型,然后做对应的逻辑处理。 2.比如动态代理,我们需要在程序运行时,动态的加载一个类,创建一个类,使用一个类。 3.比如在想要强行破解获取程序中被private的成员。 4.Java的各种框架中用的非常多,框架中用反射来判断用户自定义的类是什么类型,然后做区别处理。
Go中的反射大概也是相同的,比如,go中有一个类型 interface,interface类型相当于Java中的Object类,当以interface作为参数类型时,可以给这个参数传递任意类型的变量。 例如示例1:
package main import "fmt" func main() { testAllType(1); testAllType("Go"); } //interface{}代表任意类型 func testAllType (data interface{}){ fmt.Println(data) }
那么第一种应用场景就出现了,当我们在go中想实现一个函数/方法,这个函数/方法的参数类型在编写程序的时候不能确认,在运行时会有各种不同的类型传入这个通用的函数/方法中,我们需要对不同类型的参数做不同的处理,那么我们就得能获取到参数是什么类型的,然后根据这个类型信息做业务逻辑判断。 反射我们需要调用reflect包模块,使用reflect.typeOf()可以获取参数的类型信息对象,再根据类型信息对象的kind方法,获取到具体类型,详细参考下面代码。 例如示例2:
package main import ( "fmt" "reflect" ) func main() { handleType(1) handleType(true) } func handleType(data interface{}) { //获取类型对象 d := reflect.TypeOf(data) //kind方法是获取类型 fmt.Println(d.Kind()) switch d.Kind() { case reflect.Invalid: //无效类型逻辑处理 fmt.Println("无效类型") case reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64: fmt.Println("整形") case reflect.Bool: fmt.Println("bool类型") } }
因为传入进来的都是interface类型,所以我们需要用的时候要区分类型,然后取出其中真正类型的值。 反射取出值得方法就是先通过reflect.ValueOf()获取参数值对象,然后再通过不同的具体方法获取到值对象,比如int和bool 示例3:
package main import ( "fmt" "reflect" ) func main() { handleValue(1) handleValue(true) } func handleValue(data interface{}) { //获取类型对象 d := reflect.ValueOf(data) //kind方法是获取类型 fmt.Println(d.Kind()) switch d.Kind() { case reflect.Invalid: //无效类型逻辑处理 fmt.Println("无效类型") case reflect.Int,reflect.Int8,reflect.Int16,reflect.Int32,reflect.Int64: //取出值 var myNum = d.Int() fmt.Println(myNum) case reflect.Bool: //取出bool值 var myBool = d.Bool() fmt.Println(myBool) } }
25.变量作用域的区
Go语言的变量作用域和Java中的一样,遵循最近原则,逐渐往外层找。 这个比较简单,就不做过多赘述了。
26.Go语言和Java语言字符串操作的区别
Go语言操作字符串都是通过strings来处理,类似Java中的工具类StringUtils
- strings.HasPrefix(s string, prefix string) bool:判断字符串s是否以prefix开头
- strings.HasSuffix(s string, suffix string) bool:判断字符串s是否以suffix结尾。
- strings.Index(s string, str string) int:判断str在s中首次出现的位置,如果没有出现,则返回-1
- strings.LastIndex(s string, str string) int:判断str在s中最后出现的位置,如果没有出现,则返回-1
- strings.Replace(str string, old string, new string, n int):字符串替换
- strings.Count(str string, substr string)int:字符串计数
- strings.Repeat(str string, count int)string:重复count次str
- strings.ToLower(str string)string:转为小写
- strings.ToUpper(str string)string:转为大写
- strings.TrimSpace(str string):去掉字符串首尾空白字符
- strings.Trim(str string, cut string):去掉字符串首尾cut字符
- strings.TrimLeft(str string, cut string):去掉字符串首cut字符
- strings.TrimRight(str string, cut string):去掉字符串首cut字符
- strings.Field(str string):返回str空格分隔的所有子串的slice
- strings.Split(str string, split string) []string:返回str split分隔的所有子串的slice
- strings.Join(s1 []string, sep string):用sep把s1中的所有元素链接起来
- strings.Itoa(i int):把一个整数i转成字符串
- strings.Atoi(str string)(int, error):把一个字符串转成整数
27.Go语言和Java语言IO操作的区别
在io包中最重要的是两个接口:Reader和Writer接口。本章所提到的各种IO包,都跟这两个接口有关,也就是说,只要实现了这两个接口,它就有了IO的功能。与java中类似都有两个顶层接口
Reader接口
Reader接口的定义如下:
type Reader interface { Read(p []byte) (n int, err error) }
Writer接口
Writer接口的定义如下:
type Writer interface { Write(p []byte) (n int, err error) }
简单示例
package main import ( "fmt" "strings" ) func main() { reader := strings.NewReader("abcdefgh") p := make([]byte, 6) n, err := reader.ReadAt(p, 2) if err != nil { panic(err) } fmt.Printf("%s, %d\n", p, n) } 运行结果: c, 6
28.Go语言中有匿名函数,有闭包,Java中没有(高阶函数用法)
函数也是一种类型,它可以作为一个参数进行传递,也可以作为一个返回值传递。
Go中可以定义一个匿名函数,并把这个函数赋值给一个变量
示例1: 匿名函数赋值给变量
package main import "fmt" //定义一个匿名函数并赋值给myFun变量 var myFun = func(x,y int) int { return x+y } func main() { //调用myFun fmt.Println(myFun(1,2)) }
输出结果:
3
Go的函数内部是无法再声明一个有名字的函数的,Go的函数内部只能声明匿名函数。
示例2:
package main import "fmt" func main() { myFunc3() } func myFun1() { /*此处报错,函数内部不能声明带有名称的函数 func myFunc2() { } */ } func myFunc3() { //函数内部可以声明一个匿名函数,并把这个匿名函数赋值给f变量 var f = func() { fmt.Println("Hi,boy!") } //调用f f() //如果不想赋值给变量,那就必须在最后加上(),表示立即执行 func() { fmt.Println("Hello,girl!") }()//有参数可以写在这个小括号中 }
输出:
Hi,boy! Hello,girl!
Go中有闭包的功能。(闭包是一个通用的编程概念,一些语言有,一些没有,javascript中就有这个概念,Java中没有)
闭包,通俗易懂的讲,就是你有一个A函数,A函数有一个a参数,然后在A函数内部再定义或者调用或者写一个B函数,这个B函数叫做闭包函数。B函数内部的代码可以访问它外部的A函数的a参数,正常A函数调用返回完毕,a参数就不能用了,可是闭包函数B函数仍然可以访问这个a参数,B函数能不受A函数的调用生命周期限制可以随时访问其中的a参数,这个能访问的状态叫做已经做了闭包,闭包闭的是把a参数封闭到了B函数中,不受A函数的限制。
也就是说,我们用程序实现一个闭包的功能,实质上就是写一个让外层的函数参数或者函数内变量封闭绑定到内层函数的功能。
接下来我们看代码示例:
package main import "fmt" //我们来看看实现闭包 func main() { var f = f1(100) f(100) //print 200 f(100) //print 300 f(100) //print 400 } func f1(x int) func(int){ //此时即使f1函数执行完毕,x也不会消失 //x在func(y int)这个函数内一直存在并且叠加, //这里把x的值封闭到func(y int)这个返回函数中,使其函数一直能使用x的值 //就叫做闭包,把x变量封闭到fun(y int)这个函数包内。 return func(y int){ x+=y fmt.Printf("x=%d\n",x) } }
输出:
x=200 x=300 x=400
做下闭包的总结,如何实现一个闭包:
1.定义一个A函数,此函数返回一个匿名函数。(定义一个返回匿名函数的A函数)
2.把在A函数的b参数或A函数代码块中的b变量,放入匿名函数中,进行操作。
3.这样我们调用A函数返回一个函数,这个函数不断的调用就可以一直使用之前b参数,b变量,并且b值不会刷新,有点像在匿名函数外部自定义了一个b的成员变量(成员变量取自Java中类的相关概念)
29.Go中的map和Java中的HashMap
Go中的map也是一个存储key-value,键值对的这么一种数据结构。
我们来看下如何使用:
如何创建一个map?(map是引用类型,默认值是nil,必须用make为其创建才能使用)
创建一个map必须要用make,否则会是nil
格式为: make(map[key类型]value类型) (下面有代码示例)
往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式
示例1:创建map以及添加元素
package main import "fmt" func main() { //创建一个map必须要用make,否则会是nil //格式为: make(map[key类型]value类型) //Java中: Map<String,Integer> myMap = new HashMap<>(); myMap := make(map[string]int) //往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式 //区别于Java中的: myMap.put("li_age",20); myMap["li_age"] = 20 myMap["hong_age"] = 30 //打印一下map fmt.Println(myMap) }
我们从map中取值得格式为: 【 mapValue := map变量名[key]】
当我们填写的key在map中找不到时返回对应的value默认值,int是0,引用类型是nil
当我们的key取不到对应的值,而value的类型是一个int类型,我们如何判断这个0是实际值还是默认值呢
此时我们需要同时取两个值
通过map的key取出两个值,第二个参数为bool类型,false为该值不存在,true为成功取到值
参考下面:
示例2:从map中取值
package main import "fmt" func main() { //创建一个map必须要用make,否则会是nil //格式为: make(map[key类型]value类型) //Java中: Map<String,Integer> myMap = new HashMap<>(); myMap := make(map[string]int) //往Go中的map赋值添加元素用 【 map变量名称[key] = value 】 的方式 //区别于Java中的: myMap.put("li_age",20); myMap["li_age"] = 20 myMap["hong_age"] = 30 //打印一下map fmt.Println(myMap) //不存在的值 fmt.Println(myMap["no"]) //当我们的key取不到对应的值,而value的类型是一个int类型,我们如何判断这个0是实际值还是默认值呢 //此时我们需要同时取两个值 //通过map的key取出两个值,第二个参数为bool类型,false为该值不存在,true为成功取到值 value,existsValue := myMap["no"] if !existsValue { fmt.Println("此值不存在") } else { fmt.Printf("value = %d",value) } }
Go中因为返回值可以是两个,所以的map遍历很简单,不像java还得弄一个Iterator对象再逐个获取,它一次两个都能取出来,用for搭配range即可实现。
示例3:遍历
package main import "fmt" func main() { myMap := make(map[string]int) myMap["num1"] = 1 myMap["num2"] = 2 myMap["num3"] = 3 myMap["num4"] = 4 myMap["num5"] = 5 myMap["num6"] = 6 //遍历key,value for key,value := range myMap { fmt.Println(key,value) } //写一个参数的时候只取key for key := range myMap { fmt.Println(key) } //如果只想取value,就需要用到之前的_标识符进行占位丢弃 for _,value := range myMap { fmt.Println(value) } }
删除函数:用内置函数delete删除
示例4:删除map元素
package main import "fmt" func main() { myMap := make(map[string]int) myMap["num1"] = 1 myMap["num2"] = 2 myMap["num3"] = 3 myMap["num4"] = 4 myMap["num5"] = 5 myMap["num6"] = 6 //第二个参数为删除的key delete(myMap,"num6") //此时已经没有值了,默认值为0 fmt.Println(myMap["num6"]) }
在Java中有一些复杂的Map类型,比如:
Map<String,Map<String,Object>> data = new HashMap<>();
实际上,在Go语言中,也有复杂的类型,我们举几个代码示例
示例5:
package main import "fmt" func main() { //由map组成的切片 //第一部分 make[] 声明切片 //第二部分 map[string]int 声明该切片内部装的单个类型是map //第三部分 参数 5 表示该切片的长度和容量都是5 //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil //容量至少比长度大,能索引到几个+未来可添加元素个数(目前没有任何东西,看不见)= 切片容量 //make([]切片类型,切片长度,切片容量) //make([]切片类型,切片长度和容量等同) slice := make([]map[string]int,5,10) slice0 := make([]map[string]int,0,10) //我们看看打印的东西 fmt.Println("slice=",slice) fmt.Println("slice=0",slice0) ///* 先看这段 //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map //slice[0]["age"] = 10;//报错 //下面不报错 slice[0] = make(map[string]int,10) slice[0]["age"] = 19 fmt.Println(slice[0]["age"]) //*/ }
输出结果:
slice= [map[] map[] map[] map[] map[]] slice=0 [] 19
接下来继续看代码:
package main import "fmt" func main() { //由map组成的切片 //第一部分 make[] 声明切片 //第二部分 map[string]int 声明该切片内部装的单个类型是map //第三部分 参数 5 表示该切片的长度和容量都是5 //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil //append元素到切片时,是添加到最末尾的位置,当元素未超过容量时,都是用的同一个底层数组 //超过容量时会返回一个新的数组 //make([]切片类型,切片长度,切片容量) //make([]切片类型,切片长度和容量等同) slice := make([]map[string]int,5,10) slice0 := make([]map[string]int,0,10) //我们看看打印的东西 fmt.Println("slice=",slice) fmt.Println("slice=0",slice0) /* 先看这段 //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map //slice[0]["age"] = 10;//报错 //下面不报错 slice[0] = make(map[string]int,10) slice[0]["age"] = 19 fmt.Println(slice[0]["age"]) */ }
输出:
panic: assignment to entry in nil map
看下面这个报错:
package main import "fmt" func main() { //由map组成的切片 //第一部分 make[] 声明切片 //第二部分 map[string]int 声明该切片内部装的单个类型是map //第三部分 参数 5 表示该切片的长度和容量都是5 //长度是用索引能取到第几个元素,索引不能超过长度-1,分配长度后都是默认值,int是0,引用类型是nil //append元素到切片时,是添加到最末尾的位置,当元素未超过容量时,都是用的同一个底层数组 //超过容量时会返回一个新的数组 //make([]切片类型,切片长度,切片容量) //make([]切片类型,切片长度和容量等同) slice := make([]map[string]int,5,10) slice0 := make([]map[string]int,0,10) //我们看看打印的东西 fmt.Println("slice=",slice) fmt.Println("slice=0",slice0) /* 先看这段 //因为有5个长度,所以初始化了5个map,但是map没有通过make申请内容空间,所以报错nil map //slice[0]["age"] = 10;//报错 //下面不报错 slice[0] = make(map[string]int,10) slice[0]["age"] = 19 fmt.Println(slice[0]["age"]) */ ///* //因为初始化了0个长度,所以索引取不到值,报index out of range slice0[0]["age"] = 10; //*/ }
输出:
slice= [mappanic: runtime error: index out of range
接下来我们看一个:类似于Java中常用的map类型
package main import "fmt" func main() { //类似于Java中的Map<String,HashMap<String,Object>> var myMap = make(map[string]map[string]interface{},10) fmt.Println(myMap) //记得make myMap["li_ming_id_123"] = make(map[string]interface{},5) myMap["li_ming_id_123"]["school"] = "清华大学" fmt.Println(myMap) }
输出:
map[] map[li_ming_id_123:map[school:清华大学]]
30.Go中的time时间包模块和Java中的时间API使用区别
Go中关于时间处理的操作在time包中
1.基本获取时间信息
参考如下代码示例:
package main import ( "fmt" "time" ) func main() { //获取当前时间 now := time.Now() //获取当前年份 year := now.Year() //获取当前月份 month := now.Month() //获取当前 日期 day := now.Day() //获取当前小时 hour := now.Hour() //获取当前分钟 min := now.Minute() //获取当前秒 second :=now.Second() //获取当前时间戳,和其它编程语言一样,自1970年算起 timestamp := now.Unix() //纳秒时间戳 ntimestamp := now.UnixNano() fmt.Println("year=",year) fmt.Println("month=",month) fmt.Println("day=",day) fmt.Println("hour=",hour) fmt.Println("min=",min) fmt.Println("second=",second) fmt.Println("timestamp=",timestamp) fmt.Println("ntimestamp=",ntimestamp) }
2.格式化时间
Go的时间格式化和其它语言不太一样,它比较特殊,取了go的出生日期作为参数标准
参考如下代码示例:
package main import ( "fmt" "time" ) func main() { //获取当前时间 now := time.Now() //2006-01-02 15:04:05这个数值是一个标准写死的,只要改格式符号即可 fmt.Println(now.Format("2006-01-02 15:04:05")) fmt.Println(now.Format("2006/01/02 15:04:05")) fmt.Println(now.Format("2006/01/02"))//年月日 fmt.Println(now.Format("15:04:05"))//时分秒 }