2017面試題

java基礎

一、解釋內存中的棧(stack)、堆(heap)和方法區(method area)的用法。

答:一般咱們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用JVM中的棧空間;而經過new關鍵字和構造器建立的對象則放在堆空間,堆是垃圾收集器管理的主要區域,因爲如今的垃圾收集器都採用分代收集算法,因此堆空間還能夠細分爲新生代和老生代,再具體一點能夠分爲Eden、Survivor(又可分爲From Survivor和To Survivor)、Tenured;方法區和堆都是各個線程共享的內存區域,用於存儲已經被JVM加載的類信息、常量、靜態變量、JIT編譯器編譯後的代碼等數據;程序中的字面量(literal)如直接書寫的100、"hello"和常量都是放在常量池中,常量池是方法區的一部分,。棧空間操做起來最快可是棧很小,一般大量的對象都是放在堆空間,棧和堆的大小均可以經過JVM的啓動參數來進行調整,棧空間用光了會引起StackOverflowError,而堆和常量池空間不足則會引起OutOfMemoryError。java

String str = new String("hello");

上面的語句中變量str放在棧上,用new建立出來的字符串對象放在堆上,而"hello"這個字面量是放在方法區的。程序員

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

答:JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。 
因爲Java的跨平臺性,通過編譯的Java源程序並非一個可執行程序,而是一個或多個類文件。當Java程序須要使用某個類時,JVM會確保這個類已經被加載、鏈接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,一般是建立一個字節數組讀入.class文件,而後產生與所加載類對應的Class對象。加載完成後,Class對象還不完整,因此此時的類還不可用。當類被加載後就進入鏈接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後JVM對類進行初始化,包括:1)若是類存在直接的父類而且這個類尚未被初始化,那麼就先初始化父類;2)若是類中存在初始化語句,就依次執行這些初始化語句。 
類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程採起了父親委託機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其餘的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM不會向Java程序提供對Bootstrap的引用。下面是關於幾個類加載器的說明:web

  • Bootstrap:通常用本地代碼實現,負責加載JVM基礎核心類庫(rt.jar);
  • Extension:從java.ext.dirs系統屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap;
  • System:又叫應用類加載器,其父類是Extension。它是應用最普遍的類加載器。它從環境變量classpath或者系統屬性java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。

三、用Java寫一個單例類

  • 餓漢式單例
public class Singleton {
    private Singleton(){}
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}
  • 懶漢式單例
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static synchronized Singleton getInstance(){
        if (instance == null) instance = new Singleton();
        return instance;
    }
}

 

注意:實現一個單例有兩點注意事項,①將構造器私有,不容許外界經過構造器建立對象;②經過公開的靜態方法向外界返回類的惟一實例。這裏有一個問題能夠思考:Spring的IoC容器能夠爲普通的類建立單例,它是怎麼作到的呢?        面試

 

四、用Java寫一個冒泡排序


答:冒泡排序幾乎是個程序員都寫得出來,可是面試的時候如何寫一個逼格高的冒泡排序卻不是每一個人都能作到,下面提供一個參考代碼:算法

import java.util.Comparator;

/**
 * 排序器接口(策略模式: 將算法封裝到具備共同接口的獨立的類中使得它們能夠相互替換)
 * 
 *
 */
public interface Sorter {

   /**
    * 排序
    * @param list 待排序的數組
    */
   public <T extends Comparable<T>> void sort(T[] list);

   /**
    * 排序
    * @param list 待排序的數組
    * @param comp 比較兩個對象的比較器
    */
   public <T> void sort(T[] list, Comparator<T> comp);
}
  •  
import java.util.Comparator;

/**
 * 冒泡排序
 * 
 *
 */
public class BubbleSorter implements Sorter {

    @Override
    public <T extends Comparable<T>> void sort(T[] list) {
        boolean swapped = true;
        for (int i = 1, len = list.length; i < len && swapped; ++i) {
            swapped = false;
            for (int j = 0; j < len - i; ++j) {
                if (list[j].compareTo(list[j + 1]) > 0) {
                    T temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;
                    swapped = true;
                }
            }
        }
    }

    @Override
    public <T> void sort(T[] list, Comparator<T> comp) {
        boolean swapped = true;
        for (int i = 1, len = list.length; i < len && swapped; ++i) {
            swapped = false;
            for (int j = 0; j < len - i; ++j) {
                if (comp.compare(list[j], list[j + 1]) > 0) {
                    T temp = list[j];
                    list[j] = list[j + 1];
                    list[j + 1] = temp;
                    swapped = true;
                }
            }
        }
    }
}

 

五、用Java寫一個折半查找


答:折半查找,也稱二分查找、二分搜索,是一種在有序數組中查找某一特定元素的搜索算法。搜素過程從數組的中間元素開始,若是中間元素正好是要查找的元素,則搜素過程結束;若是某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,並且跟開始同樣從中間元素開始比較。若是在某一步驟數組已經爲空,則表示找不到指定的元素。這種搜索算法每一次比較都使搜索範圍縮小一半,其時間複雜度是O(logN)。數組

import java.util.Comparator;

public class MyUtil {

   public static <T extends Comparable<T>> int binarySearch(T[] x, T key) {
      return binarySearch(x, 0, x.length- 1, key);
   }

   // 使用循環實現的二分查找
   public static <T> int binarySearch(T[] x, T key, Comparator<T> comp) {
      int low = 0;
      int high = x.length - 1;
      while (low <= high) {
          int mid = (low + high) >>> 1;
          int cmp = comp.compare(x[mid], key);
          if (cmp < 0) {
            low= mid + 1;
          }
          else if (cmp > 0) {
            high= mid - 1;
          }
          else {
            return mid;
          }
      }
      return -1;
   }

   // 使用遞歸實現的二分查找
   private static<T extends Comparable<T>> int binarySearch(T[] x, int low, int high, T key) {
      if(low <= high) {
        int mid = low + ((high -low) >> 1);
        if(key.compareTo(x[mid])== 0) {
           return mid;
        }
        else if(key.compareTo(x[mid])< 0) {
           return binarySearch(x,low, mid - 1, key);
        }
        else {
           return binarySearch(x,mid + 1, high, key);
        }
      }
      return -1;
   }
}
  •  

說明:上面的代碼中給出了折半查找的兩個版本,一個用遞歸實現,一個用循環實現。須要注意的是計算中間位置時不該該使用(high+ low) / 2的方式,由於加法運算可能致使整數越界,這裏應該使用如下三種方式之一:low + (high - low) / 2或low + (high – low) >> 1或(low + high) >>> 1(>>>是邏輯右移,是不帶符號位的右移)瀏覽器

六、forward和redirect是什麼?

是servlet種的兩種主要的跳轉方式。forward又叫轉發,redirect叫作重定向。安全

forward(轉發):服務器

1.是服務器內部的重定向,服務器直接訪問目標地址的 url網址,把裏面的東西讀取出來,可是客戶端並不知道,所以用forward的話,客戶端瀏覽器的網址是不會發生變化的。 
2.關於request: 因爲在整個定向的過程當中用的是同一個request,所以forward會將request的信息帶到被重定向的jsp或者servlet中使用。網絡

redirect(重定向):

1.是客戶端的重定向,是徹底的跳轉。即服務器返回的一個url給客戶端瀏覽器,而後客戶端瀏覽器會從新發送一次請求,到新的url裏面,所以瀏覽器中顯示的url網址會發生變化。 
2.由於這種方式比forward多了一次網絡請求,所以效率會低於forward。

 

七、監聽器有哪些做用和用法? 


答:Java Web開發中的監聽器(listener)就是application、session、request三個對象建立、銷燬或者往其中添加修改刪除屬性時自動執行代碼的功能組件,以下所示: 
①ServletContextListener:對Servlet上下文的建立和銷燬進行監聽。 
②ServletContextAttributeListener:監聽Servlet上下文屬性的添加、刪除和替換。 
③HttpSessionListener:對Session的建立和銷燬進行監聽。

補充:session的銷燬有兩種狀況:1). session超時(能夠在web.xml中經過<session-config>/<session-timeout>標籤配置超時時間);2). 經過調用session對象的invalidate()方法使session失效。

④HttpSessionAttributeListener:對Session對象中屬性的添加、刪除和替換進行監聽。 
⑤ServletRequestListener:對請求對象的初始化和銷燬進行監聽。 
⑥ServletRequestAttributeListener:對請求對象屬性的添加、刪除和替換進行監聽。

下面是一個統計網站最多在線人數監聽器的例子。

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

/**
 上下文監聽器,在服務器啓動時初始化onLineCount和maxOnLineCount兩個變量
 並將其置於服務器上下文(ServletContext)中,其初始值都是0
*/
@WebListener
public class InitListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent evt) {
    }

    @Override
    public void contextInitialized(ServletContextEvent evt) {
        evt.getServletContext().setAttribute("onLineCount", 0);
        evt.getServletContext().setAttribute("maxOnLineCount", 0);
    }

}
  •  
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 會話監聽器,在用戶會話建立和銷燬的時候根據狀況
 修改onLineCount和maxOnLineCount的值
*/
@WebListener
public class MaxCountListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        ServletContext ctx = event.getSession().getServletContext();
        int count = Integer.parseInt(ctx.getAttribute("onLineCount").toString());
        count++;
        ctx.setAttribute("onLineCount", count);
        int maxOnLineCount = Integer.parseInt(ctx.getAttribute("maxOnLineCount").toString());
        if (count > maxOnLineCount) {
            ctx.setAttribute("maxOnLineCount", count);
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            ctx.setAttribute("date", df.format(new Date()));
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        ServletContext app = event.getSession().getServletContext();
        int count = Integer.parseInt(app.getAttribute("onLineCount").toString());
        count--;
        app.setAttribute("onLineCount", count);
    }
}
  •  

說明:這裏使用了Servlet 3規範中的@WebListener註解配置監聽器,固然你能夠在web.xml文件中用<listener>標籤配置監聽器,如108題中所示。

相關文章
相關標籤/搜索