没有双亲委派机制情况

如果不使用双亲委派机制会加载多次吗?

是的,如果不使用双亲委派机制,同一个类很可能会被多次加载,这会导致严重的问题。

1. 没有双亲委派机制的情况

直接加载类的问题

如果每个类加载器都直接加载自己需要的类,而不进行委托:

// 假设这是一个没有双亲委派机制的类加载器
public class SimpleClassLoader extends ClassLoader {
    private String classPath;
    
    public SimpleClassLoader(String classPath) {
        this.classPath = classPath;
    }
    
    @Override
    protected Class<?> loadClass(String name, boolean resolve) 
            throws ClassNotFoundException {
        // 不进行双亲委派,直接自己加载
        Class<?> c = findClass(name); // 直接加载,不检查是否已加载
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }
    
    private byte[] loadClassData(String className) {
        // 加载类数据的实现...
        return null;
    }
}

多次加载的后果

public class NoDelegationExample {
    public static void main(String[] args) throws Exception {
        // 使用没有双亲委派机制的类加载器
        SimpleClassLoader loader1 = new SimpleClassLoader("path1");
        SimpleClassLoader loader2 = new SimpleClassLoader("path2");
        
        // 同一个类被不同加载器多次加载
        Class<?> class1 = loader1.loadClass("com.example.MyClass");
        Class<?> class2 = loader2.loadClass("com.example.MyClass");
        
        // 结果是不同的类
        System.out.println("class1 == class2: " + (class1 == class2)); // false
        System.out.println("class1.equals(class2): " + class1.equals(class2)); // false
        
        // 会导致类型转换异常
        Object obj1 = class1.newInstance();
        // 下面这行会抛出ClassCastException
        // class2.cast(obj1); // 异常:不能转换
    }
}

2. 实际问题演示

类型不兼容问题

// 模拟没有双亲委派机制的情况
public class TypeCompatibilityIssue {
    public static void main(String[] args) throws Exception {
        // 两个不同的类加载器加载同一个类
        CustomClassLoader loader1 = new CustomClassLoader("classes");
        CustomClassLoader loader2 = new CustomClassLoader("classes");
        
        // 加载同一个类
        Class<?> string1 = loader1.loadClass("java.lang.String");
        Class<?> string2 = loader2.loadClass("java.lang.String");
        
        // 创建实例
        String str1 = (String) string1.getConstructor(String.class)
                                     .newInstance("Hello");
        Object str2 = string2.getConstructor(String.class)
                            .newInstance("World");
        
        // 问题出现:即使都是String类,但由于不同加载器加载,无法直接赋值
        // List<string1> 和 List<string2> 是不同的类型
        try {
            // 这会抛出异常,因为类型不匹配
            boolean result = str1.equals(str2);
        } catch (Exception e) {
            System.out.println("类型不兼容: " + e.getMessage());
        }
    }
}

内存浪费问题

public class MemoryWasteExample {
    public static void demonstrateWaste() throws Exception {
        List<Class<?>> loadedClasses = new ArrayList<>();
        
        // 模拟多次加载同一个类
        for (int i = 0; i < 100; i++) {
            CustomClassLoader loader = new CustomClassLoader("classes");
            Class<?> clazz = loader.loadClass("com.example.HeavyClass");
            loadedClasses.add(clazz);
        }
        
        // 每个类加载器都创建了独立的类元数据
        // 造成内存中存在100个相同的类定义
        System.out.println("Loaded " + loadedClasses.size() + " class instances");
        
        // 验证它们不是同一个类
        for (int i = 0; i < loadedClasses.size() - 1; i++) {
            System.out.println("Class " + i + " == Class " + (i+1) + ": " + 
                             (loadedClasses.get(i) == loadedClasses.get(i+1)));
        }
    }
}

3. 安全性问题

核心类库被篡改

// 如果没有双亲委派,用户可以加载恶意的系统类
public class SecurityRiskExample {
    public static void main(String[] args) throws Exception {
        // 假设用户在自己的classpath下放置了一个恶意的String类
        MaliciousClassLoader loader = new MaliciousClassLoader("user/classes");
        
        // 没有双亲委派的情况下,可能会加载用户自定义的String类
        Class<?> fakeString = loader.loadClass("java.lang.String");
        
        // 这个"String"类可能包含恶意代码
        Object fakeStringInstance = fakeString.newInstance();
        
        // 当程序使用这个"String"时,可能会执行恶意代码
        System.out.println("危险:加载了可能被篡改的核心类");
    }
}

4. 正常双亲委派机制的对比

正确的类加载行为

public class ProperDelegationExample {
    public static void main(String[] args) throws Exception {
        // 使用标准的双亲委派机制
        ClassLoader loader1 = new CustomClassLoader("classes");
        ClassLoader loader2 = new CustomClassLoader("classes");
        
        // 都会委托给Bootstrap ClassLoader加载核心类
        Class<?> string1 = loader1.loadClass("java.lang.String");
        Class<?> string2 = loader2.loadClass("java.lang.String");
        
        // 结果是同一个类
        System.out.println("string1 == string2: " + (string1 == string2)); // true
        
        // 类型兼容,不会出现转换异常
        String str1 = (String) string1.getConstructor(String.class)
                                     .newInstance("Hello");
        String str2 = (String) string2.getConstructor(String.class)
                                     .newInstance("World");
        
        // 可以正常比较和操作
        boolean result = str1.equals(str2); // 正常工作
        System.out.println("比较结果: " + result);
    }
}

5. 实际场景中的问题

Web应用服务器中的类加载

// 在应用服务器中,如果没有正确的类加载机制
public class WebAppProblem {
    public static void demonstrateIssue() throws Exception {
        // 每个Web应用有自己的类加载器
        WebAppClassLoader app1Loader = new WebAppClassLoader("app1/WEB-INF/classes");
        WebAppClassLoader app2Loader = new WebAppClassLoader("app2/WEB-INF/classes");
        
        // 如果不使用双亲委派
        Class<?> servlet1 = app1Loader.loadClass("javax.servlet.http.HttpServlet");
        Class<?> servlet2 = app2Loader.loadClass("javax.servlet.http.HttpServlet");
        
        // 问题:
        // 1. 浪费内存:两个相同的类被加载两次
        // 2. 类型不兼容:app1的Servlet和app2的Servlet被视为不同类型
        // 3. 安全风险:可能加载了被篡改的Servlet API
        
        System.out.println("Servlet类相同: " + (servlet1 == servlet2)); // false (问题!)
    }
}

总结

如果不使用双亲委派机制:

  1. 重复加载:同一个类可能被多个类加载器重复加载
  2. 类型不兼容:即使是相同的类,不同加载器加载的实例也无法互相转换
  3. 内存浪费:JVM中存在多个相同的类定义,浪费内存
  4. 安全风险:核心类库可能被恶意替换
  5. 程序异常:出现ClassCastException等类型转换异常

双亲委派机制正是为了解决这些问题而设计的,它确保了类的唯一性和安全性,是Java平台稳定运行的重要保障。

posted @ 2025-08-27 21:36  一刹流云散  阅读(8)  评论(0)    收藏  举报