1、servlet生命周期
(1) 初始化階段,調(diào)用init()
方法
(2) 響應(yīng)客戶端請求階段,調(diào)用service()
方法
(3) 終止階段,調(diào)用destroy()
方法
servlet容器裝載servlet
- Servlet容器啟動時自動裝載某些Servlet,實現(xiàn)它只需要在web.XML文件中的<Servlet></Servlet>之間添加如下代碼:
<loadon-startup>1</loadon-startup>
- 在Servlet容器啟動后,客戶首次向Servlet發(fā)送請求
- Servlet類文件被更新后,重新裝載Servlet,Servlet被裝載后,Servlet容器創(chuàng)建一個Servlet實例并且調(diào)用Servlet的init()方法進行初始化。在Servlet的整個生命周期內(nèi),init()方法只被調(diào)用一次。
servlet工作原理
首先客戶發(fā)送一個請求,servlet
調(diào)用service()
方法對請求進行響應(yīng),service()
方法對請求的方式進行匹配,選擇調(diào)用doGet()
, doPost()
這些方。servlet接口中沒有 doGet,doPost這些方法,httpServlet中有這些方法,但返回值都是error信息,所有需要將doPost,doGet重寫。
每一個自定義的servlet都需要實現(xiàn)servlet接口(init,service,destory就在這個接口中)。GenericServlet是一個通用的、不特定于任何協(xié)議的servlet,它實現(xiàn)了servlet接口,而httpServlet又繼承與GenericServlet,所以我們定義servlet時只需要繼承httpServlet即可。
httpServlet是特定于http協(xié)議的類,它實現(xiàn)了service()方法,并將servletRequest和servletResponse強轉(zhuǎn)為httpRequest和httpResponse。
2、sql 左外連接、右外連接、內(nèi)連接、全連接
--左外連接,就算emp表中員工的deptno沒有與dept表中的deptno對應(yīng)也會查出來,不過顯示emp表中的字段
select e.*,d.*
from emp e left join dept d on e.deptno=d.deptno;
--右外連接,就算emp表中員工的deptno沒有與dept表中的deptno對應(yīng)也會查出來,不過顯示dept表中的字段
select e.*,d.*
from emp e right join dept d on e.deptno=d.deptno;
-- 內(nèi)連接,只會查詢出兩表deptno一一對應(yīng)的數(shù)據(jù)
select e.*,d.*
from emp e inner join dept d on e.deptno=d.deptno;
-- 全連接,不管兩張表的數(shù)據(jù)是否一一對應(yīng),全都會查出來
select e.*,d.*
from emp e full join dept d on e.deptno=d.deptno;
3、數(shù)據(jù)庫并發(fā)問題
數(shù)據(jù)庫帶來的并發(fā)問題有:
- 丟失更新。
- 未確認的相關(guān)性(臟讀)。
- 不一致的分析(非重復(fù)讀)。
- 幻讀。
1.1 丟失更新
當兩個或多個事務(wù)選擇同一行,然后基于最初選定的值進行更新,會發(fā)生丟失更新問題。每個事務(wù)都不知道其實事務(wù)的存在。最后進行提交的事務(wù)會將之前的更新覆蓋,導(dǎo)致數(shù)據(jù)丟失。
此時需要對數(shù)據(jù)庫進行加鎖,兩種方式,一種是一開始就鎖住,防的很徹底,但是一旦鎖的時間過長會影響他人操作;另一種是在最后做更新提交時上鎖,將更新失敗視為小概率事件,只在最后一步才上鎖。
1.1.1 悲觀鎖
當我們對數(shù)據(jù)進行修改時,首先需要將數(shù)據(jù)查詢出來,在查詢語中加鎖,即 select .......for update nowwait
,在一開始的查詢中就開始加鎖,避免其他用戶更新
1.1.2 樂觀鎖
(a)舊值條件法:
在更新語句中,使用舊的狀態(tài)值作為更新的條件(不推薦)
(b)版本列法:
當我們設(shè)計表的時候往往會多設(shè)置兩個number/date類型的列,以便以后對表進行拓展。我們將一個number列作為版本列,每次修改數(shù)據(jù)時,以 限制條件 = 主鍵 + 版本列號
的形式進行修改,同時每修改一次數(shù)據(jù),都對版本號進行更新。
我們通常在沖突較為嚴重的系統(tǒng)中使用悲觀鎖,其他情況優(yōu)先使用樂觀鎖。
2.1 未確認的相關(guān)性(臟讀)
當一個事務(wù)讀取到另一個事務(wù)還未提交的修改時候,產(chǎn)生臟讀。
張三的工資原本是2000,財務(wù)不小心將張三工資改成了5000,此時尚未提交,張三查詢時發(fā)現(xiàn)自己工資變成5000后很開心,此時財務(wù)發(fā)現(xiàn)數(shù)據(jù)異常,回滾事務(wù),張三工資又變成了2000,這就叫臟讀。
解決辦法:在事務(wù)提交之前,其他任何事務(wù)都不能讀取其修改過的值。
3.1 不一致的分析(非重復(fù)讀)
4.1 幻讀
4、線程的兩種創(chuàng)建方式
第一種:new Thread 重寫 Thread 中的 run() 方法。
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("1: " + Thread.currentThread().getName());
System.out.println("2: " + this.getName());
}
}
};
第二種:Thread 的構(gòu)造方法中有一個是以 Runnable對象為參數(shù),Runnable是一個接口,當中有run()方法,Thread是implements了Runnable接口,繼而實現(xiàn)了run()方法,我們可以在new Thread的時候,通過構(gòu)造方法傳一個Runnable對象,而Runnable對象中又有run()方法,通過這種方式,也能實現(xiàn)線程的創(chuàng)建。
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("3: " + Thread.currentThread().getName());
}
}
});
5、SVN多用戶開發(fā) --- 代碼沖突&解決
修改同一文件的不同位置引發(fā)的沖突
現(xiàn)在有兩個用戶:zhangsan 和 lisi。他們需要同時修改一個文件,原始代碼如下:
public void Test{
System.out.println("Test!");
return 1;
}
此時 zhangsan 需要修改第二行代,
public void Test{
System.out.println("Test!xingoo");
return 1;
}
lisi 則修改第三行代碼,
public void Test{
System.out.println("Test!");
return 0;
}
假設(shè) zhangsan 先提交代碼,當 lisi 提交代碼時,會提示錯誤:
提示用戶該文件已經(jīng)過期,需要先更新文件。
此時 lisi 需要先進行更新代碼,再提交代碼。
這種情況,是最簡單的代碼沖突樣例。不同用戶修改了不同位置的代碼,因此只需要在提交前進行更新,就可以解決沖突。
修改同一文件相同位置引發(fā)的沖突
還是這個例子,zhangsan 當前代碼如下,版本號為9,
public void Test{
System.out.println("Test!xingoo");
return 1;
}
lisi 代碼如下,版本號為10,
public void Test{
System.out.println("Test!xingoo");
return 0;
}
此時 lisi 的代碼是最新的版本。
這時,zhangsan 想要修改第三行的返回值如下,
public void Test{
System.out.println("Test!xingoo");
return 2;
}
這時提交代碼會出現(xiàn)提示,發(fā)生錯誤,需要更新,OK,更新之后再次提交還是報錯,提示 文件存在沖突!
觀察文件目錄,會發(fā)現(xiàn)多了三個文件
分別打開三個文件:
其中<<<<<<.mine到====之間為當前用戶修改的內(nèi)容;
====到>>>>>.r10為版本庫中的內(nèi)容:
public void Test{
System.out.println("Test!xingoo");
<<<<<<< .mine
return 2;
=======
return 0;
>>>>>>> .r10
}
test.txt.mine 記錄當前用戶修改后的文件內(nèi)容快照
public void Test{
System.out.println("Test!xingoo");
return 2;
}
test.txt.r9 記錄當前用戶修改前,版本庫中的內(nèi)容快照
public void Test{
System.out.println("Test!xingoo");
return 1;
}
test.txt.r10 記錄當前版本庫中內(nèi)容快照
public void Test{
System.out.println("Test!xingoo");
return 0;
}
通過這四個文件就可以很快速的發(fā)現(xiàn),哪里有沖突。
至于如何修改:
直接拷貝mine文件,然后把r10文件的r10后綴去掉,進行更新。更新后,對比mine中的內(nèi)容在進行修改。
也可以直接與其他人員進行協(xié)商溝通;更新版本庫中的內(nèi)容。
6、排序算法
冒泡排序
左右相鄰的兩個數(shù)進行比較,將大的放到右邊,小的放到左邊,每次排序都會將最大的數(shù)放到序列的最后,最差時間復(fù)雜度為 O(n^2),最好情況為 O(n)
public class BubbleSort {
public static void sort(int[] num) {
for (int i = 0; i < num.length - 1; i++) {
int demo = 0;
for(int j = 0; j < num.length -1-i; j++) {
if (num[j] > num[j + 1]) {
demo = num[j];
num[j] = num[j + 1];
num[j + 1] = demo;
}
}
}
for(int k = 0; k < num.length; k++) {
System.out.print(num[k] + ", ");
}
}
public static void main(String[] args) {
int[] num = {8, 2, 3, 1, 6, 9, 7};
sort(num);
}
}
插入排序
將序列的第一個數(shù)默認為已排序序列,從第二個數(shù)開始(目標數(shù)get),對已排序序列進行從后往前的比較,如果已排序序列的當前數(shù)大于get,則該元素移動到下一位,重復(fù)比較,直到已排序序列的當前數(shù)小于get,則在該元素后一個位置將get插入。
最好空間復(fù)雜度為 O(n),最壞為O(n^2),與冒泡排序類似。
這個過程類似與打撲克摸手牌,此時已排序序列表左手已經(jīng)抓好的牌,get表示右手新摸到的牌,從右往左進行比較,直到有小于get的牌,在其后插入get。
public class InsertionSort {
public static void insertion(int[] arr) {
int len = arr.length;
for(int i = 1; i < len; i++) {
int get = arr[i];
int j = i -1;
while(j >= 0 && get < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = get;
}
}
public static void main(String[] args) {
int[] arr = {2, 3, 1, 5, 6};
insertion(arr);
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}