《Java编程思想》《Think in Java》笔记
前言
這本書不適合初學者,這本書適合已經學過Java框架并做過一兩個項目的同學來看,這本書對基礎知識的理解非常透徹。我在看的時候常常有一種醍醐灌頂的感覺,常常為“原來是這樣子的!”而激動,確實是一本非常好的書。我在看書時將書本我覺得重點的內容原封不動的摘錄下來,給想看重點的同學看看,也為一些對某些基礎概念不是很懂的同學給個了解渠道。
第1章?????對象入門
1.1???抽象的進步
?????? 所有編程語言的最終目的都是提供一種“抽象”方法。一種較有爭議的說法是:解決問題的復雜程度直接取決于抽象的種類及質量。
?????? OOP允許我們根據問題來描述問題,而不是根據方案。
?????? “純粹”的面向對象程序設計方法:
?????? (1)所有東西都是對象。可將對象想象成一種新型變量;它保存著數據,但可要求它對自身進行操作。理論上講,可從要解決的問題身上提出所有概念性的組件,然后在程序中將其表達為一個對象。
?????? (2)程序是一大堆對象的組合;通過消息傳遞,各對象知道自己該做些什么。為了向對象發出請求,需向那個對象“發送一條消息”。更具體地講,可將消息想象為一個調用請求,它調用的是從屬于目標對象的一個子例程或函數。
?????? (3)每個對象都有自己的存儲空間,可容納其他對象。或者說,通過封裝現有對象,可制作出新型對象。所以,盡管對象的概念非常簡單,但在程序中卻可達到任意高的復雜程度。
?????? (4)每個對象都有一種類型。根據語法,每個對象都是某個“類”的一個“實例”。其中,“類”(Class)是“類型”(Type)的同義詞。一個類最重要的特征就是“能將什么消息發給它?”。
?????? (5)同一類所有對象都能接收相同的消息。這實際是別有含義的一種說法,大家不久便能理解。由于類型為“圓”(Circle)的一個對象也屬于類型為“形狀”(Shape)的一個對象,所以一個圓完全能接收形狀消息。這意味著可讓程序代碼統一指揮“形狀”,令其自動控制所有符合“形狀”描述的對象,其中自然包括“圓”。這一特性稱為對象的“可替換性”,是OOP[莫星燦1]?最重要的概念之一。
1.2 對象的接口
?????? 當我們進行面向對象的程序設計時,面臨的最大一項挑戰性就是:如何在“問題空間”(問題實際存在的地方)的元素與“方案空間”(對實際問題進行建模的地方,如計算機)的元素之間建立理想的“一對一”對應或映射關系。
?????? 如何利用對象完成真正有用的工作呢?必須有一種辦法能向對象發出請求,令其做一些實際的事情,比如完成一次交易、在屏幕上畫一些東西或者打開一個開關等等。每個對象僅能接受特定的請求。我們向對象發出的請求是通過它的“接口”(Interface)定義的,對象的“類型”或“類”則規定了它的接口形式。“類型”與“接口”的等價或對應關系是面向對象程序設計的基礎。
?????? 以電燈泡為例:
?????? 在這個例子中,類型/類的名稱是 Light,可向 Light 對象發出的請求包括包括打開(on)、關閉(off)、變得更明亮(brighten)或者變得更暗淡(dim)。通過簡單地聲明一個名字(lt),我們為 Light 對象創建了一個“句柄”[莫星燦2]?。然后用new關鍵字新建類型為 Light 的一個對象。再用等號將其賦給句柄。為了向對象發送一條消息,我們列出句柄名(lt),再用一個句點符號(.)把它同消息名稱(on)連接起來。[莫星燦3]?從中可以看出,使用一些預先定義好的類時,我們在程序里采用的代碼是非常簡單和直觀的。
1.3 實現方案的隱藏
?????? 從根本上說,大致有兩方面的人員涉足面向對象的編程:“類創建者”(創建新數據類型的人)以及“客戶程序員”[莫星燦4]?(在自己的應用程序中采用現成數據類型的人)。
?????? “接口”(Interface)規定了可對一個特定的對象發出哪些請求。然而,必須在某個地方存在著一些代碼,以便滿足這些請求。這些代碼與那些隱藏起來的數據便叫作“隱藏的實現”。站在程式化程序編寫(Procedural Programming)的角度,整個問題并不顯得復雜。一種類型含有與每種可能的請求關聯起來的函數。一旦向對象發出一個特定的請求,就會調用那個函數。我們通常將這個過程總結為向對象“發送一條消息”(提出一個請求)。對象的職責就是決定如何對這條消息作出反應(執行相應的代碼)。
?????? Java采用三個顯式(明確)關鍵字以及一個隱式(暗示)關鍵字來設置類邊界:public,private,protected 以及暗示性的friendly[莫星燦5]?。若未明確指定其他關鍵字,則默認為后者。這些關鍵字的使用和含義都是相當直觀的,它們決定了誰能使用后續的定義內容。“public”(公共)意味著后續的定義任何人均可使用。而在另一方面,“private”(私有)意味著除您自己、類型的創建者以及那個類型的內部函數成員,其他任何人都不能訪問后續的定義信息。private在您與客戶程序員之間豎起了一堵墻。若有人試圖訪問私有成員,就會得到一個編譯期錯誤。“friendly”(友好的)涉及“包裝”或“封裝”(Package)的概念——即Java 用來構建庫的方法。若某樣東西是“友好的”,意味著它只能在這個包裝的范圍內使用(所以這一訪問級別有時也叫作“包裝訪問”)。“protected”(受保護的)與“private”相似,只是一個繼承的類可訪問受保護的成員,但不能訪問私有成員。繼承的問題不久就要談到。
1.4 方案的重復使用
?????? 許多人認為代碼或設計方案的重復使用是面向對象的程序設計提供的最偉大的一種杠桿。
?????? 為重復使用一個類,最簡單的辦法是僅直接使用那個類的對象。但同時也能將那個類的一個對象置入一個新類。我們把這叫作“創建一個成員對象”。新類可由任意數量和類型的其他對象構成。無論如何,只要新類達到了設計要求即可。這個概念叫作“組織”——在現有類的基礎上組織一個新類。有時,我們也將組織稱作“包含”關系,比如“一輛車包含了一個變速箱”。
[莫星燦6]?1.5 繼承:重新使用接口
?????? 使用繼承時,相當于創建了一個新類。這個新類不僅包含了現有類型的所有成員(盡管private 成員被隱藏起來,且不能訪問),但更重要的是,它復制了基礎類的接口。也就是說,可向基礎類的對象發送的所有消息亦可原樣發給衍生類的對象。根據可以發送的消息,我們能知道類的類型。這意味著衍生類具有與基礎類相同的類型!為真正理解面向對象程序設計的含義,首先必須認識到這種類型的等價關系。
1.5.1 改善基礎類
?????? 為改善一個函數,只需為衍生類的函數建立一個新定義即可。我們的目標是:“盡管使用的函數接口未變,但它的新版本具有不同的表現”。
1.5.2 等價與類似關系
?????? 但在許多時候,我們必須為衍生類型加入新的接口元素。所以不僅擴展了接口,也創建了一種新類型。這種新類型仍可替換成基礎類型,但這種替換并不是完美的,因為不可在基礎類里訪問新函數。我們將其稱作“類似”關系;新類型擁有舊類型的接口,但也包含了其他函數,所以不能說它們是完全等價的。
1.6 多形對象的互換使用
?????? 通常,繼承最終會以創建一系列類收場,所有類都建立在統一的接口基礎上。我們用一幅顛倒的樹形圖來闡明這一點(注釋⑤):
⑤:這兒采用了“統一記號法”,本書將主要采用這種方法。
?????? 對這樣的一系列類,我們要進行的一項重要處理就是將衍生類的對象當作基礎類的一個對象對待。這一點是非常重要的,因為它意味著我們只需編寫單一的代碼,令其忽略類型的特定細節,只與基礎類打交道。這樣一來,那些代碼就可與類型信息分開。所以更易編寫,也更易理解。此外,若通過繼承增添了一種新類型,如“三角形”,那么我們為“幾何形狀”新類型編寫的代碼會象在舊類型里一樣良好地工作。所以說程序具備了“擴展能力”,具有“擴展性”。
?????? 以上面的例子為基礎,假設我們用 Java 寫了這樣一個函數:
| Void doStuff(Shape s){ ? s.erase(); ? //… ? s.draw(); } |
?????? 這個函數可與任何“幾何形狀”(Shape)通信,所以完全獨立于它要描繪(draw)和刪除(erase)的任何特定類型的對象。如果我們在其他一些程序里使用 doStuff()函數:
?????? 那么對doStuff()的調用會自動良好地工作,無論對象的具體類型是什么。
?????? 這實際是一個非常有用的編程技巧。請考慮下面這行代碼:
?????? doStuff(c);
?????? 此時,一個 Circle(圓)句柄傳遞給一個本來期待 Shape(形狀)句柄的函數。由于圓是一種幾何形狀,所以doStuff()能正確地進行處理。也就是說,凡是 doStuff()能發給一個 Shape的消息,Circle也能接收。所以這樣做是安全的,不會造成錯誤。
?????? 我們將這種把衍生類型當作它的基本類型處理的過程叫作“Upcasting”(上溯造型[莫星燦7]?)。其中,“cast”(造型)是指根據一個現成的模型創建;而“Up”(向上)表明繼承的方向是從“上面”來的——即基礎類位于頂部,而衍生類在下方展開。所以,根據基礎類進行造型就是一個從上面繼承的過程,即“Upcasting”。在面向對象的程序里,通常都要用到上溯造型技術。這是避免去調查準確類型的一個好辦法。請看看doStuff()里的代碼:
| s.erase(); // ... s.draw(); |
?????? 注意它并未這樣表達:“如果你是一個Circle,就這樣做;如果你是一個Square,就那樣做;等等”。若那樣編寫代碼,就需檢查一個Shape 所有可能的類型,如圓、矩形等等。這顯然是非常麻煩的,而且每次添加了一種新的 Shape類型后,都要相應地進行修改。在這兒,我們只需說:“你是一種幾何形狀,我知道你能將自己刪掉,即erase();請自己采取那個行動,并自己去控制所有的細節吧。”
1.6.1 動態綁定
?????? 將一條消息發給對象時,如果并不知道對方的具體類型是什么,但采取的行動同樣是正確的,這種情況就叫作“多形性”(Polymorphism)。對面向對象的程序設計語言來說,它們用以實現多形性的方法叫作“動態綁定[莫星燦8]?”。編譯器和運行期系統會負責對所有細節的控制;我們只需知道會發生什么事情,而且更重要的是,如何利用它幫助自己設計程序。
1.6.2 抽象的基礎類和接口
?????? 設計程序時,我們經常都希望基礎類只為自己的衍生類提供一個接口。也就是說,我們不想其他任何人實際創建基礎類的一個對象,只對上溯造型成它,以便使用它們的接口。[莫星燦9]?為達到這個目的,需要把那個類變成“抽象”的——使用abstract 關鍵字。若有人試圖創建抽象類的一個對象,編譯器就會阻止他們。這種工具可有效強制實行一種特殊的設計。
?????? 亦可用abstract 關鍵字描述一個尚未實現的方法——作為一個“根”使用,指出:“這是適用于從這個類繼承的所有類型的一個接口函數,但目前尚沒有對它進行任何形式的實現。”抽象方法也許只能在一個抽象類里創建。繼承了一個類后,那個方法就必須實現,否則繼承的類也會變成“抽象”類。通過創建一個抽象方法,我們可以將一個方法置入接口中,不必再為那個方法提供可能毫無意義的主體代碼。
?????? interface(接口)關鍵字將抽象類的概念更延伸了一步,它完全禁止了所有的函數定義。“接口”是一種相當有效和常用的工具。另外如果自己愿意,亦可將多個接口都合并到一起(不能從多個普通class 或abstract class 中繼承)。
1.7 對象的創建和存在時間
從技術角度說,OOP(面向對象程序設計)只是涉及抽象的數據類型、繼承以及多形性,但另一些問題也可能顯得非常重要。本節將就這些問題進行探討。
最重要的問題之一是對象的創建及破壞方式。對象需要的數據位于哪兒,如何控制對象的“存在時間”呢?針對這個問題,解決的方案是各異其趣的。
第二個方法是在一個內存池中動態創建對象,該內存池亦叫“堆”或者“內存堆”。若采用這種方式,除非進入運行期,否則根本不知道到底需要多少個對象,也不知道它們的存在時間有多長,以及準確的類型是什么。這些參數都在程序正式運行時才決定的。若需一個新對象,只需在需要它的時候在內存堆里簡單地創建它即可。由于存儲空間的管理是運行期間動態進行的,所以在內存堆里分配存儲空間的時間比在堆棧里創建的時間長得多(在堆棧里創建存儲空間一般只需要一個簡單的指令,將堆棧指針向下或向下移動即可)。由于動態創建方法使對象本來就傾向于復雜,所以查找存儲空間以及釋放它所需的額外開銷不會為對象的創建造成明顯的影響。除此以外,更大的靈活性對于常規編程問題的解決是至關重要的。
1.7.1 集合與繼承器
針對一個特定問題的解決,如果事先不知道需要多少個對象,或者它們的持續時間有多長,那么也不知道如何保存那些對象。既然如此,怎樣才能知道那些對象要求多少空間呢?事先上根本無法提前知道,除非進入運行期。
“繼續器”(Iterator),它屬于一種對象,負責選擇集合內的元素,并把它們提供給繼
承器的用戶。作為一個類,它也提供了一級抽象。利用這一級抽象,可將集合細節與用于訪問那個集合的代碼隔離開。通過繼承器的作用,集合被抽象成一個簡單的序列。繼承器允許我們遍歷那個序列,同時毋需關心基礎結構是什么——換言之,不管它是一個矢量、一個鏈接列表、一個堆棧,還是其他什么東西。這樣一來,我們就可以靈活地改變基礎數據,不會對程序里的代碼造成干擾。
1.7.2 單根結構
在面向對象的程序設計中,由于C++的引入而顯得尤為突出的一個問題是:所有類最終是否都應從單獨一個基礎類繼承。在Java 中(與其他幾乎所有OOP語言一樣),對這個問題的答案都是肯定的,而且這個終級基礎類的名字很簡單,就是一個“Object[莫星燦10]?”。
?????? 單根結構中的所有對象都有一個通用接口,所以它們最終都屬于相同的類型。
?????? 單根結構中的所有對象(比如所有 Java 對象)都可以保證擁有一些特定的功能。
?????? 利用單根結構,我們可以更方便地實現一個垃圾收集器。與此有關的必要支持可安裝于基礎類中,而垃圾收集器可將適當的消息發給系統內的任何對象。如果沒有這種單根結構,而且系統通過一個句柄來操縱對象,那么實現垃圾收集器的途徑會有很大的不同,而且會面臨許多障礙。
1.7.3 集合庫與方便使用集合
由于集合是我們經常都要用到的一種工具,所以一個集合庫是十分必要的,它應該可以方便地重復使用。這樣一來,我們就可以方便地取用各種集合,將其插入自己的程序。Java 提供了這樣的一個庫,盡管它在Java1.0和 1.1中都顯得非常有限。
1. 下溯造型與模板/通用性
為了使這些集合能夠重復使用,或者“再生”,Java 提供了一種通用類型,以前曾把它叫作“Object”。單根結構意味著、所有東西歸根結底都是一個對象”!所以容納了Object 的一個集合實際可以容納任何東西。這使我們對它的重復使用變得非常簡便。
為使用這樣的一個集合,只需添加指向它的對象句柄即可,以后可以通過句柄重新使用對象。但由于集合只能容納Object,所以在我們向集合里添加對象句柄時,它會上溯造型成 Object,這樣便丟失了它的身份或者標識信息。再次使用它的時候,會得到一個Object 句柄,而非指向我們早先置入的那個類型的句柄。所以怎樣才能歸還它的本來面貌,調用早先置入集合的那個對象的有用接口呢?
在這里,我們再次用到了造型(Cast)。但這一次不是在分級結構中上溯造型成一種更“通用”的類型。而是下溯造型成一種更“特殊”的類型。這種造型方法叫作“下溯造型[莫星燦11]?”(Downcasting)。舉個例子來說,我們知道在上溯造型的時候,Circle(圓)屬于Shape(幾何形狀)的一種類型,所以上溯造型是安全的。但我們不知道一個Object到底是 Circle 還是Shape,所以很難保證下溯造型的安全進行,除非確切地知道自己要操作的是什么。
1.7.4 清除時的困境:由誰負責清除?
在Java 中,垃圾收集器在設計時已考慮到了內存的釋放問題(盡管這并不包括清除一個對象涉及到的其他方面)。垃圾收集器“知道”一個對象在什么時候不再使用,然后會自動釋放那個對象占據的內存空間。采用這種方式,另外加上所有對象都從單個根類Object 繼承的事實,而且由于我們只能在內存堆中以一種方式創建對象,所以Java 的編程要比 C++的編程簡單得多。我們只需要作出少量的抉擇,即可克服原先存在的大量障礙。
1.????垃圾收集器對效率及靈活性的影響
既然這是如此好的一種手段,為什么在C++里沒有得到充分的發揮呢?我們當然要為這種編程的方便性付出
一定的代價,代價就是運行期的開銷。正如早先提到的那樣,在C++中,我們可在堆棧中創建對象。在這種情況下,對象會得以自動清除(但不具有在運行期間隨心所欲創建對象的靈活性)。在堆棧中創建對象是為對象分配存儲空間最有效的一種方式,也是釋放那些空間最有效的一種方式。在內存堆(Heap)中創建對象可能要付出昂貴得多的代價。如果總是從同一個基礎類繼承,并使所有函數調用都具有“同質多形”特征,那么也不可避免地需要付出一定的代價。但垃圾收集器是一種特殊的問題,因為我們永遠不能確定它什么時候啟動或者要花多長的時間。這意味著在Java 程序執行期間,存在著一種不連貫的因素。所以在某些特殊的場合,我們必須避免用它——比如在一個程序的執行必須保持穩定、連貫的時候(通常把它們叫作“實時程序”,盡管并不是所有實時編程問題都要這方面的要求)。
1.8 違例控制:解決錯誤
“違例控制”將錯誤控制方案內置到程序設計語言中,有時甚至內建到操作系統內。這里的“違例”(Exception)屬于一個特殊的對象,它會從產生錯誤的地方“扔”或“擲”出來。隨后,這個違例會被設計用于控制特定類型錯誤的“違例控制器”捕獲。在情況變得不對勁的時候,可能有幾個違例控制器并行捕獲對應的違例對象。由于采用的是獨立的執行路徑,所以不會干擾我們的常規執行代碼。這樣便使代碼的編寫變得更加簡單,因為不必經常性強制檢查代碼。除此以外,“擲”出的一個違例不同于從函數返回的錯誤值,也不同于由函數設置的一個標志。那些錯誤值或標志的作用是指示一個錯誤狀態,是可以忽略的。但違例不能被忽略,所以肯定能在某個地方得到處置。最后,利用違例能夠可靠地從一個糟糕的環境中恢復。此時一般不需要退出,我們可以采取某些處理,恢復程序的正常執行。顯然,這樣編制出來的程序顯得更加可靠。[莫星燦12]?
1.9 多線程
有些時候,中斷對那些實時性很強的任務來說是很有必要的。但還存在其他許多問題,它們只要求將問題劃分進入獨立運行的程序片斷中,使整個程序能更迅速地響應用戶的請求。在一個程序中,這些獨立運行的片斷叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理”。多線程處理一個常見的例子就是用戶界面。利用線程,用戶可按下一個按鈕,然后程序會立即作出響應,而不是讓用戶等待程序完成了當前任務以后才開始響應。
最開始,線程只是用于分配單個處理器的處理時間的一種工具。但假如操作系統本身支持多個處理器,那么每個線程都可分配給一個不同的處理器,真正進入“并行運算”狀態。從程序設計語言的角度看,多線程操作最有價值的特性之一就是程序員不必關心到底使用了多少個處理器。程序在邏輯意義上被分割為數個線程;假如機器本身安裝了多個處理器,那么程序會運行得更快,毋需作出任何特殊的調校。根據前面的論述,大家可能感覺線程處理非常簡單。但必須注意一個問題:共享資源!如果有多個線程同時運行,而且它們試圖訪問相同的資源,就會遇到一個問題。舉個例子來說,兩個進程不能將信息同時發送給一臺打印機。為解決這個問題,對那些可共享的資源來說(比如打印機),它們在使用期間必須進入鎖定狀態。所以一個線程可將資源鎖定,在完成了它的任務后,再解開(釋放)這個鎖,使其他線程可以接著使用同樣的資源。
Java 的多線程機制已內建到語言中,這使一個可能較復雜的問題變得簡單起來。對多線程處理的支持是在對象這一級支持的,所以一個執行線程可表達為一個對象。Java 也提供了有限的資源鎖定方案。它能鎖定任何對象占用的內存(內存實際是多種共享資源的一種),所以同一時間只能有一個線程使用特定的內存空間。為達到這個目的,需要使用synchronized關鍵字。其他類型的資源必須由程序員明確鎖定,這通常要求程序員創建一個對象,用它代表一把鎖,所有線程在訪問那個資源時都必須檢查這把鎖。
1.10 永久性
Java 1.1 提供了對“有限永久性”的支持,這意味著我們可將對象簡單地保存到磁盤上,以后任何時間都可取回。之所以稱它為“有限”的,是由于我們仍然需要明確發出調用,進行對象的保存和取回工作。這些工作不能自動進行。在Java 未來的版本中,對“永久性”的支持有望更加全面。
1.11 Java 和因特網
Java 除了可解決傳統的程序設計問題以外,還能解決World Wide Web(萬維網)上的編程問題。
1.11.1 什么是 Web ?
1. 客戶機/服務器計算
客戶機/服務器系統的基本思想是我們能在一個統一的地方集中存放信息資源。一般將數據集中保存在某個數據庫中,根據其他人或者機器的請求將信息投遞給對方。客戶機/服務器概述的一個關鍵在于信息是“集中存放”的。所以我們能方便地更改信息,然后將修改過的信息發放給信息的消費者。將各種元素集中到一起,信息倉庫、用于投遞信息的軟件以及信息及軟件所在的那臺機器,它們聯合起來便叫作“服務器”(Server)。而對那些駐留在遠程機器上的軟件,它們需要與服務器通信,取回信息,進行適當的處理,然后在遠程機器上顯示出來,這些就叫作“客戶”(Client)。[莫星燦13]?
這樣看來,客戶機/服務器的基本概念并不復雜。這里要注意的一個主要問題是單個服務器需要同時向多個客戶提供服務。在這一機制中,通常少不了一套數據庫管理系統,使設計人員能將數據布局封裝到表格中,以獲得最優的使用。除此以外,系統經常允許客戶將新信息插入一個服務器。這意味著必須確保客戶的新數據不會與其他客戶的新數據沖突,或者說需要保證那些數據在加入數據庫的時候不會丟失(用數據庫的術語來說,這叫作“事務處理”)。客戶軟件發生了改變之后,它們必須在客戶機器上構建、調試以及安裝。所有這些會使問題變得比我們一般想象的復雜得多。另外,對多種類型的計算機和操作系統的支持也是一個大問題。最后,性能的問題顯得尤為重要:可能會有數百個客戶同時向服務器發出請求。所以任何微小的延誤都是不能忽視的。為盡可能緩解潛伏的問題,程序員需要謹慎地分散任務的處理負擔。一般可以考慮讓客戶機負擔部分處理任務,但有時亦可分派給服務器所在地的其他機器,那些機器亦叫作“中間件[莫星燦14]?”(中間件也用于改進對系統的維護)。
2. Web是一個巨大的服務器
Web實際就是一套規模巨大的客戶機/服務器系統。但它的情況要復雜一些,因為所有服務器和客戶都同時存在于單個網絡上面。但我們沒必要了解更進一步的細節,因為唯一要關心的就是一次建立同一個服務器的連接,并同它打交道(即使可能要在全世界的范圍內搜索正確的服務器)。
最開始的時候,這是一個簡單的單向操作過程。我們向一個服務器發出請求,它向我們回傳一個文件,由于本機的瀏覽器軟件(亦即“客戶”或“客戶程序”)負責解釋和格式化,并在我們面前的屏幕上正確地顯示出來。但人們不久就不滿足于只從一個服務器傳遞網頁。他們希望獲得完全的客戶機/服務器能力,使客戶(程序)也能反饋一些信息到服務器。比如希望對服務器上的數據庫進行檢索,向服務器添加新信息,或者下一份訂單等等(這也提供了比以前的系統更高的安全要求)。[莫星燦15]?在Web的發展過程中,我們可以很清晰地看出這些令人心喜的變化。
Web瀏覽器的發展終于邁出了重要的一步:某個信息可在任何類型的計算機上顯示出來,毋需任何改動。然而,瀏覽器仍然顯得很原始,在用戶迅速增多的要求面前顯得有些力不從心。它們的交互能力不夠強,而且對服務器和因特網都造成了一定程度的干擾。這是由于每次采取一些要求編程的操作時,必須將信息反饋回服務器,在服務器那一端進行處理。所以完全可能需要等待數秒乃至數分鐘的時間才會發現自己剛才拼錯了一個單詞。由于瀏覽器只是一個純粹的查看程序,所以連最簡單的計算任務都不能進行(當然在另一方面,它也顯得非常安全,因為不能在本機上面執行任何程序,避開了程序錯誤或者病毒的騷擾)。
為解決這個問題,人們采取了許多不同的方法。最開始的時候,人們對圖形標準進行了改進,使瀏覽器能顯示更好的動畫和視頻。為解決剩下的問題,唯一的辦法就是在客戶端(瀏覽器)內運行程序。這就叫作“客戶端編程”,它是對傳統的“服務器端編程”的一個非常重要的拓展。[莫星燦16]?
?[莫星燦1]OOP: 面向對象程序設計
?[莫星燦2]終于知道句柄是什么鬼了!
?[莫星燦3]調用方法的原理
?[莫星燦4]不太理解
?[莫星燦5]較形象地理解這四個關鍵字
?[莫星燦6]同感
?[莫星燦7]也叫向上轉型
?[莫星燦8]跟“什么人說什么話”道理類似
?[莫星燦9]使用抽象的原因
[莫星燦10]所有類都繼承Object
?[莫星燦11]向下轉型
?[莫星燦12]Exception Try{ }catch{ }的理解
?[莫星燦13]形象生動的詮釋何為服務器和客戶,BC端
?[莫星燦14]大公司如阿里這種應該對這個很有研究了
?[莫星燦15]早期瀏覽器形態
?[莫星燦16]B/S端
總結
以上是生活随笔為你收集整理的《Java编程思想》《Think in Java》笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android分享功能,微博、QQ、QQ
- 下一篇: 图片上传时即时生成多个缩略图