package com.access.external;
class Soup{
private Soup(){//構(gòu)造函數(shù)聲明為private,其它類不能用此構(gòu)造函數(shù)創(chuàng)建對象;
System.out.println("sffewe");
}
public static Soup makSoup(){//其它類可通過makSoup來創(chuàng)建對象;
return new Soup();
}
private static Soup ps1 = new Soup();//自己創(chuàng)建對象;
public static Soup access(){//返回對象的引用。
return ps1;
}
public void f(){}
}
class Sandwich{
void f(){
new Lunch();
}
}
public class Lunch {
void test(){
//Soup priv1 = new Soup();
Soup priv2 = Soup.makSoup();
Sandwich f1 = new Sandwich();
Soup.access().f();//不創(chuàng)建對象,但通過Soup中返回的對象引用調(diào)用其方法。
}
}
該方法返回一個句柄,它指向類Soup的一個對象。
Soup類向我們展示出如何通過將所有構(gòu)建器都設(shè)為private,從而防止直接創(chuàng)建一個類。請記住,假若不明確地至少創(chuàng)建一個構(gòu)建器,就會自動創(chuàng)建默認構(gòu)建器(沒有自變量)。若自己編寫默認構(gòu)建器,它就不會自動創(chuàng)建。把它變成private后,就沒人能為那個類創(chuàng)建一個對象。但別人怎樣使用這個類呢?上面的例子為我們揭示出了兩個選擇。第一個選擇,我們可創(chuàng)建一個static方法,再通過它創(chuàng)建一個新的Soup,然后返回指向它的一個句柄。如果想在返回之前對Soup進行一些額外的操作,或者想了解準備創(chuàng)建多少個Soup對象(可能是為了限制它們的個數(shù)),這種方案無疑是特別有用的。
第二個選擇是采用“設(shè)計方案”(Design Pattern)技術(shù),本書后面會對此進行詳細介紹。通常方案叫作“獨子”,因為它僅允許創(chuàng)建一個對象。類Soup的對象被創(chuàng)建成Soup的一個static private成員,所以有一個而且只能有一個。除非通過public方法access(),否則根本無法訪問它。
JDK SRC中注解:
基于哈希表的 Map 接口的實現(xiàn)。此實現(xiàn)提供所有可選的映射操作,并允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外,HashMap 類與 Hashtable 大致相同。)此類不保證映射的順序,特別是它不保證該順序恒久不變。
此實現(xiàn)假定哈希函數(shù)將元素正確分布在各桶之間,可為基本操作(get 和 put)提供穩(wěn)定的性能。迭代集合視圖所需的時間與 HashMap 實例的“容量”(桶的數(shù)量)及其大小(鍵-值映射關(guān)系數(shù))的和成比例。所以,如果迭代性能很重要,則不要將初始容量設(shè)置得太高(或?qū)⒓虞d因子設(shè)置得太低)。
HashMap 的實例有兩個參數(shù)影響其性能:初始容量 和加載因子。容量 是哈希表中桶的數(shù)量,初始容量只是哈希表在創(chuàng)建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數(shù)超出了加載因子與當前容量的乘積時,通過調(diào)用 rehash 方法將容量翻倍。
通常,默認加載因子 (.75) 在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數(shù) HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設(shè)置初始容量時應(yīng)該考慮到映射中所需的條目數(shù)及其加載因子,以便最大限度地降低 rehash 操作次數(shù)。如果初始容量大于最大條目數(shù)除以加載因子,則不會發(fā)生 rehash 操作。
如果很多映射關(guān)系要存儲在 HashMap 實例中,則相對于按需執(zhí)行自動的 rehash 操作以增大表的容量來說,使用足夠大的初始容量創(chuàng)建它將使得映射關(guān)系能更有效地存儲。
注意,此實現(xiàn)不是同步的。如果多個線程同時訪問此映射,而其中至少一個線程從結(jié)構(gòu)上修改了該映射,則它必須 保持外部同步。(結(jié)構(gòu)上的修改是指添加或刪除一個或多個映射關(guān)系的操作;僅改變與實例已經(jīng)包含的鍵關(guān)聯(lián)的值不是結(jié)構(gòu)上的修改。)這一般通過對自然封裝該映射的對象進行同步操作來完成。如果不存在這樣的對象,則應(yīng)該使用 Collections.synchronizedMap 方法來“包裝”該映射。最好在創(chuàng)建時完成這一操作,以防止對映射進行意外的不同步訪問,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
由所有此類的“集合視圖方法”所返回的迭代器都是快速失敗 的:在迭代器創(chuàng)建之后,如果從結(jié)構(gòu)上對映射進行修改,除非通過迭代器自身的 remove 或 add 方法,其他任何時間任何方式的修改,迭代器都將拋出 ConcurrentModificationException。因此,面對并發(fā)的修改,迭代器很快就會完全失敗,而不冒在將來不確定的時間任意發(fā)生不確定行為的風險。
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在不同步的并發(fā)修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常程序的方式是錯誤的,正確做法是:迭代器的快速失敗行為應(yīng)該僅用于檢測程序錯誤。
MVC模式是一個復(fù)雜的架構(gòu)模式,其實現(xiàn)也顯得非常復(fù)雜。但是,我們已經(jīng)終結(jié)出了很多可靠的設(shè)計模式,多種設(shè)計模式結(jié)合在一起,使MVC模式的實現(xiàn)變得相對簡單易行。Views可以看作一棵樹,顯然可以用Composite Pattern來實現(xiàn)。Views和Models之間的關(guān)系可以用Observer Pattern體現(xiàn)。Controller控制Views的顯示,可以用Strategy Pattern實現(xiàn)。Model通常是一個調(diào)停者,可采用Mediator Pattern來實現(xiàn)。
現(xiàn)在讓我們來了解一下MVC三個部分在J2EE架構(gòu)中處于什么位置,這樣有助于我們理解MVC模式的實現(xiàn)。MVC與J2EE架構(gòu)的對應(yīng)關(guān)系是:View處于Web Tier或者說是Client Tier,通常是JSP/Servlet,即頁面顯示部分。Controller也處于Web Tier,通常用Servlet來實現(xiàn),即頁面顯示的邏輯部分實現(xiàn)。Model處于Middle Tier,通常用服務(wù)端的javaBean或者EJB實現(xiàn),即業(yè)務(wù)邏輯部分的實現(xiàn)。(Enterprise Bean 與 JavaBean 不同。JavaBean 是使用 java.beans 包開發(fā)的,它是 Java 2 標準版的一部分。JavaBean 是一臺機器上同一個地址空間中運行的組件。JavaBean 是進程內(nèi)組件。Enterprise Bean 是使用 javax.ejb 包開發(fā)的,它是標準 JDK 的擴展,是 Java 2 Enterprise Edition 的一部分。Enterprise Bean 是在多臺機器上跨幾個地址空間運行的組件。因此 Enterprise Bean 是進程間組件。JavaBean 通常用作 GUI 窗口小部件,而 Enterprise Bean 則用作分布式商業(yè)對象. )
一、MVC設(shè)計思想
MVC英文即Model-View-Controller,即把一個應(yīng)用的輸入、處理、輸出流程按照Model、View、Controller的方式進行分離,這樣一個應(yīng)用被分成三個層——模型層、視圖層、控制層。
視圖(View)代表用戶交互界面,對于Web應(yīng)用來說,可以概括為HTML界面,但有可能為XHTML、XML和Applet。隨著應(yīng)用的復(fù)雜性和規(guī)模性,界面的處理也變得具有挑戰(zhàn)性。一個應(yīng)用可能有很多不同的視圖,MVC設(shè)計模式對于視圖的處理僅限于視圖上數(shù)據(jù)的采集和處理,以及用戶的請求,而不包括在視圖上的業(yè)務(wù)流程的處理。業(yè)務(wù)流程的處理交予模型(Model)處理。比如一個訂單的視圖只接受來自模型的數(shù)據(jù)并顯示給用戶,以及將用戶界面的輸入數(shù)據(jù)和請求傳遞給控制和模型。
模型(Model):就是業(yè)務(wù)流程/狀態(tài)的處理以及業(yè)務(wù)規(guī)則的制定。業(yè)務(wù)流程的處理過程對其它層來說是黑箱操作,模型接受視圖請求的數(shù)據(jù),并返回最終的處理結(jié)果。業(yè)務(wù)模型的設(shè)計可以說是MVC最主要的核心。目前流行的EJB模型就是一個典型的應(yīng)用例子,它從應(yīng)用技術(shù)實現(xiàn)的角度對模型做了進一步的劃分,以便充分利用現(xiàn)有的組件,但它不能作為應(yīng)用設(shè)計模型的框架。它僅僅告訴你按這種模型設(shè)計就可以利用某些技術(shù)組件,從而減少了技術(shù)上的困難。對一個開發(fā)者來說,就可以專注于業(yè)務(wù)模型的設(shè)計。MVC設(shè)計模式告訴我們,把應(yīng)用的模型按一定的規(guī)則抽取出來,抽取的層次很重要,這也是判斷開發(fā)人員是否優(yōu)秀的設(shè)計依據(jù)。抽象與具體不能隔得太遠,也不能太近。MVC并沒有提供模型的設(shè)計方法,而只告訴你應(yīng)該組織管理這些模型,以便于模型的重構(gòu)和提高重用性。我們可以用對象編程來做比喻,MVC定義了一個頂級類,告訴它的子類你只能做這些,但沒法限制你能做這些。這點對編程的開發(fā)人員非常重要。
業(yè)務(wù)模型還有一個很重要的模型那就是數(shù)據(jù)模型。數(shù)據(jù)模型主要指實體對象的數(shù)據(jù) 保存(持續(xù)化)。比如將一張訂單保存到數(shù)據(jù)庫,從數(shù)據(jù)庫獲取訂單。我們可以將這個模型單獨列出,所有有關(guān)數(shù)據(jù)庫的操作只限制在該模型中。
控制(Controller)可以理解為從用戶接收請求, 將模型與視圖匹配在一起,共同完成用戶的請求。劃分控制層的作用也很明顯,它清楚地告訴你,它就是一個分發(fā)器,選擇什么樣的模型,選擇什么樣的視圖,可以完成什么樣的用戶請求。控制層并不做任何的數(shù)據(jù)處理。例如,用戶點擊一個連接,控制層接受請求后, 并不處理業(yè)務(wù)信息,它只把用戶的信息傳遞給模型,告訴模型做什么,選擇符合要求的視圖返回給用戶。因此,一個模型可能對應(yīng)多個視圖,一個視圖可能對應(yīng)多個模型。模型、視圖與控制器的分離,使得一個模型可以具有多個顯示視圖。如果用戶通過某個視圖的控制器改變了模型的數(shù)據(jù),所有其它依賴于這些數(shù)據(jù)的視圖都應(yīng)反映到這些變化。因此,無論何時發(fā)生了何種數(shù)據(jù)變化,控制器都會將變化通知所有的視圖,導致顯示的更新。這實際上是一種模型的變化-傳播機制。模型、視圖、控制器三者之間的關(guān)系和各自的主要功能,如圖1所示。
二、MVC設(shè)計模式的實現(xiàn)
ASP.NET提供了一個很好的實現(xiàn)這種經(jīng)典設(shè)計模式的類似環(huán)境。開發(fā)者通過在ASPX頁面中開發(fā)用戶接口來實現(xiàn)視圖;控制器的功能在邏輯功能代碼(.cs)中實現(xiàn);模型通常對應(yīng)應(yīng)用系統(tǒng)的業(yè)務(wù)部分。在ASP.NET中實現(xiàn)這種設(shè)計而提供的一個多層系統(tǒng),較經(jīng)典的ASP結(jié)構(gòu)實現(xiàn)的系統(tǒng)來說有明顯的優(yōu)點。將用戶顯示(視圖)從動作(控制器)中分離出來,提高了代碼的重用性。將數(shù)據(jù)(模型)從對其操作的動作(控制器)分離出來可以讓你設(shè)計一個與后臺存儲數(shù)據(jù)無關(guān)的系統(tǒng)。就MVC結(jié)構(gòu)的本質(zhì)而言,它是一種解決耦合系統(tǒng)問題的方法。
2.1 視圖
視圖是模型的表示,它提供用戶交互界面。使用多個包含單顯示頁面的用戶部件,復(fù)雜的Web頁面可以展示來自多個數(shù)據(jù)源的內(nèi)容,并且網(wǎng)頁人員,美工能獨自參與這些Web頁面的開發(fā)和維護。
在ASP.NET下,視圖的實現(xiàn)很簡單??梢韵耖_發(fā)WINDOWS界面一樣直接在集成開發(fā)環(huán)境下通過拖動控件來完成頁面開發(fā)本。本文中介紹每一個頁面都采用復(fù)合視圖的形式即:一個頁面由多個子視圖(用戶部件)組成;子視圖可以是最簡單HTML 控件、服務(wù)器控件或多個控件嵌套構(gòu)而成的Web自定義控件。頁面都由模板定義,模板定義了頁面的布局,用戶部件的標簽和數(shù)目,用戶指定一個模板,平臺根據(jù)這些信息自動創(chuàng)建頁面。針對靜態(tài)的模板內(nèi)容,如頁面上的站點導航,菜單,友好鏈接,這些使用缺省的模板內(nèi)容配置;針對動態(tài)的模板內(nèi)容(主要是業(yè)務(wù)內(nèi)容),由于用戶的請求不同,只能使用后期綁定,并且針對用戶的不同,用戶部件的顯示內(nèi)容進行過濾。使用由用戶部件根據(jù)模板配置組成的組合頁面,它增強了可重用性,并原型化了站點的布局。
視圖部分大致處理流程如下:首先,頁面模板定義了頁面的布局;頁面配置文件定義視圖標簽的具體內(nèi)容(用戶部件);然后,由頁面布局策略類初始化并加載頁面;每個用戶部件根據(jù)它自己的配置進行初始化,加載校驗器并設(shè)置參數(shù),以及事件的委托等;用戶提交后,通過了表示層的校驗,用戶部件把數(shù)據(jù)自動提交給業(yè)務(wù)實體即模型。
這一部分主要定義了WEB頁面基類PageBase;頁面布局策略類PageLayout,完成頁面布局,用于加載用戶部件到頁面;用戶部件基類UserControlBase即用戶部件框架,用于動態(tài)加載檢驗部件,以及實現(xiàn)用戶部件的個性化。為了實現(xiàn)WEB應(yīng)用的靈活性,視圖部分也用到了許多配置文件例如:置文件有模板配置、頁面配置、路徑配置、驗證配置等。
2.2 控制器
為了能夠控制和協(xié)調(diào)每個用戶跨越多個請求的處理,控制機制應(yīng)該以集中的方式進行管理。因此,為了達到集中管理的目的引入了控制器。應(yīng)用程序的控制器集中從客戶端接收請求(典型情況下是一個運行瀏覽器的用戶),決定執(zhí)行什么商業(yè)邏輯功能,然后將產(chǎn)生下一步用戶界面的責任委派給一個適當?shù)囊晥D組件。
用控制器提供一個控制和處理請求的集中入口點,它負責接收、截取并處理用戶請求;并將請求委托給分發(fā)者類,根據(jù)當前狀態(tài)和業(yè)務(wù)操作的結(jié)果決定向客戶呈現(xiàn)的視圖。在這一部分主要定義了HttpReqDispatcher(分發(fā)者類)、HttpCapture(請求捕獲者類)、Controller(控制器類)等,它們相互配合來完成控制器的功能。請求捕獲者類捕獲HTTP請求并轉(zhuǎn)發(fā)給控制器類。控制器類是系統(tǒng)中處理所有請求的最初入口點??刂破魍瓿梢恍┍匾奶幚砗蟀颜埱笪薪o分發(fā)者類;分發(fā)者類分發(fā)者負責視圖的管理和導航,它管理將選擇哪個視圖提供給用戶,并提供給分發(fā)資源控制。在這一部分分別采用了分發(fā)者、策略、工廠方法、適配器等設(shè)計模式。
為了使請求捕獲者類自動捕獲用戶請求并進行處理,ASP.NET 提供低級別的請求/響應(yīng) API,使開發(fā)人員能夠使用 .NET 框架類為傳入的 HTTP 請求提供服務(wù)。為此,必須創(chuàng)作支持 System.Web.IHTTPHandler 接口和實現(xiàn) ProcessRequest() 方法的類即:請求捕獲者類,并在web.config 的 <httphandlers> 節(jié)中添加類。ASP.NET 收到的每個傳入 HTTP 請求最終由實現(xiàn) IHTTPHandler 的類的特定實例來處理。IHttpHandlerFactory 提供了處理 IHttpHandler 實例 URL 請求的實際解析的結(jié)構(gòu)。HTTP 處理程序和工廠在 ASP.NET 配置中聲明為 web.config 文件的一部分。ASP.NET 定義了一個 <httphandlers> 配置節(jié),在其中可以添加和移除處理程序和工廠。子目錄繼承 HttpHandlerFactory 和 HttpHandler 的設(shè)置。 HTTP 處理程序和工廠是 ASP.NET 頁框架的主體。工廠將每個請求分配給一個處理程序,后者處理該請求。 例如,在全局 machine.config 文件中,ASP.NET 將所有對 ASPx 文件的請求映射到 HttpCapture類:
<httphandlers>
...
...
</httphandlers>
2.3 模型
MVC系統(tǒng)中的模型從概念上可以分為兩類――系統(tǒng)的內(nèi)部狀態(tài)和改變系統(tǒng)狀態(tài)的動作。模型是你所有的商業(yè)邏輯代碼片段所在。本文為模型提供了業(yè)務(wù)實體對象和業(yè)務(wù)處理對象:所有的業(yè)務(wù)處理對象都是從ProcessBase類派生的子類。業(yè)務(wù)處理對象封裝了具體的處理邏輯,調(diào)用業(yè)務(wù)邏輯模型,并且把響應(yīng)提交到合適的視圖組件以產(chǎn)生響應(yīng)。業(yè)務(wù)實體對象可以通過定義屬性描述客戶端表單數(shù)據(jù)。所有業(yè)務(wù)實體對象都EntityBase派生子類對象,業(yè)務(wù)處理對象可以直接對它進行讀寫,而不再需要和request、response對象進行數(shù)據(jù)交互。通過業(yè)務(wù)實體對象實現(xiàn)了對視圖和模型之間交互的支持。實現(xiàn)時把"做什么"(業(yè)務(wù)處理)和"如何做"(業(yè)務(wù)實體)分離。這樣可以實現(xiàn)業(yè)務(wù)邏輯的重用。由于各個應(yīng)用的具體業(yè)務(wù)是不同的,這里不再列舉其具體代碼實例。
三、MVC設(shè)計模式的擴展
通過在ASP.NET中的MVC模式編寫的,具有極其良好的可擴展性。它可以輕松實現(xiàn)以下功能:
①實現(xiàn)一個模型的多個視圖;
?、诓捎枚鄠€控制器;
③當模型改變時,所有視圖將自動刷新;
④所有的控制器將相互獨立工作。
這就是MVC模式的好處,只需在以前的程序上稍作修改或增加新的類,即可輕松增加許多程序功能。以前開發(fā)的許多類可以重用,而程序結(jié)構(gòu)根本不再需要改變,各類之間相互獨立,便于團體開發(fā),提高開發(fā)效率。下面討論如何實現(xiàn)一個模型、兩個視圖和一個控制器的程序。其中模型類及視圖類根本不需要改變,與前面的完全一樣,這就是面向?qū)ο缶幊?/span>的好處。對于控制器中的類,只需要增加另一個視圖,并與模型發(fā)生關(guān)聯(lián)即可。該模式下視圖、控制器、模型三者之間的示意圖如圖2所示。
四、MVC的優(yōu)點
大部分用過程語言比如ASP、PHP開發(fā)出來的Web應(yīng)用,初始的開發(fā)模板就是混合層的數(shù)據(jù)編程。例如,直接向數(shù)據(jù)庫發(fā)送請求并用HTML顯示,開發(fā)速度往往比較快,但由于數(shù)據(jù)頁面的分離不是很直接,因而很難體現(xiàn)出業(yè)務(wù)模型的樣子或者模型的重用性。產(chǎn)品設(shè)計彈性力度很小,很難滿足用戶的變化性需求。MVC要求對應(yīng)用分層,雖然要花費額外的工作,但產(chǎn)品的結(jié)構(gòu)清晰,產(chǎn)品的應(yīng)用通過模型可以得到更好地體現(xiàn)。
首先,最重要的是應(yīng)該有多個視圖對應(yīng)一個模型的能力。在目前用戶需求的快速變化下,可能有多種方式訪問應(yīng)用的要求。例如,訂單模型可能有本系統(tǒng)的訂單,也有網(wǎng)上訂單,或者其他系統(tǒng)的訂單,但對于訂單的處理都是一樣,也就是說訂單的處理是一致的。按MVC設(shè)計模式,一個訂單模型以及多個視圖即可解決問題。這樣減少了代碼的復(fù)制,即減少了代碼的維護量,一旦模型發(fā)生改變,也易于維護。 其次,由于模型返回的數(shù)據(jù)不帶任何顯示格式,因而這些模型也可直接應(yīng)用于接口的使用。
再次,由于一個應(yīng)用被分離為三層,因此有時改變其中的一層就能滿足應(yīng)用的改變。一個應(yīng)用的業(yè)務(wù)流程或者業(yè)務(wù)規(guī)則的改變只需改動MVC的模型層。
控制層的概念也很有效,由于它把不同的模型和不同的視圖組合在一起完成不同的請求,因此,控制層可以說是包含了用戶請求權(quán)限的概念。
最后,它還有利于軟件工程化管理。由于不同的層各司其職,每一層不同的應(yīng)用具有某些相同的特征,有利于通過工程化、工具化產(chǎn)生管理程序代碼。
五、MVC的不足
MVC的不足體現(xiàn)在以下幾個方面:
?。?)增加了系統(tǒng)結(jié)構(gòu)和實現(xiàn)的復(fù)雜性。對于簡單的界面,嚴格遵循MVC,使模型、視圖與控制器分離,會增加結(jié)構(gòu)的復(fù)雜性,并可能產(chǎn)生過多的更新操作,降低運行效率。
?。?)視圖與控制器間的過于緊密的連接。視圖與控制器是相互分離,但確實聯(lián)系緊密的部件,視圖沒有控制器的存在,其應(yīng)用是很有限的,反之亦然,這樣就妨礙了他們的獨立重用。
(3)視圖對模型數(shù)據(jù)的低效率訪問。依據(jù)模型操作接口的不同,視圖可能需要多次調(diào)用才能獲得足夠的顯示數(shù)據(jù)。對未變化數(shù)據(jù)的不必要的頻繁訪問,也將損害操作性能。
(4) 目前,一般高級的界面工具或構(gòu)造器不支持MVC模式。改造這些工具以適應(yīng)MVC需要和建立分離的部件的代價是很高的,從而造成使用MVC的困難。
構(gòu)造函數(shù)重載:重載三要素(參數(shù)數(shù)量、參數(shù)類型、參數(shù)的排列順序)。
基本數(shù)據(jù)類型的重載:如果實參比形參的類型小,數(shù)據(jù)會先提升,如果實參比形參大,那么要先進行強制類型轉(zhuǎn)換。
返回值類型不是重載的要素:理解之一是,構(gòu)造函數(shù)要實現(xiàn)重載,但構(gòu)造函數(shù)無返回值。另外調(diào)用函數(shù)的時候可以沒有返回值類型。
this關(guān)鍵詞的使用:
1、this只能用于方法內(nèi)部,它負責返回調(diào)用這個方法的對象的引用。你可以把this對象的引用當成任何對象的引用。
2、this用于在構(gòu)造函數(shù)中調(diào)用其他構(gòu)造函數(shù),但只能調(diào)用一個,且調(diào)用的代碼應(yīng)放在程序最前面,否則編譯器會報錯。
3、this.s=s當類的數(shù)據(jù)成員與類的方法的參數(shù)同名時,使用this.s=s消除歧義。
static的含義:
它的意思是,這個方法沒有this,你不能在static方法里調(diào)用非static的方法,但你卻可以不通過對象,直接調(diào)用static方法。類的static方法只能訪問其它static方法static成員。Tag(int marker){輸出結(jié)果為:
System.out.println("Tag("+marker+")");
}
}
class Card{
Tag t1 = new Tag(1);
Card(){
System.out.println("Card()");
t3 = new Tag(22);
}
Tag t2 = new Tag(2);
void f(){
System.out.println("f()");
}
Tag t3 = new Tag(3);
}
public class Clean {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Card t = new Card();
t.f();
}
}
Tag(1)
Tag(2)
Tag(3)
Card()
Tag(22)
f()
package com.initialization.order;
class Bowl {
Bowl(){
System.out.println("Bowl(9)");
}
Bowl(int marker) {
System.out.println("Bowl(" + marker + ")");
}
static Bowl b6 = new Bowl(6);
static Bowl b9 = new Bowl();
void f(int marker) {
System.out.println("f(" + marker + ")");
}
}class Table {
static Bowl b1 = new Bowl(1);
Table() {
System.out.println("Table()");
b2.f(1);
}
void f2(int marker) {
System.out.println("f2(" + marker + ")");
}
static Bowl b2 = new Bowl(2);
}
class Cupboard {
Bowl b3 = new Bowl(3);
Bowl b10 = new Bowl();
static Bowl b4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard()");
b4.f(2);
}
void f3(int marker) {
System.out.println("f3(" + marker + ")");
}
static Bowl b5 = new Bowl(5);
}public class StaticInitialization {
//static Bowl b7 = new Bowl(7); //----------(1)
public static void main(String[] args) {
System.out.println(
"Creating new Cupboard() in main");
new Cupboard();
System.out.println(
"Creating new Cupboard() in main");
new Cupboard();
//t2.f2(1); //--------------(2)
//t3.f3(1); //---------------(3)
}
//static Bowl b8 = new Bowl(8); //----------------(4)
//static Table t2 = new Table(); //----------------(5)
//static Cupboard t3 = new Cupboard(); //---------(6)
} ///:~
調(diào)試以上代碼,總結(jié)出以下結(jié)論:
一、初始化的過程:總體來說順序為:static初始化->非static初始化->執(zhí)行構(gòu)造函數(shù);
二、代碼分析一:對現(xiàn)有代碼執(zhí)行結(jié)果如下:
Creating new Cupboard() in main
Bowl(6)
Bowl(9)
Bowl(4)
Bowl(5)
Bowl(3)
Bowl(9)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Bowl(9)
Cupboard()
f(2)
執(zhí)行過程:
1、java解釋器尋找public class類,加載StaticInitialization 類;
2、尋找StaticInitialization 類中的static定義代碼段;這里因為(1)、(4)、(5)、(6)均加了注釋,所以StaticInitialization 中沒有static需要初始化;3、進入main函數(shù)中執(zhí)行代碼輸出Creating new Cupboard() in main;
4、繼續(xù)執(zhí)行new Cupboard();,注意初始化的順序是static初始化->非static初始化->執(zhí)行構(gòu)造函數(shù);所以加載類Cupboard后,首先尋找Cupboard類中的static代碼段;
發(fā)現(xiàn)static段是: static Bowl b4 = new Bowl(4); static Bowl b5 = new Bowl(5);
同時發(fā)現(xiàn)有非static段是: Bowl b3 = new Bowl(3); Bowl b10 = new Bowl();
按順序先執(zhí)行:static Bowl b4 = new Bowl(4); 初始化,因為定義的是Bowl類的實例,所以先加載Bowl類,進入Bowl類發(fā)現(xiàn)又有static代碼段static Bowl b6 = new Bowl(6);
static Bowl b9 = new Bowl();然而我們知道初始化static Bowl b4 = new Bowl(4); 需要調(diào)用Bowl 的構(gòu)造函數(shù),但調(diào)用構(gòu)造函數(shù)之前必須將該類需要初始化的部分先進行初始化,所以執(zhí)行到這里就要先進行Bowl類中的static代碼段的初始化,之后才能調(diào)用構(gòu)造函數(shù)Bowl(int marker) 為static Bowl b4 = new Bowl(4); 進行初始化。于是b6,b9分別調(diào)用構(gòu)造函數(shù)Bowl(int marker),Bowl(),輸出Bowl(6),Bowl(9),完了之后,b4調(diào)用構(gòu)造函數(shù)Bowl(int marker)輸出Bowl(4),b4初始化結(jié)束,返回Cupboard類繼續(xù)執(zhí)行,初始化b5,輸出Bowl(5),此時Cupboard類中static部分初始化完,接下來對非static部分初始化,即對b3和b10初始化,一樣的方法,加載Bowl類,發(fā)現(xiàn)static字段在上面已經(jīng)初始化,所以這里直接調(diào)用Bowl類的構(gòu)造函數(shù),輸出Bowl(3),Bowl(9)。至此Cupboard類中需要初始化的部分已經(jīng)初始化完了,所以放心大膽的調(diào)用Cupboard類的構(gòu)造函數(shù),為main函數(shù)中代碼完成new Cupboard();的實現(xiàn),輸出Cupboard(),f(2)。程序執(zhí)行返回到main函數(shù),輸出:Creating new Cupboard() in main,代碼new Cupboard();又一次出現(xiàn),這里實際上是想演示static只會初始化一次,而非static只要創(chuàng)建了對象或調(diào)用了成員、成員函數(shù),會進行第二次初始化,于是可以看到輸出結(jié)果并沒有再輸出Bowl(6)、Bowl(9)、Bowl(4)、Bowl(5),而是輸出:Bowl(3)、Bowl(9)、Cupboard()、f(2)。
5、取消注釋(1)、(4),發(fā)現(xiàn)結(jié)果如下:
Bowl(6)
Bowl(9)
Bowl(7)
Bowl(8)
Creating new Cupboard() in main
Bowl(4)
Bowl(5)
Bowl(3)
Bowl(9)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Bowl(9)
Cupboard()
f(2)
可以看出輸出了Bowl(7)、Bowl(8),這說明在main()函數(shù)執(zhí)行之前,程序要先對StaticInitialization進行檢查,如果有static部分,則先初始化。
6、再取消注釋(2)、(5)輸出結(jié)果為:
Bowl(6)
Bowl(9)
Bowl(7)
Bowl(8)
Bowl(1)
Bowl(2)
Table()
f(1)
Creating new Cupboard() in main
Bowl(4)
Bowl(5)
Bowl(3)
Bowl(9)
Cupboard()
f(2)
Creating new Cupboard() in main
Bowl(3)
Bowl(9)
Cupboard()
f(2)
f2(1)
在前面的基礎(chǔ)上又輸出了Bowl(1)、Bowl(2)、Table()、f(1)。然而我們看到當沒有代碼
static Table t2 = new Table();的時候Table類中的static部分沒有被初始化,這說明什么?
static初始化只有在必要的時候才會進行。只有在創(chuàng)建了第一個Table對象之后才會進行初始化。總結(jié)如下:初始化順序為:加載public StaticInitialization類->初始化static->加載類Cupboard->初始化static->加載類Bowl->初始化static->執(zhí)行Bowl類構(gòu)造函數(shù)->初始化Cupboard類非static->調(diào)用Bowl類構(gòu)造函數(shù)執(zhí)行->調(diào)用Cupboard類構(gòu)造函數(shù)執(zhí)行->返回StaticInitialization類.
一種使用類的對象StaticTest st1=new StaticTest();st1.i++;
一種是直接使用類名.StaticTest.i++;
注意:如果使用類的多個對象使用變量i時,改變其中一個另一個跟著改變,因為他們使用的是同一段內(nèi)存.
StaticTest st1=new StaticTest();
StaticTest st2=new StaticTest();
st1.i++;
st2.i++;
如main函數(shù)中的方法.
public static void main(String[] args){
System.out.println("slfsfsfsf");
}
因為out是System類中聲明為static類型的對象