Go与Java(9) Go和Java微观对比-8

127 阅读6分钟

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)


	}
	
}

 

​ 结构体中的属性和方法怎么获取呢?

​ 获取结构体属性的个数是先ValueOf获取结构体值对象v后,用v.NumField()获取该结构体有几个属性,通过v.Field(i)来获取对应位置的属性的元类型。

​ 示例代码4:

​ 后续反射还有几个api和代码示例和具体应用场景,正在补。。。

25.变量作用域的区别

​ Go语言的变量作用域和Java中的一样,遵循最近原则,逐渐往外层找。

​ 这个比较简单,就不做过多赘述了。

26.Go语言和Java语言字符串操作的区别

 

27.Go语言和Java语言IO操作的区别

 

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中类的相关概念)