day38下
反射
前言
使用到一个类,JVM会将该类的class文件加载到方法区(类加载机制),
同时会在堆内存中创建该类的class对象,class对象的作为class文件的访问入口
Java的反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
理解:
反射实际上就是获取class对象,通过class对象访问class文件中的内容
访问内容:
- 属性
- 构造方法
- 普通方法(成员方法、静态方法、抽象方法…)
- 方法上的参数
- 方法上的返回值
- 数组
关键点-获取Class文件对象
利用反射获取class对象
问:class文件中包含了什么内容?
class文件包含了该类所有的信息注意:
该类的class文件只在一次到方法区中,
该类的class对象在程序中是唯一的。
所以不管使用那种方式获取class对象都是同一个
public class Test01 {public static void main(String[] args) throws ClassNotFoundException {//获取class对象方式1Class<? extends Student> clazz1 = Student.class;//获取class对象方式2Student stu = new Student();Class<? extends Student> clazz2 = stu.getClass();//获取class方式3Class<?> clazz3 = Class.forName("com.qf.reflex01.Student");System.out.println(clazz1 == clazz2);//trueSystem.out.println(clazz1 == clazz3);//true}
}
问:那种方法更好?
推荐path的获取方法更好,使用配置文件,可维护性更高;
但是根据不同情况相应使用三种方法经验:配置文件 + Class.forName()
public class Test02 {public static void main(String[] args) throws IOException, ClassNotFoundException {Properties p = new Properties();p.load(Test02.class.getClassLoader().getResourceAsStream("classPath.properties"));String path = p.getProperty("path");Class<?> clazz = Class.forName(path);System.out.println(clazz);}
}
配置文件
//配置文件:classPath.properties文件
path=com.qf.reflex01.Student
注意:
父类子类都有toString情况,注意子类toString的重写【自动生成时注意添加父类的toString,不然可能出现输出不了对应预想的结果】
下面操作属性就会出现
public class Person {private String name;private char sex;private int age;//有参、无参构造、get、set、toString方法【略】}
public class Student extends Person{private String classId;private String id;public static final String str = "风华雪月";//有参、无参构造、get、set、toString方法【略】}
反射访问内容
反射操作注意设置权限
操作属性
带Declared的方法都能获取私有的
乱填属性名未匹配对应属性异常(用try-catch)
属性参数值=修饰符值累加(数字)判断其修饰情况(即用什么修饰)4操作属性方式:
直接设置属性(set方法,前提类和对象都有)
通过反射设置属性(优点:后面配置文件设置,底层自动创建类和对象)
public class Test03 {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {// Properties p = new Properties();
// p.load(Test03.class.getClassLoader().getResourceAsStream("classPath.properties"));
// String path = p.getProperty("path");// Class<?> clazz = Class.forName(path);//获取本类及其父类公有的属性对象
// Field[] fields = clazz.getFields();
// for (Field field : fields) {
// System.out.println(field);
// }//获取本类所有的属性对象
// Field[] fields = clazz.getDeclaredFields();
// for (Field field : fields) {
// System.out.println(field);
// }//获取本类及其父类所有的属性对象
// for(Class<?> c = clazz;c!=null;c=c.getSuperclass()){
// Field[] fields = c.getDeclaredFields();
// for (Field field : fields) {
// System.out.println(field);
// }
// }// try {
// //获取本类指定的属性对象
// Field field = clazz.getDeclaredField("str");
// System.out.println(field);
//
// //获取属性参数值
// int modifiers = field.getModifiers();
// System.out.println("是否使用public修饰:" + Modifier.isPublic(modifiers));
// System.out.println("是否使用stati修饰:" + Modifier.isStatic(modifiers));
// System.out.println("是否使用final修饰:" + Modifier.isFinal(modifiers));
// System.out.println("是否使用private修饰:" + Modifier.isPrivate(modifiers));
// System.out.println("是否使用protected修饰:" + Modifier.isProtected(modifiers));
// System.out.println("是否使用transient修饰:" + Modifier.isTransient(modifiers));
//
// } catch (NoSuchFieldException e) {//属性名没有配到对应的属性就报该异常
// e.printStackTrace();
// } catch (SecurityException e) {
// e.printStackTrace();
// }//通过反射设置对象里的属性
// Student stu = new Student("奇男子", '男', 23, "2401", "001");
// Field field = clazz.getDeclaredField("id");
// field.setAccessible(true);//设置修改权限
// field.set(stu, "666");//设置stu对象里的id属性为"666"的值
// System.out.println(stu);//通过反射工具类设置对象里的属性Student stu = new Student();ReflexUtil.setField(stu, "name", "奇男子");ReflexUtil.setField(stu, "sex", '男');ReflexUtil.setField(stu, "age", 23);ReflexUtil.setField(stu, "classId", "2401");ReflexUtil.setField(stu, "id", "001");System.out.println(stu);}
}
反射的工具类
utils包下
这里反射工具类写了获取和设置的方法【这里获取属性用到getClass()更方便】
public class ReflexUtil {/*** 获取当前类及其父类的属性对象* @param clazz class对象* @param name 属性名* @return 属性对象*/public static Field getField(Class<?> clazz,String name){for(Class<?> c = clazz;c != null;c = c.getSuperclass()){try {Field field = c.getDeclaredField(name);return field;} catch (NoSuchFieldException e) {} catch (SecurityException e) {}}return null;}/*** 设置对象中的属性* @param obj 对象* @param name 属性名* @param value 属性值*/public static void setField(Object obj,String name,Object value){Field field = getField(obj.getClass(), name);if(field != null){field.setAccessible(true);try {field.set(obj, value);} catch (IllegalArgumentException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}/*** 创建对象* @param clazz class对象* @param paremeterType 构造方法中的参数类型数组* @param paremeters 构造方法中的参数数据* @return 对象*/public static <T> T newInstance(Class<T> clazz,Class<?>[] parameterTypes,Object[] paremeters){try {Constructor<T> constructor = clazz.getDeclaredConstructor(parameterTypes);constructor.setAccessible(true);T obj = constructor.newInstance(paremeters);return obj;} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}
}
操作方法
构造方法
不能获取父类的构造方法【一般考虑本类的构造方法】
反射工具类再添加创建对象的方法【外面传数组】调用构造函数创建对象
public class Test04 {public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {// Properties p = new Properties();
// p.load(Test04.class.getClassLoader().getResourceAsStream("classPath.properties"));
// String path = p.getProperty("path");// Class<?> clazz = Class.forName(path);//获取该类公有的构造方法对象
// Constructor<?>[] constructors = clazz.getConstructors();
// for (Constructor<?> constructor : constructors) {
// System.out.println(constructor);
// }//获取该类所有的构造方法对象
// Constructor<?>[] constructors = clazz.getDeclaredConstructors();
// for (Constructor<?> constructor : constructors) {
// System.out.println(constructor);
// }//获取无参构造对象
// Constructor<?> constructor = clazz.getDeclaredConstructor();
// //利用无参构造对象创建该类的对象
// constructor.setAccessible(true);//设置修改权限
// Student stu = (Student) constructor.newInstance();
// System.out.println(stu);//利用无参构造对象创建该类的对象 -- 简化版
// Student stu = (Student) clazz.newInstance();
// System.out.println(stu);//获取有参构造对象
// Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,char.class,int.class,String.class,String.class);
// //利用有参构造对象创建该类的对象
// constructor.setAccessible(true);//设置修改权限
// Student stu = (Student) constructor.newInstance("奇男子",'男',23,"2401","001");
// System.out.println(stu);//利用反射工具类去创建对象 -- 底层调用无参构造
// Student stu = ReflexUtil.newInstance(Student.class, null, null);
// System.out.println(stu);//利用反射工具类去创建对象 -- 底层调用有参构造Class<?>[] parameterTypes = {String.class,char.class,int.class,String.class,String.class};Object[] paremeters = {"奇男子",'男',23,"2401","001"};Student stu = ReflexUtil.newInstance(Student.class, parameterTypes, paremeters);System.out.println(stu);}
}
普通方法
操作数组
操作泛型
反射的实际应用
总结
3.反射 — 重要!!!
获取class对象
操作属性
操作方法
注意:封装反射的工具类