面試總結二

重點內容此貼爲轉載,原帖地址:http://blog.csdn.net/derrantcm/article/details/46658823
支持原創

114、java中實現多態的機制是什麼

答:重寫,重載。方法的重寫Overriding和重載Overloading是Java多態性的不同表現。
重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被”屏蔽”了。
如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱爲方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型。

115、靜態的多態和動態的多態的區別

答:靜態的多態: 即爲重載 ;方法名相同,參數個數或類型不相同。(overloading)。動態的多態: 即爲重寫;子類覆蓋父類的方法,將子類的實例傳與父類的引用調用的是子類的方法 實現接口的實例傳與接口的引用調用的實現類的方法。

116、extends和implement的不同

答:extends是繼承父類,只要那個類不是聲明爲final或者那個類定義爲abstract的就能繼承,JAVA中不支持多重繼承,但是可以用接口來實現,這樣就要用到implements,繼承只能繼承一個類,但implements可以實現多個接口,用逗號分開就行了,比如 class A extends B implements C,D,E。

117、適配器模式與橋樑模式的區別

答:適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。又稱爲轉換器模式、變壓器模式、包裝模式(把已有的一些類包裝起來,使之能有滿足需要的接口)。適配器模式的用意是將接口不同而功能相同或者相近的兩個接口加以轉換,包括適配器角色補充一些源角色沒有但目標接口需要的方法。就像生活中電器插頭是三相的,而電源插座是兩相的,這時需要一個三相變兩相的轉換器來滿足。
比如,在Java I/O庫中使用了適配器模式,象FileInputStream是一個適配器類,其繼承了InputStrem類型,同時持有一個對FileDiscriptor的引用。這是將一個FileDiscriptor對象適配成InputStrem類型的對象形式的適配器模式。StringReader是一個適配器類,其繼承了Reader類型,持有一個對String對象的引用。它將String的接口適配成Reader類型的接口。等等。
橋樑模式的用意是要把實現和它的接口分開,以便它們可以獨立地變化。橋樑模式並不是用來把一個已有的對象接到不相匹配的接口上的。當一個客戶端只知道一個特定的接口,但是又必須與具有不同接口的類打交道時,就應該使用橋樑模式。
比如,JDBC驅動器就是一個橋樑模式的應用,使用驅動程序的應用系統就是抽象化角色,而驅動器本身扮演實現化角色。應用系統和JDBC驅動器是相對獨立的。應用系統動態地選擇一個合適的驅動器,然後通過驅動器向數據庫引擎發出指令就可以訪問數據庫中的數據。

118、抽象類能否被實例化 ?抽象類的作用是什麼?

答:抽象類不能被實例化;抽象類通常不是由程序員定義的,而是由項目經理或模塊設計人設計抽象類的原因通常是爲了規範方法名 抽象類必須要繼承,不然沒法用,作爲模塊設計者,可以把讓底層程序員直接用得方法直接調用,而一些需要讓程序員覆蓋後自己做得方法則定義稱抽象方法。

120、String,StringBuffer, StringBuilder 的區別是什麼?String爲什麼是不可變的?

答:
1、String是字符串常量,StringBuffer和StringBuilder都是字符串變量。後兩者的字符內容可變,而前者創建後內容不可變。
2、String不可變是因爲在JDK中String類被聲明爲一個final類。
3、StringBuffer是線程安全的,而StringBuilder是非線程安全的。
ps:線程安全會帶來額外的系統開銷,所以StringBuilder的效率比StringBuffer高。如果對系統中的線程是否安全很掌握,可用StringBuffer,在線程不安全處加上關鍵字Synchronize。

121、Vector,ArrayList, LinkedList的區別是什麼?

答:
1、Vector、ArrayList都是以類似數組的形式存儲在內存中,LinkedList則以鏈表的形式進行存儲。
2、List中的元素有序、允許有重複的元素,Set中的元素無序、不允許有重複元素。(TreeSet 是二差樹實現的,Treeset中的數據是自動排好序的,不允許放入null值,HashSet 是哈希表實現的,HashSet中的數據是無序的,可以放入null,但只能放入一個null,兩者中的值都不能重複)
3、Vector線程同步,ArrayList、LinkedList線程不同步。
4、LinkedList適合指定位置插入、刪除操作,不適合查找;ArrayList、Vector適合查找,不適合指定位置的插入、刪除操作。
5、ArrayList在元素填滿容器時會自動擴充容器大小的0.5n+1,而Vector則是2n,因此ArrayList更節省空間。

122、HashTable, HashMap,TreeMap區別?

答:
1、HashTable線程同步,HashMap非線程同步。
2、HashTable不允許<鍵,值>有空值,HashMap允許<鍵,值>有空值。
3、HashTable使用Enumeration,HashMap使用Iterator。
4、HashTable中hash數組的默認大小是11,增加方式的old*2+1,HashMap中hash數組的默認大小是16,增長方式一定是2的指數倍。
5、TreeMap能夠把它保存的記錄根據鍵排序,默認是按升序排序。

123、Tomcat,Apache,JBoss,Weblogic的區別?

答:
Apache:全球應用最廣泛的http服務器,免費,出自apache基金組織
Tomcat:應用也算非常廣泛的web服務器,支持部分j2ee,免費,出自apache基金組織
JBoss:開源的應用服務器,比較受人喜愛,免費(文檔要收費)
Weblogic:應該說算是業界第一的app server,全部支持j2ee1.4,對於開發者,有免費使用一年的許可證。

124、GET,POST區別?

答:基礎知識:Http的請求格式如下。

<request line>:主要包含三個信息:1、請求的類型(GET或POST),2、要訪問的資源(如\res\img\a.jif),3、Http版本(http/1.1)
<header>:用來說明服務器要使用的附加信息
<blank line>:這是Http的規定,必須空一行
[<request-body>]:請求的內容數據

區別:
1、Get是從服務器端獲取數據,Post則是向服務器端發送數據。
2、在客戶端,Get方式通過URL提交數據,在URL地址欄可以看到請求消息,該消息被編碼過;Post數據則是放在Html header內提交。
3、對於Get方式,服務器端用Request.QueryString獲取變量的值;對用Post方式,服務器端用Request.Form獲取提交的數據值。
4、Get方式提交的數據最多1024字節,而Post則沒有限制。
5、Get方式提交的參數及參數值會在地址欄顯示,不安全,而Post不會,比較安全。
125、Session, Cookie區別

答:
1、Session由應用服務器維護的一個服務器端的存儲空間;Cookie是客戶端的存儲空間,由瀏覽器維護。
2、用戶可以通過瀏覽器設置決定是否保存Cookie,而不能決定是否保存Session,因爲Session是由服務器端維護的。
3、Session中保存的是對象,Cookie中保存的是字符串。
4、Session和Cookie不能跨窗口使用,每打開一個瀏覽器系統會賦予一個SessionID,此時的SessionID不同,若要完成跨瀏覽器訪問數據,可以使用Application。
5、Session、Cookie都有失效時間,過期後會自動刪除,減少系統開銷。

126、Servlet的生命週期

答:大致分爲4部:Servlet類加載–>實例化–>服務–>銷燬
1、Web Client向Servlet容器(Tomcat)發出Http請求。
2、Servlet容器接收Client端的請求。
3、Servlet容器創建一個HttpRequest對象,將Client的請求信息封裝到這個對象中。
4、Servlet創建一個HttpResponse對象。
5、Servlet調用HttpServlet對象的service方法,把HttpRequest對象和HttpResponse對象作爲參數傳遞給HttpServlet對象中。
6、HttpServlet調用HttpRequest對象的方法,獲取Http請求,並進行相應處理。
7、處理完成HttpServlet調用HttpResponse對象的方法,返回響應數據。
8、Servlet容器把HttpServlet的響應結果傳回客戶端。
其中的3個方法說明了Servlet的生命週期:
1、init():負責初始化Servlet對象。
2、service():負責響應客戶端請求。
3、destroy():當Servlet對象推出時,負責釋放佔用資源。

127、HTTP 報文包含內容

答:主要包含四部分:
1、request line
2、header line
3、blank line
4、request body

128、Statement與PreparedStatement的區別,什麼是SQL注入,如何防止SQL注入

答:
1、PreparedStatement支持動態設置參數,Statement不支持。
2、PreparedStatement可避免如類似 單引號 的編碼麻煩,Statement不可以。
3、PreparedStatement支持預編譯,Statement不支持。
4、在sql語句出錯時PreparedStatement不易檢查,而Statement則更便於查錯。
5、PreparedStatement可防止Sql注入,更加安全,而Statement不行。
什麼是SQL注入:
通過sql語句的拼接達到無參數查詢數據庫數據目的的方法。
如將要執行的sql語句爲 select * from table where name = “+appName+”,利用appName參數值的輸入,來生成惡意的sql語句,如將[‘or’1’=’1’] 傳入可在數據庫中執行。
因此可以採用PrepareStatement來避免Sql注入,在服務器端接收參數數據後,進行驗證,此時PrepareStatement會自動檢測,而Statement不 行,需要手工檢測。

129、sendRedirect, foward區別

答:轉發是服務器行爲,重定向是客戶端行爲。
轉發過程:客戶瀏覽器發送http請求->web服務器接受此請求->調用內部的一個方法在容器內部完成請求處理和轉發動作->將目標資源 發送給客戶;在這裏,轉發的路徑必須是同一個web容器下的url,其不能轉向到其他的web路徑上去,中間傳遞的是自己的容器內的request。在客戶瀏覽器路徑欄顯示的仍然是其第一次訪問的路徑,也就是說客戶是感覺不到服務器做了轉發的。轉發行爲是瀏覽器只做了一次訪問請求。
重定向過程:客戶瀏覽器發送http請求->web服務器接受後發送302狀態碼響應及對應新的location給客戶瀏覽器->客戶瀏覽器發現 是302響應,則自動再發送一個新的http請求,請求url是新的location地址->服務器根據此請求尋找資源併發送給客戶。在這裏location可以重定向到任意URL,既然是瀏覽器重新發出了請求,則就沒有什麼request傳遞的概念了。在客戶瀏覽器路徑欄顯示的是其重定向的 路徑,客戶可以觀察到地址的變化的。重定向行爲是瀏覽器做了至少兩次的訪問請求的。

130、反射講一講,主要是概念,都在哪需要反射機制,反射的性能,如何優化

答:反射機制的定義:是在運行狀態中,對於任意的一個類,都能夠知道這個類的所有屬性和方法,對任意一個對象都能夠通過反射機制調用一個類的任意方法,這種動態獲取類信息及動態調用類對象方法的功能稱爲java的反射機制。
反射的作用:
1、動態地創建類的實例,將類綁定到現有的對象中,或從現有的對象中獲取類型。
2、應用程序需要在運行時從某個特定的程序集中載入一個特定的類

131、關於Cache(Ehcache,Memcached)

http://xuezhongfeicn.blog.163.com/blog/static/2246014120106144143737/

133、畫出最熟悉的三個設計模式的類圖

134、寫代碼分別使得JVM的堆、棧和持久代發生內存溢出(棧溢出)

一個死循環 遞歸調用就 可以棧溢出。建好多對象實例放入ArrayList裏可以堆溢出。持久代溢出可以新建很多 ClassLoader 來裝載同一個類文件。
因爲方法區是保存類的相關信息的,所以當我們加載過多的類時就會導致方法區溢出。CGLIB同樣會緩存代理類的Class對象,但是我們可以通過配置讓它不緩存Class對象,這樣就可以通過反覆創建代理類達到使方法區溢出的目的。

package com.cdai.jvm.overflow;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MethodAreaOverflow2 {
    static class OOMObject {
    }
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method,
                        Object[] args, MethodProxy proxy) throws Throwable {
                    return method.invoke(obj, args);
                }
            });
            OOMObject proxy = (OOMObject) enhancer.create();
            System.out.println(proxy.getClass());
        }
    }
}

堆溢出比較簡單,只需通過創建一個大數組對象來申請一塊比較大的內存,就可以使堆發生溢出。

package com.cdai.jvm.overflow;
public class HeapOverflow {
    private static final int MB = 1024 * 1024;
    SuppressWarnings("unused")
    public static void main(String[] args) {
        byte[] bigMemory = new byte[1024 * MB];
    }
}

棧溢出也比較常見,有時我們編寫的遞歸調用沒有正確的終止條件時,就會使方法不斷遞歸,棧的深度不斷增大,最終發生棧溢出。

package com.cdai.jvm.overflow;
public class StackOverflow {
    private static int stackDepth = 1;
    public static void stackOverflow() {
        stackDepth++;
        stackOverflow();
    }

    public static void main(String[] args) {
        try {
            stackOverflow();
        } 
        catch (Exception e) {
            System.err.println("Stack depth: " + stackDepth);
            e.printStackTrace();
        }
    }
}

135、hashmap的內部實現機制,hash是怎樣實現的,什麼時候rehash

136、java的內存管理

137、分佈式緩存的內存管理,如何管理和釋放不斷膨脹的session,memcache是否熟悉

138、oralce的底層管理(怎樣讓查詢快,插入慢)

139、java底層是怎樣對文件操作的

140、研究了哪些框架的源碼

141、併發問題,鎖,怎麼處理死鎖,髒數據處理

141、性能問題

142、equals和hashcode這些方法怎麼使用的

143、java的NIO

http://lvwenwen.iteye.com/blog/1706221

144、先從項目模塊入手,詳細問項目模塊是怎麼實現的,遇到的問題怎麼解決(一定要說自己做過的,真實的情況)

145、sql語句優化怎麼做的,建索引的時候要考慮什麼

146、spring ioc你的理解,ioc容器啓動的過程是什麼樣的,什麼是ioc,aop 你個人的理解是什麼

147、jms 你個人的理解,就是消息接收完怎麼處理,介質處理(爲什麼重啓mq就能恢復)

解答:http://setting.iteye.com/blog/1097767

148、sychronized 機制 加了static 方法的同步異同,A 調用 B,A執行完了,B沒執行完,怎麼解決這個同步問題

149、servlet 默認是線程安全的嗎,爲什麼不是線程安全的

解答:不是 :url:http://westlifesz.iteye.com/blog/49511
http://jsjxqjy.iteye.com/blog/1563249
http://developer.51cto.com/art/200907/133827.htm

150、spring裏面的action 默認是單列的,怎麼配置成多列?

socpe =proptype?

151、socket 是用的什麼協議,tcp協議連接(握手)的過程是什麼樣的,socket使用要注意哪些問題

解答:tcp協議,

152、數據庫連接池設置幾個連接,是怎麼處理的,說說你的理解

153、自定義異常要怎麼考慮呢,checked的異常跟 unchecked 的異常的區別

154、線程池是怎麼配置的,怎麼用的,要注意哪些,說下個人的理解

155、tomact 裏session共享是怎麼做到的,

解答:http://zhli986-yahoo-cn.iteye.com/blog/1344694

156、服務器集羣有搭建過嗎

解答:http://www.iteye.com/topic/1119823

157、阿里B2B北京專場java開發面試題(2011.10.29)

http://yueyemaitian.iteye.com/blog/1387901
jvm的體系結構,畫了之後說各個部分的職責,並扯到運行期優化。

158、文件拷貝,把一個文件的內容拷貝到另外一個文件裏

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;


public class FileCopy {
    public static void main(String[] args)
    {
          try
            {
                int bytesum = 0;
                int byteread = 0;
                File oldfile = new File( "c:/test.txt");
                if ( oldfile.exists() )
                { //文件存在時
                    InputStream inStream = new FileInputStream( "c:/test.txt" ); //讀入原文件
                    FileOutputStream fs = new FileOutputStream( "d:/test.txt" );
                    byte[] buffer = new byte[ 1444 ];
                    int length;
                    while ( ( byteread = inStream.read( buffer ) ) != -1 )
                    {
                        bytesum += byteread; //字節數 文件大小
                        System.out.println( bytesum );
                        fs.write( buffer, 0, byteread );
                    }
                    inStream.close();
                }
            }
            catch ( Exception e )
            {
                System.out.println( "複製單個文件操作出錯" );
                e.printStackTrace();

            }
    }
}

159、HashMap遍歷的幾種方法

// 第一種:效率高,以後一定要使用此種方式!
  Map map = new HashMap();
  Iterator iter = map.entrySet().iterator();
  while (iter.hasNext()) {
  Map.Entry entry = (Map.Entry) iter.next();
  Object key = entry.getKey();
  Object val = entry.getValue();
  }
  
// 第二種:效率低,以後儘量少使用!
  Map map = new HashMap();
  Iterator iter = map.keySet().iterator();
  while (iter.hasNext()) {
  Object key = iter.next();
  Object val = map.get(key);
  }  

160、寫一個類,連接數據庫並執行一條sql

import java.sql.*;

public class SqlDemo{
     public static void main(String[] args) {
        try {
            String url = "jdbc:mysql://localhost/mydb";
            String user = "root";
            String pwd = "123456";

            //加載驅動,這一句也可寫爲:Class.forName("com.mysql.jdbc.Driver");
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            //建立到MySQL的連接
            Connection conn = DriverManager.getConnection(url, user, pwd);

            //執行SQL語句
            Statement stmt = conn.createStatement();//創建語句對象,用以執行sql語言
            ResultSet rs = stmt.executeQuery("select * from student");

            //處理結果集
            while (rs.next()) {
                String name = rs.getString("name");
                System.out.println(name);
            }
            rs.close();//關閉數據庫
            conn.close();
        } catch (Exception ex) {
            System.out.println("Error : " + ex.toString());
        }
    }     
}

161、JVM性能調優,都做了什麼?

答:
1).控制GC的行爲。GC是一個後臺處理,但是它也是會消耗系統性能的,因此經常會根據系統運行的程序的特性來更改GC行爲
2).控制JVM堆棧大小。一般來說,JVM在內存分配上不需要你修改,(舉例)但是當你的程序新生代對象在某個時間段產生的比較多的時候,就需要控制新生代的堆大小.同時,還要需要控制總的JVM大小避免內存溢出。
3).控制JVM線程的內存分配。如果是多線程程序,產生線程和線程運行所消耗的內存也是可以控制的,需要通過一定時間的觀測後,配置最優結果。
162、TCP頭部都什麼內容

這裏寫圖片描述
這裏寫圖片描述
 源端口和目的端口:各佔2字節.端口是傳輸層與應用層的服務接口.傳輸層的複用和分用功能都要通過端口才能實現。

 序號:佔4字節.TCP 連接中傳送的數據流中的每一個字節都編上一個序號.序號字段的值則指的是本報文段所發送的數據的第一個字節的序號

 確認號:佔 4 字節,是期望收到對方的下一個報文段的數據的第一個字節的序號

 數據偏移/首部長度:佔4位,它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠.“數據偏移”的單位是32位字(以 4 字節爲計算單位)

 保留:佔6位,保留爲今後使用,但目前應置爲0

 緊急URG:當 URG=1 時,表明緊急指針字段有效.它告訴系統此報文段中有緊急數據,應儘快傳送(相當於高優先級的數據)

 確認ACK:只有當 ACK=1 時確認號字段纔有效.當 ACK=0 時,確認號無效

 PSH(PuSH):接收 TCP 收到 PSH = 1 的報文段,就儘快地交付接收應用進程,而不再等到整個緩存都填滿了後再向上交付。

 RST (ReSeT):當 RST=1 時,表明 TCP 連接中出現嚴重差錯(如由於主機崩潰或其他原因),必須釋放連接,然後再重新建立運輸連接。

 同步 SYN:同步 SYN = 1 表示這是一個連接請求或連接接受報文

 終止 FIN:用來釋放一個連接.FIN=1 表明此報文段的發送端的數據已發送完畢,並要求釋放運輸連接

 檢驗和:佔 2 字節.檢驗和字段檢驗的範圍包括首部和數據這兩部分.在計算檢驗和時,要在 TCP 報文段的前面加上 12 字節的僞首部

 緊急指針:佔 16 位,指出在本報文段中緊急數據共有多少個字節(緊急數據放在本報文段數據的最前面)

 選項:長度可變.TCP 最初只規定了一種選項,即最大報文段長度 MSS.MSS 告訴對方 TCP:“我的緩存所能接收的報文段的數據字段的最大長度是 MSS 個字節.” [MSS(Maximum Segment Size)是 TCP 報文段中的數據字段的最大長度.數據字段加上 TCP 首部纔等於整個的 TCP 報文段]

 填充:這是爲了使整個首部長度是4 字節的整數倍

 其他選項:

 窗口擴大:佔3字節,其中有一個字節表示移位值 S.新的窗口值等於TCP 首部中的窗口位數增大到(16 + S),相當於把窗口值向左移動 S 位後獲得實際的窗口大小

 時間戳:佔10 字節,其中最主要的字段時間戳值字段(4字節)和時間戳回送回答字段(4字節)

 選擇確認:接收方收到了和前面的字節流不連續的兩2字節.如果這些字節的序號都在接收窗口之內,那麼接收方就先收下這些數據,但要把這些信息準確地告訴發送方,使發送方不要再重複發送這些已收到的數據

163、DUP報文結構

這裏寫圖片描述
這裏寫圖片描述
164、HTTP報文結構
這裏寫圖片描述
HTTP請求報文:一個HTTP請求報文由請求行(request line)、請求頭部(header)、空行和請求數據4個部分組成。
這裏寫圖片描述
or

<request-line>
<headers>
<blank line>
[<request-body>]

請求行:請求行由請求方法字段、URL字段和HTTP協議版本字段3個字段組成,它們用空格分隔。

請求頭部:請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號“:”分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的請求頭有:

User-Agent:產生請求的瀏覽器類型。
Accept:客戶端可識別的內容類型列表。
Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機。
空行:最後一個請求頭之後是一個空行,發送回車符和換行符,通知服務器以下不再有請求頭。
請求數據:請求數據不在GET方法中使用,而是在POST方法中使用。POST方法適用於需要客戶填寫表單的場合。與請求數據相關的最常使用的請求頭是Content-Type和Content-Length。

HTTP響應報文:HTTP響應由四個部分組成,分別是:狀態行、消息報頭、空行和響應正文。

<status-line>
<headers>
<blank line>
[<response-body>]

狀態行格式如下:HTTP-Version Status-Code Reason-Phrase CRLF
HTTP-Version表示服務器HTTP協議的版本;
Status-Code表示服務器發回的響應狀態代碼;
Reason-Phrase表示狀態代碼的文本描述。
CRLF表示換行
狀態代碼由三位數字組成,第一個數字定義了響應的類別,且有五種可能取值。
1xx:指示信息–表示請求已接收,繼續處理。
2xx:成功–表示請求已被成功接收、理解、接受。
3xx:重定向–要完成請求必須進行更進一步的操作。
4xx:客戶端錯誤–請求有語法錯誤或請求無法實現。
5xx:服務器端錯誤–服務器未能實現合法的請求。
常見狀態代碼、狀態描述的說明如下。
200 OK:客戶端請求成功。
400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解。
401 Unauthorized:請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用。
403 Forbidden:服務器收到請求,但是拒絕提供服務。
404 Not Found:請求資源不存在,舉個例子:輸入了錯誤的URL。
500 Internal Server Error:服務器發生不可預期的錯誤。
503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間後可能恢復正常,舉個例子:HTTP/1.1 200 OK(CRLF)。

165、TCP協議和UDP協議的區別

1,TCP協議面向連接,UDP協議面向非連接
2,TCP協議傳輸速度慢,UDP協議傳輸速度快
3,TCP協議保證數據順序,UDP協議不保證
4,TCP協議保證數據正確性,UDP協議可能丟包
5,TCP協議對系統資源要求多,UDP協議要求少

166、java對象在虛擬機中怎樣實例化。

首先判斷對象實例化的類型信息是否已經在JVM,如果不存在就對類型信息進行裝載、連接和初始化,之後就可以使用了,可以訪問類型的靜態字段和方法,最後再創建類型實例,爲實例對象分配內存空間。對象的創建分爲顯示創建和隱式創建兩種。

顯示創建分爲:
1、通過new創建;
2、通過java.lang.Class的newInstance方法創建;
3、通過clone方法創建;
4、通過java.io.ObjectInputStream的readObject方法創建。

隱式創建分爲:
1、啓動類的main方法的string數組參數;
2、常量池的CONSTANT_String_info表項被解析的時候會創建一個String對象;
3、每個加載的類都會創建一個java.lang.Class對象;
4、字符串+操作的時候,創建StringBuffer/StringBuilder對象。

對象在內存中的存儲
非嚴格意義來說,對象都存儲在堆上,由垃圾收集器負責回收不再被引用的對象。但是隨着jvm運行期編譯技術的不斷進步,棧上分配對象和標量替換技術使得非逃逸對象可以分配在棧上。當然絕大多數對象都是分配在堆上的,此處我們主要討論對象在堆中的存儲。

對象的內容有:
1、實例數據;
2、指向堆中類型信息的指針;
3、對象鎖相關的數據;
4、多線程協調完成同一件事情的時候wait set相關的隊列;
5、垃圾收集相關的內容,如存活時間、finalize方法是否運行過。

對象在內存中存儲主要有兩種方式:
1、堆劃分爲句柄池和對象池,創建對象後的得到的引用是指向句柄池的指針,句柄池指針則指向對象池裏的對象;
2、堆只分爲對象池,引用直接指向對象池中的對象。

167、java連接池中連接如何收縮?

Java連接池中一般都有設置最大、最小連接數,當連接池中的連接達到最大連接數時,就不會再創建新的連接。一般來說定時檢測連接池中空閒連接數,當超過一定時間沒有使用的話,先判斷現有連接池中的連接是否大於最小連接數,如果大於的話,就好回收這個空閒連接,否則的話,就不會進行回收。
如何確保連接池中的最小連接數呢?有動態和靜態兩種策略。動態即每隔一定時間就對連接池進行檢測,如果發現連接數量小於最小連接數,則補充相應數量的新連接,以保證連接池的正常運轉。靜態是發現空閒連接不夠時再去檢查。
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述
上面就是連接池的配置信息
168、Java多線程問題

有如下一個類:

public class MyStack {  
    private List<String> list = new ArrayList<String>();  
    public synchronized void push(String value) {  
        synchronized (this) {  
            list.add(value);  
            notify();  
        }  
    }  
    public synchronized String pop() throws InterruptedException {  
        synchronized (this) {  
            if (list.size() <= 0) {  
                wait();  
            }  
            return list.remove(list.size() - 1);  
        }  
    }  
} 

問題: 這段代碼大多數情況下運行正常,但是某些情況下會出問題。什麼時候會出現什麼問題?如何修正?
代碼分析:
從整體上,在併發狀態下,push和pop都使用了synchronized的鎖,來實現同步,同步的數據對象是基於List的數據;大部分情況下是可以正常工作的。
問題描述:

狀況1:
1. 假設有三個線程: A,B,C. A 負責放入數據到list,就是調用push操作, B,C分別執行Pop操作,移除數據。
2. 首先B先執行,於pop中的wait()方法處,進入waiting狀態,進入等待隊列,釋放鎖。
3. A首先執行放入數據push操作到List,在調用notify()之前; 同時C執行pop(),由於synchronized,被阻塞,進入Blocked狀態,放入基於鎖的等待隊列。注意,這裏的隊列和2中的waiting等待隊列是兩個不同的隊列。
4. A線程調用notify(),喚醒等待中的線程A。
5. 如果此時, C獲取到基於對象的鎖,則優先執行,執行pop方法,獲取數據,從list移除一個元素。
6. 然後,A獲取到競爭鎖,A中調用list.remove(list.size() - 1),則會報數據越界exception。

狀況2:
1. 相同於狀況1
2. B、C都處於等待waiting狀態,釋放鎖。等待notify()、notifyAll()操作的喚醒。
3. 存在被虛假喚醒的可能。

何爲虛假喚醒?
虛假喚醒就是一些obj.wait()會在除了obj.notify()和obj.notifyAll()的其他情況被喚醒,而此時是不應該喚醒的。
解決的辦法是基於while來反覆判斷進入正常操作的臨界條件是否滿足:

synchronized (obj) {  
    while (<condition does not hold>)  
        obj.wait();  
        ... // Perform action appropriate to condition  
} 

如何修復問題?
1. 使用可同步的數據結構來存放數據,比如LinkedBlockingQueue之類。由這些同步的數據結構來完成繁瑣的同步操作。
2. 雙層的synchronized使用沒有意義,保留外層即可。
3. 將if替換爲while,解決虛假喚醒的問題。

168、super.getClass方法調用

先看一下程序的代碼,看看最後的輸出結果是多少?

import java.util.Date;
public  class Test extends Date {
      public void test() {
              System.out.println(super.getClass().getName());
      }
      public static void main(String[] args) {
              new Test().test();
      }
}

如果不瞭解,很可能得出錯誤的答案,其實答案是Test,是不是很奇怪,結果竟然是Test。
這道題就屬於腦筋急轉彎的題目,很簡單的,也很容易落入陷阱中。我想大部分人之所以落入陷阱中可能是因爲這個類繼承了。
如果在test方法中,直接調用getClass().getName()方法的化,相當於調用this.getClass().getName(),這樣返回的就是Test類名,getClass()返回對象運行時的字節碼對象,當前對象是Test的實例,所以其字節碼對象就是Test
由於getClass()在Object類中定義成了final,子類不能覆蓋該方法,所以,Date類也是沒有這個方法的,在test方法中調用getClass().getName()方法,其實就是在調用從父類繼承的getClass()方法,等效於調用super.getClass().getName()方法,所以,super.getClass().getName()方法返回的也應該是Test。
如果想得到父類的名稱,應該用如下代碼:
getClass().getSuperClass().getName();

169、B樹,B+樹,B*樹

170、知道哪些鎖機制?(不限JAVA)什麼是可重入性? synchronized的可重入性?顯式鎖跟synchronized對比?semaphore機制怎麼實現?一個線程從wait()狀態醒來是不是一定被notify()了?

171、什麼情景下應用過TreeMap?TreeMap內部怎麼實現的?

TreeMap 是一個有序的key-value集合,它是通過紅黑樹實現的。
TreeMap 繼承於AbstractMap,所以它是一個Map,即一個key-value集合。
TreeMap 實現了NavigableMap接口,意味着它支持一系列的導航方法。比如返回有序的key集合。
TreeMap 實現了Cloneable接口,意味着它能被克隆。
TreeMap 實現了java.io.Serializable接口,意味着它支持序列化。
TreeMap基於紅黑樹(Red-Black tree)實現。該映射根據其鍵的自然順序進行排序,或者根據創建映射時提供的 Comparator 進行排序,具體取決於使用的構造方法。
TreeMap的基本操作 containsKey、get、put 和 remove 的時間複雜度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fast的。

178、Java網絡編程,socket描述什麼的?同一端口可否同時被兩個應用監聽?

Socket通常也稱作”套接字”,用於描述IP地址和端口,是一個通信鏈的句柄。網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱爲一個Socket,一個Socket由一個IP地址和一個端口號唯一確定。應用程序通常通過”套接字”向網絡發出請求或者應答網絡請求。 Socket是TCP/IP協議的一個十分流行的編程界面,但是,Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯繫的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程。
Socket通訊過程:服務端監聽某個端口是否有連接請求,客戶端向服務端發送連接請求,服務端收到連接請求向客戶端發出接收消息,這樣一個連接就建立起來了。客戶端和服務端都可以相互發送消息與對方進行通訊。

Socket的基本工作過程包含以下四個步驟:
1、創建Socket;
2、打開連接到Socket的輸入輸出流;
3、按照一定的協議對Socket進行讀寫操作;
4、關閉Socket。

在java.net包下有兩個類:Socket和ServerSocket。ServerSocket用於服務器端,Socket是建立網絡連接時使用的。在連接成功時,應用程序兩端都會產生一個Socket實例,操作這個實例,完成所需的會話。對於一個網絡連接來說,套接字是平等的,並沒有差別,不因爲在服務器端或在客戶端而產生不同級別。不管是Socket還是ServerSocket它們的工作都是通過SocketImpl類及其子類完成的。

Socket(String host,int port);//創建一個流套接字並將其連接到指定主機上的指定端口號
Socket(InetAddress address,int port, InetAddress localAddr,int localPort);//創建一個套接字並將其連接到指定遠程地址上的指定遠程端口
Socket(String host,int port, InetAddress localAddr,int localPort);//創建一個套接字並將其連接到指定遠程主機上的指定遠程端口
Socket(SocketImpl impl);//使用用戶指定的 SocketImpl 創建一個未連接 Socket

ServerSocket(int port);//創建綁定到特定端口的服務器套接字
ServerSocket(int port,int backlog);//利用指定的 backlog 創建服務器套接字並將其綁定到指定的本地端口號
ServerSocket(int port,int backlog, InetAddress bindAddr);//使用指定的端口、偵聽 backlog 和要綁定到的本地 IP地址創建服務器

179、在多線程裏面如何實現一個變量被多線程獨立的訪問。

1,在線程內部創建變量
2,使用threadlocal對象

180、concurrentHashMap的實現

ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。
有些方法需要跨段,比如size()和containsValue(),它們可能需要鎖定整個表而而不僅僅是某個段,這需要按順序鎖定所有段,操作完畢後,又按順序釋放所有段的鎖。這裏“按順序”是很重要的,否則極有可能出現死鎖,在ConcurrentHashMap內部,段數組是final的,並且其成員變量實際上也是final的,但是,僅僅是將數組聲明爲final的並不保證數組成員也是final的,這需要實現上的保證。這可以確保不會出現死鎖,因爲獲得鎖的順序是固定的。

ConcurrentHashMap 類中包含兩個靜態內部類 HashEntry 和 Segment。HashEntry 用來封裝映射表的鍵 / 值對;Segment 用來充當鎖的角色,每個 Segment 對象守護整個散列映射表的若干個桶。每個桶是由若干個 HashEntry 對象鏈接起來的鏈表。一個 ConcurrentHashMap 實例中包含由若干個 Segment 對象組成的數組。
HashEntry 用來封裝散列映射表中的鍵值對。在 HashEntry 類中,key,hash 和 next 域都被聲明爲 final 型,value 域被聲明爲 volatile 型。同時,HashEntry 類的 value 域被聲明爲 Volatile 型,Java 的內存模型可以保證:某個寫線程對 value 域的寫入馬上可以被後續的某個讀線程“看”到。在 ConcurrentHashMap 中,不允許用 unll 作爲鍵和值,當讀線程讀到某個 HashEntry 的 value 域的值爲 null 時,便知道產生了衝突——發生了重排序現象,需要加鎖後重新讀入這個 value 值。這些特性互相配合,使得讀線程即使在不加鎖狀態下,也能正確訪問 ConcurrentHashMap。
在 ConcurrentHashMap 中,在散列時如果產生“碰撞”,將採用“分離鏈接法”來處理“碰撞”:把“碰撞”的 HashEntry 對象鏈接成一個鏈表。由於 HashEntry 的 next 域爲 final 型,所以新節點只能在鏈表的表頭處插入。
Segment 類繼承於 ReentrantLock 類,從而使得 Segment 對象能充當鎖的角色。每個 Segment 對象用來守護其(成員對象 table 中)包含的若干個桶。
避免熱點域:在 ConcurrentHashMap中,每一個 Segment 對象都有一個 count 對象來表示本 Segment 中包含的 HashEntry 對象的個數。這樣當需要更新計數器時,不用鎖定整個 ConcurrentHashMap。
ConcurrentHashMap 在默認併發級別會創建包含 16 個 Segment 對象的數組。每個 Segment 的成員對象 table 包含若干個散列表的桶。每個桶是由 HashEntry 鏈接起來的一個鏈表。如果鍵能均勻散列,每個 Segment 大約守護整個散列表中桶總數的 1/16。
在 ConcurrentHashMap 中,線程對映射表做讀操作時,一般情況下不需要加鎖就可以完成,對容器做結構性修改的操作才需要加鎖。
用 Volatile 變量協調讀寫線程間的內存可見性。
ConcurrentHashMap 實現高併發的總結
基於通常情形而優化
在實際的應用中,散列表一般的應用場景是:除了少數插入操作和刪除操作外,絕大多數都是讀取操作,而且讀操作在大多數時候都是成功的。正是基於這個前提,ConcurrentHashMap 針對讀操作做了大量的優化。通過 HashEntry 對象的不變性和用 volatile 型變量協調線程間的內存可見性,使得大多數時候,讀操作不需要加鎖就可以正確獲得值。這個特性使得 ConcurrentHashMap 的併發性能在分離鎖的基礎上又有了近一步的提高。

總結
ConcurrentHashMap 是一個併發散列映射表的實現,它允許完全併發的讀取,並且支持給定數量的併發更新。相比於 HashTable 和用同步包裝器包裝的 HashMap(Collections.synchronizedMap(new HashMap())),ConcurrentHashMap 擁有更高的併發性。在 HashTable 和由同步包裝器包裝的 HashMap 中,使用一個全局的鎖來同步不同線程間的併發訪問。同一時間點,只能有一個線程持有鎖,也就是說在同一時間點,只能有一個線程能訪問容器。這雖然保證多線程間的安全併發訪問,但同時也導致對容器的訪問變成串行化的了。
在使用鎖來協調多線程間併發訪問的模式下,減小對鎖的競爭可以有效提高併發性。有兩種方式可以減小對鎖的競爭:
1. 減小請求 同一個鎖的 頻率。
2. 減少持有鎖的 時間。
ConcurrentHashMap 的高併發性主要來自於三個方面:
1. 用分離鎖實現多個線程間的更深層次的共享訪問。
2. 用 HashEntery 對象的不變性來降低執行讀操作的線程在遍歷鏈表期間對加鎖的需求。
3. 通過對同一個 Volatile 變量的寫 / 讀訪問,協調不同線程間讀 / 寫操作的內存可見性。
使用分離鎖,減小了請求 同一個鎖的頻率。
通過 HashEntery 對象的不變性及對同一個 Volatile 變量的讀 / 寫來協調內存可見性,使得 讀操作大多數時候不需要加鎖就能成功獲取到需要的值。由於散列映射表在實際應用中大多數操作都是成功的 讀操作,所以 2 和 3 既可以減少請求同一個鎖的頻率,也可以有效減少持有鎖的時間。
通過減小請求同一個鎖的頻率和儘量減少持有鎖的時間 ,使得 ConcurrentHashMap 的併發性相對於 HashTable 和用同步包裝器包裝的 HashMap有了質的提高。

181、TCP長連接和短連接

當網絡通信時採用TCP協議時,在真正的讀寫操作之前,server與client之間必須建立一個連接,當讀寫操作完成後,雙方不再需要這個連接時它們可以釋放這個連接,連接的建立是需要三次握手的,而釋放則需要4次握手,所以說每個連接的建立都是需要資源消耗和時間消耗的。
TCP短連接
我們模擬一下TCP短連接的情況,client向server發起連接請求,server接到請求,然後雙方建立連接。client向server發送消息,server迴應client,然後一次讀寫就完成了,這時候雙方任何一個都可以發起close操作,不過一般都是client先發起close操作。爲什麼呢,一般的server不會回覆完client後立即關閉連接的,當然不排除有特殊的情況。從上面的描述看,短連接一般只會在client/server間傳遞一次讀寫操作
短連接的優點是:管理起來比較簡單,存在的連接都是有用的連接,不需要額外的控制手段。
TCP長連接
接下來我們再模擬一下長連接的情況,client向server發起連接,server接受client連接,雙方建立連接。Client與server完成一次讀寫之後,它們之間的連接並不會主動關閉,後續的讀寫操作會繼續使用這個連接。
首先說一下TCP/IP詳解上講到的TCP保活功能,保活功能主要爲服務器應用提供,服務器應用希望知道客戶主機是否崩潰,從而可以代表客戶使用資源。如果客戶已經消失,使得服務器上保留一個半開放的連接,而服務器又在等待來自客戶端的數據,則服務器將永遠等待客戶端的數據,保活功能就是試圖在服務器端檢測到這種半開放的連接。
如果一個給定的連接在兩小時內沒有任何的動作,則服務器就向客戶發一個探測報文段,客戶主機必須處於

以下4個狀態之一:

 客戶主機依然正常運行,並從服務器可達。客戶的TCP響應正常,而服務器也知道對方是正常的,服務器在兩小時後將保活定時器復位。

 客戶主機已經崩潰,並且關閉或者正在重新啓動。在任何一種情況下,客戶的TCP都沒有響應。服務端將不能收到對探測的響應,並在75秒後超時。服務器總共發送10個這樣的探測 ,每個間隔75秒。如果服務器沒有收到一個響應,它就認爲客戶主機已經關閉並終止連接。

 客戶主機崩潰並已經重新啓動。服務器將收到一個對其保活探測的響應,這個響應是一個復位,使得服務器終止這個連接。

 客戶機正常運行,但是服務器不可達,這種情況與2類似,TCP能發現的就是沒有收到探查的響應。
從上面可以看出,TCP保活功能主要爲探測長連接的存活狀況,不過這裏存在一個問題,存活功能的探測週期太長,還有就是它只是探測TCP連接的存活,屬於比較斯文的做法,遇到惡意的連接時,保活功能就不夠使了。

在長連接的應用場景下,client端一般不會主動關閉它們之間的連接,Client與server之間的連接如果一直不關閉的話,會存在一個問題,隨着客戶端連接越來越多,server早晚有扛不住的時候,這時候server端需要採取一些策略,如關閉一些長時間沒有讀寫事件發生的連接,這樣可以避免一些惡意連接導致server端服務受損;如果條件再允許就可以以客戶端機器爲顆粒度,限制每個客戶端的最大長連接數,這樣可以完全避免某個蛋疼的客戶端連累後端服務。
長連接和短連接的產生在於client和server採取的關閉策略,具體的應用場景採用具體的策略,沒有十全十美的選擇,只有合適的選擇。

182、Java實現Socket長連接和短連接.

概念
Socket:socket實際上是對TCP/IP進行的封裝,我們可以使用socket套接字通過socket來傳輸。首先我們需要明白的一個概念就是通道,簡單地說通道就是兩個對端可以隨時傳輸數據的信道。我麼常說的所謂建立socket連接,也就是建立了客戶端與服務器端的通道。
長短連接:顯而易見,長連接也就是這個socket連接一直保持連接,也就是通道一直保持通暢,兩個對端可以隨時發送和接收數據;短連接就是我們發送一次或有限的幾次,socket通道就被關閉了。首先,我們必須明白的是socket連接後,如果沒有任何一方關閉,這個通道是一直保持着的,換句話說,如果任何一方都不關閉連接,這個socket連接就是長連接,因此Java中的socket本身就是支持長連接的(如一個簡單的實驗:服務器端不關閉連接,服務器端每隔10秒發送一次數據,服務器端每次都能正確接受數據,這個實驗就可以證明)。
那麼既然socket本身是支持長連接的,那麼爲什麼我們還要提短連接的概念呢?試想一箇中國移動的短信網關(即通過發佈socket通信接口)每時每分都有N多個連接發送短信請求,加入服務器不加任何限制地直接和客戶端使用長連接那麼可想而知服務器需要承受多麼大的壓力。所以一般的socket服務器端都是會設定超時時間的,也就是timeout,如果超過timeout服務器沒有接收到任何數據,那麼該服務器就會關閉該連接,從而使得服務器資源得到有效地使用。
如何實現長短連接
有了長短連接的概念,服務器如果超過timeout時間接收不到客戶端的通信就會斷開連接,那麼假如客戶端在timeout時間前一秒(或者更短的時間)發送一條激活數據來使服務器端重新計時,如此重複就能保證服務器一直不能進入timeout時間,從而一直保持連接,這就是長連接的實現原理。下面我們通過一張圖說明:

由上圖可見,是否是長連接完全取決於客戶端是否會在timeout時間發送心跳消息,因此長短連接是和客戶端相關的,服務器端沒有任何區別(只不過服務器端需要設定timeout而已)。

183、適配器模式,裝飾模式,代理模式異同(適配器模式和代理模式的區別答案在其中)。

適配器模式,一個適配允許通常因爲接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中。
裝飾器模式,原有的不能滿足現有的需求,對原有的進行增強。
代理模式,同一個類而去調用另一個類的方法,不對這個方法進行直接操作。
適配器的特點在於兼容,從代碼上的特點來說,適配類與原有的類具有相同的接口,並且持有新的目標對象。就如同一個三孔轉2孔的適配器一樣,他有三孔的插頭,可以插到三孔插座裏,又有兩孔的插座可以被2孔插頭插入。適配器模式是在於對原有3孔的改造。在使用適配器模式的時候,我們必須同時持有原對象,適配對象,目標對象。
裝飾器模式特點在於增強,他的特點是被裝飾類和所有的裝飾類必須實現同一個接口,而且必須持有被裝飾的對象,可以無限裝飾。
代理模式的特點在於隔離,隔離調用類和被調用類的關係,通過一個代理類去調用。
總的來說就是如下三句話:

1) 適配器模式是將一個類(a)通過某種方式轉換成另一個類(b).
2) 裝飾模式是在一個原有類(a)的基礎之上增加了某些新的功能變成另一個類(b).
3) 代理模式是將一個類(a)轉換成具體的操作類(b).

184、什麼是可重入鎖

見文章

185、volatile變量的兩種特性。

可見性:volatile變量則是通過主內存完成交換,但是兩者區別在於volatile變量能立即同步到主內存中,當一個線程修改變量的變量的時候,立刻會被其他線程感知到。
阻止重排序:普通變量僅僅會保證在該方法的執行過程中所依賴複製結果的地方都能獲取到正確的結果,而不能保證變量複製操作的
順序與程序代碼中的執行順序一致。

186、volatile變量詳解

Volatile 變量具有 synchronized 的可見性特性,但是不具備原子特性。這就是說線程能夠自動發現 volatile 變量的最新值。Volatile 變量可用於提供線程安全,但是隻能應用於非常有限的一組用例:多個變量之間或者某個變量的當前值與修改後值之間沒有約束。因此,單獨使用 volatile 還不足以實現計數器、互斥鎖或任何具有與多個變量相關的不變式(Invariants)的類(例如 “start <=end”)。此外,volatile 變量不會像鎖那樣造成線程阻塞,因此也很少造成可伸縮性問題。在某些情況下,如果讀操作遠遠大於寫操作,volatile 變量還可以提供優於鎖的性能優勢。

正確使用 volatile 變量的條件:
要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:

對變量的寫操作不依賴於當前值。
該變量沒有包含在具有其他變量的不變式中。

這些條件表明,可以被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。
第一個條件的限制使 volatile 變量不能用作線程安全計數器。雖然增量操作(x++)看上去類似一個單獨操作,實際上它是一個由讀取-修改-寫入操作序列組成的組合操作,必須以原子方式執行,而 volatile 不能提供必須的原子特性。實現正確的操作需要使 x 的值在操作期間保持不變,而 volatile 變量無法實現這點。(然而,如果將值調整爲只從單個線程寫入,那麼可以忽略第一個條件。)
使用 volatile 變量的主要原因是其簡易性:在某些情形下,使用 volatile 變量要比使用相應的鎖簡單得多。使用 volatile 變量次要原因是其性能:某些情況下,volatile 變量同步機制的性能要優於鎖。
很難做出準確、全面的評價,例如 “X 總是比 Y 快”,尤其是對 JVM 內在的操作而言。(例如,某些情況下 VM 也許能夠完全刪除鎖機制,這使得我們難以抽象地比較 volatile 和 synchronized 的開銷。)就是說,在目前大多數的處理器架構上,volatile 讀操作開銷非常低 —— 幾乎和非 volatile 讀操作一樣。而 volatile 寫操作的開銷要比非 volatile 寫操作多很多,因爲要保證可見性需要實現內存界定(Memory Fence),即便如此,volatile 的總開銷仍然要比鎖獲取低。
volatile 操作不會像鎖一樣造成阻塞,因此,在能夠安全使用 volatile 的情況下,volatile 可以提供一些優於鎖的可伸縮特性。如果讀操作的次數要遠遠超過寫操作,與鎖相比,volatile 變量通常能夠減少同步的性能開銷。
很多併發性專家事實上往往引導用戶遠離 volatile 變量,因爲使用它們要比使用鎖更加容易出錯。然而,如果謹慎地遵循一些良好定義的模式,就能夠在很多場合內安全地使用 volatile 變量。要始終牢記使用 volatile 的限制 —— 只有在狀態真正獨立於程序內其他內容時才能使用 volatile —— 這條規則能夠避免將這些模式擴展到不安全的用例。

187、Sesson怎麼創建和銷燬。

當客戶端發出第一個請求時(不管是被訪問網站的任何頁面)就會在此站點的服務其中開闢一塊內存空間,這塊內存就是session,session的銷燬有兩種方式,一種是session過期時間已到,會自動銷燬(注意這裏不是馬上就會銷燬,具體銷燬時間由Tomcat容器所決定)。在我們項目中的web.xml中就可以配置:


30

1
2
3
表示設置session過期時間爲30分鐘。值得注意的就是上面說的即使30分鐘到了session不一定會馬上銷燬,可以通過session監聽器測試得到每次session銷燬的時間都不一樣。如果要想安全的話就用下面第二種方法。在Tomcat的conf文件夾中的web.xml中可以找到Tomcat默認的session過期時間爲30分鐘。如果我們在我們的站點中配置了session過期時間Tomcat容器會以站點配置爲主,如果我們沒有在站點中配置session過期時間,將會以Tomcat下conf文件夾下的web.xml文件中配置的session過期時間爲準。
第二種銷燬方式通過手工方式銷燬,這種銷燬方式會立刻釋放服務器端session的資源,我們手動銷燬可以通過session().invalidate();實現。

188、“static”關鍵字是什麼意思?Java中是否可以覆蓋(override)一個private或者是static的方法?

“static”關鍵字表明一個成員變量或者是成員方法可以在沒有所屬的類的實例變量的情況下被訪問。Java中static方法不能被覆蓋,因爲方法覆蓋是基於運行時動態綁定的,而static方法是編譯時靜態綁定的。static方法跟類的任何實例都不相關,所以概念上不適用。

189、接口和抽象類的區別是什麼?

Java提供和支持創建抽象類和接口。它們的實現有共同點,不同點在於:
1. 接口中所有的方法隱含的都是抽象的。而抽象類則可以同時包含抽象和非抽象的方法。
2. 類可以實現很多個接口,但是隻能繼承一個抽象類
3. 類如果要實現一個接口,它必須要實現接口聲明的所有方法。但是,類可以不實現抽象類聲明的所有方法,當然,在這種情況下,類也必須得聲明成是抽象的。
4. 抽象類可以在不提供接口方法實現的情況下實現接口。
5. Java接口中聲明的變量默認都是final的。抽象類可以包含非final的變量。
6. Java接口中的成員函數默認是public的。抽象類的成員函數可以是private,protected或者是public。
7. 接口是絕對抽象的,不可以被實例化。抽象類也不可以被實例化,但是,如果它包含main方法的話是可以被調用的。

190、在監視器(Monitor)內部,是如何做線程同步的?程序應該做哪種級別的同步?

監視器和鎖在Java虛擬機中是一塊使用的。監視器監視一塊同步代碼塊,確保一次只有一個線程執行同步代碼塊。每一個監視器都和一個對象引用相關聯。線程在獲取鎖之前不允許執行同步代碼。

191、如何確保N個線程可以訪問N個資源同時又不導致死鎖?

使用多線程的時候,一種非常簡單的避免死鎖的方式就是:指定獲取鎖的順序,並強制線程按照指定的順序獲取鎖。因此,如果所有的線程都是以同樣的順序加鎖和釋放鎖,就不會出現死鎖了。

192、Java集合類框架的基本接口有哪些?

Java集合類提供了一套設計良好的支持對一組對象進行操作的接口和類。Java集合類裏面最基本的接口有:
 Collection:代表一組對象,每一個對象都是它的子元素。
 Set:不包含重複元素的Collection。
 List:有順序的collection,並且可以包含重複元素。
 Map:可以把鍵(key)映射到值(value)的對象,鍵不能重複

193、爲什麼集合類沒有實現Cloneable和Serializable接口?

集合類接口指定了一組叫做元素的對象。集合類接口的每一種具體的實現類都可以選擇以它自己的方式對元素進行保存和排序。有的集合類允許重複的鍵,有些不允許。克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的。因此,應該由集合類的具體實現來決定如何被克隆或者是序列化。

193、Iterator和ListIterator的區別是什麼?

下面列出了他們的區別:
Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。
Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以後向。
ListIterator實現了Iterator接口,幷包含其他的功能,比如:增加元素,替換元素,獲取前一個和後一個元素的索引,等等

194、快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什麼?

Iterator的安全失敗是基於對底層集合做拷貝,因此,它不受源集合上修改的影響。java.util包下面的所有的集合類都是快速失敗的,而java.util.concurrent包下面的所有的類都是安全失敗的。快速失敗的迭代器會拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠不會拋出這樣的異常。

195、Java中的HashMap的工作原理是什麼?

Java中的HashMap是以鍵值對(key-value)的形式存儲元素的。HashMap需要一個hash函數,它使用hashCode()和equals()方法來向集合/從集合添加和檢索元素。當調用put()方法的時候,HashMap會計算key的hash值,然後把鍵值對存儲在集合中合適的索引上。如果key已經存在了,value會被更新成新值。HashMap的一些重要的特性是它的容量(capacity),負載因子(load factor)和擴容極限(threshold resizing)。

196、hashCode()和equals()方法的重要性體現在什麼地方?

Java中的HashMap使用hashCode()和equals()方法來確定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。如果沒有正確的實現這兩個方法,兩個不同的鍵可能會有相同的hash值,因此,可能會被集合認爲是相等的。而且,這兩個方法也用來發現重複元素。所以這兩個方法的實現對HashMap的精確性和正確性是至關重要的。當equals()方法返回true時,兩個對象必須返回相同的hashCode值,否則在HashMap中就會出錯。

197、HashMap和Hashtable有什麼區別?

HashMap和Hashtable都實現了Map接口,因此很多特性非常相似。但是,他們有以下不同點:
HashMap允許鍵和值是null,而Hashtable不允許鍵或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
HashMap提供了可供應用迭代的鍵的集合,因此,HashMap是快速失敗的。另一方面,Hashtable提供了對鍵的列舉(Enumeration)。
一般認爲Hashtable是一個遺留的類。

198、數組(Array)和列表(ArrayList)有什麼區別?什麼時候應該使用Array而不是ArrayList?

下面列出了Array和ArrayList的不同點:
Array可以包含基本類型和對象類型,ArrayList只能包含對象類型。
Array大小是固定的,ArrayList的大小是動態變化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
對於基本類型數據,集合使用自動裝箱來減少編碼工作量。但是,當處理固定大小的基本數據類型的時候,這種方式相對比較慢。

199、ArrayList和LinkedList有什麼區別?

ArrayList和LinkedList都實現了List接口,他們有以下的不同點:
ArrayList是基於索引的數據接口,它的底層是數組。它可以以O(1)時間複雜度對元素進行隨機訪問。與此對應,LinkedList是以元素列表的形式存儲它的數據,每一個元素都和它的前一個和後一個元素鏈接在一起,在這種情況下,查找某個元素的時間複雜度是O(n)。
相對於ArrayList,LinkedList的插入,添加,刪除操作速度更快,因爲當元素被添加到集合任意位置的時候,不需要像數組那樣重新計算大小或者是更新索引。
LinkedList比ArrayList更佔內存,因爲LinkedList爲每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。

200、Comparable和Comparator接口是幹什麼的?列出它們的區別。

Java提供了只包含一個compareTo()方法的Comparable接口。這個方法可以個給兩個對象排序。具體來說,它返回負數,0,正數來表明輸入對象小於,等於,大於已經存在的對象。
Java提供了包含compare()和equals()兩個方法的Comparator接口。compare()方法用來給兩個輸入參數排序,返回負數,0,正數表明第一個參數是小於,等於,大於第二個參數。equals()方法需要一個對象作爲參數,它用來決定輸入參數是否和comparator相等。只有當輸入參數也是一個comparator並且輸入參數和當前comparator的排序結果是相同的時候,這個方法才返回true。

201、什麼是Java優先級隊列(Priority Queue)?

PriorityQueue是一個基於優先級堆的無界隊列,它的元素是按照自然順序(natural order)排序的。在創建的時候,我們可以給它提供一個負責給元素排序的比較器。PriorityQueue不允許null值,因爲他們沒有自然順序,或者說他們沒有任何的相關聯的比較器。最後,PriorityQueue不是線程安全的,入隊和出隊的時間複雜度是O(log(n))。
.

202、Java集合類框架的最佳實踐有哪些?

根據應用的需要正確選擇要使用的集合的類型對性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我們就應該用Array而不是ArrayList。
有些集合類允許指定初始容量。因此,如果我們能估計出存儲的元素的數目,我們可以設置初始容量來避免重新計算hash值或者是擴容。
爲了類型安全,可讀性和健壯性的原因總是要使用泛型。同時,使用泛型還可以避免運行時的ClassCastException。
使用JDK提供的不變類(immutable class)作爲Map的鍵可以避免爲我們自己的類實現hashCode()和equals()方法。
編程的時候接口優於實現。
底層的集合實際上是空的情況下,返回長度是0的集合或者是數組,不要返回null。

203、Enumeration接口和Iterator接口的區別有哪些?

Enumeration速度是Iterator的2倍,同時佔用更少的內存。但是,Iterator遠遠比Enumeration安全,因爲其他線程不能夠修改正在被iterator遍歷的集合裏面的對象。同時,Iterator允許調用者刪除底層集合裏面的元素,這對Enumeration來說是不可能的。

204、HashSet和TreeSet有什麼區別?

HashSet是通過HashMap實現的,TreeSet是通過TreeMap實現的,只不過Set用的只是Map的key
Map的key和Set都有一個共同的特性就是集合的唯一性.TreeMap更是多了一個排序的功能.
hashCode和equal()是HashMap用的, 因爲無需排序所以只需要關注定位和唯一性即可.
a. hashCode是用來計算hash值的,hash值是用來確定hash表索引的.
b. hash表中的一個索引處存放的是一張鏈表, 所以還要通過equal方法循環比較鏈上的每一個對象
纔可以真正定位到鍵值對應的Entry.
c. put時,如果hash表中沒定位到,就在鏈表前加一個Entry,如果定位到了,則更換Entry中的value,並返回舊value
由於TreeMap需要排序,所以需要一個Comparator爲鍵值進行大小比較.當然也是用Comparator定位的.
a. Comparator可以在創建TreeMap時指定
b. 如果創建時沒有確定,那麼就會使用key.compareTo()方法,這就要求key必須實現Comparable接口.
c. TreeMap是使用Tree數據結構實現的,所以使用compare接口就可以完成定位了.
HashSet是由一個hash表來實現的,因此,它的元素是無序的。add(),remove(),contains()方法的時間複雜度是O(1)。
另一方面,TreeSet是由一個樹形的結構來實現的,它裏面的元素是有序的。因此,add(),remove(),contains()方法的時間複雜度是O(logn)。
205、stop() 和 suspend() 方 法爲何不推薦使用?

反對使用stop(),是因爲它不安全。它會解除由線程獲取的所有鎖定,而且如果對象處於一種不連貫狀態,那麼其他線程能在那種狀態下檢查和修改它們。結果 很難檢查出真正的問題所在。suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲得的鎖定。此 時,其他任何線程都不能訪問鎖定的資源,除非被”掛起”的線程恢復運行。對任何線程來說,如果它們想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就 會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,指出線程應該活動還是掛起。若標誌指出線程應該掛起,便用wait()命其進入等待狀態。若標誌指出線程應當恢復,則用一個notify()重新啓動線程。

206、當一個線程進入一個對象的一個 synchronized 方法後,其它線程是否可進入此對象的其它方法 ?

答:情況一:當一個線程進入一個對象的一個synchronized方法後,其它線程可以訪問該對象的非同步方法。
情況二:當一個線程進入一個對象的一個synchronized方法後,其它線程不能訪問該同步方法。
情況三:當一個線程進入一個對象的一個synchronized方法後,其它線程不能同時訪問該對象的另一個同步方法。

207、接口:Collection

所有集合類的根類型,主要的一個接口方法:boolean add(Ojbect c)
雖返回的是boolean,但不是表示添加成功與否,因爲Collection規定:一個集合拒絕添加這個元素,無論什麼原因,都必須拋出異常,這個返回值表示的意義是add()執行後,集合的內容是否改了(就是元素有無數量、位置等變化)。類似的addAll,remove,removeAll,remainAll也是一樣的。
用Iterator模式實現遍歷集合
Collection有一個重要的方法:iterator(),返回一個Iterator(迭代子),用於遍歷集合的所有元素。Iterator模式可以把訪問邏輯從不同類的集合類中抽象出來,從而避免向客戶端暴露集合的內部結構。
for(Iterator it = c.iterator(); it.hasNext();) {…}
不需要維護遍歷集合的“指針”,所有的內部狀態都有Iterator來維護,而這個Iterator由集合類通過工廠方法生成。
每一種集合類返回的Iterator具體類型可能不同,但它們都實現了Iterator接口,因此,我們不需要關心到底是哪種Iterator,它只需要獲得這個Iterator接口即可,這就是接口的好處,面向對象的威力。
要確保遍歷過程順利完成,必須保證遍歷過程中不更改集合的內容(Iterator的remove()方法除外),所以,確保遍歷可靠的原則是:只在一個線程中使用這個集合,或者在多線程中對遍歷代碼進行同步。

208、JDBC:如何控制事務?

A: conn.setAutoCommit(false);
當值爲false時,表示禁止自動提交。 在默認情況下,JDBC驅動程序會在每一個更新操作語句之後自動添加commit語句,如果調用了setAutoCommit(false),則驅動程序不再添加commit語句了。
B: conn.commit();
提交事務。即驅動程序會向數據庫發送一個commit語句。
C: conn.rollback();
回滾事務。即驅動程序會向數據庫發送一個rollback語句。

209、ArrayList總結:

ArrayList是基於數組實現的,是一個動態數組,其容量能自動增長,類似於C語言中的動態申請內存,動態增長內存。

ArrayList不是線程安全的,只能用在單線程環境下,多線程環境下可以考慮用Collections.synchronizedList(List l)函數返回一個線程安全的ArrayList類,也可以使用concurrent併發包下的CopyOnWriteArrayList類。

ArrayList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實現了RandomAccess接口,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問,實現了Cloneable接口,能被克隆。

無參構造方法構造的ArrayList的容量默認爲10,帶有Collection參數的構造方法,將Collection轉化爲數組賦給ArrayList的實現數組elementData。

ArrayList在每次增加元素(可能是1個,也可能是一組)時,都要調用該方法來確保足夠的容量。當容量不足以容納當前的元素個數時,就設置新的容量爲舊的容量的1.5倍加1,如果設置後的新容量還不夠,則直接新容量設置爲傳入的參數(也就是所需的容量),而後用Arrays.copyof()方法將元素拷貝到新的數組。

ArrayList基於數組實現,可以通過下標索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低。

在查找給定元素索引值等的方法中,源碼都將該元素的值分爲null和不爲null兩種情況處理,ArrayList中允許元素爲null。

210、CopyOnWriteArrayList

和ArrayList繼承於AbstractList不同,CopyOnWriteArrayList沒有繼承於AbstractList,它僅僅只是實現了List接口。

ArrayList的iterator()函數返回的Iterator是在AbstractList中實現的;而CopyOnWriteArrayList是自己實現Iterator。

ArrayList的Iterator實現類中調用next()時,會“調用checkForComodification()比較‘expectedModCount’和‘modCount’的大小”;但是,CopyOnWriteArrayList的Iterator實現類中,沒有所謂的checkForComodification(),更不會拋出ConcurrentModificationException異常!

CopyOnWriteArrayList不會拋ConcurrentModificationException,是因爲所有改變其內容的操作(add、remove、clear等),都會copy一份現有數據,在現有數據上修改好,在把原有數據的引用改成指向修改後的數據。而不是在讀的時候copy。

211、LinkedList總結

LinkedList是基於雙向循環鏈表實現的,且頭結點中不存放數據。除了可以當做鏈表來操作外,它還可以當做棧、隊列和雙端隊列來使用。

LinkedList同樣是非線程安全的,只在單線程下適合使用。

LinkedList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實現了Cloneable接口,能被克隆。

無參構造方法直接建立一個僅包含head節點的空鏈表,包含Collection的構造方法,先調用無參構造方法建立一個空鏈表,而後將Collection中的數據加入到鏈表的尾部後面。

在查找和刪除某元素時,源碼中都劃分爲該元素爲null和不爲null兩種情況來處理,LinkedList中允許元素爲null。

LinkedList是基於鏈表實現的,因此不存在容量不足的問題,所以這裏沒有擴容的方法。

找指定位置的元素時,先將index與長度size的一半比較,如果index

212、Vector總結

Vector也是基於數組實現的,是一個動態數組,其容量能自動增長。

Vector是JDK1.0引入了,它的很多實現方法都加入了同步語句,因此是線程安全的(其實也只是相對安全,有些時候還是要加入同步語句來保證線程的安全),可以用於多線程環境。

Vector沒有實現Serializable接口,因此它不支持序列化,實現了Cloneable接口,能被克隆,實現了RandomAccess接口,支持快速隨機訪問。

Vector有四個不同的構造方法。無參構造方法的容量爲默認值10,僅包含容量的構造方法則將容量增長量明置爲0。

Vector在每次增加元素(可能是1個,也可能是一組)時,都要調用該方法來確保足夠的容量。當容量不足以容納當前的元素個數時,就先看構造方法中傳入的容量增長量參數CapacityIncrement是否爲0,如果不爲0,就設置新的容量爲就容量加上容量增長量,如果爲0,就設置新的容量爲舊的容量的2倍,如果設置後的新容量還不夠,則直接新容量設置爲傳入的參數(也就是所需的容量),而後同樣用Arrays.copyof()方法將元素拷貝到新的數組。

很多方法都加入了synchronized同步語句,來保證線程安全。

同樣在查找給定元素索引值等的方法中,源碼都將該元素的值分爲null和不爲null兩種情況處理,Vector中也允許元素爲null。

其他很多地方都與ArrayList實現大同小異,Vector現在已經基本不再使用。
HashMap總結

HashMap是基於哈希表實現的,每一個元素是一個key-value對,其內部通過單鏈表解決衝突問題,容量不足(超過了閥值)時,同樣會自動增長。

HashMap是非線程安全的,只是用於單線程環境下,多線程環境下可以採用concurrent併發包下的concurrentHashMap。

HashMap 實現了Serializable接口,因此它支持序列化,實現了Cloneable接口,能被克隆。

HashMap共有四個構造方法。構造方法中提到了兩個很重要的參數:初始容量和加載因子。這兩個參數是影響HashMap性能的重要參數,其中容量表示哈希表中槽的數量(即哈希數組的長度),初始容量是創建哈希表時的容量(如果不指明,則默認爲16),加載因子是哈希表在其容量自動增加之前可以達到多滿的一種尺度,當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 resize 操作(即擴容)。如果加載因子越大,對空間的利用更充分,但是查找效率會降低(鏈表長度會越來越長);如果加載因子太小,那麼表中的數據將過於稀疏(很多空間還沒用,就開始擴容了),對空間造成嚴重浪費。如果我們在構造方法中不指定,則系統默認加載因子爲0.75,這是一個比較理想的值,一般情況下我們是無需修改的。另外,無論指定的容量爲多少,構造方法都會將實際容量設爲不小於指定容量的2的次方的一個數,且最大值不能超過2的30次方。每次擴容也是擴大兩倍,也就是容量總是2的冪次方。

HashMap中key和value都允許爲null。

key爲null的鍵值對永遠都放在以table[0]爲頭結點的鏈表中,當然不一定是存放在頭結點table[0]中。如果key不爲null,則先求的key的hash值,根據hash值找到在table中的索引,在該索引對應的單鏈表中查找是否有鍵值對的key與目標key相等,有就返回對應的value,沒有則返回null。

213、Hashtable總結

Hashtable同樣是基於哈希表實現的,同樣每個元素是一個key-value對,其內部也是通過單鏈表解決衝突問題,容量不足(超過了閥值)時,同樣會自動增長。

Hashtable也是JDK1.0引入的類,是線程安全的,能用於多線程環境中。

Hashtable同樣實現了Serializable接口,它支持序列化,實現了Cloneable接口,能被克隆。

HashTable在不指定容量的情況下的默認容量爲11,而HashMap爲16,Hashtable不要求底層數組的容量一定要爲2的整數次冪,而HashMap則要求一定爲2的整數次冪。

Hashtable中key和value都不允許爲null,而HashMap中key和value都允許爲null(key只能有一個爲null,而value則可以有多個爲null)。但是如果在Hashtable中有類似put(null,null)的操作,編譯同樣可以通過,因爲key和value都是Object類型,但運行時會拋出NullPointerException異常,這是JDK的規範規定的。

Hashtable擴容時,將容量變爲原來的2倍加1,而HashMap擴容時,將容量變爲原來的2倍。

Hashtable計算hash值,直接用key的hashCode(),而HashMap重新計算了key的hash值,Hashtable在求hash值對應的位置索引時,用取模運算,而HashMap在求位置索引時,則用與運算,且這裏一般先用hash&0x7FFFFFFF後,再對length取模,&0x7FFFFFFF的目的是爲了將負的hash值轉化爲正值,因爲hash值有可能爲負數,而&0x7FFFFFFF後,只有符號外改變,而後面的位都不變。

214、TreeMap總結

TreeMap是基於紅黑樹實現的。

TreeMap是根據key進行排序的,它的排序和定位需要依賴比較器或覆寫Comparable接口,也因此不需要key覆寫hashCode方法和equals方法,就可以排除掉重複的key,而HashMap的key則需要通過覆寫hashCode方法和equals方法來確保沒有重複的key。

TreeMap的查詢、插入、刪除效率均沒有HashMap高,一般只有要對key排序時才使用TreeMap。

TreeMap的key不能爲null,而HashMap的key可以爲null。

215、LinkedHashMap總結

LinkedHashMap是HashMap的子類,與HashMap有着同樣的存儲結構,但它加入了一個雙向鏈表的頭結點,將所有put到LinkedHashmap的節點一一串成了一個雙向循環鏈表,因此它保留了節點插入的順序,可以使節點的輸出順序與輸入順序相同。

LinkedHashMap可以用來實現LRU算法。

LinkedHashMap同樣是非線程安全的,只在單線程環境下使用。

LinkedHashMap中加入了一個head頭結點,將所有插入到該LinkedHashMap中的Entry按照插入的先後順序依次加入到以head爲頭結點的雙向循環鏈表的尾部。

LinkedHashMap由於繼承自HashMap,因此它具有HashMap的所有特性,同樣允許key和value爲null。

源碼中的accessOrder標誌位,當它false時,表示雙向鏈表中的元素按照Entry插入LinkedHashMap到中的先後順序排序,即每次put到LinkedHashMap中的Entry都放在雙向鏈表的尾部,這樣遍歷雙向鏈表時,Entry的輸出順序便和插入的順序一致,這也是默認的雙向鏈表的存儲順序;當它爲true時,表示雙向鏈表中的元素按照訪問的先後順序排列,可以看到,雖然Entry插入鏈表的順序依然是按照其put到LinkedHashMap中的順序,但put和get方法均有調用recordAccess方法(put方法在key相同,覆蓋原有的Entry的情況下調用recordAccess方法),該方法判斷accessOrder是否爲true,如果是,則將當前訪問的Entry(put進來的Entry或get出來的Entry)移到雙向鏈表的尾部(key不相同時,put新Entry時,會調用addEntry,它會調用creatEntry,該方法同樣將新插入的元素放入到雙向鏈表的尾部,既符合插入的先後順序,又符合訪問的先後順序,因爲這時該Entry也被訪問了),否則,什麼也不做。

構造方法,前四個構造方法都將accessOrder設爲false,說明默認是按照插入順序排序的,而第五個構造方法可以自定義傳入的accessOrder的值,因此可以指定雙向循環鏈表中元素的排序規則,一般要用LinkedHashMap實現LRU算法,就要用該構造方法,將accessOrder置爲true。

LinkedHashMap是如何實現LRU的。首先,當accessOrder爲true時,纔會開啓按訪問順序排序的模式,才能用來實現LRU算法。我們可以看到,無論是put方法還是get方法,都會導致目標Entry成爲最近訪問的Entry,因此便把該Entry加入到了雙向鏈表的末尾(get方法通過調用recordAccess方法來實現,put方法在覆蓋已有key的情況下,也是通過調用recordAccess方法來實現,在插入新的Entry時,則是通過createEntry中的addBefore方法來實現),這樣便把最近使用了的Entry放入到了雙向鏈表的後面,多次操作後,雙向鏈表前面的Entry便是最近沒有使用的,這樣當節點個數滿的時候,刪除的最前面的Entry(head後面的那個Entry)便是最近最少使用的Entry。

216、ThreadLocal原理

首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。

另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,並不是通過ThreadLocal.set()來實現的,而是通過每個線程中的new 對象 的操作來創建的對象,每個線程創建一個,不是什麼對象的拷貝或副本。通過ThreadLocal.set()將這個新創建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作爲map的key來使用的。

如果ThreadLocal.set()進去的東西本來就是多個線程共享的同一個對象,那麼多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有併發訪問問題。
ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。歸納了兩點:

每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。

將一個共用的ThreadLocal靜態實例作爲key,將不同對象的引用保存到不同線程的ThreadLocalMap中,然後在線程執行的各處通過這個靜態ThreadLocal實例的get()方法取得自己線程保存的那個對象,避免了將這個對象作爲參數傳遞的麻煩。

如果要把本來線程共享的對象通過ThreadLocal.set()放到線程中也可以,可以實現避免參數傳遞的訪問方式,但是要注意get()到的是那同一個共享對象,併發訪問問題要靠其他手段來解決。但一般來說線程共享的對象通過設置爲某類的靜態變量就可以實現方便的訪問了,似乎沒必要放到線程中。
ThreadLocal的應用場合,我覺得最適合的是按線程多實例(每個線程對應一個實例)的對象的訪問,並且這個對象很多地方都要用到。
ThreadLocal類中的變量只有這3個int型:

private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode = 0;
private static final int HASH_INCREMENT = 0x61c88647;

而作爲ThreadLocal實例的變量只有threadLocalHashCode 這一個,nextHashCode 和HASH_INCREMENT 是ThreadLocal類的靜態變量,實際上HASH_INCREMENT是一個常量,表示了連續分配的兩個ThreadLocal實例的threadLocalHashCode值的增量,而nextHashCode 的表示了即將分配的下一個ThreadLocal實例的threadLocalHashCode 的值。
創建一個ThreadLocal實例即new ThreadLocal()時做了哪些操作,從構造函數ThreadLocal()裏看什麼操作都沒有,唯一的操作是這句:

private final int threadLocalHashCode = nextHashCode();
nextHashCode()做了什麼呢:
private static synchronized int nextHashCode() {
    int h = nextHashCode;
    nextHashCode = h + HASH_INCREMENT;
    return h;
}

就是將ThreadLocal類的下一個hashCode值即nextHashCode的值賦給實例的threadLocalHashCode,然後nextHashCode的值增加HASH_INCREMENT這個值。
因此ThreadLocal實例的變量只有這個threadLocalHashCode,而且是final的,用來區分不同的ThreadLocal實例,ThreadLocal類主要是作爲工具類來使用,那麼ThreadLocal.set()進去的對象是放在哪兒的呢?
看一下上面的set()方法,兩句合併一下成爲

ThreadLocalMap map = Thread.currentThread().threadLocals;

這個ThreadLocalMap 類是ThreadLocal中定義的內部類,但是它的實例卻用在Thread類中:

public class Thread implements Runnable {
    ......
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;  
    ......
}

再看這句。

if (map != null)
map.set(this, value);

也就是將該ThreadLocal實例作爲key,要保持的對象作爲值,設置到當前線程的ThreadLocalMap 中,get()方法同樣類似。

217、Static Nested Class 和 Inner Class的不同?

1 創建一個static內部類的對象,不需要一個外部類對象。創建一個非static內部類必須要一個外部類對象
2 不能從一個static內部類的一個對象訪問一個外部類對象,可以從一個非static內部類訪問其外部類對象。如OutterClassName.this進行訪問。

218、當你在瀏覽器輸入一個網址,比如http://www.taobao.com,按回車之後發生了什麼?請從技術的角度描述,如瀏覽器、網絡(UDP,TCP,HTTP等),以及服務器等各種參數與對象上由此引發的一系列活動。請儘可能的涉及到所有的關鍵技術點。

答案:答題角度有以下幾個DNS域名查找,負載均衡,HTTP協議格式,HTTP下一層的TCP協議,服務器應該返回的各種網絡的應答。

1、通過訪問的域名找出其IP地址。DNS查找過程如下:
瀏覽器緩存:瀏覽器會緩存DNS記錄,並存放一段時間。
系統緩存:如果瀏覽器緩存中沒有找到需要的記錄,瀏覽器會做一個系統調用。
路由器緩存:接着前面的查詢請求發向路由器,它一般會有自己的DNS緩存。
ISP DNS緩存:接下來要check的就是ISP緩存的DNS服務器,在這一般都能找到相應的緩存記錄
遞歸搜索:你的ISP的DNS服務器從根域名服務器開始進行遞歸搜索,從.com頂級域名服務器到葉節點服務器。

2、上述這種域名解析的方式似乎只能解析出一個ip,我們知道一般的大型網站都有好幾個ip地址,這種問題如何解決?還好,有幾種方法可以消除這個瓶頸:
輪叫調度(round-robin DNS):是DNS查找時返回多個IP時的解決方案。舉例來說,Facebook.com實際上就對應了四個IP地址。
負載均衡:是以一個特定IP地址進行偵聽並將網絡請求轉發到集羣服務器上的硬件設備。一些大型的站點一般都會使用這種昂貴的高性能負載均衡
地理DNS:根據用戶所處的地理位置,通過把域名映射到多個不同的IP地址提高可擴展性。這樣不同的服務器不能夠更新同步狀態,但映射靜態內容的話非常好。
Anycast:是一個IP地址映射多個物理主機的路由技術。美中不足,Anycast與TCP協議適應的不是很好,所以很少應用在那些方案中。大多數DNS服務器使用Anycast來獲得高效低延遲的DNS查找。

3、瀏覽器給Web服務器發送一個Http請求
這裏可以回答的更詳細一點:比如說http協議的格式,http請求的頭,比如說發起一個Get類型請求。

4、服務器給瀏覽器響應一個301永久重定向響應
爲什麼要有這一步,原因之一是爲了便於搜索引擎優化

5、瀏覽器跟中重定向地址
這個時候瀏覽器已經知道了正確的URL地址,於是重新發送一個Get請求,請求頭中的URL爲重定向之後的。

6、服務器處理請求
Web服務器軟件:web服務器軟件(IIS和Apatch)介紹到Http請求,然後確定執行什麼請求處理來處理它。請求處理就是一個能夠讀懂請求並且能生成HTML來進行相應的程序
請求處理:請求處理閱讀請求及它的參數和cookies。它會讀取也可能更新一些數據,並將數據存儲在服務器上。然後,需求處理會生成一個HTML響應。

7、服務器發回一個HTML響應

8、瀏覽器開始渲染這個HTML

9、瀏覽器發送獲取嵌入在HTML中的資源對象請求
一些嵌入在html中的資源,比如說圖片,js,css等等

10、瀏覽器發送異步(AJAX)請求
在web2.0時代,在頁面渲染成功之後,瀏覽器依然可以跟服務器進行交互,方法就是通過這個異步請求AJAX
下面這個答案說的儘管跟這道題目關係不大,但是很有意思。所以還是給出來:
你打開了www.taobao.com,這時你的瀏覽器首先查詢DNS服務器,將www.taobao.com轉換成ip地址。不過首先你會發現,你在不同的地區或者不同的網絡(電信,聯通,移動)的情況下,轉換後的ip地址很可能是不一樣的,這首先涉及到負載均衡的第一步,通過DNS解析域名時將你的訪問分配到不同的入口,同時儘可能保證你所訪問的入口是所有入口中可能較快的一個。
你通過這個入口成功的訪問了www.taobao.com的實際的入口ip地址。這時你產生了一個PV,即Page View,頁面訪問。每日每個網站的總PV量是形容一個網站規模的重要指標。淘寶網全網在平日(非促銷期間)的PV大概是16-25億之間。同時作爲一個獨立的用戶,你這次訪問淘寶網的所有頁面,均算作一個UV(Unique Visitor用戶訪問)。
因爲同一時刻訪問www.taobao.com的人數過於巨大,所以即便是生成淘寶首頁頁面的服務器,也不可能僅有一臺。僅用於生成www.taobao.com首頁的服務器就有可能有成百上千臺,那麼你的一次訪問時生成頁面給你看的任務便會被分配給其中一臺服務器完成。這個過程要保證公平公正(也就是說這成百上千臺服務器每臺負擔的用戶數要差不多),這一很複雜的過程是由幾個系統配合完成,其中最關鍵的便是LVS,Linux Virtual Server,世界上最流行的負載均衡系統之一。
經過一系列複雜的邏輯運算和數據處理,用於這次給你看的淘寶網的首頁的HTML內容便生成成功了,對Web前端稍微有點常識的童鞋都應該知道,下一步瀏覽器會去加載頁面中用到的css,js,圖片等樣式,腳本和資源文件。但是可能相對較少的同學纔會知道,你的瀏覽器在同一個域名下併發加載的資源數量是有限制的,例如ie6-7是兩個,ie8是6個,chrome個版本不大一樣,一般是4-6個。我剛剛看了一下,我訪問的淘寶網首頁需要加載126個資源,那麼如此小的併發連接數自然會加載很久。所以前端開發人員往往會將上述的這些資源文件分佈在好多個域名下,變相的繞過瀏覽器的這個限制,同時也爲下文的CDN工作做準備。
據不可靠消息,在雙十一當天高峯,淘寶的訪問流量達到最巔峯達到871GB/S。這個數字意味着需要178萬個4mb帶寬的家庭帶寬才能負擔的起,也完全有能力拖垮一箇中小城市的全部互聯網帶寬。那麼顯然,這些訪問流量不可能集中在一起。並且大家都知道,不同地區不同網絡(電信,聯通等)之間互訪會非常緩慢,但是你卻發現很少發現淘寶網訪問緩慢。這便是CDN,Content Delivery Network,即內容分發網絡的作用。淘寶在全國各地建立了上百個CDN節點,利用一些手段保證你的訪問的(這裏主要指js、css、圖片等)地方是離你最近的CDN節點,這樣便保證了大流量分散已經在各地訪問的加速。
這便出現了一個問題,那就是假如一個賣家發佈了一個新寶貝,上傳了幾張新的寶貝圖片,那麼淘寶網如何保證全國各地的CDN節點中都會同步的存在這幾張圖片供用戶使用呢?這裏邊就涉及到大量的內容分發與同步的相關技術。淘寶開發了分佈式文件系統TFS來處理這類問題。

219、進程間通信的方法主要有以下幾種:

管道(Pipe):管道可用於具有親緣關係進程間的通信,允許一個進程和另一個與它有共同祖先的進程之間進行通信。

命名管道(named pipe):命名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關係進程間的通信。命名管道在文件系統中有對應的文件名。命名管道通過命令mkfifo或系統調用mkfifo來創建。

信號(Signal):信號是比較複雜的通信方式,用於通知接受進程有某種事件發生,除了用於進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數)。

消息(Message)隊列:消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺

共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。

內存映射(mapped memory):內存映射允許任何多個進程間通信,每一個使用該機制的進程通過把一個共享的文件映射到自己的進程地址空間來實現它。

信號量(semaphore):主要作爲進程間以及同一進程不同線程之間的同步手段。

套接字(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它

類Unix系統上:Linux和System V的變種都支持套接字。而在java中我們實現多線程間通信則主要採用”共享變量”和”管道流”這兩種方法

方法一 通過訪問共享變量的方式(注:需要處理同步問題)
方法二 通過管道流
其中方法一有兩種實現方法,即
- 通過內部類實現線程的共享變量
- 通過實現Runnable接口實現線程的共享變量

210、什麼是trie樹(單詞查找樹、字典樹)?

1.Trie樹 (特例結構樹)

Trie樹,又稱單詞查找樹、字典樹,是一種樹形結構,是一種哈希樹的變種,是一種用於快速檢索的多叉樹結構。典型應用是用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。
Trie的核心思想是空間換時間。利用字符串的公共前綴來降低查詢時間的開銷以達到提高效率的目的。
Trie樹也有它的缺點,Trie樹的內存消耗非常大.當然,或許用左兒子右兄弟的方法建樹的話,可能會好點.
2. 三個基本特性:  

1)根節點不包含字符,除根節點外每一個節點都只包含一個字符。  

2)從根節點到某一節點,路徑上經過的字符連接起來,爲該節點對應的字符串。 

3)每個節點的所有子節點包含的字符都不相同。

3 .說明:和二叉查找樹不同,在trie樹中,每個結點上並非存儲一個元素。trie樹把要查找的關鍵詞看作一個字符序列。並根據構成關鍵詞字符的先後順序構造用於檢索的樹結構。在trie樹上進行檢索類似於查閱英語詞典。 一棵m度的trie樹或者爲空,或者由m棵m度的trie樹構成。

查找分析
在trie樹中查找一個關鍵字的時間和樹中包含的結點數無關,而取決於組成關鍵字的字符數。而二叉查找樹的查找時間和樹中的結點數有關O(log2n)。
如果要查找的關鍵字可以分解成字符序列且不是很長,利用trie樹查找速度優於
二叉查找樹。如:
若關鍵字長度最大是5,則利用trie樹,利用5次比較可以從26^5=11881376個可能的關鍵字中檢索出指定的關鍵字。而利用二叉查找樹至少要進行 次比較。

trie樹的應用:

字符串檢索,詞頻統計,搜索引擎的熱門查詢。
字符串最長公共前綴。
排序。
作爲其他數據結構和算法的輔助結構。如後綴樹,AC自動機等。
221、在25匹馬中,挑出速度最快的3匹。每場比賽只能有5馬一起跑。所需要的最少比賽次數是多少

至少7次
先分爲5組,每組5匹。
首先全部5組分別比賽;
其次,每組的第一名進行第六次比賽:這樣可淘汰最後兩名及其所在組的全部馬匹(共10匹),同時可淘汰第三名所在組排名靠後的其餘4匹馬,以及第二名所在組的排名最後的3匹,再加上第一名所在小組的最後2名,共計淘汰19名,同時產生25匹中的冠軍(即本輪的第一名)。
最後,剩餘的5匹進行第7次比賽,前兩名爲25匹中的亞軍和季軍。

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