1. 程式人生 > >java面試題總結(三)--本文都是筆試題,很值得學習一下

java面試題總結(三)--本文都是筆試題,很值得學習一下

來自:https://www.cnblogs.com/lanxuezaipiao/p/3371224.html

下面是我在原文中選擇了我比較容易犯錯的題

 

1. 下面哪些是Thread類的方法()

A start()       B run()       C exit()       D getPriority()

答案:ABD

解析:看Java API docs吧:http://docs.oracle.com/javase/7/docs/api/,exit()是System類的方法,如System.exit(0)。

 

2. 下面關於java.lang.Exception類的說法正確的是()

A 繼承自Throwable      B Serialable      CD 不記得,反正不正確

答案:A

解析:Java異常的基類為java.lang.Throwable,java.lang.Error和java.lang.Exception繼承 Throwable,RuntimeException和其它的Exception等繼承Exception,具體的RuntimeException繼承RuntimeException。

擴充套件:錯誤和異常的區別(Error vs Exception) 

1) java.lang.Error: Throwable的子類,用於標記嚴重錯誤。合理的應用程式不應該去try/catch這種錯誤。絕大多數的錯誤都是非正常的,就根本不該出現的。
java.lang.Exception: Throwable的子類,用於指示一種合理的程式想去catch的條件。即它僅僅是一種程式執行條件,而非嚴重錯誤,並且鼓勵使用者程式去catch它。

 

 

2)  Error和RuntimeException 及其子類都是未檢查的異常(unchecked exceptions),而所有其他的Exception類都是檢查了的異常(checked exceptions).
checked exceptions: 

通常是從一個可以恢復的程式中丟擲來的,並且最好能夠從這種異常中使用程式恢復。比如FileNotFoundException, ParseException等。檢查了的異常發生在編譯階段,必須要使用try…catch(或者throws)否則編譯不通過。
unchecked exceptions: 通常是如果一切正常的話本不該發生的異常,但是的確發生了。發生在執行期,具有不確定性,主要是由於程式的邏輯問題所引起的。比如ArrayIndexOutOfBoundException, ClassCastException等。從語言本身的角度講,程式不該去catch這類異常,雖然能夠從諸如RuntimeException這樣的異常中catch並恢復,但是並不鼓勵終端程式設計師這麼做,因為完全沒要必要。因為這類錯誤本身就是bug,應該被修復,出現此類錯誤時程式就應該立即停止執行。 因此,面對Errors和unchecked exceptions應該讓程式自動終止執行,程式設計師不該做諸如try/catch這樣的事情,而是應該查明原因,修改程式碼邏輯。

RuntimeException:RuntimeException體系包括錯誤的型別轉換、陣列越界訪問和試圖訪問空指標等等。

處理RuntimeException的原則是:如果出現 RuntimeException,那麼一定是程式設計師的錯誤。例如,可以通過檢查陣列下標和陣列邊界來避免陣列越界訪問異常。其他(IOException等等)checked異常一般是外部錯誤,例如試圖從檔案尾後讀取資料等,這並不是程式本身的錯誤,而是在應用環境中出現的外部錯誤。 

 

9. volatile關鍵字是否能保證執行緒安全?()

答案:不能

解析:volatile關鍵字用在多執行緒同步中,可保證讀取的可見性,JVM只是保證從主記憶體載入到執行緒工作記憶體的值是最新的讀取值,而非cache中。但多個執行緒對

volatile的寫操作,無法保證執行緒安全。例如假如執行緒1,執行緒2 在進行read,load 操作中,發現主記憶體中count的值都是5,那麼都會載入這個最新的值,線上程1堆count進行修改之後,會write到主記憶體中,主記憶體中的count變數就會變為6;執行緒2由於已經進行read,load操作,在進行運算之後,也會更新主記憶體count的變數值為6;導致兩個執行緒及時用volatile關鍵字修改之後,還是會存在併發的情況。

 

11. 存在使i + 1 < i的數嗎()

答案:存在

解析:如果i為int型,那麼當i為int能表示的最大整數時,i+1就溢位變成負數了,此時不就<i了嗎。

擴充套件:存在使i > j || i <= j不成立的數嗎()

答案:存在

解析:比如Double.NaN或Float.NaN,感謝@BuilderQiu網友指出。

 

12. 0.6332的資料型別是()

A float     B double     C Float      D Double

答案:B

解析:預設為double型,如果為float型需要加上f顯示說明,即0.6332f

 

15. 不通過建構函式也能建立物件嗎()

A 是     B 否

答案:A

解析:Java建立物件的幾種方式(重要):

(1) 用new語句建立物件,這是最常見的建立物件的方法。
(2) 運用反射手段,呼叫java.lang.Class或者java.lang.reflect.Constructor類的newInstance()例項方法。
(3) 呼叫物件的clone()方法。
(4) 運用反序列化手段,呼叫java.io.ObjectInputStream物件的 readObject()方法。

(1)和(2)都會明確的顯式的呼叫建構函式 ;(3)是在記憶體上對已有物件的影印,所以不會呼叫建構函式 ;(4)是從檔案中還原類的物件,也不會呼叫建構函式。

 

16. ArrayList list = new ArrayList(20);中的list擴充幾次()

A 0     B 1     C 2      D 3

答案:A

解析:這裡有點迷惑人,大家都知道預設ArrayList的長度是10個,所以如果你要往list裡新增20個元素肯定要擴充一次(擴充為原來的1.5倍),但是這裡顯示指明瞭需要多少空間,所以就一次性為你分配這麼多空間,也就是不需要擴充了。

 

19. 下面程式能正常執行嗎()

public class NULL {

    public static void haha(){
        System.out.println("haha");
    }
    public static void main(String[] args) {
        ((NULL)null).haha();
    }

}

答案:能正常執行

解析:輸出為haha,因為null值可以強制轉換為任何java類型別,(String)null也是合法的。但null強制轉換後是無效物件,其返回值還是為null,而static方法的呼叫是和類名繫結的,不借助物件進行訪問所以能正確輸出。反過來,沒有static修飾就只能用物件進行訪問,使用null呼叫物件肯定會報空指標錯了。這裡和C++很類似。這裡感謝@Florian網友解答。

 

20. 下面程式的執行結果是什麼()

class HelloA {

    public HelloA() {
        System.out.println("HelloA");
    }
    
    { System.out.println("I'm A class"); }
    
    static { System.out.println("static A"); }

}

public class HelloB extends HelloA {
    public HelloB() {
        System.out.println("HelloB");
    }
    
    { System.out.println("I'm B class"); }
    
    static { System.out.println("static B"); }
    
    public static void main(String[] args) { 
     new HelloB(); 
   }

}

答案:

static A
static B
I'm A class
HelloA
I'm B class
HelloB

解析:說實話我覺得這題很好,考查靜態語句塊、構造語句塊(就是隻有大括號的那塊)以及建構函式的執行順序。

物件的初始化順序:(1)類載入之後,按從上到下(從父類到子類)執行被static修飾的語句;(2)當static語句執行完之後,再執行main方法;(3)如果有語句new了自身的物件,將從上到下執行構造程式碼塊、構造器(兩者可以說繫結在一起)。

 

22. 下面程式碼的執行結果為:()

import java.io.*;
import java.util.*;

public class foo{

    public static void main (String[] args){

        String s;

        System.out.println("s=" + s);

    }

}

A 程式碼得到編譯,並輸出“s=”

B 程式碼得到編譯,並輸出“s=null”

C 由於String s沒有初始化,程式碼不能編譯通過

D 程式碼得到編譯,但捕獲到 NullPointException異常

答案:C

解析:開始以為會輸出null什麼的,執行後才發現Java中所有定義的基本型別或物件都必須初始化才能輸出值。

 

24. 指出下列程式執行的結果 ()這道題很有價值

public class Example {

    String str = new String("good");

    char[] ch = { 'a', 'b', 'c' };

    public static void main(String args[]) {

        Example ex = new Example();

        ex.change(ex.str, ex.ch);

        System.out.print(ex.str + " and ");

        System.out.print(ex.ch);

    }

    public void change(String str, char ch[]) {

        str = "test ok";

        ch[0] = 'g';

    }
}

A、 good and abc

B、 good and gbc

C、 test ok and abc

D、 test ok and gbc 

答案:B

解析:大家可能以為Java中String和陣列都是物件所以肯定是物件引用,然後就會選D,其實這是個很大的誤區:因為在java裡沒有引用傳遞,只有值傳遞

這個值指的是實參的地址的拷貝,得到這個拷貝地址後,你可以通過它修改這個地址的內容(引用不變),因為此時這個內容的地址和原地址是同一地址,

但是你不能改變這個地址本身使其重新引用其它的物件,也就是值傳遞,可能說的不是很清楚,下面給出一個完整的能說明情況的例子吧:

package test;

/**
 * @description Java中沒有引用傳遞只有值傳遞
 * 
 * @author Alexia
 * @date 2013-10-16
 * 
 */
class Person {

    private String name;

    private String sex;

    public Person(String x, String y) {
        this.name = x;
        this.sex = y;
    }

    // 重寫toString()方法,方便輸出
    public String toString() {

        return name + " " + sex;
    }

    // 交換物件引用
    public static void swapObject(Person p1, Person p2) {
        Person tmp = p1;
        p1 = p2;
        p2 = tmp;
    }

    // 交換基本型別
    public static void swapInt(int a, int b) {
        int tmp = a;
        a = b;
        b = tmp;
    }

    // 交換物件陣列
    public static void swapObjectArray(Person[] p1, Person[] p2) {
        Person[] tmp = p1;
        p1 = p2;
        p2 = tmp;
    }

    // 交換基本型別陣列
    public static void swapIntArray(int[] x, int[] y) {
        int[] tmp = x;
        x = y;
        y = tmp;
    }

    // 改變物件陣列中的內容
    public static void changeObjectArray(Person[] p1, Person[] p2) {
        Person tmp = p1[1];
        p1[1] = p2[1];
        p2[1] = tmp;
        
        // 再將p1[1]修改
        Person p = new Person("wjl", "male");
        p1[1] = p;
    }

    // 改變基本型別陣列中的內容
    public static void changeIntArray(int[] x, int[] y) {
        int tmp = x[1];
        x[1] = y[1];
        y[1] = tmp;

        x[1] = 5;
    }
}

public class ByValueTest {

    public static void main(String[] args) {

        // 建立並構造兩個物件
        Person p1 = new Person("Alexia", "female");
        Person p2 = new Person("Edward", "male");

        System.out.println("物件交換前:p1 = " + p1.toString());
        System.out.println("物件交換前:p2 = " + p2.toString());
        
        // 交換p1物件和p2物件
        Person.swapObject(p1, p2);
        // 從交換結果中看出,實際物件並未交換
        System.out.println("\n物件交換後:p1 = " + p1.toString());
        System.out.println("物件交換後:p2 = " + p2.toString());

        // 建立兩個物件陣列
        Person[] arraya = new Person[2];
        Person[] arrayb = new Person[2];

        // 分別構造陣列物件
        arraya[0] = new Person("Alexia", "female");
        arraya[1] = new Person("Edward", "male");
        arrayb[0] = new Person("jmwang", "female");
        arrayb[1] = new Person("hwu", "male");

        System.out.println('\n' + "物件陣列交換前:arraya[0] = "
                + arraya[0].toString() + ", arraya[1] = "
                + arraya[1].toString());
        System.out.println("物件陣列交換前:arrayb[0] = "
                + arrayb[0].toString() + ", arrayb[1] = "
                + arrayb[1].toString());
        
        // 交換這兩個物件陣列
        Person.swapObjectArray(arraya, arrayb);
        System.out.println('\n' + "物件陣列交換後:arraya[0] = "
                + arraya[0].toString() + ", arraya[1] = "
                + arraya[1].toString());
        System.out.println("物件陣列交換後:arrayb[0] = "
                + arrayb[0].toString() + ", arrayb[1] = "
                + arrayb[1].toString());

        // 建立兩個普通陣列
        int[] a = new int[2];
        int[] b = new int[2];

        // 給陣列個元素賦值
        for (int i = 0; i < a.length; i++) {
            a[i] = i;
            b[i] = i + 1;
        }

        System.out.println('\n' + "基本型別陣列交換前:a[0] = " + a[0] + ", a[1] = " + a[1]);
        System.out.println("基本型別陣列交換前:b[0] = " + b[0] + ", b[1] = " + b[1]);

        // 交換兩個基本型別陣列
        Person.swapIntArray(a, b);
        System.out.println('\n' + "基本型別陣列交換後:a[0] = " + a[0] + ", a[1] = " + a[1]);
        System.out.println("基本型別陣列交換後:b[0] = " + b[0] + ", b[1] = " + b[1]);
        
        // 改變物件陣列的內容
        Person.changeObjectArray(arraya, arrayb);
        System.out.println('\n' + "物件陣列內容交換並改變後:arraya[1] = " + arraya[1].toString());
        System.out.println("物件陣列內容交換並改變後:arrayb[1] = " + arrayb[1].toString());
        
        // 改變基本型別陣列的內容
        Person.changeIntArray(a, b);
        System.out.println('\n' + "基本型別陣列內容交換並改變後:a[1] = " + a[1]);
        System.out.println("基本型別陣列內容交換並改變後:b[1] = " + b[1]);
    }
}

程式有些囉嗦,但能反映問題,該程式執行結果為:

物件交換前:p1 = Alexia female
物件交換前:p2 = Edward male

物件交換後:p1 = Alexia female
物件交換後:p2 = Edward male

物件陣列交換前:arraya[0] = Alexia female, arraya[1] = Edward male
物件陣列交換前:arrayb[0] = jmwang female, arrayb[1] = hwu male

物件陣列交換後:arraya[0] = Alexia female, arraya[1] = Edward male
物件陣列交換後:arrayb[0] = jmwang female, arrayb[1] = hwu male

基本型別陣列交換前:a[0] = 0, a[1] = 1
基本型別陣列交換前:b[0] = 1, b[1] = 2

基本型別陣列交換後:a[0] = 0, a[1] = 1
基本型別陣列交換後:b[0] = 1, b[1] = 2

物件陣列內容交換並改變後:arraya[1] = wjl male
物件陣列內容交換並改變後:arrayb[1] = Edward male

基本型別陣列內容交換並改變後:a[1] = 5
基本型別陣列內容交換並改變後:b[1] = 1

說明:不管是物件、基本型別還是物件陣列、基本型別陣列,在函式中都不能改變其實際地址但能改變其中的內容。

 

27. 下面的方法,當輸入為2的時候返回值是多少?()

public static int getValue(int i) {
        int result = 0;
        switch (i) {
        case 1:
            result = result + i;
        case 2:
            result = result + i * 2;
        case 3:
            result = result + i * 3;
        }
        return result;
    }

A0                    B2                    C4                     D10

答案:D

解析:注意這裡case後面沒有加break,所以從case 2開始一直往下執行。

 

28. 選項中哪一行程式碼可以替換題目中//add code here而不產生編譯錯誤?()

public abstract class MyClass {

     public int constInt = 5;
     //add code here
     public void method() {
     }
}

Apublic abstract void method(int a);

B constInt = constInt + 5;

C public int method();

D public abstract void anotherMethod() {}

答案:A

解析:考察抽象類的使用。

抽象類遵循的原則:

(1)abstract關鍵字只能修飾類和方法,不能修飾字段

(2)抽象類不能被例項化(無法使用new關鍵字建立物件例項),只能被繼承。

(3)抽象類可以包含屬性,方法,構造方法,初始化塊,內部類,列舉類,和普通類一樣,普通方法一定要實現,變數可以初始化或不初始化但不能初始化後在抽象類中重新賦值或操作該變數(只能在子類中改變該變數)。

(4)抽象類中的抽象方法(加了abstract關鍵字的方法)不能實現。

(5)含有抽象方法的類必須定義成抽象類。

 

擴充套件:抽象類和介面的區別,做個總結吧:

(1)介面是公開的,裡面不能有私有的方法或變數,是用於讓別人使用的,而抽象類是可以有私有方法或私有變數的。

(2)abstract class 在 Java 語言中表示的是一種繼承關係,一個類只能使用一次繼承關係。但是,一個類卻可以實現多個interface,實現多重繼承。介面還有標識(裡面沒有任何方法,如Remote介面)和資料共享(裡面的變數全是常量)的作用。

(3)在abstract class 中可以有自己的資料成員,也可以有非abstarct的成員方法,而在interface中,只能夠有靜態的不能被修改的資料成員(也就是必須是 static final的,不過在 interface中一般不定義資料成員),所有的成員方法預設都是 public abstract 型別的。

(4)abstract class和interface所反映出的設計理念不同。其實abstract class表示的是"is-a"關係,interface表示的是"has-a"關係。

(5)實現介面的一定要實現接口裡定義的所有方法,而實現抽象類可以有選擇地重寫需要用到的方法,一般的應用裡,最頂級的是介面,然後是抽象類實現介面,最後才到具體類實現。抽象類中可以有非抽象方法。介面中則不能有實現方法。

(6)介面中定義的變數預設是public static final 型,且必須給其初值,所以實現類中不能重新定義,也不能改變其值。抽象類中的變數預設是 friendly 型,其值可以在子類中重新定義,也可以在子類中重新賦值。

 

最後,給大家出個思考題:下面程式的執行結果是什麼?

public class Dervied extends Base {

    private String name = "dervied";

    public Dervied() {
        tellName();
        printName();
    }
    
    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }
    
    public void printName() {
        System.out.println("Dervied print name: " + name);
    }

    public static void main(String[] args){
        
        new Dervied();    
    }
}

class Base {
    
    private String name = "base";

    public Base() {
        tellName();
        printName();
    }
    
    public void tellName() {
        System.out.println("Base tell name: " + name);
    }
    
    public void printName() {
        System.out.println("Base print name: " + name);
    }
}

輸出結果

Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied