Java基礎知識篇 面向對象和麵向過程的區別 面向過程: 優點:性能比面向對象高,因為類調用時需要實例化,開銷比較大,比較消耗資源;比如單片機、嵌入式開發、Linux/Unix等一般採用面向過程開發,性能是最重要的因素。 缺點:沒有面向對象易維護、易復用、易擴展 面向對象: 優點:易維護、易復用、易 ...
Java基礎知識篇
面向對象和麵向過程的區別
面向過程:
優點:性能比面向對象高,因為類調用時需要實例化,開銷比較大,比較消耗資源;比如單片機、嵌入式開發、Linux/Unix等一般採用面向過程開發,性能是最重要的因素。
缺點:沒有面向對象易維護、易復用、易擴展
面向對象:
優點:易維護、易復用、易擴展,由於面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易於維護
缺點:性能比面向過程低
Java語言有哪些特點?
1,簡單易學;2,面向對象(封裝,繼承,多態);3,平臺無關性(Java虛擬機實現平臺無關性);4,可靠性;5,安全性;6,支持多線程(C++語言沒有內置的多線程機制,因此必須調用操作系統的多線程功能來進行多線程
程式設計,而Java語言卻提供了多線程支持);7,支持網路編程並且很方便(Java語言誕生本身就是為簡化網路編程設計的,因此Java語言不僅支持網路編程而且很方便);8,編譯與解釋並存;
什麼是位元組碼?採用位元組碼的最大好處是什麼?什麼Java是虛擬機?
先看下java中的編譯器和解釋器:
Java中引入了虛擬機的概念,即在機器和編譯程式之間加入了一層抽象的虛擬的機器。這台虛擬的機器在任何平臺上都提供給編譯程式一個的共同的介面。編譯程式只需要面向虛擬機,生成虛擬機能夠理解的代碼,然後由解釋器來將虛擬機代碼轉換為特定系統的機器碼執行。在Java中,這種供虛擬機理解的代碼叫做位元組碼(即擴展名為.class的文件),它不面向任何特定的處理器,只面向虛擬機。每一種平臺的解釋器是不同的,但是實現的虛擬機是相同的。Java源程式經過編譯器編譯後變成位元組碼,位元組碼由虛擬機解釋執行,虛擬機將每一條要執行的位元組碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,然後在特定的機器上運行,這就是上面提到的Java的特點的編譯與解釋並存的解釋。
Java源代碼---->編譯器---->jvm可執行的Java位元組碼(即虛擬指令)---->jvm---->jvm中解釋器----->機器可執行的二進位機器碼---->程式運行。
採用位元組碼的好處:
Java語言通過位元組碼的方式,在一定程度上解決了傳統解釋型語言執行效率低的問題,同時又保留瞭解釋型語言可移植的特點。所以Java程式運行時比較高效,而且,由於位元組碼並不專對一種特定的機器,因此,Java程式無須重新編譯便可在多種不同的電腦上運行。
什麼是Java虛擬機
任何一種可以運行Java位元組碼的軟體均可看成是Java的虛擬機(JVM)
什麼是Java程式的主類?應用程式和小程式的主類有何不同?
一個程式中可以有多個類,但只能有一個類是主類。在Java應用程式中,這個主類是指包含main()方法的類。而在Java小程式中,這個主類是一個繼承自系統類JApplet或Applet的子類。應用程式的主類不一定要求是public類,但小程式的主類要求必須是public類。主類是Java程式執行的入口點。
什麼是JDK?什麼是JRE?
JDK: 顧名思義它是給開發者提供的開發工具箱,是給程式開發者用的。它除了包括完整的JRE(Java Runtime Environment),Java運行環境,還包含了其他供開發者使用的工具包。
JRE:普通用戶而只需要安裝JRE(Java Runtime Environment)來 來運行Java程式。而程式開發者必須安裝JDK來編譯、調試程式。
環境變數Path和ClassPath的作用是什麼?如何設置這兩個環境變數?
Java環境變數PATH和CLASSPATH - 簡書 http://www.jianshu.com/p/d63b099cf283
Java應用程式與小程式之間有那些差別?
簡單說應用程式是從主線程啟動(也就是main()方法)。applet小程式沒有main方法,主要是嵌在瀏覽器頁面上運行(調用init()線程或者run()來啟動),嵌入瀏覽器這點跟flash的小游戲類似。
字元型常量和字元串常量的區別
1) 形式上:
字元常量是單引號引起的一個字元
字元串常量是雙引號引起的若幹個字元
2) 含義上:
字元常量相當於一個整形值(ASCII值),可以參加表達式運算
字元串常量代表一個地址值(該字元串在記憶體中存放位置)
3) 占記憶體大小
字元常量只占一個位元組
字元串常量占若幹個位元組(至少一個字元結束標誌)
Java語言採用何種編碼方案?有何特點?
Java語言採用Unicode編碼標準,Unicode(標準碼),它為每個字元制訂了一個唯一的數值,因此在任何的語言,平臺,程式都可以放心的使用。
構造器Constructor是否可被override
在講繼承的時候我們就知道父類的私有屬性和構造方法並不能被繼承,所以Constructor也就不能被override,但是可以overload,所以你可以看到一個類中有多個構造函數的情況。
重載和重寫的區別
重載:發生在同一個類中,方法名必須相同,參數類型不同、個數不同、順序不同,方法返回值和訪問修飾符可以不同,發生在編譯時。
重寫:發生在父子類中,方法名、參數列表必須相同,返回值小於等於父類,拋出的異常小於等於父類,訪問修飾符大於等於父類;如果父類方法訪問修飾符為private則子類中就不是重寫。
java 面向對象編程三大特性------封裝、繼承、多態
https://blog.csdn.net/jianyuerensheng/article/details/51602015
String和StringBuffer、StringBuilder的區別是什麼?String為什麼是不可變的?
可變性
String類中使用字元數組保存字元串,private final char value[],所以string對象是不可變的。StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元數組保存字元串,char[]value,這兩種對象都是可變的。
線程安全性
String中的對象是不可變的,也就可以理解為常量,線程安全。AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字元串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,所以是線程安全的。StringBuilder並沒有對方法進行加同步鎖,所以是非線程安全的。
性能
每次對String 類型進行改變的時候,都會生成一個新的String對象,然後將指針指向新的String 對象。StringBuffer每次都會對StringBuffer對象本身進行操作,而不是生成新的對象並改變對象引用。相同情況下使用StirngBuilder 相比使用StringBuffer 僅能獲得10%~15% 左右的性能提升,但卻要冒多線程不安全的風險。
對於三者使用的總結:
如果要操作少量的數據用 = String
單線程操作字元串緩衝區 下操作大量數據 = StringBuilder
多線程操作字元串緩衝區 下操作大量數據 = StringBuffer
自動裝箱與拆箱
裝箱:將基本類型用它們對應的引用類型包裝起來;
拆箱:將包裝類型轉換為基本數據類型;
Java使用自動裝箱和拆箱機制,節省了常用數值的記憶體開銷和創建對象的開銷,提高了效率,由編譯器來完成,編譯器會在編譯期根據語法決定是否進行裝箱和拆箱動作。
類、方法、成員變數和局部變數的可用修飾符 -
http://blog.csdn.net/yttcjj/article/details/6939239
在一個靜態方法內調用一個非靜態成員為什麼是非法的?
由於靜態方法可以不通過對象進行調用,因此在靜態方法里,不能調用其他非靜態變數,也不可以訪問非靜態變數成員。
在Java中定義一個不做事且沒有參數的構造方法的作用
Java程式在執行子類的構造方法之前,如果沒有用super()來調用父類特定的構造方法,則會調用父類中“沒有參數的構造方法”。因此,如果父類中只定義了有參數的構造方法,而在子類的構造方法中又沒有用super()來調用父類中特定的構造方法,則編譯時將發生錯誤,因為Java程式在父類中找不到沒有參數的構造方法可供執行。解決辦法是在父類裡加上一個不做事且沒有參數的構造方法。
import java和javax有什麼區別
http://www.cnblogs.com/EasonJim/p/6993139.html
介面和抽象類的區別是什麼?
1.介面的方法預設是public,所有方法在介面中不能有實現,抽象類可以有非抽象的方法
2.介面中的實例變數預設是final類型的,而抽象類中則不一定
3.一個類可以實現多個介面,但最多只能實現一個抽象類
4.一個類實現介面的話要實現介面的所有方法,而抽象類不一定
5.介面不能用new實例化,但可以聲明,但是必須引用一個實現該介面的對象
從設計層面來說,抽象是對類的抽象,是一種模板設計,介面是行為的抽象,是一種行為的規範。
成員變數與局部變數的區別有那些?
-
從語法形式上,看成員變數是屬於類的,而局部變數是在方法中定義的變數或是方法的參數;成員變數可以被public,private,static等修飾符所修飾,而局部變數不能被訪問控制修飾符及static所修飾;成員變數和局部變數都能被final所修飾;
-
從變數在記憶體中的存儲方式來看,成員變數是對象的一部分,而對象存在於堆記憶體,局部變數存在於棧記憶體
-
從變數在記憶體中的生存時間上看,成員變數是對象的一部分,它隨著對象的創建而存在,而局部變數隨著方法的調用而自動消失。
-
成員變數如果沒有被賦初值,則會自動以類型的預設值而賦值(一種情況例外被final修飾但沒有被static修飾的成員變數必須顯示地賦值);而局部變數則不會自動賦值。
創建一個對象用什麼運算符?對象實體與對象引用有何不同?
new運算符,new創建對象實例(對象實例在堆記憶體中),對象引用指向對象實例(對象引用存放在棧記憶體中)。一個對象引用可以指向0個或1個對象(一根繩子可以不系氣球,也可以系一個氣球);一個對象可以有n個引用指向它(可以用n條繩子系住一個氣球)
什麼是方法的返回值?返回值在類的方法里的作用是什麼?
方法的返回值是指我們獲取到的某個方法體中的代碼執行後產生的結果!(前提是該方法可能產生結果)。返回值的作用:接收出結果,使得它可以用於其他的操作!
一個類的構造方法的作用是什麼?若一個類沒有聲明構造方法,改程式能正確執行嗎?為什麼?
主要作用是完成對類對象的初始化工作。可以執行。因為一個類即使沒有聲明構造方法也會有預設的不帶參數的構造方法。
構造方法有哪些特性?
1,名字與類名相同;2,沒有返回值,但不能用void聲明構造函數;3,生成類的對象時自動執行,無需調用。
靜態方法和實例方法有何不同?
靜態方法和實例方法的區別主要體現在兩個方面:
-
在外部調用靜態方法時,可以使用"類名.方法名"的方式,也可以使用"對象名.方法名"的方式。而實例方法只有後面這種方式。也就是說,調用靜態方法可以無需創建對象。
-
靜態方法在訪問本類的成員時,只允許訪問靜態成員(即靜態成員變數和靜態方法),而不允許訪問實例成員變數和實例方法;實例方法則無此限制
對象的相等與指向他們的引用相等,兩者有什麼不同?
對象的相等 比的是記憶體中存放的內容是否相等而 引用相等 比較的是他們指向的記憶體地址是否相等。
在調用子類構造方法之前會先調用父類沒有參數的構造方法,其目的是?
幫助子類做初始化工作。
什麼是多態機制?Java語言是如何實現多態的?
http://blog.csdn.net/bornlili/article/details/55213563
equals 和 == 的區別?
通俗點講:==是看看左右是不是一個東西。equals是看看左右是不是長得一樣。如何記住嘛。如果單純是想記住,==:等於。equals:相同。兩個長得一樣的人,只能說長的相同(equals),但是不等於他們倆是一個人。你只要記住equals,==就不用記了。
術語來講的區別:1.==是判斷兩個變數或實例是不是指向同一個記憶體空間 equals是判斷兩個變數或實例所指向的記憶體空間的值是不是相同
2.==是指對記憶體地址進行比較 equals()是對字元串的內容進行比較3.==指引用是否相同 equals()指的是值是否相同
創建線程有幾種不同的方式?你喜歡哪一種?為什麼?
- 繼承Thread類
- 實現Runnable介面
- 應用程式可以使用Executor框架來創建線程池
- 實現Callable介面。
我更喜歡實現Runnable介面這種方法,當然這也是現在大多程式員會選用的方法。因為一個類只能繼承一個父類而可以實現多個介面。同時,線程池也是非常高效的,很容易實現和使用。
簡述線程,程式、進程的基本概念。以及他們之間關係是什麼?(參考書籍:《Java程式設計基礎》第五版)
線程與進程相似,但線程是一個比進程更小的執行單位。一個進程在其執行的過程中可以產生多個線程。與進程不同的是同類的多個線程共用同一塊記憶體空間和一組系統資源,所以系統在產生一個線程,或是在各個線程之間作切換工作時,負擔要比進程小得多,也正因為如此,線程也被稱為輕量級進程。
程式是含有指令和數據的文件,被存儲在磁碟或其他的數據存儲設備中,也就是說程式是靜態的代碼。
進程是程式的一次執行過程,是系統運行程式的基本單位,因此進程是動態的。系統運行一個程式即是一個進程從創建,運行到消亡的過程。簡單來說,一個進程就是一個執行中的程式,它在電腦中一個指令接著一個指令地執行著,同時,每個進程還占有某些系統資源如CPU時間,記憶體空間,文件,文件,輸入輸出設備的使用權等等。換句話說,當程式在執行時,將會被操作系統載入記憶體中。
線程是進程劃分成的更小的運行單位。線程和進程最大的不同在於基本上各進程是獨立的,而各線程則不一定,因為同一進程中的線程極有可能會相互影響。從另一角度來說,進程屬於操作系統的範疇,主要是同一段時間內,可以同時執行一個以上的程式,而線程則是在同一程式內幾乎同時執行一個以上的程式段。
什麼是多線程?為什麼程式的多線程功能是必要的?
多線程就是幾乎同時執行多個線程(一個處理器在某一個時間點上永遠都只能是一個線程!即使這個處理器是多核的,除非有多個處理器才能實現多個線程同時運行。)。幾乎同時是因為實際上多線程程式中的多個線程實際上是一個線程執行一會然後其他的線程再執行,並不是很多書籍所謂的同時執行。這樣可以帶來以下的好處:
- 使用線程可以把占據長時間的程式中的任務放到後臺去處理
- 用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度
- 程式的運行速度可能加快
- 在一些等待的任務實現上如用戶輸入、文件讀寫和網路收發數據等,線程就比較有用了。在這種情況下可以釋放一些珍貴的資源如記憶體占用等等。
還有其他很多使用多線程的好處,這裡就不一一說明瞭。
多線程與多任務的差異是什麼?(參考書籍:《Java程式設計基礎》第五版)
多任務與多線程是兩個不同的概念,
多任務是針對操作系統而言的,表示操作系統可以同時運行多個應用程式。
而多線程是針對一個進程而言的,表示在一個進程內部可以幾乎同時執行多個線程
線程有哪些基本狀態?這些狀態是如何定義的?
- 新建(new):新創建了一個線程對象。
- 可運行(runnable):線程對象創建後,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲 取cpu的使用權。
- 運行(running):可運行狀態(runnable)的線程獲得了cpu時間片(timeslice),執行程式代碼。
- 阻塞(block):阻塞狀態是指線程因為某種原因放棄了cpu使用權,也即讓出了cpu timeslice,暫時停止運行。直到線程進入可運行(runnable)狀態,才有 機會再次獲得cpu timeslice轉到運行(running)狀態。阻塞的情況分三種:
(一). 等待阻塞:運行(running)的線程執行o.wait()方法,JVM會把該線程放 入等待隊列(waitting queue)中。
(二). 同步阻塞:運行(running)的線程在獲取對象的同步鎖時,若該同步鎖 被別的線程占用,則JVM會把該線程放入鎖池(lock pool)中。
(三). 其他阻塞: 運行(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入可運行(runnable)狀態。 - 死亡(dead):線程run()、main()方法執行結束,或者因異常退出了run()方法,則該線程結束生命周期。死亡的線程不可再次復生。
備註:
可以用早起坐地鐵來比喻這個過程:
還沒起床:sleeping
起床收拾好了,隨時可以坐地鐵出發:Runnable
等地鐵來:Waiting
地鐵來了,但要排隊上地鐵:I/O阻塞
上了地鐵,發現暫時沒座位:synchronized阻塞
地鐵上找到座位:Running
到達目的地:Dead
什麼是線程的同步?程式中為什麼要實現線程的同步?是如何實現同步的?
當一個線程對共用的數據進行操作時,應使之成為一個”原子操作“,即在沒有完成相關操作之前,不允許其他線程打斷它,否則,就會破壞數據的完整性,必然會得到錯誤的處理結果,這就是線程的同步。
在多線程應用中,考慮不同線程之間的數據同步和防止死鎖。當兩個或多個線程之間同時等待對方釋放資源的時候就會形成線程之間的死鎖。為了防止死鎖的發生,需要通過同步來實現線程安全。
在監視器(Monitor)內部,是如何做線程同步的?程式應該做哪種級別的同步?
在 java 虛擬機中, 每個對象( Object 和 class )通過某種邏輯關聯監視器,每個監視器和一個對象引用相關聯, 為了實現監視器的互斥功能, 每個對象都關聯著一把鎖.
一旦方法或者代碼塊被 synchronized 修飾, 那麼這個部分就放入了監視器的監視區域, 確保一次只能有一個線程執行該部分的代碼, 線程在獲取鎖之前不允許執行該部分的代碼
另外 java 還提供了顯式監視器( Lock )和隱式監視器( synchronized )兩種鎖方案
什麼是死鎖(deadlock)?
死鎖 :是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去 。
產生原因:
- 因為系統資源不足。
- 進程運行推進順序不合適。
- 資源分配不當等。
- 占用資源的程式崩潰等。
如果系統資源充足,進程的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則就會因爭奪有限的資源而陷入死鎖。其次,進程運行推進順序與速度不同,也可能產生死鎖。
下麵四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要下列條件之一不滿足,就不會發生死鎖。
- 互斥條件:一個資源每次只能被一個進程使用。
- 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
- 迴圈等待條件:若幹進程之間形成一種頭尾相接的迴圈等待資源關係。
死鎖的解除與預防:
理解了死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和 解除死鎖。所以,在系統設計、進程調度等方面註意如何不讓這四個必要條件成立,如何確 定資源的合理分配演算法,避免進程永久占據系統資源。此外,也要防止進程在處於等待狀態的情況下占用資源。因此,對資源的分配要給予合理的規劃。
如何確保N個線程可以訪問N個資源同時又不導致死鎖?
上面一題我們知道了發生死鎖的四個必要條件。我們只要使其中一個不成立就行了。一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序,並強制線程按照指定的順序獲取鎖。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖,就不會出現死鎖了。這也就是破壞了第四個條件迴圈等待條件。
Java中垃圾回收有什麼目的?什麼時候進行垃圾回收?
垃圾回收是在記憶體中存在沒有引用的對象或超過作用域的對象時進行。
垃圾回收的目的是識別並且丟棄應用不再使用的對象來釋放和重用資源。
finalize()方法什麼時候被調用?析構函數(finalization)的目的是什麼?
1)垃圾回收器(garbage colector)決定回收某對象時,就會運行該對象的finalize()方法;
finalize是Object類的一個方法,該方法在Object類中的聲明protected void finalize() throws Throwable { }
在垃圾回收器執行時會調用被回收對象的finalize()方法,可以覆蓋此方法來實現對其資源的回收。註意:一旦垃圾回收器準備釋放對象占用的記憶體,將首先調用該對象的finalize()方法,並且下一次垃圾回收動作發生時,才真正回收對象占用的記憶體空間
2)GC本來就是記憶體回收了,應用還需要在finalization做什麼呢? 答案是大部分時候,什麼都不用做(也就是不需要重載)。只有在某些很特殊的情況下,比如你調用了一些native的方法(一般是C寫的),可以要在finaliztion里去調用C的釋放函數。
如果對象的引用被置為null,垃圾收集器是否會立即釋放對象占用的記憶體?
不會,在下一個垃圾回收周期中,這個對象將是可被回收的。
JavaWeb基礎知識總結
Servlet總結:
在Java Web程式中,Servlet主要負責接收用戶請求HttpServletRequest,在doGet(),doPost()中做相應的處理,並將回應HttpServletResponse反饋給用戶。Servlet可以設置初始化參數,供Servlet內部使用。一個Servlet類只會有一個實例,在它初始化時調用init()方法,銷毀時調用destroy()方法。Servlet需要在web.xml中配置(MyEclipse中創建Servlet會自動配置),一個Servlet可以設置多個URL訪問。Servlet不是線程安全,因此要謹慎使用類變數。
闡述Servlet和CGI的區別?
CGI的不足之處:
1,需要為每個請求啟動一個操作CGI程式的系統進程。如果請求頻繁,這將會帶來很大的開銷。
2,需要為每個請求載入和運行一個CGI程式,這將帶來很大的開銷
3,需要重覆編寫處理網路協議的代碼以及編碼,這些工作都是非常耗時的。
Servlet的優點:
1,只需要啟動一個操作系統進程以及載入一個JVM,大大降低了系統的開銷
2,如果多個請求需要做同樣處理的時候,這時候只需要載入一個類,這也大大降低了開銷
3,所有動態載入的類可以實現對網路協議以及請求解碼的共用,大大降低了工作量。
4,Servlet能直接和Web伺服器交互,而普通的CGI程式不能。Servlet還能在各個程式之間共用數據,使資料庫連接池之類的功能很容易實現。
補充:Sun Microsystems公司在1996年發佈Servlet技術就是為了和CGI進行競爭,Servlet是一個特殊的Java程式,一個基於Java的Web應用通常包含一個或多個Servlet類。Servlet不能夠自行創建並執行,它是在Servlet容器中運行的,容器將用戶的請求傳遞給Servlet程式,並將Servlet的響應回傳給用戶。通常一個Servlet會關聯一個或多個JSP頁面。以前CGI經常因為性能開銷上的問題被詬病,然而Fast CGI早就已經解決了CGI效率上的問題,所以面試的時候大可不必信口開河的詬病CGI,事實上有很多你熟悉的網站都使用了CGI技術。
參考:《javaweb整合開發王者歸來》P7
Servlet介面中有哪些方法及Servlet生命周期探秘
Servlet介面定義了5個方法,其中前三個方法與Servlet生命周期相關:
- void init(ServletConfig config) throws ServletException
- void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
- void destory()
- java.lang.String getServletInfo()
- ServletConfig getServletConfig()
生命周期: Web容器載入Servlet並將其實例化後,Servlet生命周期開始,容器運行其init()方法進行Servlet的初始化;請求到達時調用Servlet的service()方法,service()方法會根據需要調用與請求對應的doGet或doPost等方法;當伺服器關閉或項目被卸載時伺服器會將Servlet實例銷毀,此時會調用Servlet的destroy()方法。init方法和destory方法只會執行一次,service方法客戶端每次請求Servlet都會執行。Servlet中有時會用到一些需要初始化與銷毀的資源,因此可以把初始化資源的代碼放入init方法中,銷毀資源的代碼放入destroy方法中,這樣就不需要每次處理客戶端的請求都要初始化與銷毀資源。
參考:《javaweb整合開發王者歸來》P81
get和post請求的區別?
①get請求用來從伺服器上獲得資源,而post是用來向伺服器提交數據;
②get將表單中數據按照name=value的形式,添加到action 所指向的URL 後面,並且兩者使用"?"連接,而各個變數之間使用"&"連接;post是將表單中的數據放在HTTP協議的請求頭或消息體中,傳遞到action所指向URL;
③get傳輸的數據要受到URL長度限制(1024位元組即256個字元);而post可以傳輸大量的數據,上傳文件通常要使用post方式;
④使用get時參數會顯示在地址欄上,如果這些數據不是敏感數據,那麼可以使用get;對於敏感數據還是應用使用post;
⑤get使用MIME類型application/x-www-form-urlencoded的URL編碼(也叫百分號編碼)文本的格式傳遞參數,保證被傳送的參數由遵循規範的文本組成,例如一個空格的編碼是"%20"。
補充:GET方式提交表單的典型應用是搜索引擎。GET方式就是被設計為查詢用的。
什麼情況下調用doGet()和doPost()?
Form標簽里的method的屬性為get時調用doGet(),為post時調用doPost()。
轉發(Forward)和重定向(Redirect)的區別?
轉發是伺服器行為,重定向是客戶端行為。
轉發(Forword)
通過RequestDispatcher對象的forward(HttpServletRequest request,HttpServletResponse response)方法實現的。RequestDispatcher可以通過HttpServletRequest 的getRequestDispatcher()方法獲得。例如下麵的代碼就是跳轉到login_success.jsp頁面。
request.getRequestDispatcher("login_success.jsp").forward(request, response);
重定向(Redirect) 是利用伺服器返回的狀態嗎來實現的。客戶端瀏覽器請求伺服器的時候,伺服器會返回一個狀態碼。伺服器通過HttpServletRequestResponse的setStatus(int status)方法設置狀態碼。如果伺服器返回301或者302,則瀏覽器會到新的網址重新請求該資源。
- 從地址欄顯示來說
forward是伺服器請求資源,伺服器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然後把這些內容再發給瀏覽器.瀏覽器根本不知道伺服器發送的內容從哪裡來的,所以它的地址欄還是原來的地址.
redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以地址欄顯示的是新的URL.
- 從數據共用來說
forward:轉發頁面和轉發到的頁面可以共用request裡面的數據.
redirect:不能共用數據.
- 從運用地方來說
forward:一般用於用戶登陸的時候,根據角色轉發到相應的模塊.
redirect:一般用於用戶註銷登陸時返回主頁面和跳轉到其它的網站等
- 從效率來說
forward:高.
redirect:低.
自動刷新(Refresh)
自動刷新不僅可以實現一段時間之後自動跳轉到另一個頁面,還可以實現一段時間之後自動刷新本頁面。Servlet中通過HttpServletResponse對象設置Header屬性實現自動刷新例如:
Response.setHeader("Refresh","1000;URL=http://localhost:8080/servlet/example.htm");
其中1000為時間,單位為毫秒。URL指定就是要跳轉的頁面(如果設置自己的路徑,就會實現沒過一秒自動刷新本頁面一次)
Servlet與線程安全
Servlet不是線程安全的,多線程併發的讀寫會導致數據不同步的問題。 解決的辦法是儘量不要定義name屬性,而是要把name變數分別定義在doGet()和doPost()方法內。雖然使用synchronized(name){}語句塊可以解決問題,但是會造成線程的等待,不是很科學的辦法。
註意:多線程的併發的讀寫Servlet類屬性會導致數據不同步。但是如果只是併發地讀取屬性而不寫入,則不存在數據不同步的問題。因此Servlet里的只讀屬性最好定義為final類型的。
參考:《javaweb整合開發王者歸來》P92
JSP和Servlet是什麼關係?
其實這個問題在上面已經闡述過了,Servlet是一個特殊的Java程式,它運行於伺服器的JVM中,能夠依靠伺服器的支持向瀏覽器提供顯示內容。JSP本質上是Servlet的一種簡易形式,JSP會被伺服器處理成一個類似於Servlet的Java程式,可以簡化頁面內容的生成。Servlet和JSP最主要的不同點在於,Servlet的應用邏輯是在Java文件中,並且完全從表示層中的HTML分離開來。而JSP的情況是Java和HTML可以組合成一個擴展名為.jsp的文件。有人說,Servlet就是在Java中寫HTML,而JSP就是在HTML中寫Java代碼,當然這個說法是很片面且不夠準確的。JSP側重於視圖,Servlet更側重於控制邏輯,在MVC架構模式中,JSP適合充當視圖(view)而Servlet適合充當控制器(controller)。
JSP工作原理:
JSP是一種Servlet,但是與HttpServlet的工作方式不太一樣。HttpServlet是先由源代碼編譯為class文件後部署到伺服器下,為先編譯後部署。而JSP則是先部署後編譯。JSP會在客戶端第一次請求JSP文件時被編譯為HttpJspPage類(介面Servlet的一個子類)。該類會被伺服器臨時存放在伺服器工作目錄裡面。下麵通過實例給大家介紹。
工程JspLoginDemo下有一個名為login.jsp的Jsp文件,把工程第一次部署到伺服器上後訪問這個Jsp文件,我們發現這個目錄下多了下圖這兩個東東。
.class文件便是JSP對應的Servlet。編譯完畢後再運行class文件來響應客戶端請求。以後客戶端訪問login.jsp的時候,Tomcat將不再重新編譯JSP文件,而是直接調用class文件來響應客戶端請求。
由於JSP只會在客戶端第一次請求的時候被編譯 ,因此第一次請求JSP時會感覺比較慢,之後就會感覺快很多。如果把伺服器保存的class文件刪除,伺服器也會重新編譯JSP。
開發Web程式時經常需要修改JSP。Tomcat能夠自動檢測到JSP程式的改動。如果檢測到JSP源代碼發生了改動。Tomcat會在下次客戶端請求JSP時重新編譯JSP,而不需要重啟Tomcat。這種自動檢測功能是預設開啟的,檢測改動會消耗少量的時間,在部署Web應用的時候可以在web.xml中將它關掉。
參考:《javaweb整合開發王者歸來》P97
JSP有哪些內置對象?作用分別是什麼?
JSP有9個內置對象:
- request:封裝客戶端的請求,其中包含來自GET或POST請求的參數;
- response:封裝伺服器對客戶端的響應;
- pageContext:通過該對象可以獲取其他對象;
- session:封裝用戶會話的對象;
- application:封裝伺服器運行環境的對象;
- out:輸出伺服器響應的輸出流對象;
- config:Web應用的配置對象;
- page:JSP頁面本身(相當於Java程式中的this);
- exception:封裝頁面拋出異常的對象。
Request對象的主要方法有哪些?
- setAttribute(String name,Object):設置名字為name的request 的參數值
- getAttribute(String name):返回由name指定的屬性值
- getAttributeNames():返回request 對象所有屬性的名字集合,結果是一個枚舉的實例
- getCookies():返回客戶端的所有 Cookie 對象,結果是一個Cookie 數組
- getCharacterEncoding() :返回請求中的字元編碼方式 = getContentLength() :返回請求的 Body的長度
- getHeader(String name) :獲得HTTP協議定義的文件頭信息
- getHeaders(String name) :返回指定名字的request Header 的所有值,結果是一個枚舉的實例
- getHeaderNames() :返回所以request Header 的名字,結果是一個枚舉的實例
- getInputStream() :返回請求的輸入流,用於獲得請求中的數據
- getMethod() :獲得客戶端向伺服器端傳送數據的方法
- getParameter(String name) :獲得客戶端傳送給伺服器端的有 name指定的參數值
- getParameterNames() :獲得客戶端傳送給伺服器端的所有參數的名字,結果是一個枚舉的實例
- getParameterValues(String name):獲得有name指定的參數的所有值
- getProtocol():獲取客戶端向伺服器端傳送數據所依據的協議名稱
- getQueryString() :獲得查詢字元串
- getRequestURI() :獲取發出請求字元串的客戶端地址
- getRemoteAddr():獲取客戶端的 IP 地址
- getRemoteHost() :獲取客戶端的名字
- getSession([Boolean create]) :返回和請求相關 Session
- getServerName() :獲取伺服器的名字
- getServletPath():獲取客戶端所請求的腳本文件的路徑
- getServerPort():獲取伺服器的埠號
- removeAttribute(String name):刪除請求中的一個屬性
request.getAttribute()和 request.getParameter()有何區別?
從獲取方向來看:
getParameter()是獲取 POST/GET 傳遞的參數值;
getAttribute()是獲取對象容器中的數據值;
從用途來看:
getParameter用於客戶端重定向時,即點擊了鏈接或提交按扭時傳值用,即用於在用表單或url重定向傳值時接收數據用。
getAttribute用於伺服器端重定向時,即在 sevlet 中使用了 forward 函數,或 struts 中使用了
mapping.findForward。 getAttribute 只能收到程式用 setAttribute 傳過來的值。
另外,可以用 setAttribute,getAttribute 發送接收對象.而 getParameter 顯然只能傳字元串。
setAttribute 是應用伺服器把這個對象放在該頁面所對應的一塊記憶體中去,當你的頁面伺服器重定向到另一個頁面時,應用伺服器會把這塊記憶體拷貝另一個頁面所對應的記憶體中。這樣getAttribute就能取得你所設下的值,當然這種方法可以傳對象。session也一樣,只是對象在記憶體中的生命周期不一樣而已。getParameter只是應用伺服器在分析你送上來的 request頁面的文本時,取得你設在表單或 url 重定向時的值。
總結:
getParameter 返回的是String,用於讀取提交的表單中的值;(獲取之後會根據實際需要轉換為自己需要的相應類型,比如整型,日期類型啊等等)
getAttribute 返回的是Object,需進行轉換,可用setAttribute 設置成任意對象,使用很靈活,可隨時用
include指令include的行為的區別
include指令: JSP可以通過include指令來包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是該JSP文件的一部分,會被同時編譯執行。 語法格式如下:
<%@ include file="文件相對 url 地址" %>
include動作: 動作元素用來包含靜態和動態的文件。該動作把指定文件插入正在生成的頁面。語法格式如下:
JSP九大內置對象,七大動作,三大指令-
講解JSP中的四種作用域
JSP中的四種作用域包括page、request、session和application,具體來說:
- page代表與一個頁面相關的對象和屬性。
- request代表與Web客戶機發出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個Web組件;需要在頁面顯示的臨時數據可以置於此作用域。
- session代表與某個用戶與伺服器建立的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶自己的session中。
- application代表與整個Web應用程式相關的對象和屬性,它實質上是跨越整個Web應用程式,包括多個頁面、請求和會話的一個全局作用域。
如何實現JSP或Servlet的單線程模式?
對於JSP頁面,可以通過page指令進行設置。
<%@page isThreadSafe=”false”%>
對於Servlet,可以讓自定義的Servlet實現SingleThreadModel標識介面。
說明:如果將JSP或Servlet設置成單線程工作模式,會導致每個請求創建一個Servlet實例,這種實踐將導致嚴重的性能問題(伺服器的記憶體壓力很大,還會導致頻繁的垃圾回收),所以通常情況下並不會這麼做。
實現會話跟蹤的技術有哪些?
- 使用Cookie
向客戶端發送Cookie
- Cookie c =new Cookie("name","value"); //創建Cookie
- c.setMaxAge(60*60*24); //設置最大時效,此處設置的最大時效為一天
- response.addCookie(c); //把Cookie放入到HTTP響應中
從客戶端讀取Cookie
- String name ="name";
- Cookie[]cookies =request.getCookies();
- if(cookies !=null){
- for(int i= 0;i<cookies.length;i++){
- Cookie cookie =cookies[i];
- if(name.equals(cookis.getName()))
- //something is here.
- //you can get the value
- cookie.getValue();
- }
- }
優點: 數據可以持久保存,不需要伺服器資源,簡單,基於文本的Key-Value
缺點: 大小受到限制,用戶可以禁用Cookie功能,由於保存在本地,有一定的安全風險。
- URL 重寫
在URL中添加用戶會話的信息作為請求的參數,或者將唯一的會話ID添加到URL結尾以標識一個會話。
優點: 在Cookie被禁用的時候依然可以使用
缺點: 必須對網站的URL進行編碼,所有頁面必須動態生成,不能用預先記錄下來的URL進行訪問。
3.隱藏的表單域
<input type="hidden" name ="session" value="..."/>
優點: Cookie被禁時可以使用
缺點: 所有頁面必須是表單提交之後的結果。
- HttpSession
在所有會話跟蹤技術中,HttpSession對象是最強大也是功能最多的。當一個用戶第一次訪問某個網站時會自動創建 HttpSession,每個用戶可以訪問他自己的HttpSession。可以通過HttpServletRequest對象的getSession方 法獲得HttpSession,通過HttpSession的setAttribute方法可以將一個值放在HttpSession中,通過調用 HttpSession對象的getAttribute方法,同時傳入屬性名就可以獲取保存在HttpSession中的對象。與上面三種方式不同的 是,HttpSession放在伺服器的記憶體中,因此不要將過大的對象放在裡面,即使目前的Servlet容器可以在記憶體將滿時將HttpSession 中的對象移到其他存儲設備中,但是這樣勢必影響性能。添加到HttpSession中的值可以是任意Java對象,這個對象最好實現了 Serializable介面,這樣Servlet容器在必要的時候可以將其序列化到文件中,否則在序列化時就會出現異常。
Cookie和Session的的區別?
- 由於HTTP協議是無狀態的協議,所以服務端需要記錄用戶的狀態時,就需要用某種機制來識具體的用戶,這個機制就是Session.典型的場景比如購物車,當你點擊下單按鈕時,由於HTTP協議無狀態,所以並不知道是哪個用戶操作的,所以服務端要為特定的用戶創建了特定的Session,用用於標識這個用戶,並且跟蹤用戶,這樣才知道購物車裡面有幾本書。這個Session是保存在服務端的,有一個唯一標識。在服務端保存Session的方法很多,記憶體、資料庫、文件都有。集群的時候也要考慮Session的轉移,在大型的網站,一般會有專門的Session伺服器集群,用來保存用戶會話,這個時候 Session 信息都是放在記憶體的,使用一些緩存服務比如Memcached之類的來放 Session。
- 思考一下服務端如何識別特定的客戶?這個時候Cookie就登場了。每次HTTP請求的時候,客戶端都會發送相應的Cookie信息到服務端。實際上大多數的應用都是用 Cookie 來實現Session跟蹤的,第一次創建Session的時候,服務端會在HTTP協議中告訴客戶端,需要在 Cookie 裡面記錄一個Session ID,以後每次請求把這個會話ID發送到伺服器,我就知道你是誰了。有人問,如果客戶端的瀏覽器禁用了 Cookie 怎麼辦?一般這種情況下,會使用一種叫做URL重寫的技術來進行會話跟蹤,即每次HTTP交互,URL後面都會被附加上一個諸如 sid=xxxxx 這樣的參數,服務端據此來識別用戶。
- Cookie其實還可以用在一些方便用戶的場景下,設想你某次登陸過一個網站,下次登錄的時候不想再次輸入賬號了,怎麼辦?這個信息可以寫到Cookie裡面,訪問網站的時候,網站頁面的腳本可以讀取這個信息,就自動幫你把用戶名給填了,能夠方便一下用戶。這也是Cookie名稱的由來,給用戶的一點甜頭。所以,總結一下:Session是在服務端保存的一個數據結構,用來跟蹤用戶的狀態,這個數據可以保存在集群、資料庫、文件中;Cookie是客戶端保存用戶信息的一種機制,用來記錄用戶的一些信息,也是實現Session的一種方式。
Java集合篇常見問題
List,Set,Map三者的區別及總結
-
List:對付順序的好幫手
List介面存儲一組不唯一(可以有多個元素引用相同的對象),有序的對象 -
Set:註重獨一無二的性質
不允許重覆的集合。不會有多個元素引用相同的對象。
-
Map:用Key來搜索的專家
使用鍵值對存儲。Map會維護與Key有關聯的值。兩個Key可以引用相同的對象,但Key不能重覆,典型的Key是String類型,但也可以是任何對象。
Arraylist 與 LinkedList 區別
Arraylist底層使用的是數組(存讀數據效率高,插入刪除特定位置效率低),LinkedList底層使用的是雙向迴圈鏈表數據結構(插入,刪除效率特別高)。學過數據結構這門課後我們就知道採用鏈表存儲,插入,刪除元素時間複雜度不受元素位置的影響,都是近似O(1)而數組為近似O(n),因此當數據特別多,而且經常需要插入刪除元素時建議選用LinkedList.一般程式只用Arraylist就夠用了,因為一般數據量都不會蠻大,Arraylist是使用最多的集合類。
ArrayList 與 Vector 區別(為什麼要用Arraylist取代Vector呢?)
Vector類的所有方法都是同步的。可以由兩個線程安全地訪問一個Vector對象、但是一個線程訪問Vector
,代碼要在同步操作上耗費大量的時間。Arraylist不是同步的,所以在不需要同步時建議使用Arraylist。
HashMap 和 Hashtable 的區別
-
HashMap是非線程安全的,HashTable是線程安全的;HashTable內部的方法基本都經過synchronized修飾。
- 因為線程安全的問題,HashMap要比HashTable效率高一點,HashTable基本被淘汰。
-
HashMap允許有null值的存在,而在HashTable中put進的鍵值只要有一個null,直接拋出NullPointerException。
Hashtable和HashMap有幾個主要的不同:線程安全以及速度。僅在你需要完全的線程安全的時候使用Hashtable,而如果你使用Java5或以上的話,請使用ConcurrentHashMap吧
HashSet 和 HashMap 區別
HashMap 和 ConcurrentHashMap 的區別
- ConcurrentHashMap對整個桶數組進行了分割分段(Segment),然後在每一個分段上都用lock鎖進行保護,相對於HashTable的synchronized鎖的粒度更精細了一些,併發性能更好,而HashMap沒有鎖機制,不是線程安全的。(JDK1.8之後ConcurrentHashMap啟用了一種全新的方式實現,利用CAS演算法。)
- HashMap的鍵值對允許有null,但是ConCurrentHashMap都不允許。
HashSet如何檢查重覆
當你把對象加入HashSet時,HashSet會先計算對象的hashcode值來判斷對象加入的位置,同時也會與其他加入的對象的hashcode值作比較,如果沒有相符的hashcode,HashSet會假設對象沒有重覆出現。但是如果發現有相同hashcode值的對象,這時會調用equals()方法來檢查hashcode相等的對象是否真的相同。如果兩者相同,HashSet就不會讓加入操作成功。(摘自我的Java啟蒙書《Head fist java》第二版)
hashCode()與equals()的相關規定:
- 如果兩個對象相等,則hashcode一定也是相同的
- 兩個對象相等,對兩個equals方法返回true
- 兩個對象有相同的hashcode值,它們也不一定是相等的
- 綜上,equals方法被覆蓋過,則hashCode方法也必須被覆蓋
- hashCode()的預設行為是對堆上的對象產生獨特值。如果沒有重寫hashCode(),則該class的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)。
==與equals的區別
- ==是判斷兩個變數或實例是不是指向同一個記憶體空間 equals是判斷兩個變數或實例所指向的記憶體空間的值是不是相同
- ==是指對記憶體地址進行比較 equals()是對字元串的內容進行比較3.==指引用是否相同 equals()指的是值是否相同
comparable 和 comparator的區別?
- comparable介面實際上是出自java.lang包 它有一個 compareTo(Object obj)方法用來排序
- comparator介面實際上是出自 java.util 包它有一個compare(Object obj1, Object obj2)方法用來排序
一般我們需要對一個集合使用自定義排序時,我們就要重寫compareTo方法或compare方法,當我們需要對某一個集合實現兩種排序方式,比如一個song對象中的歌名和歌手名分別採用一種排序方法的話,我們可以重寫compareTo方法和使用自製的Comparator方法或者以兩個Comparator來實現歌名排序和歌星名排序,第二種代表我們只能使用兩個參數版的Collections.sort().
Comparator定製排序
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Comparator;
- public class CollectionsSort {
- public static void main(String[] args) {
- ArrayList<Integer> arrayList = new ArrayList<Integer>();
- arrayList.add(-1);
- arrayList.add(3);
-