您的位置:首页 > 编程语言 > Java开发

java高新技术总结(二)

2011-04-14 18:29 218 查看
枚举:

enum Week{
MON(2),TUE,;
private Week(){

}
private Week(int i){

}

}


反汇编的结果为:

final class Week extends java.lang.Enum{
public static final Week MON;

public static final Week TUE;

public static Week[] values();
Code:
0:   getstatic       #1; //Field $VALUES:[LWeek;
3:   invokevirtual   #2; //Method "[LWeek;".clone:()Ljava/lang/Object;
6:   checkcast       #3; //class "[LWeek;"
9:   areturn

public static Week valueOf(java.lang.String);
Code:
0:   ldc_w   #4; //class Week
3:   aload_0
4:   invokestatic    #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Lj
ava/lang/String;)Ljava/lang/Enum;
7:   checkcast       #4; //class Week
10:  areturn

static {};
Code:
0:   new     #4; //class Week
3:   dup
4:   ldc     #7; //String MON
6:   iconst_0
7:   iconst_2
8:   invokespecial   #8; //Method "<init>":(Ljava/lang/String;II)V
11:  putstatic       #9; //Field MON:LWeek;
14:  new     #4; //class Week
17:  dup
18:  ldc     #10; //String TUE
20:  iconst_1
21:  invokespecial   #11; //Method "<init>":(Ljava/lang/String;I)V
24:  putstatic       #12; //Field TUE:LWeek;
27:  iconst_2
28:  anewarray       #4; //class Week
31:  dup
32:  iconst_0
33:  getstatic       #9; //Field MON:LWeek;
36:  aastore
37:  dup
38:  iconst_1
39:  getstatic       #12; //Field TUE:LWeek;
42:  aastore
43:  putstatic       #1; //Field $VALUES:[LWeek;
46:  return

}


反汇编的结果着实让我吃了一惊。

其实枚举就是一个继承自java.lang.Enum的final类型的类,枚举类通过static{}静态语句块对枚举进行初始化。我们在枚举中定义了两个构造函数。
但是反汇编的结果却没有看见构造函数的代码。这是怎么回事呢?在static{}语句块中确实有invokespecial指令,难道是调用父类的构造方法?java.
lang.Enum类有个构造方法protect Enum(String name,int ordinal),不过编译器不容许我们调用,也不容许我们继承java.lang.Enum类,似乎专门为
枚举服务。static{}中调用的Method "<init>":(Ljava/lang/String;I)V,这个构造函数很符合java.lang.Enum类的构造方法。但是
Method "<init>":(Ljava/lang/String;II)V这个方法调用很奇怪,这个方法这什么地方定义的呢?
4: ldc #7; //String MON
6: iconst_0
7: iconst_2
8: Method "<init>":(Ljava/lang/String;II)V
通过这个语句可以看出在这个方法接受的参数值为String MON,int 0,int 2。这显然是构造枚举中的MON。这个很像超类的构造方法接受的参数
后面再添加个int型的参数,也就是protect Enum(String name,int ordinal,int other)。但这构造函数是在哪定义的呢?Week的真正构造函数是什么呢?
由于反汇编无法得出结果,又没有源代码。想了很多办法,比如在构造方法中抛出运行时异常,发现不仅抛出了该异常,而且还抛出了
ExceptionInInitializerError,这个异常通常是在装载类并执行静态初始化的时候就发生了异常,这说明Week枚举中的static{}确实调用了该构造方法。
创建了一个Week对象。但是Week枚举的构造方法到底是什么样子呢?
经过了几番波折,终于想到了一个办法---暴力反射。代码如下:

Class clazz = Class.forName("enumtest.Week");
Constructor[] c = clazz.getDeclaredConstructors();
for(int i =0;i<c.length;i++){
System.out.println(c[i]);
}


打印的结果为:
private enumtest.Week(java.lang.String,int)
private enumtest.Week(java.lang.String,int,int)
发现Week枚举中确实和java.lang.Enum类有这同样参数的默认构造方法,我们在程序中定义的构造方法,都是在默认构造方法参数后面添加我们定义
参数的。但是java编译器是怎么做到这一点的?javap为什么没有反编译出构造方法?这很让人费解。
枚举的这些特性,使得他具备单例的特征。用枚举实现单例确实是个不错的办法。

反射:

类名.class
这个用法看上去很像是对类中静态变量的调用,但实际上不是这样,这种用法有点像数组的length属性,java编译会按照特殊方式编译它。这个代表
类的字节码,也就是.class文件。编译器会在常量池中定义一个代表该类的表项,Class clazz = Object.class该语句先把常量池中代表Object的表项
从常量池中压入栈顶,并存储在clazz变量中。并没有创建对象的指令。这是否说明了java虚拟机在执行这条语句是并未创建Class对象?
通过查看Class类的源代码,Class类中只有一个私有的无参数的构造函数,无法通过new关键字创建Class对象。如果想获得Class对只能通过forName
静态方法。但这不能说明Class类是一个单例,因为forName方法会根据传入的类的完全限定名返回不同的Class对象。那为什么会这么做呢?我认为Class
类是特殊的单例,
Object.class==new Object().getClass==Class.forName("java.lang.Object");
这三个语句返回的是同一个Class对象,这说明每个Class对象对应的是该Class对象的类的字节码。也就是说程序中无论通过什么样的方式得到同
样类的Class对象都是同一个对象。那java虚拟机是如何做到这一点的呢?由于forName,和getClass方法都是native的,而java编译器在编译 类名.class
语句时只是将常量池中所对应的表项压入栈顶而已。 我们无法得知其实现细节,究竟java虚拟机是在加载类的过程中就创建了该类的Class对象。还是
执行到上面的语句时才会创建Class对象。这些问题还有待研究。

反射就是将java类的元素映射成java类。Class类就是对所有java类的统一描述。类中的方法,字段,构造方法分别由Method,Filed,Constructor描述
。为什么会把构造方法和其他方法分开呢,其实在java虚拟机中,对象的构造和初始化是分开的。但在java语言中把这个系列动作统一成一个动作。就是说
创建对象时必须由某个构造函数对其进行初始化。
虽然同一个类的Class对象都是==的,但是通过对象获得的Method,Field,Constructor都是!=的。

内省:

内省是java反射的一个应用。它假定java类满足某种规则,并且根据这种规则来推断java类中的字段,构造方法,以及方法。并用java反射对其进行操作
可以通过配置文件的信息的信息,这样可以程序变得很灵活。最典型的例子就是对javabean进行内省,一个满足javabean规范的java类就是一个javabean。
例如javabean必须为每个字段定义getter和setter方法,并且提供无参的构造函数。在很多框架中都有这样的应用,比如在hibernate中,hibernate-hbm.xml
映射文件中配置的java类就必须满足这些条件。

注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnno{
}
class AnnoTest{
@MyAnno
public void xx(){
}
}


反编译结果为:

interface MyAnno extends java.lang.annotation.Annotation{
}


class AnnoTest extends java.lang.Object{
AnnoTest();
Code:
0:   aload_0
1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
4:   return

public void xx();
Code:
0:   return

}


可以看出其实注解就是继承自java.lang.annotation.Annotation的接口,从反编译的结果看并没有看见任何注解的痕迹,似乎注解只是留给java编译器
看的。但是注解却可以保持到运行期间,这是如何做到的呢?注解的方法被称为属性,可以在声明注解时为其赋值。这让我联想到了javabean,保留在
运行时的注解,是不是会为其动态地创建实现类?注解似乎是专门为反射用的。因为在java.lang.reflect包中专门有一个AccessibleObject类来操作注解。
如在spring2.5中通过给类加个注解就可以决定这个类是否交给spring管理,这样免去了在配置文件中进行复杂的配置。
这是否说明了java编译器在.class文件中动了手脚?当然这些只是猜测而已。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: