java中的反射

来源:互联网 发布:迪蒙小额贷款系统源码 编辑:程序博客网 时间:2024/06/05 02:30

1.什么是反射

就是可以运行时类型信息使得你可以在程序运行时发现和使用类型的信息。

2.java中的Class类的创建

方式一:Class.forName("...");

所有的类都是在第一次使用时,被动态的加载到jvm中。当程序创建第一个对类的静态成员的引用时,就会加载该类。也说明了构造器也是类的静态方法。而且使用new操作符创建类的新对象也会被当作对类的静态成员的引用。(可以发现java程序并非在开始运行前就被完全加载,其各部分是在必需时才会加载的)。

示例:

public class ClassObject {public static void main(String[] args) {new Apple();try {Class.forName("cn.spy.reflect.Banana");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Apple{static {System.out.println("Apple");}}class Banana{static{System.out.println("Banana");}}
结果:


可以发现一旦类的Class对象被载入了内存,它就被用来创建这个类的所有对象。static的初始化也是在类加载时进行的。如果我们想要在运行时使用类型的信息,就必须首先得获取对应的Class对象的引用。因为Class.forName的形式获取类的结果在编译期是不可知的,所以毫无疑问,如果找不到加载的类,就会报找不到类的异常。

方式二:对象.getClass();

如果我们已经拥有了类的对象,可以采用getClass方法来获取类的引用。返回Class类对象。通过该对象可以获取类名(getName)、方法名(getMethod)、超类(getSupperClass)、包(getPackage)等。

方式三:字面量方式:类名.class

注意一点是当使用 .class 方式来创建对Class对象的引用时,不会自动初始化该Class对象。

为了使用字面量方式:

①加载:采用类加载器来执行,查找字节码,从字节码中创建一个Class对象。

②链接:在链接阶段将验证类中的字节码,为静态域分配存储空间,还要分析该类创建的对其他类的所有引用。

③初始化:如果该类还有父类,那么就对父类也进行初始化,执行构造器和静态初始化代码块。

示例:

public class ClassObject {public static void main(String[] args) {Class appleClass =Apple.class;System.out.println(Apple.price);System.out.println(Apple.amount);try {Class.forName("cn.spy.reflect.Banana");} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Apple{static final int price =10;static int amount =100;static {System.out.println("Apple");}}class Banana{static int price =11;static{System.out.println("Banana");}}class Orange{static {System.out.println("Orange");}}
结果:


可以发现使用字面量,并不会马上创建类,尽管调用静态不可变的变量,但是仍未初始化,但是调用静态非不可变的变量时,将进行初始化。Class.forName创建类时,将立即进行初始化。

3.java中使用Class类创建对象

示例:

public class ClassObject {public static void main(String[] args){Class<?> clazz =new Apple().getClass().getSuperclass();try {clazz.newInstance();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}new Banana();}}class Fruit{static{System.out.println("Fruit");}}class Apple extends Fruit{static {System.out.println("Apple");}}class Banana{static{System.out.println("Banana");}}
结果:


Class类可以获取父类。并不知道是什么类型,使用?符号(意思是我知道应该指定类型,但是我此时不知道,这样就不会有警告)。调用newInstance方法就是虚拟构造器不知道确切类型,但是得正确的创建对象。而且获取该对象也还需要转型,可以发现如果我们随意写个类型,就会报类型强制转换异常。由于newInstance是用来创建对象的,如果没有对应的无参构造方法,也会报异常。所以这个newInstance的缺陷也暴露了,只能使用无参的构造方法创建对象。

4.类型检查

instanceof  :

示例:

public class ClassObject {public static void main(String[] args){Class<?> clazz =new Apple().getClass().getSuperclass();try {/* 错误写法:报强制类型转换异常 * String str =(String) clazz.newInstance(); * */if(clazz.newInstance() instanceof String){String str =(String) clazz.newInstance();}} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}new Banana();}}class Fruit{static{System.out.println("Fruit");}}class Apple extends Fruit{static {System.out.println("Apple");}}class Banana{static{System.out.println("Banana");}}
结果:

如果我们使用instanceof做类型检查,判断此处的对象clazz.newInstance是否是String类型的对象,如果不是String类型的对象,或者clazz.newInstance对象为null,则返回false。判断其是否可以做强制类型转换,以免报异常。

动态instanceof:isInstance()

示例:

public static void main(String[] args){Class<?> clazz =new Apple().getClass().getSuperclass();try {if(String.class.isInstance(clazz.newInstance())){String str =(String) clazz.newInstance();}} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}new Banana();}
结果:


这种是使用了一种动态地方式来测试对象的途径。并且相对instanceof而言会更灵活一些。

5.java中的动态代理

java中的动态代理也应用了反射的知识:    聊聊代理模式


6.内省与反射的区别

反射是让你可以通过名称来得到对象(类,属性,方法)的技术。

内省是Java语言对Bean类属性、事件的一种缺省处理方法。我们使用getter/setter方法来存取属性值,这些定义在java.beans包中的规则是一种默认的规则方式,不需要我们去刻意了解。






原创粉丝点击