MethodHandles.lookup().findSpecial() 不能获取私有方法的方法句柄嘛?
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() 可以获取私有方法的句柄,但是测试下来不可以?
哪位知道是啥原因?如果不可以,那我怎么获取私有方法句柄?
1
jinzhongyuan OP 报错信息: java.lang.IllegalAccessException: no private access for invokespecial: class com.yjz.jvm.反射.jdk7 方法句柄.User, from com.yjz.jvm.反射.jdk7 方法句柄.句柄测试
|
2
DebugTy 2020-05-14 14:35:52 +08:00
不清楚,但是用中文汉字命名这也太夸张了吧
|
3
jinzhongyuan OP @DebugTy 老哥,我是为了测试
|
4
acrisliu 2020-05-14 14:48:48 +08:00 1
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 类里面写测试代码。 |
6
jinzhongyuan OP |
7
acrisliu 2020-05-14 15:00:37 +08:00
@jinzhongyuan #6 private 的作用就是保持私有不让外部访问,你如果要在外部访问就不要用 private 。如果 private 能被随意调用,那封装就没意义了。
|
8
jinzhongyuan OP |
9
misaka19000 2020-05-14 15:04:01 +08:00
不懂什么叫“句柄”,我猜你想要的是这个
method.setAccessible() |
10
jinzhongyuan OP @misaka19000 这个东东 MethodHandle
|
11
acrisliu 2020-05-14 15:07:26 +08:00
@jinzhongyuan #8 个人用的很少
|
12
jinzhongyuan OP @acrisliu 好的,3q
|
13
Jrue0011 2020-05-14 15:21:24 +08:00
stackoverflow 上的办法是先用反射获取 method 对象,然后 method.setAccessible(),再 MethodHandles.lookup().unreflect(method),这样获得的方法句柄即使本来是 private 的也可以执行
|
14
gz911122 2020-05-14 15:28:09 +08:00
java 里面句柄是什么...
|
15
jinzhongyuan OP @Jrue0011 嗯嗯,我刚刚也查到了,3q
|
16
jinzhongyuan OP @gz911122 MethodHandle,感觉是 method 的引用
|
17
wangyanrui 2020-05-14 15:55:53 +08:00
你在当前类的 私有方法或构造器 里面,是可以用 MethodHandles.lookup().findSpecial() 找到私有方法的,不会报异常!
如果我说错了,不要捶我 |
18
jinzhongyuan OP @wangyanrui 差不多吧,当前类里面就可以
|
19
wangyanrui 2020-05-14 16:00:54 +08:00
方法句柄跟反射的权限检查不一样,他是在句柄的创建阶段完成的。
而 invokespecial 指令又是发生在调用 1. 私有实例方法 2. 构造器 3. 使用 super 关键字调用父类的实例方法或构造器, 4. 所实现接口的默认方法 但是第一个私有实例犯法,你在实例的外部,本身就木的办法获取私有实例方法,so... 你可以尝试写一个实例公开方法,里面调用私有方法 return 一个方法句柄 纯属猜测,没有验证。。。 |
20
jinzhongyuan OP @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); 外部可以获取私有方法句柄,外部可以调用成功 点赞 |
21
yukiloh 2020-05-14 16:54:25 +08:00 via Android
涨姿势了
句柄可以理解为引用的吧 |
22
jinzhongyuan OP @yukiloh 我也不是很懂,但是 api 用下来的感觉就是引用,句柄引用 Method 类
|
23
wangyanrui 2020-05-14 17:14:25 +08:00
方法句柄是为了 invokedynamic 而生的, 你用方法句柄的 findSpecial(),其实底层还是走的 invokespecial 指令
|
24
jinzhongyuan OP @wangyanrui 现实中使用方法句柄的场景多吗 /
|
25
wangyanrui 2020-05-14 17:26:22 +08:00 via Android
@jinzhongyuan 不敢回复,不知道多不多,反正我是没用过😂😂
|
26
jinzhongyuan OP @wangyanrui 好吧
|