System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
.getName());
Java虛擬機中的所有類裝載器採用具有父子關係的樹形結構進行組織,在實例化每個類裝載器對象時,需要爲其指定一個父級類裝載器對象或者默認採用系統類裝載器爲其父級類加載。
首先當前線程的類加載器去加載線程中的第一個類。如果類A中引用了類B,Java虛擬機將使用加載類A的類裝載器來加載類B。(類A繼承了類B,那麼他們的加載器是同一個加載器)還可以直接調用ClassLoader.loadClass()方法來指定某個類加載器去加載某個類。
可以寫,但是這個類永遠不會被加載,因爲在加載器BootStrap中存在此類,所以加載器優先加載BootStrap負責範圍中的System的類
類加載器中的loadClass方法內部實現了父類委託機制,因此我們沒有必要自己覆蓋loadClass,否則需要自己去實 現父類委託機制。我們只需要覆蓋findClass方法。loadClass方法中調用了findClass方法,使用的是模板設計模 式。我們得到了Class文件後,就可以通過defineClass方法將二進制數據轉換成字節碼。這就是自定義類加載器的 編寫原理。
編寫一個加密文件的程序
package cn.itheima.ClassLoader;
public class ClassLoaderAttachment
{
public String toString()
{
return "Hello,Class loader!";
}
}
加密程序:其實就是講一個文件寫入到指定目錄,在寫入的時候加入一些規則即可。
package cn.itheima.ClassLoader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader
{
/**
* @param args
* 文件加密其實就是在寫入文件的時候加上特意的規則,本程序是使用反碼來進行加密的
*/
public static void main(String[] args) throws Exception
{
//將class文件目錄和寫入目錄指定給主函數,分別用args[0]和args[1]存儲
String srcPath = args[0];
String destDir = args[1];
//讀取指定目錄中的文件
InputStream ips = new FileInputStream(srcPath);
//獲取指定目錄文件的文件名
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
//定義存放加密後的文件目錄
String destPath = destDir + "\\"+ destFileName;
//寫入目標目錄
OutputStream ops = new FileOutputStream(destPath);
encrypt(ips,ops);
ips.close();
ops.close();
}
//加密其實就是將類文件讀取後,在寫入的時候進行非正常的寫入
public static void encrypt(InputStream ips,OutputStream ops) throws Exception
{
//單個字符字節寫入,將每個字節進行加密
int a = 0;
while((a = ips.read())!= -1)
{
ops.write(a^0xff);//加密的一種方式,反碼加密
}
}
}
注意:
要加密某個文件,首先要讀取文件,在讀取文件的時候必須要傳入文件的目錄路徑,此程序中時直接用的主函數傳入的參數進行讀取的,具體的步驟如下:右鍵-->RunAs-->Run Configurations-->Arguments-->在Program Arguments中輸入要讀取的文件路徑,和寫入的文件目錄。注意:這兩個參數間用空格分開,絕對不可以使用","。當然你也可以直接輸入他們各自的目標路徑,個人覺得這樣更省事。加密後的類文件,如果按照正常的類進行加載的話,是不可能正常加載成功的,這時候我們就需要寫一個解密的類加載程序。
1.加載器繼承ClassLoader2.複寫findClass()方法3.使用defineClass()方法
package cn.itheima.ClassLoader;
import java.util.Date;
public class ClassLoaderAttachment extends Date
{
public String toString()
{
return "Hello,Class Loader!";
}
}
加密解密程序package cn.itheima.ClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader
{
/**
* @param args
* 文件加密其實就是在寫入文件的時候加上特意的規則,本程序是使用反碼來進行加密的
*/
private String srcPath;
public MyClassLoader(){}
public MyClassLoader(String srcPath)
{
this.srcPath = srcPath;
}
@SuppressWarnings("deprecation")
@Override
//複寫findClass方法,自定義加載器
protected Class<?> findClass(String name) throws ClassNotFoundException
{
//讀取加密後的文件,進行解密,得到原來的類文件
String classFileName = srcPath + "\\"+name.substring(srcPath.lastIndexOf("\\")+1)+".class";
try
{
InputStream ips = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
encrypt(ips,bos);//解密文件
ips.close();
byte[] bytes = bos.toByteArray();
return defineClass(bytes,0,bytes.length);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.findClass(name);
}
public static void main(String[] args) throws Exception
{
//將class文件目錄和寫入目錄指定給主函數,分別用args[0]和args[1]存儲
String srcPath = args[0];
String destDir = args[1];
//讀取指定目錄中的文件
InputStream ips = new FileInputStream(srcPath);
//獲取指定目錄文件的文件名
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
//定義存放加密後的文件目錄
String destPath = destDir + "\\"+ destFileName;
//寫入目標目錄
OutputStream ops = new FileOutputStream(destPath);
encrypt(ips,ops);
ips.close();
ops.close();
}
//加密其實就是將類文件讀取後,在寫入的時候進行非正常的寫入
public static void encrypt(InputStream ips,OutputStream ops) throws Exception
{
//單個字符字節寫入,將每個字節進行加密
int a = 0;
while((a = ips.read())!= -1)
{
ops.write(a^0xff);//加密的一種方式,反碼加密
}
}
}
使用自定義加載器
package cn.itheima.ClassLoader;
import java.util.Date;
public class ClassLoaderTest2
{
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
// 加載文件獲得對應的class文件
Class clazz = new MyClassLoader("DestFolder").loadClass("ClassLoaderAttachment");
//創建類文件對象
Date d = (Date)clazz.newInstance();
//調用該對象的toString()方法
System. out.println(d.toString());
}
}
注意:
1、之所以讓ClassLoaderAttachment類繼承Date是因爲,如果直接寫ClassLoaderAttachment d = (ClassLoaderAttachment)clazz.newInstance();這條語句,編譯的時候就會報錯,因爲此時的ClassLoaderAttachment在AppClassLoader加載進內存後就無法識別。所以需要通過藉助一個父類對象繞過編譯器。也就是:Date d1 = (Date)clazz.newInstance();。2、如果想讓父類加載器AppClassLoader加載ClassLoaderAttachment類,則需要執行下面的語句:Class clazz = new MyClassLoader("ClassLoaderLib").loadClass("com.itheima.day2.ClassLoaderAttachment");但是父類加載可能會出錯。3、在創建ClassLoaderAttachment類時,在父類選項中,要選擇 Date類,否則之後再繼承Date類會報錯。4、在測試類加載器是否是自定義類加載器時,一定要先把ClassPath路徑下的ClassLoaderAttachment.class文件刪除,否則類加載器會優先使用其父類加載器加載(類加載器的委託機制),而不是自定義的加載器。
類加載器的一個高級問題的實驗分析(我覺得這個問題是涉及到Web方面的類加載的問題處理和操作過程)
編寫一個能打印出自己的類加載器名稱和當前類加載器的父子結構關係鏈的MyServlet,正常發佈後,看到打印結果爲WebAppClassloader。把MyServlet.class文件打jar包,放到ext目錄中,重啓tomcat,發現找不到HttpServlet的錯誤。把servlet.jar也放到ext目錄中,問題解決了,打印的結果是ExtclassLoader 。
package cn.itheima.itheimaweb.web.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet
{
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
ClassLoader loader =this.getClass().getClassLoader();
while(loader!=null)
{
out.println(loader.getClass().getName()+"<br/>");
loader = loader.getParent();
}
out.close();
}
}
org.apache.catalina.loader.StandardClassLoader
org.apache.catalina.loader.StandardClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
可以看到MyServlet是由WebappClassLoader類加載器加載的