Android面試之Java 基礎篇

本文是Android面試題整理中的一篇,結合右下角目錄食用更佳,包括:html

  • Java設計思想
  • 抽象類和接口
  • 類和方法
  • 內部類
  • 錯誤和異常
  • 關鍵字和運算符
  • 基本類型和經常使用類
  • 編碼
  • 其餘未分類等

Java設計思想


0. OOP是什麼

面向對象編程(Object Oriented Programming)java

1. JDK和JRE

  1. JDK:java development kit:java開發工具包,是開發人員所須要安裝的環境(包含JRE)
  2. JRE:java runtime environment:java運行環境,java程序運行所須要安裝的環境

2. 面向對象的特徵有哪些

面向對象的特徵有:抽象封裝繼承多態面試

  1. 抽象:抽象是將一類對象的共同特徵總結出來構造類的過程,包括數據抽象和行爲抽象兩方面。抽象只關注對象有哪些屬性和行爲,並不關注這些行爲的細節是什麼。
  2. 封裝:隱藏對象的實現細節,僅對外公開接口,是針對一個對象來講的
  3. 多態:多態性是指容許不一樣子類型的對象對同一消息做出不一樣的響應。簡單的說就是用一樣的對象引用調用一樣的方法可是作了不一樣的事情
  4. 繼承:繼承是從已有類獲得繼承信息建立新類的過程。提供繼承信息的類被稱爲父類(超類、基類);獲得繼承信息的類被稱爲子類(派生類)

3. java是值傳遞仍是引用傳遞

java是值傳遞。能夠理解爲傳入的是一個引用的副本,指向統一地址。當值改變時,原引用和副本指向地址中的值都變了;當副本指向的地址改變,指向新值時,原引用指向的地址沒有改變,原值也沒有改變。算法

抽象類和接口


0. 接口的意義

  1. 規範
  2. 擴展
  3. 回掉
  4. java是單繼承的

1. 抽象類的意義

  1. 爲其餘子類提供一個公共的類型
  2. 封裝子類中重複定義的內容
  3. 定義抽象方法,子類能夠有不一樣的實現

2. 抽象類和接口有什麼不一樣

  1. 單繼承:java中只能夠繼承一個類,可是能夠實現多個接口
  2. 成員變量:接口的成員變量都是public static final 的,抽象類能夠有各類類型
  3. 方法:抽象類中能夠有方法的具體實現,接口中方法都是抽象的
  4. 擴展://jdk 7 : 只能聲明全局常量(public static final)和抽象方法(public abstract) void method1(); // jdk 8 : 聲明靜態方法 和 默認方法 public static void method2(){ System.out.println("method2"); } default void method3(){ System.out.println("method3"); method4(); } //jdk 9 : 聲明私有方法 private void method4(){ System.out.println("私有方法"); }}

3. 接口是否可繼承(extends)接口?抽象類是否可實現(implements)接口?抽象類是否可繼承具體類(concrete class)?

  1. 接口能夠繼承接口,並且支持多重繼承
  2. 抽象類能夠實現(implements)接口
  3. 抽象類是否可繼承具體類,也能夠繼承抽象類

4. Java標識符命名規範

0. 規範(強制)

  1. 數字、字母、下劃線、$(java中內部類編譯後會生成包含$的類名) 組成
  2. 不能以數字開頭
  3. 不能和關鍵字或保留關鍵字相同

1. 推薦的命名方式(非強制)

  1. 方法:java中一般用小駝峯命名法
  2. 常量:一般用大寫字母,不一樣單詞間用「_」分隔開,如MOBILE_NUM
  3. 類名:大駝峯命名法

類和方法


0. 一個".java"源文件中是否能夠包含多個類(不是內部類)?有什麼限制?

一個".java"文件內能夠有多個類,但只能有一個類是公開的數據庫

1. 構造器(constructor)是否可被重寫(override)

構造器不能被繼承,所以不能被重寫,但能夠被重載編程

2. 靜態變量和成員變量的區別

  1. 靜態變量屬於類,被多個實例共享,成員變量屬於實例
  2. 靜態變量儲存在方法區,成員變量在堆
  3. 靜態變量在類加載時候存在,成員變量在實例加載以後存在
  4. 靜態方法能夠直接使用靜態變量,不能直接使用成員變量

3. Object 中定義了哪些方法

clone/toString/wait/notify/notifyAll/equals/hashcode/finalize/getClass設計模式

4. Cloneable 實現原理

  1. Cloneable是一個接口,沒有具體方法
  2. clone方法是Object類中方法,會檢查當前實例是否實現Cloneable接口,沒有實現則拋出異常,實現了就調用native方法進行clone(clone進行的是淺拷貝),源碼以下
protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }
        return internalClone();
    }
複製代碼

5. 兩個對象值相同(x.equals(y) == true),但卻可有不一樣的hash code,這句話對不對?

  1. 不對。
  2. java 規定,值相同,hashCode必定要相同;hashCode相同,值可能不一樣
  3. 若是值相同,hashCode不一樣,就會形成Hashset、HashMap等藉助hashCode實現的數據結構出現錯亂,相同的值或者key可能出現屢次

6. 如何實現對象的克隆

  1. 經過實現Cloneable接口實現clone:這裏要注意深拷貝和淺拷貝問題,若是該類內部變量是引用類型的,而且內部變量類沒有實現Cloneable接口,那麼克隆出來的該變量是淺拷貝的(只是複製了引用,兩個引用指向統一實例)
  2. 經過實現Serializable接口,經過對象的序列化和反序列化實現克隆。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MyUtil {

private MyUtil() {
throw new AssertionError();
}

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);

ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();

// 說明:調用ByteArrayInputStream或ByteArrayOutputStream對象的close方法沒有任何意義
// 這兩個基於內存的流只要垃圾回收器清理對象就可以釋放資源,這一點不一樣於對外部資源(如文件流)的釋放
}
}
複製代碼

7. 談一談」==「與」equals()"的區別

  1. == :對於基本類型,比較的是他們的值;對於引用類型,比較的是引用的值,也就是對象實例的地址
  2. equals()方法是Object類中的方法,默認實現是public boolean equals(Object obj) {return (this == obj);};咱們能夠重寫該方法達到咱們的目的,例如String重寫了該方法要求每一個字符都相等。

8. 類中方法調用順序

指出下面程序的運行結果數組

class A {

static {
System.out.print("1");
}

public A() {
System.out.print("2");
}
}

class B extends A{

static {
System.out.print("a");
}

public B() {
System.out.print("b");
}
}

public class Hello {

public static void main(String[] args) {
A ab = new B();
ab = new B();
}

}
複製代碼

執行結果:1a2b2b。 建立對象時構造器的調用順序是: 父類靜態初始化塊 -> 子類靜態初始化塊 -> 父類初始化塊 ->調用了父類構造器 -> 子類初始化塊 -> 調用子類的構造器緩存

9. 重載(Overload)和重寫(Override)的區別

重寫(Override)和重載(Overload)其實並沒有聯繫,多是由於名稱類似,容易引發混淆 > 重寫發生在運行時,重載發生在編譯期安全

重寫(Override)

重寫是針對父類和子類來講的,是在子類中重寫父類的方法。

  1. 要求方法名,參數個數和類型必須相同
  2. 返回的數據類型必須與父類相同或者是其子類
  3. 訪問修飾符的限制必定要大於父類中該方法的訪問修飾符(public>protected>default>private)
  4. 重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常

重載(Overload)

重載是針對一個類說的,是Java中多態性的一種表現

  1. 要求方法名相同
  2. 必須有不一樣的參數列表
  3. 能夠有不一樣的返回類型
  4. 能夠有不一樣的修飾符
  5. 能夠拋出不一樣的異常。
擴展:華爲的面試題中曾經問過這樣一個問題 - "爲何不能根據返回類型來區分重載"
答:由於調用時不能指定類型信息,編譯器不知道你要調用哪一個函數。例如:
float max(int a, int b);
int max(int a, int b);
當調用max(1, 2);時沒法肯定調用的是哪一個。
參考:https://www.zhihu.com/question/21455159/answer/59874307
複製代碼

10. 闡述靜態變量和實例變量的區別。

  1. 靜態變量(static 修飾的變量)屬於類,被全部類的實例共享,沒有實例時也可經過類直接訪問
  2. 實例變量:必須經過實例來訪問

11. 是否能夠從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用?

不能夠,靜態方法只能訪問靜態成員,由於非靜態方法的調用要先建立對象,在調用靜態方法時可能對象並無被初始化

12. 抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被synchronized修飾?

  1. 抽象方法不能是靜態的:靜態方法不能被子類重寫,抽象方法必須被子類重寫,衝突;
  2. 抽象方法不能是native的:本地方法是由本地代碼(如C代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的
  3. 抽象方法不能用sychronized:synchronized和方法的實現細節有關,抽象方法不涉及實現細節,所以也是相互矛盾的

13. Super與this表示什麼

Super表示當前類的父類對象;This表示當前類的對象

14. hashcode()和equals()的關係

  1. equals 相等,hashcode必定相等
  2. hashcode相等,equals不必定相等

內部類


1. 內部類的做用

  1. 內部類能夠很好的實現隱藏
  2. 內部類擁有外圍類的全部元素的訪問權限
  3. 能夠間接實現多重繼承
  4. 能夠避免修改接口而實現同一個類中兩種同名方法的調用

2. 靜態嵌套類(Static Nested Class,或者叫靜態內部類)和內部類(Inner Class)的不一樣

  1. 內部類持有外部類的引用(this),靜態內部類不持有
  2. 由於持有外部類的引用,因此new時須要先有外部類的實例,再用外部類實例new內部類實例,舉例:new Outer().new Inner();
  3. 擴展:在Android中,由於內部類持用外部類引用,因此容易形成內存泄漏,通常推薦使用靜態內部類

3. Anonymous Inner Class(匿名內部類)是否能夠繼承其它類?是否能夠實現接口?

能夠繼承其餘類,也能夠實現接口

解析:
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){

}
})

這裏new的就是一個匿名內部類,這個匿名內部類實現了View.OnClickListener接口。因此匿名內部類自己必定會繼承或實現一個且僅一個類或者接口。
複製代碼

4. 內部類(非靜態內部類)能夠引用它的包含類(外部類)的成員嗎?有沒有什麼限制?

  1. 一個內部類對象能夠訪問建立它的外部類對象的成員,包括私有成員。
  2. 應用局部變量,局部變量前要加final修飾

5. 內部類訪問局部變量的時候,爲何要加final

  1. 內部類和局部變量生命週期不一樣(方法結束後局部變量的生命週期就結束了,而內部類只要有引用就不結束,內部類的生命週期>=局部變量)
  2. Java爲了解決這一問題,會在編譯時在內部類的構造方法裏邊,將局部變量做爲參數傳入內部類
  3. 這就形成了局部變量若是改變,內部類不知情的場景,因此要加final,保證引用不可改變

擴展:在java8中,能夠不使用final關鍵字,可是若是咱們改變局部變量的引用,編譯會發生錯誤,從而保證了局部變量的引用不變。

6. 爲何內部類會持有外部類的引用?持有的引用是this?仍是其它?

內部類雖然和外部類寫在同一個文件中, 可是編譯完成後, 仍是生成各自的class文件,內部類經過this訪問外部類的成員。

  1. 編譯器自動爲內部類添加一個成員變量, 這個成員變量的類型和外部類的類型相同, 這個成員變量就是指向外部類對象
  2. 編譯器自動爲內部類的構造方法添加一個參數, 參數的類型是外部類的類型, 在構造方法內部使用這個參數爲內部類中添加的成員變量賦值;
  3. 在調用內部類的構造函數初始化內部類對象時,會默認傳入外部類的引用。

錯誤和異常


1. java中的異常

  1. 基類是Throwable,Error和Exception繼承自Throwable
  2. Error一般是系統拋出來的,也能夠catch到,但通常不可恢復,開發是也不作處理
  3. Exception分爲受檢查異常和不受檢查異常,受檢查異常會在編譯時強制要求咱們try/catch

2. throw 和 throws

  1. throw:拋出異常
  2. throws:在方法聲明處使用,表示此方法可能拋出的異常,調用此方法處須要處理這些異常。

3. Error和Exception有什麼區別?

  1. Error是系統拋出的,不能在運行時捕獲,好比內存溢出
  2. Exception 是須要咱們捕捉並處理的異常,如類型轉換錯誤等,咱們能夠經過捕捉異常,使程序發生異常時仍可正常運行

4. 運行時異常與受檢異常有何異同?

  1. checked exception:這種異常,JAVA編譯器強制要求咱們必需對出現的這些異常進行try/catch或者繼續上拋
  2. runtime exception:出現運行時異常後,系統會把異常一直往上層拋,一直遇處處理代碼。若是沒有處理塊,到最上層,若是是多線程就由Thread.run()拋出,若是是單線程就被main()拋出。拋出以後,若是是線程,這個線程也就退出了。若是是主程序拋出的異常,那麼這整個程序也就退出了

5. 列出一些你常見的運行時異常

NullPointerException (空指針異常) ClassCastException (類轉換異常) IndexOutOfBoundsException (下標越界異常) IllegalArgumentException (非法參數異常)

6. Exception繼承相關考題

題目1:
類ExampleA繼承Exception,類ExampleB繼承ExampleA。有以下代碼片段,請問執行此段代碼的輸出是什麼?
try {
throw new ExampleB("b")
} catch(ExampleA e){
System.out.println("ExampleA");
} catch(Exception e){
System.out.println("Exception");
}

解析:ExampleA。(根據里氏代換原則[能使用父類型的地方必定能使用子類型],抓取ExampleA類型異常的catch塊可以抓住try塊中拋出的ExampleB類型的異常)

題目2:
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}

class Human {

public static void main(String[] args)
throws Exception {
try {
try {
throw new Sneeze();
}
catch ( Annoyance a ) {
System.out.println("Caught Annoyance");
throw a;
}
}
catch ( Sneeze s ) {
System.out.println("Caught Sneeze");
return ;
}
finally {
System.out.println("Hello World!");
}
}
}
解析:輸出Caught AnnoyanceCaught SneezeHello World!
複製代碼

關鍵字和運算符


1. &和&&的區別;|和||的區別?

  1. &有兩種用法:(1)按位與;(2)邏輯與,咱們這裏說的是邏輯與。
  2. 與運算要求左右兩端的布爾值都是true整個表達式的值纔是true
  3. &&運算符是短路邏輯與運算,若是&&左邊的表達式的值是false,右邊的表達式會被直接短路掉,不會進行運算
  4. &左右兩邊的表達式都會計算,咱們經常使用&&,好比if(username != null &&!username.equals("hahaha")){}

2, transient關鍵字

若是用transient聲明一個實例變量,當對象存儲時,它的值不須要維持。換句話來講就是,用transient關鍵字標記的成員變量不參與序列化過程

3. 修飾符的區別

修飾符一共有四個:private、protected、public和default(也有人管默認叫friendly)

  1. private:私有的,除本身外任何類不能使用
  2. protected:同包可使用,其餘包子類可使用
  3. public:任何類可使用
  4. default:同包可使用,其餘包不能使用
修飾符 當前類 同 包 子 類 其餘包
public
protected ×
default × ×
private × × ×

4. Java有沒有goto?

goto 和 const 是Java中的保留字,在目前版本的Java中沒有使用。

5. 在Java中,如何跳出當前的多重嵌套循環

在最外層循環前加一個標記如A,而後用break A;能夠跳出多重循環 (應該避免使用帶標籤的break和continue,由於它不會讓你的程序變得更優雅)。

6. switch 是否能做用在byte 上,是否能做用在long 上,是否能做用在String上?

  1. 在Java 5之前,switch(expr)中,expr只能是byte、short、char、int
  2. Java 5開始,Java中引入了枚舉類型,expr也能夠是enum類型
  3. 從Java 7開始,expr還能夠是字符串(String)
  4. long類型不支持

7. static

  1. 能夠修飾內部類(靜態內部類)
  2. 能夠修飾成員變量,該變量屬於類,被全部實例共享
  3. 能夠修飾方法,該方法屬於類,被全部實例共享
  4. 能夠修飾代碼塊(靜態代碼塊),該代碼塊在第一次被加載時被調用

8. Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally分別如何使用?

  1. Java經過面向對象的方法進行異常處理,把各類不一樣的異常進行分類在Java中,每一個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法能夠捕獲到這個異常並能夠對其進行處理。
  2. Java的異常處理是經過5個關鍵詞來實現的:try、catch、throw、throws和finally。通常狀況下是用try來執行一段程序,若是系統會拋出(throw)一個異常對象,能夠經過它的類型來捕獲(catch)它,或經過老是執行代碼塊(finally)來處理;try用來指定一塊預防全部異常的程序;catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各類異常(固然聲明異常時容許無病呻吟);finally爲確保一段代碼無論發生什麼異常情況都要被執行;t
  3. try語句能夠嵌套,每當遇到一個try語句,異常的結構就會被放入異常棧中,直到全部的try語句都完成。若是下一級的try語句沒有對某種異常進行處理,異常棧就會執行出棧操做,直到遇到有處理這種異常的try語句或者最終將異常拋給JVM。

9. 闡述final、finally、finalize的區別。

這是三個不一樣的概念,只是由於長得較像而被出成了一道題

final

final是一個修飾符,用來修飾類,變量,方法

  1. final修飾的類不能被繼承
  2. final修飾的方法不能被重寫
  3. final修飾的成員變量是不可變的,若是成員變量是基本數據類型,初始化以後成員變量的值不能被改變,若是成員變量是引用類型,那麼它只能指向初始化時指向的那個對象,不能再指向別的對象,可是對象當中的內容是容許改變的

finally

finally與try,catch一塊兒搭配使用,不管是否catch到異常,finally中的內容都會執行

finalize

finalize是Object類中的方法,垃圾回收器在垃圾回收時會調用該方法,咱們能夠在子類中重寫該方法來作一些清理工做

10. finally 語句必定會執行嗎

在極特殊的狀況下可能不執行

  1. 調用了System.exit()方法
  2. JVM崩潰了

基本類型和經常使用類


0. int和Integer有什麼區別?

  1. int是基本類型,Integer是int的包裝類型
  2. 包裝類型能夠有一些本身的方法,引入包裝類型可使java更好的面向對象
  3. 每一個基本類型都有其包裝類:
  • 原始類型: boolean,char,byte,short,int,long,float,double
  • 包裝類型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
擴展1:
java5中引入了自動拆裝箱功能,例如在比較時能夠自動拆裝箱
class AutoUnboxingTest {

    public static void main(String[] args) {
        Integer a = new Integer(3);
        Integer b = 3;                  // 將3自動裝箱成Integer類型
        int c = 3;
        System.out.println(a == b);     // false 兩個引用沒有引用同一對象
        System.out.println(a == c);     // true a自動拆箱成int類型再和c比較
    }
}
複製代碼
擴展2:
一道和裝箱有關的面試題
public class Test03 {

    public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

        System.out.println(f1 == f2); //true
        System.out.println(f3 == f4); //false
    }
}

分析:自動裝箱時,使用的時Integer的valueof方法,當int在-128到127之間時,並不會new一個新的對象,而是直接使用常量池中的Integer
具體分析: 

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
IntegerCache是Integer的內部類,其代碼以下所示:
/**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
    簡單的說,若是整型字面量的值在-128到127之間,那麼不會new新的Integer對象,而是直接引用常量池中的Integer對象,
    因此上面的面試題中f1==f2的結果是true,而f3==f4的結果是false複製代碼

1. float f=3.4;是否正確?

答:不正確。3.4是雙精度數,將雙精度型(double)賦值給浮點型(float)屬於下轉型(down-casting,也稱爲窄化)會形成精度損失,所以須要強制類型轉換float f =(float)3.4; 或者寫成float f =3.4F;。

2. short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?

  1. 對於short s1 = 1; s1 = s1 + 1;因爲1是int類型,所以s1+1運算結果也是int 型,須要強制轉換類型才能賦值給short型。
  2. short s1 = 1; s1 += 1;能夠正確編譯,由於s1+= 1;至關於s1 = (short)(s1 + 1);其中有隱含的強制類型轉換。

3. Java中char 型變量中能不能存貯一箇中文漢字,爲何?

Java中 char型變量用來存儲Unicode編碼的字符,unicode編碼字符集中包含了漢字,因此char類型能夠儲存漢字 char類型佔兩個字節

4. 數組有沒有length()方法?String有沒有length()方法?

數組有length屬性,String有length()方法

5. String是基本數據類型嘛

  1. 不是。
  2. java中8個基本類型爲:byte、short、char、int、float、long、double、boolean。
  3. java中除基本類型外,都是引用類型(枚舉是java5之後引入的特殊引用類型)
  4. String類型比較特殊,不可變。但它不是基本類型

6. 是否能夠繼承String類

String 類是final的,不能被繼承

7. String和StringBuilder、StringBuffer的區別

String 是隻讀字符串,StringBuilder和StringBuffer能夠改變,StringBuilder效率高,線程不安全,StringBuffer線程安全。 在拼接String時,使用+編譯器會幫咱們進行優化,使用StringBuilder進行拼接,這時+和StringBuilder沒有多大區別。但當循環中使用+時,咱們應該顯示的使用StringBuilder,以防止屢次調用new StringBuilder,形成沒必要要的性能浪費。

循環中使用+舉例:
String str = "hello,world!";
        String result = "";

        for (int i = 0; i < loopCount; i++) {
            result += str;
        }
這個時候編譯器會優化成
String str = "hello,world!";
        String result = "";

        for (int i = 0; i < loopCount; i++) {
            result = new StringBuilder(result).append(str).toString();
        }
屢次new StringBuilder形成了性能浪費。
複製代碼
擴展例題
class StringEqualTest {

    public static void main(String[] args) {
        String s1 = "Programming";
        String s2 = new String("Programming");
        String s3 = "Program";
        String s4 = "ming";
        String s5 = "Program" + "ming";
        String s6 = s3 + s4;
        System.out.println(s1 == s2); // false
        System.out.println(s1 == s5); //true
        System.out.println(s1 == s6); //false
        System.out.println(s1 == s6.intern()); //true
        System.out.println(s2 == s2.intern()); //false
    }
}

解析:1. String是引用類型,這裏 == 比較的是引用是否相同,便是否指向相同的地址
     2. 在new String對象時,會產生一個新的對象,並不會使用常量池中的字符串
     3. intern會在常量池中尋找該字符串(若是沒有責新建),並返回他的地址
複製代碼

8. String s = new String("xyz");建立了幾個字符串對象?

兩個對象,一個是靜態區的"xyz";一個是用new建立在堆上的對象。

9. String 和基本數據類型之間的轉換

  1. String 轉基本數據類型:調用基本數據類型對應包裝類的parseXXX(String)或valueOf(String)方法
  2. 基本數據類型轉String:基本數據類型+「」;String.valueof(12)

10. 實現字符串的反轉

  1. 方法有不少,能夠用StringBuffer/StringBuilder的reverse方法,這裏reverse是經過位移實現的
  2. 再舉例一種遞歸方法:
public String reverse(String originString){
if(originString == null || originString.length <= 1)
return originString;
return reverse(originString.subString(1)) + originString.charAt(0);
}

複製代碼

11. String 爲何要設計成不可變的

1. 安全性

  1. 線程安全,不可變天生線程安全
  2. String常被用做HashMap的key,若是可變會引有安全問題,如兩個key相同
  3. String常被用做數據庫或接口的參數,可變的話也會有安全問題

2. 效率

  1. 經過字符串池能夠節省不少空間
  2. 每一個String對應一個hashcode,再次使用的話不用從新計算

編碼


0. 講一下Java的編碼方式

爲何須要編碼

計算機存儲信息的最小單元是一個字節即8bit,因此能表示的範圍是0~255,這個範圍沒法保存全部的字符,因此須要一個新的數據結構char來表示這些字符,從char到byte須要編碼。

常見的編碼方式有如下幾種:

  1. ASCII:總共有 128 個,用一個字節的低 7 位表示,031 是控制字符如換行回車刪除等;32126 是打印字符,能夠經過鍵盤輸入而且可以顯示出來。
  2. GBK:碼範圍是 8140~FEFE(去掉 XX7F)總共有 23940 個碼位,它能表示 21003 個漢字,它的編碼是和 GB2312 兼容的,也就是說用 GB2312 編碼的漢字能夠用 GBK 來解碼,而且不會有亂碼。
  3. UTF-16:UTF-16 具體定義了 Unicode 字符在計算機中存取方法。UTF-16 用兩個字節來表示 Unicode 轉化格式,這個是定長的表示方法,不論什麼字符均可以用兩個字節表示,兩個字節是 16 個 bit,因此叫 UTF-16。UTF-16 表示字符很是方便,每兩個字節表示一個字符,這個在字符串操做時就大大簡化了操做,這也是 Java 以 UTF-16 做爲內存的字符存儲格式的一個很重要的緣由。
  4. UTF-8:統一採用兩個字節表示一個字符,雖然在表示上很是簡單方便,可是也有其缺點,有很大一部分字符用一個字節就能夠表示的如今要兩個字節表示,存儲空間放大了一倍,在如今的網絡帶寬還很是有限的今天,這樣會增大網絡傳輸的流量,並且也不必。而 UTF-8 採用了一種變長技術,每一個編碼區域有不一樣的字碼長度。不一樣類型的字符能夠是由 1~6 個字節組成。

Java中須要編碼的地方通常都在字符到字節的轉換上,這個通常包括磁盤IO和網絡IO。

Reader 類是 Java 的 I/O 中讀字符的父類,而 InputStream 類是讀字節的父類,InputStreamReader 類就是關聯字節到字符的橋樑,它負責在 I/O 過程當中處理讀取字節到字符的轉換,而具體字節到字符的解碼實現它由 StreamDecoder 去實現,在 StreamDecoder 解碼過程當中必須由用戶指定 Charset 編碼格式。

1. Unicode與UTF-8的關係

Unicode是字符集 UTF-8是一種編碼方式,達到了對數據流壓縮的目的

其餘未分類


何時用斷言(assert)

  1. 斷言在軟件開發中是一種經常使用的調試方式
  2. 斷言檢查一般在開發和測試時開啓,發佈時關閉
  3. 斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲true;若是表達式的值爲false,那麼系統會報告一個AssertionError。斷言的使用以下面的代碼所示:assert(a > 0); // throws an AssertionError if a <= 0
  4. Android中不推薦使用斷言

Java中的四種引用及應用場景

Java中的引用有四種:強引用,弱引用,軟引用,虛引用

  1. 強引用: 一般咱們使用new操做符建立一個對象時所返回的引用即爲強引用
  2. 弱引用: 若一個對象只能經過弱引用到達,那麼它就會被回收(即便內存充足),一樣可用於圖片緩存中,這時候只要Bitmap再也不使用就會被回收
  3. 軟引用: 若一個對象只能經過軟引用到達,那麼這個對象在內存不足時會被回收,可用於圖片緩存中,內存不足時系統會自動回收再也不使用的Bitmap
  4. 虛引用: 虛引用是Java中最「弱」的引用,經過它甚至沒法獲取被引用的對象,它存在的惟一做用就是當它指向的對象回收時,它自己會被加入到引用隊列中,這樣咱們能夠知道它指向的對象什麼時候被銷燬

動態代理和靜態代理的區別,動態代理的使用場景

  1. 優勢:動態代理與靜態代理相比較,最大的好處是接口中聲明的全部方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,咱們能夠進行靈活處理,而不須要像靜態代理那樣每個方法進行中轉。
  2. 缺點:它始終沒法擺脫僅支持 interface 代理的桎梏,由於它的設計註定了這個遺憾。

併發和並行的區別

  1. 併發:(在一個時間段內同時執行多件事)如單核cpu以時間片的方式讓多個線程輪循執行,在外界看來他們是同時執行的
  2. 並行:(在一個時刻同時執行多件事)多核cpu每一個核運行一個App,他們是真正的同時執行

java中的流類型

  1. 字節流:字節流繼承於InputStream、OutputStream,以字節方式讀取
  2. 字符流:字符流繼承於Reader、Writer,以字符方式讀取

字節流和字符流的區別

  1. 字節流操做的基本單元是字節;字符流是Unicode字符
  2. 字節流不使用緩衝區,字符流使用緩衝區
  3. 字節流一般用於處理二進制數據,實際上它能夠處理任意類型的數據,但它不支持直接寫入或讀取Unicode碼元;字符流一般處理文本數據,它支持寫入及讀取Unicode碼元。

IO和NIO區別

  1. IO面向流,NIO面向緩衝區
  2. IO是阻塞的,NIO是非阻塞的
  3. Java NIO的選擇器容許一個單獨的線程來監視多個輸入通道,你能夠註冊多個通道使用一個選擇器,而後使用一個單獨的線程來「選擇」通道:這些通道里已經有能夠處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道

大文件的複製

利用NewIO的FileChanel

如何在Java中建立Immutable對象

  1. 私有化成員變量
  2. 經過構造方法初始化成員變量
  3. 只提供get方法,不提供set方法
  4. get方法返回的是一個拷貝副本

舉例說明同步和異步

  1. java 中同步常指多線程時數據的同步,在多線程中若是數據不是線程安全的,那麼有可能會出現不少錯誤
  2. 異步多指方法調用時,是否等待方法調用完成才繼續向下執行,例如一般網絡請求就是異步的。

Java中如何實現序列化,有什麼意義。

  1. 序列化是將對象的狀態信息轉換爲能夠存儲或傳輸的形式的過程。
  2. java將對象序列化成了字節信息
  3. java經過實現Serializable接口實現序列化

for-each與常規for循環的效率對比

  1. for-each 使代碼更加簡潔優雅
  2. for-each 其實是經過迭代器(Iterator)實現的

java8 有哪些新特性

支持Lambda 表達式,方法引用,增長了新的時間工具類等

你在項目中哪些地方用到了XML

XML的主要做用有兩個方面:數據交換和信息配置。在作數據交換時,XML將數據用標籤組裝成起來,而後壓縮打包加密後經過網絡傳送給接收者,接收解密與解壓縮後再從XML文件中還原相關信息進行處理,XML曾經是異構系統間交換數據的事實標準,但此項功能幾乎已經被JSON(JavaScript Object Notation)取而代之。固然,目前不少軟件仍然使用XML來存儲配置信息,咱們在不少項目中一般也會將做爲配置信息的硬代碼寫在XML文件中,Java的不少框架也是這麼作的,並且這些框架都選擇了dom4j做爲處理XML的工具,由於Sun公司的官方API實在不怎麼好用

什麼是DAO模式

  1. DAO 是 Data Access Object 的縮寫
  2. 位於業務邏輯和持久化數據之間
  3. 實現對持久化數據的訪問。

簡述一下你瞭解的設計模式

所謂設計模式,就是一套被反覆使用的代碼設計經驗的總結(情境中一個問題通過證明的一個解決方案)。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。設計模式令人們能夠更加簡單方便的複用成功的設計和體系結構。將已證明的技術表述成設計模式也會使新系統開發者更加容易理解其設計思路。 在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中給出了三類(建立型[對類的實例化過程的抽象化]、結構型[描述如何將類或對象結合在一塊兒造成更大的結構]、行爲型[對在不一樣的對象之間劃分責任和算法的抽象化])共23種設計模式,包括:Abstract Factory(抽象工廠模式),Builder(建造者模式),Factory Method(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式);Facade(門面模式),Adapter(適配器模式),Bridge(橋樑模式),Composite(合成模式),Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State(狀態模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(責任鏈模式)。 面試被問到關於設計模式的知識時,能夠揀最經常使用的做答,例如:

  • 工廠模式:工廠類能夠根據條件生成不一樣的子類實例,這些子類有一個公共的抽象父類而且實現了相同的方法,可是這些方法針對不一樣的數據進行了不一樣的操做(多態方法)。當獲得子類的實例後,開發人員能夠調用基類中的方法而沒必要考慮到底返回的是哪個子類的實例。
  • 代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。實際開發中,按照使用目的的不一樣,代理能夠分爲:遠程代理、虛擬代理、保護代理、Cache代理、防火牆代理、同步化代理、智能引用代理。
  • 適配器模式:把一個類的接口變換成客戶端所期待的另外一種接口,從而使本來因接口不匹配而沒法在一塊兒使用的類可以一塊兒工做。
  • 模板方法模式:提供一個抽象類,將部分邏輯以具體方法或構造器的形式實現,而後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不一樣的子類能夠以不一樣的方式實現這些抽象方法(多態實現),從而實現不一樣的業務邏輯。 除此以外,還能夠講講上面提到的門面模式、橋樑模式、單例模式、裝潢模式(Collections工具類和I/O系統中都使用裝潢模式)等,反正基本原則就是揀本身最熟悉的、用得最多的做答,以避免言多必失。

Comparable和Comparator接口是幹什麼的

  1. 他們都是接口
  2. Comparable是比較本實例和其餘實例的大小
  3. Comparator是比較器,用來輸入兩個實例後比較兩個實例大小

URI和URL

URI 是統一資源標識符,而URL 是統一資源定位符

XML文檔定義有幾種形式?它們之間有何本質區別?解析XML文檔有哪幾種方式?

  1. XML文檔定義分爲DTD和Schema兩種形式,兩者都是對XML語法的約束
  2. 其本質區別在於Schema自己也是一個XML文件,能夠被XML解析器解析,並且能夠爲XML承載的數據定義類型,約束能力較之DTD更強大
  3. 對XML的解析主要有DOM(文檔對象模型,Document Object Model)、SAX(Simple API for XML)和StAX(Java 6中引入的新的解析XML的方式,Streaming API for XML),其中DOM處理大型文件時其性能降低的很是厲害,這個問題是由DOM樹結構佔用的內存較多形成的,並且DOM解析方式必須在解析文件以前把整個文檔裝入內存,適合對XML的隨機訪問(典型的用空間換取時間的策略);SAX是事件驅動型的XML解析方式,它順序讀取XML文件,不須要一次所有裝載整個文件。當遇到像文件開頭,文檔結束,或者標籤開頭與標籤結束時,它會觸發一個事件,用戶經過事件回調代碼來處理XML文件,適合對XML的順序訪問;顧名思義,StAX把重點放在流上,實際上StAX與其餘解析方式的本質區別就在於應用程序可以把XML做爲一個事件流來處理。將XML做爲一組事件來處理的想法並不新穎(SAX就是這樣作的),但不一樣之處在於StAX容許應用程序代碼把這些事件逐個拉出來,而不用提供在解析器方便時從解析器中接收事件的處理程序。

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

Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11. Math.round()獲得的值大於等於原來的值。

用最有效率的方法計算2乘以8?

2 << 3(左移n位至關於乘以2的n次方,右移n位至關於除以2的n次方)。

什麼是UML

UML(Unified Modeling Language)是統一建模語言。爲軟件開發提供模型化和可視化支持,方便溝通交流。

18. UML中有哪些經常使用的圖

UML定義了多種圖形化的符號來描述軟件系統部分或所有的靜態結構和動態結構,包括:用例圖(use case diagram)、類圖(class diagram)、時序圖(sequence diagram)、協做圖(collaboration diagram)、狀態圖(statechart diagram)、活動圖(activity diagram)、構件圖(component diagram)、部署圖(deployment diagram)等。在這些圖形化符號中,有三種圖最爲重要,分別是:用例圖(用來捕獲需求,描述系統的功能,經過該圖能夠迅速的瞭解系統的功能模塊及其關係)、類圖(描述類以及類與類之間的關係,經過該圖能夠快速瞭解系統)、時序圖(描述執行特定任務時對象之間的交互關係以及執行順序,經過該圖能夠了解對象能接收的消息也就是說對象可以向外界提供的服務)。 用例圖:

類圖:
時序圖:

寫一個方法,輸入一個文件名和一個字符串,統計這個字符串在這個文件中出現的次數。

import java.io.BufferedReader;
import java.io.FileReader;

public class MyUtil{

private MyUtil(){
throw new AssertError;
}

public static int countWordInFile(String filename,String word){
int counter = 0;
try(FileReader fr = new FileReader(filename){
try(BufferReader br = new BufferReader(fr)){
String line = null;
while((line = br.readLine()) != null){
int index = -1;
while(line.length() >= word.length() && (index = line.indexof(word) >= 0){
counter ++;
line = line.subString(index+word.length());
})
}


}catch(Exception e){
Log.e(e);
}

} catch(Exception e){
Log.e(e);
}
)
}

}
複製代碼

如何用Java代碼列出一個目錄下全部的文件?

import java.io.File;

class Test12 {

public static void main(String[] args) {
File f = new File("/Users/Hao/Downloads");
for(File temp : f.listFiles()) {
if(temp.isFile()) {
System.out.println(temp.getName());
}
}
}
}



若是須要對文件夾繼續展開,代碼以下所示:

import java.io.File;

class Test12 {

public static void main(String[] args) {
showDirectory(new File("/Users/Hao/Downloads"));
}

public static void showDirectory(File f) {
_walkDirectory(f, 0);
}

private static void _walkDirectory(File f, int level) {
if(f.isDirectory()) {
for(File temp : f.listFiles()) {
_walkDirectory(temp, level + 1);
}
}
else {
for(int i = 0; i < level - 1; i++) {
System.out.print("\t");
}
System.out.println(f.getName());
}
}
}



在Java 7中可使用NIO.2的API來作一樣的事情,代碼以下所示:

class ShowFileTest {

public static void main(String[] args) throws IOException {
Path initPath = Paths.get("/Users/Hao/Downloads");
Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
System.out.println(file.getFileName().toString());
return FileVisitResult.CONTINUE;
}

});
}
}
複製代碼

日期和時間

1. 獲取當前年月日,時分秒
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));    // 0 - 11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));

2. 取得從1970年1月1日0時0分0秒到如今的毫秒數
Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis(); // Java 8

3. 如何取得某月的最後一天
Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH);

4. 如何格式化日期
利用java.text.DataFormat 的子類(如SimpleDateFormat類)中的format(Date)方法可將日期格式化。Java 8中能夠用java.time.format.DateTimeFormatter來格式化時間日期,代碼以下所示
public static void main(String[] args) {
SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();
System.out.println(oldFormatter.format(date1));

// Java 8
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}

複製代碼

打印昨天的當前時刻

Calender calender = Calender.getInstance();
calender.add(Calender.DATE,-1);
calender.getTime();


在Java 8中,能夠用下面的代碼實現相同的功能。

import java.time.LocalDateTime;

class YesterdayCurrent {

public static void main(String[] args) {
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);

System.out.println(yesterday);
}
}

複製代碼

用java寫一個單例

public class Single{
private static Single  instance = new Single();

private Single(){

}
public static Single getInstance(){
return instance;
}
}
複製代碼

用java寫一個冒泡排序

public <T extends Comparable<T>> void sort(T[] list){

boolean swap = true;
T temp;
for(int i = list.length - 1 ; i> 0 && swap; i--){
swap = false;
for (int j = 0;j<i-1;j++){
if (list[j].compareTo(list[j+1]) > 0) {
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
swap = true;
}
}
}
複製代碼

}

用Java寫一個折半查找

public static <T> int binarySearch(T[] x, T key, Comparator<T> comp) {
int low = 0;
int high = x.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int cmp = comp.compare(x[mid], key);
if (cmp < 0) {
low= mid + 1;
}
else if (cmp > 0) {
high= mid - 1;
}
else {
return mid;
}
}
return -1;
}
複製代碼

參考資料

Java面試題全集(上)

Java線程面試題 Top 50

相關文章
相關標籤/搜索