1、反射机制
我们在正常的使用过程之中只关心类产生对象,而后通过对象调用类之中的方法,可是,除了这种传统的方式外,也可以利用反射操作。
1.1、认识反射
既然有反,那么一定存在有正,正是指通过类产生对象,而后通过对象执行操作。而反呢?通过对象找到它所在类的信息。所有的对象都支持反这一操作,因为Object类有一个方法:public final Class<?> getClass()。
1 package cn.kvikon.demo; 2 import java.util.Date; 3 4 public class TestDemo { 5 public static void main(String [] args) throws Exception { 6 Date date = new Date() ; 7 System.out.println(date.getClass()) ; 8 } 9 }
现在的代码可以直接通过对象取得它所对应的类型,那么也就不难理解instanceof关键字的处理方式。
1.2、实例化Class类对象
在Object类之中定义的getClass()方法,发现里面返回的类型是Class,而Class是作为一切反射操作的源头。所以首先一定要清楚如何实例化Class实例化对象,实际上此类对象有三种实例化方式:
· 方式一:利用Object类之中的getClass()方法;
1 package cn.kvikon.demo ; 2 3 import java.util.Date ; 4 5 public class TestDemo { 6 public static void main(String [] args) throws Exception { 7 Date date = new Date() ; 8 Class<?> cls = date.getClass() ; 9 System.out.println(cls) ; 10 } 11 12 }
· 方式二:利用“类.class”取得;
1 package cn.kvikon.demo ; 2 3 import java.util.Date ; 4 5 public class TestDemo { 6 public static void main(String [] args) throws Exception { 7 Class<?> cls = Date.class ; 8 System.out.println(cls) ; 9 } 10 11 }
· 方式三:Class类内部提供了一个static方法;
public static Class<?> forName(String className) throws ClassNotFoundException
1 package cn.kvikon.demo ; 2 3 public class TestDemo { 4 public static void main(String [] args) throws Exception { 5 Class<?> cls = Class.forName("java.util.Date") ; 6 System.out.println(cls) ; 7 } 8 }
以上给出的三个方式最没用处的就是第一种方式了,而第二种方式是在日后学习Hibernate的时候使用,现阶段主要使用第三种方式,而第三种方式是Spring实现基础。
1.3、 利用反射实例化对象
对象的实例化操作在之前只能够利用关键字new实现,但是从今天开始,对象实例化也可以利用反射机制进行处理,因为在Class类之中定义了一个方法:“public T newInstance() throws InstantiationException,IllegalAccessException”;
package cn.kvikon.demo ; class Person { public String toString () { return "是个人!" ; } } public class TestDemo { public static void main(String [] args) throws Exception { Class<?> cls = Class.forName("cn.kvikon.demo.Person") ; Person per = (Person) cls.newInstance() ; System.out.println(per) ; } }
疑问?既然关键字new和反射都可以实例化对象,那么有什么区别呢?
· 通过工厂设计模式来分析问题,使用关键字new。
package cn.kvikon.demo ; interface Fruit { public void eat () ; } class Apple implements Fruit { public void eat () { System.out.println("吃苹果") ; } } class Factory { public static Fruit getInstance (String className) { if ("apple".equals(className)) { return new Apple () ; } return null ; } public class FactoryDemo { public static void main (String [] args) { Fruit fru = Factory.getInstance("apple") ; fru.eat() ; } } }
如果说现在增加了一个Fruit接口子类,那么就需要修改Factory类。很明显这种做法很蠢,希望最适合的工厂可以不做任何修改而适应所有的子类。但是此时的代码不行,因为造成这一关键性问题的主要原因是new(造成Factory类与每一个接口子类直接耦合)。
· 通过反射处理,反射处理的好处是主需要有“包.类名称”就可以实例化。
1 package cn.kvikon.demo ; 2 interface Fruit { 3 public void eat () ; 4 } 5 class Apple implements Fruit { 6 public void eat () { 7 System.out.println("吃苹果") ; 8 } 9 } 10 class Orange implements Fruit { 11 public void eat () { 12 System.out.println("吃桔子") ; 13 } 14 } 15 class banana implements Fruit { 16 public void eat () { 17 System.out.println("吃香蕉") ; 18 } 19 } 20 class Factory { 21 public static Fruit getInstance (String className) { 22 Fruit fruit = null ; 23 try { 24 fruit = (fruit) Class.forName(className).newInstance () ; 25 } catch (Exception e) { 26 e.printStackTrace () ; 27 } 28 return fruit ; 29 } 30 public class FactoryDemo { 31 public static void main (String [] args) { 32 Fruit fru = Factory.getInstance("cn.kvikon.Apple") ; 33 fru.eat() ; 34 } 35 } 36 }
现在子类可以随意增加,工厂类不需要做任何的修改。
使用反射之后最大的好处就是解耦和。
1.4、 使用反射操作类的结构
类的结构就三部分:构造方法、普通方法、属性,而这些操作有了反射,一切都可以轻松面对。
(1) 调用构造
· 构造方法是在类实例化的时候默认调用的,一个类之中可以提供有多个构造方法。而且一个类如果没有定义任何一个构造方法的话,则会自动生成一个无参的,什么都不做的构造方法。
在Class类之中定义了如下取得构造方法的操作:
· 取得全部构造:public Constructor<?>[] getConstructors () throws SecurityException ;
· 取得指定构造:public Constructor<T> getConstructor(Class<?> ... parameterTypes) throws NoSuchMethodException,SecurytyException 。
1 package cn.kvikon.demo ; 2 class Person { 3 private String name ; 4 private int age ; 5 public Person (String name, int age) { 6 this.name = name ; 7 this.age = age ; 8 } 9 public String toString () { 10 return "姓名: " + this.name + ",年龄:" + this.age ; 11 } 12 } 13 public class FactoryDemo { 14 public static void main (String [] args) { 15 Class<?> cls = Class.forName ("cn.kvikon.demo.Person") ; 16 Person per = (Person) cls.newInstance () ; 17 System.out.println (per) ; 18 } 19 } 20 }
以上错误提示:Exception in thread "main" java.lang.InstantiationException: cn.kvikon.demo.Person
在使用Class类对象反射实例化对象的时候,类之中必须提供有无参构造,如果不提供就出错了。那么如果说现在类中就没有无参构造的话,那么就必须利用反射找到指定的构造方法明确传入所需要的参数才可以实例化对象,这就要通过Constructor类完成了。
1 package cn.kvikon.demo ; 2 3 import java.lang.reflect.Constructor ; 4 5 class Person { 6 private String name ; 7 private int age ; 8 public Person (String name, int age) { 9 this.name = name ; 10 this.age = age ; 11 } 12 public String toString () { 13 return "姓名: " + this.name + ",年龄:" + this.age ; 14 } 15 } 16 public class FactoryDemo { 17 public static void main (String [] args) { 18 Class<?> cls = Class.forName ("cn.kvikon.demo.Person") ; 19 // 根据构造方法的参数类型找到一个指定的构造 20 Constructor<?> cons = cls.getConstructor (String.class, int.class) ; 21 Person per = (Person) cons.newInstance("张三" , 20) ; 22 System.out.println (per) ; 23 } 24 } 25 }
如果为了统一的话,还是保留有无参构造会比较好。
(2) 调用方法
除了使用对象调用类中的方法之外,也可以利用反射完成,在Class类里面定义了如下方法:
· 取得全部方法:public Method[] getMethods() throws SecurityException ;
· 取得指定方法:public Method getMethod(String name, Class<?> ... parameterTypes) throws NoSuchMethodException, SecurityException。
不管是否使用反射,如果要调用类中的方法请一定要保证有实例化对象。
范例:通过反射调用类中的setter、getter方法
1 package cn.kvikon.demo ; 2 3 import java.lang.reflect.Constructor ; 4 5 class Person { 6 private String name ; 7 public void setName (String name) { 8 this.name = name ; 9 } 10 public String getName () { 11 return name ; 12 } 13 } 14 public class FactoryDemo { 15 public static void main (String [] args) { 16 String attribute = "name" ; // 操作的是类中的name属性 17 Class<?> cls = Class.forName ("cn.kvikon.demo.Person") ; 18 Object obj = cls.newInstance () ; // 实例化对象 19 Method setMethod = cls.getMethod ("set" + initcap (attribute) , String.class) ; 20 setMethod.invoke (obj , "张三") ; // 对象.setName ("张三") ; 21 Method getMethod = cls.getMethod ("get" + initcap (attribute) ) ; 22 System.out.println (getMethod.invoke (obj)) ; // 对象.getName () 23 } 24 public static String initcap (String str) { 25 return str.substring (0,1).toUpperCase ().concat (Str.substring(1).toLowerCase ()) ; 26 } 27 }
(3) 调用属性
类之中的属性一定是使用private封装的,但是由于其也是组成的部分,所以也可以反射操作,通过Class类对象取得;
· 取得定义的全部属性:public Field[] getDeclareFields() throws SecuriryException ;
· 取得指定的属性: public Field getDeclareFields(String name) throws NoSuchFieldException, SecurityException .
· 设置属性内容: public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
· 取得属性内容: public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
范例:操作属性
package cn.kvikon.demo ; import java.lang.reflect.Method ; class Person { private String name ; } } public class TestDemo { public static void main (String [] args) throws Exception{ Class<?> cls = Class.forName ("cn.kvikon.demo.Person") ; Object obj = cls.newInstance () ; // 实例化对象 System.out.println (per) ; Field nameField = cls.getDeclaredField ("name") ; // 取得name属性 nameField.setAccessible (true) ; // 取消封装 nameField.set (obj , "张三") ; // 对象.name = "张三" ; System.out.println(nameField.get(obj)) ; } }
但是所有的属性一定要通过setter、getter设置和取得,以上的代码只是针对于Field操作做一个说明。
以上是反射深入操作的基础。
总结:除了new之外,可以使用Class.forName().newInstance()实例化对象。