1. 程式人生 > >java基礎知識點、面試選擇題歸納整理

java基礎知識點、面試選擇題歸納整理

前言

之前刷了一些題,為了方便自己日後可以快速的查缺補漏以及方便有需要的人,這裡整理了一些個人感覺比較有意義的選擇題,題目均來自牛客網的java相關選擇題。
如各位看官發現哪裡寫的不對的,請幫忙指出,謝謝。
題目基本就更新完了(2018.01.30)
ps:答案設定成了白色字型,想要看答案,只需選中答案那一行即可看到答案。

正文

一、陷阱之i等於i自增

1.以下程式碼執行的結果顯示是多少()?

public class Inc {
    public static void main(String args[]){
        Inc inc = new Inc();
        int
i = 0; inc.fermin(i); i = i++; System.out.println(i); } void fermin(int i){ i++; } }

A. 0
B. 1
C. 2
D. 3

答案:A
筆記:
對於i=i++,先是執行i++,i自增1並返回自增前的值,因此如果i的初始值為0,那麼執行i++,i變成1,並返回自增前的值,也就是0,再執行賦值操作,因此i=0。故先A。記住,遇到i=i++,i的值不變。

2.檢查程式,是否存在問題,如果存在指出問題所在,如果不存在,說明輸出結果()

public class Demo {
    public static void main(String args[]){
        int count = 0;
        int num = 0;
        for(int i=0;i<=100;i++){
            num = num+i;
            count = count++;
        }
        System.out.println(num*count);
    }
}

A. 505000
B. 0
C. 執行時錯誤
D. 5050

答案:B

二、正則表示式

1.Java中用正則表示式擷取字串中第一個出現的英文左括號之前的字串。比如:北京市(海淀區)(朝陽區)(西城區),擷取結果為:北京市。正則表示式為()
A. “.*?(?=\()”
B. “.*?(?=()”
C. “.*(?=\()”
D. “.*(?=()”

答案:A
筆記:
.表示匹配\n除外的任何字元,*表示匹配0個或多個,.*後接?表示非貪婪匹配,(?=pattern),即正向肯定預查,這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。因此(?=\\()表示匹配左括號。
正則表示式參考網址:http://www.runoob.com/regexp/regexp-metachar.html

2.關於下面的程式,哪一個選項是正確的輸出結果()

    public static void main (String[] args) { 
        String classFile = "com.jd.". replaceAll(".", "/") + "MyClass.class";
        System.out.println(classFile);
    }

A. com. jd
B. com/jd/MyClass.class
C. ///////MyClass.class
D. com.jd.MyClass

答案:C
筆記:
public String replaceAll(String regex,String replacement),有該方法的宣告可知第一個引數是regex,即表示正則表示式的字串,而在正則表示式中,”.”表示的是除\n外的任意字元,因此原字串中的”com.jd.”都會被替換成”/”,如果想要達到正確的結果,則需要使用轉移符號,即改成:replaceAll(“.”, “/”),這樣就能得到”com/jd/MyClass.class”這個結果。

三、關鍵字相關題目

1.關於下面的程式,說法正確的是()

public class Demo {
    static String x = "1";
    static int y = 1;
    public static void main (String[] args) { 
        static int z= 2;
        System.out.println(x+y+z);
    }       
}

A. 3
B. 112
C. 13
D. 程式有編譯錯誤

答案:D
筆記:
static變數是類變數,不能定義在方法中。

2.true、false、null、sizeof、goto、synchronized 哪些是Java關鍵字()
A. true
B. false
C. null
D. sizeof
E. goto
F. synchronized

答案:EF
筆記:
goto和const既是保留字也是關鍵字。除了這兩個之外,關鍵字和保留字是嚴格區分開的。
null、true、false是保留字。所謂保留字,就是現有java版本尚未使用,但以後版本可能會作為關鍵字使用。
關鍵字有50個,保留字有14個。
而sizeof是C語言中的關鍵字,在java中並不是。

3.以下關於final關鍵字說法錯誤的是()
A final是java中的修飾符,可以修飾類、介面、抽象類、方法和屬性
B final修飾的類肯定不能被繼承
C final修飾的方法不能被過載
D final修飾的變數不允許被再次賦值

答案:C
筆記:
final修飾類表示類不可被繼承,修飾方法表示方法不可被重寫,修飾常量表示引用不可改。

4.有關下述Java程式碼描述正確的選項是()

public class TestClass {
   private static void testMethod(){
        System.out.println("testMethod");
   }
   public static void main(String[] args) {
        ((TestClass)null).testMethod();
   }
}

A. 編譯不通過
B. 編譯通過,執行異常,報NullPointerException
C. 編譯通過,執行異常,報IllegalArgumentException
D. 編譯通過,執行異常,報NoSuchMethodException
E. 執行正常,輸出testMethod

答案:E
筆記:
將null強制轉換成TestClss型別,因此((TestClass)null)這個整體也就表示一個TestClass型別的物件了。
static成員屬於類成員(成員包括屬性和方法),static方法,即類方法,可以直接通過”類名.方法名”進行訪問。但static方法也可以通過”物件.方法名”進行訪問,也就是說我們可以通過類的例項去呼叫,但其實質是轉化成類來呼叫。

5.在使用super和this關鍵字時,以下描述正確的是()
A 在子類構造方法中使用super()顯示呼叫父類的構造方法,super()必須寫在子類構造方法的第一行,否則編譯不通過
B super()和this()不一定要放在構造方法內第一行
C this()和super()可以同時出現在一個建構函式中
D this()和super()可以在static環境中使用,包括static方法和static語句塊

答案:A
筆記:
this()和super()都必須寫在第一行,因此不能同時出現在同一個構造方法中。
super()呼叫的是父類的構造器,this()呼叫的是同一類中過載的構造器。
static方法中無法使用this關鍵字。且this()和super()只能出現在構造方法中。

6.關鍵字super的作用是()
A 用來訪問父類被隱藏的非私有成員變數
B 用來呼叫父類中被重寫的方法
C 用來呼叫父類的建構函式
D 以上都是

答案:D
筆記:
super代表父類對應的物件,所以用super訪問在子類中無法直接使用的父類成員和方法、以及構造方法。

四、多型

1.對於如下程式碼段,可以放入到橫線位置,使程式正確編譯執行,而且不產生錯誤的選項是( )

class A{
    public A foo(){
        return this;
    }
}
class B extends A{
    public A foo(){
        return this;
    }
}
class C extends B{
    _______
}

A. public void foo(){}
B. public int foo(){return 1;}
C. public A foo(B b){return b;}
D. public A foo(){return A;}

答案:C
筆記:
A和B,返回值型別類B的foo方法不一樣,因此,不可能是方法重寫,因此,只有可能是方法過載,但是方法過載又要求方法形參列表不同,因此,也不可能是方法過載。因此,無法通過編譯。
繼續分析C,由於C選項的foo方法形參列表與類B的形參列表不同,因此,不可能是方法重寫,只可能是方法過載,而C選項的foo方法滿足方法過載的所有要求(方法名相同、形參列表不同),因此C選項正確。
D,返回A,編譯不通過。

2.(多選題)類Parent和Child定義如下,對於如下程式碼段,可以放入到橫線位置,使程式正確編譯執行,而且不產生錯誤的選項是( )

class Parent{
    public float aFun(float a, float b){return a+b;}
}
class Child extends Parent{
    _______
}

A. float aFun(float a, float b){return a+b;}
B. public int aFun(int a, int b){return a+b;}
C. public float aFun(float a, float b){return a+b;}
D. private int aFun(int a, int b){return a+b;}

答案:BCD
筆記:
對於BD選項,方法形參列表不同,因此不可能是方法重寫,只可能是方法過載,方法過載的條件是方法名相同,形參列表不同,因此,滿足條件。(方法過載無關返回值型別和方法訪問許可權)。
對於AC選項,形參列表相同,因此不可能是方法過載,因此只可能是方法重寫。方法重寫條件:兩同兩小一大,C均滿足,A不滿足”一大”。
具體分析A,普通類方法的預設訪問許可權是包內訪問許可權,也就是default,但不能顯式的寫default,由於父類的aFun方法的訪問許可權是public,對於方法重寫,子類重寫的方法的訪問許可權不應小於父類方法的訪問許可權,故A錯。

3.以下方法,哪個不是對add方法的過載()

public class Test{
    public void add( int x,int y,int z){}
}

A. public int add(int x,int y,float z){return 0;}
B. public int add(int x,int y,int z){return 0;}
C. public void add(int x,int y){}
D. 以上都不是

答案:B
筆記:
過載規則:方法名相同,形參列表不同。B的話形參列表和方法名都與Test類中的add方法相同。返回值型別、方法訪問許可權無關過載。

4.下列哪些針對程式碼執行結果的描述是正確的?

public class Car extends Vehicle
{
    public static void main (String[] args)
    {
        new Car().run();
    }
    private final void run()
    {
        System.out.println ("Car");
    }
}
class Vehicle
{
    private final void run()
    {
        System.out.println("Vehicle");
    }
}

A. Car
B. Vehicle
C. Compiler error
D. Exception thrown at runtime

答案:A
筆記:
final修飾方法表方法不可重寫。而private修飾方法,表示該方法是私有方法,即子類不可見。因此在這道題中,類Car中的run方法和父類Vehicle中的run方法沒有任何關係。也就是說不存在方法重寫與過載。因此,當執行new Car()時,執行的是子類的run方法。並且沒有編譯錯誤,執行時也不會出現異常。

5.下列程式執行後結果為( )

class A {
    public int func1(int a, int b) {
        return a - b;
    }
}
class B extends A {
    public int func1(int a, int b) {
        return a + b;
    }
}
public class ChildClass {
    public static void main(String[] args) {
    A a = new B();
    B b = new B();
    System.out.println("Result=" + a.func1(100, 50));
    System.out.println("Result=" + b.func1(100, 50));
    }
}

A Result=150 Result=150
B Result=100 Result=100
C Result=100 Result=150
D Result=150 Result=100

答案:A
筆記:
對於方法,非靜態:編譯看左,執行看右 靜態成員變數:都看左。
1.成員變數:編譯和執行都參考左邊。
2.成員函式(非靜態):編譯看左邊,執行看右邊
3.靜態函式:編譯和執行都看左邊。

6.這個程式的輸出結果是()

package Wangyi;
class Base
{
    public void method()
    {
        System.out.println("Base");
    } 
}
class Son extends Base
{
    public void method()
    {
        System.out.println("Son");
    }

    public void methodB()
    {
        System.out.println("SonB");
    }
}
public class Test01
{
    public static void main(String[] args)
    {
        Base base = new Son();
        base.method();
        base.methodB();
    }
}

A Base SonB
B Son SonB
C Base Son SonB
D 編譯不通過

答案:D
筆記:
Base base = new Son();
這句new 了一個派生類,賦值給基類,所以下面的操作編譯器認為base物件就是Base型別的
Base類中不存在methodB()方法,所以編譯不通過。
對於成員函式:編譯看左邊,執行看右邊。意思編譯時候,看左邊有沒有該方法,執行的時候結果看 new 的物件是誰,就呼叫的誰。
1.成員變數:編譯和執行都參考左邊。
2.成員函式(非靜態):編譯看左邊,執行看右邊
3.靜態函式:編譯和執行都看左邊。

7.在java的多型呼叫中,new的是哪一個類就是呼叫的哪個類的方法()
A 對
B 錯

答案:B
筆記:
1.成員變數:編譯和執行都參考左邊。
2.成員函式(非靜態):編譯看左邊,執行看右邊
3.靜態函式:編譯和執行都看左邊。
舉例如:

public class Demo{
    public static void main(String[] args){
        P p = new M();
        p.f();
    }
}
class P{
    public static void f(){
        System.out.println("parent");
    }
}
class M extends P{
}

儘管new的是類M,但呼叫的是父類P的方法,除非子類重寫了該方法。

8.對於如下程式碼段

class A{
    public A foo(){return this;}
}
class B extends A{
    public A foo(){
        return this;
    }
}
class C extends B
{
    _______
}

可以放入到橫線位置,使程式正確編譯執行,而且不產生錯誤的選項是( )
A public void foo(){}
B public int foo(){return 1;}
C public A foo(B b){return b;}
D public A foo(){return A;}

答案:C
筆記:
很明顯考的是過載和重寫,對於AB,返回值型別與父類方法的返回值型別不同,因此肯定不是方法重寫,因此只可能是方法過載,但過載又要求形參列表必須不同,因此也不可能是過載,因此AB編譯不通過。對於D,返回A,很明顯是有問題的。對於C,形參列表不同,因此是方法過載。

總結:方法重寫Override與方法過載Overload

方法重寫:
子類方法重寫父類方法遵循“兩同兩小一大”的規則:
“兩同”即方法名相同,形參列表相同
“兩小”指的是子類方法返回值型別應比父類方法返回值型別更小或相等,子類方法宣告丟擲的異常類應比父類方法宣告丟擲的異常類更小或相等(這是因為返回值型別更大或者更小,是對於同一型別而言的。也就是說,返回值的型別需要有繼承關係才去考慮大小這個概念。型別不同,肯定不是方法重寫)
“一大”指的是子類方法的訪問許可權應比父類方法的訪問許可權更大或相等。
方法過載:
過載是一個類中出現多個同名的方法。因此,我們考慮過載,是在類中有同名方法的前提下。
方法名相同、方法形參列表不同。

五、修飾符問題

1.以下宣告合法的是()
A. default String s;
B. public final static native int w();
C. abstract double d;
D. abstract final double hyperboilcCosine;(hyperboilcCosine譯為雙曲餘弦函式)

答案:B
筆記:
在類中不能顯式的用default修飾變數和方法,但如果沒有寫訪問許可權控制符,預設是包許可權。因此A錯。abstract表抽象,不能修飾變數,因此C錯。abstract和final不能同時方法或類,因此D錯。static表靜態屬性,final修飾方法表方法不可重寫,B選項宣告合法。
補充:
普通類中方法和屬性的預設訪問許可權是default,但不能顯示的寫default。
抽象類的方法和屬性的預設訪問許可權,在jdk1.7中是protected,在jdk1.8中是default。jdk1.8中這樣做是為了讓抽象類與其實現類必須在同一個包中。
介面的方法和屬性的預設訪問許可權,是public。
普通類與抽象類中的方法和屬性,不能使用default修飾。介面中的方法,可以用default修飾,表示預設方法,且該方法必須有實現體。

2.下面哪一個是正確的輸出結果()

class Foo {
    final int i;
    int j;
    public void doSomething() {
        System.out.println(++j + i);
    }
}

A 0
B 1
C 2
D 不能執行,因為編譯有錯

答案:D
筆記:
final修飾的成員在使用前必須初始化。
再看下面的例子:

public static void main(String[] args){
    for(int i=0;i<10;i++){
        final int a = i;
        System.out.print(a+" ");
    }
}

我們知道被final修飾的變數只能賦值一次,那麼上面的例子會出現編譯問題嗎。不會,因為遍歷a的作用於只在花括號之間,每輪迴圈結束,變數a就被回收了。也就是說每輪迴圈的變數a都是不一樣的。

六、基本資料型別的自動轉換與強制轉換

1.(多選題)Java類Demo中存在方法func1、func2、func3和func4,請問該方法中,哪些是不合法的定義( )

public class Demo{
    float func1(){
        int i=1;
        return;
    }
    float func2(){
        short i=2;
        return i;
    }
    float func3(){
        long i=3;
        return i;
    }
    float func4(){
        double i=4;
        return i;
    }
}

A. func1
B. func2
C. func3
D. func4

答案:AD
筆記:
資料型別的轉換,分為自動轉換和強制轉換。
資料型別從大到小需要強制轉換,必須在程式碼中宣告,由小到大不需要,會自動轉換。
不同型別資料間的優先關係如下:byte、short、char < int < long

byte b1=1,b2=2,b3,b6,b8;
final byte b4=4,b5=6,b7;
b3=(b1+b2);  /*語句1*/
b6=b4+b5;    /*語句2*/
b8=(b1+b4);  /*語句3*/
b7=(b2+b5);  /*語句4*/
System.out.println(b3+b6);

A. 語句1
B. 語句2
C. 語句3
D. 語句4

答案:ACD
筆記:
byte、short、char型別的任意兩個資料想要進行運算操作,都會先轉換成int型別,再進行運算操作。final修飾的變數例外。
對於A,當執行b1+b2,b1和b2會先轉換成int型別,再進行加運算,再把結果賦給byte型別的b3,就會出現資料型別由大到小,從而出現編譯錯誤。故A錯。
對於B,當執行b4+b5,由於b4和b5都是final型別的,因此不會自動轉換成int型別,所以和的型別視左邊變數型別而定,故B對。
對於C,當執行b1+b4,雖然b4是final型別,不會自動轉換成int型別,但是b1會轉化成int型別,故和的型別為int型別,因此由於缺少強制轉換,故C錯。
對於D,同C一個道理,故D錯。

3.下面有關java基本型別的預設值和取值範圍,說法錯誤的是()
A 位元組型的型別預設值是0,取值範圍是-2^7—2^7-1
B boolean型別預設值是false,取值範圍是true\false
C 字元型型別預設是0,取值範圍是-2^15 —2^15-1
D long型別預設是0,取值範圍是-2^63—2^63-1

答案:B
筆記:

七、原碼反碼補碼

1.經過強制型別轉換以後,變數a,b的值分別為多少()

short a = 128;
byte b = (byte)a;

A. 128 127
B. 128 -128
C. 128 128
D. 編譯錯誤

答案:B
筆記:
一個short變數佔2個位元組,即16位,一個byte變數佔1個位元組,即8位。
a的二進位制表示為:00000000 10000000,把a強制轉換成byte型別再賦給b,因此高8位被截掉,剩下低8位,故b的二進位制表示為:10000000。原碼10000000表示的是-0,計算機中使用的是補碼,原碼10000000對應的補碼在計算機中表示的是-128。

2.變數a是一個64位有符號的整數,初始值用16進製表示為:0x 7FFFFFFF FFFFFFFF,變數b是一個64位有符號的整數,初始值用16進製表示為:0x 80000000 00000000,則a+b的結果用10進製表示為()
A. 1
B. -1
C. 2^63+2^62+…+2^2+2^1+2^0
D. -(2^63+2^62+…+2^2+2^1+2^0)

答案:B
筆記:
a+b,得到的結果的16進製表示為0x FFFFFFFF FFFFFFFF,這是計算機中表示的方式,即反碼的表示,負數的反碼等於原碼(除符號位外的其他位)取反+1,由此可得原碼為0x 10000000 00000001,其10進製表示為-1,故答案選B。

3.變數a是一個64位有符號的整數,初始值用16進製表示為:0Xf000000000000000; 變數b是一個64位有符號的整數,初始值用16進製表示為:0x7FFFFFFFFFFFFFFF。 則a-b的結果用10進製表示為多少?()
A 1
B -(2^62+2^61+2^60+1)
C 2^62+2^61+2^60+1
D 2^59+(2^55+2^54+…+2^2+2^1+2^0)

答案:C
筆記:
0Xf000000000000000補碼為1111000000000000000000000000000000000000000000000000000000000000
0x7FFFFFFFFFFFFFFF補碼為0111111111111111111111111111111111111111111111111111111111111111
a-b=a+(-b)=
1111000000000000000000000000000000000000000000000000000000000000+
1000000000000000000000000000000000000000000000000000000000000001=
10111000000000000000000000000000000000000000000000000000000000001(高位溢位捨去)
則結果為
0111000000000000000000000000000000000000000000000000000000000001=
2^62+2^61+2^60+1
其中利用到了~n=-n-1這個公式。

4.檢查程式,是否存在問題,如果存在指出問題所在,如果不存在,說明輸出結果()

int i = 5;
int j = 10;
System.out.println(i+~j);

A. 15
B. -5
C. -6
D. 編譯錯誤

答案:C
筆記:
~是位運算子號,表示取反。有個公式,-n-1=~n。因此i+~j=i+(-j-1)=5-10-1=-6。
或者這麼做:
先計算出j在計算機中的表示方式,即j的補碼,從而算出j的補碼取反,再求5的補碼,再相加(在計算機中都是以補碼的進行操作),再轉換成原碼,從而轉換成10進製表示。
10是正數,因此反碼等於補碼為00001010,取反得到11110101,5的補碼為00000101,
01110101+
10000101=
11111010,這是反碼錶示,其對應的原碼為10000110,轉換成10進位制,即-6。
ps:負數的補碼時:符號位位1,其餘各位求反,末位加1。

八、編碼轉碼

1.下面哪段程式能夠正確的實現了GBK編碼位元組流到UTF-8編碼位元組流的轉換()

byte[] src,dst;

A dst=String.fromBytes(src,”GBK”).getBytes(“UTF-8”)
B dst=new String(src,”GBK”).getBytes(“UTF-8”)
C dst=new String(“GBK”,src).getBytes()
D dst=String.encode(String.decode(src,”GBK”)),”UTF-8” )

答案:B
筆記:
先用new String(src,”GBK”)解碼得到字串,再用getBytes(“UTF-8”)得到UTF8編碼位元組陣列。
String(byte[] b,Charset charset):通過使用指定的charset解碼指定的byte陣列,構造一個新的String。
還有一種情形是new String(str.getBytes(“gbk”),”utf-8”);先通過getBytes方法得到gbk編碼位元組陣列,再通過new String(bytes,”utf-8”)解碼得到字串。

九、import相關問題

1.有一個原始碼,只包含import java.util.*,這是一個import語句,下面敘述正確的是()
A. 只能寫在原始碼的第一句
B. 可以訪問java/util目錄下及其子目錄下的所有類
C. 能訪問java/util目錄下的所有類,不能訪問java/util子目錄下的所有類
D. 編譯錯誤

答案:C
筆記:
匯入包,只能訪問該包目錄下的類,而不包括其子目錄中的類。
package語句必須寫在第一行(忽略註釋行),import語句寫在package語句之後,類的定義之前。

十、陷阱之有參建構函式

1.以下輸出的結果為()

class Base{
    public Base(String s){
        System.out.println("B");
    }
}
public class X extends Base{
    public X(String s){
        System.out.println("D");
    }
    public static void main(String args[]){
        new X("C");
    }
}

A. BD
B. DB
C. C
D. 編譯錯誤

答案:D
筆記:
當父類只有有參建構函式而沒定義無參建構函式的時候,要特別小心。子類必須顯式呼叫父類的構造方法,否則,編譯將不能通過。如果父類中定義了無參建構函式,子類會預設呼叫父類的無參建構函式。

十一、JVM相關問題

1.對於JVM記憶體配置引數:
-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRadio=3,其最小記憶體值和Survivor區總大小分別是()
A. 5120m,1024m
B. 5120m,2048m
C. 10240m,1024m
D. 10240m,2048m

答案:D
筆記:
-Xms:初始堆大小
-Xmx:最大堆大小
-Xmn:年輕代大小
-Xss:每個執行緒的堆疊大小
-XXSurvivorRadio:年輕代中Eden區與Survivor區的大小比值。
-XX:NewRatio:設定年輕代與年老代的比值
Survivor區有兩個(又稱為from區和to區),因此一個Survivor區佔總的年輕代記憶體的五分之一。

2.下列哪項不屬於jdk1.6垃圾收集器()
A. Serial收集器
B. parNew收集器
C. CMS收集器
D. G1收集器

答案:D
筆記:
這裡寫圖片描述

3.(多選題)以下哪些jvm的垃圾回收方式採用的是複製演算法回收 1/1
A. 新生代序列收集器
B. 老年代序列收集器
C. 新生代並行回收收集器
D. 老年代並行回收收集器
E. cms收集器

答案:AC
筆記:
新生代採用複製演算法,老年代採用標記-整理或標記-清除演算法。
MS表示 mark sweep,即標記-清除,
MC表示 mark compression,即標記-壓縮,
因此,CMS表示Concurrent Mark Sweep,即多執行緒標記-清除。
G1收集器比較特殊,整體上是基於標記-整理,區域性採用複製。

4.下列Java程式碼中的變數a、b、c分別在記憶體的__儲存區存放。

class A {
    private String a = “aa”;
    public boolean methodB() {
        String b = “bb”;
        final String c = “cc”;
    }
}

A 堆區、堆區、堆區
B 堆區、棧區、堆區
C 堆區、棧區、棧區
D 堆區、堆區、棧區
E 靜態區、棧區、堆區
F 靜態區、棧區、棧區

答案:C
筆記:
a是類中的成員變數的引用,存放在堆中,b和c都是方法中的區域性變數的引用,存在於棧中。

5.下面輸出順序順序的是()

class B extends Object{
    static{
        System.out.println("Load B");
    }
    public B(){
        System.out.println("Create B");
    }
}
class A extends B{
    static{
        System.out.println("Load A");
    }
    public A(){
        System.out.println("Create A");
    }
}

public class Testclass{
    public static void main(String[] args){
        new A();
    }
}

A. Load B ->Create B->Load A -> Create A
B. Load B -> Load A ->Create B ->Create A
C. Load B -> Create B-> Create A -> Load A
D. Create B ->Create A ->Load B ->Load A

答案:B
筆記:
類的載入順序:
1. 初始化父類中的靜態成員變數和靜態程式碼塊 ;
2. 初始化子類中的靜態成員變數和靜態程式碼塊 ;
3. 初始化父類的普通成員變數和程式碼塊,再執行父類的構造方法;
4. 初始化子類的普通成員變數和程式碼塊,再執行子類的構造方法;

6.下面對JVM敘述不正確的是:()
A JVM的全稱是Java Virtual Machine
B JVM是一種計算機硬體技術,它是Java程式的執行平臺
C JVM是在計算機硬體系統上用軟體實現的一臺假想機
D Java程式在執行時.JVM把Java位元組碼解釋成機器碼

答案:B
筆記:
JVM即Java Virtual Machine,即Java虛擬機器,是在現有的平臺如windows、linux上執行的一個軟體,虛擬出一臺介面統一的計算機,實現java語言的跨平臺特性。
Java虛擬機器是一顆可執行Java位元組碼的虛擬機器程序。Java原始檔被編譯成能被Java虛擬機器執行的位元組碼檔案。Java被設計稱允許應用程式可以執行在任意的平臺,而不需要程式設計師為每一個平臺單獨重寫或者是重新編譯。Java虛擬機器讓這個變成可能,因為它知道底層硬體平臺的指令長度和其他特性。

7.下面有關JVM記憶體,說法錯誤的是?()
A 程式計數器是一個比較小的記憶體區域,用於指示當前執行緒所執行的位元組碼執行到了第幾行,是執行緒隔離的
B 虛擬機器棧描述的是Java方法執行的記憶體模型,用於儲存區域性變數,運算元棧,動態連結,方法出口等資訊,是執行緒隔離的
C 方法區用於儲存JVM載入的類資訊、常量、靜態變數、以及編譯器編譯後的程式碼等資料,是執行緒隔離的
D 原則上講,所有的物件都在堆區上分配記憶體,是執行緒之間共享的

答案:C
筆記:
方法區在JVM中也是一個非常重要的區域,它與堆一樣,是被執行緒共享的區域。在方法區中,儲存了每個類的資訊(包括類的名稱、方法資訊、欄位資訊)、靜態變數、常量以及編譯器編譯後的程式碼等。

8.以下程式碼的輸出結果是()

public class B {
    public static B t1 = new B();
    public static B t2 = new B();
    {
        System.out.println("構造塊");
    }
    static {
        System.out.println("靜態塊");
    }
    public static void main(String[] args) {
        B t = new B();
    }
}

A 靜態塊 構造塊 構造塊 構造塊
B 構造塊 靜態塊 構造塊 構造塊
C 構造塊 構造塊 靜態塊 構造塊
D 構造塊 構造塊 構造塊 靜態塊

答案:C
筆記:
開始時JVM載入B.class,先執行靜態成員的宣告,再執行main方法。另外,每一次例項化物件時,先執行構造塊,再執行建構函式塊。

9.When is the text “Hi there”displayed?()

public class StaticTest
{
    static
    {
        System.out.println(“Hi there”);
    }

    public void print()
    {
        System.out.println(“Hello”);
    }

    public static void main(String args[])
    {
        StaticTest st1 = new StaticTest();
        st1.print();
        StaticTest st2 = new StaticTest();
        st2.print();
    }
}

A Never.
B Each time a new object of type StaticTest is created.
C Once when the class is loaded into the Java virtual machine.
D Only when the main() method is executed.

答案:C
筆記:
被static修飾的屬性或方法,僅在類第一次載入進JVM時執行。

10.What will be printed when you execute the following code?

class C {
    C() {
        System.out.print("C");
    }
}

class A {
    C c = new C();

    A() {
        this("A");
        System.out.print("A");
    }

    A(String s) {
        System.out.print(s);
    }
}

class Test extends A {
    Test() {
        super("B");
        System.out.print("B");
    }

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

A BB
B CBB
C BAB
D None of the above

答案:B
筆記:
1.首先,初始化父類中的靜態成員變數和靜態程式碼塊,按照在程式中出現的順序初始化;
2.然後,初始化子類中的靜態成員變數和靜態程式碼塊,按照在程式中出現的順序初始化;
3.其次,初始化父類的普通成員變數和程式碼塊,再執行父類的構造方法;
4.最後,初始化子類的普通成員變數和程式碼塊,再執行子類的構造方法;
其中,super(“B”); 表示呼叫父類的構造方法,不呼叫父類的無參建構函式。

11.下面有關java classloader說法正確的是()?
A ClassLoader就是用來動態載入class檔案到記憶體當中用的
B JVM在判定兩個class是否相同時,只用判斷類名相同即可,和類載入器無關
C ClassLoader使用的是雙親委託模型來搜尋類的
D Java預設提供的三個ClassLoader是Boostrap ClassLoader,Extension ClassLoader,Application ClassLoader

答案:ACD
筆記:
from牛客網使用者“早起吃蟲啦”:

JDK中提供了三個ClassLoader,根據層級從高到低為:
Bootstrap ClassLoader,主要載入JVM自身工作需要的類。
Extension ClassLoader,主要載入%JAVA_HOME%\lib\ext目錄下的庫類。
Application ClassLoader,主要載入Classpath指定的庫類,一般情況下這是程式中的預設類載入器,也是ClassLoader.getSystemClassLoader() 的返回值。(這裡的Classpath預設指的是環境變數中配置的Classpath,但是可以在執行Java命令的時候使用-cp 引數來修改當前程式使用的Classpath)
JVM載入類的實現方式,我們稱為 雙親委託模型:
如果一個類載入器收到了類載入的請求,他首先不會自己去嘗試載入這個類,而是把這個請求委託給自己的父載入器,每一層的類載入器都是如此,因此所有的類載入請求最終都應該傳送到頂層的Bootstrap ClassLoader中,只有當父載入器反饋自己無法完成載入請求時,子載入器才會嘗試自己載入。
雙親委託模型的重要用途是為了解決類載入過程中的安全性問題。
假設有一個開發者自己編寫了一個名為Java.lang.Object的類,想借此欺騙JVM。現在他要使用自定義ClassLoader來載入自己編寫的java.lang.Object類。然而幸運的是,雙親委託模型不會讓他成功。因為JVM會優先在Bootstrap ClassLoader的路徑下找到java.lang.Object類,並載入它。

from牛客網使用者“大蘿蔔小蘿蔔”:

  1. 什麼是類載入器?
    把類載入的過程放到Java虛擬機器外部去實現,讓應用程式決定如何去獲取所需要的類。實現這個動作的程式碼模組稱為“類載入器”。
  2. 有哪些類載入器,分別載入哪些類
    類載入器按照層次,從頂層到底層,分為以下三種:
    (1)啟動類載入器 : 它用來載入 Java 的核心庫,比如String、System這些類
    (2)擴充套件類載入器 : 它用來載入 Java 的擴充套件庫。
    (3) 應用程式類載入器 : 負責載入使用者類路徑上所指定的類庫,一般來說,Java 應用的類都是由它來完成載入的。
  3. 雙親委派模型
    我們應用程式都是由以上三種類載入器互相配合進行載入的,還可以加入自己定義的類載入器。稱為 類載入器的雙親委派模型 ,這裡類載入器之間的父子關係一般不會以繼承的關係來實現,而是都使用 組合關係 來複用父載入器的。
  4. 雙親委託模型的工作原理
    是當一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器去完成,每一個層次的類載入都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有當父載入器反饋自己無法載入這個載入請求的時候,子載入器才會嘗試自己去載入。
  5. 使用雙親委派模型好處?(原因)
    第一:可以避免重複載入,當父親已經載入了該類的時候,子類不需要再次載入。
    第二:考慮到安全因素,如果不使用這種委託模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義型別,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因為String已經在啟動時被載入,所以使用者自定義類是無法載入一個自定義的類裝載器。

十二、網路程式設計

1.關於Socket通訊程式設計,以下描述錯誤的是:( )
A 伺服器端通過new ServerSocket()建立TCP連線物件
B 伺服器端通過TCP連線物件呼叫accept()方法建立通訊的Socket物件
C 客戶端通過new Socket()方法建立通訊的Socket物件
D 客戶端通過new ServerSocket()建立TCP連線物件

答案:D
筆記:
伺服器端通過new ServerSocket()建立TCP連線物件,通過TCP連線物件呼叫accept()方法建立通訊的Socket物件。
客戶端通過new Socket()方法建立通訊的Socket物件

十三、泛型

1.以下說法錯誤的是()
A 虛擬機器中沒有泛型,只有普通類和普通方法
B 所有泛型類的型別引數在編譯時都會被擦除
C 建立泛型物件時請指明型別,讓編譯器儘早的做引數檢查
D 泛型的型別擦除機制意味著不能在執行時動態獲取List中T的實際型別

答案:D
筆記:
虛擬機器中沒有泛型,只有普通類和普通方法;所有泛型類的型別引數在編譯時都會被擦除;建立泛型物件的時候,一定要指出型別變數T的具體型別。爭取讓編譯器檢查出錯誤,而不是留給JVM執行的時候丟擲類不匹配的異常。
通過反射可以獲取獲取List中T的實際型別。

2.(多選題)Which four statements are true ?()
class A {}
class B extends A {}
class C extends A {}
class D extends B {}
A. The type List<A>is assignable to List.
B. The type List<B>is assignable to List<A>.
C. The type List<Object>is assignable to List<?>.
D. The type List<D>is assignable to List<?extends B>.
E. The type List<?extends A>is assignable to List<A>.
F. The type List<Object>is assignable to any List reference.
G. The type List<?extends B>is assignable to List<?extends A>.

答案:ACDG
筆記:
from牛客網的使用者:曉宇大美女~
1. 只看尖括號裡邊的,明確點和範圍兩個概念
2. 如果尖括號裡的是一個類,那麼尖括號裡的就是一個點,比如List<A>List<B>List<Object>
3. 如果尖括號裡面帶有問號,那麼代表一個範圍,<? extends A> 代表小於等於A的範圍,<? super A>代表大於等於A的範圍,<?>代表全部範圍
4. 尖括號裡的所有點之間互相賦值都是錯,除非是倆相同的點
5. 尖括號小範圍賦值給大範圍,對,大範圍賦值給小範圍,錯。如果某點包含在某個範圍裡,那麼可以賦值,否則,不能賦值
6. List<?>List 是相等的,都代表最大範圍
7. List既是點也是範圍,當表示範圍時,表示最大範圍

十四、繼承

1.以下程式碼執行的結果顯示是多少()

public class Demo {
    class Super {
        int flag = 1;
        Super() {
            test();
        }
        void test() {
            System.out.println("Super.test() flag=" + flag);
        }
    }
    class Sub extends Super {
        Sub(int i) {
            flag = i;
            System.out.println("Sub.Sub()flag=" + flag);
        }
        void test() {
            System.out.println("Sub.test()flag=" + flag);
        }
    }
    public static void main(String[] args) {
        new Demo().new Sub(5);
    }
}

A. Sub.test() flag=1 Sub.Sub() flag=5
B. Sub.Sub() flag=5 Sub.test() flag=5
C. Sub.test() flag=0 Sub.Sub() flag=5
D. Super.test() flag=1 Sub.Sub() flag=5

答案:A
筆記:
當例項化Sub類的之前,會先例項化其父類,因此父類先初始化執行int flag=1,然後執行父類的構造方法,父類的構造方法中執行了test方法,而由於子類重寫了父類的test方法,因此父類中呼叫了是子類的test方法,因此當執行父類的構造方法,輸出的是Sub.test() flag=1,執行完父類的構造方法後,繼續執行子類的構造方法,輸出結果:Sub.Sub() flag=5。

2.下面程式碼的輸出是什麼()

public class Base{
    private String baseName = "base";
    public Base(){
        callName();
    }
    public void callName(){
        System. out. println(baseName);
    }
    static class Sub extends Base{
        private String baseName = "sub";
        public void callName(){
            System. out. println (baseName) ;
        }
    }
    public static void main(String[] args){
        Base b = new Sub();
    }
}

A. null
B. sub
C. base

答案:A
筆記:
在main函式中例項化Sub,會先例項化其父類Base,因此先例項化Base的私有屬性baseName=”base”,然後執行父類的構造方法,在構造方法中呼叫了callName方法,由於子類重寫了該方法,於是,父類呼叫的是子類的callName方法,重點是!子類的callName方法中呼叫的baseName是子類的私有屬性(子類無法訪問父類的私有屬性),但此時子類由於還沒有例項化baseName屬性,因此輸出為null。
為什麼會出現子類屬性還沒有例項化的情況呢,這是因為父類的屬性例項化以及父類執行構造方法的順序先於子類。具體順序可以看下面的繼承模組的總結。

3.下列對繼承的說法,正確的是( )
A. 子類能繼承父類的所有成員
B. 子類繼承父類的非私有方法和狀態
C. 子類只能繼承父類的public方法和狀態
D. 子類只能繼承父類的方法

答案:A
筆記:
摘自牛客網使用者——檸檬。2020:
從繼承的概念來說: private和final不被繼承。Java官方文件上是這麼說的。
從記憶體的角度來說: 父類的一切都被繼承(從父類構造方法被呼叫就知道了,因為new一個物件,就會呼叫構造方法,子類被new的時候就會呼叫父類的構造方法,所以從記憶體的角度來說,子類擁有一個完整的父類)。子類物件所引用的記憶體有父類變數的一份拷貝.
所以說,子類是繼承了父類的所有成員(成員指的是欄位、方法、巢狀類)。但是對於私有成員,雖然繼承了,但是無法訪問。
另外!!很重要的一點是,建構函式不是成員!!因此,子類並沒有繼承父類的構造方法,構造方法不被子類繼承,但是子類可以呼叫父類的構造方法。

4.(多選題)java中關於繼承的描述正確的是()
A. 一個子類只能繼承一個父類
B. 子類可以繼承父類的構造方法
C. 繼承具有傳遞性
D. 父類一般具有通用性,子類更具體

答案:CD
筆記:
子類繼承父類的所有成員(成員包括屬性和方法以及巢狀類),但能繼承不代表有許可權訪問。另外,構造方法不屬於成員之一,即子類沒有繼承父類的構造方法,也就是說子類只能呼叫父類的構造方法,但沒有繼承父類的構造方法。

總結-在繼承中程式碼的執行順序

在繼承中程式碼的執行順序為:
1.父類靜態物件,父類靜態程式碼塊;
2.子類靜態物件,子類靜態程式碼塊;
3.父類非靜態物件,父類非靜態程式碼塊;
4.父類建構函式;
5.子類非靜態物件,子類非靜態程式碼塊;
6.子類建構函式;

十五、內部類

1.下列說法正確的是()
A. 對於區域性內部類,只有在方法的區域性變數被標記為final或區域性變數是effctively final的,內部類才能使用它們
B. 成員內部類位於外部類內部,可以直接呼叫外部類的所有方法(靜態方法和非靜態方法)
C. 由於匿名內部類只能用在方法內部,所以匿名內部類的用法與區域性內部類是一致的
D. 靜態內部類可以直接訪問外部類的成員變數

答案:A
筆記:
區域性內部類,可以參考區域性變數,指的是定義在方法中的類。
成員內部類特指非靜態的,靜態內部類不能稱之為成員,故B錯。
匿名內部類並不只能用於方法內部,匿名內部類的特點是隻能使用一次。故C錯。
靜態內部類不能直接訪問外部類的非靜態成員,但是可以通過new 外部類().成員的方式訪問,故A錯。

2.Given the following code:

class Enclosingone
{
    public class InsideOne {}

}
public class inertest
{
    public static void main(string[]args)
    {
        EnclosingOne eo = new EnclosingOne();
        //insert code here
    }

}

Which statement at line 11 constructs an instance of the inner class?
A. InsideOne ei=eo.new InsideOne();
B. eo.InsideOne ei=eo.new InsideOne();
C. InsideOne ei=EnclosingOne.new InsideOne();
D. EnclosingOne.InsideOne ei=eo.new InsideOne();

答案:D
筆記:
如果內部類是靜態內部類,那麼正確的答案寫法就是:
EncolsingOne.InsideOne obj = new EnclosingOne.InsideOne();

4.下列外部類定義中,不正確的是:( )
A class x { …. }
B class x extends y { …. }
C static class x implements y1,y2 { …. }
D public class x extends Applet { …. }

答案:C
筆記:
外部類的修飾符只能是public,abstract,final。
當一個類是內部類時可以用public,protected和private修飾,或者是預設的包訪問許可權。

5.關於訪問許可權說法正確的是( )
A 類定義前面可以修飾public,protected和private
B 內部類前面可以修飾public,protected和private
C 區域性內部類前面可以修飾public,protecte