我的消息吃了我的服務(wù)器!Kyle指出,通常,Web服務(wù)開發(fā)者開始經(jīng)歷“內(nèi)存溢出”的錯(cuò)誤或者奇怪的“性能問題”時(shí),總是會(huì)發(fā)現(xiàn)服
務(wù)器擁有極高的處理負(fù)載,CPU使用率接近100%,以及較低的吞吐量和高網(wǎng)絡(luò)延遲。導(dǎo)致這些癥狀的典型原因是非常大的(有時(shí)會(huì)達(dá)到50
MB或者更大)消息。而且,這些大消息往往包含了非常大的、作為XML消息主體的、采用base-64編碼的二進(jìn)制編碼信息。導(dǎo)致其發(fā)生的原因通常是:
……開發(fā)者不理解技術(shù)的局限性:XML處理對(duì)解決許多問題都有用,但是你必須認(rèn)識(shí)到消息是要被解析的——并且在大多數(shù)……產(chǎn)品中,這就意味著許多或者所有的消息都會(huì)駐留在內(nèi)存中。
Kyle建議采用如下方法來改善這種情況:
- 不要發(fā)送冗余信息。在許多情況下,發(fā)送二進(jìn)制數(shù)據(jù)時(shí),你可能會(huì)發(fā)現(xiàn)消息高度重復(fù)。如果是這樣,你可能就要考慮在HTTP層面使用壓縮技術(shù)來改善你的網(wǎng)絡(luò)延遲。雖然這不會(huì)幫助你處理負(fù)載,但可能有助于減輕其中一個(gè)問題。
- 在XML消息體中,根本不要嵌入二進(jìn)制信息。這是較好的解決方法,還有幾種不同的途徑可以實(shí)現(xiàn)這一效果。比如,你可以使用帶有附件的SOAP或者消息傳輸優(yōu)化機(jī)制(MTOM)繞過解析開銷,盡管這無助于網(wǎng)絡(luò)延遲問題。
- ……還有一個(gè)更好的辦法,使用SOAP根本不發(fā)送大的二進(jìn)制blob。替代方法,通過受控的文件傳輸系統(tǒng),使用一個(gè)“帶外數(shù)據(jù)”傳輸……或者“聲明標(biāo)簽(claim Check,參見《EIP模式》或這里)”模式,避免在SOAP和HTTP上發(fā)送大的二進(jìn)制文件。
任何一種技術(shù)都有它使用的環(huán)境,在做架構(gòu)設(shè)計(jì)的時(shí)候一定要避免因?yàn)閭€(gè)人的偏好,無意識(shí)的舍棄某些選擇。 一種簡(jiǎn)單的方法論是,根據(jù)需要達(dá)到的目的,列出所有可能的實(shí)現(xiàn)方案,最后做出決定。
不好意思,你的數(shù)據(jù)正在顯示。根據(jù)Kyle所說,另一個(gè)典型的Web服務(wù)的“性能問題” 是,使用Web服務(wù)的層面非常、非常低——通常Web服務(wù)跟一個(gè)SQL語句相關(guān),這是因?yàn)椋?/p>
誤解了SOA架構(gòu)原則。一個(gè)優(yōu)秀SOA架構(gòu)的關(guān)鍵原則是你的服務(wù)應(yīng)該具有高復(fù)用性。
根據(jù)Kyle所說,這些情況通常發(fā)生在:
……如果設(shè)計(jì)是根據(jù)現(xiàn)有代碼“自上而下”衍生出服務(wù),這類服務(wù)就會(huì)出現(xiàn);通常,開發(fā)者會(huì)看著他們現(xiàn)有的架構(gòu)圖并且決定將架構(gòu)中的每一層(包括表現(xiàn)層)轉(zhuǎn)變成服務(wù)集。
相反,在SOA架構(gòu)的正確位置使用粗粒度的Web服務(wù)會(huì)更好。再次強(qiáng)調(diào),檢查一個(gè)架構(gòu)的標(biāo)準(zhǔn)分層模型,通常在架構(gòu)中會(huì)有一個(gè)明確定義的地方已經(jīng)封裝
了系統(tǒng)業(yè)務(wù)邏輯。可以使用“遠(yuǎn)程門面模式(Remote Facade Pattern)”來包裝這些服務(wù),以便用合適的方式來暴露基于模型的服務(wù)。
同樣是可以利用方法論來避免問題,但對(duì)于粒度的把握就是一個(gè)經(jīng)驗(yàn)的問題。
模式(Schema)?我們不需要任何發(fā)臭的模式!
Kyle指出,通常開發(fā)者試圖重用現(xiàn)有代碼來生成和解析作為Web服務(wù)實(shí)現(xiàn)基礎(chǔ)的XML。這些實(shí)現(xiàn)通常使用XML解析器來編組/解組消息,同時(shí)使用
Java
HTTP類來發(fā)送和接收XML文檔。使用Web服務(wù)時(shí),通用的方法是,創(chuàng)建使用模式元素的WSDL文檔,使XML不受阻地通過,然后在現(xiàn)有代碼中對(duì)它們進(jìn)
行解析。
這個(gè)問題的癥狀是組織沒有看到SOA承諾的好處,而且維護(hù)他們的解決方案似乎比以前使用Web服務(wù)的時(shí)候更難(而不是更容易)
簡(jiǎn)單的解決方案是,每當(dāng)寫Web服務(wù)時(shí),不管使用WS-*標(biāo)準(zhǔn)還是使用REST方法,都要確保你創(chuàng)建了代表你文檔結(jié)構(gòu)的完整準(zhǔn)確的XML模式。
如果你正在構(gòu)建WS-* Web服務(wù),那么這個(gè)XML應(yīng)該被包含在描述你的Web服務(wù)的WSDL之中。即使你在使用REST方法,擁有易于訪問的XML模式將鼓勵(lì)你的服務(wù)被重用。
如何看懂Java混淆后的反編譯代碼
作者:dozb
一般情況下Java應(yīng)用的開發(fā)者為了保護(hù)代碼不被別人抄襲,在生成class文件的時(shí)候都java文件進(jìn)行了混淆,這種class文件用反編譯工具得到的結(jié)果很難看懂,并且不能進(jìn)行編譯。本文從研究的角度,淺析如何讀懂這種反編譯過來的文件。
例子一:賦值
反編譯過來的代碼如下:
Node node;
Node node1 = _$3.getChildNodes().item(0);
node1;
node1;
JVM INSTR swap ;
node;
getChildNodes();
0;
item();
getChildNodes();
0;
item();
getNodeValue();
String s;
s;
原始語句:
Node node;
Node node1 = currDocument.getChildNodes().item(0);
node = node1;
String s = node.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
注解:
JVM INSTR swap ; //賦值語句
練習(xí):
String s1;
String s8 = node.getChildNodes().item(1).getChildNodes().item(0).getNodeValue();
s8;
s8;
JVM INSTR swap ;
s1;
10;
Integer.parseInt();
int i;
i;
例子二:不帶參數(shù)創(chuàng)建對(duì)象
反編譯過來的代碼如下:
JVM INSTR new #244 <Class CrossTable>;
JVM INSTR dup ;
JVM INSTR swap ;
CrossTable();
CrossTable crosstable;
crosstable;
原始語句:
CrossTable crosstable = new CrossTable();
注解:
練習(xí):
JVM INSTR new #246 <Class Database>;
JVM INSTR dup ;
JVM INSTR swap ;
Database();
Object obj;
obj;
例子三:帶參數(shù)創(chuàng)建對(duì)象
反編譯過來的代碼如下:
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s2);
StringBuffer();
s.substring(j, i);
append();
s6;
append();
toString();
s2;
原始語句:
s2 = (new StringBuffer(String.valueOf(s2))).append(s.substring(j, i)).append(s6).toString();
注解:
此語句實(shí)際上是:s2 += s.substring(j, i) + s6;
練習(xí):
例子四:for循環(huán)
反編譯過來的代碼如下:
int k = 0;
goto _L4
_L8:
...
k++;
_L4:
if(k < as.length) goto _L8; else goto _L7
原始語句:
for(int k=0;k < as.length;k++)
{
...
}
注解:
例子五:while循環(huán)
反編譯過來的代碼如下:
String s1 = "";
goto _L1
_L3:
JVM INSTR new #262 <Class StringBuffer>;
JVM INSTR dup ;
JVM INSTR swap ;
String.valueOf(s1);
StringBuffer();
_$2(resultset, s, l);
append();
toString();
s1;
_L1:
if(resultset.next()) goto _L3; else goto _L2
原始語句:
String s1 = "";
while(resultset.next())
{
s1 = s1 + resultSetToString(resultset, s, l);
}
[說明]幾個(gè)關(guān)鍵字將不翻譯
-
ClassLoader
-
System
-
Context
-
Thread
走出ClassLoader的迷宮
System、Current和Context ClassLoader?分別在何種情形下使用?
1、問題:在何種情形下使用thread.getcontextclassloader()?
盡管沒經(jīng)常遇到這個(gè)問題,但是想獲得準(zhǔn)確的答案并不那么容易,特別是在開發(fā)應(yīng)用框架的時(shí)候,你需要?jiǎng)討B(tài)的加載一些類和資源,不可避免的你會(huì)被此困擾。一般來說,動(dòng)態(tài)載入資源有三種ClassLoader可以選擇,System ClassLoader(也叫App ClassLoader)、當(dāng)前類的ClassLoader和CurrentThread的Context ClassLoader。那么, 如何選擇使用?
首先可以簡(jiǎn)單排除的是System ClassLoader,這個(gè)ClassLoader負(fù)責(zé)從參數(shù)-classpath、-cp、和操作系統(tǒng)CLASSPATH中載入資源。并且,任何ClassLoader的getSystemXXX()方法都是有以上幾個(gè)路徑指定的。我們應(yīng)該很少需要編寫直接使用ClassLoader的程序,否則你的代碼將只能在命令行運(yùn)行,發(fā)布你的代碼成為ejb、web應(yīng)用或者java web start應(yīng)用,我肯定他們會(huì)崩潰!
接下來,我們只剩下兩個(gè)選擇了:當(dāng)前ClassLoader和Thread Context ClassLoader
Current ClassLoader:當(dāng)前類所屬的ClassLoader,在虛擬機(jī)中類之間引用,默認(rèn)就是使用這個(gè)ClassLoader。另外,當(dāng)你使用Class.forName(), Class.getResource()這幾個(gè)不帶ClassLoader參數(shù)的方法是,默認(rèn)同樣適用當(dāng)前類的ClassLoader。你可以通過方法XX.class.GetClassLoader()獲取。
Thread Context ClassLoader,沒一個(gè)Thread有一個(gè)相關(guān)聯(lián)系的Context ClassLoader(由native方法建立的除外),可以通過Thread.setContextClassLoader()方法設(shè)置。如果你沒有主動(dòng)設(shè)置,Thread默認(rèn)集成Parent Thread的 Context ClassLoader(注意,是parent Thread 不是父類)。如果 你整個(gè)應(yīng)用中都沒有對(duì)此作任何處理,那么 所有的Thread都會(huì)以System ClassLoader作為Context ClassLoader。知道這一點(diǎn)很重要,因?yàn)閺膚eb服務(wù)器,java企業(yè)服務(wù)器使用一些復(fù)雜而且精巧的ClassLoader結(jié)構(gòu)去實(shí)現(xiàn)諸如JNDI、線程池和熱部署等功能以來,這種簡(jiǎn)單的情況越發(fā)的少見了。
這篇文章中為什么把Thread Context ClassLoader放在首要的位置,別人并沒有大張旗鼓的介紹它?很多開發(fā)者都對(duì)此不甚了解,因?yàn)閟un沒有提供很好的說明文檔。
事實(shí)上,Context ClassLoader提供一個(gè)突破委托代理機(jī)制的后門。虛擬機(jī)通過父子層次關(guān)系組織管理ClassLoader,沒有個(gè)ClassLoader都有一個(gè)Parent ClassLoader(BootStartp不在此范圍之內(nèi)),當(dāng)要求一個(gè)ClassLoader裝載一個(gè)類是,他首先請(qǐng)求Parent ClassLoader去裝載,只有parent ClassLoader裝載失敗,才會(huì)嘗試自己裝載。
但是,某些時(shí)候這種順序機(jī)制會(huì)造成困擾,特別是jvm需要?jiǎng)討B(tài)載入有開發(fā)者提供的資源時(shí)。就以JNDI為例,JNDI的類是由bootstarp ClassLoader從rt.jar中間載入的,但是JNDI具體的核心驅(qū)動(dòng)是由正式的實(shí)現(xiàn)提供的,并且通常會(huì)處于-cp參數(shù)之下(注:也就是默認(rèn)的System ClassLoader管理),這就要求bootstartp ClassLoader去載入只有SystemClassLoader可見的類,正常的邏輯就沒辦法處理。怎么辦呢?parent可以通過獲得當(dāng)前調(diào)用Thread的方法獲得調(diào)用線程的Context ClassLoder 來載入類。
順帶補(bǔ)充一句,JAXP從1.4之后也換成了類似JNDI的ClassLoader實(shí)現(xiàn),嘿嘿,剛剛我說什么來著,SUN文檔缺乏 ^_^
介紹完這些之后,我們走到的十字路口,任一選擇都不是萬能的。一些人認(rèn)為Context ClassLoader將會(huì)是新的標(biāo)準(zhǔn)。但是 一旦你的多線程需要通訊某些共享數(shù)據(jù),你會(huì)發(fā)現(xiàn),你將有一張極其丑陋的ClassLoader分布圖,除非所有的線程使用一樣的Context ClassLoader。并且委派使用當(dāng)前ClassLoder對(duì)一些方法來說是默認(rèn)繼承來的,比如說Class.forName()。盡管你明確的在任何你能控制的地方使用Context ClassLoader,但是畢竟還有很多代碼不歸你管(備注:想起一個(gè)關(guān)于UNIX名字來源的笑話)。
某些應(yīng)用服務(wù)器使用不同的ClassLoder作為Context ClassLoader和當(dāng)前ClassLoader,并且這些ClassLoader有著相同的ClassPath,但沒有父子關(guān)系,這使得情況更復(fù)雜。請(qǐng)列位看官,花幾秒鐘時(shí)間想一想,為什么這樣不好?被載入的類在虛擬機(jī)內(nèi)部有一個(gè)全名稱,不同的ClassLoader載入的相同名稱的類是不一樣的,這就隱藏了類型轉(zhuǎn)換錯(cuò)誤的隱患。(注:奶奶的 俺就遇到過,JBOSSClassLoader機(jī)制蠻挫的)
這種混亂事實(shí)上在java類中也有,試著去猜測(cè)任何一個(gè)包含動(dòng)態(tài)加載的java規(guī)范的ClassLoader機(jī)制,以下是一個(gè)清單:
- JNDI uses context classloaders
- Class.getResource() and
Class.forName()
use the current classloader
- JAXP uses context classloaders (as of J2SE 1.4)
- java.util.ResourceBundle uses the caller's current classloader
- URL protocol handlers specified via
java.protocol.handler.pkgs
system property are looked up in the bootstrap and system classloaders only
- Java Serialization API uses the caller's current classloader by default
而且關(guān)于這些資源的類加載機(jī)制文檔時(shí)很少。
java開發(fā)人員應(yīng)該怎么做?
如果你的實(shí)現(xiàn)是利用特定的框架,那么恭喜你,實(shí)現(xiàn)它遠(yuǎn)比實(shí)現(xiàn)框架要簡(jiǎn)單得多!例如,在web應(yīng)用和EJB應(yīng)用中,你僅僅只要使用 Class.getResource()就足夠了。
其他的情形下,俺有個(gè)建議(這個(gè)原則是俺工作中發(fā)現(xiàn)的,侵權(quán)必究,抵制盜版。),
下面這個(gè)類可以在整個(gè)應(yīng)用中的任何地方使用,作為一個(gè)全局的ClassLoader(所有的示例代碼可以從download下載):
1 public abstract class ClassLoaderResolver {
2 /**
3 * This method selects the best classloader instance to be used for
4 * class/resource loading by whoever calls this method. The decision
5 * typically involves choosing between the caller's current, thread context,
6 * system, and other classloaders in the JVM and is made by the
7 * {@link IClassLoadStrategy} instance established by the last call to
8 * {@link #setStrategy}.
9 *
10 * @return classloader to be used by the caller ['null' indicates the
11 * primordial loader]
12 */
13 public static synchronized ClassLoader getClassLoader() {
14 final Class caller = getCallerClass(0);
15 final ClassLoadContext ctx = new ClassLoadContext(caller);
16
17 return s_strategy.getClassLoader(ctx);
18 }
19
20 public static synchronized IClassLoadStrategy getStrategy() {
21 return s_strategy;
22 }
23
24 public static synchronized IClassLoadStrategy setStrategy(
25 final IClassLoadStrategy strategy) {
26 final IClassLoadStrategy old = s_strategy;
27 s_strategy = strategy;
28
29 return old;
30 }
31
32 /**
33 * A helper class to get the call context. It subclasses SecurityManager to
34 * make getClassContext() accessible. An instance of CallerResolver only
35 * needs to be created, not installed as an actual security manager.
36 */
37 private static final class CallerResolver extends SecurityManager {
38 protected Class[] getClassContext() {
39 return super.getClassContext();
40 }
41
42 } // End of nested class
43
44 /*
45 * Indexes into the current method call context with a given offset.
46 */
47 private static Class getCallerClass(final int callerOffset) {
48 return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET
49 + callerOffset];
50 }
51
52 private static IClassLoadStrategy s_strategy; // initialized in <clinit>
53
54 private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if
55 // this class is
56 // redesigned
57 private static final CallerResolver CALLER_RESOLVER; // set in <clinit>
58
59 static {
60 try {
61 // This can fail if the current SecurityManager does not allow
62 // RuntimePermission ("createSecurityManager"):
63
64 CALLER_RESOLVER = new CallerResolver();
65 } catch (SecurityException se) {
66 throw new RuntimeException(
67 "ClassLoaderResolver: could not create CallerResolver: "
68 + se);
69 }
70
71 s_strategy = new DefaultClassLoadStrategy();
72 }
73 } // End of class.
74
75
76
通過ClassLoaderResolver.getClassLoader()方法獲得一個(gè)ClassLoader的引用,并且利用正常的ClassLoader的api去加載資源,你也可以使用 ResourceLoader
API作為備選方案
1 public abstract class ResourceLoader {
2
3 /**
4 * @see java.lang.ClassLoader#loadClass(java.lang.String)
5 */
6 public static Class loadClass (final String name)throws ClassNotFoundException{
7
8 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
9
10 return Class.forName (name, false, loader);
11
12 }
13
14 /**
15
16 * @see java.lang.ClassLoader#getResource(java.lang.String)
17
18 */
19
20
21 public static URL getResource (final String name){
22
23 final ClassLoader loader = ClassLoaderResolver.getClassLoader (1);
24
25 if (loader != null)return loader.getResource (name);
26 else return ClassLoader.getSystemResource (name);
27 }
28 more methods
29
30 } // End of class
而決定使用何種ClassLoader策略是由接口實(shí)現(xiàn)的,這是一種插件機(jī)制,方便變更。
public interface IClassLoadStrategy{
ClassLoader getClassLoader (ClassLoadContext ctx);
} // End of interface
它需要一個(gè)ClassLoader Context 對(duì)象去決定使用何種ClassLoader策略。
1 public class ClassLoadContext{
2
3 public final Class getCallerClass (){
4 return m_caller;
5 }
6
7 ClassLoadContext (final Class caller){
8 m_caller = caller;
9
10 }
11
12 private final Class m_caller;
13
14 } // End of class
ClassLoadContext.getCallerClass()返回調(diào)用者給ClassLoaderResolver 或者 ResourceLoader,因此能獲得調(diào)用者的ClassLoader。需要注意的是,調(diào)用者是不會(huì)變的 (注:作者使用的final修飾字)。俺的方法不需要對(duì)現(xiàn)有的業(yè)務(wù)方法做擴(kuò)展,而且可以作為靜態(tài)方法是用。而且,你可以根據(jù)自己的業(yè)務(wù)場(chǎng)景實(shí)現(xiàn)獨(dú)特的ClassLoaderContext。
看出來沒,這是一種很熟悉的設(shè)計(jì)模式,XD ,把獲得ClassLoader的策略從業(yè)務(wù)中獨(dú)立出來,這個(gè)策略可以是"總是用ContextClassLoader"或者"總是用當(dāng)前ClassLoader"。想預(yù)先知道那種策略是正確的比較困難,那么這種模式可以讓你簡(jiǎn)單的改變策略。
俺寫了一個(gè)默認(rèn)的實(shí)現(xiàn),基本可以對(duì)付95%的場(chǎng)景(enjoy yourself)
1 public class DefaultClassLoadStrategy implements IClassLoadStrategy{
2
3 public ClassLoader getClassLoader (final ClassLoadContext ctx){
4
5 final ClassLoader callerLoader = ctx.getCallerClass ().getClassLoader ();
6
7 final ClassLoader contextLoader = Thread.currentThread ().getContextClassLoader ();
8
9 ClassLoader result;
10 // If 'callerLoader' and 'contextLoader' are in a parent-child
11 // relationship, always choose the child:
12 if (isChild (contextLoader, callerLoader))result = callerLoader;
13 else if (isChild (callerLoader, contextLoader))result = contextLoader;
14 else{
15 // This else branch could be merged into the previous one,
16 // but I show it here to emphasize the ambiguous case:
17 result = contextLoader;
18 }
19 final ClassLoader systemLoader = ClassLoader.getSystemClassLoader ();
20
21
22 // Precaution for when deployed as a bootstrap or extension class:
23 if (isChild (result, systemLoader))result = systemLoader;
24 return result;
25 }
26
27
28
29
more methods 
30
31 } // End of class
32
上面的邏輯比較簡(jiǎn)單,如果當(dāng)前ClassLoader和Context ClassLoader是父子關(guān)系,那就總選兒子,根據(jù)委托原則,這個(gè)很容易理解。
如果兩人平級(jí),選擇正確的ClassLoader很重要,運(yùn)行時(shí)不允許含糊。這種情況下,我的代碼選擇Context ClassLoader(這是俺個(gè)人的經(jīng)驗(yàn)之談),當(dāng)然也不要擔(dān)心不能改變,你能隨便根據(jù)需要改變。一般而言,Context ClassLoader比較適合框架,而Current ClassLoader在業(yè)務(wù)邏輯中用的更多。
最后,檢查確保選中的ClassLoader不是System ClassLoader的parent,一旦高于System ClassLoader ,請(qǐng)使用System ClassLoader(你的類部署在Ext路徑下面,就會(huì)出現(xiàn)這種情況)。
請(qǐng)注意,俺故意沒關(guān)注被載入資源的名稱。Java XML API 成為java 核心api的經(jīng)歷告訴我們,根據(jù)資源名稱過濾是很不cool的idea。而且 我也沒有去確認(rèn)到底哪個(gè)ClassLoader被取得了,因?yàn)橹灰宄恚@很容易被推理出來。(哈哈,俺是強(qiáng)淫)
盡管討論java 的ClassLoader不是一個(gè)很cool的話題(譯者注,當(dāng)年不cool,但是現(xiàn)在很cool),而且Java EE的ClassLoader策略越發(fā)的依賴各種平臺(tái)的升級(jí)。如果這沒有一個(gè)更好的設(shè)計(jì)的話,將會(huì)變成一個(gè)大大的問題。不敢您是否同意俺的觀點(diǎn),俺尊重你說話的權(quán)利,所以請(qǐng)給俺分享您的意見經(jīng)驗(yàn)。
作者介紹:
Vladimir Roubtsov,曾經(jīng)使用多種語言有超過13年的編程經(jīng)歷(恩 現(xiàn)在應(yīng)該超過15年了 hoho),95年開始接觸java(hoho 俺是99年看的第一本java書)。現(xiàn)在為Trilogy in Austin, Texas開發(fā)企業(yè)軟件。
翻譯完了,MMD 翻譯還是很麻煩的。 XD ........
43 Things : ClassLoader
首先,轉(zhuǎn)載一篇文章,個(gè)人認(rèn)為是看到過了講得最清楚的 XD
當(dāng)JVM(Java虛擬機(jī))啟動(dòng)時(shí),會(huì)形成由三個(gè)類加載器組成的初始類加載器層次結(jié)構(gòu):
bootstrap classloader
|
extension classloader
|
system classloader
bootstrap
classloader -
引導(dǎo)(也稱為原始)類加載器,它負(fù)責(zé)加載Java的核心類。在Sun的JVM中,在執(zhí)行java的命令中使用-Xbootclasspath選項(xiàng)或使用
-D選項(xiàng)指定sun.boot.class.path系統(tǒng)屬性值可以指定附加的類。這個(gè)加載器的是非常特殊的,它實(shí)際上不是
java.lang.ClassLoader的子類,而是由JVM自身實(shí)現(xiàn)的。大家可以通過執(zhí)行以下代碼來獲得bootstrap
classloader加載了那些核心類庫:
URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls.toExternalForm());
}
在我的計(jì)算機(jī)上的結(jié)果為:
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
file:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
file:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
file:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
file:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
file:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
file:/C:/j2sdk1.4.1_01/jre/classes
這時(shí)大家知道了為什么我們不需要在系統(tǒng)屬性CLASSPATH中指定這些類庫了吧,因?yàn)镴VM在啟動(dòng)的時(shí)候就自動(dòng)加載它們了。
extension
classloader -
擴(kuò)展類加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統(tǒng)屬性指定的)中JAR的類
包。這為引入除Java核心類以外的新功能提供了一個(gè)標(biāo)準(zhǔn)機(jī)制。因?yàn)槟J(rèn)的擴(kuò)展目錄對(duì)所有從同一個(gè)JRE中啟動(dòng)的JVM都是通用的,所以放入這個(gè)目錄的
JAR類包對(duì)所有的JVM和system
classloader都是可見的。在這個(gè)實(shí)例上調(diào)用方法getParent()總是返回空值null,因?yàn)橐龑?dǎo)加載器bootstrap
classloader不是一個(gè)真正的ClassLoader實(shí)例。所以當(dāng)大家執(zhí)行以下代碼時(shí):
System.out.println(System.getProperty("java.ext.dirs"));
ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
結(jié)果為:
C:"j2sdk1.4.1_01"jre"lib"ext
the parent of extension classloader : null
extension
classloader是system classloader的parent,而bootstrap classloader是extension
classloader的parent,但它不是一個(gè)實(shí)際的classloader,所以為null。
system
classloader -
系統(tǒng)(也稱為應(yīng)用)類加載器,它負(fù)責(zé)在JVM被啟動(dòng)時(shí),加載來自在命令java中的-classpath或者java.class.path系統(tǒng)屬性或者
CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類路徑。總能通過靜態(tài)方法ClassLoader.getSystemClassLoader()找
到該類加載器。如果沒有特別指定,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器。執(zhí)行以下代碼即可獲得:
System.out.println(System.getProperty("java.class.path"));
輸出結(jié)果則為用戶在系統(tǒng)屬性里面設(shè)置的CLASSPATH。
classloader
加載類用的是全盤負(fù)責(zé)委托機(jī)制。所謂全盤負(fù)責(zé),即是當(dāng)一個(gè)classloader加載一個(gè)Class的時(shí)候,這個(gè)Class所依賴的和引用的所有
Class也由這個(gè)classloader負(fù)責(zé)載入,除非是顯式的使用另外一個(gè)classloader載入;委托機(jī)制則是先讓parent(父)類加載器
(而不是super,它與parent
classloader類不是繼承關(guān)系)尋找,只有在parent找不到的時(shí)候才從自己的類路徑中去尋找。此外類加載還采用了cache機(jī)制,也就是如果
cache中保存了這個(gè)Class就直接返回它,如果沒有才從文件中讀取和轉(zhuǎn)換成Class,并存入cache,這就是為什么我們修改了Class但是必
須重新啟動(dòng)JVM才能生效的原因。
每個(gè)ClassLoader加載Class的過程是:
1.檢測(cè)此Class是否載入過(即在cache中是否有此Class),如果有到8,如果沒有到2
2.如果parent classloader不存在(沒有parent,那parent一定是bootstrap classloader了),到4
3.請(qǐng)求parent classloader載入,如果成功到8,不成功到5
4.請(qǐng)求jvm從bootstrap classloader中載入,如果成功到8
5.尋找Class文件(從與此classloader相關(guān)的類路徑中尋找)。如果找不到則到7.
6.從文件中載入Class,到8.
7.拋出ClassNotFoundException.
8.返回Class.
其中5.6步我們可以通過覆蓋ClassLoader的findClass方法來實(shí)現(xiàn)自己的載入策略。甚至覆蓋loadClass方法來實(shí)現(xiàn)自己的載入過程。
類加載器的順序是:
先
是bootstrap classloader,然后是extension classloader,最后才是system
classloader。大家會(huì)發(fā)現(xiàn)加載的Class越是重要的越在靠前面。這樣做的原因是出于安全性的考慮,試想如果system
classloader“親自”加載了一個(gè)具有破壞性的“java.lang.System”類的后果吧。這種委托機(jī)制保證了用戶即使具有一個(gè)這樣的類,
也把它加入到了類路徑中,但是它永遠(yuǎn)不會(huì)被載入,因?yàn)檫@個(gè)類總是由bootstrap classloader來加載的。大家可以執(zhí)行一下以下的代碼:
System.out.println(System.class.getClassLoader());
將會(huì)看到結(jié)果是null,這就表明java.lang.System是由bootstrap classloader加載的,因?yàn)閎ootstrap classloader不是一個(gè)真正的ClassLoader實(shí)例,而是由JVM實(shí)現(xiàn)的,正如前面已經(jīng)說過的。
下面就讓我們來看看JVM是如何來為我們來建立類加載器的結(jié)構(gòu)的:
sun.misc.Launcher,顧名思義,當(dāng)你執(zhí)行java命令的時(shí)候,JVM會(huì)先使用bootstrap classloader載入并初始化一個(gè)Launcher,執(zhí)行下來代碼:
System.out.println("the Launcher's classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
結(jié)果為:
the Launcher's classloader is null (因?yàn)槭怯胋ootstrap classloader加載,所以class loader為null)
Launcher
會(huì)根據(jù)系統(tǒng)和命令設(shè)定初始化好class loader結(jié)構(gòu),JVM就用它來獲得extension classloader和system
classloader,并載入所有的需要載入的Class,最后執(zhí)行java命令指定的帶有靜態(tài)的main方法的Class。extension
classloader實(shí)際上是sun.misc.Launcher$ExtClassLoader類的一個(gè)實(shí)例,system
classloader實(shí)際上是sun.misc.Launcher$AppClassLoader類的一個(gè)實(shí)例。并且都是
java.net.URLClassLoader的子類。
讓我們來看看Launcher初試化的過程的部分代碼。
Launcher的部分代碼:
1 public class Launcher {
2 public Launcher() {
3 ExtClassLoader extclassloader;
4 try {
5 //初始化extension classloader
6 extclassloader = ExtClassLoader.getExtClassLoader();
7 } catch(IOException ioexception) {
8 throw new InternalError("Could not create extension class loader");
9 }
10 try {
11 //初始化system classloader,parent是extension classloader
12 loader = AppClassLoader.getAppClassLoader(extclassloader);
13 } catch(IOException ioexception1) {
14 throw new InternalError("Could not create application class loader");
15 }
16 //將system classloader設(shè)置成當(dāng)前線程的context classloader(將在后面加以介紹)
17 Thread.currentThread().setContextClassLoader(loader);
18 

19 }
20 public ClassLoader getClassLoader() {
21 //返回system classloader
22 return loader;
23 }
24 }
25
extension classloader的部分代碼:
1 static class Launcher$ExtClassLoader extends URLClassLoader {
2
3 public static Launcher$ExtClassLoader getExtClassLoader()
4 throws IOException
5 {
6 File afile[] = getExtDirs();
7 return (Launcher$ExtClassLoader)AccessController.doPrivileged(new Launcher$1(afile));
8 }
9 private static File[] getExtDirs() {
10 //獲得系統(tǒng)屬性“java.ext.dirs”
11 String s = System.getProperty("java.ext.dirs");
12 File afile[];
13 if(s != null) {
14 StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);
15 int i = stringtokenizer.countTokens();
16 afile = new File;
17 for(int j = 0; j < i; j++)
18 afile[j] = new File(stringtokenizer.nextToken());
19
20 } else {
21 afile = new File[0];
22 }
23 return afile;
24 }
25 }
system classloader的部分代碼:
1 static class Launcher$AppClassLoader extends URLClassLoader
2 {
3
4 public static ClassLoader getAppClassLoader(ClassLoader classloader)
5 throws IOException
6 {
7 //獲得系統(tǒng)屬性“java.class.path”
8 String s = System.getProperty("java.class.path");
9 File afile[] = s != null ? Launcher.access$200(s) : new File[0];
10 return (Launcher$AppClassLoader)AccessController.doPrivileged(new Launcher$2(s, afile, classloader));
11 }
12 }
看
了源代碼大家就清楚了吧,extension
classloader是使用系統(tǒng)屬性“java.ext.dirs”設(shè)置類搜索路徑的,并且沒有parent。system
classloader是使用系統(tǒng)屬性“java.class.path”設(shè)置類搜索路徑的,并且有一個(gè)parent
classloader。Launcher初始化extension classloader,system
classloader,并將system classloader設(shè)置成為context classloader,但是僅僅返回system
classloader給JVM。
這里怎么又出來一個(gè)context
classloader呢?它有什么用呢?我們?cè)诮⒁粋€(gè)線程Thread的時(shí)候,可以為這個(gè)線程通過setContextClassLoader方法來
指定一個(gè)合適的classloader作為這個(gè)線程的context
classloader,當(dāng)此線程運(yùn)行的時(shí)候,我們可以通過getContextClassLoader方法來獲得此context
classloader,就可以用它來載入我們所需要的Class。默認(rèn)的是system
classloader。利用這個(gè)特性,我們可以“打破”classloader委托機(jī)制了,父classloader可以獲得當(dāng)前線程的context
classloader,而這個(gè)context
classloader可以是它的子classloader或者其他的classloader,那么父classloader就可以從其獲得所需的
Class,這就打破了只能向父classloader請(qǐng)求的限制了。這個(gè)機(jī)制可以滿足當(dāng)我們的classpath是在運(yùn)行時(shí)才確定,并由定制的
classloader加載的時(shí)候,由system classloader(即在jvm
classpath中)加載的class可以通過context
classloader獲得定制的classloader并加載入特定的class(通常是抽象類和接口,定制的classloader中是其實(shí)現(xiàn)),例
如web應(yīng)用中的servlet就是用這種機(jī)制加載的.
好了,現(xiàn)在我們了解了classloader的結(jié)構(gòu)和工作原理,那么我們
如何實(shí)現(xiàn)在運(yùn)行時(shí)的動(dòng)態(tài)載入和更新呢?只要我們能夠動(dòng)態(tài)改變類搜索路徑和清除classloader的cache中已經(jīng)載入的Class就行了,有兩個(gè)方
案,一是我們繼承一個(gè)classloader,覆蓋loadclass方法,動(dòng)態(tài)的尋找Class文件并使用defineClass方法來;另一個(gè)則非常
簡(jiǎn)單實(shí)用,只要重新使用一個(gè)新的類搜索路徑來new一個(gè)classloader就行了,這樣即更新了類搜索路徑以便來載入新的Class,也重新生成了一
個(gè)空白的cache(當(dāng)然,類搜索路徑不一定必須更改)。噢,太好了,我們幾乎不用做什么工作,java.netURLClassLoader正是一個(gè)符
合我們要求的classloader!我們可以直接使用或者繼承它就可以了!
這是j2se1.4 API的doc中URLClassLoader的兩個(gè)構(gòu)造器的描述:
URLClassLoader(URL[] urls)
Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
URLClassLoader(URL[] urls, ClassLoader parent)
Constructs a new URLClassLoader for the given URLs.
其中URL[] urls就是我們要設(shè)置的類搜索路徑,parent就是這個(gè)classloader的parent classloader,默認(rèn)的是system classloader。
好,現(xiàn)在我們能夠動(dòng)態(tài)的載入Class了,這樣我們就可以利用newInstance方法來獲得一個(gè)Object。但我們?nèi)绾螌⒋薕bject造型呢?可以將此Object造型成它本身的Class嗎?
首先讓我們來分析一下java源文件的編譯,運(yùn)行吧!javac命令是調(diào)用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方法來編譯:
public static int compile(String as[]);
public static int compile(String as[], PrintWriter printwriter);
返回0表示編譯成功,字符串?dāng)?shù)組as則是我們用javac命令編譯時(shí)的參數(shù),以空格劃分。例如:
javac -classpath c:"foo"bar.jar;. -d c:" c:"Some.java
則
字符串?dāng)?shù)組as為{"-classpath","c:""foo""bar.jar;.","-d","c:""","c:""Some.java"},
如果帶有PrintWriter參數(shù),則會(huì)把編譯信息出到這個(gè)指定的printWriter中。默認(rèn)的輸出是System.err。
其中
Main是由JVM使用Launcher初始化的system
classloader載入的,根據(jù)全盤負(fù)責(zé)原則,編譯器在解析這個(gè)java源文件時(shí)所發(fā)現(xiàn)的它所依賴和引用的所有Class也將由system
classloader載入,如果system classloader不能載入某個(gè)Class時(shí),編譯器將拋出一個(gè)“cannot resolve
symbol”錯(cuò)誤。
所以首先編譯就通不過,也就是編譯器無法編譯一個(gè)引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯(cuò)誤或者沒有把所需類庫放到CLASSPATH中,大家一定經(jīng)常看到這個(gè)“cannot resolve symbol”這個(gè)編譯錯(cuò)誤吧!
其
次,就是我們把這個(gè)Class放到編譯路徑中,成功的進(jìn)行了編譯,然后在運(yùn)行的時(shí)候不把它放入到CLASSPATH中而利用我們自己的
classloader來動(dòng)態(tài)載入這個(gè)Class,這時(shí)候也會(huì)出現(xiàn)“java.lang.NoClassDefFoundError”的違例,為什么呢?
我
們?cè)賮矸治鲆幌拢紫日{(diào)用這個(gè)造型語句的可執(zhí)行的Class一定是由JVM使用Launcher初始化的system
classloader載入的,根據(jù)全盤負(fù)責(zé)原則,當(dāng)我們進(jìn)行造型的時(shí)候,JVM也會(huì)使用system
classloader來嘗試載入這個(gè)Class來對(duì)實(shí)例進(jìn)行造型,自然在system
classloader尋找不到這個(gè)Class時(shí)就會(huì)拋出“java.lang.NoClassDefFoundError”的違例。
OK,
現(xiàn)在讓我們來總結(jié)一下,java文件的編譯和Class的載入執(zhí)行,都是使用Launcher初始化的system
classloader作為類載入器的,我們無法動(dòng)態(tài)的改變system
classloader,更無法讓JVM使用我們自己的classloader來替換system
classloader,根據(jù)全盤負(fù)責(zé)原則,就限制了編譯和運(yùn)行時(shí),我們無法直接顯式的使用一個(gè)system
classloader尋找不到的Class,即我們只能使用Java核心類庫,擴(kuò)展類庫和CLASSPATH中的類庫中的Class。
還
不死心!再嘗試一下這種情況,我們把這個(gè)Class也放入到CLASSPATH中,讓system
classloader能夠識(shí)別和載入。然后我們通過自己的classloader來從指定的class文件中載入這個(gè)Class(不能夠委托
parent載入,因?yàn)檫@樣會(huì)被system
classloader從CLASSPATH中將其載入),然后實(shí)例化一個(gè)Object,并造型成這個(gè)Class,這樣JVM也識(shí)別這個(gè)Class(因?yàn)?
system
classloader能夠定位和載入這個(gè)Class從CLASSPATH中),載入的也不是CLASSPATH中的這個(gè)Class,而是從
CLASSPATH外動(dòng)態(tài)載入的,這樣總行了吧!十分不幸的是,這時(shí)會(huì)出現(xiàn)“java.lang.ClassCastException”違例。
為
什么呢?我們也來分析一下,不錯(cuò),我們雖然從CLASSPATH外使用我們自己的classloader動(dòng)態(tài)載入了這個(gè)Class,但將它的實(shí)例造型的時(shí)
候是JVM會(huì)使用system
classloader來再次載入這個(gè)Class,并嘗試將使用我們的自己的classloader載入的Class的一個(gè)實(shí)例造型為system
classloader載入的這個(gè)Class(另外的一個(gè))。大家發(fā)現(xiàn)什么問題了嗎?也就是我們嘗試將從一個(gè)classloader載入的Class的一
個(gè)實(shí)例造型為另外一個(gè)classloader載入的Class,雖然這兩個(gè)Class的名字一樣,甚至是從同一個(gè)class文件中載入。但不幸的是JVM
卻認(rèn)為這個(gè)兩個(gè)Class是不同的,即JVM認(rèn)為不同的classloader載入的相同的名字的Class(即使是從同一個(gè)class文件中載入的)是
不同的!這樣做的原因我想大概也是主要出于安全性考慮,這樣就保證所有的核心Java類都是system
classloader載入的,我們無法用自己的classloader載入的相同名字的Class的實(shí)例來替換它們的實(shí)例。
看到這里,聰明的讀者一定想到了該如何動(dòng)態(tài)載入我們的Class,實(shí)例化,造型并調(diào)用了吧!
那
就是利用面向?qū)ο蟮幕咎匦灾坏亩嘈涡浴N覀儼盐覀儎?dòng)態(tài)載入的Class的實(shí)例造型成它的一個(gè)system
classloader所能識(shí)別的父類就行了!這是為什么呢?我們還是要再來分析一次。當(dāng)我們用我們自己的classloader來動(dòng)態(tài)載入這我們只要把
這個(gè)Class的時(shí)候,發(fā)現(xiàn)它有一個(gè)父類Class,在載入它之前JVM先會(huì)載入這個(gè)父類Class,這個(gè)父類Class是system
classloader所能識(shí)別的,根據(jù)委托機(jī)制,它將由system
classloader載入,然后我們的classloader再載入這個(gè)Class,創(chuàng)建一個(gè)實(shí)例,造型為這個(gè)父類Class,注意了,造型成這個(gè)父類
Class的時(shí)候(也就是上溯)是面向?qū)ο蟮膉ava語言所允許的并且JVM也支持的,JVM就使用system
classloader再次載入這個(gè)父類Class,然后將此實(shí)例造型為這個(gè)父類Class。大家可以從這個(gè)過程發(fā)現(xiàn)這個(gè)父類Class都是由
system classloader載入的,也就是同一個(gè)class
loader載入的同一個(gè)Class,所以造型的時(shí)候不會(huì)出現(xiàn)任何異常。而根據(jù)多形性,調(diào)用這個(gè)父類的方法時(shí),真正執(zhí)行的是這個(gè)Class(非父類
Class)的覆蓋了父類方法的方法。這些方法中也可以引用system
classloader不能識(shí)別的Class,因?yàn)楦鶕?jù)全盤負(fù)責(zé)原則,只要載入這個(gè)Class的classloader即我們自己定義的
classloader能夠定位和載入這些Class就行了。
這樣我們就可以事先定義好一組接口或者基類并放入CLASSPATH中,然
后在執(zhí)行的時(shí)候動(dòng)態(tài)的載入實(shí)現(xiàn)或者繼承了這些接口或基類的子類。還不明白嗎?讓我們來想一想Servlet吧,web application
server能夠載入任何繼承了Servlet的Class并正確的執(zhí)行它們,不管它實(shí)際的Class是什么,就是都把它們實(shí)例化成為一個(gè)Servlet
Class,然后執(zhí)行Servlet的init,doPost,doGet和destroy等方法的,而不管這個(gè)Servlet是從web-
inf/lib和web-inf/classes下由system
classloader的子classloader(即定制的classloader)動(dòng)態(tài)載入。說了這么多希望大家都明白了。在applet,ejb等
容器中,都是采用了這種機(jī)制.
對(duì)于以上各種情況,希望大家實(shí)際編寫一些example來實(shí)驗(yàn)一下。
最后我再說點(diǎn)別
的,classloader雖然稱為類加載器,但并不意味著只能用來加載Class,我們還可以利用它也獲得圖片,音頻文件等資源的URL,當(dāng)然,這些資
源必須在CLASSPATH中的jar類庫中或目錄下。我們來看API的doc中關(guān)于ClassLoader的兩個(gè)尋找資源和Class的方法描述吧:
public URL getResource(String name)
用指定的名字來查找資源,一個(gè)資源是一些能夠被class代碼訪問的在某種程度上依賴于代碼位置的數(shù)據(jù)(圖片,音頻,文本等等)。
一個(gè)資源的名字是以'/'號(hào)分隔確定資源的路徑名的。
這個(gè)方法將先請(qǐng)求parent
classloader搜索資源,如果沒有parent,則會(huì)在內(nèi)置在虛擬機(jī)中的classloader(即bootstrap
classloader)的路徑中搜索。如果失敗,這個(gè)方法將調(diào)用findResource(String)來尋找資源。
public static URL getSystemResource(String name)
從用來載入類的搜索路徑中查找一個(gè)指定名字的資源。這個(gè)方法使用system class loader來定位資源。即相當(dāng)于ClassLoader.getSystemClassLoader().getResource(name)。
例如:
System.out.println(ClassLoader.getSystemResource("java/lang/String.class"));
的結(jié)果為:
jar:file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar!/java/lang/String.class
表明String.class文件在rt.jar的java/lang目錄中。
因此我們可以將圖片等資源隨同Class一同打包到j(luò)ar類庫中(當(dāng)然,也可單獨(dú)打包這些資源)并添加它們到class loader的搜索路徑中,我們就可以無需關(guān)心這些資源的具體位置,讓class loader來幫我們尋找了!
以上是轉(zhuǎn)自bea論壇的一篇文章,作者不清楚,估計(jì)是bea內(nèi)部的大牛。是值得俺們頂禮膜拜的神一般的存在 XD
最后 附上自己的幾點(diǎn)理解
bootstrap classloader ------- 對(duì)應(yīng)jvm中某c++寫的dll類
Extenson ClassLoader ---------對(duì)應(yīng)內(nèi)部類ExtClassLoader
System ClassLoader ---------對(duì)應(yīng)內(nèi)部類AppClassLoader
Custom ClassLoader ----------對(duì)應(yīng)任何URLClassLoader的子類(你也可以繼承SecureClassLoader或者更加nb一點(diǎn) 直接繼承ClassLoader,這樣的話你也是神一般的存在了 XD)
以上四種classloder按照從上到下的順序,依次為下一個(gè)的parent
這個(gè)第一概念
第二個(gè)概念是幾個(gè)有關(guān)的classloader的類
抽象類 ClassLoader
|
SecureClassLoader
|
URLClassloader
| |
sun的ExtClassLoader sun的AppClassLoader
以上的類之間是繼承關(guān)系,與第一個(gè)概念說的parent是兩回事情,需要小心。
第三個(gè)概念是Thread的ContextClassLoader
其實(shí)從Context的名稱就可以看出來,這只是一個(gè)用以存儲(chǔ)任何classloader引用的臨時(shí)存儲(chǔ)空間,與classloader的層次沒有任何關(guān)系。
第四 就是如何實(shí)現(xiàn)自己的classloader了,本來是要翻譯另外一篇文章Find a way out of the ClassLoader maze
不過今天時(shí)間都花在《小夫妻天天惡戰(zhàn)》這篇神文上了 XD 強(qiáng)烈推薦任何和俺一樣期望彪悍人生的朋友都去看看
翻譯明天補(bǔ)上,浪費(fèi)時(shí)間是可恥的 XD
匿鳥,晚上還有朋友推薦的 《lie with me》(與我同眠)要看。XD
今天在TSS上又看到有人討論java多繼承的問題,是想起這個(gè)話題的原因。^_^
java中任何類都默認(rèn)繼承 Java.lang.Object,除非被另一個(gè)繼承覆蓋(override),hoho 俺一直這么稱呼override的,感覺更加貼切一些。
請(qǐng)看以下代碼:
package org.myth.test;
public class SuperSon{
SuperSon(){
System.out.println("this is super son");
}
}
對(duì)于編譯器來說,這段代碼會(huì)被首先補(bǔ)全為:
package org.myth.test;
public class SuperSon extends Object{
SuperSon(){
System.out.println("this is super son");
}
}
對(duì)待任何一個(gè)類,編譯器會(huì)去檢查extends關(guān)鍵字,如果沒有,編譯器會(huì)默認(rèn)添加extens Object
extends Object就是一段默認(rèn)隱藏的代碼,同樣在Constructor中,也有一段默認(rèn)隱藏的代碼。
package org.myth.test;
public class SuperSon extends Object{
SuperSon(){
super();//這就是一段默認(rèn)隱藏代碼
System.out.println("this is super son");
}
//整個(gè)構(gòu)造方法也是一段默認(rèn)隱藏代碼
}
如同編譯類時(shí)編譯器回去檢查extends關(guān)鍵字一樣,編譯器會(huì)首先檢查是否存在constructor,如果沒有,默認(rèn)增加ClassName()構(gòu)造方法。
在構(gòu)造方法內(nèi)部,編譯器會(huì)檢查第一行代碼是否為super構(gòu)造方法,如果不是,默認(rèn)添加super()
這個(gè)就是為什么 new一個(gè)對(duì)象的時(shí)候,首先調(diào)用的是父類的構(gòu)造方法。
一個(gè)錯(cuò)誤代碼示例:
package org.myth.test;
public class SuperMan {
SuperMan(String s){
System.out.println("this is super man");
}
}
package org.myth.test;
public class SuperSon extends SuperMan{
SuperSon(){
System.out.println("this is super son");
}
}
嘿嘿 第一篇文章