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