Java面試手冊:核心基礎-1

面向對象設計原則

1.單一職責原則

- 在面向對象設計中,分工理論就是單一職責原則(Single Pesponsibility Prineiple, SRP)
- 兩個含義
  - 避免相同的職責分散到不同的類中
  - 避免一個類承擔太多職責
- 爲什麼要遵循單一設計原則
  - 可以減少類之間的耦合:當需求變化時,只修改一個類,從而隔離了變化。
  - 提高類的複用性
  - 單一職責使得組件可以方便的拆卸和組裝
- 應用:用工廠模式來實現不同數據庫操作類。
- 工廠模式(Factory): 允許你在代碼執行時實例化對象。之所以被稱爲工廠模式,是因爲它負責“生產”對象。
- 一些應該遵循的做法:
  - 根據業務流程,把業務對象提煉出來,如果業務流程鏈路太複雜,就把這個業務對象分離爲多個單一業務對象。
    當業務鏈標準化後,對業務對象的內部情況做進一部處理。把第一次標準化視爲最高層抽象,第二次視爲次高層抽象,以此類推,直到“恰如其分”的設計層次。
  - 職責的分類需要注意。有業務職責,還要脫離業務的抽象職責,從認識業務到抽象算法是一個層層遞進的過程。
    就好比命令模式中的顧客,服務員和廚師的職責,作爲老闆的你需要規劃好各自的職責範圍,既要防止越俎代庖,也要防止互相推諉。

2.接口隔離原則

  設計應用程序的時候,如果一個模塊包含多個子模塊,那麼我們應該小心對該模塊做出抽象。
- 接口隔離原則(Interface Segregation Principle, ISP): 客戶端不應該被強迫實現不會使用的接口,應該把胖接口中的方法分組,然後用多個接口代替它,每個接口服務於一個子模塊。簡單的說,就是使用多個專門的接口比使用單個接口要好得多。
- 接口隔離原則的主要觀點
  - 一個類對另外一個類的依賴性應當是建立在最小的接口上
    - ISP 可以達到不強迫客戶依賴於他們不使用的方法,接口的實現類應該只呈現爲單一職責的角色(遵守SRP原則)。
    - ISP 還可降低客戶端之間的相互影響——當某個客戶程序要求提供新的職責(需求變更)而迫使接口發生改變時,影響到其他客戶程序的可能性會是最小的。
  - 客戶端程序不應該依賴它不需要的接口方法
    - ISP強調的是接口對客戶端的承諾越少越好,並且要做到專一。
  - 接口污染:過於臃腫的接口設計是對接口的污染。接口污染就是爲接口添加不必要的職責,如果開發人員在接口中增加一個新功能的主要目的只是減少接口實現類的數目,則此設計導致接口被不斷的“污染” 並 “變胖”,圖一是胖接口,圖二是使用隔離原則。

3.開放 - 封閉原則

 - 開放:模塊的行爲必須是開放的、支持擴展的,而不是僵化的。
 - 關閉:在模塊的功能進行擴展時,不應該影響或大規模影響已有的程序模塊。
 - 一個模塊在擴展性方面應該是開放的,在更改性方面應該是封閉的。
 - 該原則的**核心是想是對抽象編程,而不是具體編程**,因爲抽象相對穩定。
   讓類依賴於固定的抽象,這樣的修改就是封閉的,通過面向對象的繼承和多態機制,可以實現對抽象體的繼承,
   通過覆寫其方法改變固有行爲,實現新的擴展方法,所以對於擴展就是開放的。
 - 在設計方面充分應用 抽象 和 封裝 的思想。
 - 在系統功能編程實現方面應用面向接口的編程。

4.替換原則 :里氏替換原則(Liskov Substiution Principle, LSP)定義以及主要思想:子類型必須能夠替換掉它們的父類型、並出現在父類能夠出現的任何地方。

 - 解決如果正確進行繼承設計和合理地應用繼承機制:
   - 如何正確地進行繼承方面的設計?
   - 最佳的繼承層次如何獲得?
   - 怎樣避免所設計的類層次陷入不符合OCP原則的狀況?
 - 如何遵守替換原則:
   - 父類的方法都要在子類中實現或者重寫, 派生類只實現其抽象類中聲明的方法, 而不應當給出多餘的方法定義或實現。
   - 在客戶端程序中只應該出現父類對象,而不是直接使用子類對象, 這樣可以實現運行期綁定(多態綁定)。

5.依賴倒置原則:依賴倒置的核心原則是解耦,如果脫離這個最原始的原則,那就是本末倒置。

- 將依賴關係倒置爲依賴接口:
   - 上層模塊不應該依賴下層模塊, 它們共同依賴於一個抽象。
   - 抽象不能依賴於具體, 具體應該要依賴於抽象
- IOC(Inversion of Control) 是依賴倒置原則(Dependence Inversion Principle, DIP)的同義詞。
   - DI: 依賴注入
   - DS: 依賴查找
- 如何滿足DIP:
   - 每個較高層次類都爲它所需要的服務提出一個接口聲明, 較低層次實現這個接口
   - 每個高層類都通過該抽象接口使用服務

6.深拷貝和淺拷貝

簡單來講就是複製、克隆;Person p=new Person(“張三”);

淺拷貝就是對對象中的數據成員進行簡單賦值,如果存在動態成員或者指針就會報錯

深拷貝就是對對象中存在的動態成員或指針重新開闢內存空間

7.值傳遞和引用傳遞

值傳遞是針對基本數據類型而言,傳遞的是值得副本,對副本的改變不會影響到原變量

引用傳遞:就是將一個堆內存空間的使用權交給多個棧內存空間,每一個棧內存空間都可以對堆內存空間進行修改

8.web 容器功能

通信支持、管理Servlet生命週期,多線程、將jsp轉換成java等等

9.java內存分配

寄存器:我們無法控制

靜態域:static定義的靜態成員

常量池:編譯時被確定並保存在.class文件中的(final)常量值和一些文本修飾的符號引用(類和接口的全限定名,字段的名稱和描述符,方法和名稱和描述符)

非ram存儲:硬盤等永久存儲空間

堆內存:new創建的對象和數組,由java虛擬機自動垃圾回收器管理,存取速度慢

棧內存:基本類型的變量和對象的引用變量(堆內存空間的訪問地址),速度快,可以共享,但是大小與生存期必須確定,缺乏靈活性

11.一個".java"源文件中是否可以包括多個類(不是內部類)?有什麼限制?

可以有多個類,但只能有一個public的類,並且public的類名必須與文件名相一致。

12.Java有沒有goto?

java中的保留字,現在沒有在java中使用。

13.簡述邏輯操作(&,|,^)與條件操作(&&,||)的區別

條件操作只能操作布爾型的,而邏輯操作不僅可以操作布爾型,而且可以操作數值型

邏輯操作不會產生短路.

使用邏輯操作符時,我們會遇到一種“短路”現象。即一旦能夠明確無誤地確定整個表達式的值,就不再計算表達式餘下部分了。因此,整個邏輯表達式靠後的部分有可能不會被運算

14.說說&和&&的區別。

  • ==相同點==:&和&&都可以用作邏輯與的運算符,表示邏輯與(and),當運算符兩邊的表達式的結果都爲true時,整個運算結果才爲true,否則,只要有一方爲false,則結果爲false。
  • ==&&具有短路功能==:即如果第一個表達式爲false,則不再計算第二個表達式。
  • 舉個列子1:對於if(str != null && !str.equals(“”))表達式,當str爲null時,後面的表達式不會執行,所以不會出現NullPointerException;如果將&&改爲&,則會拋出NullPointerException異常。
  • 再比如: If(x== 33 & ++y>0) y會增長,If(x==33 && ++y>0) Y不會增長。
  • ==&可以用作位運算符==:當&操作符兩邊的表達式不是boolean類型時,&表示按位與操作,我們通常使用0x0f來與一個整數進行&運算,來獲取該整數的最低4個bit位,例如,0x31 & 0x0f的結果爲0x01。
  • 按位與: 即二進制對應的位置,如果同時爲1,那麼即爲1,否則爲0 。
  • 備註:這道題先說兩者的共同點,再說出&&和&的特殊之處,並列舉一些經典的例子來表明自己理解透徹深入、實際經驗豐富。

15.switch語句能否作用在byte上,能否作用在long上,能否作用在String上?

  • 在switch(expr1)中,expr1只能是一個整數表達式或者枚舉常量(更大字體),整數表達式可以是int基本類型或Integer包裝類型,由於,byte,short,char都可以隱含轉換爲int,所以,這些類型以及這些類型的包裝類型也是可以的。long不符合switch的語法規定,並且不能被隱式轉換成int類型,所以,它不能作用於swtich語句中。
  • 對於string而言:JDK1.7以前是不能作爲switch的,以後即可以作用於switch中。

16.short s1 = 1; s1 = s1 + 1;有什麼錯? short s1 = 1; s1 += 1;有什麼錯?

  • 注意數據類型的轉換:由於s1+1運算時會自動提升表達式的類型,所以結果是int型,再賦值給short類型s1時,編譯器將報告需要強制轉換類型的錯誤。
  • 對於short s1 = 1; s1 += 1;由於 += 是java語言規定的運算符,java編譯器會對它進行特殊處理,因此可以正確編譯。

17.char型變量中能不能存貯一箇中文漢字?爲什麼?

  • char型變量是用來存儲Unicode編碼的字符的,unicode編碼字符集中包含了漢字,所以,char型變量中當然可以存儲漢字。
  • 不過要注意的是:
  • 如果某個特殊的漢字沒有被包含在unicode編碼字符集中,那麼,這個char型變量中就不能存儲這個特殊漢字
  • unicode編碼佔用兩個字節,所以,char類型的變量也是佔用兩個字節。

18.用最有效率的方法算出2乘以8等於幾?

  • 方法:將一個數左移n位,就相當於乘以了2的n次方,那麼,一個數乘以8(2的3次方),只要將其左移3位即可,而位運算cpu直接支持的,效率最高,所以,2乘以8等於幾的最效率的方法是將2左移3位,即2 << 3。

19.使用final關鍵字修飾一個變量時,是引用不能變,還是引用的對象不能變?

  • 使用final關鍵字修飾一個變量時,是指引用變量不能變,引用變量所指向的對象中的內容還是可以改變的。例如,對於如下語句:final StringBuffer a=new StringBuffer("immutable");
  • 執行如下語句將報告編譯期錯誤:a=new StringBuffer("");
  • 但是,執行如下語句則可以通過編譯:a.append(" broken!");

java 有人在定義方法的參數時,可能想採用如下形式來阻止方法內部修改傳進來的參數對象: public void method(final StringBuffer param) { } 實際上,這是辦不到的,在該方法內部仍然可以增加如下代碼來修改參數對象: param.append("a");

20.java 中對象的創建方法有幾種?

  • 通過new來創建
  • 通過反射創建
  • 通過複製創建

21."=="和equals方法究竟有什麼區別?

  • 如果是在object對象中,那麼兩者所表示的都是值是否相等(public boolean equals(Object obj){return (this==obj); }),可以看到object的equals方法是使用的“==”比較。
  • 一般而言,要比較兩個基本類型的數據或兩個引用變量是否相等,只能用==操作符,也就是比較內存中所存儲的兩個變量的值是否相等。
  • 比較兩個對象是否相等:實際上是比較兩個堆內存的首地址是否相等,即使用的“==”(如果一個變量指向的數據是對象類型的,那麼,這時候涉及了兩塊內存,堆內存中放的是棧內存中存儲的首地址)
  • 如果比較兩個獨立對象的內容是否相等,那麼使用重寫後的equals.(string類型,data類型的equals都是重寫了object的方法)
  • 舉例:String a=new String("foo"); String b=new String("foo");它們的首地址是不同的,即a和b中存儲的數值是不相同的,所以,表達式a==b將返回false,而這兩個對象中的內容是相同的,所以,表達式a.equals(b)將返回true。
  • 在實際開發中,我們經常要比較傳遞進行來的字符串內容是否等,此時是使用的equals()方法。
  • 對於equals()而言,默認情況是從object類繼承的,當希望能夠比較該類創建的兩個實例對象的內容是否相同,那麼你必須覆蓋equals方法,由自己寫代碼來決定在什麼情況即可認爲兩個對象的內容是相同的。

22.靜態變量和實例變量的區別?

  • 在語法定義上的區別:靜態變量前要加static關鍵字,而實例變量前則不加。
  • 在程序運行時的區別:
  • 實例變量屬於某個對象的屬性,必須創建了實例對象,其中的實例變量纔會被分配空間,才能使用這個實例變量。
  • 靜態變量不屬於某個實例對象,而是屬於類,所以也稱爲類變量,只要程序加載了類的字節碼,不用創建任何實例對象,靜態變量就會被分配空間,靜態變量就可以被使用了。、
  • 總之,實例變量必須創建對象後纔可以通過這個對象來使用,靜態變量則可以直接使用類名來引用。
  • 舉例:

java 對於下面的程序,無論創建多少個實例對象,永遠都只分配了一個staticVar變量,並且每創建一個實例對象,這個staticVar就會加1; 但是,每創建一個實例對象,就會分配一個instanceVar,即可能分配多個instanceVar,並且每個instanceVar的值都只自加了1次。 public class VariantTest { public static int staticVar = 0; public int instanceVar = 0; public VariantTest() { staticVar++; instanceVar++; System.out.println(“staticVar=” + staticVar + ”,instanceVar=” + instanceVar); } }

23.是否可以從一個static方法內部發出對非static方法的調用?

  • 不可以。
  • 對於static修飾的靜態方法,是隨着類的加載而加載,且調用的時可以不用創建對象而直接調用
  • 非靜態方法要與對象聯繫在一起,只有創建了對象了以後才能調用非靜態方法,即當一個靜態方法被調用的時候,有可能還沒有創建任何實例對象。

24.Integer與int的區別

  • int是java提供的8種原始數據類型之一,系統給的默認值爲0。
  • Java爲每個原始類型提供了封裝類,Integer是java爲int提供的封裝類。系統給的默認值爲null。
  • 即Integer可以區分出未賦值和值爲0的區別,int則無法表達出未賦值的情況:
  • 在JSP開發中,Integer的默認爲null,所以用el表達式在文本框中顯示時,值爲空白字符串,而int默認的默認值爲0,用el表達式在文本框中顯示時,結果爲0,所以,int不適合作爲web層的表單數據的類型。
  • 在Hibernate中,如果將OID定義爲Integer類型,那麼Hibernate就可以根據其值是否爲null而判斷一個對象是否是臨時的,如果將OID定義爲了int類型,還需要在hbm映射文件中設置其unsaved-value屬性爲0。
  • Integer提供了多個與整數相關的操作方法,例如,將一個字符串轉換成整數,Integer中還定義了表示整數的最大值和最小值的常量。

25.Math.round(11.5)等於多少? Math.round(-11.5)等於多少?

  • Math類中提供了三個與取整有關的方法:ceil(向上取整)、floor(向下取整)、round(四捨五入)
  • 舉例:
  • Math.ceil(11.3)的結果爲12,Math.ceil(-11.3)的結果是-11
  • Math.floor(11.6)的結果爲11,Math.floor(-11.6)的結果是-12
  • 算法爲Math.floor(x+0.5),即將原來的數字加上0.5後再向下取整,所以,Math.round(11.5)的結果爲12,Math.round(-11.5)的結果爲-11。

26.下面的代碼有什麼不妥之處?

  • 1. if(username.equals(“zxx”){}; 2.int x = 1; return x==1?true:false;
  • 答:第一個問題少了一個右括號;第二個問題沒有錯誤

27.說出作用域public,private,protected,以及不寫時的區別

  • 有4種訪問權限,4個訪問範圍

28.Overload和Override的區別。Overload的方法是否可以改變返回值的類型?

  • Overload:表示方法重載,表示同一個類中可以有多個名稱相同的方法,但這些方法的參數列表各不相同(即參數個數、類型、位置不同),通過定義不同的輸入參數來區分這些方法,然後再調用時,JVM就會根據不同的參數樣式,來選擇合適的方法執行:
  • 在使用重載時只能通過不同的參數樣式。例如,不同的參數類型,不同的參數個數,不同的參數順序(當然,同一方法內的幾個參數類型必須不一樣,例如可以是fun(int,float),但是不能爲fun(int,int));
  • 不能通過訪問權限、返回類型、拋出的異常進行重載;
  • 方法的異常類型和數目不會對重載造成影響;
  • 對於繼承來說,如果某一方法在父類中是訪問權限是priavte,那麼就不能在子類對其進行重載,如果定義的話,也只是定義了一個新方法,而不會達到重載的效果。
  • Override:表示方法重寫(覆蓋),表示子類中的方法可以與父類中的某個方法的名稱和參數完全相同,當通過子類創建的實例對象調用這個方法時,將調用子類中的定義方法,這相當於把父類中定義的那個完全相同的方法給覆蓋了,這也是面向對象編程的多態性的一種表現。
  • 重寫的方法的標誌必須要和被重寫的方法的標誌完全匹配,才能達到重寫的效果;
  • 重寫的方法的返回值必須和被重寫的方法的返回一致;
  • 重寫的方法所拋出的異常必須和被重寫方法的所拋出的異常一致,或者是其子類(因爲子類是解決父類的一些方法,不能比父類更多問題);
  • 被重寫的方法不能爲private,否則在其子類中只是新定義了一個方法,並沒有對其進行重寫(子類方法的訪問權限只能比父類的更大,不能更小)。

29.同學貢獻的一些題

  • ClassLoader如何加載class 。
  • jvm裏有多個類加載,每個類加載可以負責加載特定位置的類,例如,bootstrap類加載負責加載jre/lib/rt.jar中的類, 我們平時用的jdk中的類都位於rt.jar中。extclassloader負責加載jar/lib/ext/*.jar中的類,appclassloader負責classpath指定的目錄或jar中的類。除了bootstrap之外,其他的類加載器本身也都是java類,它們的父類是ClassLoader。
  • 一個房子裏有椅子,椅子有腿和背,房子與椅子是什麼關係,椅子與腿和背是什麼關係?
  • 如果房子有多個椅子,就是聚合關係,否則是一種關聯關係,當然,聚合是一種特殊的關聯。椅子與腿和背時組合關係。
  • 說說has a與is a的區別
  • is-a表示的是屬於得關係,比如兔子屬於一種動物(繼承關係)。has-a表示組合,包含關係,比如兔子包含有腿,頭等組件;
  • Servlet的生命週期: init、 service、 destroy。

30.分層設計的好處

  • 把各個功能按調用流程進行了模塊化,模塊化帶來的好處就是可以隨意組合
  • 舉例:如果要註冊一個用戶,流程爲顯示界面並通過界面接收用戶的輸入,接着進行業務邏輯處理,在處理業務邏輯又訪問數據庫,如果我們將這些步驟全部按流水帳的方式放在一個方法中編寫,這也是可以的,但這其中的壞處就是,當界面要修改時,由於代碼全在一個方法內,可能會碰壞業務邏輯和數據庫訪問的碼,同樣,當修改業務邏輯或數據庫訪問的代碼時,也會碰壞其他部分的代碼。
  • 分層就是要把界面部分、業務邏輯部分、數據庫訪問部分的代碼放在各自獨立的方法或類中編寫,這樣就不會出現牽一髮而動全身的問題了
  • 分層的好處:
  • 實現了軟件之間的解耦;
  • 便於進行分工
  • 便於維護
  • 提高軟件組件的重用
  • 便於替換某種產品,比如持久層用的是hibernate,需要更換產品用toplink,就不用該其他業務代碼,直接把配置一改。
  • 便於產品功能的擴展。
  • 便於適用用戶需求的不斷變化

31.序列化接口的id有什麼用?

  • 對象經常要通過IO進行傳送,讓你寫程序傳遞對象,你會怎麼做?把對象的狀態數據用某種格式寫入到硬盤,Person->“zxx,male,28,30000”Person,既然大家都要這麼幹,並且沒有個統一的幹法,於是,sun公司就提出一種統一的解決方案,它會把對象變成某個格式進行輸入和輸出,這種格式對程序員來說是透明(transparent)的,但是,我們的某個類要想能被sun的這種方案處理,必須實現Serializable接口。ObjectOutputStream.writeObject(obj);<br />Object obj = ObjectInputStream.readObject();
  • 假設兩年前我保存了某個類的一個對象,這兩年來,我修改該類,刪除了某個屬性和增加了另外一個屬性,兩年後,我又去讀取那個保存的對象,或有什麼結果?未知!sun的jdk就會蒙了。爲此,一個解決辦法就是在類中增加版本後,每一次類的屬性修改,都應該把版本號升級一下,這樣,在讀取時,比較存儲對象時的版本號與當前類的版本號,如果不一致,則直接報版本號不同的錯!

32.hashCode方法的作用?

  • hashcode這個方法是用來鑑定2個對象是否相等的。
  • 與equals()方法的區別:
  • 簡單來講,equals方法主要是用來判斷從表面上看或者從內容上看,2個對象是不是相等,equals這個方法是給用戶調用的,如果你想判斷2個對象是否相等,你可以重寫equals方法,然後在代碼中調用,就可以判斷他們是否相等了
  • hashcode方法一般用戶不會去調用,比如在hashmap中,由於key是不可以重複的,他在判斷key是不是重複的時候就判斷了hashcode這個方法,而且也用到了equals方法。
  • hashcode相當於是一個對象的編碼,他和equals不同就在於他返回的是int型的,比較起來不直觀。我們一般在覆蓋equals的同時也要覆蓋hashcode,讓他們的邏輯一致。
  • ==舉例==:有個學生類,屬性只有姓名和性別,那麼我們可以認爲只要姓名和性別相等,那麼就說這2個對象是相等的。如果姓名和性別相等就算2個對象相等的話,那麼hashcode的方法也要返回姓名的hashcode值加上性別的hashcode值,這樣從邏輯上,他們就一致了。++要從物理上判斷2個對象是否相等,用==就可以了。++

33.構造器Constructor是否可被override?

  • 重寫發生在繼承過程中(子父類間),但是構造器Constructor不能被繼承,因此不能重寫Override,但可以被重載Overload。

34.接口是否可繼承接口? 抽象類是否可實現(implements)接口? 抽象類是否可繼承具體類(concrete class)? 抽象類中是否可以有靜態的main方法?

  • 接口是特殊的抽象類,他是多態的經典體現。
  • 抽象類是指具有抽象方法的類,該類中出了有抽象方法外,其他普通方法具有的屬性抽象方法都有。他們的唯一區別就是不能創建實例對象和允許有abstract方法;
  • 因此:接口可以繼承接口。抽象類可以實現(implements)接口,抽象類可繼承具體類。抽象類中可以有靜態的main方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章