1. 程式人生 > >牛客面試題記錄JAVA

牛客面試題記錄JAVA

有一個原始碼,只包含import java.util.* ; 這一個import語句,下面敘述正確的是?   ( )

能訪問java/util目錄下的所有類,不能訪問java/util子目錄下的所有類

解析:匯入java.util.*不能讀取其子目錄的類,因為如果java.util裡面有個a類,java.util.regex裡面也有個a類,我們若是要呼叫a類的方法或屬性時,應該使用哪個a類呢。避免類名混淆,也是使用包的原因。


關於下面的程式Test.java說法正確的是(    )。

1

2

3

4

5

6

7

8

public class Test {

    static String x="1";

    static int y=1;

    public static void main(String args[]) {

        static int z=2;

        

System.out.println(x+y+z);

    }

}

程式有編譯錯誤

解析:被static修飾的變數稱為靜態變數(類變數),類變數屬於整個類,只能定義在類中方法外。而區域性變數屬於方法,只在該方法內有效,所以static不能修飾區域性變數。


在建立派生類物件,建構函式的執行順序:

基類建構函式,派生類物件成員建構函式,派生類本身的建構函式

解析:父類靜態域——》子類靜態域——》父類成員初始化——》父類構造塊——》父類構造方法——》子類成員初始化——》子類構造塊——》子類構造方法;


下列有關Thread的描述,哪個是正確的?

將一個執行緒標記成daemon執行緒,意味著當主執行緒結束,並且沒有其它正在執行的非daemon執行緒時,該daemon執行緒也會自動結束。

解析:

1.啟動一個執行緒的方法是 start()

2.結束執行緒用的是interrupt()方法,而stop()是強制結束執行緒,並不推薦使用,同時stop()方法已被棄用

3.daemon執行緒是守護執行緒,當主執行緒結束時,守護執行緒會自動結束

4.一個執行緒等待另外一個執行緒的方法是wait()方法


關於 JAVA 堆疊

解析:

Java把記憶體分成兩種,一種叫做棧記憶體,一種叫做堆記憶體。

在函式中定義的一些基本型別的變數和物件的引用變數都是在函式的棧記憶體中分配。當在一段程式碼塊中定義一個變數時,java就在棧中為這個變數分配記憶體空間,當超過變數的作用域後,java會自動釋放掉為該變數分配的記憶體空間,該記憶體空間可以立刻被另作他用。

堆記憶體用於存放由new建立的物件和陣列。在堆中分配的記憶體,由java虛擬機器自動垃圾回收器來管理。在堆中產生了一個數組或者物件後,還可以在棧中定義一個特殊的變數,這個變數的取值等於陣列或者物件在堆記憶體中的首地址,在棧中的這個特殊的變數就變成了陣列或者物件的引用變數,以後就可以在程式中使用棧記憶體中的引用變數來訪問堆中的陣列或者物件,引用變數相當於為陣列或者物件起的一個別名,或者代號。

引用變數是普通變數,定義時在棧中分配記憶體,引用變數在程式執行到作用域外釋放。而陣列&物件本身在堆中分配,即使程式執行到使用new產生陣列和物件的語句所在地程式碼塊之外,陣列和物件本身佔用的堆記憶體也不會被釋放,陣列和物件在沒有引用變數指向它的時候(比如先前的引用變數x=null時),才變成垃圾,不能再被使用,但是仍然佔著記憶體,在隨後的一個不確定的時間被垃圾回收器釋放掉。這個也是java比較佔記憶體的主要原因。

   總結起來就是物件儲存在堆記憶體,引用變數儲存在棧記憶體。棧記憶體指向堆記憶體。


程式碼片段: 

1

2

3

4

5

byte b1=1,b2=2,b3,b6; 

final byte b4=4,b5=6

b6=b4+b5; 

b3=(b1+b2); 

System.out.println(b3+b6);

關於上面程式碼片段敘述正確的是()

語句:b3=b1+b2編譯出錯

解析:沒有final修飾的變數相加後會被自動提升為int型,與目標型別byte不相容,需要強制轉換(向下轉型)。

表示式的資料型別自動提升, 關於型別的自動提升,注意下面的規則。

①所有的byte,short,char型的值將被提升為int型;

②如果有一個運算元是long型,計算結果是long型;

③如果有一個運算元是float型,計算結果是float型;

④如果有一個運算元是double型,計算結果是double型;

而宣告為final的變數會被JVM優化,第3行相當於 b6 = 10


 

下面程式碼的輸出是什麼?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

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();

    }

}

解析:

1.首先,需要明白類的載入順序

(1) 父類靜態程式碼塊(包括靜態初始化塊,靜態屬性,但不包括靜態方法)

(2) 子類靜態程式碼塊(包括靜態初始化塊,靜態屬性,但不包括靜態方法 )

(3) 父類非靜態程式碼塊( 包括非靜態初始化塊,非靜態屬性 )

(4) 父類建構函式

(5) 子類非靜態程式碼塊 ( 包括非靜態初始化塊,非靜態屬性 )

(6) 子類建構函式

其中:類中靜態塊按照宣告順序執行,並且(1)和(2)不需要呼叫new類例項的時候就執行了(意思就是在類載入到方法區的時候執行的)

2.其次,需要理解子類覆蓋父類方法的問題,也就是方法重寫實現多型問題。

Base b = new Sub();它為多型的一種表現形式,宣告是Base,實現是Sub類, 理解為 b 編譯時表現為Base類特性,執行時表現為Sub類特性。

當子類覆蓋了父類的方法後,意思是父類的方法已經被重寫,題中 父類初始化呼叫的方法為子類實現的方法,子類實現的方法中呼叫的baseName為子類中的私有屬性。

由1.可知,此時只執行到步驟4.,子類非靜態程式碼塊和初始化步驟還沒有到,子類中的baseName還沒有被初始化。所以此時 baseName為空。 所以為null。

 new Sub();在創造派生類的過程中首先建立基類物件,然後才能建立派生類。

建立基類即預設呼叫Base()方法,在方法中呼叫callName()方法,由於派生類中存在此方法,則被呼叫的callName()方法是派生類中的方法,此時派生類還未構造,所以變數baseName的值為null

 


若所用變數都已正確定義,以下選項中,非法的表示式是()

’a’ = 1/3

解析:'a'是個常數,不能賦值。當字元型與整型運算時會自動轉換成整型。a的ASCII碼為97


以下是java concurrent包下的4個類,選出差別最大的一個c

A、Semaphore:類,控制某個資源可被同時訪問的個數;

B、ReentrantLock:類,具有與使用synchronized方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大;

C、 Future:介面,表示非同步計算的結果;

D、 CountDownLatch: 類,可以用來在一個執行緒中等待多個執行緒完成任務的類。


java介面的方法修飾符可以為?(忽略內部介面)

abstract

解析:1、抽象類中的抽象方法(其前有abstract修飾)不能用private、static、synchronized、native訪問修飾符修飾。原因如下:抽象方法沒有方法體,是用來被繼承的,所以不能用private修飾;static修飾的方法可以通過類名來訪問該方法(即該方法的方法體),抽象方法用static修飾沒有意義;使用synchronized關鍵字是為該方法加一個鎖。。而如果該關鍵字修飾的方法是static方法。則使用的鎖就是class變數的鎖。如果是修飾 類方法。則用this變數鎖。但是抽象類不能例項化物件,因為該方法不是在該抽象類中實現的。是在其子類實現的。所以。鎖應該歸其子類所有。所以。抽象方 法也就不能用synchronized關鍵字修飾了;native,這個東西本身就和abstract衝突,他們都是方法的宣告,只是一個吧方法實現移交給子類,另一個是移交給本地作業系統。如果同時出現,就相當於即把實現移交給子類,又把實現移交給本地作業系統,那到底誰來實現具體方法呢? 

2、介面是一種特殊的抽象類,介面中的方法全部是抽象方法(但其前的abstract可以省略),所以抽象類中的抽象方法不能用的訪問修飾符這裡也不能用。而且protected訪問修飾符也不能使用,因為介面可以讓所有的類去 實現(非繼承) ,不只是其子類,但是要用public去修飾。介面可以去繼承一個已有的介面。 

考察點:抽象類和介面

相同點:都不能被例項化,位於繼承樹的頂端,都包含抽象方法

不同點:1、設計目的:介面體現的一種規範,類似與整個系統的總綱,制訂了系統各模組應該遵循的標準,因此介面不應該經常改變,一旦改變對整個系統是輻射性的。

               抽象類作為多個子類的共同父類,體現的是一種模板式設計,可以當作系統實現過程中的中間產品,已經實現了系統部分功能。

            2、使用不同:(1)介面只能包含抽象方法,抽象類可以包含普通方法。

                                   (2)接口裡不能定義靜態方法,抽象類可以。

                                   (3)介面只能定義靜態常量屬性不能定義普通屬性,抽象類可以。

                                   (4)介面不包含構造器,抽象類可以(不是用於建立物件而是讓子類完成初始化)。

                                   (5)接口裡不能包含初始化塊,抽象類完全可以。

                                   (6)介面多繼承,抽象類但繼承(只能有一個直接父類)。

總結:介面所有方法全是抽象方法只能 public abstract修飾 (預設public abstract修飾 ),屬性預設public static final修飾。

             抽象類除了包含抽象方法外與普通類無區別。 


關於構造方法:

A:靜態成員變數或靜態程式碼塊>main方法>非靜態成員變數或非靜態程式碼塊>構造方法

B:think in java中提到構造器本身並沒有任何返回值。

C: 構造方法的主要作用是完成對類的物件的初始化工作。

D: 一般在建立(new)新物件時,系統會自動呼叫構造方法。