12 类加载器和反射机制
12.1 类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象。
1、类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三个步骤来实现对这个类的初始化。
加载
就是指将calss文件读入到内存,并为之穿件一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
为栈开辟空间
默认初始化
显示初始化
构造初始化
2、类的加载时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
3、类加载器特点
并非一次性加载
需要的时候加载
static语句块在加载后执行一次
动态(dynamic)语句块每次new新的对象都会执行(等同于构造方法中的语句)
4、类加载器的组成
Bootstrap ClassLoader根类加载器
也被称为引导类加载器,负责Java核心类的加载。(在JDK中JRE的lib目录下rt.jar文件中)
Extension ClassLoader扩展类加载器
负责JRE的扩展目录中jar包的加载。(在JDK中JRE的lib目录下的ext目录中)
System ClassLoader系统加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所制定的jar包和类路径。
12.2 反射机制
1、介绍
JAVA反射机制实在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为JAVA反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。
通俗来讲,反射就是通过class文件对象,去使用该文件中的成员变量、构造方法、成员方法。
Class类:
成员变量:Field
构造函数:Constructor
成员方法:Method
2、获取calss文件对象的方式
Object类的getClass()方法
数据类型的静态属性class
Class类中的静态方法:public static Class forName(String className)
在开发中一般使用第三种,因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { //方式1 Person p1 = new Person(); Class c1 = p1.getClass(); Person p2 = new Person(); Class c2 = p1.getClass(); System.out.println(p1==p2); System.out.println(c1==c2); //方式2 Class c3 = Person.class; System.out.println(c1==c3); //方式3 Class c4 = Class.forName("Person"); System.out.println(c1==c4); }}public class Person { private String name; int age; public String sex; public Person() { } private Person(String name) { this.name = name; } public Person(String name, int age, String sex) { this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public void function(){ System.out.println("function"); } public String function(String s,int a){ return s+"-----"+a; } private void show(){ System.out.println("show"); }}12.2.1、通过反射获取构造方法并使用
1、获取构造方法:
public Constructor[] getConstructors();所有公共构造方法
public Constructor[] getDeclaredConstructors();所有构造方法
public Constructor<T> getConstructor(Class... parameterTypes);单个公共构造方法
参数表示:要获取的构造方法的构造参数的个数及数据类型的class字节码文件对象。、
public Constructor<T> getDeclaredConstructor(Class... parameterTypes);单个任意构造方法
2、获取无参构造方法并使用
package cn.ref;
import java.lang.reflect.Constructor;public class ReflectDemo { public static void main(String[] args) throws Exception { //获取字节码对象 Class c = Class.forName("cn.ref.Person"); //获取单个构造方法 Constructor con = c.getConstructor();//返回的是构造方法对象 //通过获取的构造方法创建一个新实例 Object obj = con.newInstance(); System.out.println(obj); Person p = (Person)obj; p.function(); }}3、获取带参构造方法并使用
package cn.ref;
import java.lang.reflect.Constructor;public class ReflectDemo { public static void main(String[] args) throws Exception { //获取字节码对象 Class c = Class.forName("cn.ref.Person"); //获取带参构造方法 Constructor con = c.getConstructor(String.class,int.class,String.class);//返回的是构造方法对象 //通过获取的构造方法创建一个新实例 Object obj = con.newInstance("张三",20,"男"); System.out.println(obj); Person p = (Person)obj; System.out.println(p.getName()); }}4、获取私有构造方法并使用
package cn.ref;
import java.lang.reflect.Constructor;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); // 获取私有构造方法 Constructor con = c.getDeclaredConstructor(String.class);// 返回的是构造方法对象 //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查 con.setAccessible(true);//设置为true才能访问私有,否则报java.lang.IllegalAccessException // 通过获取的构造方法创建一个新实例 Object obj = con.newInstance("张三"); System.out.println(obj); Person p = (Person) obj; System.out.println(p.getName()); }}12.2.2、通过反射获取成员变量并使用
1、获取成员变量
public Field getField(String name);获取单个公共成员变量
public Field getDeclaredField(String name);获取单个任意成员变量
public Field[] getFields();获取所有公共成员变量
public Field[] getDeclaredFields();获取所有成员变量
2、获取公共成员变量并使用
package cn.ref;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); //通过构造方法创建一个实例 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取成员变量 Field f = c.getField("sex"); //赋值 f.set(obj, "女"); System.out.println(obj); Person p = (Person) obj; System.out.println(p.getSex()); }}3、获取私有成员变量并使用
package cn.ref;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); //通过构造方法创建一个实例 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取成员变量 Field f = c.getDeclaredField("name"); f.setAccessible(true); //赋值 f.set(obj, "张三"); System.out.println(obj); Person p = (Person) obj; System.out.println(p.getName()); }}12.2.3、通过反射获取成员方法并使用
1、获取成员方法
public Method getMethod(String name, Class... parameterTypes);获取单个公共成员方法
public Method getDeclaredMethod(String name, Class... parameterTypes);获取单个任意成员方法
public Method[] getMethods();获取所有公共成员方法
public Method[] getDeclaredMethods();获取所有成员方法
2、获取无参无返回值成员方法并使用
package cn.ref;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); //通过构造方法创建一个实例 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取无参成员方法Method m = c.getMethod("function");
//调用方法 m.invoke(obj); }}3、获取带参带返回值成员方法并使用package cn.ref;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); // 通过构造方法创建一个实例 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取带参成员方法 Method m = c.getMethod("function",String.class,int.class); // 调用方法 Object o = m.invoke(obj,"hello",20); System.out.println(o); }}4、获取私有成员方法并使用
package cn.ref;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class ReflectDemo { public static void main(String[] args) throws Exception { // 获取字节码对象 Class c = Class.forName("cn.ref.Person"); // 通过构造方法创建一个实例 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取私有成员方法 Method m = c.getDeclaredMethod("show"); m.setAccessible(true); // 调用方法 m.invoke(obj); }}12.2.4、反射机制的使用
1、通过反射运行配置文件的内容
测试类:
package cn.refTest;
import java.io.FileReader;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.util.Properties;public class Test { public static void main(String[] args) throws Exception { //加载配置文件 Properties p =new Properties(); FileReader fr =new FileReader("class.properties"); p.load(fr); fr.close(); //获取数据 String className = p.getProperty("className"); String methodName = p.getProperty("methodName"); //反射 Class c = Class.forName(className); Constructor con = c.getConstructor(); Object obj = con.newInstance(); //调用方法 Method m = c.getMethod(methodName); m.invoke(obj); }}Student类:
package cn.refTest;
public class Student { public void show(){ System.out.println("学习"); }}Teacher类:
package cn.refTest;
public class Teacher { public void show(){ System.out.println("授课"); }}Worker类
package cn.refTest;
public class Worker { public void show(){ System.out.println("工作"); }}配置文件:
className=cn.refTest.Student
methodName=show2、给定ArrayList<Integer>的一个对象,在这个集合中添加一个字符串。
/*
* ArrayList原始的add方法是Object类型 * 所以拿到它的字节码文件对象,通过反射就可以将字符串放进去 */package cn.refTest2;
import java.lang.reflect.Method;import java.util.ArrayList;public class ArrayListTest { public static void main(String[] args) throws Exception { ArrayList<Integer> array = new ArrayList<Integer>(); Class c = array.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(array, "hello");// 调用array的add方法,传入的值是hello m.invoke(array, "java"); System.out.println(array); }}12.2.4 动态代理
1、代理:本来应该自己做的事,却请了别人来做,被请的人就是代理对象。
2、动态代理:其实就是通过反射来生成的一个代理。
在Java中java.lang.reflect包下提供了一个Proxy类和InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口来做代理,如果来对类来做,可以使用cglib。
Proxy类中的方法创建动态代理对象:
Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);
最终会调用InvocationHandle方法:
Object invoke(Object proxy,Method method,Object[] args);
3、实现动态代理,在登陆、注册等操作之前和之后加入权限校验和日志记录(功能都用输出语句代替)
Test类:
package cn.refTest4;
import java.lang.reflect.Proxy;public class Test { public static void main(String[] args) { // TODO Auto-generated method stub UserDao ud = new UserDaoImp(); //准备对ud对象做代理 MyInvocationHandler handler = new MyInvocationHandler(ud); //创建动态代理对象 UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler); proxy.add(); proxy.delete(); proxy.update(); proxy.find(); StudentDao sd = new StudentDaoImp(); //准备对ud对象做代理 MyInvocationHandler handler2 = new MyInvocationHandler(sd); //创建动态代理对象 StudentDao stu = (StudentDao) Proxy.newProxyInstance(sd.getClass().getClassLoader(), sd.getClass().getInterfaces(), handler2); stu.login(); stu.regist(); }}MyInvocationHandler类:
package cn.refTest4;
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("权限校验"); Object obj = method.invoke(target, args); System.out.println("日志记录"); return obj; }}StudentDao接口:
package cn.refTest4;
public interface StudentDao { public abstract void login(); public abstract void regist();}UserDao接口:
package cn.refTest4;
public interface UserDao { public abstract void add(); public abstract void delete(); public abstract void update(); public abstract void find();}StudentDao类:
package cn.refTest4;
public class StudentDaoImp implements StudentDao { @Override public void login() { System.out.println("登录功能"); } @Override public void regist() { System.out.println("注册功能"); }}UserDaoImp类:
package cn.refTest4;
public class UserDaoImp implements UserDao { @Override public void add() { System.out.println("添加功能"); } @Override public void delete() { System.out.println("删除功能"); } @Override public void update() { System.out.println("更新功能"); } @Override public void find() { System.out.println("查找功能"); }}