Java面試題全集(10)
白玉 IT哈哈
91、用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容器可以爲普通的類創建單例,它是怎麼做到的呢?
92、什麼是UML?
答:UML是統一建模語言(Unified Modeling Language)的縮寫,它發表於1997年,綜合了當時已經存在的面向對象的建模語言、方法和過程,是一個支持模型化和軟件系統開發的圖形化語言,爲軟件開發的所有階段提供模型化和可視化支持。使用UML可以幫助溝通與交流,輔助應用設計和文檔的生成,還能夠闡釋系統的結構和行爲。
93、UML中有哪些常用的圖?
答:UML定義了多種圖形化的符號來描述軟件系統部分或全部的靜態結構和動態結構,包括:用例圖(use case diagram)、類圖(class diagram)、時序圖(sequence diagram)、協作圖(collaboration diagram)、狀態圖(statechart diagram)、活動圖(activity diagram)、構件圖(component diagram)、部署圖(deployment diagram)等。在這些圖形化符號中,有三種圖最爲重要,分別是:用例圖(用來捕獲需求,描述系統的功能,通過該圖可以迅速的瞭解系統的功能模塊及其關係)、類圖(描述類以及類與類之間的關係,通過該圖可以快速瞭解系統)、時序圖(描述執行特定任務時對象之間的交互關係以及執行順序,通過該圖可以瞭解對象能接收的消息也就是說對象能夠向外界提供的服務)。
用例圖:
類圖:
時序圖:
94、用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;
}
}
}
}
}
95、用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(>>>是邏輯右移,是不帶符號位的右移)
96、闡述Servlet和CGI的區別?
答:Servlet與CGI的區別在於Servlet處於服務器進程中,它通過多線程方式運行其service()方法,一個實例可以服務於多個請求,並且其實例一般不會銷燬,而CGI對每個請求都產生新的進程,服務完成後就銷燬,所以效率上低於Servlet。
補充:Sun Microsystems公司在1996年發佈Servlet技術就是爲了和CGI進行競爭,Servlet是一個特殊的Java程序,一個基於Java的Web應用通常包含一個或多個Servlet類。Servlet不能夠自行創建並執行,它是在Servlet容器中運行的,容器將用戶的請求傳遞給Servlet程序,並將Servlet的響應回傳給用戶。通常一個Servlet會關聯一個或多個JSP頁面。以前CGI經常因爲性能開銷上的問題被詬病,然而Fast CGI早就已經解決了CGI效率上的問題,所以面試的時候大可不必信口開河的詬病CGI,事實上有很多你熟悉的網站都使用了CGI技術。
97、Servlet接口中有哪些方法?
答:Servlet接口定義了5個方法,其中前三個方法與Servlet生命週期相關:
- void init(ServletConfig config) throws ServletException
- void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
- void destory()
- java.lang.String getServletInfo()
- ServletConfig getServletConfig()
Web容器加載Servlet並將其實例化後,Servlet生命週期開始,容器運行其init()方法進行Servlet的初始化;請求到達時調用Servlet的service()方法,service()方法會根據需要調用與請求對應的doGet或doPost等方法;當服務器關閉或項目被卸載時服務器會將Servlet實例銷燬,此時會調用Servlet的destroy()方法。
98、轉發(forward)和重定向(redirect)的區別?
答:forward是容器中控制權的轉向,是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL 的響應內容讀取過來,然後把這些內容再發給瀏覽器,瀏覽器根本不知道服務器發送的內容是從哪兒來的,所以它的地址欄中還是原來的地址。redirect就是服務器端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址,因此從瀏覽器的地址欄中可以看到跳轉後的鏈接地址,很明顯redirect無法訪問到服務器保護起來資源,但是可以從一個網站redirect到其他網站。forward更加高效,所以在滿足需要時儘量使用forward(通過調用RequestDispatcher對象的forward()方法,該對象可以通過ServletRequest對象的getRequestDispatcher()方法獲得),並且這樣也有助於隱藏實際的鏈接;在有些情況下,比如需要訪問一個其它服務器上的資源,則必須使用重定向(通過HttpServletResponse對象調用其sendRedirect()方法實現)。
99、JSP有哪些內置對象?作用分別是什麼?
答:JSP有9個內置對象:
- request:封裝客戶端的請求,其中包含來自GET或POST請求的參數;
- response:封裝服務器對客戶端的響應;
- pageContext:通過該對象可以獲取其他對象;
- session:封裝用戶會話的對象;
- application:封裝服務器運行環境的對象;
- out:輸出服務器響應的輸出流對象;
- config:Web應用的配置對象;
- page:JSP頁面本身(相當於Java程序中的this);
- exception:封裝頁面拋出異常的對象。
補充:如果用Servlet來生成網頁中的動態內容無疑是非常繁瑣的工作,另一方面,所有的文本和HTML標籤都是硬編碼,即使做出微小的修改,都需要進行重新編譯。JSP解決了Servlet的這些問題,它是Servlet很好的補充,可以專門用作爲用戶呈現視圖(View),而Servlet作爲控制器(Controller)專門負責處理用戶請求並轉發或重定向到某個頁面。基於Java的Web開發很多都同時使用了Servlet和JSP。JSP頁面其實是一個Servlet,能夠運行Servlet的服務器(Servlet容器)通常也是JSP容器,可以提供JSP頁面的運行環境,Tomcat就是一個Servlet/JSP容器。第一次請求一個JSP頁面時,Servlet/JSP容器首先將JSP頁面轉換成一個JSP頁面的實現類,這是一個實現了JspPage接口或其子接口HttpJspPage的Java類。JspPage接口是Servlet的子接口,因此每個JSP頁面都是一個Servlet。轉換成功後,容器會編譯Servlet類,之後容器加載和實例化Java字節碼,並執行它通常對Servlet所做的生命週期操作。對同一個JSP頁面的後續請求,容器會查看這個JSP頁面是否被修改過,如果修改過就會重新轉換並重新編譯並執行。如果沒有則執行內存中已經存在的Servlet實例。我們可以看一段JSP代碼對應的Java程序就知道一切了,而且9個內置對象的神祕面紗也會被揭開。
JSP頁面:
<%@ page pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%><!DOCTYPE html><html>
<head>
<base href="<%=basePath%>">
<title>首頁</title>
<style type="text/css">
* { font-family: "Arial"; }
</style>
</head>
<body>
<h1>Hello, World!</h1>
<hr/>
<h2>Current time is: <%= new java.util.Date().toString() %></h2>
</body></html>
對應的Java代碼:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/7.0.52
* Generated at: 2014-10-13 13:28:38 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/package org.apache.jsp;import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory
.getDefaultFactory();
private static java.util.Map<java.lang.String, java.lang.Long> _jspx_dependants;
private javax.el.ExpressionFactory _el_expressionfactory;
private org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String, java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(
getServletConfig().getServletContext()).getExpressionFactory();
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory
.getInstanceManager(getServletConfig());
}
public void _jspDestroy() {
}
public void _jspService(
final javax.servlet.http.HttpServletRequest request,
final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
// 內置對象就是在這裏定義的
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write('\r');
out.write('\n');
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";// 以下代碼通過輸出流將HTML標籤輸出到瀏覽器中 out.write("\r\n");
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write(" <head>\r\n");
out.write(" <base href=\"");
out.print(basePath);
out.write("\">\r\n");
out.write(" <title>首頁</title>\r\n");
out.write(" <style type=\"text/css\">\r\n");
out.write(" \t* { font-family: \"Arial\"; }\r\n");
out.write(" </style>\r\n");
out.write(" </head>\r\n");
out.write(" \r\n");
out.write(" <body>\r\n");
out.write(" <h1>Hello, World!</h1>\r\n");
out.write(" <hr/>\r\n");
out.write(" <h2>Current time is: ");
out.print(new java.util.Date().toString());
out.write("</h2>\r\n");
out.write(" </body>\r\n");
out.write("</html>\r\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)) { out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer();
} catch (java.io.IOException e) {
}
if (_jspx_page_context != null)
_jspx_page_context.handlePageException(t);
else
throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
100、get和post請求的區別?
答:
①get請求用來從服務器上獲得資源,而post是用來向服務器提交數據;
②get將表單中數據按照name=value的形式,添加到action 所指向的URL 後面,並且兩者使用"?"連接,而各個變量之間使用"&"連接;post是將表單中的數據放在HTTP協議的請求頭或消息體中,傳遞到action所指向URL;
③get傳輸的數據要受到URL長度限制(1024字節);而post可以傳輸大量的數據,上傳文件通常要使用post方式;
④使用get時參數會顯示在地址欄上,如果這些數據不是敏感數據,那麼可以使用get;對於敏感數據還是應用使用post;
⑤get使用MIME類型application/x-www-form-urlencoded的URL編碼(也叫百分號編碼)文本的格式傳遞參數,保證被傳送的參數由遵循規範的文本組成,例如一個空格的編碼是"%20"。