80道最新java基礎部分面試題(七)

自己整理的面試題,希望可以幫到大家,需要更多資料的可以私信我哦,大家一起學習進步!

70、TreeSet裏面放對象,如果同時放入了父類和子類的實例對象,那比較時使用的是父類的compareTo方法,還是使用的子類的compareTo方法,還是拋異常!
(應該是沒有針對問題的確切的答案,當前的add方法放入的是哪個對象,就調用哪個對象的compareTo方法,至於這個compareTo方法怎麼做,就看當前這個對象的類中是如何編寫這個方法的)
實驗代碼:
public class Parent implements Comparable {
private int age = 0;
public Parent(int age){
this.age = age;
}
public int compareTo(Object o) {
// TODO Auto-generated method stub
System.out.println("method of parent");
Parent o1 = (Parent)o;
return age>o1.age?1:age<o1.age?-1:0;
}

}

public class Child extends Parent {

public Child(){
    super(3);
}
public int compareTo(Object o) {

        // TODO Auto-generated method stub
        System.out.println("method of child");

// Child o1 = (Child)o;
return 1;

}

}

public class TreeSetTest {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    TreeSet set = new TreeSet();
    set.add(new Parent(3));
    set.add(new Child());
    set.add(new Parent(4));
    System.out.println(set.size());
}

}

71、說出一些常用的類,包,接口,請各舉5個
要讓人家感覺你對java ee開發很熟,所以,不能僅僅只列core java中的那些東西,要多列你在做ssh項目中涉及的那些東西。就寫你最近寫的那些程序中涉及的那些類。

常用的類:BufferedReader BufferedWriter FileReader FileWirter String Integer
java.util.Date,System,Class,List,HashMap

常用的包:java.lang java.io java.util java.sql ,javax.servlet,org.apache.strtuts.action,org.hibernate
常用的接口:Remote List Map Document NodeList ,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate)、Session(Hibernate),HttpSession
72、java中有幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承,請說出他們分別是哪些類?
字節流,字符流。字節流繼承於InputStream OutputStream,字符流繼承於InputStreamReader OutputStreamWriter。在java.io包中還有許多其他的流,主要是爲了提高性能和使用方便。
73、字節流與字符流的區別
要把一片二進制數據數據逐一輸出到某個設備中,或者從某個設備中逐一讀取一片二進制數據,不管輸入輸出設備是什麼,我們要用統一的方式來完成這些操作,用一種抽象的方式進行描述,這個抽象描述方式起名爲IO流,對應的抽象類爲OutputStream和InputStream ,不同的實現類就代表不同的輸入和輸出設備,它們都是針對字節進行操作的。
在應用中,經常要完全是字符的一段文本輸出去或讀進來,用字節流可以嗎?計算機中的一切最終都是二進制的字節形式存在。對於“中國”這些字符,首先要得到其對應的字節,然後將字節寫入到輸出流。讀取時,首先讀到的是字節,可是我們要把它顯示爲字符,我們需要將字節轉換成字符。由於這樣的需求很廣泛,人家專門提供了字符流的包裝類。
底層設備永遠只接受字節數據,有時候要寫字符串到底層設備,需要將字符串轉成字節再進行寫入。字符流是字節流的包裝,字符流則是直接接受字符串,它內部將串轉成字節,再寫入底層設備,這爲我們向IO設別寫入或讀取字符串提供了一點點方便。
字符向字節轉換時,要注意編碼的問題,因爲字符串轉成字節數組,
其實是轉成該字符的某種編碼的字節形式,讀取也是反之的道理。

講解字節流與字符流關係的代碼案例:
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class IOTest {
public static void main(String[] args) throws Exception {
String str = "中國人";
/*FileOutputStream fos = new FileOutputStream("1.txt");

    fos.write(str.getBytes("UTF-8"));
    fos.close();*/

    /*FileWriter fw = new FileWriter("1.txt");
    fw.write(str);
    fw.close();*/
    PrintWriter pw = new PrintWriter("1.txt","utf-8");
    pw.write(str);
    pw.close();

    /*FileReader fr = new FileReader("1.txt");
    char[] buf = new char[1024];
    int len = fr.read(buf);
    String myStr = new String(buf,0,len);
    System.out.println(myStr);*/
    /*FileInputStream fr = new FileInputStream("1.txt");
    byte[] buf = new byte[1024];
    int len = fr.read(buf);
    String myStr = new String(buf,0,len,"UTF-8");
    System.out.println(myStr);*/
    BufferedReader br = new BufferedReader(
            new InputStreamReader(
                new FileInputStream("1.txt"),"UTF-8"    
                )
            );
    String myStr = br.readLine();
    br.close();
    System.out.println(myStr);
}

}
74、什麼是java序列化,如何實現java序列化?或者請解釋Serializable接口的作用。

我們有時候將一個java對象變成字節流的形式傳出去或者從一個字節流中恢復成一個java對象,例如,要將java對象存儲到硬盤或者傳送給網絡上的其他計算機,這個過程我們可以自己寫代碼去把一個java對象變成某個格式的字節流再傳輸,但是,jre本身就提供了這種支持,我們可以調用OutputStream的writeObject方法來做,如果要讓java 幫我們做,要被傳輸的對象必須實現serializable接口,這樣,javac編譯時就會進行特殊處理,編譯的類纔可以被writeObject方法操作,這就是所謂的序列化。需要被序列化的類必須實現Serializable接口,該接口是一個mini接口,其中沒有需要實現的方法,implements Serializable只是爲了標註該對象是可被序列化的。

例如,在web開發中,如果對象被保存在了Session中,tomcat在重啓時要把Session對象序列化到硬盤,這個對象就必須實現Serializable接口。如果對象要經過分佈式系統進行網絡傳輸或通過rmi等遠程調用,這就需要在網絡上傳輸對象,被傳輸的對象就必須實現Serializable接口。

75、描述一下JVM加載class文件的原理機制?
JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader 是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。

76、heap和stack有什麼區別。
java的內存分爲兩類,一類是棧內存,一類是堆內存。棧內存是指程序進入一個方法時,會爲這個方法單獨分配一塊私屬存儲空間,用於存儲這個方法內部的局部變量,當這個方法結束時,分配給這個方法的棧會釋放,這個棧中的變量也將隨之釋放。
堆是與棧作用不同的內存,一般用於存放不放在當前方法棧中的那些數據,例如,使用new創建的對象都放在堆裏,所以,它不會隨方法的結束而消失。方法中的局部變量使用final修飾後,放在堆中,而不是棧中。

77、GC是什麼? 爲什麼要有GC?   
GC是垃圾收集的意思(Gabage Collection),內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會導致程序或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域從而達到自動回收內存的目的,Java語言沒有提供釋放已分配內存的顯示操作方法。

78、垃圾回收的優點和原理。並考慮2種回收機制。
Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程序員最頭疼的內存管理的問題迎刃而解,它使得Java程序員在編寫程序的時候不再需要考慮內存管理。由於有個垃圾回收機制,Java中的對象不再有"作用域"的概念,只有對象的引用纔有"作用域"。垃圾回收可以有效的防止內存泄露,有效的使用可以使用的內存。垃圾回收器通常是作爲一個單獨的低級別的線程運行,不可預知的情況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清楚和回收,程序員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。回收機制有分代複製垃圾回收和標記垃圾回收,增量垃圾回收。

79、垃圾回收器的基本原理是什麼?垃圾回收器可以馬上回收內存嗎?有什麼辦法主動通知虛擬機進行垃圾回收?
對於GC來說,當程序員創建對象時,GC就開始監控這個對象的地址、大小以及使用情況。通常,GC採用有向圖的方式記錄和管理堆(heap)中的所有對象。通過這種方式確定哪些對象是"可達的",哪些對象是"不可達的"。當GC確定一些對象爲"不可達"時,GC就有責任回收這些內存空間。可以。程序員可以手動執行System.gc(),通知GC運行,但是Java語言規範並不保證GC一定會執行。

80、什麼時候用assert。
assertion(斷言)在軟件開發中是一種常用的調試方式,很多開發語言中都支持這種機制。在實現中,assertion就是在程序中的一條語句,它對一個boolean表達式進行檢查,一個正確程序必須保證這個boolean表達式的值爲true;如果該值爲false,說明程序已經處於不正確的狀態下,assert將給出警告或退出。一般來說,assertion用於保證程序最基本、關鍵的正確性。assertion檢查通常在開發和測試時開啓。爲了提高性能,在軟件發佈後,assertion檢查通常是關閉的。
package com.huawei.interview;

public class AssertTest {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    int i = 0;
    for(i=0;i<5;i++)
    {
        System.out.println(i);
    }
    //假設程序不小心多了一句--i;
    --i;
    assert i==5;        
}

}

81、java中會存在內存泄漏嗎,請簡單描述。
所謂內存泄露就是指一個不再被程序使用的對象或變量一直被佔據在內存中。java中有垃圾回收機制,它可以保證一對象不再被引用的時候,即對象編程了孤兒的時候,對象將自動被垃圾回收器從內存中清除掉。由於Java 使用有向圖的方式進行垃圾回收管理,可以消除引用循環的問題,例如有兩個對象,相互引用,只要它們和根進程不可達的,那麼GC也是可以回收它們的,例如下面的代碼可以看到這種情況的內存回收:
package com.huawei.interview;

import java.io.IOException;

public class GarbageTest {

/**
 * @param args
 * @throws IOException 
 */
public static void main(String[] args) throws IOException {
    // TODO Auto-generated method stub
    try {
        gcTest();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println("has exited gcTest!");
    System.in.read();
    System.in.read();       
    System.out.println("out begin gc!");        
    for(int i=0;i<100;i++)
    {
        System.gc();
        System.in.read();   
        System.in.read();   
    }
}

private static void gcTest() throws IOException {
    System.in.read();
    System.in.read();       
    Person p1 = new Person();
    System.in.read();
    System.in.read();       
    Person p2 = new Person();
    p1.setMate(p2);
    p2.setMate(p1);
    System.out.println("before exit gctest!");
    System.in.read();
    System.in.read();       
    System.gc();
    System.out.println("exit gctest!");
}

private static class Person
{
    byte[] data = new byte[20000000];
    Person mate = null;
    public void setMate(Person other)
    {
        mate = other;
    }
}

}

java中的內存泄露的情況:長生命週期的對象持有短生命週期對象的引用就很可能發生內存泄露,儘管短生命週期對象已經不再需要,但是因爲長生命週期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景,通俗地說,就是程序員可能創建了一個對象,以後一直不再使用這個對象,這個對象卻一直被引用,即這個對象無用但是卻無法被垃圾回收器回收的,這就是java中可能出現內存泄露的情況,例如,緩存系統,我們加載了一個對象放在緩存中(例如放在一個全局map對象中),然後一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。
檢查java中的內存泄露,一定要讓程序將各種分支情況都完整執行到程序結束,然後看某個對象是否被使用過,如果沒有,則才能判定這個對象屬於內存泄露。

如果一個外部類的實例對象的方法返回了一個內部類的實例對象,這個內部類對象被長期引用了,即使那個外部類實例對象不再被使用,但由於內部類持久外部類的實例對象,這個外部類對象將不會被垃圾回收,這也會造成內存泄露。

下面內容來自於網上(主要特點就是清空堆棧中的某個元素,並不是徹底把它從數組中拿掉,而是把存儲的總數減少,本人寫得可以比這個好,在拿掉某個元素時,順便也讓它從數組中消失,將那個元素所在的位置的值設置爲null即可):
我實在想不到比那個堆棧更經典的例子了,以致於我還要引用別人的例子,下面的例子不是我想到的,是書上看到的,當然如果沒有在書上看到,可能過一段時間我自己也想的到,可是那時我說是我自己想到的也沒有人相信的。
    public class Stack {
    private Object[] elements=new Object[10];
    private int size = 0;
    public void push(Object e){
    ensureCapacity();
    elements[size++] = e;
    }
    public Object pop(){
    if( size == 0)
    throw new EmptyStackException();
    return elements[--size];
    }
    private void ensureCapacity(){
    if(elements.length == size){
    Object[] oldElements = elements;
    elements = new Object[2 * elements.length+1];
    System.arraycopy(oldElements,0, elements, 0, size);
    }
    }
    }
    上面的原理應該很簡單,假如堆棧加了10個元素,然後全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件:無用,無法回收。
    但是就是存在這樣的東西也不一定會導致什麼樣的後果,如果這個堆棧用的比較少,也就浪費了幾個K內存而已,反正我們的內存都上G了,哪裏會有什麼影響,再說這個東西很快就會被回收的,有什麼關係。下面看兩個例子。
    例子1
    public class Bad{
    public static Stack s=Stack();
    static{
    s.push(new Object());
    s.pop(); //這裏有一個對象發生內存泄露
    s.push(new Object()); //上面的對象可以被回收了,等於是自愈了
    }
    }
    因爲是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那麼最多也就只有100個對象無法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的情況就是他們都是無用的,因爲我們一旦放新的進取,以前的引用自然消失!

內存泄露的另外一種情況:當一個對象被存儲進HashSet集合中以後,就不能修改這個對象中的那些參與計算哈希值的字段了,否則,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作爲的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中單獨刪除當前對象,造成內存泄露。

82、能不能自己寫個類,也叫java.lang.String?

可以,但在應用的時候,需要用自己的類加載器去加載,否則,系統的類加載器永遠只是去加載jre.jar包中的那個java.lang.String。由於在tomcat的web應用程序中,都是由webapp自己的類加載器先自己加載WEB-INF/classess目錄中的類,然後才委託上級的類加載器加載,如果我們在tomcat的web應用程序中寫一個java.lang.String,這時候Servlet程序加載的就是我們自己寫的java.lang.String,但是這麼幹就會出很多潛在的問題,原來所有用了java.lang.String類的都將出現問題。

雖然java提供了endorsed技術,可以覆蓋jdk中的某些類,具體做法是….。但是,能夠被覆蓋的類是有限制範圍,反正不包括java.lang這樣的包中的類。

(下面的例如主要是便於大家學習理解只用,不要作爲答案的一部分,否則,人家懷疑是題目泄露了)例如,運行下面的程序:
package java.lang;

public class String {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    System.out.println("string");
}

}
報告的錯誤如下:
java.lang.NoSuchMethodError: main
Exception in thread "main"
這是因爲加載了jre自帶的java.lang.String,而該類中沒有main方法。

  1. Java代碼查錯
  2. abstract class Name {
       private String name;
       public abstract boolean isStupidName(String name) {}
    }
    大俠們,這有何錯誤?
    答案: 錯。abstract method必須以分號結尾,且不帶花括號。

  3. public class Something {
       void doSomething () {
           private String s = "";
           int l = s.length();
       }
    }
    有錯嗎?
    答案: 錯。局部變量前不能放置任何訪問修飾符 (private,public,和protected)。final可以用來修飾局部變量
    (final如同abstract和strictfp,都是非訪問修飾符,strictfp只能修飾class和method而非variable)。

  4. abstract class Something {
       private abstract String doSomething ();
    }
    這好像沒什麼錯吧?
    答案: 錯。abstract的methods不能以private修飾。abstract的methods就是讓子類implement(實現)具體細節的,怎麼可以用private把abstract
    method封鎖起來呢? (同理,abstract method前不能加final)。

  5. public class Something {
       public int addOne(final int x) {
           return ++x;
       }
    }
    這個比較明顯。
    答案: 錯。int x被修飾成final,意味着x不能在addOne method中被修改。

  6. public class Something {
       public static void main(String[] args) {
           Other o = new Other();
           new Something().addOne(o);
       }
       public void addOne(final Other o) {
           o.i++;
       }
    }
    class Other {
       public int i;
    }
    和上面的很相似,都是關於final的問題,這有錯嗎?
    答案: 正確。在addOne method中,參數o被修飾成final。如果在addOne method裏我們修改了o的reference
    (比如: o = new Other();),那麼如同上例這題也是錯的。但這裏修改的是o的member vairable
    (成員變量),而o的reference並沒有改變。

  7. class Something {
        int i;
        public void doSomething() {
            System.out.println("i = " + i);
        }
    }
    有什麼錯呢? 看不出來啊。
    答案: 正確。輸出的是"i = 0"。int i屬於instant variable (實例變量,或叫成員變量)。instant variable有default value。int的default value是0。

  8. class Something {
        final int i;
        public void doSomething() {
            System.out.println("i = " + i);
        }
    }
    和上面一題只有一個地方不同,就是多了一個final。這難道就錯了嗎?
    答案: 錯。final int i是個final的instant variable (實例變量,或叫成員變量)。final的instant variable沒有default value,必須在constructor (構造器)結束之前被賦予一個明確的值。可以修改爲"final int i = 0;"。

  9. public class Something {
         public static void main(String[] args) {
            Something s = new Something();
            System.out.println("s.doSomething() returns " + doSomething());
        }
        public String doSomething() {
            return "Do something ...";
        }
    }
     看上去很完美。
    答案: 錯。看上去在main裏call doSomething沒有什麼問題,畢竟兩個methods都在同一個class裏。但仔細看,main是static的。static method不能直接call non-static methods。可改成"System.out.println("s.doSomething() returns " + s.doSomething());"。同理,static method不能訪問non-static instant variable。

  10. 此處,Something類的文件名叫OtherThing.java
    class Something {
        private static void main(String[] something_to_do) {       
            System.out.println("Do something ...");
        }
    }
     這個好像很明顯。
    答案: 正確。從來沒有人說過Java的Class名字必須和其文件名相同。但public class的名字必須和文件名相同。
    10.
    interface  A{
       int x = 0;
    }
    class B{
       int x =1;
    }
    class C extends B implements A {
       public void pX(){
          System.out.println(x);
       }
       public static void main(String[] args) {
          new C().pX();
       }
    }
    答案:錯誤。在編譯時會發生錯誤(錯誤描述不同的JVM有不同的信息,意思就是未明確的x調用,兩個x都匹配(就象在同時import java.util和java.sql兩個包時直接聲明Date一樣)。對於父類的變量,可以用super.x來明確,而接口的屬性默認隱含爲 public static final.所以可以通過A.x來明確。

  11. interface Playable {
        void play();
    }
    interface Bounceable {
        void play();
    }
    interface Rollable extends Playable, Bounceable {
        Ball ball = new Ball("PingPang");
    }
    class Ball implements Rollable {
        private String name;
        public String getName() {
            return name;
        }
        public Ball(String name) {
            this.name = name;       
        }
       public void play() {
            ball = new Ball("Football");
            System.out.println(ball.getName());
        }
    }
    這個錯誤不容易發現。
    答案: 錯。"interface Rollable extends Playable, Bounceable"沒有問題。interface可繼承多個interfaces,所以這裏沒錯。問題出在interface Rollable裏的"Ball ball = new Ball("PingPang");"。任何在interface裏聲明的interface variable (接口變量,也可稱成員變量),默認爲public static final。也就是說"Ball ball = new Ball("PingPang");"實際上是"public static final Ball ball = new Ball("PingPang");"。在Ball類的Play()方法中,"ball = new Ball("Football");"改變了ball的reference,而這裏的ball來自Rollable interface,Rollable interface裏的ball是public static final的,final的object是不能被改變reference的。因此編譯器將在"ball = new Ball("Football");"這裏顯示有錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章