前往Shuct.Net首页

Shudepb PB反编译专家长时间以来,为业内同类软件事实上的唯一选择.细节,彰显专业.态度,决定品质.

关于反编译的搜索

Java那些不为人知的特殊方法 - 编程大巴 - 次元立方网 - 电脑知识与技术互动交流平台 次元立方 下载 编程 数据库 安全 设计 网页 系统 服务器 组网 嵌入式 基础 QQ RSS 编程大巴 开发文档 文档推荐 资源下载 在线手册 首页 > 编程大巴 > 编程语言 > Java > 正文 编程大巴 http://www.it165.net/pro Java那些不为人知的特殊方法 作者: 发布日期:2014-03-07 22:19:10 我来说两句(0) 0 Tag标签:Java那些不为人知的特殊方法 如果你用过反射并且执行过 getDeclaredMethods方法的话,你可能会感到很惊讶。你会发现很多源代码里没有的方法。或许你也看过到这些方法的一些修饰符,并且发现里面有的方法是volatile的。顺便说一句,Java面试里如果问到“什么是volatile方法?”,你可能会出一身冷汗。正确的答案应该是方法不能是volatile的。同时 getDeclaredMethods或者 getMethods返回的一些方法, Modifier.isVolatile(method.getModifiers())的返回值是true。 immutator项目的一些用户遇到过这样的问题。他发现immutator(这个项目探索了Java一些不太为人所知的细节)生成的Java源代码使用volatile作为方法的关键字,这样的代码没法通过编译。结果就是这项目没法使用。 这是怎么回事?什么又是syntethic和bridge方法? 可见性 当你创建一个内部的或者说嵌套的时候,这个类的私有变量和方法对上层的类是可见的。这个在不可变嵌套式Builder模式中用到了。这在Java语言规范里是定义好的一个行为。 package synthetic; public class SyntheticMethodTest1 { private A aObj = new A(); public class A { private int i; } private class B { private int i = aObj.i; } public static void main(String[] args) { SyntheticMethodTest1 me = new SyntheticMethodTest1(); me.aObj.i = 1; B bObj = me.new B(); System.out.println(bObj.i); } } JVM是如何处理这个的?JVM是不知道类是内部的还是说嵌套的。JVM对所有的类对一视同仁,都认为是顶层的。所有的类都会被编译的顶层的类,那些内部类编译完后会生成...$... class的类文件。 $ ls -Fart ../ SyntheticMethodTest2$A.class MyClass.java SyntheticMethodTest4.java SyntheticMethodTest2.java SyntheticMethodTest2.class SyntheticMethodTest3.java ./ MyClassSon.java SyntheticMethodTest1.java 如果你创建一个内部的类的话,编译完后它其实就是个完全的顶层的类。 那这些私有变量是如何被外部类访问的呢?如果它们是个顶层类的私有变量,它们的确也是,那为什么别的类还能直接访问这些变量? javac是这样解决这个问题的,对于那些声明为private 的字段,方法或者构造函数,如果它们还被外部类所使用,就会生成一个sythetic的方法。这些sythetic方法是用来访问最终的私有变量/方法/构造函数的。这些方法的生成也很智能,只有那些确实被外部类用到的才会生成这样的方法。 package synthetic; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class SyntheticMethodTest2 { public static class A { private A(){} private int x; private void x(){}; } public static void main(String[] args) { A a = new A(); a.x = 2; a.x(); System.out.println(a.x); for (Method m : A.class.getDeclaredMethods()) { System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getName()); } System.out.println("--------------------------"); for (Method m : A.class.getMethods()) { System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName()); } System.out.println("--------------------------"); for( Constructor c : A.class.getDeclaredConstructors() ){ System.out.println(String.format("%08X", c.getModifiers()) + " " + c.getName()); } } } 生成的这些方法的名字都取决于具体的实现,最后叫什么也不好说。我只能说在我运行的这个平台上,上述程序的输出是这样的: 2 00001008 access$1 00001008 access$2 00001008 access$3 00000002 x -------------------------- 00000111 void wait 00000011 void wait 00000011 void wait 00000001 boolean equals 00000001 String toString 00000101 int hashCode 00000111 Class getClass 00000111 void notify 00000111 void notifyAll -------------------------- 00000002 synthetic.SyntheticMethodTest2$A 00001000 synthetic.SyntheticMethodTest2$A 在上面这个程序中,我们把值赋给了变量x,然后又调用 了同名的一个方法。这会触发编译器来生成对应的synthetic方法。你会看到它生成了三个方法,应该是x变量的setter和getter方法,以及x()方法的一个synthetic方法。这些synthetic方法并不存在于getMethods方法里返回的列表中,因为这些是synthetic方法,它们是不能直接调用的。从这点来说,它们和私有方法差不多。 看一下java.lang.reflect.Modifier里面定义的常量,可以明白这些十六进制的数字代表的是什么: 00001008 SYNTHETIC|STATIC 00000002 PRIVATE 00000111 NATIVE|FINAL|PUBLIC 00000011 FINAL|PUBLIC 00000001 PUBLIC 00001000 SYNTHETIC 列表中有两个是构造方法。还有一个私有方法和一个synthetic的。私有的这个是因为我们确实定义了。synthetic的方法出现是因为我们从外部调用了内部的私有成员。这里面还没有出现bridge方法。 泛型和继承 到现在为止看起来还不错。不过我们还没有看到”volatile”的方法。 看一下java.lang.reflect.Modifier的源码你会发现0x00000040这个常量定义了两次。一次是定义成VOLATILE,还有一次是BRIDGE(后者是包内部私有的,并不对外开放)。 想出现volatile的方法,只需要写个简单的程序 就行了: package synthetic; import java.lang.reflect.Method; import java.util.LinkedList; public class SyntheticMethodTest3 { public static class MyLink extends LinkedList { @Override public String get(int i) { return ""; } } public static void main(String[] args) { for (Method m : MyLink.class.getDeclaredMethods()) { System.out.println(String.format("%08X", m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName()); } } } 我们的这个链表,有一个返回String的get(int)方法。先别讨论代码整洁的问题了。这只是段演示这个主题的示例代码而已。简洁的代码当然也同样会出现问题,不过越复杂的代码越难发现问题罢了。 输出 是这样的: 00000001 String get 00001041 Object get 我们有两个get方法。一个是代码里的这个,另外一个是synthetic和bridge的方法。用javap反编译后会是这样的: public java.lang.String get(int); Code: Stack=1, Locals=2, Args_size=2 0: ldc #2; //String 2: areturn LineNumberTable: line 12: 0 public java.lang.Object get(int); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: iload_1 2: invokevirtual #3; //Method get:(I)Ljava/lang/String; 5: areturn 有趣的是,两个方法的签名是一模一样的,只有返回类型不同。这个在JVM里面是允许的,不过在Java语言里是不行的。bridge的这个方法别的啥也不干,就只是调用了下原始的那个方法。 为什么我们需要这个synthetic方法呢?谁来调用它。比如现在有段代码想要调用一个非MyLink类型变量的get(int)方法: List a = new MyLink(); Object z = a.get(0); 它不能调用返回String的方法,因为List里没这样的方法。为了解释的更清楚一点,我们重写下add方法而不是get方法: package synthetic; import java.util.LinkedList; import java.util.List; public class SyntheticMethodTest4 { public static class MyLink extends LinkedList { @Override public boolean add(String s) { return true; } } public static void main(String[] args) { List a = new MyLink(); a.add(""); a.add(13); } } 我们会发现 这个bridge方法 public boolean add(java.lang.Object); Code: Stack=2, Locals=2, Args_size=2 0: aload_0 1: aload_1 2: checkcast #2; //class java/lang/String 5: invokevirtual #3; //Method add:(Ljava/lang/String;)Z 8: ireturn 不仅调用 了原始的方法,它还进行了类型检查。这个是在运行时进行检查的,并不是JVM自己来检查。正如你所想,在18行的地方会抛出一个异常: Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at synthetic.SyntheticMethodTest4$MyLink.add(SyntheticMethodTest4.java:1) at synthetic.SyntheticMethodTest4.main(SyntheticMethodTest4.java:18) 下次如果你在面试中被问到volatile方法的话,说不定面试官知道的还没你多:-) 译者注:其实作者说到最后 也没讲完到底什么是volatile方法,其实volatile方法如篇首所说,是不存在的,所谓的volatile方法就是指bridge方法。由于在修饰符中volatile和bridge是同一个值,在之前版本的javap中存在一个BUG,一个bridge方法在反编译后会显示成volatile,所以存在”volatile方法”的说法。 原创文章转载请注明出处:deepinmind 延伸阅读: 返回到首页 返回到编程大巴 Win32 OpenGL ASP.NET MVC cocos2d-x WCF Linq Android QT MFC JQuery 使用.NET JustDecompile来反编 前言  在项目的进行中有时会碰到需要去了解由第三方... 详细 快快乐乐学LINQ系列OrderBy(), ThenBy()简介 VisualStudio编译档案的小技巧 Xamarin通过Native Code呼叫JavaScript fu Windows Phone开发 Tilt Effect 今日排行 周排行 月排行 java定时器+多线程(池)+java队列的简单... java查询oracle数据库所有表DatabaseMe... Spring MVC前台属性数据的传递和后台属... Java NIO框架Netty教程(二) 白话概念 将java中Double类型的科学计数法转换为... Java NIO框架Netty教程(一) Hello Netty POI用addPicture插入图片到word里面无法... 使用spring4.0+maven构建超简单的web项目 使用Sun的FtpClient做FTP上传下载 Java NIO框架Netty教程(四) ChannelBuffer Spring MVC前台属性数据的传递和后台属... POI用addPicture插入图片到word里面无法... java定时器+多线程(池)+java队列的简单... Java开源报表JasperReport、iReport4.5... Java NIO框架Netty教程(一) Hello Netty Java NIO框架Netty教程(十三) 并发访... Java NIO框架Netty教程(十四) Netty中... Java NIO框架Netty教程(十五)-利用Net... DOS命令行中用MAVEN构建Java和Java Web项目 使用spring4.0+maven构建超简单的web项目 Spring MVC前台属性数据的传递和后台属... POI用addPicture插入图片到word里面无法... Java NIO框架Netty教程(一) Hello Netty java定时器+多线程(池)+java队列的简单... Java开源报表JasperReport、iReport4.5... Java NIO框架Netty教程(十三) 并发访... Java NIO框架Netty教程(十四) Netty中... Servlet3.0下基于注解的SpringMVC3.1配... Java NIO框架Netty教程(十五)-利用Net... 将java中Double类型的科学计数法转换为... 最新文章 openfire3.9.1源码部署及运行 AndroidSQLite数据库创建和使用实战(一) jQuery下拉滚动条刷新ajax获取数据 AndroidSQLite数据库创建和使用实战(二) Axiom 3D数据绑定基本流程 HTML5+NodeJs实现WebSocket即时通讯 WebKit中的JavaScriptBinding Windows下编译valgrindforAndroid Hadoop(5)MapReduce 排序:次排序(... AndroidBinderIPC机制 热门专题 李华明iOS-Cocos2d游戏开发 本教程为 李华明 编著的iOS-Cocos2d游戏开发系列教程:教程涵盖关于i...... 详细 Directx11 游戏编程入门教程 专题主要学习DirectX的初级编程入门学习,对Directx11的入门及初学者有...... 详细 Javascript 面向对象编程 "面向对象的JavaScript"这一说法多少有些冗余,因为JavaScript 语言本...... 详细 谷歌在美搜索市场有所下滑 北京时间4月12日凌晨消息,市场研究公司comScore周...... 详细 腾讯QQ遭黑客攻击 受损用户资料被修改且维权难 视频行业洗牌 优酷土豆合并发展 Win8系统难以出身 或将逼迫Windows9现身 iPad商标纠纷案 苹果唯冠或将和解 黑客攻破联合国网站 窃取部分内部资料 淘宝网遭走私团伙利用 成走私电脑销售渠道 次元立方 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式 本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规