java高新技术总结(二)
2011-04-14 18:29
218 查看
枚举:
反汇编的结果为:
反汇编的结果着实让我吃了一惊。
其实枚举就是一个继承自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文件中动了手脚?当然这些只是猜测而已。
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文件中动了手脚?当然这些只是猜测而已。
相关文章推荐
- java高新技术开发实战重点总结
- java高新技术总结(一)
- 黑马程序员——高新技术---Java基础-集合特点和数据结构总结
- 传智播客java培训2010年4月16日JAVA高新技术总结(1)
- java-高新技术总结
- 黑马程序员-Java基础总结15——高新技术Day01
- JAVA高新技术---反射总结
- java 高新技术 总结(一)
- 黑马程序员-Java基础总结16——高新技术Day02(JavaBean、注解与泛型)
- 黑马程序员—Java高新技术总结(一)
- 黑马程序员 Java自学总结十八 Java高新技术第一天
- 黑马程序员---Java高新技术反射知识点总结
- 黑马程序员——JAVA高新技术总结(一)
- 黑马程序员 Java自学总结十九 Java高新技术第二天
- 黑马程序员——JAVA高新技术总结(一)
- 黑马程序员_java高新技术总结【1】(枚举和部分java新特性)
- java高新技术【3】(枚举总结)
- 黑马程序员 JAVA__高新技术--泛型总结
- 黑马程序员——JAVA高新技术总结(二)
- 黑马程序员__JAVA高新技术--反射、注解总结