反射(reflection)
可以在运行时加载、探知、使用编译期间完全未知的类
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
Class c = Class.forName("com.company.User");
加载完类后,在堆内存中就产生了一个Class类型对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为"反射"
Class类
要点
1.java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身
2.Class类的对象中包含了某个被加载的类的结构,一个被加载的类对应一个Class对象
3.当一个class被加载,或当加载器(class loader)的defineClass()被jvm调用,JVM便自动产生一个Class对象
4.Class类是Reflection的根源 针对任何想动态加载、运行的类唯有先获得相应的Class对象
获得Class对象的三种方法:
1.Class.forName("类路径") (最常用)
2.类路径.getClass()
3.类.class;
/**
*测试Class类
*/
public class Test01 {
public static void main(String[] args){
try {
//1.Class.forName() 最常用
Class clazz = Class.forName("com.company.User");
Class clazz2 = Class.forName("com.company.User");
System.out.println(clazz.hashCode()==clazz2.hashCode()); //true 说明一个类只对应一个Class对象
//2. getClass方法
Class strclazz1 = "hahh".getClass();
//3. .class方法
Class strclazz2 = String.class;
System.out.println(strclazz1.hashCode()==strclazz2.hashCode()); //true 同一对象
int[] arr01 = new int[10];
int[] arr02 = new int[20];
int[][] arr03 = new int[10][3];
System.out.println(arr01.getClass().hashCode()==arr02.getClass().hashCode()); //true 数组Class对象和长度无关
System.out.println(arr01.getClass().hashCode()==arr03.getClass().hashCode()); //false 和维度有关
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
反射机制的常见作用
- 动态加载类、动态获取类的信息(属性、方法、构造器)
- 动态构造对象
- 动态调用类和对象的任意方法、构造器
- 动态调用和处理属性
- 获取泛型信息
- 处理注解
通过反射API动态获取类的信息
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
*通过反射API获取类的相关信息
*/
public class Test01 {
public static void main(String[] args){
try {
Class clazz = Class.forName("com.company.User");
//获取类名
System.out.println(clazz.getName()); //包名+类名 com.company.User
System.out.println(clazz.getSimpleName()); //类名 User
//获取属性信息
//Field name = clazz.getField("name"); //获取单个属性 NoSuchFieldException 只能返回public修饰的属性
//Field[] fields = clazz.getFields(); //获取所有属性 [] 也是只能回public修饰的属性
//System.out.println(Arrays.toString(fields));
Field name = clazz.getDeclaredField("name");
System.out.println(name); //private java.lang.String com.company.User.name
Field[] fields = clazz.getDeclaredFields();
for(Field temp:fields){
System.out.println(temp);
//private int com.company.User.id
//private java.lang.String com.company.User.name
//private int com.company.User.age
}
//获取方法信息
//和获得属性信息基本一致,注意方法有参数时,要加上方法参数类型的Class对象
Method method = clazz.getMethod("setName", String.class); //public修饰的单个方法 String.class表示setName的参数形式
Method[] methods = clazz.getMethods(); //获取所有方法
Method method1 = clazz.getDeclaredMethod("setName", String.class); //获取单个方法 private修饰的也能获取
Method[] methods1 = clazz.getDeclaredMethods(); //获取所有方法 private修饰的也能获取
//获得构造器信息 和上面一样 注意获得某个特有的构造器和方法一样,要加上构造器的参数类型的Class对象
clazz.getConstructor();
clazz.getConstructors();
clazz.getDeclaredConstructor();
clazz.getDeclaredConstructors();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
通过反射API动态操作类的属性、方法、构造器
import com.company.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 通过反射API动态操作类的属性、方法、构造器
*/
public class Test02 {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.company.User");
//通过反射API调用构造器,创建对象
User user1 = (User) clazz.newInstance(); //实际是调用了User无参构造方法
Constructor<User> c = clazz.getDeclaredConstructor(int.class,String.class,int.class);
User user2 = c.newInstance(101,"陈一",20);
System.out.println(user2.getName()); //陈一
//通过反射API调用普通方法
User user3 = (User) clazz.newInstance();
//当然有对象了可以直接操作对象,user3.setName("陈二");但是下面的方法也有优点
Method setNameMethod = clazz.getMethod("setName", String.class);
setNameMethod.invoke(user3,"陈二"); //和上面user3.setName("陈二")效果相同
//优点:方法名称和参数都是变量,可以动态调用
System.out.println(user3.getName()); //陈二
//通过反射API调用属性
User user4 = (User) clazz.newInstance();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); //这个属性不需要作安全检查,可以直接访问 方法也是(如果是私有方法也需要设置)
nameField.set(user4,"陈四"); //通过反射设置属性的值
System.out.println(user4.getName()); //陈四
System.out.println(nameField.get(user4)); //陈四 通过反射读属性的值
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
通过反射操作泛型
java采用泛型擦除机制来引入泛型。java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除
为了通过反射操作这些类型以迎合实际开发的需要,java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是和原始数据类型齐名的类型。
ParameterizedType:表示一种参数化的类型,比如Collection<String>
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式,比如?,?extends Number,? super Integer
demo
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
* 通过反射获取泛型信息
*/
public class Demo{
//定义两个带泛型的方法
public void test01(Map<String,Person> map,List<Person> list){
System.out.println("Demo.test01()");
}
public Map<Integer,Person> test02(){
System.out.println("Demo.test02()");
return null;
}
public static void main(String[] args) {
try {
//获得指定方法参数泛型信息
Method m = Demo.class.getMethod("test01", Map.class,List.class);
Type[] t = m.getGenericParameterTypes(); //获得带泛型参数类型
for (Type paramType : t) {
System.out.println("#"+paramType);
if(paramType instanceof ParameterizedType){
//获取泛型中的具体信息
Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
for (Type genericType : genericTypes) {
System.out.println("泛型类型:"+genericType);
}
}
}
//获得指定方法返回值泛型信息
Method m2 = Demo.class.getMethod("test02", null);
Type returnType = m2.getGenericReturnType();
if(returnType instanceof ParameterizedType){
Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
for (Type genericType : genericTypes) {
System.out.println("返回值,泛型类型:"+genericType);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}