Friday, March 4, 2016

Java Decompile Code



http://blog.csdn.net/u013256816/article/details/50778902
int i=0; int y = i++ + ++i; i=0; int z = i++ + ++i + ++i + ++i + i++ + ++i;
在java中(eclipse),运行结果:2 19。 
0 iconst_0 *向栈顶压入一个int常量0*,java基于栈操作,这里首先将代码[int i=0;]中的0压入栈顶 1 istore_0 [i] *将栈顶元素存入本地变量0[这个变量0就是i]中*,.此时栈内无元素 2 iload_0 [i] *将本地变量0[i]放入栈顶中*,此时栈内有一个元素,即为0 3 iinc 0 1 [i] *将制定的int型变量[i]增加指定值[1]*,这时i=0+1=1 6 iinc 0 1 [i] *将制定的int型变量[i]增加指定值[1]*,这时i=1+1=2 9 iload_0 [i] *将本地变量0[i]放入栈顶中*,此时栈内有两个元素,02,栈顶为2 10 iadd *将栈顶两个int类型数值相加*,结果压入栈顶,此时栈内一个元素为0+2=2 11 istore_1 [y] *将栈顶元素存入本地变量1中*[变量1就是y] 12 getstatic java.lang.System.out : java.io.PrintStream [21] 15 iload_1 [y] 16 invokevirtual java.io.PrintStream.println(int) : void [27] 19 return
可以看到i++ + ++i的运行结果:遇到i++是先取i(初始i=0)的值(压入栈),然后进行自加(此时i=1),遇到+号标记继续(脑补一下逆波兰表达式,这里就不说明java的词法分析、语法分析、语义分析、代码生成的过程了),遇到++i,先进行自加(此时i=2),然后取i的值(压入栈),然后将栈顶两元素相加即可结果。 
假如有个变量m=i++ + i++ + ++i(i初始为0)那么结果等于多少呢,我们来分析一下。 
初始i=0, 遇到i++,将i的值压入栈(栈内一个元素:0),自加,此时i=1,遇到+号标记继续,遇到i++,将i值压入栈内(栈内元素:1,0),算上之前标记的+号,栈内两元素相加之后压入栈(栈内元素:1),i值自加,此时i=2,遇到+号标记继续,遇到++i,将i值自加,此时i=3压入栈内(栈内元素3,1),算上之前标记的+号,栈内两元素相加之后入栈(栈内元素为4),最后将栈顶元素存入本地变量m中,结束。整个相加过程m=0+1+3=4. 到这里,如果觉得有疑问可以打开编译器跑一下m=i++ + i++ + ++i(i初始为0)。 
那么int z = i++ + ++i + ++i + ++i + i++ + ++i(初始i=0);可以得到的结果为z=0+2+3+4+4+6=19. 
public static Map<String,String> m = new HashMap<String, String>(){ { put("key1","value1"); } };

那么这段代码的背后到底是什么呢?同样祭出我们的反编译。
发现生成了两个class文件,分别为JavapTest2.class和JavapTest2$1.class.
public static Map<String,String> m = new HashMap<String, String>(){ { m.put("key1","value1"); }
这样,发现编译器也没有报错,但是这样可不可以呢?在类中加入一个main方法:public static void main(String args[]){}运行一下,报如下错误(ExceptionInInitializerError):
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
    at interview.JavapTest2$1.<init>(JavapTest2.java:10)
    at interview.JavapTest2.<clinit>(JavapTest2.java:8)
首先JavapTest2的程序入口是main方法,这个方法什么事都没干,但是这里已经触发了对JavaTest2的类的实例化(就是上面异常中的<clinit>),那么运行的是这段:
  static {};
     0  new interview.JavapTest2$1 [12]
     3  dup
     4  invokespecial interview.JavapTest2$1() [14]
     7  putstatic interview.JavapTest2.m : java.util.Map [17]
    10  return
这段指令是首先是new JavaTest2$1这个匿名内部类,然后dup(将当前栈顶元素复制一份,并压入栈中),然后调用匿名内部类的构造函数,直到这里根本没有interview.JavapTest2.m的什么事,所以执行到这一步还没有m什么鸟事。
 interview.JavapTest2.m此时为null. 因为m为static类型,在类加载之后的准备阶段会为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量的定义为:
 public static int value = 123;
 那变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。
 这里的m是引用类型,引用类型的零值是null.
附:ExceptionInInitializerError在JVM规范中这样定义: 
1. 如果JVM试图创建类ExceptionInInitializerError的新实例,但是因为出现OOM而无法创建新实例,那么就抛出OOM作为代替; 
2. 如果初始化器抛出一些Exception,而且Exception类不是Error或者它的某个子类,那么就会创建ExceptionInInitializerError类的一个新实例,并用Exception作为参数,用这个实例代替Exception.

语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了。这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能、或能提升语法的严谨性、或能减少编码出错的机会。Java提供给了用户大量的语法糖,比如泛型、自动装箱、自动拆箱、foreach循环、变长参数、内部类、枚举类、断言(assert)等。 
反编译出来的内容很多,看不懂也没关系,关键看到Iterator这个标志,其实在对有实现Iterable接口的对象采用foreach语法糖的话,编译器会将这个for关键字转化为对目标的迭代器使用。 
for(java.util.Iterator i$ = list.iterator(); i$.hasNext();) { String s = (String) i$.next(); { System.out.println(s); } }

注:如果要想使自己自定义的类可以采用foreach语法糖就必须实现Iterable接口。
细心的朋友可能会注意到,java中的数组也可以采用foreach的语法糖,但是数组并没有实现Iterable接口呀。 
可以看到对于数组而言,其实就是转换为普通的遍历而已;
关于foreach语法糖的信息就这样结束了嚒? 显然没有!
对于实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历,所以第一个例子中有欠妥之处。 
实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。 
可以使用类似如下的代码作判断:
        if (list instanceof RandomAccess)
        { 
            for (int i = 0; i < list.size(); i++){}
        }else
        {
            Iterator<?> iterator = list.iterable(); while (iterator.hasNext()){iterator.next()}
        }
其实如果看过ArrayList源码的同学也可以注意到:ArrayList底层是采用数组实现的,如果采用Iterator遍历,那么还要创建许多指针去执行这些值(比如next();hasNext())等,这样必然会增加内存开销以及执行效率。
public interface RandomAccess 
Marker interface used by List implementations to indicate that they support fast (generally constant time) random access. The primary purpose of this interface is to allow generic algorithms to alter their behavior to provide good performance when applied to either random or sequential access lists. 
The best algorithms for manipulating random access lists (such as ArrayList) can produce quadratic behavior when applied to sequential access lists (such as LinkedList). Generic list algorithms are encouraged to check whether the given list is an instanceof this interface before applying an algorithm that would provide poor performance if it were applied to a sequential access list, and to alter their behavior if necessary to guarantee acceptable performance. 
It is recognized that the distinction between random and sequential access is often fuzzy. For example, some List implementations provide asymptotically linear access times if they get huge, but constant access times in practice. Such a List implementation should generally implement this interface. As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop: 
for (int i=0, n=list.size(); i < n; i++) 
list.get(i);
runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts