數據庫
如何實現數據庫的分頁?
Mysql:可用這個模式select * from table where 條件 limit 當前頁碼*頁面容量 - 1,每頁的數據量;
e.g:select * from user where age = 35 limit 11,6;其中:當前頁碼為2,頁面容量為6。
Oracle:select * from (select A.* from (select * from user order by id) A where id > 5) B where B.id < 20;這種方式對于mysql也是適用的,因此不能說是Oracle獨有的方式
談一談存儲過程?
存儲過程是一組為了完成特定功能的SQL語句集,其語法如下:
CREATE PROCEDURE 過程名([[IN|OUT|INOUT] 參數名 數據類型[IN|OUT|INOUT] 參數名 數據類型…]]) [特性 ...] 過程體
一個存儲過程如下
DELIMITER //
CREATE PROCEDURE myproc(OUT s int)
BEGIN
SELECT COUNT(*) INTO s FROM students;
END
//
DELIMITER ;
其中類型參數有多個取值。其取值說明如下:
. LANGUAGE SQL:說明routine_body部分是由SQL語言的語句組成,這也是數據庫系統默認的語言。
. [NOT] DETERMINISTIC:指明存儲過程的執行結果是否是確定的。DETERMINISTIC表示結果是確定的。每次執行存儲過程時,相同的輸入會得到相同的輸出。NOT DETERMINISTIC表示結果是非確定的,相同的輸入可能得到不同的輸出。默認情況下,結果是非確定的。
. { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }:指明子程序使用SQL語句的限制。CONTAINS SQL表示子程序包含SQL語句,但不包含讀或寫數據的語句;NO SQL表示子程序中不包含SQL語句;READS SQL DATA表示子程序中包含讀數據的語句;MODIFIES SQL DATA表示子程序中包含寫數據的語句。默認情況下,系統會指定為CONTAINS SQL。
. SQL SECURITY { DEFINER | INVOKER }:指明誰有權限來執行。DEFINER表示只有定義者自己才能夠執行;INVOKER表示調用者可以執行。默認情況下,系統指定的權限是DEFINER。
. COMMENT 'string':注釋信息。
舉個例子,如下
mysql> DELIMITER &&//將&&改為默認的換行
mysql> create procedure SearchUser
-> (IN user_id INT,OUT count INT)
-> READS SQL DATA
-> BEGIN
-> select count(*) into count from user
-> where id=user_id;
-> END &&
mysql>DELIMITER ;//把默認的換行再改成';'
mysql>call SearchUser(1,@count);//調用存儲過程
mysql>select @count;//顯示返回的結果
存儲函數的格式如下
CREATE FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
舉個例子
mysql> delimiter &&
mysql> create function select_name(age int)
-> returns varchar(20)
-> begin
-> return(select userName
-> from user
-> where userAge=age);
-> end &&
mysql>delimiter ;
mysql>select select_name(42);
Inner Join和Outer Join的區別
Inner Join:取兩個表查詢結果的交集
Outer Join:
1. Left Join:以左邊的表為全集,右邊的表中匹配則有值,不匹配則沒有值
2. Right Join:以右邊的表為全集,左邊的表中匹配則有值,不匹配則沒有值
3. Full Join:取左右兩邊的表的并集,兩個表都是匹配則有值,不匹配則沒有值
描述一下Oracle的體系結構
組成:數據庫、表空間、邏輯對象、數據段,數據區間和數據塊
描述一下數據庫結構化和非結構化數據
結構化數據:可以被數據或統一的結構表示的數據,像數字和符號
非結構化數據:不能被統一的結構表示的數據,像所有格式的文檔,XML,HTML和圖像和音視頻等。
描述一下SQL是如何注入的?
如何保證數據庫的安全讀寫?
什么是數據庫的死鎖,哪些情形會發生死鎖,如何避免數據庫的死鎖
數據庫死鎖
:當多個線程同時訪問數據庫的情況下,比如兩個線程,假如需要獲取兩個資源才能訪問數據庫的話,假設這兩個資源分別為A和B,假如一個線程獲取A,另一個線程獲取B,那么在這種情況下會出現數據庫無法訪問的情形。
發生死鎖的必要條件
1. 互斥條件:對數據庫的寫是獨占式的,就是說一個線程在對數據庫的寫操作的過程中必須排斥其它線程對數據庫
的任何讀寫行為
2. 請求和保持條件:一個線程獲得了訪問數據庫所必須的一個資源,但是有需要獲得其它資源才能訪問,而其它資
源又被其它線程所擁有,那么此時可能(在其它資源不是互斥資源的情況下,互斥條件一定會死鎖)會發生死鎖
3. 不剝奪條件:線程在獲取資源的情況下,它的資源不會被剝奪
4. 環路等待條件:指一個獲取資源的線程集合中前一個線程依次獲取下一個線程所需的資源,這種情況會發生死鎖
避免數據庫的死鎖
一般來說破壞死鎖形成的必要條件之一就可以避免死鎖,從發生死鎖的必要條件來說說
1. 破壞互斥條件:在數據庫的操作中,假如我們對數據庫的修改是任何時刻任意修改的,那么就沒有必要加鎖來
進行訪問了,于是此時就永遠不會發生死鎖,但是這樣訪問數據的行為非常危險,所以這種做法不可取
2. 破壞請求和保持條件:也就是說我們可以將鎖的個數減為1,那么就可以確保一個時間只有一個線程在訪問鎖,
也可以使用銀行家算法來設計資源的數量來避免死鎖,這種方法是可行的,而且我覺得可以適應更復雜的情形
3. 不剝奪條件:就是說我們可以控制獲取鎖的時間限制,等到一定時間必須釋放鎖,這個條件也不錯,但是有風險
假如一個操作所花的時間過長,而設的時間又比較短,那么就可能出現事務的執行經常會失敗的情形
4. 環路等待條件:比如一個集合{p0,p1...p5},其中p0獲取p1所需要的資源,p1獲取p2所需要的資源,以此類
推,那么假如我們修改其中一個獲取資源的方式,比如把p0改為獲取p5所需的資源,那么此時p5就會和p0同時獲取
同一個資源,但是其它線程總能因為存在一個資源必然能夠獲得而不會發生阻塞
描述一下數據庫連接池
數據庫連接池是通過對數據庫連接復用的方式來提升數據庫訪問效率的一種技術,假如我們需要使用一個連接池來提高我們的多線程程序訪問數據庫的效率,我們可以這樣來設計我們的數據庫連接池:
- 創建一個包含一個上限值為特定大小數據庫連接的連接池,這個連接池的實現可以通過用一個隊列來管理要打開的連接。對于連接池中的連接,我們可以通過延遲創建連接的方式來進行管理,即我們先遍歷連接池中的連接,假如連接是空閑的,我們不用創建連接,而是直接使用連接;假如所有連接都被使用,那么我們看連接池中的連接數是否達到我們設定的上限,沒有設定上限的話,我們就創建一個連接并使用,到達上限的話,那么請求就會被阻塞直到有數據庫連接被空閑下來
- 連接可以通過動態代理來進行創建
Java基礎
類的加載過程
假如我們在加載一個類,我們首先使用的是ExtensionClassLoader類加載器來加載類,當這個類加載器沒有該類的class字節碼時,則采用SystemClassLoader來加載,如果這個類加載器也沒有這個class字節碼,則給ClassLoader(應用類加載器)進行加載,如果加載失敗,則拋出ClassNotFoundException
利用ArrayList實現棧和隊列效果
棧:一個簡單的做法就是在push的時候直接添加ArrayList.add(e),在出棧的時候獲取并移除最后一個元素ArrayList.remove(list.length() - 1);
隊列:一個簡單的做法就是在push的時候直接添加ArrayList.add(e),在出棧的時候獲取并移除第一個元素ArrayList.remove(0);
Vector和ArrayList的區別
1、Vector是線程安全的,ArrayList是線程非安全的,因此Vector的性能會更好
2、當Vector的容量超出其初始容量時,它所要求的空間會拓展至原來的一倍,而ArrayList只會拓展為原來的50%,因此ArrayList會更節省空間
說一說常用的并發工具類
CountDownLatch:用來同步一個或多個任務,讓它們等待由其它任務執行的一組操作完成
CyclicBarrier:創建一個任務,它們并行地執行工作,然后在進行下一個步驟之前等待
DelayQueue:其中的對象只能在其到期時才能從隊列中取走
ReetrantLock:重入鎖,允許對一個鎖的再次獲取
ReetrantReadWriteLock:讀寫鎖,允許多個線程讀,只允許一個線程寫
Semaphore:信號量,允許多個線程同時訪問一個資源
HashMap和TreeMap的區別
HashMap:遍歷時的順序是無序的,但是性能要更高
TreeMap:元素是按照自然排序獲取自定義排序的方式進行存儲的,因此遍歷的時候會得到一個有序的結果
Java 的annotation的作用
注解為我們代碼中添加信息提供了一種形式化的方法,一般有下面幾個作用
1. 生成文檔,
2. 跟蹤代碼的依賴性,實現替代配置文件功能
3. 在編譯時進行格式檢查,比如@Override,我們可以確定我們要覆寫的方法名是正確的而不是一個新的方法
下面是一個自定義的注解,源碼來自《Java編程思想》
UseCase.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
public int id();
public String description() default "no description";
}
PasswordUtil.java
public class PasswordUtil {
@UseCase(id = 47,description = "Passwords must contain at one numberic")
public boolean validatePassword(String password){
return password.matches("\\w*\\d\\w*");
}
@UseCase(id = 48)
public String encryptPassword(String password){
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49,description = "New password can't equal previousli used one")
public boolean checkForNewPassword(List<String> prepassword, String password){
return !prepassword.contains(password);
}
}
描述一下MVC模式
即模型-視圖-控制器模式,如下
模型:應用對象
視圖:數據的展示
控制器:邏輯處理
大型web系統這樣的架構有利于功能上的解耦和代碼維護
什么是同步調用?什么是異步調用?什么是回調?Java如何將異步調用轉化為同步調用?
同步調用:阻塞式調用,調用方需要等待被調用方執行完才返回
異步調用:一種類似消息和通知的機制,接口的服務在接收到通知之后,會主動通知客戶方。
回調:被調用方在接口被調用時也會調用調用方的接口
異步調用轉同步調用:可以使用互斥鎖的機制來實現異步調用轉化為同步調用,在異步調用中,不同的調用線程沒有相互的聯系,我們可以加入一個競爭鎖的方式使得其他調用必須等到當前的調用完成方才繼續,這是我的一個想法。
如何優化Java程序的性能,請詳細地說一下
. 盡量將類的描述符為final,這樣Java編譯器會尋找機會內聯(不是特別明白)
. 盡量重用對象,這是因為對象在沒有被引用的時候會出發虛擬機的垃圾回收機制
. 盡量使用局部變量,因為局部變量存儲在棧中,棧的請求比堆的請求更有效率
. 不要重復初始化變量
. 在Java + Oracle的過程中,SQL盡量大寫,這樣可以省去Oracle的解析時間
. Java在進行數據庫的操作過程中,對I/O的操作是應該盡量在使用完之后關閉流
. 對象在使用完之后,盡量將其設為null,以免內存泄漏
. 減少變量的計算
. 采用懶加載機制
. 不要用異常,盡量使用try-catch,因為異常會創建一個異常對象
. StringBuffer在使用時應盡量設置一個合理的大小,因為在長度不足而進行拓展的時候,拓展的長度是所需長度的2倍再加上2個字節
. 復制大量數據時,采用System.copy()
. 盡量用移位代替乘法
. JSP中關閉無用的會話
. 不要將數組設為public static final,因為這會占用棧中的空間
. HashMap使用entry進行遍歷而不是先獲取Key的Set,再對Set中的成員使用get方法
. 盡量使用StringBuilder和ArrayList,而不是StringTable和Vector,后者是線程安全的,但是會影響效率
. 在for循環的外部使用try-catch,而不是在循環內部使用
. 利用設計模式重構,增加拓展性和可讀性
cookie和session的區別
1、cookie數據放在客戶端瀏覽器上,session數據放在服務器上
2、cookie不是很安全,session的安全性更高
3、session會保留在你的機器中一段時間,訪問增多會占用更多的機器性能,考慮性能的時候應盡量使用cookie
4、單個cookie的大小不能超過4K,很多瀏覽器規定cookie的個數不能超過20個
5、建議:
登錄信息保留在session中
其它信息如需保留可以放在cookie中
forward和redirect的區別
1. 地址欄:forward因為是在服務器中請求數據,而根本沒有通知瀏覽器,所以瀏覽器不知道里面發生了什么變化,所以地址欄不變,redirect則是給瀏覽器發送一條返回碼,讓瀏覽器跳轉到服務器對應的地址上
2. 數據共享:因為forward是在服務端做的操作,所以request的數據是可以進行共享的,而redirect則是兩次請求,則其的request數據不可共享
3. 用的場景:forward一般在登錄時根據表單跳轉到對應的界面上,而redirect則是在系統退出登錄時跳轉到主界面上
4. 效率上:forward效率高,redirect效率較低
描述一下Java的代理模式和反射機制,說說那些框架上用到了這些
代理模式:簡單的來說就是通過調用代理類中和目標類中相同的方法來實現有條件的調用目標類中的對應的方法,代理模式包括靜態代理,動態代理以及Spring中的Cglib代理模式
反射機制:程序通過在運行中,可以得到任意類中的信息,包括類中的方法和屬性
Spring框架中的IoC機制用到了反射機制
hibernate中的sql的拼裝也使用到了Java的反射機制
說一說Servlet生命周期的具體情況
初始化階段:
1. Servlet容器把Servlet類加載到內存中去
2. Servlet容器創建一個ServletConfig對象,用來設置運行時的初始化配置,并將ServletConfig對象和
當前的ServletContext對象相關聯
3. 創建一個Servlet對象
4. 調用Servlet的初始化方法init(ServletConfig config)
處理請求階段:初始化階段完成之后,Servlet容器會處理請求,在處理請求的時候,會有這樣的操作
1. 會創建ServletRequest和ServletRespose對象,這兩個對象分別是來處理客戶端提出的請求和返回給客
戶端的響應的對象
2. 調用Servlet的service方法,這個上面我們創建的ServletRequest和ServletRespose對象是這個方法
的參數,客戶端的請求最終都會被這個方法進行處理
結束階段:當我們退出服務器程序的時候,Servlet容器的做法是這樣的
1. 首先Servlet容器會調用destroy()方法,一般來說,這個方法是Servlet編程人員所要進行編寫考慮的,
在這個方法中,我們可以實現對文件流的關閉以及其它的一些回收處理
2. Servlet會回收Servlet對象,然后回收ServletConfig對象
上面的初始化和銷毀在一個Servlet的生命周期中只做一次,那么我們修改配置的時候,我們可能需要重啟Servlet才可生效
描述一下文件的IO操作和NIO的操作的區別
- 處理對象:IO的處理對象是面向流的,也就是說我們會把文件按照字節一個一個或者一次多個地去讀取或者寫入處理,這個過程是順序的,假如我們需要對某些數據進行特殊處理,那么我們得為這些數據建立緩存,但是NIO對數據的處理是可以回退操作的
- 是否阻塞:IO的處理是阻塞的,也就是說在讀取數據文件的時候其它的操作會被阻塞,NIO是非阻塞的,在進行文件操作的時候可以進行其它的操作
- 數據處理:我們可以使用IO流的裝飾器來對數據進行一些特定規則的處理,比如文件按行讀取,但是由于NIO的設計是面向緩沖器的,所以NIO的數據處理并不是非常的靈活
- 文件處理速度:NIO的處理的速度更加快,而且我們可以針對不同文件的處理設計不同大小的緩沖器,這樣就可以獲取最高的效率,也更加靈活
- 處理場景:傳統的IO在一個文件的讀寫中的流程都是這樣的:打開文件流->讀寫文件流->關閉文件流,這種方式在文件小、高并發的場景中不適用,此時使用NIO就可以進行更好的操作,因為NIO中有Selector這個組件,它可以管理多個處理通道
描述一下計算機的層次結構
7層模型:從下到上:物理層,數據鏈路層,網絡層,傳輸層,會話層,表示層,應用層(OSI參考模型)
TCP/IP模型:從下到上:網絡接入層,網際互聯層,傳輸層,應用層
描述一下Java內存回收的時機?
Java內存回收是在Java虛擬機中自動進行的,Java虛擬機把堆內存分為年輕代和老年代,年輕代又可以被分為Eden區和Survivor區,我們在創建Java對象的時候虛擬機一般會先在Eden區進行內存的分配,當Eden區分配完了之后,就意味著Eden區需要對內存進行重整,這個時候會把清理之后還存活的對象放到Survivor區,這個回收被稱為Minor GC,在一定周期數量的Minor GC完成之后或者Survivor區里面的內存被用完之后,會觸發將年輕代中保存下來的對象放到老年代中,這被稱為Major GC,調整堆中年輕代和老年代的區域比可以對Java程序的運行性能進行改善,還有一種是Full GC的模式,這種模式會啟動Minor GC和Major GC,一般不要使用這種方式,這會很消耗系統的性能。我們可以調用System.gc()來通知虛擬機進行垃圾回收,但是這個操作并不是強制性的而是對Java虛擬機的一個建議,因此存在這個建議不一定會被java虛擬機接受,這就與Java虛擬機的具體實現有關了。
描述一下負載均衡,以及負載均衡的幾種策略
負載均衡:是將客戶端的請求分攤到多個操作單元上去進行,這個操作的關鍵在于操作是否是均勻的
負載均衡的方法:服務器的負載均衡分為幾個方面的負載均衡,對于不同的負載均衡有不同的解決方法:
客戶端->反向代理層:在將域名解析為ip的過程中,我們可以將所有請求解析為數量大致相等的ip,這些ip是反向
代理服務器的ip,這種方法為DNS輪詢。
反向代理層->站點層:反向代理服務器將請求均勻的分布到處理具體請求的服務器中去,最簡單的是通過服務器的
ip進行DNS的輪詢的方式來分布上去,也就是說每個服務器的處理請求數量是一致的,但是由于不同的服務器處理能
力不同,因此可以采用最少路由的方式進行處理,這更加符合效率最優的原則
站點層->服務層:在一臺服務器上可能會開啟多個處理請求的線程,那么充分利用服務器的多線程優勢就成為了解決
這個問題的關鍵,這個時候我們可以創建服務連接池的方式來實現這個方法
服務層->數據層:在線程訪問數據庫的時候如何做到提供數據訪問的服務器的滿載是問題的關鍵,可以采用兩種方式
1. 將數據切分為多個大小相等的數據庫,劃分依據為id(或者其它可以劃分不同數據的鍵值)的范圍值
2. 將數據的狀況進行劃分,比如說按存儲的數據記錄的id的hash值
描述一下http和https的區別
https是基于SSL加密協議的HTTP協議,區別在于HTTPS多了一個建立安全連接的步驟,這是怎么回事呢?我們看一下HTTPS建立連接的過程
1. 客戶端向服務器發送HTTPS的請求,希望請求服務器與自己進行連接
2. 服務器對這種請求一般不會拒絕,一般這時候會把客戶端請求的網站中的證書給發回客戶端
3. 客戶端收到證書之后,會拿到證書里面的公鑰
4. 此時客戶端會生成一個會話密鑰,這個密鑰一般是對稱密鑰,生成之后,對會話密鑰將使用從證書中拿到的公鑰
進行加密,并將加密的結果傳給服務器
5. 服務器利用與證書中相對應的私鑰進行解密,這會拿到客戶端生成的會話密鑰了
6. 做完這一切之后,客戶端向服務器發送HTTP請求,那么問題來了,這與一般的HTTP請求有何區別,區別就是
HTTP請求的報文會用會話密鑰進行加密
描述一下安全證書
安全證書是認證網站的發布者是不是一個合法的網址的一個證明文件,一個證書文件包括了一下的一些信息
1. 證書的頒布機構
2. 證書的有效期
3. 證書的所有者(一般為網址)
4. 證書中的公鑰
5. 簽名所使用的算法
6. 指紋及指紋算法
在HTTPS的通信過程中,客戶端可以使用證書來認證一個網站是不是合法的,同時可以使用證書中的公鑰來加密發送給服務器的會話密鑰
說一說JSP的內置對象
request:請求對象
response:響應對象
pageContext:頁面上下文對象
Application:應用程序對象
session:會話對象
out:輸出對象
config:配置對象
page:頁面對象
exception:例外對象
描述一下多態的兩個特征,重載和重寫
重載:是針對同一種方法名的,不同方法參數和方法返回值的多個同名方法的實現,任何同名方法,只要它們的方法參數和返回值中有任意一個不一樣,那么我們就說這實現了類的多態,重載是動態綁定的一種,即在運行時自動尋找匹配的方法
重寫:需要對父類的方法進行覆寫的一種做法,
描述一下OOP和AOP的區別
OOP:是針對業務處理過程中實例的屬性和行為進行抽象封裝,已獲得更加清晰高效地邏輯單元
AOP:從業務邏輯的角度對系統的業務模塊進行分離從而達到統一管理的目的,比如日志管理,性能分析這些操作我們可以將這些大多數類的操作從類中進行抽離
區別就是:OOP針對的實體的抽象和封裝,AOP這是對相同的業務邏輯進行分離
談一談Executor框架的實現原理?
談一談synchronize的幾種方法的區別
1. 修飾普通方法:對當前的類實例進行加鎖,其它線程不能訪問該對象
2. 修飾靜態方法:對當前的類進行加鎖,其它線程不能訪問該類的所有對象
3. 修飾代碼塊:對同步的塊里面的對象進行線程獨享,其它的線程不能訪問同步塊中的對象或類對象
設計模式和框架
描述一下B/S架構和C/S架構的各自的特點和優缺點
硬件環境:C/S一般建立在專用的網絡上,B/S一般建立在廣域網的基礎之上,只需要操作系統和瀏覽器就行
程序架構和安全:C/S更加注重流程,可以定制很多安全措施,而B/S需要考慮安全性和效率,那么B/S則需要建立在更加優化的基礎之上
系統的維護性:C/S架構的系統的維護需要從多方面進行考慮,維護時需要考慮操作系統的變更,B/S因為是基于瀏覽器構建的,那么我們可以根據瀏覽器的部分變更來進行維護即可,可以在升級時實現無縫對接
處理的問題:C/S是面向固定的客戶群的,操作系統相關,安全性能高,而B/S架構是面向未知的用戶群的,因此,與操作系統關系不大
說一下Spring中的bean的生命周期
1. 尋找所有bean,并根據bean定義的信息實例化bean
2. 使用依賴注入,按bean所定義的配置初始化bean的屬性
3. 若bean實現了BeanNameAware接口,那么工廠調用bean的setBeanName()方法,傳遞bean的id
4. 若bean實現了BeanFactoryAware接口,bean調用setBeanFactory(),調用工廠自身
5. 若bean實現了ApplicationContextAware接口,那么setApplicationContext()方法被調用
6. 若BeanPostProcessor和bean關聯,那么它的postProcessBeforeInitialization()方法被調用
7. 若bean指定了init-method="init",那么它將被調用
8. 若BeanPostProcessor和bean關聯,那么它的postProcessAfterInitialization()將被調用
9. 若bean實現了DisposableBean接口,那么destroy()將被調用
10. 如果指定了destroy-method="close",那么這個方法將被調用
Spring中有哪兩種事務
事務是一系列具有acid特征的操作集合,其中acid特征如下
原子性:事務要求操作的原子性,要么操作都成功,要么操作都失敗
一致性:事務操作的結果對整個業務模型必須是一致的,不能對一部分業務成功,一部分業務失敗
隔離性:可能有許多事務同時處理相同的數據,那么每個事務都要與其它事務隔離開來
持久性:假如事務完成,無論發生什么樣的狀況,它的結果都不應該受到影響
spring并不管理事務,而是提供多種事務管理器來管理事務,spring的事務類型有
. JDBC事務
. Hibernate事務
. JPA事務
. JTA事務
談一談Spring的IoC原理
假如兩個類有相互依賴的行為,比如在一個類中創建另一個類的對象,這樣的做法會使系統的耦合度增高,IoC會將思想是將將有相互依賴關系的對象通過spring容器進行構建,簡單來說,就是對象原來之間相互控制,現在都交由spring容器控制,這就是控制反轉
談一談Spring中的AOP原理
AoP是將許多業務處理過程中的關注點進行分離的一種技術,這種技術可以防止耦合的發生
描述一下訪問者模式
表示一個作用于某對象結構中的各元素的操作。它使你可以在不改變各元素類的前提下定義作用于這些元素的新操作看下面的代碼
package com.fan;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
abstract class Element{
public abstract void accept(IVistor vistor);
public abstract void doSomething();
}
interface IVistor{
public void visit(Element element);
}
class Vistor implements IVistor{
@Override
public void visit(Element element) {
element.doSomething();
}
}
class ConcreateElement1 extends Element{
@Override
public void accept(IVistor vistor) {
vistor.visit(this);
}
@Override
public void doSomething() {
System.out.println("這是元素1");
}
}
class ConcreateElement2 extends Element{
@Override
public void accept(IVistor vistor) {
vistor.visit(this);
}
@Override
public void doSomething() {
System.out.println("這是元素2");
}
}
class ObjectStructure{
public static List<Element> getList(){
List<Element> list = new ArrayList<Element>();
Random rand = new Random(34);
for(int i = 0;i < 10;i++){
int a = rand.nextInt(100);
if(a > 50)
list.add(new ConcreateElement1());
else
list.add(new ConcreateElement2());
}
return list;
}
}
class Client{
public static void test(){
List<Element> list = ObjectStructure.getList();
for(Element e : list)
e.accept(new Vistor());
}
}
/**
* Created by Alpha on 17/4/18.
*/
public class VistorMode {
public static void TestVistorMode(){
Client.test();
}
}
我們可以看到,在我們的客戶端的代碼不變的前提下,我們可以通過增加元素接口的實現來達添加新的功能,這樣有利于我們面向接口進行編程,如上的代碼,我們的客戶端的訪問邏輯完全植根于對Element接口而絲毫與Element的實現沒有關系,Element中的方法也只與IVistor中定義的接口有關,這樣就實現了業務邏輯框架和業務實現相分離的結果
說一說依賴注入
描述一下觀察者模式
描述一下紅黑樹和B+樹之間的區別,最好說一下具體的實現?
JavaScript
HTTP
說一說一個http的請求過程
- 連接
由于瀏覽器需要和服務器之間建立Socket的連接,那么必須獲取服務器的ip地址和port,通過DNS對請求的地
址進行解析,我們就可以得到服務器的ip和端口號,DNS是樹狀結構的,因此會在解析不出域名的情況下會一直
往上提交直到根部
- 請求
連接成功后往服務器發送請求,請求一般是GET、POST或者HEAD類型,后面接上服務器上的文件地址和協議
- 應答
服務器會根據請求的信息返回一個包含http頭部和http體的信息給請求者,應答的組成具體如下
http頭部
HTTP 1.0 200 ok:這是返回的請求碼
MIME_Version:1.0:這是一個MIME的版本
content_type:返回的內容類型,一般為text/html
content_length:返回的內容長度
http內容體
經過編碼的數據
- 關閉連接
響應完瀏覽器的請求之后,服務器會關閉與瀏覽器之間的連接
描述一下HTTP返回值為301和302之間的區別?
Servlet是單例模式生成的嗎?
是的,但是是有條件的,只有相同業務的請求是單例模式的,而不同業務的請求則不是單例模式
代碼坑
下面這個代碼的輸出是什么
/**
* 測試HashMap的輸出問題
*/
public static void TestHashMapOutput(){
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1,"Out1");
map.put(3,"Out3");
map.put(2,"Out2");
map.put(4,"Out4");
List<String> list = new ArrayList<String>();
for(Map.Entry<Integer,String> entry : map.entrySet()){
list.add(entry.getValue());
}
System.out.println(list);
}
輸出結果如下
原因:當我們把元素存入HashMap之中時,存入的鍵值對會以
鍵
的hash的方式進行順序排列,當我們通過迭代器來獲取Map里面的值的時候,我們會得到根據鍵的hash排列結果,而一個int的整數的hash值為自身,所以鍵值會從小到大排列,置于為什么輸出的結果是[Out1,Out2,Out3,Out4]
,可以去查看List的toString方法
下面這個代碼有什么問題
public static void findCodeError(){
float pi = 3.14,double r;
int i = 0;
List<double> all = null;
while (i < 3){
System.out.println("輸入圓的半徑:");
Scanner scanner = new Scanner(System.in);
r = scanner.nextDouble();
double S = pi * r * r;
all.put(S);
}
Collections.sort(all, new Comparator() {
public int compare(Double a, Double b) {
return (int)a - b;
}
});
System.out.println("面積從大到小依次為:");
for (double one: all
) {
System.out.println(one);
}
}
問題
1. 3.14默認為double型的,float不能初始化為一個double,要么強制轉化,要么寫為`float pi = 3.14f`
2. double型和float變量的定義不能用一個‘,’進行分隔
3. List的泛型不能是基本類型,只能是類,可以改為List<Double>
4. 循環是一個死循環,無法跳出
5. List對象沒有初始化就賦值,而且賦值的方式不對,應該為list.add(S)
6. sort方法里面的Comparator內部類實現沒有用泛型,也沒有指定具體的泛型
7. compare的返回有問題,可以改為 return a > b? 1:-1;
8. 面積從大到小錯誤,sort是從小到大的,當然這取決于compare的具體實現
算法
談一下