0.反射知识体系
1.反射概论
1)反射概念
2)Java对象两种类型
Person p = new Student();
import java.lang.reflect.Method; class Person{ public void speak(){ System.out.println("I am a person"); } } class Student extends Person{ public void speak(){ //覆盖父类方法 System.out.println("I am a student"); } //子类新添加方法 public void study(){ System.out.println("Student can study"); } } public class PSTest { public static void main(String[] args) throws Exception{ Person p = new Student(); //直接调用报错,因为Person中没有study()方法 //p.study(); //第1种解决方法:判断并强制转换类型 if(p instanceof Student){ Student s = (Student)p; s.study(); } //第2种解决方法:运行得到该对象及类信息 Class<?> clazz = p.getClass(); //得到类名 System.out.println(clazz.getName()); //甚至直接调用方法 Method m = clazz.getMethod("study"); m.invoke(p); } }
2.反射框架
1)lang包中的反射类
(1)Class<T>
(2)Package
Package
对象包含有关 Java 包的实现和规范的版本信息。2)反射包-java.lang.reflect(基于JavaSE 6)
(1)接口摘要(共9个)
b. GenericArrayType GenericArrayType 表示一种数组类型,其组件类型为参数化类型或类型变量。
c. GenericDeclaration 声明类型变量的所有实体的公共接口。
d. InvocationHandler InvocationHandler 是代理实例的调用处理程序 实现的接口。
e. Member 成员是一种接口,反映有关单个成员(字段或方法)或构造方法的标识信息。
f. ParameterizedType ParameterizedType 表示参数化类型,如 Collection<String>。
g. Type Type 是 Java 编程语言中所有类型的公共高级接口。
h. TypeVariable<D extends GenericDeclaration> TypeVariable 是各种类型变量的公共高级接口。
i. WildcardType WildcardType 表示一个通配符类型表达式,如 ?、? extends Number 或 ? super Integer。
(2)类摘要(共8个)
b. Array Array 类提供了动态创建和访问 Java 数组的方法。
c. Constructor<T> Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
d. Field Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
e. Method Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
f. Modifier Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。
g. Proxy Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
h. ReflectPermission 反射操作的 Permission 类。
(3)异常摘要(共3个)
b. MalformedParameterizedTypeException 当反射方法遇到语义错误的参数化类型,而反射方法需要实例化该类型时,抛出该异常。
c. UndeclaredThrowableException 如果代理实例的调用处理程序的 invoke 方法抛出一个经过检查的异常(不可分配给 RuntimeException 或 Error 的 Throwable),且该异常不可分配给该方法(在代理实例上调用该方法,并将其指派到调用处理程序)的 throws 子句中声明的任何异常类,则由代理实例上的方法调用抛出此异常。
(4)错误摘要(共1个)
3. 反射操作-查看类信息
1)获得Class对象
每个类加载之后,系统就会为该类生成对应的Class对象,通过该Class对象就可以访问到JVM中的这个类,Java程序中获取Class对象有如下3种方式:(1)Class类的静态方法forName(String clazzName),字符串参数的值是某个类的全限定类名,必须添加完整包名,注:此方法还有另一种重载形式。
(2)调用某个类的class属性来获取该类对应的Class对象,如Person.class将会返回Person类对应的Class对象。(3)调用某个对象的getClass()方法。该方法是java.lang.Object类中的方法,所有对象都可调用该方法,将返回对象所属类对应的Class对象。
第1种方式与第2种方式都是通过类直接取得该类的Class对象,相比第2种方式有如下两种优势:
(1)代码更加安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
(2)程序性能更好,无须调用方法。
如果只有类的字符串如“java.lang.String”获取该字符串对应的Class对象,只能使用第1种方式,此方法可能抛出ClassNotFoundException异常。一旦获得某个类对应的Class对象,就可以调用Class对象的方法来获得该对象和该类的真实信息。
2)获得Class信息
Class类提供了大量的实例方法来获取Class对象所对应类的详细信息,大致包含如下方法,每个方法又包含多种重载版本:
(1)获取Class对象对应类包含的构造器
a. Constructor<?>[] getConstructors():返回此Class对象对应类的所有public构造器。
b.Constructor<T> getConstructor(Class<?>... parameterTypes):返回此Class对象对应类的指定public构造器。
c. Constructor<?>[] getDeclaredConstructors():返回此Class对象对应类的所有构造器,与构造器的访问权限无关。
d.Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes):返回此对象对应类的指定构造器,与构造器的访问权限无关。
import java.lang.reflect.Constructor; class Person{ public Person(){ System.out.println("TestTarget"); } public Person(String name){ System.out.println(name); } protected Person(String stuName,String className){ System.out.println(stuName + " - " + className); } private Person(String name, int age){ System.out.println(name + " - " + age); } } public class GetConstructorsTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException{ //1.获得Class对象(共3种方式) //1)Class.forName(String clazzName); //Class<?> clazz = Class.forName("tad.blog.reflect.Person"); //2)类的class属性 Class<Person> clazz = Person.class; //3)对象getClass()方法 //Class<?> clazz = new Person().getClass(); //System.out.println(clazz.getName()); //2.1.获取所有public构造器 Constructor<?>[] publicCons = clazz.getConstructors(); System.out.println("所有public构造器"); for(Constructor<?> c : publicCons){ System.out.println(c.getName()); } //2.2.获取特定public构造器 Constructor<Person> certainCons = clazz.getConstructor(String.class); System.out.println("特定public构造器"); System.out.println("名称:" + certainCons.getName() + ";修饰符: " + certainCons.getModifiers()); //2.3.获取所有构造器 Constructor<?>[] allCons = clazz.getDeclaredConstructors(); System.out.println("所有构造器"); for(Constructor<?> c : allCons){ System.out.println(c.getName()); } //2.4.获取特定构造器,访问修饰符无关 Constructor<Person> certainConsPro = clazz.getDeclaredConstructor(String.class,int.class); System.out.println("特定构造器"); System.out.println("名称:" + certainConsPro.getName() + " ;修饰符: " + certainConsPro.getModifiers()); } }
(2)获取Class对象对应类包含的方法
a. Method[] getMethods():返回此Class对象所表示的类的所有public方法。
b. Method getMethod(String name,Class<?>... parameterTypes):返回此Class对象对应类的指定public方法。
c.Method[] getDeclaredMethods():返回此Class对象对应类的全部方法,与方法的访问权限无关。
d. Method getDeclaredMethod(String name,Class<?>... parameterTypes):返回此对象对应类的指定方法,与方法的访问权限无关。
import java.lang.reflect.Method; class Fruit{ public void show(){ } public void show(String info){ } void getWater(){ } protected void grow(){ } private void deep(){ } private void deep(int length){ } } public class GetMethodsTest { public static void main(String[] args) throws NoSuchMethodException { //1.获得Class对象 Class<Fruit> clazz = Fruit.class; //2.1.获得所有public Method System.out.println("所有public方法"); Method[] publicMethods = clazz.getMethods(); for(Method m : publicMethods){ System.out.println(m.getName()); } //2.2.获得特定public Method System.out.println("特定public方法"); Method certainMethod = clazz.getMethod("show",String.class); System.out.println(certainMethod.getName()); //2.3.获得所有Method,修饰符无关 System.out.println("所有方法"); Method[] allMethods = clazz.getDeclaredMethods(); for(Method m : allMethods){ System.out.println(m.getName()); } //2.4.获得特定Method,修饰符无关 System.out.println("特定方法"); Method certainMethodDeep = clazz.getDeclaredMethod("deep", int.class); System.out.println(certainMethodDeep.getName()); } }
(3)访问Class对象对应类包含的字段
import java.lang.reflect.Field; class Animal{ public String name; public int age; private String secrets; } public class GetFieldsTest { public static void main(String[] args) throws NoSuchFieldException { //1.获得Class对象 Class<Animal> clazz = Animal.class; //2.1.获得所有public字段 Field[] fields = clazz.getFields(); System.out.println("所有public字段"); for(Field f : fields){ System.out.println(f.getName()); } //2.2.获得特定public字段 Field f = clazz.getField("name"); System.out.println("特定public字段"); System.out.println(f.getName()); //2.3.获得所有字段,访问修饰符无关 Field[] allFields = clazz.getDeclaredFields(); System.out.println("获得所有字段"); for(Field af : allFields){ System.out.println(af.getName()); } //2.4.获得特定字段,访问修饰符无关 Field certainField = clazz.getDeclaredField("secrets"); System.out.println("获得特定字段"); System.out.println(certainField.getName()); } }
(4)访问Class对象对应类包含的Annotation
@Deprecated @Addressing @SuppressWarnings(value="unchecked") class Building{ @Override public String toString(){ return "Building"; } } public class GetAnnotationsTest { public static void main(String[] args) { //1.获得Class对象 Class<Building> clazz = Building.class; //2.1.获得所有Annotation System.out.println("获得所有Annotation"); Annotation[] as = clazz.getAnnotations(); for(Annotation a : as){ System.out.println(a); } //2.2.获得特定Annotation System.out.println("获得特定Annotation"); Annotation a = clazz.getAnnotation(Addressing.class); System.out.println(a); //2.3.获得直接修饰的所有Annotation System.out.println("获得所有Annotation"); Annotation[] ass = clazz.getDeclaredAnnotations(); for(Annotation ann : ass){ System.out.println(ann); } } }
(5)访问Class对象对应类包含的内部类
(6)访问Class对象对应类所在的外部类
(7)访问Class对象对应类所实现的接口、继承的父类
a. Class<?>[] getInterfaces():返回该Class对象对应类所实现的全部接口。
b. Class<? super T> getSuperClass():返回该Class对象对应类的超类的Class对象。
(8)获取Class对象对应类的修饰符、所在包、类名等基本信息
(9)判断该Class对象对应类是否为接口、枚举、注释类型等
interface Interfa{ } interface Interfa2{ } final class SuperClass implements Interfa,Interfa2{ public static class InnerClass{ public static void declaringClass() { Class<InnerClass> clazz = InnerClass.class; System.out.println("外部类"); System.out.println(clazz.getDeclaringClass()); } } } public class TestAll{ public static void main(String[] args) { Class<SuperClass> clazz = SuperClass.class; //获得所有内部类 System.out.println("所有内部类"); Class<?>[] inners = clazz.getDeclaredClasses(); for(Class<?> inner : inners){ System.out.println(inner); } //获得外部类 SuperClass.InnerClass.declaringClass(); //获得继承父类 System.out.println("继承父类"); System.out.println(clazz.getSuperclass()); //获得实现接口 System.out.println("实现接口"); Class<?>[] inters = clazz.getInterfaces(); for(Class<?> i : inters){ System.out.println(i); } //获得修饰符 System.out.println("获得修饰符"); int modifier = clazz.getModifiers(); System.out.println(modifier); //获得包 System.out.println("获得包"); Package p = clazz.getPackage(); System.out.println(p.getName()); //获得全类名及简单类名 System.out.println("全类名及简单类名"); System.out.println(clazz.getName()); System.out.println(clazz.getSimpleName()); //自我判断API System.out.println("注解? " + clazz.isAnnotation()); System.out.println("数组? " + clazz.isArray()); SuperClass sc = new SuperClass(); System.out.println("实例? " + clazz.isInstance(sc)); } }
4.反射操作-生成并操作对象
Class对象可以获得该类里的方法(由Method对象表示)、构造器(由Constructor对象表示)、字段(由Field对象表示),这3个类都位于java.lang.reflect包下,并实现java.lang.reflect.Member接口。程序可以通过Method对象执行对应方法,通过Constructor对象调用对应构造器创建实例,能通过Field对象直接访问并修改对象的属性值。
1)创建对象
通过反射创建对象有以下两种方式:
(1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法实际上利用默认构造器来创建该类的实例。
(2)先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择指定的构造器来创建实例。
通过第一种方式来创建对象是比较常见的情形,在很多JavaEE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序需要根据字符串来创建对应的实例,就必须使用反射。
(1)获取该类的Class对象。
(2)利用Class对象的getConstructor()方法来获取指定的构造器。
(3)调用Constructor的newInstance()方法创建Java对象。
import java.lang.reflect.Constructor; class Computer{ public Computer(String name){ show(name); } public void show(){ System.out.println("Computer"); } public void show(String name){ System.out.println("My name is " + name); } } public class ObjFactory { public static void main(String[] args) throws Exception { //Class<Computer> clazz = Computer.class; //1.第1种方式创建对象 //Computer com = clazz.newInstance(); //System.out.println(com); //com.show(); //2.第2种方式创建对象 //2.1.获取Class对象 Class<?> comClazz = Class.forName("tad.blog.reflect.Computer"); //2.2.获得指定构造器 Constructor<?> c = comClazz.getConstructor(String.class); //2.3.创建Java对象 c.newInstance("Tad"); } }
2)访问属性值
通过Class对象的getFields()或getField()方法可以获取该类所包括的全部Field或指定Field。Field提供如下两组方法读取或设置字段的值。(1)getXxx(Object obj):获取obj对象该字段的属性值。此处的Xxx对应的8个基本类型,如果该属性是引用类型,则取消get后面的Xxx。
(2)setXxx(Object obj,Xxx val):将obj对象的字段设置成val值,此处的Xxx对应8个基本类型,如果该属性的类型是引用类型,则取消set后面的Xxx。
import java.lang.reflect.Field; class Dog { private String name; private int age; public String toString(){ return "Dog[name:"+name+",age:"+age+"]"; } } public class FieldTest { public static void main(String[] args) throws Exception { //创建Person对象 Dog p = new Dog(); //Person对应Class对象 Class<Dog> clazz = Dog.class; //获取Person名为name的Field Field nameField = clazz.getDeclaredField("name"); //取消访问权限 nameField.setAccessible(true); //调用set方法为p对象的name字段值设置值 nameField.set(p,"Tad");//引用类型,没有Xxx,可能是因为引用类型的地址是一样的吧! Field ageField = clazz.getDeclaredField("age"); ageField.setAccessible(true); ageField.setInt(p,23);//8个基本数据类型,setXxx() System.out.println(p); } }
3)调用方法
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或getMethod()方法来获得全部方法或指定方法--两个方法的返回值是Method数组或者是Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可以通过该Method来调用它对应的方法。在Method里包含一个invoke()方法,方法签名如下:
Object invoke(Object obj, Object...args):该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。
import java.lang.reflect.Method; public class MethodTest { public static void main(String[] args) throws Exception { //1.获得Class对象 Class<MethodTest> clazz = MethodTest.class; //2.获得Method对象 Method m = clazz.getMethod("show", String.class); //3.调用invoke()方法 m.invoke(new MethodTest(), "Tad is a great person in the world."); } public void show(String info){ System.out.println(info); } }
5.反射操作-数组操作
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以使用Array来动态创建数组,操作数组元素等。
(1)static Object newInstance(Class<?> componentType, int length):创建一个具有指定的元素类型、指定维度的新数组。
(2)static xxx getXxx(Object array, int index):返回array数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变为get(Object array, int index)。
(3)static void setXxx(Object array, int index, xxx val):将array数组中第index个元素的值设为val,其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变成set(Object array, int index, Object val)。
import java.lang.reflect.Array; public class ArrayTest { public static void main(String[] args) { Object arr = Array.newInstance(String.class, 3); Array.set(arr, 0, "Tad"); Array.set(arr, 1, "great"); Array.set(arr, 2, "person"); System.out.println(Array.get(arr, 0)); System.out.println(Array.get(arr, 1)); System.out.println(Array.get(arr, 2)); System.out.println("长度:" + Array.getLength(arr)); } }
6.利用反射生成JDK动态代理
1)利用Proxy和InvocationHandler创建动态代理
在java的java.lang.reflect包下提供了Proxy类和InvocationHandler接口,通过两者可以生成JDK动态代理类或动态代理对象。Proxy提供了用于创建动态代理类和代理对象的静态方法,也是所有动态代理类的父类。如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类,如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
Proxy提供了如下两个方法来创建动态代理类和动态代理实例。
(1)static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader参数指定生成动态代理类的类加载器。
(2)static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实例类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象的invoke方法。
实际上,即使采用第一个方法获取了一个动态代理类之后,当程序需要通过该代理类来创建对象时一样需要传入InvocationHandler对象,也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface Skills{ public void bark(); public void run(); } /** * 委托类,业务执行者 */ class Dog implements Skills{ public void bark() { System.out.println("Dog can bark."); } public void run(){ System.out.println("Dog can run"); } } /** * 调用处理类,完成代理与委托绑定 */ class InvocationHandlerImpl implements InvocationHandler { private Object target; public InvocationHandlerImpl(Object target){ this.target = target; } /** * 方法调用,调用代理到调用委托的转换 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(target, args); } } public class DynamicProxy { public static void main(String[] args) { InvocationHandler h = new InvocationHandlerImpl(new Dog()); Skills o = (Skills)Proxy.newProxyInstance(Dog.class.getClassLoader(), Dog.class.getInterfaces(), h); o.bark(); o.run(); } }
2)动态代理与AOP
待补充...
7.反射和泛型
JDK1.5以后,Java的Class类增加泛型功能,从而允许使用泛型来限制Class类,例如String.class类型实际是Class<String>。如果Class对应的类暂时未知,则使用Class<?>。通过在反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。1)泛型和Class类
使用Class<T>泛型可以避免强制类型转换。2)利用反射获取泛型信息
通过指定类对应的Class对象,可以获得该类包含的所有字段,不管该字段使用private修饰还是使用public修饰,获得Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码获得指定字段的类型。Class<?> a = f.getType();
但这种方式只对普通类型的字段有效,如果该字段的类型是有泛型的类型,如Map<String,Integer>类型,则不能准确地得到该字段的泛型参数。
为了获得指定字段的泛型类型,应先使用如下方法来获取指定Field的泛型类型。
Type gType = f.getGenericType();
然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType代表参数化的类型,也就是增加了泛型限制的类型。ParameterizedType类提供了如下两个方法:
(1)getRawType():返回没有泛型信息的原始类型。
(2)getActualTypeArguments():返回泛型参数的类型。
import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Map; public class ReflectGenericTest { public int num; public Map<String,Integer> kv; public static void main(String[] args) throws NoSuchFieldException { Class<ReflectGenericTest> c = ReflectGenericTest.class; Field fNum = c.getField("num"); System.out.println(fNum.getType()); //只能输出Map Field f = c.getField("kv"); System.out.println(f.getType()); //获得实际类型参数 Type type = f.getGenericType(); ParameterizedType p = (ParameterizedType) type; System.out.println("原始类型:" + p.getRawType()); Type[] types = p.getActualTypeArguments(); for(Type t : types){ System.out.println(t); } } }