Java反射与Golang反射简单对比

0x00 前言

前段时间学习了golang的基础语法,发现其反射的概念与Java的差别挺大,做个简单对比,记录一下。
为了测试Java反射,创建如下User类:

public class User {
    public String username;
    private String password;
    
	public User() {}

	public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public void publicMethod() {
        System.out.println("publicMethod is running ...");
    }

    private void privateMethod() {
        System.out.println("private is running ...");
    }
}

按照上面的User类,创建如下Golang结构:

type User struct {
	Username string
	password string
}

func (u User) PublicMethod() {
	fmt.Println("PublicMethod is running ... ")
}

func (u User) privateMethod() {
	fmt.Println("privateMethod is running ...")
}

0x01 操作公有字段

1.1 Java反射操作公有字段

Java操作User类的公有字段username时,有如下几步:
1、获取User类的Class对象
2、获取username字段对应的Field对象
3、通过Field对象操作指定User对象的username字段值

public static void main(String[] args) throws Exception {
		// 获取User类的Class对象,方法很多,这里采用Class.forName()方法
        Class clazz = Class.forName("hldf.reflecttest.User");
        // 获取User类中的username字段对应的Field对象
        Field field = clazz.getField("username");
        
        User user = new User("zhangsan", "123456");
        // 获取user对象的username字段值
        String username = (String) field.get(user);
        System.out.println(username); // 输出zhangsan

        // 设置user对象的username字段值为lisi
        field.set(user, "lisi");
        username = (String) field.get(user);
        System.out.println(username); // 输出lisi
    }

1.2 Golang反射操作公有字段

Golang操作User结构的公有字段Username时,只需要通过reflect.ValueOf()方法获取User结构对应的reflect.Value对象来进行读取和修改操作即可:

func main() {
	user := User{"zhangsan", "123456"}
	// 获取user对象对应的Value对象
	v := reflect.ValueOf(user)
	// 获取user对象的Username字段对应的Value对象
	field := v.FieldByName("Username")
	fmt.Println(field.String()) // 输出 zhangsan

	// 修改结构的字段时必须传入指针类型
	v = reflect.ValueOf(&user)
	// 调用FieldByName方法的Value对象不能是指针类型生成的,
	// 因此需先调用Elem方法获取指针指向的实际值
	field = v.Elem().FieldByName("Username")
	// 修改字段值
	field.SetString("lisi")
	fmt.Println(field.String()) // 输出 lisi
}

0x02 操作私有字段

2.1 Java反射操作私有字段

按照上面1.1中所述方式操作User类的私有字段password时是不会成功的,需做如下修改:
1、使用Class类的getDeclaredField方法获取私有字段password的Field对象
2、调用Field对象的setAccessible方法获取修改私有字段的权限

public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("hldf.reflecttest.User");
        // 获取私有字段对应的Field对象时需使用getDeclaredField方法
        Field field = clazz.getDeclaredField("password");

        // 获取修改私有字段password的权限
        field.setAccessible(true);

        User user = new User("zhangsan", "123456");
        String password = (String) field.get(user);
        System.out.println(password); // 输出123456

        field.set(user, "654321");
        password = (String) field.get(user);
        System.out.println(password); // 输出654321
    }

2.2 Golang反射操作私有字段

使用上面Golang反射操作公有字段的方法操作私有字段时,读取操作可成功执行,但修改操作出现异常,且Golang没有提供类似Java的setAccessible方法来获取修改私有字段的权限的方法,因此Golang无法修改结构的私有字段:

func main() {
	// 私有字段值可读取成功
	user := User{"zhangsan", "123456"}
	v := reflect.ValueOf(user)
	field := v.FieldByName("password")
	fmt.Println(field.String()) // 输出 123456

	// 修改私有字段时,抛出异常
	v = reflect.ValueOf(&user)
	field = v.Elem().FieldByName("password")
	field.SetString("654321") // 抛出异常,提示无法操作未导出的字段
	fmt.Println(field.String()) 
}

0x03 调用公有方法

3.1 Java反射调用公有方法

Java操作User类的公有方法publicMethod与上面操作公有字段username类似:
1、获取User类的Class对象
2、获取publicMethod方法对应的Method对象
3、通过Method对象操作指定User对象的publicMethod方法

public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("hldf.reflecttest.User");
        
        // 获取User类中publicMethod方法对应的Method对象
        Method publicMethod = clazz.getMethod("publicMethod");
        
        User user = new User();
        // 调用user对象的publicMethod方法
        publicMethod.invoke(user);
    }

3.2 Golang反射调用公有方法

与上面操作结构的字段一样,这里同样只用到了reflect.Value对象:

func main() {
	v := reflect.ValueOf(User{})
	
	// 获取user对象的PublicMethod方法对应的Value对象
	publicMethod := v.MethodByName("PublicMethod")
	
	// 调用方法,PublicMethod方法为无参方法,因此传入的参数值为空
	publicMethod.Call(nil)
}

0x04 调用私有方法

4.1 Java反射调用私有方法

同样,按照上面3.1中所述方法调用User类的私有方法privateMethod时是不会成功的,同样需做如下修改:
1、使用Class类的getDeclaredMethod方法获取私有方法privateMethod的Field对象
2、调用Method对象的setAccessible方法获取调用私有方法的权限

public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("hldf.reflecttest.User");
     
        Method privateMethod = clazz.getDeclaredMethod("privateMethod");

        // 获取调用私有方法的权限
        privateMethod.setAccessible(true);

        User user = new User();
        privateMethod.invoke(user);
    }

4.2 Golang反射调用私有方法

与上面修改私有字段结果相同,Golang同样没有提供获取调用私有方法权限的方法:

func main() {
	v := reflect.ValueOf(User{})
	
	privateMethod := v.MethodByName("privateMethod") // 获取的值为空
	
	privateMethod.Call(nil) //抛出异常
}

0x05 总结

1、Java中的类(Class类)、字段(Field类)、方法(Method类)等均有相应的类来进行表示,因此需通过这些类来对对象进行操作,并且调用这些类的方法时需传入实际要被操作的对象值;而Golang中对字段、方法等的操作只需要使用reflect.ValueOf()方法,获取到实际要被操作的对象对应的reflect.Value对象,然后通过调用Value对象的方法即可直接操作对象。
2、Java提供了setAccessible方法来获取操作私有字段和私有方法的权限,而Golang未提供类似机制,因此Golang无法通过反射来操作私有字段和私有方法。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值