美文网首页
Java类加载机制与反射

Java类加载机制与反射

作者: 官子寒 | 来源:发表于2020-01-08 21:26 被阅读0次

1. 类的加载、连接和初始化

1.1 JVM和类

  • 当调用java程序时,会启动一个JVM进程

JVM中止的情形:

  • 程序运行到最后正常结束
  • System.exit()Runtime.getRuntime().exit()代码处结束程序
  • 出现未捕获的异常或错误
  • 平台强制结束了程序

1.2 类的加载

类加载过程:加载 -> 连接 -> 初始化

加载:将类的class文件读入内存,并创建一个java.lang.Class对象;这个过程由JVM的类加载器完成,除此之外,开发者可以通过继承ClassLoader来创建类加载器

连接:负责把类的二进制数据合并到JRE中,分为三个阶段
1)验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致
2)准备:负责为类的类变量分配内存,并设置默认初始值
3)解析:将类的二进制数据中的符号引用替换成直接引用
Java 符号引用 与 直接引用

初始化
1)假如这个类还没有被加载和连接,则程序先加载并连接该类
2)假如该类的直接父类没有被初始化,则先初始化其直接父类
3)假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化的时机

  • 创建该类的实例
  • 调用某个类的类方法
  • 访问某个类或接口的类变量,或为该变量赋值
  • 使用反射方式强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exc命令运行某个主类.当运行某个主类时,程序会先初始化该主类
  • final类型的变量如果在编译期间可以确定下来,系统在编译时会将它出现的地方替换成它的值,因此不会导致该类的初始化
class Test {
    static {
        System.out.println("Test的静态初始化块");
    }
}
public class Tester {

    public static void main(String[] args) {
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        try {
            loader.loadClass("Test");
            System.out.println("系统加载Test类");
            Class.forName("Test");
            System.out.println("系统初始化Test类");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
Test

可以看到,通过loader.loadClass("Test),类需要先加载,再进行初始化

2. 类加载器

  • 类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
  • 载入JVM的类有一个唯一的标识 --- 全限定类名类加载器
  • 类加载器层次结构有3层:Bootstrap ClassLoader根类加载器,负责加载Java的核心类,Extension ClassLoaderSystem ClassLoader
  • JVM除了根类加载器以外都是ClassLoader的子类
  • JVM的加载机制:
    1)全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类负责
    2)父类委托:先让父类加载,父类加载不了由自己加载
    3)缓存机制:当程序中需要使用某个Class时,累计扎起先从缓存区搜寻该Class
加载器层次结构

2.1 创建自定义的类加载器

自定义构建方法
public class ClassLoaderPropTest extends ClassLoader {

    private byte[] getBytes(String fileName) throws IOException {
        File file = new File(fileName);
        long len = file.length();
        byte[] raw = new byte[(int) len];
        FileInputStream fin = new FileInputStream(file);
        int r = fin.read(raw);
        if(r!=len){
            System.out.println("无法读取全部文件");
            throw new IOException("无法读取全部文件");
        }
        return raw;
    }

    private boolean compile(String javaFile) throws IOException {
        System.out.println("正在编译..." + javaFile);
        Process p = Runtime.getRuntime().exec("javac" + javaFile);
        try{
            p.waitFor();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int ret = p.exitValue();
        return ret == 0;
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        String fileStub = name.replace(".", "/");
        String javaFileName = fileStub + ".java";
        String classFileName = fileStub + ".class";
        File javaFile = new File(javaFileName);
        File classFile = new File(classFileName);
        if((javaFile.exists() && !classFile.exists()) || (javaFile.lastModified() > classFile.lastModified())){
            try {
                if(!compile(javaFileName) || !classFile.exists()) {
                    throw new ClassNotFoundException("Class Not Found: " + javaFileName);
                }
                if(classFile.exists()) {
                    try {
                        byte[] raw = getBytes(classFileName);
                        clazz = defineClass(name, raw, 0, raw.length);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(clazz == null) {
                    throw new ClassNotFoundException("没有编译完成:" + name);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return clazz;
    }

    public static void main(String[] args) throws Exception {
        String progClass = args[0];
        System.out.println(progClass);
        String[] progArgs = new String[args.length - 1];
        System.arraycopy(args, 1, progArgs, 0, progArgs.length);
        ClassLoaderPropTest classLoader = new ClassLoaderPropTest();
        Class<?> clazz = classLoader.loadClass(progClass);
        Method main = clazz.getMethod("main", (new String[0]).getClass());
        Object arqsArray[] = {progArgs} ;
        main.invoke (null ,arqsArray) ;
    }
}

2.2 URlClassLoader

  • 系统类加载器扩展类加载器的父类
  • 既可以从本地获取二进制文件,也可以从远端获取
  • URLClassLoader(URL[] urls): 使用默认的父类加载器创建一个ClassLoader对象,该对象将从urls 所指定的系列路径来查询并加载类。
  • URLClassLoader(URL[] urls, ClassLoader parent): 使用指定的父类加载器创建一个ClassLoader
    对象,其他功能与前一个构造器相同。

3. 通过反射查看类信息

  • 适用场景:编译时无法预知该类和对象可能属于哪些类,程序只能依靠运行时的消息来发现该类的真实信息
  • 获得Class对象的方式:
方法名 作用
forName(String clazzName) 静态方法
class 例如:Person.class,代码更安全,性能更好
getClass() Object方法
  • 获取class对应类所办含的构造器
方法名 作用
Connstructor<T> getConstructor(Class<?>.. . parameterTypes) 返回此Class 对象对应类的、带指定形参列表的public 构造器
Constructor<?>[] getConstructors() 返回此Class 对象对应类的所有public 构造器
Constructor<T> getDeclaredConstructor(Class<?>.. . parameterTypes) 返回此Class 对象对应类的、带指定形参列表的构造器,与构造器的访问权限无关
Constructor<?>[] getD eclaredConstructor() 返回此Class 对象对应类的所有构造器,与构造器的访问权限无关
  • 获取Class对应类所包含的方法
方法名 作用
Method getMethod(String name, Class<?>... parameterTypes) 返回此Class 对象对应类的、带指定形参列表的public 方法
Method getMethods() 返回此Class 对象所表示的类的所有public 方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回此Class 对象对应类的、带指定形参列表的方法,与方法的访问权限无关。
Method[] getDeclaredMethod() 返回此Class 对象对应类的全部方法, 与方法的访问权限无关
  • 获取Class对应类所包含的成员变量
  • 获取Class对应类所包含的Annotation
  • 获取Class对应类所包含的内部类
反射API
public class reflectionTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class orangeClass = Orange.class;
        Method method = orangeClass.getMethod("info");
        Orange or1 = new Orange(1);
        method.invoke(or1);
    }
}
Parameters
  • javac编译时没有形参名信息,因此如果需要调用isNamePresentgetName的话,需要在编译时使用javac -parameters

4. 通过反射生成并查看对象

4.1 创建对象

public class ObjectPoolFactory {
    private Map<String, Object> objectPool = new HashMap<>();
    private Object createObject(String clazzName) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> clazz = Class.forName(clazzName);
        return clazz.getConstructor().newInstance();
    }
    public void initPool(String fileName) {
        try {
            FileInputStream fis = new FileInputStream(fileName);
            Properties properties = new Properties();
            properties.load(fis);
            for(String name: properties.stringPropertyNames()) {
                objectPool.put(name, createObject(properties.getProperty(name)));
            }
        } catch (IOException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }

    }

    public Object getObject(String name) {
        return objectPool.get(name);
    }

    public static void main(String[] args) {
        ObjectPoolFactory oPF = new ObjectPoolFactory();
        oPF.initPool("obj.txt");
        System.out.println(oPF.getObject("a"));
        System.out.println(oPF.getObject("b"));
    }
}
ObjectPool

4.2 调用方法

4.3 访问字段

属性

4.4 操作数组

数组

5. 使用反射生成JDK动态代理

5.1 使用Proxy和InvocationHandler

方法名 作用
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 创建一个动态代理类所对应的Class 对象, 该代理类将实现interfaces 所指定的多个接口。第一个ClassLoader 参数指定生成动态代理类的类加载器
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h) 直接创建一个动态代理对象, 该代理对象的实现类实现了interfaces 指定的系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler 对象的invoke 方法
动态代理

5.2 动态代理和AOP

public class DogUtil {
    public void mehod1() {
        System.out.println("---正在执行第一个方法---");
    }

    public void mehod2() {
        System.out.println("---正在执行第二个方法---");
    }

}
public class MyInvocationHandler implements InvocationHandler {
    Object proxyObject;

    public void setProxyObject(Object proxyObject) {
        this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        DogUtil dogUtil = new DogUtil();
        dogUtil.mehod1();
        dogUtil.mehod2();
        Object ret = method.invoke(proxyObject, args);
        return ret;
    }
}
public class InvocationHandlerFactory {
    public static Object getProxy(Object target) {
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        myInvocationHandler.setProxyObject(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),myInvocationHandler);
    }
}
public class TestProxt {
    public static void main(String[] args) {
        DogParent dog = new Dog();
        DogParent dogProxy = (DogParent) InvocationHandlerFactory.getProxy(dog);
        dogProxy.info();
    }
}
Result 注意事项

动态代理对象Proxy的三个参数

相关文章

网友评论

      本文标题:Java类加载机制与反射

      本文链接:https://www.haomeiwen.com/subject/jczfactx.html