多說兩句關於ClassLoader的面試題

Java的高級知識中ClassLoader是很重要的一環。面試中有很多關於ClassLoader的問題,今天分析一道例子。

問題

場景:假設有三個類 A B C,B和C跟A類不在一個路徑下。A使用B類,但沒辦法通過直接引用的方法使用它。B類對C有引用。 問題1:A如何訪問B類? 問題2:B和C類的ClassLoader是誰?

我們分兩篇說明這兩個問題。今天先分析問題1。

爲什麼A不能直接引用B類

類的加載是通過ClassLoader去做的,當A類要使用B類的時候,A的ClassLoader首先會從最根的ClassLoader去尋找類B,然後依次往下找,最終如果A的ClassLoader也找不到的話,會報ClassNotFoundException。這就是雙親委派的簡單原理。

A類的ClassLoader找不到B類的原因是,A的ClassLoader只會去找同個路徑下的class文件,而B不在這個路徑下。

接下來回答怎麼加載B類的問題

自定義ClassLoader

類的加載是通過ClassLoader去加載class文件。對於ClassLoader,其實我們可以隨心所欲的自定義它,只要重載findClass()方法就可以。

這裏實現了一個可以根據路徑加載class的ClassLoader,

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * Created by phoenix on 2018/3/12.
 */

public class DiskClassLoader extends ClassLoader {

    private String mFilePath;

    public DiskClassLoader(String mFilePath) {
        this.mFilePath = mFilePath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String fileName = getFileName(name);
        File file = new File(mFilePath, fileName);
        try {
            FileInputStream ins = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            try {
                while((len = ins.read()) != -1) {
                    bos.write(len);
                }
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
            byte[] data = bos.toByteArray();
            ins.close();
            bos.close();
            return defineClass(name, data, 0, data.length);
        } catch (IOException exp) {
            exp.printStackTrace();
        }
        return super.findClass(name);
    }

    private String getFileName(String name){
        int index = name.lastIndexOf('.');
        if(index == -1) {
            return name + ".class";
        } else {
            return name.substring(index) + ".class";
        }
    }
}

它的構造方法接受一個String作爲路徑,重載的findClass方法可以加載指定的class文件。 其實邏輯很簡單,不過二十多行代碼,這裏就不解釋了。 下面看如何使用它。

A的代碼像下面這樣,

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class A {

  private static void println(Object msg) {
    System.out.println("A " + msg);
  }

  public static void main(String[] args) {
    // System.out.println("Ming looking for socker");
    println(A.class.getClassLoader());
    checkClassCast();
  }

  private static void checkClassCast() {
    try {
      DiskClassLoader loader1 = new DiskClassLoader("../BDirectory");

      Class class1 = loader1.findClass("B");

      Object classB = class1.newInstance();

      Method method = class1.getDeclaredMethod("loaderTest", (Class<?>[]) null);//<--B類的方法
      method.invoke(classB, (Object[])null);
    } catch(Exception e){
      e.printStackTrace();
    }
  }
}

代碼註釋中 loaderTest()是B類裏的方法,通過自定義ClassLoader,我們也能調用上在另外路徑下的類B了。

下次我們會再聊聊如何回答第二個問題, B和C的ClassLoader是誰?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章