注解与反射

注解与反射

一、注解

1.1、注解是什么

image-20240716181040059

1.2、内置注解

image-20240716181458681

注意镇压注解镇压的是警告(黄色),不是严重错误注解(红色)

image-20240716182314811

1.3、元注解

image-20240716182438994

public class AnnotationDemo {
    @Annoal
    int a;
    public static void main(String[] args) {
    }
    @Annoal
    void toDong(){

    }
}
//自定义注解
//@Target 注解作用领域 METHOD:方法 FIELD:字段 等等
@Target({ElementType.METHOD,ElementType.FIELD})
//@Target 表示注解在什么级别下保存该注解 RUNTIME:运行时
@Retention(RetentionPolicy.RUNTIME)
@interface Annoal{

}

1.4、自定义注解

image-20240717170602080

public class AnnotationDemo1 {
    @Annoal
    int a;

    @Annoal01(name = "小明",city = {"广州","深圳"})
    void toDong01(){

    }
    @Annoal02(value = "sss",age = 88)
    void toDong02(){

    }
    @Annoal03("good boy")
    void toDong03(){

    }
}
//自定义注解
//@Target 注解作用领域 METHOD:方法 FIELD:字段 等等
@Target({ElementType.METHOD,ElementType.FIELD})
//@Target 表示注解在什么级别下保存该注解 RUNTIME:运行时
@Retention(RetentionPolicy.RUNTIME)
@interface Annoal01{
    //注意里面的参数格式为方法
    String name ();
    //default为默认值 不带参数时生效
    int age() default 18;
    String[] city();
}

@Target({ElementType.METHOD,ElementType.FIELD})
//@Target 表示注解在什么级别下保存该注解 RUNTIME:运行时
@Retention(RetentionPolicy.RUNTIME)
@interface Annoal02{
    String value();
    int age();
}

@Target({ElementType.METHOD,ElementType.FIELD})
//@Target 表示注解在什么级别下保存该注解 RUNTIME:运行时
@Retention(RetentionPolicy.RUNTIME)
@interface Annoal03{
    //value 可以不加名字绑定 为默认参数 单独存在时有效
    String value();
}

二、反射

2.1、概述

静态与动态语言

image-20240717172323216

Java Reflection

image-20240717172604348

反射机制应用

image-20240717172910604

反射优缺点

image-20240717172938480

反射相关API

image-20240717173017335

2.2、Class类

image-20240717173542149

image-20240717173659499

Class类常用方法

image-20240717174423326

image-20240717175148544

public class ReflectionDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //创建学生类
        Pension pension = new Student();
        //通过对象直接获取反射对象
        Class c1 = Student.class;
        //通过实例化对象 获取反射对象
        Class c2 = pension.getClass();
        //通过包路径获取反射对象
        Class c3 = Class.forName("com.yzy.reflection.Student");
        //补充:包装类可通过.type获取反射对象
        Class c4 = Integer.class;
        //补充:包装类可通过.type获取反射对象的类型
        Class c5 = Integer.TYPE;
        //比较反射的类是否一致
        System.out.println(c1.hashCode()+";"+c2.hashCode()+";"+c3.hashCode()+";"+c4+";"+c5);
        //获取父类对象
        System.out.println(c1.getSuperclass()+";"+c2.getSuperclass()+";"+c3.getSuperclass());

    }
}

class Pension{
    String name = "人";
    void speakState(){
        System.out.println(name);
    }
}

class Student extends Pension{
    String name = "学生";
    void speakState(){
        System.out.println(name);
    }
}

class Teacher extends  Pension{
    String name = "老师";
    void speakState(){
        System.out.println(name);
    }
}

结果

image-20240717180840536

有Class对象的类

image-20240717175251900

public class ReflectionDemo2 {
    public static void main(String[] args) throws ClassNotFoundException {
        //接口
        Class<Runnable> c1 = Runnable.class;
        //类
        Class<NumName> c2 = NumName.class;
        //注解
        Class<Override> c3 = Override.class;
        //枚举
        Class<ElementType> c4 = ElementType.class;
        //一维数组
        Class<int[]> c5 = int[].class;
        //二维数组
        Class<int[][]> c6 = int[][].class;
        //参数类型
        Class<Void> c7 = void.class;
        //Class
        Class<Class> c8 = Class.class;
        //包装类型
        Class<Integer> c9 = Integer.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
        //维度相同反射类型一致
        int[] a = new int[1];
        int[] b = new int[412];
        System.out.println(a.getClass().hashCode()+";"+b.getClass().hashCode());

        //维度相同反射类型一致
        NumName name1 = new NumName();
        NumName name2 = new NumName();
        System.out.println(name1.getClass().hashCode()+";"+name2.getClass().hashCode()+";"+NumName.class.hashCode());
        //类型一致但本质是两个不同的对象
        System.out.println(name1.hashCode()+";"+name2.hashCode());
        System.out.println(NumName.class.getName()+";"+name2.name);

    }
}

class NumName{
    String name = "人";
    void speakState(){
        System.out.println(name);
    }
}

2.3、获取运行时类的结构

image-20240719125026761

示例:

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //反射获取类
        Class aClass = Class.forName("com.yzy.reflection.Demo01");
        //获取类的路径
        System.out.println("获取类的路径:");
        System.out.println(aClass.getName());
        //获取类的名字
        System.out.println("获取类的名字:");
        System.out.println(aClass.getSimpleName());
        //获取类的所有public属性,包括父类
        Field[] fields = aClass.getFields();
        System.out.println("获取类的所有public属性,包括父类:");
        for (Field field : fields) {
            System.out.println(field);
        }
        //获取类的属性,不包括父类
        Field[] fields1 = aClass.getDeclaredFields();
        //所有方法与构造器同理
        //Method[] fields1 = aClass.getDeclaredMethods();
        //Constructor[] fields1 = aClass.getDeclaredConstructors();
        System.out.println("获取类的属性,不包括父类:");
        for (Field field : fields1) {
            System.out.println(field);
        }

        //获取指定构造器
        System.out.println("获取指定构造器:");
        //无参
        System.out.println(aClass.getDeclaredConstructor(null));
        //有参
        System.out.println(aClass.getDeclaredConstructor(String.class));
        //获取指定方法同理
        System.out.println("获取指定方法同理:");
        System.out.println(aClass.getMethod("getName"));
        System.out.println(aClass.getMethod("setAge", int.class));
    }
}
class Demo01 extends Demo02{
    public String name = "小米";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Demo01(String name) {
        this.name = name;
    }
    public Demo01() {

    }
}
class Demo02{
    public int age = 18;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Demo02(int age) {
        this.age = age;
    }
    public Demo02() {

    }
}

image-20240719130849727

2.4、获得类该如何使用

image-20240719131108794

调用类的方法

image-20240719131643302

image-20240719131740309

public class Test02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //反射获取类
        Class aClass = Class.forName("com.yzy.reflection.Demo01");
        //获取类的路径
        System.out.println("获取类的路径:");
        System.out.println(aClass.getName());
        //获取类的名字
        System.out.println("获取类的名字:");
        System.out.println(aClass.getSimpleName());
        //获取类的所有public属性,包括父类
        Field[] fields = aClass.getFields();
        System.out.println("获取类的所有public属性,包括父类:");
        for (Field field : fields) {
            System.out.println(field);
        }
        //获取类的属性,不包括父类
        Field[] fields1 = aClass.getDeclaredFields();
        //所有方法与构造器同理
        //Method[] fields1 = aClass.getDeclaredMethods();
        //Constructor[] fields1 = aClass.getDeclaredConstructors();
        System.out.println("获取类的属性,不包括父类:");
        for (Field field : fields1) {
            System.out.println(field);
        }

        //获取指定构造器
        System.out.println("获取指定构造器:");
        //无参
        System.out.println(aClass.getDeclaredConstructor(null));
        //有参
        System.out.println(aClass.getDeclaredConstructor(String.class));
        //获取指定方法同理
        System.out.println("获取指定方法同理:");
        System.out.println(aClass.getMethod("getName"));
        System.out.println(aClass.getMethod("setAge", int.class));
    }
}
class Demo01 extends Demo02{
    public String name = "小米";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Demo01(String name) {
        this.name = name;
    }
    public Demo01() {

    }
}
class Demo02{
    private int age = 18;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Demo02(int age) {
        this.age = age;
    }
    public Demo02() {

    }
}

image-20240719134822521

setAccessible

image-20240719131826468

public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        //反射获取类
        Class aClass = Class.forName("com.yzy.reflection.Demo03");

        //使用反射的类new对象
        Demo03 demo03 = (Demo03)aClass.newInstance();//本质上是通过无参构造new 所以需要无参构造
        //可以调用方法
        //demo03.setName("dada");.....

        //使用反射的类通过有参构造new对象
        //获取有参构造方法
        Constructor constructor = aClass.getDeclaredConstructor(String.class);
        //通过有参构造new对象
        Demo03 demo003 = (Demo03) constructor.newInstance("小明");

        //获取反射类,获取类方法,调用类方法
        //反射获取类
        Class<Demo03> aClass1 = Demo03.class;
        //new一个反射实例类
        Demo03 instance = aClass1.newInstance();
        //反射获取类方法
        Method setName = aClass1.getDeclaredMethod("setName", String.class);
        //激活类方法 第一个参数为反射实例类
        setName.invoke(instance,"小女孩");
        //反射获取类方法
        Method getName = aClass1.getDeclaredMethod("getName");
        //激活类方法 第一个参数为反射实例类
        System.out.println(getName.invoke(instance));;

        //通过反射操作属性
        Class<Demo04> aClass2 = Demo04.class;
        Demo04 demo04 = aClass2.newInstance();
        Field age = aClass2.getDeclaredField("age");
        demo04.setAge(19);
        //age.set(demo04,22);  会报错 原因私有属性不可以直接操作
        //但是反射机制很强,是可以直接操作的,需要关闭安全校验
        //关闭该属性的安全校验 默认为打开:false
        age.setAccessible(true);
        age.set(demo04,22);
        System.out.println(demo04.getAge());
    }
}
class Demo03 extends Demo04{
    public String name = "小米";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Demo03(String name) {
        this.name = name;
    }
    public Demo03() {

    }
}
class Demo04{
    private int age = 18;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Demo04(int age) {
        this.age = age;
    }
    public Demo04() {

    }
}

image-20240719134903669

反射性能问题

image-20240719141429845

public class Test007 {

    public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        User user = new User();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
             user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println("普通方式:"+(end-begin)+"ms");

        Class<User> aClass = User.class;
        User user1 = aClass.newInstance();
        long begin1 = System.currentTimeMillis();
        Method getName = aClass.getDeclaredMethod("getName");
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user1,null);
        }
        long end1 = System.currentTimeMillis();
        System.out.println("反射方式:"+(end1-begin1)+"ms");


        Class<User> aClass1 = User.class;
        User user2 = aClass1.newInstance();
        long begin3 = System.currentTimeMillis();
        Field name = aClass1.getDeclaredField("name");
        name.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            name.get(user2);
        }
        long end3 = System.currentTimeMillis();
        System.out.println("反射关闭安全校验方式:"+(end3-begin3)+"ms");
    }

}
class User{
    String name = "用户";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(String name) {
        this.name = name;
    }

    public User() {
    }
}

image-20240719141246865

2.5、反射操作泛型

image-20240719174325434

public class Test08 {
    public static void main(String[] args) throws NoSuchMethodException {
        //反射获取方法
        Method uu = UUser.class.getDeclaredMethod("uu", Map.class, User.class);
        //获取参数类型
        Type[] genericParameterTypes = uu.getGenericParameterTypes();
        for (Type type:genericParameterTypes){
            //判断参数类型是否存在泛型
            if (type instanceof  ParameterizedType){
                //将参数里的泛型拆分出来一个类型数组
                Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                //遍历数组就是泛型的真实类型
                for (Type parameterizedType:actualTypeArguments){
                    System.out.println(parameterizedType);
                }
            }
        }

        //反射获取方法
        Method uub = UUser.class.getDeclaredMethod("uub");
        //获取返回类型
        Type type1 = uub.getGenericReturnType();
        System.out.println(type1);
        //判断参数类型是否存在泛型
        if (type1 instanceof  ParameterizedType){
            //将参数里的泛型拆分出来一个类型数组
            Type[] actualTypeArguments = ((ParameterizedType) type1).getActualTypeArguments();
            //遍历数组就是泛型的真实类型
            for (Type parameterizedType:actualTypeArguments){
                System.out.println(parameterizedType);
            }
        }
    }
}
class UUser{

    void uu(Map<String,User> map,User user){
        System.out.println();
    }

    Map<String,User> uub(){
        return null;
    }

}

结果image-20240719175802087

2.6、反射获取注解信息

带着问题练习

image-20240719180312402

public class Test09 {
    public static void main(String[] args) throws NoSuchFieldException {
        //反射获取类上的所有注解
        Annotation[] annotations = Pojo.class.getAnnotations();
        for (Annotation annotation:annotations) {
            System.out.println(annotation);
        }
        //反射获取类上的指定注解
        Table table = Pojo.class.getAnnotation(Table.class);
        System.out.println(table.value());
        //反射获取类属性上的所有注解
        Field name = Pojo.class.getDeclaredField("name");
        Annotation[] annotations1 = name.getAnnotations();
        for (Annotation annotation1:annotations1){
            System.out.println(annotation1);
        }
        //反射获取类属性上的指定注解
        File file = name.getAnnotation(File.class);
        System.out.println(file.File_name()+";"+file.type()+";"+file.length());

    }
}
@Table("user")
class Pojo{
    @File(File_name = "id",type = "int",length = 30)
    int id;
    @File(File_name = "name",type = "String",length = 255)
    String name;
    @File(File_name = "age",type = "int",length = 30)
    int age;
}


//定义表注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    //表名
    String value();
}
//定义字段注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface File{
    String File_name();
    String type();
    int length();
}

结果

image-20240719183019318

三、类加载

3.1、类加载内存分析

image-20240718180437445

类加载过程

image-20240718180503125

类加载和类加载器(ClassLoader)

image-20240718180540093

public class TestClassLoader {
    public static void main(String[] args) {
        StudentBoy boy = new StudentBoy();
        System.out.println(StudentBoy.boy);
        School school = new School();
        System.out.println(School.school);

    }
}

class StudentBoy extends School{
    static String boy = "男学生";
    static {
        boy = "小男孩学生";
    }
    StudentBoy(){
        System.out.println(boy+":构造函数初始化");
    }
}

class School{
    static {
        school = "北京大学";
    }
    School(){
        System.out.println(school+":构造函数初始化");
    }
    static String school = "清华大学";

}public class TestClassLoader {
    public static void main(String[] args) {
        StudentBoy boy = new StudentBoy();
        System.out.println(StudentBoy.boy);
        School school = new School();
        System.out.println(School.school);

    }
}

class StudentBoy extends School{
    static String boy = "男学生";
    static {
        boy = "小男孩学生";
    }
    StudentBoy(){
        System.out.println(boy+":构造函数初始化");
    }
}

class School{
    static String school = "清华大学";
    static {
        school = "北京大学";
    }
    School(){
        System.out.println(school+":构造函数初始化");
    }
}

结果(静态代码对合并,有先后顺序)

image-20240718190353962

回顾知识点:子类默认会隐式调用父类的构造方法,也就是super();所有类加载是很多类就已经加载了,实例化的时候会复制一份格式给实例对象,这就对象是new出来的原理。

image-20240718190219392

image-20240718190142224

类的初始化

image-20240718191059786

public class TestClassLoader {
    static{
        System.out.println("main类初始化");
    }
    public static void main(String[] args) throws ClassNotFoundException {
//        System.out.println("new 会初始化父类子类");
//        StudentBoy boy = new StudentBoy();
        System.out.println("反射 会初始化父类子类 ");
        //这种反射不会初始化父类子类
        //Class boyClass = StudentBoy.class;
        Class.forName("com.yzy.reflection.StudentBoy");
        System.out.println("调用静态常量只会加载拥有静态常量对象");
        System.out.println(StudentBoy.boy);
        System.out.println("final 不会会初始化类");
        System.out.println(StudentBoy.boy1);
    }
}

class StudentBoy extends School{
    static String boy = "男学生";
    static final String boy1 = "男学生";
    static {
        System.out.println(boy+":子类初始化");
        boy = "小男孩学生";
    }
}
class School{
    static String school = "清华大学";
    static {
        System.out.println(school+":父类初始化");
    }
}

image-20240718192416178

类加载器的作用

image-20240719101652200

image-20240719101235544

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //用户类加载器
        ClassLoader loader = ClassLoader.getSystemClassLoader();
        System.out.println(loader);
        //引导类加载器
        ClassLoader parent = loader.getParent();
        System.out.println(parent);
        //根类加载器
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
        //例子
        //用户加载器
        System.out.println(Test01.class.getClassLoader());
        //根加载器 java包下的类是由根加载器加载的
        System.out.println(Object.class.getClassLoader());
        //加载器加载类路径
        System.out.println(System.getProperty("java.class.path"));
        /* 类加载的路径
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\charsets.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\access-bridge-64.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\cldrdata.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\dnsns.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\jaccess.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\jfxrt.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\localedata.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\nashorn.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\sunec.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\sunjce_provider.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\sunmscapi.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\sunpkcs11.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\ext\zipfs.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\jce.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\jfr.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\jfxswt.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\jsse.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\management-agent.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\resources.jar;
        C:\Users\yzy\.jdks\corretto-1.8.0_322-1\jre\lib\rt.jar;D:\java实训\day001\iotcp\out\production\iotcp;
        D:\idea\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar
         */
    }
}

image-20240719112218194

3.2、双亲委派机制

双亲委派机制(Parent-Delegate Model)是Java类加载器中采用的一种类加载策略。该机制的核心思想是:如果一个类加载器收到了类加载请求,默认先将该请求委托给其父类加载器处理。只有当父级加载器无法加载该类时,才会尝试自行加载。

类加载器与层级关系

Java中的类加载器主要有如下三种:

image-20240719112425478

  • 启动类加载器(Bootstrap ClassLoader): 负责加载 %JAVA_HOME%/jre/lib 目录下的核心Java类库如 rt.jar、charsets.jar 等。
  • 扩展类加载器(Extension ClassLoader): 负责加载 %JAVA_HOME%/jre/lib/ext 目录下的扩展类库。
  • 应用类加载器(Application ClassLoader): 负责加载用户类路径(ClassPath)下的应用程序类。

这三种类加载器之间存在父子层级关系。启动类加载器是最高级别的加载器,没有父加载器;扩展类加载器的父加载器是启动类加载器;应用类加载器的父加载器是扩展类加载器。

除了以上三个内置类加载器,用户还可以通过继承 java.lang.ClassLoader 类自定义类加载器,根据实际需求处理类加载请求。

双亲委派机制作用及如何破环机制

通过上述两块内容,我们对双亲委派机制、加载流程及层级有了一些了解,这时我们不妨抛出几个疑问。

为什么需要双亲委派
双亲委派机制有哪些优缺点
如何打破这个机制
有哪些工具选择了破坏机制。

  1. 为什么需要双亲委派

  2. 通过双亲委派机制,可以避免类的重复加载,当父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。

  3. 通过双亲委派机制,可以保证安全性。因为BootstrapClassLoader在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.String,那么这个类是不会被随意替换的。

那么,就可以避免有人自定义一个有破坏功能的java.lang.String被加载。这样可以有效的防止核心Java API被篡改。

实现双亲委派机制 的代码也都集中在这个方法之中:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

通过以上代码得出结论:

  1. 当类加载器接收到类加载的请求时,首先检查该类是否已经被当前类加载器加载;
  2. 若该类未被加载过,当前类加载器会将加载请求委托给父类加载器去完成;
  3. 若当前类加载器的父类加载器为null,会委托启动类加载器完成加载;
  4. 若父类加载器无法完成类的加载,当前类加载器才会去尝试加载该类。

双亲委派机制的优缺点

优点:

  • 避免重复加载:由于类加载器直接从父类加载器那里加载类,避免了类的重复加载。
  • 提高安全性:通过双亲委派模型,Java 标准库中的核心类库(如 java.lang.*)由启动类加载器加载,这样能保证这些核心类库不会被恶意代码篡改或替换,从而提高程序的安全性。
  • 保持类加载的一致性:这种方式确保了同一个类的加载由同一个类加载器完成,从而在运行时保证了类型的唯一性和相同性。这也有助于减轻类加载器在处理相互关联的类时的复杂性。

缺点:

  • 灵活性降低:由于类加载的过程需要不断地委托给父类加载器,这种机制可能导致实际应用中类加载的灵活性降低。
  • 增加了类加载时间:在类加载的过程中,需要不断地查询并委托父类加载器,这意味着类加载所需要的时间可能会增加。在类数量庞大或
  • 类加载器层次比较深的情况下,这种时间延迟可能会变得更加明显。

如何打破这个机制

想要破坏这种机制,那么就需要自定义一个类加载器,继承ClassLoader类重写其中的loadClass方法,使其不进行双亲委派即可。
写个示例

import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CustomClassLoader extends ClassLoader {
    

    // 自定义类加载器必须提供一个加载类文件的位置
    private String classesPath;
    
    public CustomClassLoader(String classesPath, ClassLoader parent) {
        super(parent);
        this.classesPath = classesPath;
    }
    
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        //首先,检查已加载的类
        Class<?> loadedClass = findLoadedClass(name);
    
        if (loadedClass == null) {
            // 如果已加载类中没有该类, 尝试用自定义的方法加载
            try {
                loadedClass = findClassInPath(name);
            } catch (ClassNotFoundException e) {
                // 如果自定义加载方法找不到类,则委托给父类加载器
                loadedClass = super.loadClass(name, resolve);
            }
        }
    
        if (resolve) {
            resolveClass(loadedClass);
        }
        
        return loadedClass;
    }
    
    private Class<?> findClassInPath(String className) throws ClassNotFoundException {
        try {
            String filePath = className.replace('.', '/') + ".class";
            byte[] classBytes = Files.readAllBytes(Paths.get(classesPath, filePath));
    
            return defineClass(className, classBytes, 0, classBytes.length);
        } catch (Exception e) {
            throw new ClassNotFoundException("Class not found in classes path: " + className, e);
        }
    }
    
    public static void main(String[] args) throws Exception {
        String pathToClasses = "/path/to/your/classes";
        String className = "com.example.SampleClass";
        String methodName = "sampleMethod";
    
        // 创建自定义类加载器实例,将类的加载权交给它
        CustomClassLoader customClassLoader = new CustomClassLoader(pathToClasses, CustomClassLoader.class.getClassLoader());
    
        // 使用自定义类加载器加载类
        Class<?> customClass = customClassLoader.loadClass(className);
    
        // 创建类的实例并调用方法
        Object obj = customClass.newInstance();
        Method method = customClass.getDeclaredMethod(methodName);
        method.setAccessible(true);
        method.invoke(obj);
    }

}

上面的示例代码中,我们重写了 loadClass 方法,先尝试通过 findClassInPath 从指定的路径加载类,如果无法加载就委托给父类加载器。这样,我们就实现了打破双亲委派机制的自定义类加载器。

以下是代码的详细解析:

  • 自定义类加载器 CustomClassLoader 继承 Java ClassLoader 类。

  • 在类加载器的构造方法中设置自定义类加载器的类路径 classesPath 和父加载器 parent

  • 重写 loadClass 方法。首先检查已加载的类,如果已加载则返回。否则尝试用自定义的方法在 classesPath 中加载类。如果自定义加载方法找不到类,则委托给父类加载器。

  • 实现名为 findClassInPath 的自定义加载方法。这个方法使用类名 classNameclassesPath 指定的目录下查找对应的 .class 文件,然后将文件内容读取为字节数组并调用 defineClass 方法,将其转换为 Java 类的 Class 对象。如果类不存在或出现其他错误,会抛出 ClassNotFoundException 异常。

  • 在 main 方法中,创建一个 CustomClassLoader 类的实例。将类的加载任务交给自定义类加载器,指定加载路径和要加载的类。

使用自定义类加载器加载目标类,创建类的实例,并调用指定方法。

有哪些工具选择了破坏机制

  • OSGi(Open Service Gateway Initiative):OSGi 是一个模块化系统和服务平台,提供了一个强大的类加载器模型。在 OSGi 中,每个模块都有一个独立的类加载器,可以按需加载来自不同模块的类。这有助于解决 JAR 地狱问题,提高模块化和动态更新能力。
  • Tomcat Web容器:Tomcat 的 Web 应用类加载器可以加载 Web 应用程序中的本地类库,从而使得每个 Web 应用程序可以使用各自的版本的类库。这些 Web 应用的类加载器都是${tomcat-home}/lib 中类库的子类加载器。
  • Java Agent: Java Agent 是一种基于 Java Instrumentation API 的技术,它可以在运行时修改已加载的类的字节码,从而实现类的热替换、AOP(面向切面编程)等功能。这种技术在诸如热部署、性能监控和分布式追踪等场景中有广泛应用。
  • **JDK 中的 URLClassLoader:**JDK 自带的 URLClassLoader 可以用来加载指定 URL 路径下的类。实际上,它实现了一种子类优先的策略,先尝试加载自身路径下的类,再委托给父类加载器,从而打破了双亲委派机制。

这些工具和技术之所以要打破双亲委派机制,主要是出于以下原因:

  1. 实现模块化和动态更新:例如 OSGi,通过独立的类加载器实现不同模块间解耦,并支持模块的动态卸载和更新。
  2. 解决类库版本冲突(JAR地狱问题):在复杂系统中,不同模块可能依赖不同版本的类库。为避免版本冲突,可使用独立的类加载器,使它们分别加载各自的类库版本。
  3. 运行时修改类:Java Agent 可以在运行时修改类字节码,从而支持热替换、AOP 和性能监控等功能。
  4. 支持 Web 应用程序的独立部署和更新:例如 Tomcat,可以为每个 Web 应用程序分配一个独立的类加载器,实现各自部署与更新。

需要注意的是,打破双亲委派机制可能会带来类加载冲突、安全性和性能等问题,因此在实际应用中要谨慎使用。

总结

双亲委派机制可以确保Java应用类型安全,同时避免类加载冲突。在某些特定场景下,我们可以通过自定义类加载器对类加载策略进行调整,以满足应用特性和性能需求。

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值