一文分析java基礎面試題中易出錯考點

前言

這篇文章主要針對的是筆試題中出現的經過查看代碼執行結果選擇正確答案題材。

正式進入題目內容:java

一、(單選題)下面代碼的輸出結果是什麼?json

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 argn[]){
        Base b = new Sub();
    }
}


    A、null              B、Sub                  C、base

    題目解析:這道題主要考察的是java面向對象的基本特徵之多態特性以及一個類的加載順序。首先咱們要明白一個類的加載順序,若是是存在有父類和子類繼承關係的狀況下,一個類文件的加載順序是怎麼樣子的呢?服務器

加載順序以下:    函數

     (1)、父類的靜態變量,再加載父類的靜態代碼塊,靜態方法除外;工具

     (2)、子類的靜態變量,再加載子類的靜態代碼塊,靜態方法除外;spa

     (3)、父類的非靜態變量、再加載父類的非靜態代碼塊;操作系統

     (4)、父類的構造函數;code

     (5)、子類的非靜態變量,再加載子類的非靜態代碼塊;對象

     (6)、子類的構造函數。繼承

 其中步驟(1)和(2)是在類進入到鏈接階段的時候就執行了,而不是等到經過new 實例化對象的時候才執行。

 其次是多態特徵的問題,Base b = new Sub();它是一種多態性的表現,聲明是Base類型,而運行是Sub類型。題目中,callNam()方法是Sub類重寫Base類中的方法,而不是擴展的方法。

     當Base b = new Sub();操做時,按照類文件加載順序,會先執行Base的無參構造調用callName()方法,若是子類沒有重寫callName()方法,那麼則執行父類的callName()方法,若是子類重寫了callName()方法,則執行子類的,遵循這個原則 :「若是子類沒有,則從父類查找」。可是此時子類的非靜態變量還未賦值,所以輸出結果爲null,選擇A。

二、(單選題)下面代碼的輸出結果是什麼?

public class Test{
    
    public static int a = 1;
    
    public static void main(String argn[]){
        int a = 10;
        a++;
        Test.a++;
        Test t = new Test();
        System.out.println("a = "+a+"  t.a = "+t.a);
    }
}



A、a = 10  t.a = 3
B、a = 11  t.a = 2
C、a = 12  t.a = 1
D、a = 11  t.a = 1

題目解析:這道題考察的是成員變量和局部變量的差別以及自增運算符。咱們來分析main方法中執行結果,按照執行順序,代碼執行是從上到下,在main方法中定義一個變量a,那麼這裏的變量a是局部變量,它的生命週期在這個main方法體中,會隨着方法體被調用而調用,調用完畢而銷燬。執行到第二步,進行了自增操做,a++,是先賦值在自增,那麼操做結束後a的值爲11,則排除A和C,繼續往下走,執行到Test.a++;操做結果至關因而給類中的成員變量a進行了+1操做,然後new了Test的實例對象,再經過實例對象.a變量,至關因而調用類中的成員變量,所以結果爲2,選B。

三、(單選題)下面代碼的輸出結果是什麼?

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

A、0           B、1           C、2            D、不能執行,由於編譯有誤

題目解析:這道題主要考察的是對final修飾符的掌握程度。由於final修飾符修飾的變量爲常量,所以若是定義某成員變量或者局部變量爲final類型的時候,必定要進行初始化。選D。

四、(單選題)下面代碼的輸出結果是什麼?

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 Test{

    public static void main(String[] args) {
        Base b = new Son();
        b.method();
        b.methodB();
    }
}

A、Base SonB
B、Son SonB
C、Base Son SonB
D、編譯不經過

題目解析:這道題主要考察的是多態的知識點。Base b = new Son();是多態的表現形式,父類對象調用了子類建立了Son對象。b調用的method()方法就是調用了子類重寫的method()方法,而此時b仍是屬於Base對象,b調用的methodB()方法時,Base類中沒有該方法,所以會編譯不經過,選D。

五、(單選題)下面代碼的輸出結果是什麼?

public class Test{

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

A、3           B、123           C、13           D、程序有編譯錯誤

題目解析:這道題考察的是static關鍵字的知識點。static修飾的變量爲靜態變量,該靜態變量是與類一一對應的,只要該類被加載了,那麼靜態變量就能被使用。可是static關鍵字不能修飾局部變量,所以在程序編譯時報出,選D。 

六、對於JVM內存配置參數:-Xmx10240m -Xms10240m -Xmn5120m -XXSurvivorRatio=3,其最小內存值和Survivor區總大小分別是()?

A、5120m,1024m           B、5120m,2048m            C、10240m,1024m            D10240m,2048m

題目解析:這道題主要考察的是對JVM內存參數的知識點,所以藉此對JVM內存參數作更詳細的說明:

1)、堆/Heap

JVM管理的內存叫堆,在32Bit操做系統上有4G的限制,通常來講Windows下爲2G,而Linux 下爲3G;64Bit的就沒有這個限制。
-Xmx爲JVM最大分配的堆內存大小,默認爲物理內存的1/4;
-Xms爲JVM初始分配的堆內存大小,默認爲物理內存的1/64;

默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制,能夠由 -XX:MinHeapFreeRatio=指定。 
默認空餘堆內存大於70%時,JVM會減小堆直到-Xms的最小限制,能夠由 -XX:MaxHeapFreeRatio=指定。 
服務器通常設置-Xms、-Xmx相等以免在每次GC後調整堆的大小,因此上面的兩個參數沒啥用。 

2)、分代/堆模型

JVM中堆內存分爲:permantspace(持久代)和heapspace。

持久代中主要用於存放靜態類型數據,如java Class,Method,field等反射對象,與垃圾收集器要收集的java對象關係不大。

而heapspace則分爲年輕代和年老代,即HeapSpace = 【old + new{=Eden,from,to}】。

3)、年輕代(Young Generation)

全部新生成的對象首先都是放在年輕代中,年輕代的目標是儘量快速的收集掉那些生命週期較短的對象。年輕代分爲3個區,一個Eden區,兩個Survivor區(from和to),題目中的-Xmn爲堆內存中年輕代的大小,而-XXSurvivorRatio=3爲年輕代中Eden區和一個Suvivor區大小比值。所以年輕代爲5120m,Eden:Suvivor=3,而堆內存中有兩個Suvivor區,所以Survivor區總大小爲2048m。

大部分對象在Eden區生成。當Eden區滿時,還存活的對象將會複製到Survivor區(兩個中的一個),兩個2Survivor區是對稱的,沒有前後關係,因此同一個Survivor區中可能同時存在從Eden區複製過來的對象,和從另一個Survivor區複製過來的對象。當一個Survivor區滿時,此區存活的對象將會複製到另一個Survivor區,當兩個Survivor區都滿時,從以前Survivor區複製過來的對象若是還存在,那麼將可能被複制到年老代。針對年輕代的垃圾回收即young GC。

4)、年老代(Old Generation)

在年老代中經歷過N次(可配置)垃圾回收後仍然存活的對象將會被複制到年老代。所以年老代存放的都是一些生命週期較長的對象。針對年老代的垃圾回收爲Full GC。

5)、持久代(permantspace)

用於存放靜態類型數據,如java class,method等。默認爲64M,可經過設置 -XX:MaxPermSize=xxx 來增長其空間大小。持久代對垃圾回收沒有顯著影響。可是有些應用可能動態生成或調用一些Class,例如Hibernate CGLib等,在這種時候每每須要設置一個比較大的持久代空間來存放這些運行過程當中動態增長的類型數據。因此當一組對象生成時,內存申請過程以下:

  1. JVM會試圖爲相關Java對象在年輕代的Eden區中初始化一塊內存區域。
  2. 當Eden區空間足夠時,內存申請結束。不然執行下一步。
  3. JVM試圖釋放在Eden區中全部不活躍的對象(Young GC)。釋放後若Eden空間仍然不足以放入新對象,JVM則試圖將部分Eden區中活躍對象放入Survivor區。
  4. Survivor區被用來做爲Eden區及年老代的中間交換區域。當年老代空間足夠時,Survivor區中存活了必定次數的對象會被移到年老代。
  5. 當年老代空間不夠時,JVM會在年老代進行徹底的垃圾回收(Full GC)。
  6. Full GC後,若Survivor區及年老代仍然沒法存放從Eden區複製過來的對象,則會致使JVM沒法在Eden區爲新生成的對象申請內存,即出現「Out of Memory」。

  OOM(「Out of Memory」)異常通常主要有以下2種緣由

1. 年老代溢出,表現爲:java.lang.OutOfMemoryError:Javaheapspace

這是最多見的狀況,產生的緣由多是:設置的內存參數Xmx太小或程序的內存泄露及使用不當問題。

例如循環上萬次的字符串處理、建立上千萬個對象、在一段代碼內申請上百M甚至上G的內存。還有的時候雖然不會報內存溢出,卻會使系統不間斷的垃圾回收,也沒法處理其它請求。這種狀況下除了檢查程序、打印堆內存等方法排查,還能夠藉助一些內存分析工具,好比MAT就很不錯。


2. 持久代溢出,表現爲:java.lang.OutOfMemoryError:PermGenspace

一般因爲持久代設置太小,動態加載了大量Java類而致使溢出 ,解決辦法惟有將參數 -XX:MaxPermSize 調大(通常256m能知足絕大多數應用程序需求)。將部分Java類放到容器共享區(例如Tomcat share lib)去加載的辦法也是一個思路,但前提是容器裏部署了多個應用,且這些應用有大量的共享類庫

相關文章
相關標籤/搜索