反射与类操作
取得父类信息
利用反射可以做出一个对象所具备的所有操作行为,而且最关键的是这一切的操作都可以基于Object类型进行,
在java里面任何的程序类都一定会有一个父类,那么在Class类里面就可以通过此方式来取得父类或者是实现的父接口,有如下的两个方法提供:
取得类的包名称:public Package getPackage()
取得父类的Class对象:public Class<? super T> getSuperclass()
取得父接口对象:public Class<?>[] getInterfaces()
1 package cn.Tony.demo; 2 3 interface IMessage{ 4 } 5 interface IFruit{ 6 } 7 class Person implements IMessage,IFruit{ 8 public void print() {} 9 } 10 public class TestDemo{ 11 public static void main(String[] args) throws Exception { 12 Class<?> cls=Person.class; 13 System.out.println(cls.getPackage().getName()); 14 System.out.println(cls.getSuperclass().getName()); 15 Class<?> itf[]=cls.getInterfaces(); 16 for(int x=0;x<itf.length;x++) { 17 System.out.println(itf[x].getName()); 18 } 19 20 } 21 } 22
通过反射可以取得类结构上的所有信息。
反射调用的结构
一个类中可以多个构造方法,那么如果要想取得类中的构造的调用,就可以使用Class类中的两个方法:
取得指定参数类型的构造:public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException,SecurityException
取得类中的所有构造:public Constructor<?>[] getConstructors()throws SecurityException
以上两个方法返回类型都是:java.lang.reflect.Constructor类的实例化对象,这个类里面关注一个方法:
实例化对象:InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException
范例:取得类中所有构造方法信息
1 package cn.Tony.demo; 2 3 import java.lang.reflect.Constructor; 4 5 interface IMessage{ 6 } 7 interface IFruit{ 8 } 9 class Person implements IMessage,IFruit{ 10 private String name; 11 private int age; 12 public Person() {} 13 public Person(String name) { 14 this.name=name; 15 } 16 public Person(String name,int age) { 17 this.name=name; 18 this.age=age; 19 } 20 } 21 public class TestDemo{ 22 public static void main(String[] args) throws Exception { 23 Class<?> cls=Person.class; 24 Constructor<?> ct[]=cls.getConstructors(); 25 for(int x=0;x<ct.length;x++) { 26 System.out.println(ct[x]); 27 } 28 } 29 } 30
以上的操作是直接利用了Constructor类中的toString方法取得了构造方法的信息,而如果你只用getName()方法就比较麻烦了。
讲解Constructor类的目的并不是让你去分析构造方法的取得组成,实际上那些操作和你们没有关系,你们最需要关注的是之前的一个问题结论:在定义简单java类的时候一定要保留一个无参构造,
范例:观察Class实例化对象的问题
1 package cn.Tony.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 @Override 13 public String toString() { 14 return "Person [name=" + name + ", age=" + age + "]"; 15 } 16 } 17 public class TestDemo{ 18 public static void main(String[] args) throws Exception { 19 Class<?> cls=Person.class; 20 System.out.println(cls.newInstance()); 21 } 22 } 23
Class类通过反射实例化类对象的时候,只能够调用类中的无参构造,那么如果现在类中没有无参构造,无法使用Class类操作。只能够通过明确的构造调用执行实例化处理,
范例:通过Constructor类实例化对象。
1 package cn.Tony.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 @Override 13 public String toString() { 14 return "Person [name=" + name + ", age=" + age + "]"; 15 } 16 } 17 public class TestDemo{ 18 public static void main(String[] args) throws Exception { 19 Class<?> cls=Person.class; 20 Constructor<?> ct=cls.getConstructor(String.class,int.class); 21 System.out.println(ct.newInstance("张三",20)); 22 } 23 } 24
结论:以后写简单Java类要写无参构造,你就不需要以上内容了。
调用普通方法(核心)
实际上类中的构造方法定义你可能永远也不会编写。但是类中普通反方法的调用你在开发中一定会使用到,并且使用好了可以为你节省大量的重复编码,在Class类中定义有两个取得普通方法的定义:
取得全部方法:public Method[] getMethods()throws SecurityException
取得指定方法:public Method getMethod(String name, Class<?>... parameterTypes)throws NoSuchMethodException,SecurityException
以上两个方法返回的类型是java.lang.reflect.Method类的对象,在此类中提供一个调用的方法支持:public Object invoke(Object obj,Object... args)throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
范例:取得一个类中的全部方法
1 package cn.Tony.demo; 2 3 import java.lang.reflect.Method; 4 5 class Person { 6 private String name; 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 this.name = name; 12 } 13 } 14 public class TestDemo{ 15 public static void main(String[] args) throws Exception { 16 Class<?> cls=Person.class; 17 Method[] cmt=cls.getMethods();//取得全部方法 18 for(int x=0;x<cmt.length;x++) { 19 System.out.println(cmt[x]); 20 } 21 } 22 } 23
那么在之前所编写的程序对于类中的setter getter方法采用的都是明确的对象
1 Person per=new Person(); 2 per.setName("abc"); 3 System.out.println(per.getName());
而现在有了反射机制处理之后,这个时候的程序即使你没有明确的Person类型的对象(依然需要实例化对象,Object描述,所有的普通方法必须在有实例化对象之后才可以进行调用),就需要通过反射完成。
范例:通过反射调用setter getter(有明确开发要求)
1 package cn.Tony.demo; 2 3 import java.lang.reflect.Method; 4 5 class Person { 6 private String name; 7 public String getName() { 8 return name; 9 } 10 public void setName(String name) { 11 this.name = name; 12 } 13 } 14 public class TestDemo{ 15 public static void main(String[] args) throws Exception { 16 String attribute="name";//明确告诉你属性的名称 17 String value="Tony";//明确要告诉你设置的内容 18 Class<?> cls=Class.forName("cn.Tony.demo.Person"); 19 Object obj=cls.newInstance();//任何情况下调用类中的普通实例化对象 20 //取得setName这个方法的实例化对象 21 //setName()是方法名称,但是这个方法名称是根据给定的属性信息拼凑得来的,同时该方法需要接受一个String类型的参数 22 Method setMethod= cls.getMethod("set"+initcap(attribute), String.class); 23 //随后需要通过Method类对象调用指定方法。调用方法必须有实例化对象,同时要传入一个参数, 24 setMethod.invoke(obj, value);//相当于:Person对象.setName(value); 25 Method getMethod=cls.getMethod("get"+initcap(attribute)); 26 Object ret=getMethod.invoke(obj); 27 System.out.println(ret); 28 } 29 public static String initcap(String str) { 30 return str.substring(0, 1).toUpperCase()+str.substring(1); 31 } 32 } 33
此类操作的好处是:不在局限于某一具体类型的对象,而是可以通过Object类型进行所有类的方法操作!
调用类成员
在之前已经成功的实现了类的构造,已经方法调用,那么除了这两种的模式外还需要有成员调用。前提:类中的所有属性一定要对象实例化之后才会进行空间的分配,所以此时如果要想调用类的属性。必须要保证有实例化对象,而通过反射的newInstance()方法可以直接取得实例化对象,返回类型是Object类型
在Class类里面提有两组取得属性的操作方法:
取得父类属性:
取得类中的全部属性:public Field[] getFields()throws SecurityException
取得类中指定名称的属性:public Field getField(String name)throws NoSuchFieldException,
SecurityException
取得本类属性:
取得类中的全部属性:public Field[] getDeclaredFields()throws SecurityException
取得类中指定名称的属性:public Field getDeclaredField(String name)throws NoSuchFieldException,SecurityException
范例:取得类中的全部属性
1 package cn.Tony.demo; 2 3 import java.lang.reflect.Field; 4 5 class Person { 6 private String name; 7 } 8 class Student extends Person{ 9 private String school; 10 } 11 public class TestDemo{ 12 public static void main(String[] args) throws Exception { 13 Class<?> cls=Class.forName("cn.Tony.demo.Student");//获取对象 14 Object obj=cls.newInstance();//实例化对象,否则属性无法开辟空间 15 { 16 Field fields[]=cls.getFields();//取得全部属性 17 for(int x=0;x<fields.length;x++) { 18 System.out.println(fields[x]); 19 } 20 } 21 System.out.println("--------------------"); 22 { 23 Field fields[]=cls.getDeclaredFields();//取得全部属性 24 for(int x=0;x<fields.length;x++) { 25 System.out.println(fields[x]); 26 } 27 } 28 } 29 } 30
因为再实际的开发中属性基本上都使用封装处理。所以没必要关注父类属性。所以以后进行操作取得的属性都以本类为主
而后现在就需要关注属性的核心描述类:java.lang.reflect.Field。在这个类里面有两个重要的方法
设置属性内容:public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException
取得属性内容:public Object get(Object obj)throws IllegalArgumentException,
IllegalAccessException
在AccessibleObject类中提供一个方法:
动态设置封装:public void setAccessible(boolean flag)throws SecurityException
范例:通过反射操作属性
1 package cn.Tony.demo; 2 import java.lang.reflect.Field; 3 class Person { 4 private String name; 5 } 6 public class TestDemo{ 7 public static void main(String[] args) throws Exception { 8 Class<?> cls=Class.forName("cn.Tony.demo.Person");//获取对象 9 Object obj=cls.newInstance(); 10 Field nameField=cls.getDeclaredField("name"); 11 nameField.setAccessible(true);//取消了封装 12 nameField.set(obj, "张三"); 13 System.out.println(nameField.get(obj)); 14 } 15 } 16
但是如果在实际的开发之中使用更多的属性操作绝对不可能直接按照如上的模式进行,一定要使用setter getter 方法,因为要给用户操作的机会。
在Field类里面有一个特别有用的方法:
取得属性类型:public Class<?> getType()
1 package cn.Tony.demo; 2 import java.lang.reflect.Field; 3 class Person { 4 private String name; 5 } 6 public class TestDemo{ 7 public static void main(String[] args) throws Exception { 8 Class<?> cls=Class.forName("cn.Tony.demo.Person");//获取对象 9 Object obj=cls.newInstance(); 10 Field nameField=cls.getDeclaredField("name"); 11 System.out.println(nameField.getType().getName());//包.类 12 System.out.println(nameField.getType().getSimpleName());//类名称 13 } 14 } 15
将Field取得属性与Method类中的invoke()结合在一起,就可以编写飞车灵活的程序了。