一、注意点
- 面向对象的世界中,万事万物皆对象。
- Java中的类,是
java.util.Class
类的实例对象 - 类的类类型,可从以下三种方式得到:
-
Class<T> clazz = T.class;
【因为每个.java文件在被编译成.class文件之后,T类内部就生成了一个静态属性,也就是T.class,它保存了这个类的基本信息】 -
Class<T> clazz = t.getClass();
【底层是native方法,用Java声明,用C语言实现,最终在Java中实现,其实就是获取这个静态属性】 -
Class<T> clazz = Class.forName("类的全称");
【代表动态加载类】
动态实现装载类的方式:
1. 隐式装载:new一个对象时,JRE检查并自动加载
2. 显式装载:①Class.forName()
②ClassLoader.loadClass()
-
- 基本数据类型和void关键字都存在类类型。
-
Class.getCanonicalName()
返回的是Java Language Specification中定义的基础类的规范化名称,换句话,就是返回全类名,没有就返回null。
二、Class类能给我们什么信息(常见):
- 获取并打印所有属性信息【访问权限+属性类名+属性名】
public static Field[] getFieldList(Class clazz) {
Field[] fields = clazz.getDeclaredFields();///获取类中所有的属性(忽略访问修饰符)
for (Field fieid : fields) {
Type type = fieid.getType();///获取属性类型
System.out.println(Modifier.toString(fieid.getModifiers())+" "+type.getTypeName() + " " + fieid.getName());
}
return fields;
}
关键点:
Class.getDeclaredFields():Field[]
: 获取类中所有的属性(忽略访问修饰符)Fieid.getType():Type
: 获取属性类型获取所有方法信息【访问权限+方法名+ 返回值类型 + 方法参数类型】
public static Method[] getMethodList(Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
///分析每一个方法
getMethodMsg(method);
}
return methods;
}
/**
* 获取Method的返回值类型和参数类型等信息
* @param method
*/
public static void getMethodMsg(Method method) {
StringBuilder sb = new StringBuilder();
///获取访问权限
sb.append(Modifier.toString(method.getModifiers())+" ");
///返回值类型
Class<?> returnType = method.getReturnType();
////参数类型
Class[] paramsTypeList = method.getParameterTypes();
sb.append(returnType.getName()).append("
").append(method.getName()).append("(");
for (Class paramsType : paramsTypeList) {
sb.append(paramsType.getName()).append(",");
}
sb.append(")");
///删除多余的","号
if (sb.lastIndexOf(",") == sb.length() - 1) {
sb.delete(sb.length() - 2, sb.length() - 1);
}
System.out.println(sb.toString());
}
关键点:
Class.getDeclaredMethods():Method[]
: 获取类中所有的方法(忽略访问修饰符)Method.getParameterTypes():Class[]
: 获取方法的参数类类型列表Method.getReturnType():Class
: 获取方法的返回值类型获取构造函数信息【函数名+参数类型】
public static void getConstructorMsg(Object obj) {
Class clazz = obj.getClass();
///封装了构造函数的信息
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor item:constructors){
System.out.print(item.getName()+"(");
Class[] paramTypes = item.getParameterTypes();
for (Class type: paramTypes){
System.out.print(type.getName()+",");
}
System.out.println(")");
}
}
关键点:
Class.getDeclaredConstructors():Constructor[]
: 获取这个类中所有的构造函数信息(每一个Constructor
类对象拥有其中一个构造函数的信息)Constructor.getParameterTypes(): Class[]
: 获取这个构造函数的参数类类型列表调用无参构造方法生成类对象
//动态生成类对象(无参构造函数)
Class<MyObj> clazz = (Class<MyObj>)MyObj.getClass();
T obj = clazz.newInstance();
- 调用有参构造方法生成类对象
//动态生成类对象(有参构造函数)
Class[] classes = new Class[]{Integer.class, String.class};
Object[] values = new Object[]{100, "hello world"};
Constructor<C> constructor = clazz.getDeclaredConstructor(classes);
C res = (C) constructor.newInstance(values);
- 对象类型匹配
方式一:对象 instanceof 类型
如:
Animal dog = new Dog();///Dog是Animal的子类
boolean b = dog instanceof Dog;//判断dog这个对象是不是Dog类型的实例,结果为true
方式二:class对象.isInstance(类对象)
如:
Animal dog = new Dog();///Dog是Animal的子类
boolean b = Dog.class.isInstance(dog);
三、反射的基本用法:
-
调用方法:
Object 返回值 = method.invoke(对象, 参数值列表)
相当于:对象.method(参数值列表)
如:
Person xiaoming = new Person();
Class c = xiaoming.getClass();
//Method m = c.getMethod("print", new Class[]{int.class, int.class});
//m.invoke(xiaoming, new int[]{1,2});
Method m = c.getMethod("print", int.class, int.class);
m.invoke(xiaoming, 1,2);
唯一确定某个方法的因素:方法名称 + 方法参数列表
-
修改属性:
Field.set(对象, 要修改的field值)
相当于:对象属性赋值操作
如:
Field field = teacher.getClass().getDeclaredField("name");//获取teacher对象的name属性
field.setAccessible(true);//无视field的访问权限问题
field.set(teacher, "小明老师");
四、关于集合的反射
- 反射都是在编译完之后的操作
- 编译之后,有着泛型的集合类型,将去泛型化。【换句话说,Java集合类的泛型,是为了防止错误输入而设定的,只在编译时有效】
- 通过反射,可以绕过编译,这样一来,比如: 在
ArrayList<String> list
上执行add(100);
添加一个整型,则不会报错。【但是这样做,就不能使用for:each
来遍历集合】
List<String> list = new ArrayList<>();
list.add("hello");///添加元素“hello”
try {
Method methodAdd = list.getClass().getMethod("add", Object.class);
methodAdd.invoke(list,1);///添加元素1
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(list.toString());
///以下遍历报错:java.lang.ClassCastException
///for (String item:list){
/// System.out.println(item);
///}
五、Java反射相关的异常
- ClassNotFoundException:未在命名空间内找到指定类。(类名错误,或者类不存在)
- SecurityException:修改了不允许修改的Accessible标志时
- NoSuchMethodException:找不到特定方法
- NoSuchFieldException:找不到特定属性
- IllegalArgumentException:向方法传递了一个不合法或不正确的参数
- InstantiationException:程序视图使用Class类中的newInstance方法(此方法本身不能应用于基本类型)创建一个类的实例,而指定的类对象无法被实例化时。【如:实例化抽象类或接口,或者视图创建数组类的实例】
- IllegalAccessException:当我的程序代码想要通过反射去创建一个实例(非数组)、设置或获取一个字段,或者调用一个方法,但是当前正在执行的方法无法访问指定类、字段、方法或构造函数的定义时,抛出的异常。