Java 方法句柄的疑问

2020-05-14 14:30:48 +08:00
 jinzhongyuan

MethodHandles.lookup().findSpecial() 不能获取私有方法的方法句柄嘛?

user 类

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public void speck(String msg) {
        System.out.println(name + "在说:" + msg);
    }

    private void sing() {
        System.out.println(name + "在私有方法里唱歌");
    }
}

测试类

public class 句柄测试 {
    @SneakyThrows
    @Test
    public void 句柄测试() {
        User user = new User("张三");

        MethodHandles.Lookup lookup = MethodHandles.lookup();

        MethodHandle speckMethodHandle = lookup.findVirtual(User.class, "speck", MethodType.methodType(void.class, String.class));

        //此处执行没问题,"speck" 是 public 方法
        speckMethodHandle.invokeExact(user, "今天天气不错");

        //此处报错
        MethodHandle singMethodHandle = lookup.findSpecial(User.class, "sing", MethodType.methodType(void.class), User.class);

        singMethodHandle.invokeExact(user);
    }
}

报错信息

java.lang.IllegalAccessException: no private access for invokespecial: class com.yjz.jvm.反射.jdk7 方法句柄.User, from com.yjz.jvm.反射.jdk7 方法句柄.句柄测试

	at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
	at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
	at java.lang.invoke.MethodHandles$Lookup.findSpecial(MethodHandles.java:1002)
	at com.yjz.jvm.反射.jdk7 方法句柄.句柄测试.句柄测试(句柄测试.java:24)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)

Disconnected from the target VM, address: '127.0.0.1:50979', transport: 'socket'

Process finished with exit code -1

我看网上博客说 MethodHandles.lookup().findSpecial() 可以获取私有方法的句柄,但是测试下来不可以?

哪位知道是啥原因?如果不可以,那我怎么获取私有方法句柄?

1178 次点击
所在节点    Java
26 条回复
jinzhongyuan
2020-05-14 14:32:09 +08:00
报错信息: java.lang.IllegalAccessException: no private access for invokespecial: class com.yjz.jvm.反射.jdk7 方法句柄.User, from com.yjz.jvm.反射.jdk7 方法句柄.句柄测试
DebugTy
2020-05-14 14:35:52 +08:00
不清楚,但是用中文汉字命名这也太夸张了吧
jinzhongyuan
2020-05-14 14:37:29 +08:00
@DebugTy 老哥,我是为了测试
acrisliu
2020-05-14 14:48:48 +08:00
1. 无论是否测试,不建议在包名类名方法名等,比如这个日志中英混合看起来就很乱;
2. 看 findSpecial()方法的文档:Before method resolution, if the explicitly specified caller class is not identical with the lookup class, or if this lookup object does not have private access privileges, the access fails. 文档写明了如果 caller 没有访问 lookup 私有方法的权限,就会报错。
3. 解决办法:直接在 User 类里面写测试代码。
acrisliu
2020-05-14 14:49:13 +08:00
@acrisliu #4 第一条是不建议在包名类名方法名等用中文,少打了几个字。
jinzhongyuan
2020-05-14 14:58:53 +08:00
@acrisliu 受教了。另外确实在 User 类里面写就没问题。

但是我在 User 里面就没有必要用方法句柄了吧?
acrisliu
2020-05-14 15:00:37 +08:00
@jinzhongyuan #6 private 的作用就是保持私有不让外部访问,你如果要在外部访问就不要用 private 。如果 private 能被随意调用,那封装就没意义了。
jinzhongyuan
2020-05-14 15:02:32 +08:00
@acrisliu 哦哦,也是,谢谢大佬。

另外想问一下方法句柄用的多吗?我看框架里面大部分是用的反射,没什么方法句柄的场景
misaka19000
2020-05-14 15:04:01 +08:00
不懂什么叫“句柄”,我猜你想要的是这个

method.setAccessible()
jinzhongyuan
2020-05-14 15:05:32 +08:00
@misaka19000 这个东东 MethodHandle
acrisliu
2020-05-14 15:07:26 +08:00
@jinzhongyuan #8 个人用的很少
jinzhongyuan
2020-05-14 15:11:23 +08:00
@acrisliu 好的,3q
Jrue0011
2020-05-14 15:21:24 +08:00
stackoverflow 上的办法是先用反射获取 method 对象,然后 method.setAccessible(),再 MethodHandles.lookup().unreflect(method),这样获得的方法句柄即使本来是 private 的也可以执行
gz911122
2020-05-14 15:28:09 +08:00
java 里面句柄是什么...
jinzhongyuan
2020-05-14 15:36:17 +08:00
@Jrue0011 嗯嗯,我刚刚也查到了,3q
jinzhongyuan
2020-05-14 15:36:54 +08:00
@gz911122 MethodHandle,感觉是 method 的引用
wangyanrui
2020-05-14 15:55:53 +08:00
你在当前类的 私有方法或构造器 里面,是可以用 MethodHandles.lookup().findSpecial() 找到私有方法的,不会报异常!
如果我说错了,不要捶我
jinzhongyuan
2020-05-14 15:58:21 +08:00
@wangyanrui 差不多吧,当前类里面就可以
wangyanrui
2020-05-14 16:00:54 +08:00
方法句柄跟反射的权限检查不一样,他是在句柄的创建阶段完成的。
而 invokespecial 指令又是发生在调用
1. 私有实例方法
2. 构造器
3. 使用 super 关键字调用父类的实例方法或构造器,
4. 所实现接口的默认方法
但是第一个私有实例犯法,你在实例的外部,本身就木的办法获取私有实例方法,so...
你可以尝试写一个实例公开方法,里面调用私有方法 return 一个方法句柄

纯属猜测,没有验证。。。
jinzhongyuan
2020-05-14 16:38:08 +08:00
@wangyanrui 原来你是扮猪吃老虎啊

user.java

//提供一个 public 方法,返回私有方法的句柄,这样外面就能获取私有方法句柄了
@SneakyThrows
public MethodHandle getSingMethodHandle() {
return MethodHandles.lookup().findVirtual(this.getClass(), "sing", MethodType.methodType(void.class));
}

------------------------------------------
Test.java

User user = new User("张三");

MethodHandle privateMethodHandle =user.getSingMethodHandle();

privateMethodHandle.invoke(user);

外部可以获取私有方法句柄,外部可以调用成功


点赞

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/671606

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX