Java的反射机制

 Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。

  • 反射机制的利弊

其实好处就是,增加程序的灵活性,避免将程序写死到代码里;但是坏处也有,就是性能是一个问题,反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。且不安全,通过反射机制我们能拿到类的私有成员。

  • 详解

Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。

packagecn.mldn.demo;classPerson {}Public classTestDemo {    Public staticvoidmain(String[] args) throwsException {        Person per = newPerson() ; // 正着操作        System.out.println(per.getClass().getName()); // 反着来    }}

以上的代码使用了一个getClass()方法,而后就可以得到对象所在的“包.类”名称,这就属于“反”了,但是在这个“反”的操作之中有一个getClass()就作为发起一切反射操作的开端。
Person的父类是Object类,而上面所使用getClass()方法就是Object类之中所定义的方法。
·取得Class对象:public final Class<?> getClass(),反射之中的所有泛型都定义为?,返回值都是Object。
 而这个getClass()方法返回的对象是Class类的对象,所以这个Class就是所有反射操作的源头。但是在讲解其真正使用之前还有一个需要先解释的问题,既然Class是所有反射操作的源头,那么这个类肯定是最为重要的,而如果要想取得这个类的实例化对象,Java中定义了三种方式:
方式一:通过Object类的getClass()方法取得,基本不用:

packagecn.mldn.demo;classPerson {publicclassTestDemo {publicstaticvoidmain(String[] args) throwsException {    Person per = newPerson() ; // 正着操作    Class<?> cls = per.getClass() ; // 取得Class对象    System.out.println(cls.getName()); // 反着来    }}

方式二:使用“类.class”取得,在日后学习hibernate开发的时候使用

packagecn.mldn.demo;classPerson {}publicclassTestDemo {    publicstaticvoidmain(String[] args) throwsException {        Class<?> cls = Person.class// 取得Class对象System.out.println(cls.getName()); // 反着来    }}

方式三:使用Class类内部定义的一个static方法,主要使用
·取得Class类对象:public static Class<?> forName(String className) throws ClassNotFoundException;

packagecn.mldn.demo;classPerson {}publicclassTestDemo {    publicstaticvoidmain(String[] args) throwsException {        Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象        System.out.println(cls.getName()); // 反着来    }}

那么现在一个新的问题又来了,取得了Class类的对象有什么用处呢?对于对象的实例化操作之前一直依靠构造方法和关键字new完成,可是有了Class类对象之后,现在又提供了另外一种对象的实例化方法:
·通过反射实例化对象:public T newInstance() throws InstantiationException, IllegalAccessException;
范例:通过反射实例化对象

packagecn.mldn.demo;classPerson {    @Override    publicString toString({        return"Person Class Instance .";    }}publicclassTestDemo {publicstaticvoidmain(String[] args) throwsException {    Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象        Object obj = cls.newInstance() ; // 实例化对象,和使用关键字new一样        Person per = (Person) obj ; // 向下转型        System.out.println(per);    }}

那么现在可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用?
对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
范例:回顾一下之前所编写的工厂设计模式

packagecn.mldn.demo;interfaceFruit {    publicvoideat() ;}classApple implementsFruit {    publicvoideat() {        System.out.println("吃苹果。");    };}classFactory {    publicstatic Fruit getInstance(String className{        if("apple".equals(className)){            returnnew Apple() ;        }        returnnull;    }}publicclassFactoryDemo {    publicstaticvoidmain(String[] args) {        Fruit f = Factory.getInstance("apple") ;        f.eat() ;    }}

以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?
反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。

packagecn.mldn.demo;interfaceFruit {    publicvoideat() ;}classApple implementsFruit {    publicvoideat() {        System.out.println("吃苹果。");    };}classOrange implementsFruit {    publicvoideat() {        System.out.println("吃橘子。");    };}classFactory {    publicstaticFruit getInstance(String className{        Fruit f = null;        try{            f = (Fruit) Class.forName(className).newInstance() ;        } catch(Exception e) {            e.printStackTrace();        }        returnf ;    }}publicclassFactoryDemo {    publicstaticvoidmain(String[] args) {        Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;        f.eat() ;    }}

发现,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。如果单独从开发角度而言,与开发者关系不大,但是

50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信