java高新技术总结(二)

枚举:

反汇编的结果为:

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


  其实枚举就是一个继承自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枚举的构造方法到底是什么样子呢?
   经过了几番波折,终于想到了一个办法---暴力反射。代码如下:

打印的结果为:
      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类就必须满足这些条件。

 

 

 

注解:

 

 反编译结果为:

 

 

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值