Java面向對象的特徵有哪些方面?
- 抽象
忽略與主題無關的其他信息
- 繼承
extends的意思是擴展,即子類是父類的擴展。擴展的意義在於不需要重新造輪子,這也是繼承的意義。
- 子類繼承父類就可以得到父類的全部屬性和方法,除了私有的屬性和方法以及父類的構造方法。
- Java 只允許單繼承,目的是不想使得繼承鏈過於複雜而導致系統難以維護。如果想”多繼承”,可以使用接口代爲實現。
- 如果沒有指定父類,那麼它的直接父類是java.lang.Object
- 繼承會導致重寫,子類有時會需要重寫父類的方法,自身的行爲替換父類的行爲。方法的重寫是實現多態的必要條件。重寫需要符合三個條件
方法名、形參列表相同。
返回值類型和異常類型,子類需要小於父類。
訪問權限,子類需要大於父類。
- 封裝
封裝的意思是想讓你看到的就讓你看到,不想讓你看見的就隱藏起來。優點是高內聚低耦合。
高內聚:封裝細節,保證安全。
低耦合:簡化使用,便於擴展。
Java使用訪問修飾符實現封裝
private:私有的,只能在同一個類中使用
default:默認的,可以在同一個包中使用
protected:受保護的,可以由子類使用
public:公開的,可以被所有的類訪問
一般使用peivate修飾屬性,但提供get/set方法供外界訪問。
- 多態
同一個方法調用,不同的對象可能會有不同的行爲。例如同樣是十一假期,有人會出去旅遊,有人會宅在家裏。
多態是方法的多態不是屬性的多態。
多態的必要條件,繼承、方法重寫、父類引用指向子類對象。
java.util.comparator 接口是幹什麼?
如果我們需要控制某個類的次序並且這個類本身不支持排序,那麼就可以建立一個類比較器來進行排序。
類比較器的只需要實現 java.util.Comparator 接口並實現對應的方法就可以了~
String類爲什麼使用final修飾 ?
- 如果String類可以被修改的話,那麼就很難做到修改A中的String值的同時而不修改B中的String值。原因是String的兩個引用有可能會來自同一塊內存地址。
- final修飾符是線程安全的,在創建後就不能修改,因此不會產生線程安全問題,所以可以在多線程環境下使用。
- 在現實生活中,如密碼,如手機號碼,如文件名稱之類都是以String類型的方式存儲,如果String不使用final修飾,那麼就有可能賬號和密碼會被人不知不覺地修改了。Interger等包裝類型也是同樣的道理。而用於精確計算的BigDecimel類則不同,它主要是爲了解決二進制不能很好的表示浮點數而設計的。
- String類還是用了Static修飾符修飾,目的是方便調用。如果不使用Static,那麼就不能在靜態方法下直接調用String的方法或值,而必須使用new進行實例化。
- String常量池的存儲位置
在JDK1.6中 String存儲在jvm的PermGen中,並且限制大小。同時該區域還用於存儲類信息。
在JDK1.7中 String存儲在heap 中, 大小無限制。
序列化是什麼和什麼時候使用序列化?
序列化在Java中一般指對象的序列化。具體含義是將對象轉換爲字節序列即二進制的過程。在這個過程中不僅僅保留當前對象的數據,而且會以遞歸地形式保存引用的每個對象的數據。利用對象序列化可以實現對對象的”深複製”。即複製對象本身以及引用的對象本身。序列化一個對象就可以得到整個對象序列。而反序列化則是將字節序列恢復爲對象的過程。
pojo implements Serializable
序列化通常有兩種用途,都是將對象寫入字節流中。
當需要把對象的字節序列永久的保存到硬盤上時,一般以文件的形式。
字節序列需要在網絡上傳輸時。P:選擇一種高效的序列化和反序列化方式可以提升整個架構的性能。
Java虛擬機內存中的堆區(heap),棧區(stack),和靜態區(static/method)。
- 堆區
存儲真正的對象,每個對象都包含一個與之對應的class信息,這個class信息的作用是得到操作指令。
特點:只有一個堆區,隨着JVM的啓動而創建,所有線程共享區域,因此不存放基本類型和對象引用,只存放對象本身。如果堆內存剩餘的空間不足以創建對象,JVM會拋出OutOfMemoryError的錯誤。P:堆一般由程序員分配。
- 棧區
存放基本數據類型和引用數據類型。P:Strtus中的壓棧出棧操作
特點:每個線程包含一個棧區,並且棧中的數據都是私有的。棧中的數據由編譯器自動分配釋放。當一段代碼定義一個變量時,JVM就爲該變量在棧中開闢空間,當該變量退出作用域時,JVM就釋放該空間,這個空間就隨時可以被其他變量使用。P:棧分爲三個部分。基本類型變量區、執行環境上下文、操作指令區。
- 靜態區
又名方法區,是所有線程共享的區域,存放所有的類信息(類名,類方法信息,類字段信息)和static修飾的變量。
特點:整個程序中永遠唯一的元素。全局變量和靜態變量都放一起。不同的是,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和靜態變量在相鄰的另一塊區域。
HashMap和HashTable的區別?
HashMap是HashTable的輕量級的非線程安全的實現。它們都完成了Map接口。採用的算法hash/rehash都大概一樣,底層也都是採用數據加鏈表結構實現,所以性能上不會有很大差異。除了HashMap和HashTable,Hash集合中還有一個HashSet,它裏面存儲的並不是key-value結構,僅僅是存儲不重複的元素,相當於簡化版的HashMap,內部採用HashMap實現,所有的value都是同一個Object,只是包含HashMap中的key而已。
- 主要的區別在於HashMap允許空鍵值,而HashTable不允許。
- 由於HashMap是非線程安全,所以效率上會高於HashTable。
- HashMap將HashTable中的contains()改成了containsValue()和containsKey()。原因大概是便於閱讀吧~
- HashMap直接繼承Map,而Hashtable繼承Dictionary。
- HashMap在多線程的環境下需要爲方法提供外同步(Collections.synchronizedMap),而HashTable不需要。
int類型和Integer類型有什麼區別?
Java提供了兩種不同的類型,數據類型和引用類型,int是Java的數據類型,Integer是Java的引用類型。不僅是int,Java爲每個基本類型都準備了對應的引用類型。它們的行爲和語義都不同。
- 大小和速度的問題
- 存儲數據結構問題
- 缺省值不同的問題
String、StringBuffer和StringBuilder的區別?
String是作爲常量在Java中被定義的,而StringBuffer是變量,意味着它可以修改對象內容。如果需要經常對一個字符串進行修改那麼就可以考慮使用StringBuffer,因爲它不會生成新的對象。
JDK1.5新增了一個可變的字符序列StringBuilder,它與StringBuilder有兼容但不同步的API,是StringBuilder的一個簡單替換,用於字符串緩衝區被單個線程使用的時候,Java建議優先使用StringBuilder,因爲在大多數實現中,它比StringBuffer速度要快,並且使用方法基本相同。
什麼是運行時異常和非運行時異常?
非運行時異常指的是編譯時異常或者說受檢異常,Java編譯器強制要求這類異常在代碼編寫的時候就處理,或拋出或捕獲。運行時異常指的是虛擬機正常運行中可能會遇到的異常。
說說你常見的異常
*Java.lang.NullPointerException
程序遇上了空指針,簡單地說就是調用了未經初始化的對象或者不存在的對象,這個錯誤經常出現在創建圖片(路徑),使用數組(初始化)中。
- java.lang.ClassNotFoundException
類不存在,注意檢查類的名稱和路徑是否正確
- java.lang.ArrayIndexOutOfBoundsException
數組越界訪問
- java.lang.IllegalArgumentException
方法參數錯誤
- java.lang.IllegalStateException
- org.springframework.web.util.NestedServletException
寫一個Singleton單例模式吧
單例模式的特點
單例模式中的類只能有一個實例
單例類必須自己創建自己的實例
單例類必須給其他對象提供實例
在計算機系統中,線程池、端口、緩存、日誌、對話框、打印機、顯卡的驅動程序常常被設計成單例模式,這主要是爲了避免發生諸如兩臺計算機同時打印時打印機該如何工作的問題,還有就是端口衝突的問題。再例如聽歌,一般音樂軟件同一時間只能播放一首歌。,雖然你有兩個耳朵,但是這不代表你開心同時聽兩首歌~
單例模式有兩種表現形式
// 第一種形式
Class Singleton {
Private Singleton(){}
Private static Singleton instance = new Singleton();
// 提供一個供外界訪問本類的靜態API
Public static Singleton getInstance(){}
}
// 第二種形式
Class Singleton {
Private static Singleton instance = null;
// 創建靜態訪問器,爲了防止兩個線程同時進行對象的創建,加上同步鎖
Public static synchronized Singleton getInstance(){
If (instance == null) {
Instance = new Singleton();
}
Return instance;
}
}
J2EE常用的設計模式,並重點說一下工廠模式。
Factory(工廠模式)
Factory Method(工廠方法模式)
Prototype(原始模型模式)
Singleton(單例模式)
Adapter(適配器模式)
Decorator(裝飾模式)
Proxy(代理模式)
Observer(觀察者模式)
工廠模式是最常用的模式之一,根據工廠模式實現的類可以根據提供的數據生成一組類中的某一個類的實例,通過這一組類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作。首先需要定義一個基類,該類的子類通過不同的方法實現了基類中的方法,然後需要定義一個工廠類,工廠類可以根據條件生成不同的子類實例。當得到子類實例侯,開發人員可以調用基類的方法而不必考慮到底返回哪一個子類的實例。
寫一個工廠模式
工廠模式是Java中最常用的模式,原理是利用Java反射機制和多態的特性。目的是讓程序更靈活,可以使項目並行開發,其中的粘合劑就是接口和配置文件。
Interface InterfaceTest{
Public void getName();
}
// 有了接口就可以根據接口
進行並行開發
// 程序員A
Class Test1 implements InterfaceTest{
Public void getName(){
System.out.println(“test1”);
}
}
// 程序員B
Class Test2 implements InterfaceTest{
Public void getName(){
System.out.println(“test2”);
}
}
// 工廠類 生產接口對象
// 在調用時得到的是接口對象,一個接口變量指向實現該接口的類對象
// 通過鍵獲取值,而不是類的全路徑
Class Factory{
Private static Properties pro = new Properties();
Static {
try {
// 加載配置文件
Pro.load(new FileInputStream(“xxx.xxx”));
} catch (Exception e) {
e.printStackTrace();
}
}
private static Factory factory = new Factory();
private Factory(){};
Public static Factory getFactory() {
return factory;
}
Public InterfaceTest getInterface() {
InterfaceTest interfaceTest = null;
try {
// 根據key,獲取value value爲類的全路徑
String classInfo = pro.getProperty(“name”);
// 利用反射生成class對象
Class c = Class.forName(classInfo);
Object obj = c.newInstance();
interfaceTest = (interfaceTest )obj;
}catch (Exception e) {
e.printStackTrace();
}
return interfaceTest ;
}
}
// 調用方法
class FactoryTest {
public static void main(String[] args) {
Factory factory = Factory.getFactory();
// 通過創建的實例調用獲得接口對象的方法獲取接口對象
InterfaceTest inter = factory.getInterface();
// 調用接口定義的方法
Inter.getName();
}
}
說出ArrayList、Vector、LinkedList的存儲性能和特性。
ArrayList和Vector都使用數組的方式存儲數據。當數組元素大於實際存儲的數據時以便增加和插入元素,它們都允許直接按序號索引元素,但是插入數據要涉及到數組元素移動等內存操作,所以索引數據塊而插入數據慢,Vector由於使用了synchronized方法保證線程安全,因此性能上較ArrayList差。
LinkedList使用雙向鏈表實現存儲,按序號索引數據需要進行前向或後向遍歷,索引速度慢。因爲插入數據時只需要記錄本項的前後項即可,插入速度較快。
Collection和Collections
Collection是集合類的上級接口,繼承於它的接口主要是Set和List。
Collections是針對集合類的一個幫助類,提供了一系列靜態API,實現了對集合的搜索、排序、線程安全化等操作。
final、finally、finalized之間的區別?
final用於聲明屬性、方法和類,分別表示該屬性不可變、該方法不可覆蓋、該類不可繼承。
- 定義變量時加上final,代表一旦被初始化後就不可改變
- 將方法聲明爲final,則說明你已經知道這個方法提供的功能已經滿足你要求不需要進行擴展,並且也不允許任何從此類繼承的類來覆寫這個方法。只能通過繼承這個方法來直接使用。
- 當final用於類的時候,代表此類在繼承樹中是一個葉子類,如果你認爲此類涉及已經很完美而不需要進行修改和擴展的時候就大膽使用它吧。
finally是異常處理結構語句的一部分,表示該代碼塊中的代碼總是執行。如果是斷電或者強行中斷程序,則該塊中的代碼纔不會被執行。
finalize是Object類中的一個方法,在垃圾回收器執行的時候會調用被回收對象的此方法,可以覆蓋此方法提供垃圾回收時其他資源的回收,例如關閉文件。
說出你知道的線程同步方法
wait()使一個線程處於等待狀態,並釋放所持有對象的lock.
sleep()使一個正在運行的線程處於睡眠狀態,是靜態方法,調用此方法要捕捉InterruptedException異常。
notify()喚醒一個處於等待狀態的線程。注意是在調用此方法時,並不能確切知道喚醒的是那個等待狀態的線程,也不是按優先級,而是由JVM確定喚醒的線程。
allnotity()喚醒所有處於等待狀態的線程,注意不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
OverLoad 和 Override的區別
方法的重載和重寫是Java多態的表現。前者是父子類之間,後者是同一個類之中。如果說子類中定義某方法與父類中的方法名稱和參數都相同的話,我們就說該方法被重寫了。如果在同一個類中定義了多個同名的方法,它們有不同的參數個數和參數類型,則稱之爲方法的重載。
interface和abstract
兩者是Java面向對象的重要實現。聲明方法的存在而不去實現它的類叫做抽象類,它的子類必須全部實現它父類的所有方法,否則它也是一個抽象類。抽象類不能有抽象構造函數和抽象靜態方法。也不能被實例化,並且它只能被public、protected修飾符修飾。
接口是抽象類的變體。在接口中所有方法都是抽象的,並且它只可以定義static final的成員變量。Instanceof運算符可以用來決定某對象的類是否實現了接口。
什麼情況下使用同步或者異步編程
如果數據將在線程間共享,正在寫的數據以後或者已經可能被另一個線程讀到,那麼這些數據就是共享數據,就必須使用同步存取。
當應用程序在對象上調用了一個需要花費很長時間來執行的方法,例如下載操作,這個時候不希望程序等待方法的返回,你在下載音樂的同時也可以聽音樂,這個時候就需要使用異步編程。類似這種場景下異步途徑往往更具有效率。
GC是什麼?爲什麼需要GC?
內存處理是程序員最容易出錯的地方,忘記或者錯誤的內存回收會導致堆棧溢出系統崩潰等嚴重錯誤。Java並沒有提供顯示的釋放已分配內存的操作方法,而是提供GC功能,用於自動檢測對象是否超出作用域從而達到自動回收內存的目的。這個功能被稱之爲垃圾回收機制。
垃圾回收的優缺點?和回收機制
優點是程序員不再需要考慮內存管理,同時垃圾回收可以有效地防止內存泄露,有效地使用可用的內存。
缺點則是垃圾回收器作爲一個單獨的低級別線程運行,在不可預知的情況下對內存已經死亡或者長時間沒有使用的對象進行清除和回收,程序員不能監測到異常情況,也不能實時調用垃圾回收器對對象進行垃圾回收。程序員可以手動調用System.gc(),但java語言規範並不保證GC一定會運行。
垃圾回收的機制有:分代複製垃圾回收、標記垃圾回收、增量垃圾回收。
啓動一個線程使用start()還是run()?
start()使線程所代表的虛擬處理機處於可運行狀態,這意味着它可以由JVM調度並執行,但並不意味着線程就會立即執行。此時線程處於就緒狀態。
run()線程體,包含要執行線程的內容,當線程得到CPU的時間片,就會開始執行相應線程的此方法。
使用start()開闢線程,使用run()執行線程。
爲什麼要有多線程?
線程的出現是爲了更好的利用CPU,提高程序運行效率。
說一說你開發過程中常見到的runtime exception
- ClassCastException
- IndexOutOfBoundsException
- NullPointerException
- SystemException
線程池的實現原理
使用線程池對線程進行統一分配,調優,監控可以降低資源消耗,提高相應速度,提高線程的可管理性。
J2EE服務器會在啓動時建立若干線程池連接,並一直維持不少於此數目的池連接,當客戶端程序需要連接時,池連接程序會返回一個未使用的池連接並標記爲忙。如果當前沒有空閒連接,池驅動程序就新建一定數量的連接,新建連接的數量由配置參數決定,當使用的池連接調用完成後,池驅動程序將池連接標記爲空閒,其他調用就可以使用這個連接。
forward和redirect
forward轉發是服務器請求資源,服務器直接訪問目標地址的URL,將URL的響應內容讀取過來,然後將這些內容再發送給瀏覽器,瀏覽器因此並不知道服務器發送的內容是從哪裏來的,所以它的地址欄還是原來的地址。
redirect重定向是服務端根據業務邏輯,發送一個驗證碼,告知瀏覽器去請求某個地址,一般來說,瀏覽器會用剛纔請求的參數重新請求,所以session、request參數都可以獲取到。
Set集合中的元素不能重複,那麼怎樣區分重複與否呢?使用==還是equals()?它們有何區別?
Set裏的元素不能重複,那麼可以使用iterator()方法區分是否重複。
equals()是判斷兩個Set是否相等。屬於深度比較,爲的是當兩個分離的對象的內容和類型相匹配的話返回ture。而”==”是比較兩個對象的引用是否指向同一個內存地址,也就是判斷對象是否分離。
hashCode()通常被設計用於提高性能。它和equals()的區別就在於當兩個對象相等(equals()),那麼它們就一定擁有同樣的哈希值,但如果兩個對象的哈希值相等,則不一定代表這兩個對象就一定相等。
Java中異常機制的簡單原理和應用
當Java程序違反了java的語義規則時,虛擬機就會將發生的錯誤表示爲一個異常。違反語義包括了兩種情況。
Java類庫內置的語義檢查。如果數組下標越界則會引發IndexOutBoundsException;訪問null對象時會引發NullPointerException。
另一種是程序員擴展的語義檢查,自定義異常並自由選擇在何時使用throw關鍵字引發異常。所有自定義的異常都必須繼承java.lang.Thowable。
XML文檔定義有幾種形式?它們有何本質區別?解析XML文檔有幾種方式?
兩種形式:dtd和schema
- dtd(data type definition)
數據類型定義,用於描述XML文檔的文檔結構,是早期的XML文檔定義形式。
- schema
本身基於XML編寫,在類型和語法的限定能力上比dtd強,處理也方便,因此正逐漸代替dtd成爲新的模式定義語言。
本質區別:schema本身是xml,可以被xml解析器解析。(這也是從DTD上發展schema的根本原因)
解析方式:DOM、SAX、STAX等
- DOM
處理大型文件時性能下降厲害,這個問題是由DOM的樹結構造成的,這種結構佔用的內存較多,而且DOM必須在解析文件之前把整個文檔都裝進內存,便於對XML的隨機訪問。
- SAX
事件驅動型的XML解析方式。它按順序讀取XML文件,而不需要一次全部裝載整個文件。當遇到像文件開頭,文檔結束或者標籤開頭與標籤結束時會觸發一個事件,用戶通過在其回調事件中寫入處理代碼來處理XML文件,適合對XML的順序訪問。
- STAX
Streaming API for XML(StAX)
Servlet
Public void init(ServletConfig config);
Public ServletConfig getServletConfig();
Public String getServletInfo();
Public void service(ServletRequest request, ServletResponse response);
Public void destroy();
- init()方法在servlet生命週期中僅執行一次,在服務器裝載servlet時執行。缺省的init()通常是符合要求的,不過也可以根據需要override,比如管理服務器端資源,一次性裝入Gif圖像,初始化數據庫連接等。由於缺省的init()中設置了servlet的初始化參數,並使用了ServletConfig對象參數來啓動配置,所以覆蓋init()時,需要調用super.init()以確保仍然執行這些任務。
- service()是servlet的核心方法,在調用service()之前,應確保已完成了init()。對於HttpServlert,每當用戶請求一個HttpServlet對象,該對象的service()就會被調用,HttpServlet缺省的service()中的服務功能就是調用與HTTP請求的方法對應的do功能,所以對於HttpServlet,一般都重寫doPost()或者doGet()。
- destroy()在servlet的生命週期中也僅執行一次,在服務器停止並卸載servlet時執行,將servlet作爲服務器進程的一部分進行關閉。缺省的destroy()是符合要求的,但也可以override。比如在卸載servlet時將統計的數字保存在文件中,比如關閉數據庫連接。
- getServletConfig()返回一個servletConfig對象。該對象用來返回初始化參數和servletContext。servletContext接口提供有關servlet的環境信息。
- getServletInfo()提供有關servlet的信息,比如作者、版本、版權。
#throw和throws
>throw語句用來明確地拋出一個異常
>throws用來表明一個成員函數可能拋出的各種異常
#排序有哪幾種方法?請列舉並口述用java實現快速排序。
>排序的方法有
* 插入排序(直接插入排序,希爾排序)
* 交換排序(冒泡排序,快速排序)
* 選擇排序(直接選擇排序,堆排序)
* 歸併排序
* 分配排序(箱排序,基數排序)
>快速排序的僞代碼
從a[0:n-1]中選擇一個元素作爲middle,該元素爲支點。
將餘下的元素分割爲left和right兩段。使left中元素都小於等於支點,而right中的元素都大於等於支點。
遞歸地使用快速排序方法對left進行排序,然後是right
所得出的結果爲left+middle+right;
#MVC的各個部分都有那些技術實現?怎麼實現的?
- Model
代表着業務邏輯,通過JavaBean實現
- View
是應用的表現層,用於用戶交互,使用JSP頁面技術實現
- Controller
應用程序處理過程控制,一般是Servlet
>優點:開發效率高;程序靈活性和擴展性好;代碼重用度高;便於人員分工。
>缺點:代碼複雜度增加;代碼數量增加;不適合開發小型程序。
#java中有幾種方法可以實現一個線程?用什麼關鍵字修飾同步方法?Stop()和suspend()爲何不推薦使用?
>有兩種方法,分別是繼承Thread類和實現Runnable接口。
>使用關鍵字synchronized修飾同步方法
>反對使用stop()是因爲不安全。它會接觸由線程獲取的所有鎖定,而且如果對象處於一種不連貫的狀態,那麼其他線程能在那種狀態下檢查和修改他們,結果很難檢查出真正的問題所在.suspend()方法容易發生死鎖。調用suspend()的時候,目標線程會停下來,但卻仍然持有在這之前獲取的鎖定,此時,其他線程都不能訪問鎖定的資源,除非被”掛起”的線程恢復運行。對任何線程來說,如果它們都想恢復目標線程,同時又試圖使用任何一個鎖定的資源,就會造成死鎖。所以不應該使用suspend(),而應在自己的Thread類中置入一個標誌,指出線程應該活動還是掛起。若標誌指出線程應該掛起,便使用wait()命令其進入等待狀態,若標誌指出線程應當恢復,則使用一個notify()重新啓動線程。
#java中有幾種類型的流?JDK爲每種類型的流提供了一些抽象類以供繼承,請說出他們分別是哪些類?
>字節流、字符流。字節流繼承InputStream OutputStream
字符流繼承於InputStreamReader OutputStreamWriter
>在java.io包中還有許多其他的流,主要是爲了提高性能和使用方便
#內部類可以引用他包含類的成員嗎?有什麼限制?
>一個內部類對象可以訪問創建它的外部類對象的內容。
>內部類如果不是static,那麼它可以訪問創建它的外部類對象的所有屬性。如果是static,即爲nested class,那麼它只可以訪問創建它的外部類對象的所有static屬性。
>一般類只有public或者package等訪問修飾符。而內部類可以實現static、protected、private等訪問修飾符。
>當從內部類繼承的時候內部類是不會被覆蓋的,它們是完全獨立的實體,每個都在自己的命名空間內,如果從內部類中明確地繼承,就可以覆蓋原來內部類的方法。
#進程和線程的區別
+ 進程
是相對於操作系統而言的。例如你一邊聽歌一邊玩遊戲,我們就會說此時系統內有兩個進程在運行,專業的話說叫做多個程序幾乎在同一時間執行多個任務。
+ 線程
相對某一程序而言。例如你一邊聽歌一邊看歌曲排行榜,還可以下載歌曲,這三件事情互相不會干擾,就可以說這一音樂程序至少有三個線程在運行,專業的表述爲一個程序在同一時間內執行多個任務。
#你能說出TCP/IP的七層協議嗎?
>應用層,、表示層、會話層、傳輸層、網絡層、數據鏈路層、物理層。
#在connection類中提供了3個控制事務的方法,說說它們。
- setAutoCommit(boolean bln)
保持數據的完整性。一個系統的更新操作可能要涉及多張表,需要多個SQL語句進行操作,循環連續的進行插入操作,如果在開始時設置了”conn.setAutoCommit(false);”,然後再進行”conn.commit()”。這樣即使插入的時候報錯,修改的內容也不會提交到數據庫,而如果沒有手動進行setAutoCommit,那麼出錯的時候就會造成,前幾條數據插入成功而後幾條數據插入失敗的情況,這就是髒數據。
- commit()
提交事務,用於把事務所做的修改都保存到數據庫中,它把上一個commit或者rollback命令之後的全部事務都保存到數據庫中。
- rollback()
撤銷事務,在事務運行的過程中發生了某種故障,事務不能繼續執行,系統將事務中對數據庫的所有已經完成的操作全部撤銷,回滾到事務開始的狀態,這裏的操作特指數據庫更新操作,並且回滾後,事務進入提交狀態,因爲回滾是回滾到事務開始時的狀態。
#EL表達式的隱含對象
* applicationScope
應用程序範圍內的scoped變量組成的集合
* cookie
所有cookie組成的集合
* header
HTTP請求頭部,字符串
* headerValues
HTTP請求頭部,字符串集合
* pageContext
當前頁面的javax.servlet.jsp.PageContext對象
* initParam
全部應用程序參數名組成的集合
* pageScope
頁面範圍內所有對象的集合
* param
所有請求參數字符串組成的集合
* paramValues
所有作爲字符串集合的請求參數
* requestScope
所有請求範圍的對象的集合
* sessionScope
所有會話範圍的對象集合
#簡述Statement和PreparedStatement的區別
>無論多少次地使用同一SQL命令PreparedStatement都只對它解析和編譯一次,當使用Statement對象時,每次執行一個SQL命令的時候都會對它解析和編譯。因此使用PreparedStatement要比使用Statement速度要快。
#java中包的用途
* 允許將類文件組織起來,便於查找合適的類
* 包可以包含其他的,形成有層次的包空間
* 包有助於避免命名衝突
#請簡述一下什麼是流?
>流是指一連串流動的字符,以先進先出的方式發送和接收數據的通道。流分爲輸入流和輸出流,相對於內存而言,數據輸入到內存就是輸入流,反之就是輸出流。
#java.io.reader和java.io.inputStream的區別?
>兩者共同組成了java輸入類。Reader用於讀入16位字符,即Unicode編碼的字符;而InputStream用於讀入ASCLL字符和二進制數據。
#請說出ArrayList和Vector的區別?
- 同步性
數組序列是線程不安全也不同步的,而矢量隊列則完全相反。
- 數據增長
需要增長時,數組序列增長原來的一半,而矢量隊列增長一倍。
#super關鍵字是幹什麼的?爲什麼使用它?它的訪問範圍?
>super用於解決如何直接訪問或初始化從父類繼承來的成員。
>在寫子類無參數構造方法時,不用顯式調用父類無參數構造方法,系統會自動提供,但在寫子類帶參數的構造方法時,應該在第一句寫super(參數)來初始化父類成員變量
>訪問範圍包括父類屬性、一般方法和構造方法
#Cookie和Session的區別與聯繫?
- Session的概念
Session 是存放在服務器端的,類似於Session結構來存放用戶數據。當瀏覽器第一次發送請求時,服務器自動生成一個Session和Session ID用來標識這個Session,並將通過相應發送到瀏覽器。當瀏覽器第二次發送請求,會將前一次服務器響應中的Session ID放在請求中一併發送到服務器上,服務器從請求中提取Session ID並和保存的所有Session ID進行對比,找到這個用戶對應的Session。一般瀏覽器提供了兩種方式來保存Session,還有一種是程序員使用HTML隱藏域的方式自定義實現。
使用Cookie實現保存,這是最常見的方法,例如”記住我的登錄狀態”這一功能就是通過這種方式實現的。服務器通過設置Cookie的方式將Session ID發送到瀏覽器。如果我們不設置過期時間,那麼Cookie將不會保存在硬盤上,會隨着瀏覽器的關閉而消失,Session ID也就不復存在了。如果我們設置這個時間爲若干天后,那麼這個Cookie會保存在客戶端硬盤中,即使瀏覽器關閉,這個值仍然存在,下次訪問相應的網站時同樣會發送到服務器上去。
使用URL附加信息的方式,這也是有時候我們會在JSP網站上看到”xxx.jsp?JSESSIONID=x”的原因。這種方式和使用Cookie的方式中不設置過期時間是一樣的。
第三種方式是通過在頁面表單裏增加隱藏域的方式,這種方式實際上跟URL附加信息的方式一樣,只不過前者使用GET方式發送數據,後者使用POST方式發送數據,但是明顯後者比前者麻煩。
- 會話技術
會話指的是用戶登錄網站後的一系列操作,例如瀏覽商品->添加到購物車->提交訂單->付款。會話跟蹤時WEB程序中常用的技術,用戶跟蹤用戶的整個會話,常用的會話跟蹤技術就是Session和Cookie。Session在服務端記錄信息並確認用戶身份,Cookie在客戶端記錄信息並確認用戶身份。
- 區別
Cookie存儲在瀏覽器客戶端,Session存儲在服務端,簡單說,當你登錄一個網站的時候,如果web服務器端使用的是session,那麼所有的數據都保存在服務器上面,客戶端每次請求服務器的時候會發送當前會話的session id,服務器根據當前session id判斷相應用戶數據標誌,以確定用戶是否登錄或者具有某種權限。由於數據時存儲在服務器上面,所以無法僞造,當時如果你能獲取某個登錄用戶的session id,用特殊的瀏覽器僞造該用戶的請求也是能夠成功的。session id 是服務器和客戶端鏈接時隨機分配的,一般來說不會重複,但如果存在大量併發的請求,也不是沒有重複的可能性。
- 聯繫
Cookie是Session的一種,但Cookie不會佔用服務器資源,是存儲在客戶端內存或者一個cookie的文本文件中;而Session則會佔用服務器資源,從這點上看,應該儘量使用Cookie而不是Session。
如果用戶禁用cookie,那麼可以採用url重寫技術來進行頁面處理,調用session中大量有用的方法從session中獲取數據後置入頁面。
- 應用場景
Cookie的典型應用時下次直接登錄和在線商城的購物車的設計,當然這其中也會有一些安全和性能的問題存在。
由於Cookie的不安全性,所以一般會在Session上保存重要的信息。
#JSP的九大內置對象及其作用
>內置對象指的是可以不加聲明和創建就可以在JSP腳本中使用的成員變量.
>產生的時機:一個JSP頁面對應一個Servlet類,每個Servlet有三個方法。init()初始化JSP;destory()銷燬JSP;service()對用戶請求產生相應的方法。Request和response是service()的形參,application、page、out、pageContext、session這些對象都是在service()中生成的實例。
* javax.servlet.httpServletRequest
客戶端的請求信息被封裝在request對象中,主要用於接受通過HTTP協議傳送到服務器的數據,包括頭信息,系統信息,請求方式以及參數等,作用域爲一次請求。
* javax.servlet.httpServletResponse
代表對客戶端的相應,主要是將JSP容器處理過的對象傳回客戶端,response對象只在JSP頁面有效。
* Session
Session是由服務器自動創建的與用戶請求相關的對象,服務器爲每個用戶都生成一個session對象,用戶保存用戶的信息,跟蹤用戶的操作狀態。它內部使用Map類來保存數據,即key/value,value可以是複雜的對象類型,而不僅僅侷限於字符串類型。
* application
Application對象可以將信息保存到服務器直到服務器關閉爲止,可以認爲是系統中的全局變量,是ServletContext類的實例。
* out
此對象用於在web瀏覽器內輸出信息,並且管理應用服務器上的輸出緩衝區,是JspWriter的實例。
* pageContext
此對象提供了對JSP頁面內所有對象以及命名空間的訪問,它的本類名也叫做pageContext。
* config
獲取服務器的配置信息
* page
它是java.lang.Object的實例,代表着JSP本身,本質上包含當前Servlet接口引用的變量,可以看做Java編程中的this
* exception
顯示異常信息,只有在包含isErrorPage=”true”的頁面上纔可以使用。JAVA程序可以使用try/catch處理異常信息,但在JSP頁面中出現異常就會生成exception對象,並把該對象傳送到在page指令中設定的錯誤頁面中,程序員在錯誤頁面中處理exception對象即可。跟JAVA一樣,JSP中exception對象也是由系統提供的繼承機構,它實際上是java.lang.Throwable的對象。
#說一說你常用的Linux命令
* ls 顯示文件或目錄
-l 列出文件詳細信息(list)
-a 列出當前目錄下所有的文件及目錄,包括隱藏文件(all)
* mkdir 創建目錄
- p 創建目錄,如果沒有父目錄則創建父目錄(parent)
* cd 切換目錄
* touch 創建空文件
* echo 創建帶有內容的文件
* cat 查看文件內容
* cp 拷貝
* mv 移動或者重命名
* rm 刪除文件
-r 遞歸刪除,刪除文件時一併刪除子目錄及文件
-f 強制刪除
* find 在文件系統中搜索某文件
* wc 統計文本中行數、字數、字符數
* grep 在文本文件中查找某個字符串
* rmdir 刪除空目錄
* tree 顯示目錄爲樹形結構,需要安裝tree包
* pwd 顯示當前目錄
* in 創建鏈接文件
* more、less 分頁顯示文本文件內容
* head、tail 顯示文件頭、尾內容
* Ctrl+alt+F1 命令行全屏模式
------系統管理命令------
* stat 顯示指定文件的詳細信息,比ls更詳細
* who 顯示在線登錄用戶
* whoami 顯示當前操作用戶
* hostname 顯示主機名
* uname 顯示系統信息
* top 動態顯示當前耗費資源最多的進程信息
* ps 顯示瞬時進程的運行狀態,ps aux:以BSD格式顯示進程;ps ef:以標準格式顯示進程
* kill 殺死進程,一般使用ps或top命令查看進程的id,然後使用kill命令殺死對應進程。
* du 查看目錄大小
-h 帶有單位地顯示目錄信息
* df 查看磁盤大小
-h 帶單位顯示磁盤信息
* ifconfig 查看網絡情況
* ping 測試網絡連通
* netstat 顯示網絡狀態信息
* man 幫助命令,使用-加上其他命令來查看對應參數的使用
* clear 清屏
* alias 對命令重命名
* sudo 允許系統管理員讓普通用戶執行一些或者root命令的工具
-b 在後臺執行指令
-h 顯示幫助
-H 將HOME環境變量設置爲新身份的HOME環境變量
-k 結束密碼的有效期,使用sudo的時候先輸入密碼,默認爲5分鐘有效期限
-l 列出目前用戶可執行與無法執行的指令
-p 改變詢問密碼的提示符號
-s<shell> 執行指定的shell
-u<用戶> 以指定的用戶作爲新的身份,若無此參數,則默認使用root爲新的身份
-v 延長密碼有效期限5分鐘
-V 顯示版本信息
------打包壓縮相關命令------
* tar 打包壓縮
-c 歸檔文件
-x 壓縮文件
-v 顯示壓縮或解壓縮過程(view)
-f 使用檔名
-z gzip壓縮文件
-j bzip2壓縮文件
例如
tar -cvf /home/java.tar home/java 只打包不壓縮
tar -zcvf /home/java.tar home/java 打包並使用gzip壓縮
------打包壓縮相關命令------
* shutdown
-r 關機重啓
-h 關機不重啓
now 立即關機
* halt 關機
* reboot 重啓
------VIM相關命令------
Vim中有三種模式,命令模式、插入模式和編輯模式,使用ESC或i或:來切換模式。命令模式下
:q 退出
:q! 強制退出
:wq 保存並退出
:set number 顯示行號
:set nonumber 隱藏行號
/java 在文檔中查找java這個字符串,按n跳下一個,shift+n上一個
yyp 複製光標所在行,並粘貼
------文件權限管理------
R 讀 數值表示爲4
W 寫 數值表示爲2
X 可執行 數值表示爲1
更改權限命令語法
sudo chmod[u=所屬用戶/g=所屬組/o=其他用戶/a所有用戶][+=增加權限/-=減少權限][r/w/x] 目錄/文件名
#Java運行時區域
程序計數器:記錄當前指令所在單元地址,也就是程序執行.class文件某行。由於存儲的只是一個行數number,所以區域較小。在多線程環境中,每個線程擁有一個程序計數器,分別記錄着各自程序的執行程序所在行數。
JVM棧:存放執行的方法,方法中的局部變量和對象的引用(對象的實例存放在堆中)。由於擁有着先進後出的特性,所以可以看做一個桶。對桶中數據的操作成爲壓棧和彈棧。
本地方法棧:存放執行的本地方法,即使用native關鍵字修飾的方法。當程序執行到本地方法時,程序計數器不會記錄,而是使用undefined替代(undefined和null的主要區別在於null表示此處不應該有值,而undefined表示此處有值但是還沒有定義)
Java堆:存放類的實例和數組。分爲新生代(young generation)和老年代(old generation),這樣劃分主要是爲了方便垃圾回收器的工作,垃圾回收器會頻繁地掃描新生代中的對象並進行垃圾回收,沒有進行垃圾回收的對象就被移到了老年代中。
P:爲了更加方便垃圾回收器的工作,JVM在新生代中又進行劃分(eden和survicor)~~
方法區:又被稱爲非堆,存放類的基本信息以及常量(final)和靜態(static)變量,這些數據被定義爲永久代,垃圾回收器不會回收這些數據。
P1:此方法區跟java中的方法沒有任何關係~
P2:JDK文檔中說方法區是JAVA堆邏輯上的一部分。
P3:在JDK8中取消了永久代而是使用了metespace替代
運行時常量池:存放字面量(例如int number = 10 中的10就是一個字面量)和符號引用(例如類與引用名的全限定名,List<Dept> deptList = new ArrayList<Dept>();中的deptList實際是一個java.lang.List)
P1:常量池的出現是爲了避免頻繁的創建和銷燬對象而影響到系統性能,其實現了對於對象的共享。例如字符串常量池就是在編譯期間就把所有字符串文字放到一個常量池中。
P2:字符串常量池在JDK7時被移到了Java堆中(String name = “老王”中name作爲引用存放到字符串常量池中,”老王”作爲實例存放到堆中)。
P3:程序計數器、JVM棧和本地方法棧是線程私有的,Java堆、方法區和運行時常量池是線程共享的,因此一個多線程程序出現BUG,那麼一定是線程共享中的區域出現了問題。
P4:Java規範中指出,除了程序計數器之外其他區域都有可能會發生OutOfMemoryError(內存溢出)異常。除了程序計數器和JVM棧其他區域都有可能會發生StackOverflowError(棧溢出)異常。使用JConsole(java自帶)可以監控當前程序的內存和系統資源使用情況。
#常用的SQL語句
# stuscore 表中數據結構
stuid
name
subject
score
1
張三
數學
91
1
張三
英語
89
1
張三
語文
87
2
李四
語文
89
2
李四
英語
28
2
李四
數學
99
3
王五
語文
66
3
王五
數學
24
3
王五
英語
32
4
朱六
數學
88
>計算每個人的總成績並排名,要求顯示字段:名字和總成績
思路:根據名字分組 group by 根據成績排名 order by
SELECT name,SUM(score)
FROM stuscore
GROUP BY name
ORDER BY SUM(score)
>計算每個人的總成績並排名,要求顯示字段:名字、學號和總成績
思路:DISTINCT 去重關鍵字 使用子查詢先查詢學號和總成績
SELECT DISTINCT t1.name, subject,MAX(score)
FROM stuscore t1,
(SELECT stuid,SUM(score) AS allscore
FROM stuscore GROUP BY stuid) t2
WHERE t1.stuid=t2.stuid
ORDER BY t2.allscore DESC
>計算每個人單科的最高成績,要求顯示字段:學號、姓名、課程、最高成績
思路:有兩種寫法
#寫法一
SELECT stuid,name,subject,MAX(score)
FROM stuscore
GROUP BY stuid
#寫法二
SELECT t1.stuid,t1.name,t1.subject,t1.score
FROM stuscore t1,
(SELECT stuid,MAX(score) AS maxscore
FROM stuscore GROUP BY stuid) t2
WHERE t1.stuid=t2.stuid AND t1.score=t2.maxscore
>計算每個人的平均成績,要求顯示字段:學號、成績、平均成績
思路:使用AVG()函數求出平均成績,然後根據name分組
SELECT stuid,name,AVG(score)
FROM stuscore
GROUP BY name
>列出各門課程成績最好的學生,要求顯示字段:學號、姓名、科目、成績
#寫法一,這種寫法會出現問題
SELECT stuid,name,subject,MAX(score)
FROM stuscore
GROUP BY subject
#寫法二
SELECT s1.stuid,s1.name,s1.subject,s2.max_score
FROM stuscore s1,
(SELECT subject,MAX(score) AS max_score
FROM stuscore GROUP BY subject) s2
WHERE s1.subject=s2.subject AND s1.score=s2.max_score
>列出成績最好的兩位學生,要求顯示字段:學號、姓名、成績
思路:使用DESC將查詢結果降序,然後使用limit截取列數
SELECT stuid,name,SUM(score) AS sumscore
FROM stuscore
GROUP BY name
ORDER BY sumscore DESC
LIMIT 2
#寫法二,在SQL Server中的top關鍵字
SELECT top 2 * FROM stuscore ORDER BY sumscore DESC
>列出數學成績最好的學生
SELECT name,subject,score
FROM stuscore
WHERE subject=’數學’
ORDER BY score DESC
>求出某位學生的數學成績排名
思路:使用rownum臨時變量輸出mysql的排序後的行號
SELECT s.rowNo
FROM (SELECT (@rowNum:=@rowNum+1) AS rowNo,name,subject,score
FROM stuscore, (SELECT (@rowNum :=0)) b WHERE subject=’數學’
ORDER BY stuscore.’score’ DESC) s
WHERE name=’張三’
>統計學科成績優、良、及格的個數
SELECT subject,
(SELECT COUNT(*) FROM stuscore WHERE score>80 AND score <=100) AS 優,
(SELECT COUNT(*) FROM stusocre WHERE score>60 AND score <80) AS 良,
(SELECT COUNT(*) FROM stuscore WHERE score<60) AS 不及格
FROM stuscore
GROUP BY subject
>使用SQL語句輸出以下格式的數據
# 學號 姓名 語文 數學 英語 總分 平均分
SELECT stuid AS 學號,name AS 姓名,
SUM(CASE WHEN subject=’語文’ THEN score ELSE 0 END) AS 語文,
SUM(CASE WHEN subject=’數學’ THEN score ELSE 0 END) AS 數學,
SUM(CASE WHEN subject=’英語’ THEN score ELSE 0 END) AS 英語,
SUM(score) AS 總分,(SUM(score)/COUNT(*)) AS 平均分
FROM stuscore
GROUP BY stuid,name
>使用一條SQL語句查詢出每門課都大於80的學生姓名
思路:使用HAVING()篩選成組後的各種數據,真實表中沒有此數據,這些數據是通過一些函數生存。需要注意的是where子句在聚合前先篩選記錄,作用於group by 和 having子句之前
SELECT name
FROM stusocre
GROUP BY name
HAVING MIN(score)>=80 AND COUNT(subject)>=3
>修改表結構,學歷
#添加列信息
Alter table stuscore add 學歷 varchar(6);
#刪除列信息
Alter table stuscore drop column 學歷;
>修改表數據 朱六
#將數學成績改爲90
UPDATE stuscore SET score=90 WHERE name LIKE ‘朱六’;
#刪除表數據似乎不能使用like進行delete
DELETE FROM stuscore WHERE stuid=4;
>連接查詢
# 當用戶查看的數據來自於多張表的時候,連接查詢會將多張表按照某個指定的條件進行記錄的拼接。最終的結果是記錄數可能會變化,但字段數一定會增加。
SELECT s1.*,s2.* FROM student AS s1 left join stuscore AS s2 ON s1.student_id = s2.stuscore_id
#能介紹一下redis嗎?在項目中你是怎麼使用它的?
>redis是最常用的非關係型數據庫(Not-Only SQL)中的鍵值存儲數據庫(key-value),除了鍵值存儲數據庫之外還有文檔型數據庫(MongoDB)、圖形(Graph)數據庫(InfoGrid)、列存儲數據庫(HBase)。 對數據庫高併發讀寫(High performance)的需求;對海量數據的高效率存儲訪問(Huge Storage)的需求;對數據庫的高可擴展性(High Scalability)和高可用性( High Availability)的需求,帶動了NOSQL的發展。
>redis是用C語言開發的一個開源的高性能鍵值對(key-value)數據庫,類似於memcached(比之出色的地方在redis的value的最大限制是1GB,而memcached只有1MB),它通過異步的方式將整個數據庫加載到內存中然後進行操作,因爲是純內存操作,所以性能很好。不僅僅是出色的性能,它還通過提供多種鍵值數據類型來適應不同場景下的存儲需求。
- 字符串類型
redis沒有采用C語言對字符串的處理方式,而是自定義了一個數據結構SDS(imple dynamic string),簡單動態字符串。打開redis源碼包,在src下的sds.h文件下查看sds的源碼如下:
struct sdshdr {
// 字符串長度
unsigned int len;
// buf數組中未使用的字節數量
unsigned int free;
// 用於保存字符串
char buf[];
};
>C語言對字符串的存儲是使用字符數組,遇到'\0'字符則認爲字符串結束,而redis的字符串可以存儲任何類型的數據,因爲任何類型數據都可以表示成二進制,sds結構中的char buf[]就是存儲了二進制數據。並且redis的字符串是二進制安全的,什麼是二進制安全?簡單理解就是存入什麼數據取出的還是什麼數據。redis中的sds不像c語言處理字符串那樣遇到'\0'字符則認證字符串結束,它不會對存儲進去的二進制數據進行處理,存入什麼數據取出還是什麼數據。
>應用:自增主鍵,例如電商項目中的訂單號和商品編號都是採用String的遞增數字特性生成,使用INCR(此命令用於將key中存儲的數字值增1)命令完成。
定義商品編號key:items:id
192.168.101.3:7003> INCR items:id
(integer) 2
192.168.101.3:7003> INCR items:id
(integer) 3
散列類型
- hash
如果有一個對象User以JSON序列化(String)的形式存儲在redis,它的存儲過程是User->JSON->Redis。該User對象有id、name、age等屬性。如果在業務中只需要修改name屬性,其他屬性不做修改時該怎麼做?如果採用傳統的存儲過程時必然會導致資源的的浪費,使用hash可以解決此問題。
hash會怎麼做?它會提供字段與字段值的映射,並且規定字段值只能是字符串不能是散列或者集合類型。使用HSET(此命令不區分插入和更新操作,當執行插入操作時HSET命令返回1,當執行更新操作時返回0)命令對字段值做修改:
HSET user name laowang
應用:商品信息,包括商品id、商品名稱、商品描述、商品庫存、商品好評
商品信息在redis中爲item:1001
獲取命令
HGET items:1001 id
"3"
HGETALL items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "999.9"
列表類型
-arrayList && linkedList
ArrayList使用數組方式存儲數據,所以根據索引查詢數據速度快,而新增或者刪除元素時需要設計到位移操作,所以比較慢。LinkedList使用雙向鏈接(AB互相指向)方式存儲數據,每個元素都記錄前後元素的指針,所以插入、刪除數據時只是更改前後元素的指針指向即可,速度非常快,然後通過下標查詢元素時需要從頭開始索引,所以比較慢,但是如果查詢前幾個元素或後幾個元素速度比較快。
-List
列表類型可以存儲一個有序的字符串列表,常用的操作是向列表兩端添加元素,或者獲得列表的某一個片段。列表類型內部是使用雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間複雜度爲0(1)(算法中的時間複雜度,它和空間複雜度被合稱爲算法的複雜度,是衡量算法重要指標),獲取越接近兩端的元素速度就越快。這意味着即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。
應用,商品評論列表
思路:在redis中創建商品評論列表,當用戶發佈商品評論時,將評論信息轉成json存儲到list中,用戶在頁面查詢評論列表,從redis中取出json數據展示到頁面。
定義商品評論列表key:
商品編號爲1001的商品評論key:items: comment:1001
LPUSH items:comment:1001 '{"id":1,"name":"色情買家舉報了!!!","date":20180510}'
集合類型
-set
集合的含義是每個元素的順序不同,且沒有順序。集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在等,由於集合類型的Redis內部是使用值爲空的散列表實現,所有這些操作的時間複雜度都爲0(1)。 Redis還提供了多個集合之間的交集、並集、差集的運算。
有序集合類型
-
在集合類型的基礎上有序集合類型爲集合中的每個元素都關聯一個分數,這使得我們不僅可以完成插入、刪除和判斷元素是否存在在集合中,還能夠獲得分數最高或最低的前N個元素、獲取指定分數範圍內的元素等與分數有關的操作。
在某些方面有序集合和列表類型有些相似。
1、二者都是有序的。
2、二者都可以獲得某一範圍的元素。
但是,二者有着很大區別:
1、列表類型是通過鏈表實現的,獲取靠近兩端的數據速度極快,而當元素增多後,訪問中間數據的速度會變慢。
2、有序集合類型使用散列表實現,所有即使讀取位於中間部分的數據也很快。
3、列表中不能簡單的調整某個元素的位置,但是有序集合可以(通過更改分數實現)
4、有序集合要比列表類型更耗內存。
- 應用:商品銷售排行榜
根據商品銷售量對商品進行排行顯示,定義sorted set集合,商品銷售量爲元素的分數。
定義商品銷售排行榜key:items:sellsort
寫入商品銷售量:
商品編號1001的銷量是9,商品編號1002的銷量是10
ZADD items:sellsort 9 1001 10 1002
商品編號1001的銷量加1
ZINCRBY items:sellsort 1 1001
商品銷量前10名:
ZRANGE items:sellsort 0 9 withscores
redis的主要缺點在於數據庫容量受到物理內存的限制不能用作海量數據的高性能讀寫(意思就是要考慮那些低內存的用戶電腦),因此redis最合適的使用場景是那些較小數據量的高性能操作和運算上,例如:
緩存(數據查詢、短連接、新聞內容、商品內容等等)(最多使用)
分佈式集羣架構中的session分離。
聊天室的在線好友列表。
任務隊列(秒殺、搶購、12306等等)
應用排行榜。
網站訪問統計。
數據過期處理(可以精確到毫秒)
redis不僅僅只能用命令的形式操作,主流的後端語言都有對應的客戶端支持,java官方推薦使用jedis和redisson。jedis託管在github上,你可以通過https://github.com/xetorthio/jedis訪問和下載jedis客戶端。
Redis的高性能是由於其將所有數據都存儲在了內存中,爲了使Redis在重啓之後仍能保證數據不丟失,需要將數據從內存中同步到硬盤中,這一過程就是持久化。Redis支持兩種方式的持久化,一種是RDB方式,一種是AOF方式。可以單獨使用其中一種或將二者結合使用。
- RDB方式的持久化是通過快照(snapshotting)完成的,當符合一定條件時Redis會自動將內存中的數據進行快照並持久化到硬盤。RDB是Redis默認採用的持久化方式。Redis啓動後會讀取RDB快照文件,將數據從硬盤載入到內存。根據數據量大小與結構和服務器性能不同,這個時間也不同。通常將記錄一千萬個字符串類型鍵、大小爲1GB的快照文件載入到內存中需要花費20~30秒鐘。
P:通過RDB方式實現持久化,一旦Redis異常退出,就會丟失最後一次快照以後更改的所有數據。這就需要開發者根據具體的應用場合,通過組合設置自動快照條件的方式來將可能發生的數據損失控制在能夠接受的範圍。如果數據很重要以至於無法承受任何損失,則可以考慮使用AOF方式進行持久化。
- 默認情況下Redis沒有開啓AOF(append only file)方式的持久化,可以通過appendonly參數開啓:appendonly yes。開啓AOF持久化後每執行一條會更改Redis中的數據的命令,Redis就會將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通過dir參數設置的,默認的文件名是appendonly.aof,可以通過appendfilename參數修改:appendfilename appendonly.aof
爲了保證redis不會因爲重啓等原因而導致數據丟失,redis提供了持久化的解決方案,redis服務重新啓動後會將硬盤中的數據恢復到內存中,但是此過程中如果redis服務器的硬盤損壞的話可能就會導致數據丟失。爲了更好的保證數據的完整性,避免這種單點故障,可以使用redis提供了主從複製機制。主redis(master)中的數據有兩個副本(replication)即從redis1(slave)和從redis2(slave),即使一臺redis服務器宕機其它兩臺redis服務也可以繼續提供服務
redis中常見的性能問題和解決方案
- master寫內存快照(snapshotting),sava命令調度rdbSava函數會阻塞主線程的工作,當快照比較大時對性能影響非常大,會間斷性暫停服務,所以master最好不要寫內存快照。
- master AOF持久化,如果不重寫AOF文件,這個持久化方式對性能的影響是最小的,但是AOF文件會不斷增大,AOF文件過大會影響master重啓的恢復速度。master最好不要做任何持久化工作,包括內存快照和AOF日誌文件,特別是不要啓用內存快照做持久化,如果數據比較關鍵,某個Slave開啓AOF備份數據,策略爲每秒同步一次。
- master調用BGREWRITEAOF(redis中的一個命令,作用是異步執行一個AOF文件重寫操作。重寫會創建一個當前AOF文件的體積優化版本,即使BGREWRITEAOF執行失敗,也不會有任何數據丟失,舊的AOF文件在BGREWRITEAOF操作成功之前不會被修改。從redis2.4開始,AOF重寫由redis自行觸發,BGREWRITEAOF僅用於手動觸發重寫操作。)重寫AOP文件,在AOP重寫的時候會大量的佔用CPU和內存資源,導致服務load過高,出現短暫暫停現象。
- redis主從複製的性能問題,爲了主從複製的速度和連接的穩定性,Slave和Master最好在同一個局域網下。
MySQL中有2000W數據,redis中只存了20W數據,那麼如何保證reids中的數據都是熱點數據。
redis內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略(回收策略),redis提供了6種數據淘汰策略。
volatile-lru:從已設置過期時間的數據集(server.db[i].expires)中挑選最近最少使用的數據淘汰。
volatile-ttl:從已設置過期時間的數據集(server.db[i].expires)中挑選將要過期的數據淘汰。
volatile-random:從已設置過期時間的數據集(server.db[i].expires)中任何選擇數據淘汰。
allkeys-lru:從數據集(server.db(i).dict)中挑選最近最少使用的數據淘汰。
allkeys-random:從數據集(server.db(i).dict)中任何選擇數據淘汰。
no-enviction(驅逐):禁止驅逐數據。
什麼是spring框架,它有哪些模塊,使用它有什麼好處。
Spring框架是一個爲Java應用程序的開發提供了綜合、廣泛的基礎性支持的java平臺,也是一個實現IOC和AOP的容器框架,設計理念就是簡化開發。
spring框架集成了許多模塊,常用的有spring ioc、spring aop,spring mvc、spring boot、spring security。
使用spring的好處
- 降低開發組件之間的耦合度,實現軟件各層的解耦。
- spring提供DI
- spring通過AOP技術完成了對事務的管理,使得開發人員不再需要手工控制事務,也不需要處理複雜的事務傳播。同時提供的AOP技術可以很容易實現比如權限攔截、運行期監控、日誌管理等功能
在Hibernate中管理事務
public void save(){
Session session = sessionFactory.getCurrentSession();
Session.beginTransaction();
Info info = new Info(“百度”);
Info.setContent(“一家良心企業”);
session.save();
session.getTransaction().commit();
}
在JDBC中管理事務
Connection conn = null;
try{
conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.executeUpdate(update person where name=”老王”)
conn.commit();
} catch(Exception e){
} final {conn.close()}
在spring中管理事務
@Transactional
public void save(){
sessionFactory.getCurrentSession.svae(info);
}
- 提供了很多輔助類,比如JdbcTemplate、HibernateTemplate,用於加快應用的開發
- spring容器提供了單例支持(spring配置文件中定義的bean默認爲單例模式),開發人員不需要編寫代碼來實現單例。
- spring對於主流的框架,比如MyBatis、Hibernate提供了集成支持。
- 相對於其他集成框架,比如說EJB,spring IoC更加輕量級,使其在有限的內存和CPU資源下可以進行應用程序的開發和發佈
請你談一談Spring的IOC和AOP?
- IOC是一種設計模式的實現,有兩層實現。
對象的創建都不再由程序員控制,而是交由Spring控制,稱之爲控制反轉。
對象的依賴關係由Spring的配置文件描述,並且這種關係只有在使用的時候纔會建立,稱之爲依賴注入。
spring中的org.springfremework.beans包和org.springfreamework.context包構成了spring框架的IoC容器的基礎,org.springframework.beans.factory.BeanFactory是spring IoC容器的具體實現,用於包裝和管理各種bean,因此該接口是spring IoC的核心接口。ApplicationContext接口對BeanFactory進行了擴展,提供了message resource用於國際化的機制、事務傳播和應用層的特別配置,比如針對WEB應用的WebApplicationContext。
- AOP是一種編程思想,叫做面向切面,是OOP的延續和補充。P:這種在運行時,動態地將代碼切入到類的指定方法,指定位置上的編程思想就是面向切面的編程。
在Spring中的具體實現是將系統中的非核心業務基於代理機制做提取,進行單獨處理。比如事務,比如日誌,比如安全授權等。
spring框架中大量使用了設計模式,請列舉幾個
- 代理模式
Spring AOP就是採用動態代理實現的,spring中有兩種動態代理方式,jdk代理和cglib代理。
- 單例模式
Spring配置文件中的bean默認爲單例
- 模版方法
Spring提供了許多模板類用於解決代碼的重複問題,例如RestTemplate、JmsTemplate、JpaTemplate
- 前端控制器
Spring 提供給了DispatcherServlet來對請求進行分發
- 視圖幫助
Spring提供了一系列的JSP標籤,高效宏來輔助視圖
- 工廠模式
使用BeanFactory創建對象實例