Java 方法句柄的疑问 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jinzhongyuan
V2EX    Java

Java 方法句柄的疑问

  •  
  •   jinzhongyuan 2020-05-14 14:30:48 +08:00 1585 次点击
    这是一个创建于 1986 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    user 类

    public class User { private String name; public User(String name) { this.name = name; } public void speck(String msg) { System.ou.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() 可以获取私有方法的句柄,但是测试下来不可以?

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

    26 条回复    2020-05-14 17:28:47 +08:00
    jinzhongyuan
        1
    jinzhongyuan  
    OP
       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
        2
    DebugTy  
       2020-05-14 14:35:52 +08:00
    不清楚,但是用中文汉字命名这也太夸张了吧
    jinzhongyuan
        3
    jinzhongyuan  
    OP
       2020-05-14 14:37:29 +08:00
    @DebugTy 老哥,我是为了测试
    acrisliu
        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 类里面写测试代码。
    acrisliu
        5
    acrisliu  
       2020-05-14 14:49:13 +08:00
    @acrisliu #4 第一条是不建议在包名类名方法名等用中文,少打了几个字。
    jinzhongyuan
        6
    jinzhongyuan  
    OP
       2020-05-14 14:58:53 +08:00
    @acrisliu 受教了。另外确实在 User 类里面写就没问题。

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

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

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

    纯属猜测,没有验证。。。
    jinzhongyuan
        20
    jinzhongyuan  
    OP
       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);

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


    点赞
    yukiloh
        21
    yukiloh  
       2020-05-14 16:54:25 +08:00 via Android
    涨姿势了
    句柄可以理解为引用的吧
    jinzhongyuan
        22
    jinzhongyuan  
    OP
       2020-05-14 16:56:18 +08:00
    @yukiloh 我也不是很懂,但是 api 用下来的感觉就是引用,句柄引用 Method 类
    wangyanrui
        23
    wangyanrui  
       2020-05-14 17:14:25 +08:00
    方法句柄是为了 invokedynamic 而生的, 你用方法句柄的 findSpecial(),其实底层还是走的 invokespecial 指令
    jinzhongyuan
        24
    jinzhongyuan  
    OP
       2020-05-14 17:18:45 +08:00
    @wangyanrui 现实中使用方法句柄的场景多吗 /
    wangyanrui
        25
    wangyanrui  
       2020-05-14 17:26:22 +08:00 via Android
    @jinzhongyuan 不敢回复,不知道多不多,反正我是没用过
    jinzhongyuan
        26
    jinzhongyuan  
    OP
       2020-05-14 17:28:47 +08:00
    @wangyanrui 好吧
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5212 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 05:43 PVG 13:43 LAX 22:43 JFK 01:43
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86