JAVA面試精選【Java基礎第三部分】


  上一篇,我們給出了大概35個題目,都是基礎知識,有童鞋反映題目過時了,其實不然,這些是基礎中的基礎,但是也是必不可少的,面試題目中還是有一些基礎題目的,我們本着先易后難的原則,逐漸給出不同級別的題目,猛料還在后頭呢,繼續關注哦。續接上次提到的70題,本章將給童鞋們准備了13道經典題目

  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

  72java中有幾種類型的流?JDK為每種類型的流提供了一些抽象類以供繼承,請說出他們分別是哪些類?

字節流,字符流。字節流繼承於InputStream OutputStream,字符流繼承於InputStreamReaderOutputStreamWriter。在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 = "中國人";

        /*FileOutputStreamfos  = newFileOutputStream("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 = newchar[1024];

        int len =fr.read(buf);

        String myStr = newString(buf,0,len);

        System.out.println(myStr);*/

        /*FileInputStreamfr = new FileInputStream("1.txt");

        byte[] buf = newbyte[1024];

        int len =fr.read(buf);

        String myStr = newString(buf,0,len,"UTF-8");

        System.out.println(myStr);*/

        BufferedReader br =new BufferedReader(

                      newInputStreamReader(

                             newFileInputStream("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接口,其中沒有需要實現的方法,implementsSerializable只是為了標注該對象是可被序列化的

 

 

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

 

  75、描述一下JVM加載class文件的原理機制?

JVM中類的裝載是由ClassLoader和它的子類來實現的,Java ClassLoader是一個重要的Java運行時系統組件。它負責在運行時查找和裝入類文件的類。

 

  76heapstack有什么區別。

java的內存分為兩類,一類是棧內存,一類是堆內存。棧內存是指程序進入一個方法時,會為這個方法單獨分配一塊私屬存儲空間,用於存儲這個方法內部的局部變量,當這個方法結束時,分配給這個方法的棧會釋放,這個棧中的變量也將隨之釋放。

堆是與棧作用不同的內存,一般用於存放不放在當前方法棧中的那些數據,例如,使用new創建的對象都放在堆里,所以,它不會隨方法的結束而消失。方法中的局部變量使用final修飾后,放在堆中,而不是棧中。

 

  77GC是什么?為什么要有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;

 

publicclass AssertTest {

 

    /**

     * @paramargs

     */

    public static voidmain(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;    

    }

 

}

 

  81java中會存在內存泄漏嗎,請簡單描述。

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

package com.huawei.interview;

 

import java.io.IOException;

 

publicclass GarbageTest {

 

    /**

     * @paramargs

     * @throwsIOException

     */

    public static voidmain(String[] args)throws IOException {

       // TODO Auto-generated method stub

       try {

           gcTest();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

       System.out.println("hasexited gcTest!");

       System.in.read();

       System.in.read();  

       System.out.println("out begingc!");    

       for(int i=0;i<100;i++)

       {

           System.gc();

           System.in.read();

           System.in.read();

       }

    }

 

    private static voidgcTest()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("beforeexit gctest!");

       System.in.read();

       System.in.read();  

       System.gc();

       System.out.println("exitgctest!");

    }

 

    private static classPerson

    {

       byte[] data =new byte[20000000];

       Person mate = null;

       public void setMate(Personother)

       {

           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;

 

publicclass String {

 

    /**

     * @paramargs

     */

    public static voidmain(String[] args) {

       // TODO Auto-generated method stub

       System.out.println("string");

    }

 

}

報告的錯誤如下:

java.lang.NoSuchMethodError:main

Exception inthread "main"

這是因為加載了jre自帶的java.lang.String,而該類中沒有main方法。

 

  83. Java代碼查錯

1.
abstract class Name {
   private String name;
   public abstract boolean isStupidName(String name) {}
}
大俠們,這有何錯誤?
答案: 錯。abstract method必須以分號結尾,且不帶花括號。
2.
public class Something {
   void doSomething () {
       private String s = "";
       int l = s.length();
   }
}
有錯嗎?
答案: 錯。局部變量前不能放置任何訪問修飾符 (private,public,和protected)。final可以用來修飾局部變量
(final如同abstract和strictfp,都是非訪問修飾符,strictfp只能修飾class和method而非variable)。
3.
abstract class Something {
   private abstract String doSomething ();
}
這好像沒什么錯吧?
答案: 錯。abstract的methods不能以private修飾。abstract的methods就是讓子類implement(實現)具體細節的,怎么可以用private把abstract
method封鎖起來呢? (同理,abstract method前不能加final)。
4.
public class Something {
   public int addOne(final int x) {
       return ++x;
   }
}
這個比較明顯。
答案: 錯。int x被修飾成final,意味着x不能在addOne method中被修改。
5.
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並沒有改變。
6.
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。
7.
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;"。
8.
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-staticmethods。可改成"System.out.println("s.doSomething()returns " + s.doSomething());"。同理,static method不能訪問non-static instant variable。
9.
此處,Something類的文件名叫OtherThing.java
class Something {
    private static void main(String[] something_to_do){       
        System.out.println("Dosomething ...");
    }
}
 這個好像很明顯。
答案: 正確。從來沒有人說過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 staticfinal.所以可以通過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 = newBall("Football");
        System.out.println(ball.getName());
    }
}
這個錯誤不容易發現。
答案: 錯。"interfaceRollable extends Playable, Bounceable"沒有問題。interface可繼承多個interfaces,所以這里沒錯。問題出在interface Rollable里的"Ball ball =new Ball("PingPang");"。任何在interface里聲明的interface variable (接口變量,也可稱成員變量),默認為public static final。也就是說"Ball ball = new Ball("PingPang");"實際上是"public staticfinal Ball ball = new Ball("PingPang");"。在Ball類的Play()方法中,"ball = newBall("Football");"改變了ball的reference,而這里的ball來自Rollable interface,Rollable interface里的ball是public static final的,final的object是不能被改變reference的。因此編譯器將在"ball = newBall("Football");"這里顯示有錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM