2、性能優化--lazy loaded
上面的代碼雖然簡單,但是有一個問題----無論這個類是否被使用,都會創建一個instance對象。如果這個創建很耗時,比如說鏈接10000次數據庫(誇張一點啦....),並且這個類還不一定會被使用,那麼這個創建過程就是無用的,怎麼辦呢?
爲了解決這個問題,我們想到的新的解決方案:
public class SingletonClass { private static SingletonClass instance = null; public static SingletonClass getInstance() { if(instance == null) { instance = new SingletonClass(); } return instance; } private SingletonClass() { } }
代碼的變化有倆處----首先,把 instance 設置爲 null ,知道第一次使用的時候判是否爲 null 來創建對象。因爲創建對象不在聲明處,所以那個 final 的修飾必須去掉。
我們來想象一下這個過程。要使用 SingletonClass ,調用 getInstance()方法,第一次的時候發現instance時null,然後就創建一個對象,返回出去;第二次再使用的時候,因爲這個 instance事static的,共享一個對象變量的,所以instance的值已經不是null了,因此不會再創建對象,直接將其返回。
這個過程就稱爲lazy loaded ,也就是遲加載-----直到使用的時候才經行加載。
這樣寫法也比較完美:但是還可以優化
public class SingletonClass{ private static SingletonClass instance = null; public static SingletonClass getInstance(){ if(instance == null){ synchronized(SingletonClass.class){ if(instance == null){ instance = new SingletonClass(); } } } return instance; } private SingletonClass(){ } }
通過單例模式的學習告訴我們:
1、單例模式理解起來簡單,但是具體實現起來還是有一定的難度。
2、synchronized關鍵字鎖定的是對象,在用的時候,一定要在恰當的地方使用(注意需要使用鎖的對象和過程,可能有的時候並不是整個對象及整個過程都需要鎖)。
到這兒,單例模式基本已經講完了,結尾處,筆者突然想到另一個問題,就是採用類的靜態方法,實現單例模式的效果,也是可行的,此處二者有什麼不同?
首先,靜態類不能實現接口。(從類的角度說是可以的,但是那樣就破壞了靜態了。因爲接口中不允許有static修飾的方法,所以即使實現了也是非靜態的)
其次,單例可以被延遲初始化,靜態類一般在第一次加載是初始化。之所以延遲加載,是因爲有些類比較龐大,所以延遲加載有助於提升性能。
再次,單例類可以被繼承,他的方法可以被覆寫。但是靜態類內部方法都是static,無法被覆寫。
最後一點,單例類比較靈活,畢竟從實現上只是一個普通的Java類,只要滿足單例的基本需求,你可以在裏面隨心所欲的實現一些其它功能,但是靜態類 不行。從上面這些概括中,基本可以看出二者的區別,但是,從另一方面講,我們上面最後實現的那個單例模式,內部就是用一個靜態類來實現的,所以,二者有很 大的關聯,只是我們考慮問題的層面不同罷了。兩種思想的結合,才能造就出完美的解決方案,就像HashMap採用數組+鏈表來實現一樣,其實生活中很多事 情都是這樣,單用不同的方法來處理問題,總是有優點也有缺點,最完美的方法是,結合各個方法的優點,才能最好的解決問題!
三:建造者模式(Builder)
工廠類模式提供的是創建單個類的模式,而建造者模式則是將各種產品集中起來進行管理,用來創建複合對象,所謂複合對象就是指某個類具有不同的屬性,其實建造者模式就是前面抽象工廠模式和最後的Test結合起來得到的。我們看一下代碼:
還和前面一樣,一個Sender接口,兩個實現類MailSender和SmsSender。最後,建造者類如下:
[java] view plaincopy
- public class Builder {
- private List<Sender> list = new ArrayList<Sender>();
- public void produceMailSender(int count){
- for(int i=0; i<count; i++){
- list.add(new MailSender());
- }
- }
- public void produceSmsSender(int count){
- for(int i=0; i<count; i++){
- list.add(new SmsSender());
- }
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- Builder builder = new Builder();
- builder.produceMailSender(10);
- }
- }
從這點看出,建造者模式將很多功能集成到一個類裏,這個類可以創造出比較複雜的東西。所以與工程模式的區別就是:工廠模式關注的是創建單個產品,而建造者模式則關注創建符合對象,多個部分。因此,是選擇工廠模式還是建造者模式,依實際情況而定。
四:原型模式(Prototype)
原型模式雖然是創建型的模式,但是與工程模式沒有關係,從名字即可看出,該模式的思想就是將一個對象作爲原型,對其進行復制、克隆,產生一個和原對象類似的新對象。本小結會通過對象的複製,進行講解。在Java中,複製對象是通過clone()實現的,先創建一個原型類:
[java] view plaincopy
- public class Prototype implements Cloneable {
- public Object clone() throws CloneNotSupportedException {
- Prototype proto = (Prototype) super.clone();
- return proto;
- }
- }
很簡單,一個原型類,只需要實現Cloneable接口,覆寫clone方法,此處clone方法可以改成任意的名稱,因爲Cloneable接口 是個空接口,你可以任意定義實現類的方法名,如cloneA或者cloneB,因爲此處的重點是super.clone()這句 話,super.clone()調用的是Object的clone()方法,而在Object類中,clone()是native的,具體怎麼實現,我會 在另一篇文章中,關於解讀Java中本地方法的調用,此處不再深究。在這兒,我將結合對象的淺複製和深複製來說一下,首先需要了解對象深、淺複製的概念:
淺複製:將一個對象複製後,基本數據類型的變量都會重新創建,而引用類型,指向的還是原對象所指向的。
深複製:將一個對象複製後,不論是基本數據類型還有引用類型,都是重新創建的。簡單來說,就是深複製進行了完全徹底的複製,而淺複製不徹底。
此處,寫一個深淺複製的例子:
[java] view plaincopy
- public class Prototype implements Cloneable, Serializable {
- private static final long serialVersionUID = 1L;
- private String string;
- private SerializableObject obj;
- /* 淺複製 */
- public Object clone() throws CloneNotSupportedException {
- Prototype proto = (Prototype) super.clone();
- return proto;
- }
- /* 深複製 */
- public Object deepClone() throws IOException, ClassNotFoundException {
- /* 寫入當前對象的二進制流 */
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(this);
- /* 讀出二進制流產生的新對象 */
- ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bis);
- return ois.readObject();
- }
- public String getString() {
- return string;
- }
- public void setString(String string) {
- this.string = string;
- }
- public SerializableObject getObj() {
- return obj;
- }
- public void setObj(SerializableObject obj) {
- this.obj = obj;
- }
- }
- class SerializableObject implements Serializable {
- private static final long serialVersionUID = 1L;
- }
要實現深複製,需要採用流的形式讀入當前對象的二進制輸入,再寫出二進制數據對應的對象。
//其他幾種模式
我們接着討論設計模式,上篇文章我講完了5種創建型模式,這章開始,我將講下7種結構型模式:適配器模式、裝飾模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中對象的適配器模式是各種模式的起源,我們看下面的圖:
適配器模式將某個類的接口轉換成客戶端期望的另一個接口表示,目的是消除由於接口不匹配所造成的類的兼容性問題。主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。首先,我們來看看類的適配器模式,先看類圖:
核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口時Targetable,通過Adapter類,將Source的功能擴展到Targetable裏,看代碼:
[java] view plaincopy
- public class Source {
- public void method1() {
- System.out.println("this is original method!");
- }
- }
[java] view plaincopy
- public interface Targetable {
- /* 與原類中的方法相同 */
- public void method1();
- /* 新類的方法 */
- public void method2();
- }
[java] view plaincopy
- public class Adapter extends Source implements Targetable {
- @Override
- public void method2() {
- System.out.println("this is the targetable method!");
- }
- }
Adapter類繼承Source類,實現Targetable接口,下面是測試類:
[java] view plaincopy
- public class AdapterTest {
- public static void main(String[] args) {
- Targetable target = new Adapter();
- target.method1();
- target.method2();
- }
- }
輸出:
this is original method!
this is the targetable method!
這樣Targetable接口的實現類就具有了Source類的功能
五:算法:
將之前介紹的所有排序算法整理成NumberSort類,代碼
NumberSort
六:深入探索Java工作原理:JVM內存回收及其他
Java語言引入了Java虛擬機,具有跨平臺運行的功能,能夠很好地適應各種Web應用。同時,爲了提高Java語言的性能和健壯性,還引入瞭如垃圾回收機制等新功能,通過這些改進讓Java具有其獨特的工作原理。
1.Java虛擬機
Java源程序通過編譯器編譯成.Class文件,然後java虛擬機中的java 解釋器負責將字節碼文件解釋成爲特定的機器碼進行運行。
java是一種半編譯半解釋型語言。半編譯是指:java源代碼,會經過javac命令變成 .class文件。半解釋是指: .class文件被jvm解釋的過程。也就是因爲jvm的半解釋纔有了java的動態語言特性:反射和annotation。
和android區別
alvik有自己的libdex庫負責對.class進行處理。libdex主要對.class進行處理生成自己的dex文件。主要做的工作是,對虛擬機指令進行轉換(dalvik是基於寄存器的,sun虛擬機是基於棧的),對類的靜態數據進行歸類、壓縮。
dalvik基於寄存器,而JVM基於stack ,Dalvik執行的是特有的DEX文件格式,而JVM運行的是*.class文件格式。
優勢:1、在編譯時提前優化代碼而不是等到運行時
2、 虛擬機很小,使用的空間也小;被設計來滿足可高效運行多種虛擬機實例。
Java虛擬機的建立需要針對不同的軟硬件平臺來實現,既要考慮處理器的型號,也要考慮操作系統的種類。由此在SPARC結構、X86結構、MIPS和PPC等嵌入式處理芯片上,在UNIX、Linux、Windows和部分實時操作系統上都可實現Java虛擬機。
2.無用內存自動回收機制
而在Java運行環境中,始終存在着一個系統級的線程,專門跟蹤內存的使用情況, 定期檢測出不再使用的內存,並自動進行回收,避免了內存的泄露,也減輕了程序員的工作量。
1.JVM
JVM是Java平臺的核心,爲了讓編譯產生的字節碼能更好地解釋與執行,因此把JVM分成了6個部分:JVM解釋器、指令系統、寄存器、棧、存儲區和碎片回收區。
基於android的Socket通信 Android框架
可以很明顯看出,Android系統架構由5部分組成,分別是:Linux Kernel、Android Runtime、Libraries、Application Framework、Applications。第二部分將詳細介紹這5個部分。
2、架構詳解
現在我們拿起手術刀來剖析各個部分。其實這部分SDK文檔已經幫我們做得很好了,我們要做的就是拿來主義,然後再加上自己理解。下面自底向上分析各層。
2.1、Linux Kernel
Android基於Linux 2.6提供核心系統服務,例如:安全、內存管理、進程管理、網絡堆棧、驅動模型。Linux Kernel也作爲硬件和軟件之間的抽象層,它隱藏具體硬件細節而爲上層提供統一的服務。
2.2、Android Runtime
Android 包含一個核心庫的集合,提供大部分在Java編程語言核心類庫中可用的功能。每一個Android應用程序是Dalvik虛擬機中的實例,運行在他們自己 的進程中。Dalvik虛擬機設計成,在一個設備可以高效地運行多個虛擬機。Dalvik虛擬機可執行文件格式是.dex,dex格式是專爲Dalvik 設計的一種壓縮格式,適合內存和處理器速度有限的系統。
大多數虛擬機包括JVM都是基於棧的,而Dalvik虛擬機則是基於寄存器的。 兩種架構各有優劣,一般而言,基於棧的機器需要更多指令,而基於寄存器的機器指令更大。dx 是一套工具,可以將 Java .class 轉換成 .dex 格式。一個dex文件通常會有多個.class。由於dex有時必須進行最佳化,會使文件大小增加1-4倍,以ODEX結尾。
Dalvik虛擬機依賴於Linux 內核提供基本功能,如線程和底層內存管理。
2.3、Libraries
Android包含一個C/C++庫的集合,供Android系統的各個組件使用。這些功能通過Android的應用程序框架(application framework)暴露給開發者。下面列出一些核心庫:
- 系統C庫——標準C系統庫(libc)的BSD衍生,調整爲基於嵌入式Linux設備
- 媒體庫——基於PacketVideo的OpenCORE。這些庫支持播放和錄製許多流行的音頻和視頻格式,以及靜態圖像文件,包括MPEG4、 H.264、 MP3、 AAC、 AMR、JPG、 PNG
- 界面管理——管理訪問顯示子系統和無縫組合多個應用程序的二維和三維圖形層
- LibWebCore——新式的Web瀏覽器引擎,驅動Android 瀏覽器和內嵌的web視圖
- SGL——基本的2D圖形引擎
- 3D庫——基於OpenGL ES 1.0 APIs的實現。庫使用硬件3D加速或包含高度優化的3D軟件光柵
- FreeType ——位圖和矢量字體渲染
- SQLite ——所有應用程序都可以使用的強大而輕量級的關係數據庫引擎
2.4、Application Framework
通過提供開放的開發平臺,Android使開發者能夠編制極其豐富和新穎的應用程序。開發者可以自由地利用設備硬件優勢、訪問位置信息、運行後臺服務、設置鬧鐘、向狀態欄添加通知等等,很多很多。
開發者可以完全使用核心應用程序所使用的框架APIs。應用程序的體系結構旨在簡化組件的重用,任何應用程序都能發佈他的功能且任何其他應用程序可以使用這些功能(需要服從框架執行的安全限制)。這一機制允許用戶替換組件。
所有的應用程序其實是一組服務和系統,包括:
- 視圖(View)——豐富的、可擴展的視圖集合,可用於構建一個應用程序。包括包括列表、網格、文本框、按鈕,甚至是內嵌的網頁瀏覽器
- 內容提供者(Content Providers)——使應用程序能訪問其他應用程序(如通訊錄)的數據,或共享自己的數據
- 資源管理器(Resource Manager)——提供訪問非代碼資源,如本地化字符串、圖形和佈局文件
- 通知管理器(Notification Manager)——使所有的應用程序能夠在狀態欄顯示自定義警告
- 活動管理器(Activity Manager)——管理應用程序生命週期,提供通用的導航回退功能
2.5、Applications
Android裝配一個核心應用程序集合,包括電子郵件客戶端、SMS程序、日曆、地圖、瀏覽器、聯繫人和其他設置。所有應用程序都是用Java編程語言寫的。更加豐富的應用程序有待我們去開發!
一、Socket通信簡介
Android 與服務器的通信方式主要有兩種,一是Http通信,一是Socket通信。兩者的最大差異在於,http連接使用的是“請求—響應方式”,即在請求時建立 連接通道,當客戶端向服務器發送請求後,服務器端才能向客戶端返回數據。而Socket通信則是在雙方建立起連接後就可以直接進行數據的傳輸,在連接時可 實現信息的主動推送,而不需要每次由客戶端想服務器發送請求。 那麼,什麼是socket?Socket又稱套接字,在程序內部提供了與外界通信的端口,即端口通信。通過建立socket連接,可爲通信雙方的數據傳輸 傳提供通道。socket的主要特點有數據丟失率低,使用簡單且易於移植。
1.2Socket的分類
根據不同的的底層協議,Socket的實現是多樣化的。本指南中只介紹TCP/IP協議族的內容,在這個協議族當中主要的Socket類型爲流套接字 (streamsocket)和數據報套接字(datagramsocket)。流套接字將TCP作爲其端對端協議,提供了一個可信賴的字節流服務。數據 報套接字使用UDP協議,提供數據打包發送服務。
二、Socket 基本通信模型
三、Socket基本實現原理
3.1基於TCP協議的Socket
服務器端首先聲明一個ServerSocket對象並且指定端口號,然後調 用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處於堵塞狀態。 (Socketsocket=serversocket.accept()),一旦接收到數據,通過inputstream讀取接收的數據。
客戶端創建一個Socket對象,指定服務器端的ip地址和端口號 (Socketsocket=newSocket("172.168.10.108",8080);),通過inputstream讀取數據,獲取服務器 發出的數據(OutputStreamoutputstream=socket.getOutputStream()),最後將要發送的數據寫入到 outputstream即可進行TCP協議的socket數據傳輸。