prepare for interview

轉(zhuǎn)自

9.解釋內(nèi)存中的棧空民,堆狡耻,方法區(qū)用法

答:通常我們定義一個(gè)基本數(shù)據(jù)類型的變量仔蝌,一個(gè)對(duì)象的引用,還有就是函數(shù)調(diào)用的現(xiàn)場(chǎng)保存都使用JVM中的椝伎郑空間沾谜;而通過(guò)new關(guān)鍵字和構(gòu)造器創(chuàng)建的對(duì)象則放在堆空間,堆是垃圾收集器管理的主要區(qū)域胀莹,由于現(xiàn)在的垃圾收集器都采用分代收集算法基跑,所以堆空間還可以細(xì)分為新生代和老生代,再具體一點(diǎn)可以分為Eden描焰、Survivor(又可分為From Survivor和To Survivor)媳否、Tenured;方法區(qū)和堆都是各個(gè)線程共享的內(nèi)存區(qū)域荆秦,用于存儲(chǔ)已經(jīng)被JVM加載的類信息篱竭、常量、靜態(tài)變量步绸、JIT編譯器編譯后的代碼等數(shù)據(jù)掺逼;程序中的字面量(literal)如直接書寫的100、"hello"和常量都是放在常量池中瓤介,常量池是方法區(qū)的一部分吕喘,。椥躺#空間操作起來(lái)最快但是棧很小氯质,通常大量的對(duì)象都是放在堆空間,棧和堆的大小都可以通過(guò)JVM的啟動(dòng)參數(shù)來(lái)進(jìn)行調(diào)整祠斧,椢挪欤空間用光了會(huì)引發(fā)StackOverflowError,而堆和常量池空間不足則會(huì)引發(fā)OutOfMemoryError琢锋。
String str = new String("hello");
上面的語(yǔ)句中變量str放在棧上辕漂,用new創(chuàng)建出來(lái)的字符串對(duì)象放在堆上,而"hello"這個(gè)字面量是放在方法區(qū)的吩蔑。
補(bǔ)充1:較新版本的Java(從Java 6的某個(gè)更新開(kāi)始)中钮热,由于JIT編譯器的發(fā)展和"逃逸分析"技術(shù)的逐漸成熟,棧上分配烛芬、標(biāo)量替換等優(yōu)化技術(shù)使得對(duì)象一定分配在堆上這件事情已經(jīng)變得不那么絕對(duì)了隧期。
補(bǔ)充2:運(yùn)行時(shí)常量池相當(dāng)于Class文件常量池具有動(dòng)態(tài)性,Java語(yǔ)言并不要求常量一定只有編譯期間才能產(chǎn)生赘娄,運(yùn)行期間也可以將新的常量放入池中仆潮,String類的intern()方法就是這樣的。

21遣臼、描述一下JVM加載class文件的原理機(jī)制性置?

答:10.類加載機(jī)制

22、char 型變量中能不能存貯一個(gè)中文漢字揍堰,為什么鹏浅?

答:可以嗅义,因?yàn)閖ava統(tǒng)一使用UNICODE

26、抽象的(abstract)方法是否可同時(shí)是靜態(tài)的(static),是否可同時(shí)是本地方法(native)隐砸,是否可同時(shí)被synchronized修飾之碗?

答:都不能。抽象方法需要子類重寫季希,而靜態(tài)的方法是無(wú)法被重寫的褪那,因此二者是矛盾的。本地方法是由本地代碼(如C代碼)實(shí)現(xiàn)的方法式塌,而抽象方法是沒(méi)有實(shí)現(xiàn)的博敬,也是矛盾的。synchronized和方法的實(shí)現(xiàn)細(xì)節(jié)有關(guān)峰尝,抽象方法不涉及實(shí)現(xiàn)細(xì)節(jié)偏窝,因此也是相互矛盾的

29、如何實(shí)現(xiàn)對(duì)象克挛溲А囚枪?

答:有兩種方式:
(1). 實(shí)現(xiàn)Cloneable接口并重寫Object類中的clone()方法;
(2). 實(shí)現(xiàn)Serializable接口劳淆,通過(guò)對(duì)象的序列化和反序列化實(shí)現(xiàn)克隆链沼,可以實(shí)現(xiàn)真正的深度克隆,代碼如下沛鸵。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MyUtil {

    private MyUtil() {
        throw new AssertionError();
    }

    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();

        // 說(shuō)明:調(diào)用ByteArrayInputStream或ByteArrayOutputStream對(duì)象的close方法沒(méi)有任何意義
        // 這兩個(gè)基于內(nèi)存的流只要垃圾回收器清理對(duì)象就能夠釋放資源括勺,這一點(diǎn)不同于對(duì)外部資源(如文件流)的釋放
    }
}

下面是測(cè)試代碼:

import java.io.Serializable;

class Person implements Serializable {
    private static final long serialVersionUID = -9102017020286042305L;

    private String name;    // 姓名
    private int age;        // 年齡
    private Car car;        // 座駕

    public Person(String name, int age, Car car) {
        this.name = name;
        this.age = age;
        this.car = car;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }

}

class Car implements Serializable {
    private static final long serialVersionUID = -5713945027627603702L;

    private String brand;       // 品牌
    private int maxSpeed;       // 最高時(shí)速

    public Car(String brand, int maxSpeed) {
        this.brand = brand;
        this.maxSpeed = maxSpeed;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    @Override
    public String toString() {
        return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
    }

}

class CloneTest {

    public static void main(String[] args) {
        try {
            Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
            Person p2 = MyUtil.clone(p1);   // 深度克隆
            p2.getCar().setBrand("BYD");
            // 修改克隆的Person對(duì)象p2關(guān)聯(lián)的汽車對(duì)象的品牌屬性
            // 原來(lái)的Person對(duì)象p1關(guān)聯(lián)的汽車不會(huì)受到任何影響
            // 因?yàn)樵诳寺erson對(duì)象時(shí)其關(guān)聯(lián)的汽車對(duì)象也被克隆了
            System.out.println(p1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意:基于序列化和反序列化實(shí)現(xiàn)的克隆不僅僅是深度克隆,更重要的是通過(guò)泛型限定曲掰,可以檢查出要克隆的對(duì)象是否支持序列化疾捍,這項(xiàng)檢查是編譯器完成的,不是在運(yùn)行時(shí)拋出異常栏妖,這種是方案明顯優(yōu)于使用Object類的clone方法克隆對(duì)象乱豆。讓問(wèn)題在編譯的時(shí)候暴露出來(lái)總是好過(guò)把問(wèn)題留到運(yùn)行時(shí)。

34吊趾、匿名內(nèi)部類可以繼承其他類或?qū)崿F(xiàn)其他接口宛裕,在Swing編程和Android開(kāi)發(fā)中常用此方式來(lái)實(shí)現(xiàn)事件監(jiān)聽(tīng)和回調(diào)。

37论泛、指出下面程序的運(yùn)行結(jié)果揩尸。

class A {
    static {
        System.out.print("1");
    }
    public A() {
        System.out.print("2");
    }
}

class B extends A{
    static {
        System.out.print("a");
    }

    public B() {
        System.out.print("b");
    }
}

public class Hello {
    public static void main(String[] args) {
        A ab = new B();
        ab = new B();
    }
}

答:執(zhí)行結(jié)果:1a2b2b。創(chuàng)建對(duì)象時(shí)構(gòu)造器的調(diào)用順序是:先初始化靜態(tài)成員屁奏,然后調(diào)用父類構(gòu)造器岩榆,再初始化非靜態(tài)成員,最后調(diào)用自身構(gòu)造器。
提示:如果不能給出此題的正確答案勇边,說(shuō)明之前第21題Java類加載機(jī)制還沒(méi)有完全理解犹撒,趕緊再看看吧。

56粒褒、TreeMap和TreeSet在排序時(shí)如何比較元素油航?Collections工具類中的sort()方法如何比較元素?

答:TreeSet要求存放的對(duì)象所屬的類必須實(shí)現(xiàn)Comparable接口怀浆,該接口提供了比較元素的compareTo()方法曙砂,當(dāng)插入元素時(shí)會(huì)回調(diào)該方法比較元素的大小退个。TreeMap要求存放的鍵值對(duì)映射的鍵必須實(shí)現(xiàn)Comparable接口從而根據(jù)鍵對(duì)元素進(jìn)行排序。Collections工具類的sort方法有兩種重載的形式歇父,第一種要求傳入的待排序容器中存放的對(duì)象比較實(shí)現(xiàn)Comparable接口以實(shí)現(xiàn)元素的比較函筋;第二種不強(qiáng)制性的要求容器中的元素必須可比較沙合,但是要求傳入第二個(gè)參數(shù),參數(shù)是Comparator接口的子類型(需要重寫compare方法實(shí)現(xiàn)元素的比較)跌帐,相當(dāng)于一個(gè)臨時(shí)定義的排序規(guī)則首懈,其實(shí)就是通過(guò)接口注入比較元素大小的算法,也是對(duì)回調(diào)模式的應(yīng)用(Java中對(duì)函數(shù)式編程的支持)谨敛。

**58究履、線程的sleep()方法和yield()方法有什么區(qū)別? **

答:
① sleep()方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí)脸狸,因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì)最仑;yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);
② 線程執(zhí)行sleep()方法后轉(zhuǎn)入阻塞(blocked)狀態(tài)炊甲,而執(zhí)行yield()方法后轉(zhuǎn)入就緒(ready)狀態(tài)泥彤;
③ sleep()方法聲明拋出InterruptedException,而yield()方法沒(méi)有聲明任何異常卿啡;
④ sleep()方法比yield()方法(跟操作系統(tǒng)CPU調(diào)度相關(guān))具有更好的可移植性吟吝。

60、請(qǐng)說(shuō)出與線程同步以及線程調(diào)度相關(guān)的方法颈娜。

答:

  • wait():使一個(gè)線程處于等待(阻塞)狀態(tài)剑逃,并且釋放所持有的對(duì)象的鎖;
  • sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài)官辽,是一個(gè)靜態(tài)方法炕贵,調(diào)用此方法要處理InterruptedException異常;
  • notify():?jiǎn)拘岩粋€(gè)處于等待狀態(tài)的線程野崇,當(dāng)然在調(diào)用此方法的時(shí)候称开,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無(wú)關(guān)鳖轰;
  • notityAll():?jiǎn)拘阉刑幱诘却隣顟B(tài)的線程清酥,該方法并不是將對(duì)象的鎖給所有線程,而是讓它們競(jìng)爭(zhēng)蕴侣,只有獲得鎖的線程才能進(jìn)入就緒狀態(tài)焰轻;

提示:關(guān)于Java多線程和并發(fā)編程的問(wèn)題,建議看《關(guān)于Java并發(fā)編程的總結(jié)和思考》昆雀。

補(bǔ)充:Java 5通過(guò)Lock接口提供了顯式的鎖機(jī)制(explicit lock)辱志,增強(qiáng)了靈活性以及對(duì)線程的協(xié)調(diào)。Lock接口中定義了加鎖(lock())和解鎖(unlock())的方法狞膘,同時(shí)還提供了newCondition()方法來(lái)產(chǎn)生用于線程之間通信的Condition對(duì)象揩懒;此外,Java 5還提供了信號(hào)量機(jī)制(semaphore)挽封,信號(hào)量可以用來(lái)限制對(duì)某個(gè)共享資源進(jìn)行訪問(wèn)的線程的數(shù)量已球。在對(duì)資源進(jìn)行訪問(wèn)之前,線程必須得到信號(hào)量的許可(調(diào)用Semaphore對(duì)象的acquire()方法)辅愿;在完成對(duì)資源的訪問(wèn)后智亮,線程必須向信號(hào)量歸還許可(調(diào)用Semaphore對(duì)象的release()方法)。

下面的例子演示了100個(gè)線程同時(shí)向一個(gè)銀行賬戶中存入1元錢点待,在沒(méi)有使用同步機(jī)制和使用同步機(jī)制情況下的執(zhí)行情況阔蛉。

銀行賬戶類:

public class Account {
    private double balance; // 賬戶余額
    public void deposit(double money) { 
        double newBalance = balance + money; 
        try { 
            Thread.sleep(10); // 模擬此業(yè)務(wù)需要一段處理時(shí)間 
        } catch(InterruptedException ex) {
            ex.printStackTrace(); 
        } 
        balance = newBalance; 
    } 
    /** 
     *  獲得賬戶余額
     * @return [description]
     */
    public double getBalance() {
        return balance; 
    }
}

存錢線程類:

public class AddMoneyThread implements Runnable {
    private Account account; // 存入賬戶 
    private double money;   // 存入金額 
    public AddMoneyThread(Account account, double money) { 
        this.account = account; 
        this.money = money; 
    } 

    @Override 
    public void run() {
        account.deposit(money); 
    }
}

測(cè)試類:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test01 { 
    public static void main(String[] args) { 
        Account account = new Account(); 
        ExecutorService service = Executors.newFixedThreadPool(100); 
        for(int i = 1; i <= 100; i++) { 
            service.execute(new AddMoneyThread(account, 1)); 
        } 
        service.shutdown(); 
        while(!service.isTerminated()) {

        } 
        System.out.println("賬戶余額: " + account.getBalance()); 
    }
}

在沒(méi)有同步的情況下,執(zhí)行結(jié)果通常是顯示賬戶余額在10元以下癞埠,出現(xiàn)這種狀況的原因是馍忽,當(dāng)一個(gè)線程A試圖存入1元的時(shí)候,另外一個(gè)線程B也能夠進(jìn)入存款的方法中燕差,線程B讀取到的賬戶余額仍然是線程A存入1元錢之前的賬戶余額遭笋,因此也是在原來(lái)的余額0上面做了加1元的操作,同理線程C也會(huì)做類似的事情徒探,所以最后100個(gè)線程執(zhí)行結(jié)束時(shí)瓦呼,本來(lái)期望賬戶余額為100元,但實(shí)際得到的通常在10元以下(很可能是1元哦)测暗。解決這個(gè)問(wèn)題的辦法就是同步央串,當(dāng)一個(gè)線程對(duì)銀行賬戶存錢時(shí),需要將此賬戶鎖定碗啄,待其操作完成后才允許其他的線程進(jìn)行操作质和,代碼有如下幾種調(diào)整方案:

  • 在銀行賬戶的存款(deposit)方法上同步(synchronized)關(guān)鍵字
public class Account {
    private double balance; // 賬戶余額
    public synchronized void deposit(double money) { 
        double newBalance = balance + money; 
        try { 
            Thread.sleep(10); // 模擬此業(yè)務(wù)需要一段處理時(shí)間 
        } catch(InterruptedException ex) {
            ex.printStackTrace(); 
        } 
        balance = newBalance; 
    } 
    /** 
     *  獲得賬戶余額
     * @return [description]
     */
    public double getBalance() {
        return balance; 
    }
}
  • 在線程調(diào)用存款方法時(shí)對(duì)銀行賬戶進(jìn)行同步
 public class AddMoneyThread implements Runnable { 
   private Account account; // 存入賬戶 
   private double money; // 存入金額 
   public AddMoneyThread(Account account, double money) { 
       this.account = account; 
       this.money = money; 
   } 
   @Override 
   public void run() { 
       synchronized (account) { 
           account.deposit(money); 
       } 
   }
}

  • 通過(guò)Java 5顯示的鎖機(jī)制,為每個(gè)銀行賬戶創(chuàng)建一個(gè)鎖對(duì)象稚字,在存款操作進(jìn)行加鎖和解鎖的操作
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account { 
   private Lock accountLock = new ReentrantLock(); 
   private double balance; // 賬戶余額 
   
   public void deposit(double money) { 
       accountLock.lock(); 
       try { 
           double newBalance = balance + money; 
           try { 
               Thread.sleep(10); // 模擬此業(yè)務(wù)需要一段處理時(shí)間 
           } catch (InterruptedException ex) { 
               ex.printStackTrace(); 
           } 
           balance = newBalance; 
       } finally { 
           accountLock.unlock(); 
       } 
   }  

   public double getBalance() { 
       return balance; 
   }
}

按照上述三種方式對(duì)代碼進(jìn)行修改后饲宿,重寫執(zhí)行測(cè)試代碼Test01厦酬,將看到最終的賬戶余額為100元。當(dāng)然也可以使用Semaphore或CountdownLatch來(lái)實(shí)現(xiàn)同步瘫想。

65仗阅、什么是線程池(thread pool)?

答:在面向?qū)ο缶幊讨泄梗瑒?chuàng)建和銷毀對(duì)象是很費(fèi)時(shí)間的减噪,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其它更多資源。在Java中更是如此车吹,虛擬機(jī)將試圖跟蹤每一個(gè)對(duì)象筹裕,以便能夠在對(duì)象銷毀后進(jìn)行垃圾回收。所以提高服務(wù)程序效率的一個(gè)手段就是盡可能減少創(chuàng)建和銷毀對(duì)象的次數(shù)窄驹,特別是一些很耗資源的對(duì)象創(chuàng)建和銷毀朝卒,這就是”池化資源”技術(shù)產(chǎn)生的原因。線程池顧名思義就是事先創(chuàng)建若干個(gè)可執(zhí)行的線程放入一個(gè)池(容器)中馒吴,需要的時(shí)候從池中獲取線程不用自行創(chuàng)建,使用完畢不需要銷毀線程而是放回池中瑟曲,從而減少創(chuàng)建和銷毀線程對(duì)象的開(kāi)銷饮戳。
Java 5+中的Executor接口定義一個(gè)執(zhí)行線程的工具。它的子類型即線程池接口是ExecutorService洞拨。要配置一個(gè)線程池是比較復(fù)雜的扯罐,尤其是對(duì)于線程池的原理不是很清楚的情況下,因此在工具類Executors面提供了一些靜態(tài)工廠方法烦衣,生成一些常用的線程池歹河,如下所示:

  • newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作花吟,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)秸歧。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它衅澈。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行键菱。
  • newFixedThreadPool:創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程今布,直到線程達(dá)到線程池的最大大小经备。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束部默,那么線程池會(huì)補(bǔ)充一個(gè)新線程侵蒙。
  • newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程傅蹂,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程纷闺,當(dāng)任務(wù)數(shù)增加時(shí)算凿,此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制急但,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小澎媒。
  • newScheduledThreadPool:創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求波桩。
  • newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池戒努。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。

第60題的例子中演示了通過(guò)Executors工具類創(chuàng)建線程池并使用線程池執(zhí)行線程的代碼镐躲。如果希望在服務(wù)器上使用線程池储玫,強(qiáng)烈建議使用newFixedThreadPool方法來(lái)創(chuàng)建線程池,這樣能獲得更好的性能萤皂。

72撒穷、用Java的套接字編程實(shí)現(xiàn)一個(gè)多線程的回顯(echo)服務(wù)器。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {

   private static final int ECHO_SERVER_PORT = 6789;

   public static void main(String[] args) {        
       try(ServerSocket server = new ServerSocket(ECHO_SERVER_PORT)) {
           System.out.println("服務(wù)器已經(jīng)啟動(dòng)...");
           while(true) {
               Socket client = server.accept();
               new Thread(new ClientHandler(client)).start();
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

   private static class ClientHandler implements Runnable {
       private Socket client;

       public ClientHandler(Socket client) {
           this.client = client;
       }

       @Override
       public void run() {
           try(BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
                   PrintWriter pw = new PrintWriter(client.getOutputStream())) {
               String msg = br.readLine();
               System.out.println("收到" + client.getInetAddress() + "發(fā)送的: " + msg);
               pw.println(msg);
               pw.flush();
           } catch(Exception ex) {
               ex.printStackTrace();
           } finally {
               try {
                   client.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }
}

注意:上面的代碼使用了Java 7的TWR語(yǔ)法裆熙,由于很多外部資源類都間接的實(shí)現(xiàn)了AutoCloseable接口(單方法回調(diào)接口)端礼,因此可以利用TWR語(yǔ)法在try結(jié)束的時(shí)候通過(guò)回調(diào)的方式自動(dòng)調(diào)用外部資源類的close()方法,避免書寫冗長(zhǎng)的finally代碼塊入录。此外蛤奥,上面的代碼用一個(gè)靜態(tài)內(nèi)部類實(shí)現(xiàn)線程的功能,使用多線程可以避免一個(gè)用戶I/O操作所產(chǎn)生的中斷影響其他用戶對(duì)服務(wù)器的訪問(wèn)僚稿,簡(jiǎn)單的說(shuō)就是一個(gè)用戶的輸入操作不會(huì)造成其他用戶的阻塞凡桥。當(dāng)然,上面的代碼使用線程池可以獲得更好的性能蚀同,因?yàn)轭l繁的創(chuàng)建和銷毀線程所造成的開(kāi)銷也是不可忽視的缅刽。

下面是一段回顯客戶端測(cè)試代碼:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class EchoClient {

    public static void main(String[] args) throws Exception {
        Socket client = new Socket("localhost", 6789);
        Scanner sc = new Scanner(System.in);
        System.out.print("請(qǐng)輸入內(nèi)容: ");
        String msg = sc.nextLine();
        sc.close();
        PrintWriter pw = new PrintWriter(client.getOutputStream());
        pw.println(msg);
        pw.flush();
        BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
        System.out.println(br.readLine());
        client.close();
    }
}

如果希望用NIO的多路復(fù)用套接字實(shí)現(xiàn)服務(wù)器,代碼如下所示蠢络。NIO的操作雖然帶來(lái)了更好的性能衰猛,但是有些操作是比較底層的,對(duì)于初學(xué)者來(lái)說(shuō)還是有些難于理解刹孔。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class EchoServerNIO {

    private static final int ECHO_SERVER_PORT = 6789;
    private static final int ECHO_SERVER_TIMEOUT = 5000;
    private static final int BUFFER_SIZE = 1024;

    private static ServerSocketChannel serverChannel = null;
    private static Selector selector = null;    // 多路復(fù)用選擇器
    private static ByteBuffer buffer = null;    // 緩沖區(qū)

    public static void main(String[] args) {
        init();
        listen();
    }

    private static void init() {
        try {
            serverChannel = ServerSocketChannel.open();
            buffer = ByteBuffer.allocate(BUFFER_SIZE);
            serverChannel.socket().bind(new InetSocketAddress(ECHO_SERVER_PORT));
            serverChannel.configureBlocking(false);
            selector = Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void listen() {
        while (true) {
            try {
                if (selector.select(ECHO_SERVER_TIMEOUT) != 0) {
                    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                    while (it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        handleKey(key);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void handleKey(SelectionKey key) throws IOException {
        SocketChannel channel = null;

        try {
            if (key.isAcceptable()) {
                ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                channel = serverChannel.accept();
                channel.configureBlocking(false);
                channel.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {
                channel = (SocketChannel) key.channel();
                buffer.clear();
                if (channel.read(buffer) > 0) {
                    buffer.flip();
                    CharBuffer charBuffer = CharsetHelper.decode(buffer);
                    String msg = charBuffer.toString();
                    System.out.println("收到" + channel.getRemoteAddress() + "的消息:" + msg);
                    channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));
                } else {
                    channel.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (channel != null) {
                channel.close();
            }
        }
    }

}
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public final class CharsetHelper {
    private static final String UTF_8 = "UTF-8";
    private static CharsetEncoder encoder = Charset.forName(UTF_8).newEncoder();
    private static CharsetDecoder decoder = Charset.forName(UTF_8).newDecoder();

    private CharsetHelper() {
    }

    public static ByteBuffer encode(CharBuffer in) throws CharacterCodingException{
        return encoder.encode(in);
    }

    public static CharBuffer decode(ByteBuffer in) throws CharacterCodingException{
        return decoder.decode(in);
    }
}

75腕侄、闡述JDBC操作數(shù)據(jù)庫(kù)的步驟。

答:JDBC操作數(shù)據(jù)庫(kù)的步驟芦疏。

  • 加載驅(qū)動(dòng)Class.forName("oracle.jdbc.driver.OracleDriver");
  • 創(chuàng)建連接Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "scott", "tiger");
  • 創(chuàng)建語(yǔ)句PreparedStatement ps = con.prepareStatement("select * from emp where sal between ? and ?"); ps.setInt(1, 1000); ps.setInt(2, 3000);
  • 執(zhí)行語(yǔ)句ResultSet rs = ps.executeQuery();
  • 處理結(jié)果while(rs.next()) { System.out.println(rs.getInt("empno") + " - " + rs.getString("ename")); }
  • 關(guān)閉資源冕杠。finally { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }

提示:關(guān)閉外部資源的順序應(yīng)該和打開(kāi)的順序相反,也就是說(shuō)先關(guān)閉ResultSet酸茴、再關(guān)閉Statement分预、在關(guān)閉Connection。上面的代碼只關(guān)閉了Connection(連接)薪捍,雖然通常情況下在關(guān)閉連接時(shí)笼痹,連接上創(chuàng)建的語(yǔ)句和打開(kāi)的游標(biāo)也會(huì)關(guān)閉配喳,但不能保證總是如此,因此應(yīng)該按照剛才說(shuō)的順序分別關(guān)閉凳干。此外晴裹,第一步加載驅(qū)動(dòng)在JDBC 4.0中是可以省略的(自動(dòng)從類路徑中加載驅(qū)動(dòng)),但是我們建議保留救赐。

76涧团、Statement和PreparedStatement有什么區(qū)別?哪個(gè)性能更好经磅?

答:與Statement相比泌绣,
①PreparedStatement接口代表預(yù)編譯的語(yǔ)句,它主要的優(yōu)勢(shì)在于可以減少SQL的編譯錯(cuò)誤并增加SQL的安全性(減少SQL注射攻擊的可能性)预厌;②PreparedStatement中的SQL語(yǔ)句是可以帶參數(shù)的阿迈,避免了用字符串連接拼接SQL語(yǔ)句的麻煩和不安全;
③當(dāng)批量處理SQL或頻繁執(zhí)行相同的查詢時(shí)轧叽,PreparedStatement有明顯的性能上的優(yōu)勢(shì)苗沧,由于數(shù)據(jù)庫(kù)可以將編譯優(yōu)化后的SQL語(yǔ)句緩存起來(lái),下次執(zhí)行相同結(jié)構(gòu)的語(yǔ)句時(shí)就會(huì)很快(不用再次編譯和生成執(zhí)行計(jì)劃)炭晒。

補(bǔ)充:為了提供對(duì)存儲(chǔ)過(guò)程的調(diào)用待逞,JDBC API中還提供了CallableStatement接口。存儲(chǔ)過(guò)程(Stored Procedure)是數(shù)據(jù)庫(kù)中一組為了完成特定功能的SQL語(yǔ)句的集合腰埂,經(jīng)編譯后存儲(chǔ)在數(shù)據(jù)庫(kù)中飒焦,用戶通過(guò)指定存儲(chǔ)過(guò)程的名字并給出參數(shù)(如果該存儲(chǔ)過(guò)程帶有參數(shù))來(lái)執(zhí)行它蜈膨。雖然調(diào)用存儲(chǔ)過(guò)程會(huì)在網(wǎng)絡(luò)開(kāi)銷屿笼、安全性、性能上獲得很多好處翁巍,但是存在如果底層數(shù)據(jù)庫(kù)發(fā)生遷移時(shí)就會(huì)有很多麻煩驴一,因?yàn)槊糠N數(shù)據(jù)庫(kù)的存儲(chǔ)過(guò)程在書寫上存在不少的差別。

77灶壶、使用JDBC操作數(shù)據(jù)庫(kù)時(shí)肝断,如何提升讀取數(shù)據(jù)的性能?如何提升更新數(shù)據(jù)的性能驰凛?

答:要提升讀取數(shù)據(jù)的性能胸懈,可以指定通過(guò)結(jié)果集(ResultSet)對(duì)象的setFetchSize()方法指定每次抓取的記錄數(shù)(典型的空間換時(shí)間策略);要提升更新數(shù)據(jù)的性能可以使用PreparedStatement語(yǔ)句構(gòu)建批處理恰响,將若干SQL語(yǔ)句置于一個(gè)批處理中執(zhí)行趣钱。

78、在進(jìn)行數(shù)據(jù)庫(kù)編程時(shí)胚宦,連接池有什么作用首有?

答:由于創(chuàng)建連接和釋放連接都有很大的開(kāi)銷(尤其是數(shù)據(jù)庫(kù)服務(wù)器不在本地時(shí)燕垃,每次建立連接都需要進(jìn)行TCP的三次握手,釋放連接需要進(jìn)行TCP四次握手井联,造成的開(kāi)銷是不可忽視的)卜壕,為了提升系統(tǒng)訪問(wèn)數(shù)據(jù)庫(kù)的性能,可以事先創(chuàng)建若干連接置于連接池中烙常,需要時(shí)直接從連接池獲取轴捎,使用結(jié)束時(shí)歸還連接池而不必關(guān)閉連接,從而避免頻繁創(chuàng)建和釋放連接所造成的開(kāi)銷军掂,這是典型的用空間換取時(shí)間的策略(浪費(fèi)了空間存儲(chǔ)連接轮蜕,但節(jié)省了創(chuàng)建和釋放連接的時(shí)間)。池化技術(shù)在Java開(kāi)發(fā)中是很常見(jiàn)的蝗锥,在使用線程時(shí)創(chuàng)建線程池的道理與此相同跃洛。基于Java的開(kāi)源數(shù)據(jù)庫(kù)連接池主要有:C3P0终议、Proxool汇竭、DBCPBoneCP穴张、Druid等细燎。

補(bǔ)充:在計(jì)算機(jī)系統(tǒng)中時(shí)間和空間是不可調(diào)和的矛盾,理解這一點(diǎn)對(duì)設(shè)計(jì)滿足性能要求的算法是至關(guān)重要的皂甘。大型網(wǎng)站性能優(yōu)化的一個(gè)關(guān)鍵就是使用緩存玻驻,而緩存跟上面講的連接池道理非常類似,也是使用空間換時(shí)間的策略偿枕¤邓玻可以將熱點(diǎn)數(shù)據(jù)置于緩存中,當(dāng)用戶查詢這些數(shù)據(jù)時(shí)可以直接從緩存中得到渐夸,這無(wú)論如何也快過(guò)去數(shù)據(jù)庫(kù)中查詢嗤锉。當(dāng)然,緩存的置換策略等也會(huì)對(duì)系統(tǒng)性能產(chǎn)生重要影響墓塌,對(duì)于這個(gè)問(wèn)題的討論已經(jīng)超出了這里要闡述的范圍瘟忱。

79、什么是DAO模式苫幢?

答:DAO(Data Access Object)顧名思義是一個(gè)為數(shù)據(jù)庫(kù)或其他持久化機(jī)制提供了抽象接口的對(duì)象访诱,在不暴露底層持久化方案實(shí)現(xiàn)細(xì)節(jié)的前提下提供了各種數(shù)據(jù)訪問(wèn)操作。在實(shí)際的開(kāi)發(fā)中韩肝,應(yīng)該將所有對(duì)數(shù)據(jù)源的訪問(wèn)操作進(jìn)行抽象化后封裝在一個(gè)公共API中触菜。用程序設(shè)計(jì)語(yǔ)言來(lái)說(shuō),就是建立一個(gè)接口伞梯,接口中定義了此應(yīng)用程序中將會(huì)用到的所有事務(wù)方法玫氢。在這個(gè)應(yīng)用程序中帚屉,當(dāng)需要和數(shù)據(jù)源進(jìn)行交互的時(shí)候則使用這個(gè)接口,并且編寫一個(gè)單獨(dú)的類來(lái)實(shí)現(xiàn)這個(gè)接口漾峡,在邏輯上該類對(duì)應(yīng)一個(gè)特定的數(shù)據(jù)存儲(chǔ)攻旦。DAO模式實(shí)際上包含了兩個(gè)模式,一是Data Accessor(數(shù)據(jù)訪問(wèn)器)生逸,二是Data Object(數(shù)據(jù)對(duì)象)牢屋,前者要解決如何訪問(wèn)數(shù)據(jù)的問(wèn)題,而后者要解決的是如何用對(duì)象封裝數(shù)據(jù)槽袄。

80烙无、事務(wù)的ACID是指什么?

答: - 原子性(Atomic):事務(wù)中各項(xiàng)操作遍尺,要么全做要么全不做截酷,任何一項(xiàng)操作的失敗都會(huì)導(dǎo)致整個(gè)事務(wù)的失敗乾戏; - 一致性(Consistent):事務(wù)結(jié)束后系統(tǒng)狀態(tài)是一致的迂苛; - 隔離性(Isolated):并發(fā)執(zhí)行的事務(wù)彼此無(wú)法看到對(duì)方的中間狀態(tài); - 持久性(Durable):事務(wù)完成后所做的改動(dòng)都會(huì)被持久化鼓择,即使發(fā)生災(zāi)難性的失敗三幻。通過(guò)日志和同步備份可以在故障發(fā)生后重建數(shù)據(jù)。

98呐能、轉(zhuǎn)發(fā)(forward)和重定向(redirect)的區(qū)別念搬?

答:forward是容器中控制權(quán)的轉(zhuǎn)向,是服務(wù)器請(qǐng)求資源摆出,服務(wù)器直接訪問(wèn)目標(biāo)地址的URL朗徊,把那個(gè)URL 的響應(yīng)內(nèi)容讀取過(guò)來(lái),然后把這些內(nèi)容再發(fā)給瀏覽器懊蒸,瀏覽器根本不知道服務(wù)器發(fā)送的內(nèi)容是從哪兒來(lái)的荣倾,所以它的地址欄中還是原來(lái)的地址悯搔。redirect就是服務(wù)器端根據(jù)邏輯骑丸,發(fā)送一個(gè)狀態(tài)碼,告訴瀏覽器重新去請(qǐng)求那個(gè)地址妒貌,因此從瀏覽器的地址欄中可以看到跳轉(zhuǎn)后的鏈接地址通危,很明顯redirect無(wú)法訪問(wèn)到服務(wù)器保護(hù)起來(lái)資源,但是可以從一個(gè)網(wǎng)站redirect到其他網(wǎng)站灌曙。forward更加高效菊碟,所以在滿足需要時(shí)盡量使用forward(通過(guò)調(diào)用RequestDispatcher對(duì)象的forward()方法,該對(duì)象可以通過(guò)ServletRequest對(duì)象的getRequestDispatcher()方法獲得)在刺,并且這樣也有助于隱藏實(shí)際的鏈接逆害;在有些情況下头镊,比如需要訪問(wèn)一個(gè)其它服務(wù)器上的資源,則必須使用重定向(通過(guò)HttpServletResponse對(duì)象調(diào)用其sendRedirect()方法實(shí)現(xiàn))魄幕。

**100相艇、get和post請(qǐng)求的區(qū)別? **

答:
①get請(qǐng)求用來(lái)從服務(wù)器上獲得資源纯陨,而post是用來(lái)向服務(wù)器提交數(shù)據(jù)坛芽;
②get將表單中數(shù)據(jù)按照name=value的形式,添加到action 所指向的URL 后面翼抠,并且兩者使用"?"連接咙轩,而各個(gè)變量之間使用"&"連接;post是將表單中的數(shù)據(jù)放在HTTP協(xié)議的請(qǐng)求頭或消息體中阴颖,傳遞到action所指向URL活喊;
③get傳輸?shù)臄?shù)據(jù)要受到URL長(zhǎng)度限制(1024字節(jié));而post可以傳輸大量的數(shù)據(jù)量愧,上傳文件通常要使用post方式胧弛;
④使用get時(shí)參數(shù)會(huì)顯示在地址欄上,如果這些數(shù)據(jù)不是敏感數(shù)據(jù)侠畔,那么可以使用get结缚;對(duì)于敏感數(shù)據(jù)還是應(yīng)用使用post;
⑤get使用MIME類型application/x-www-form-urlencoded的URL編碼(也叫百分號(hào)編碼)文本的格式傳遞參數(shù)软棺,保證被傳送的參數(shù)由遵循規(guī)范的文本組成红竭,例如一個(gè)空格的編碼是"%20"。

**105喘落、實(shí)現(xiàn)會(huì)話跟蹤的技術(shù)有哪些茵宪? **

答:由于HTTP協(xié)議本身是無(wú)狀態(tài)的,服務(wù)器為了區(qū)分不同的用戶瘦棋,就需要對(duì)用戶會(huì)話進(jìn)行跟蹤稀火,簡(jiǎn)單的說(shuō)就是為用戶進(jìn)行登記,為用戶分配唯一的ID赌朋,下一次用戶在請(qǐng)求中包含此ID凰狞,服務(wù)器據(jù)此判斷到底是哪一個(gè)用戶。
①URL 重寫:在URL中添加用戶會(huì)話的信息作為請(qǐng)求的參數(shù)沛慢,或者將唯一的會(huì)話ID添加到URL結(jié)尾以標(biāo)識(shí)一個(gè)會(huì)話赡若。
②設(shè)置表單隱藏域:將和會(huì)話跟蹤相關(guān)的字段添加到隱式表單域中,這些信息不會(huì)在瀏覽器中顯示但是提交表單時(shí)會(huì)提交給服務(wù)器团甲。
這兩種方式很難處理跨越多個(gè)頁(yè)面的信息傳遞逾冬,因?yàn)槿绻看味家薷腢RL或在頁(yè)面中添加隱式表單域來(lái)存儲(chǔ)用戶會(huì)話相關(guān)信息,事情將變得非常麻煩。
③cookie:cookie有兩種身腻,一種是基于窗口的产还,瀏覽器窗口關(guān)閉后,cookie就沒(méi)有了嘀趟;另一種是將信息存儲(chǔ)在一個(gè)臨時(shí)文件中雕沉,并設(shè)置存在的時(shí)間。當(dāng)用戶通過(guò)瀏覽器和服務(wù)器建立一次會(huì)話后去件,會(huì)話ID就會(huì)隨響應(yīng)信息返回存儲(chǔ)在基于窗口的cookie中坡椒,那就意味著只要瀏覽器沒(méi)有關(guān)閉,會(huì)話沒(méi)有超時(shí)尤溜,下一次請(qǐng)求時(shí)這個(gè)會(huì)話ID又會(huì)提交給服務(wù)器讓服務(wù)器識(shí)別用戶身份倔叼。會(huì)話中可以為用戶保存信息。會(huì)話對(duì)象是在服務(wù)器內(nèi)存中的宫莱,而基于窗口的cookie是在客戶端內(nèi)存中的丈攒。如果瀏覽器禁用了cookie,那么就需要通過(guò)下面兩種方式進(jìn)行會(huì)話跟蹤授霸。當(dāng)然巡验,在使用cookie時(shí)要注意幾點(diǎn):首先不要在cookie中存放敏感信息;其次cookie存儲(chǔ)的數(shù)據(jù)量有限(4k)碘耳,不能將過(guò)多的內(nèi)容存儲(chǔ)cookie中显设;再者瀏覽器通常只允許一個(gè)站點(diǎn)最多存放20個(gè)cookie。當(dāng)然辛辨,和用戶會(huì)話相關(guān)的其他信息(除了會(huì)話ID)也可以存在cookie方便進(jìn)行會(huì)話跟蹤捕捂。
④HttpSession:在所有會(huì)話跟蹤技術(shù)中,HttpSession對(duì)象是最強(qiáng)大也是功能最多的斗搞。當(dāng)一個(gè)用戶第一次訪問(wèn)某個(gè)網(wǎng)站時(shí)會(huì)自動(dòng)創(chuàng)建HttpSession指攒,每個(gè)用戶可以訪問(wèn)他自己的HttpSession∑Х伲可以通過(guò)HttpServletRequest對(duì)象的getSession方法獲得HttpSession允悦,通過(guò)HttpSession的setAttribute方法可以將一個(gè)值放在HttpSession中,通過(guò)調(diào)用HttpSession對(duì)象的getAttribute方法虑啤,同時(shí)傳入屬性名就可以獲取保存在HttpSession中的對(duì)象隙弛。與上面三種方式不同的是,HttpSession放在服務(wù)器的內(nèi)存中咐旧,因此不要將過(guò)大的對(duì)象放在里面驶鹉,即使目前的Servlet容器可以在內(nèi)存將滿時(shí)將HttpSession中的對(duì)象移到其他存儲(chǔ)設(shè)備中绩蜻,但是這樣勢(shì)必影響性能铣墨。添加到HttpSession中的值可以是任意Java對(duì)象,這個(gè)對(duì)象最好實(shí)現(xiàn)了Serializable接口办绝,這樣Servlet容器在必要的時(shí)候可以將其序列化到文件中伊约,否則在序列化時(shí)就會(huì)出現(xiàn)異常姚淆。

106、過(guò)濾器有哪些作用和用法屡律?

答: Java Web開(kāi)發(fā)中的過(guò)濾器(filter)是從Servlet 2.3規(guī)范開(kāi)始增加的功能腌逢,并在Servlet 2.4規(guī)范中得到增強(qiáng)。對(duì)Web應(yīng)用來(lái)說(shuō)超埋,過(guò)濾器是一個(gè)駐留在服務(wù)器端的Web組件搏讶,它可以截取客戶端和服務(wù)器之間的請(qǐng)求與響應(yīng)信息,并對(duì)這些信息進(jìn)行過(guò)濾霍殴。當(dāng)Web容器接受到一個(gè)對(duì)資源的請(qǐng)求時(shí)媒惕,它將判斷是否有過(guò)濾器與這個(gè)資源相關(guān)聯(lián)。如果有来庭,那么容器將把請(qǐng)求交給過(guò)濾器進(jìn)行處理妒蔚。在過(guò)濾器中,你可以改變請(qǐng)求的內(nèi)容月弛,或者重新設(shè)置請(qǐng)求的報(bào)頭信息肴盏,然后再將請(qǐng)求發(fā)送給目標(biāo)資源。當(dāng)目標(biāo)資源對(duì)請(qǐng)求作出響應(yīng)時(shí)候帽衙,容器同樣會(huì)將響應(yīng)先轉(zhuǎn)發(fā)給過(guò)濾器菜皂,在過(guò)濾器中你可以對(duì)響應(yīng)的內(nèi)容進(jìn)行轉(zhuǎn)換,然后再將響應(yīng)發(fā)送到客戶端厉萝。
常見(jiàn)的過(guò)濾器用途主要包括:對(duì)用戶請(qǐng)求進(jìn)行統(tǒng)一認(rèn)證幌墓、對(duì)用戶的訪問(wèn)請(qǐng)求進(jìn)行記錄和審核、對(duì)用戶發(fā)送的數(shù)據(jù)進(jìn)行過(guò)濾或替換冀泻、轉(zhuǎn)換圖象格式常侣、對(duì)響應(yīng)內(nèi)容進(jìn)行壓縮以減少傳輸量、對(duì)請(qǐng)求或響應(yīng)進(jìn)行加解密處理弹渔、觸發(fā)資源訪問(wèn)事件胳施、對(duì)XML的輸出應(yīng)用XSLT等。
和過(guò)濾器相關(guān)的接口主要有:Filter肢专、FilterConfig和FilterChain舞肆。

**107、監(jiān)聽(tīng)器有哪些作用和用法博杖? **

答:Java Web開(kāi)發(fā)中的監(jiān)聽(tīng)器(listener)就是application椿胯、session、request三個(gè)對(duì)象創(chuàng)建剃根、銷毀或者往其中添加修改刪除屬性時(shí)自動(dòng)執(zhí)行代碼的功能組件哩盲,如下所示:
①ServletContextListener:對(duì)Servlet上下文的創(chuàng)建和銷毀進(jìn)行監(jiān)聽(tīng)。
②ServletContextAttributeListener:監(jiān)聽(tīng)Servlet上下文屬性的添加、刪除和替換廉油。
③HttpSessionListener:對(duì)Session的創(chuàng)建和銷毀進(jìn)行監(jiān)聽(tīng)惠险。

補(bǔ)充:session的銷毀有兩種情況:1). session超時(shí)(可以在web.xml中通過(guò)<session-config>/<session-timeout>標(biāo)簽配置超時(shí)時(shí)間);2). 通過(guò)調(diào)用session對(duì)象的invalidate()方法使session失效抒线。
④HttpSessionAttributeListener:對(duì)Session對(duì)象中屬性的添加班巩、刪除和替換進(jìn)行監(jiān)聽(tīng)。
⑤ServletRequestListener:對(duì)請(qǐng)求對(duì)象的初始化和銷毀進(jìn)行監(jiān)聽(tīng)嘶炭。
⑥ServletRequestAttributeListener:對(duì)請(qǐng)求對(duì)象屬性的添加抱慌、刪除和替換進(jìn)行監(jiān)聽(tīng)。

108眨猎、web.xml文件中可以配置哪些內(nèi)容遥缕?

答:web.xml用于配置Web應(yīng)用的相關(guān)信息,如:監(jiān)聽(tīng)器(listener)宵呛、過(guò)濾器(filter)单匣、 Servlet、相關(guān)參數(shù)宝穗、會(huì)話超時(shí)時(shí)間户秤、安全驗(yàn)證方式、錯(cuò)誤頁(yè)面等逮矛,下面是一些開(kāi)發(fā)中常見(jiàn)的配置:

119鸡号、Servlet中如何獲取用戶配置的初始化參數(shù)以及服務(wù)器上下文參數(shù)?

答:可以通過(guò)重寫Servlet接口的init(ServletConfig)方法并通過(guò)ServletConfig對(duì)象的getInitParameter()方法來(lái)獲取Servlet的初始化參數(shù)须鼎【ò椋可以通過(guò)ServletConfig對(duì)象的getServletContext()方法獲取ServletContext對(duì)象,并通過(guò)該對(duì)象的getInitParameter()方法來(lái)獲取服務(wù)器上下文參數(shù)晋控。當(dāng)然汞窗,ServletContext對(duì)象也在處理用戶請(qǐng)求的方法(如doGet()方法)中通過(guò)請(qǐng)求對(duì)象的getServletContext()方法來(lái)獲得。

153赡译、Spring支持的事務(wù)管理類型有哪些仲吏?你在項(xiàng)目中使用哪種方式?

答:Spring支持編程式事務(wù)管理和聲明式事務(wù)管理蝌焚。許多Spring框架的用戶選擇聲明式事務(wù)管理裹唆,因?yàn)檫@種方式和應(yīng)用程序的關(guān)聯(lián)較少,因此更加符合輕量級(jí)容器的概念只洒。聲明式事務(wù)管理要優(yōu)于編程式事務(wù)管理许帐,盡管在靈活性方面它弱于編程式事務(wù)管理,因?yàn)榫幊淌绞聞?wù)允許你通過(guò)代碼控制業(yè)務(wù)毕谴。
事務(wù)分為全局事務(wù)和局部事務(wù)成畦。全局事務(wù)由應(yīng)用服務(wù)器管理距芬,需要底層服務(wù)器JTA支持(如WebLogic、WildFly等)羡鸥。局部事務(wù)和底層采用的持久化方案有關(guān)蔑穴,例如使用JDBC進(jìn)行持久化時(shí)忠寻,需要使用Connetion對(duì)象來(lái)操作事務(wù)惧浴;而采用Hibernate進(jìn)行持久化時(shí),需要使用Session對(duì)象來(lái)操作事務(wù)奕剃。
Spring提供了如下所示的事務(wù)管理器衷旅。
事務(wù)管理器實(shí)現(xiàn)類 目標(biāo)對(duì)象
DataSourceTransactionManager 注入DataSource
HibernateTransactionManager 注入SessionFactory
JdoTransactionManager 管理JDO事務(wù)
JtaTransactionManager 使用JTA管理事務(wù)
PersistenceBrokerTransactionManager 管理Apache的OJB事務(wù)
這些事務(wù)的父接口都是PlatformTransactionManager。Spring的事務(wù)管理機(jī)制是一種典型的策略模式纵朋,PlatformTransactionManager代表事務(wù)管理接口柿顶,該接口定義了三個(gè)方法,該接口并不知道底層如何管理事務(wù)操软,但是它的實(shí)現(xiàn)類必須提供getTransaction()方法(開(kāi)啟事務(wù))嘁锯、commit()方法(提交事務(wù))、rollback()方法(回滾事務(wù))的多態(tài)實(shí)現(xiàn)聂薪,這樣就可以用不同的實(shí)現(xiàn)類代表不同的事務(wù)管理策略家乘。使用JTA全局事務(wù)策略時(shí),需要底層應(yīng)用服務(wù)器支持藏澳,而不同的應(yīng)用服務(wù)器所提供的JTA全局事務(wù)可能存在細(xì)節(jié)上的差異仁锯,因此實(shí)際配置全局事務(wù)管理器是可能需要使用JtaTransactionManager的子類,如:WebLogicJtaTransactionManager(Oracle的WebLogic服務(wù)器提供)翔悠、UowJtaTransactionManager(IBM的WebSphere服務(wù)器提供)等业崖。

156、Spring MVC的工作原理是怎樣的蓄愁?

答:Spring MVC的工作原理如下圖所示:
Spring MVC的工作原理

① 客戶端的所有請(qǐng)求都交給前端控制器DispatcherServlet來(lái)處理双炕,它會(huì)負(fù)責(zé)調(diào)用系統(tǒng)的其他模塊來(lái)真正處理用戶的請(qǐng)求。 ② DispatcherServlet收到請(qǐng)求后撮抓,將根據(jù)請(qǐng)求的信息(包括URL雄家、HTTP協(xié)議方法、請(qǐng)求頭胀滚、請(qǐng)求參數(shù)趟济、Cookie等)以及HandlerMapping的配置找到處理該請(qǐng)求的Handler(任何一個(gè)對(duì)象都可以作為請(qǐng)求的Handler)。 ③在這個(gè)地方Spring會(huì)通過(guò)HandlerAdapter對(duì)該處理器進(jìn)行封裝咽笼。 ④ HandlerAdapter是一個(gè)適配器顷编,它用統(tǒng)一的接口對(duì)各種Handler中的方法進(jìn)行調(diào)用。 ⑤ Handler完成對(duì)用戶請(qǐng)求的處理后剑刑,會(huì)返回一個(gè)ModelAndView對(duì)象給DispatcherServlet媳纬,ModelAndView顧名思義双肤,包含了數(shù)據(jù)模型以及相應(yīng)的視圖的信息。 ⑥ ModelAndView的視圖是邏輯視圖钮惠,DispatcherServlet還要借助ViewResolver完成從邏輯視圖到真實(shí)視圖對(duì)象的解析工作茅糜。 ⑦ 當(dāng)?shù)玫秸嬲囊晥D對(duì)象后,DispatcherServlet會(huì)利用視圖對(duì)象對(duì)模型數(shù)據(jù)進(jìn)行渲染素挽。 ⑧ 客戶端得到響應(yīng)蔑赘,可能是一個(gè)普通的HTML頁(yè)面,也可以是XML或JSON字符串预明,還可以是一張圖片或者一個(gè)PDF文件缩赛。

**161、闡述Spring框架中Bean的生命周期撰糠? **

答:
① Spring IoC容器找到關(guān)于Bean的定義并實(shí)例化該Bean酥馍。
② Spring IoC容器對(duì)Bean進(jìn)行依賴注入。
③ 如果Bean實(shí)現(xiàn)了BeanNameAware接口阅酪,則將該Bean的id傳給setBeanName方法旨袒。
④ 如果Bean實(shí)現(xiàn)了BeanFactoryAware接口,則將BeanFactory對(duì)象傳給setBeanFactory方法术辐。
⑤ 如果Bean實(shí)現(xiàn)了BeanPostProcessor接口砚尽,則調(diào)用其postProcessBeforeInitialization方法。
⑥ 如果Bean實(shí)現(xiàn)了InitializingBean接口术吗,則調(diào)用其afterPropertySet方法尉辑。
⑦ 如果有和Bean關(guān)聯(lián)的BeanPostProcessors對(duì)象,則這些對(duì)象的postProcessAfterInitialization方法被調(diào)用较屿。
⑧ 當(dāng)銷毀Bean實(shí)例時(shí)隧魄,如果Bean實(shí)現(xiàn)了DisposableBean接口,則調(diào)用其destroy方法隘蝎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末购啄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嘱么,更是在濱河造成了極大的恐慌狮含,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曼振,死亡現(xiàn)場(chǎng)離奇詭異几迄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)冰评,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門映胁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人甲雅,你說(shuō)我怎么就攤上這事解孙】犹睿” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵弛姜,是天一觀的道長(zhǎng)脐瑰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)廷臼,這世上最難降的妖魔是什么苍在? 我笑而不...
    開(kāi)封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮中剩,結(jié)果婚禮上忌穿,老公的妹妹穿的比我還像新娘抒寂。我一直安慰自己结啼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布屈芜。 她就那樣靜靜地躺著郊愧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪井佑。 梳的紋絲不亂的頭發(fā)上懒棉,一...
    開(kāi)封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天独郎,我揣著相機(jī)與錄音,去河邊找鬼。 笑死术浪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兔沃。 我是一名探鬼主播玛迄,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宁舰!你這毒婦竟也來(lái)了拼卵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛮艰,失蹤者是張志新(化名)和其女友劉穎腋腮,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體壤蚜,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡即寡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袜刷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聪富。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖水泉,靈堂內(nèi)的尸體忽然破棺而出善涨,到底是詐尸還是另有隱情窒盐,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布钢拧,位于F島的核電站蟹漓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏源内。R本人自食惡果不足惜葡粒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膜钓。 院中可真熱鬧嗽交,春花似錦、人聲如沸颂斜。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沃疮。三九已至盒让,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間司蔬,已是汗流浹背邑茄。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俊啼,地道東北人肺缕。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像授帕,于是被迫代替她去往敵國(guó)和親同木。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法豪墅,類相關(guān)的語(yǔ)法泉手,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法偶器,異常的語(yǔ)法斩萌,線程的語(yǔ)...
    子非魚_t_閱讀 31,622評(píng)論 18 399
  • 轉(zhuǎn)自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帥199207閱讀 8,518評(píng)論 3 93
  • 你們總說(shuō)生活節(jié)奏快霎苗,自己太累了想慢下來(lái)姆吭。你們?nèi)コ啥迹惤湔担B門……除了朋友圈里的照片又留下了什么内狸。也許你該靜下...
    亞特斯蒂閱讀 140評(píng)論 0 0
  • 最近天氣太熱检眯,公司里的梅姐將以前留了很久的長(zhǎng)發(fā)突然剪短了。 早晨在電梯里碰到她昆淡,她很不習(xí)慣地用手摸了又摸锰瘸,有些不自...
    sunny視界閱讀 1,152評(píng)論 14 31
  • 1.她的長(zhǎng)報(bào)做惡人經(jīng)驗(yàn)就是在暗示,現(xiàn)在我被噴的情況就是跟她當(dāng)時(shí)類似昂灵,王總不去做只能自己擔(dān)著避凝,自己上陣做壞人;所以現(xiàn)...
    ZQ鄭閱讀 379評(píng)論 1 0