1、Java的类加载机制
(1)概念
Java中的类加载机制指虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换、解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。
它将类的各个组成部分封装成了不同的对象。方便操作
(2)作用:
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
(3)java代码在计算机中经历的阶段
*源代码阶段(硬盘中):.java文件 被javac编译为.class文件
*Class类对象阶段(内存中):类加载器将.class文件加载进内存,生成class类对象
*运行时阶段:创建对象后进入Runtime状态
2、获取Class对象
同一个字节码文件在一次程序运行过程中,只会被加载一次。
(1)源代码阶段:
将字节码文件加载进内存,返回class对象:Class.forName(“全类名”);
(2)Class类对象阶段:
类名.class;
(3)运行时阶段:
对象.getClass;
//1、源代码阶段
Class cla1 = Class.forName("cn.sweetie.login.domain.Person2");//全包类名
//2、Class类对象阶段
Class cla2 = Person2.class;
//3、运行时阶段
Person2 person2 = new Person2();
Class cla3 = person2.getClass();
3、通过class对象获取成员变量们Fields
(1)方法:
*获取public修饰的成员变量
Field[] getFields();
Field getField(String name);
*获取被任何修饰符修饰的成员变量
Field[] getDeclaredFields();
Field getDeclaredField(String name);
(2)对获取来的成员变量后,获取值和改变值
a.获取值 get(Object object)
b.改变值 set(Object object,Object value)
注意:1、不是public修饰的变量,要进行暴力反射,才能成功get、set值。
暴力反射:成员变量对象名.setAccessible(true);
Class cla2 = Person2.class;
Field id = cla2.getDeclaredField("id");
Person2 person2 = new Person2();
id.setAccessible(true);
id.get(person2);
2、如果是final修饰的成员变量,反射可以修改成功包装类型的变量值;基本类型和String类型修改不成功
*person2.java
private final Integer id = 1;
private final int age = 18;
public Integer getId() {
return id;
}
public int getAge() {
return age;
}
*test.java
Class cla2 = Person2.class;
Person2 person2 = new Person2();
Field id = cla2.getDeclaredField("id"); //Integer
Field age = cla2.getDeclaredField("age"); //int
id.setAccessible(true);
age.setAccessible(true);
id.set(person2,2);
age.set(person2, 19);
Integer id1 = person2.getId();
int age1 = person2.getAge();
System.out.println(id.get(person2)); //2
System.out.println(person2.getId()); //2
System.out.println(age.get(person2)); //19
System.out.println(person2.getAge()); //18
4、通过class对象获取构造方法们Constructors
(1)方法:
*获取public修饰的构造方法
Constructor getConstructor(Class<?>… parameterTypes)
Constructor<?>[] getConstructors()
*获取被任何修饰符修饰的构造方法
Constructor getDeclaredConstructor(Class<?>… parameterTypes)
Constructor<?>[] getDeclaredConstructors()
(2)用获取来的构造器创建对象
构造器对象.newInstance()
注意:用反射获取非public修饰的构造器方法后,创建对象要进行暴力反射
构造器对象名.setAccessible(true);
(3)特殊:如果是public修饰的空参构造,直接使用class类对象.newInstance()来创建对象
5、通过 class对象获取成员方法们Methods
(1)方法:
*获取public修饰的成员方法
Method[] getMethods()
Method getMethod(String name, Class<?>… parameterTypes)
*获取被任何修饰符修饰的成员方法
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
(2)执行方法
Object invoke(Object obj, Object… args)
Class cla2 = Person2.class;
Person2 person2 = new Person2();
Method eat = cla2.getMethod("eat",String.class);
eat.invoke(person2,"菠菜");
注意:用反射获取非public修饰的成员方法后,调用方法要进行暴力反射
成员方法对象名.setAccessible(true);
6、用 class对象获取全类名
String getName()
练习
题目:写一个“框架”,要求可以调用任意类的任意空参方法。
思路:在配置文件里写类名和方法名,用反射将是该类名的类加载进入内存,创建对象,并调用是该方法名的方法。
解答:
(1)在src根目录下创建pro.properties配置文件。里边有类名信息和方法名信息。使用者可以仅通过修改配置文件来调用任意类的任意方法
className = cn.sweetie.reflect.domain.Student //全类名
methodName = sleep
(2)写两个JavaBean样例,里边有无参的成员方法
(2)使用反射
1、加载配置文件进入内存
InputStream rs = ReflectDemo.class.getClassLoader().getResourceAsStream("pro.properties");
2、使用properties集合(Map-》HashTable-》Properties)load 输入流对象,将配置文件中键值对存储到集合中来。
Properties properties = new Properties();
properties.load(rs);
3、拿到类名和方法名
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
4、加载该类进入内存
Class<?> cla = Class.forName(className);
5、使用 类对象.方法名 创建对象(JavaBean中有空参构造方法的话)
Object o = cla.newInstance();
6、通过类对象找到该方法
Method method = cla.getMethod(methodName);
7、调用方法,传入对象参数
method.invoke(o);