三大特征:封裝,繼承,多態 多態:簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。 抽象:抽象是將一類對象的共同特征總結出來構造類的過程 包裝,可以講基本類型當做對象來使用,抽象只關心對象有那些屬性和行為,而不關心這些行為的細節是什麼。 Integer:當數值在 128 127之間的時 ...
三大特征:封裝,繼承,多態
- 多態:簡單的說就是用同樣的對象引用調用同樣的方法但是做了不同的事情。
抽象:抽象是將一類對象的共同特征總結出來構造類的過程
包裝,可以講基本類型當做對象來使用,抽象只關心對象有那些屬性和行為,而不關心這些行為的細節是什麼。
Integer:當數值在-128-127之間的時候,不會new一個新對象
Integer c3 = new Integer(100); Integer d1 = new Integer(100);
// false 兩個對象不相等Integer c = 100; Integer c4 = 100;
// 如果數值在-128-127之間相同(取常量池中的對象),範圍外面不同int b2 = 200; Integer b5 = new Integer(200);
// 相同
&& : 短路運算符,即如果第一個條件判斷不成立則不會判斷第二個條件(||)也是
round : 四捨五入,將數字+0.5然後下取整
switch : 可以判斷的類型, byte short char int String (Long不能判斷)
2 << 3 : 右移/2的n次方, 左移*2的n次方
數組有length屬性,String有length方法
構造器不能被繼承,所以也就不能被重寫,但是可以被重載
equals 相同兩個對象的hashcode相同,hashcode相同,兩個對象不一定相同
String 為 final類,不可以被繼承
String和StringBuilder、StringBuffer的區別
String是只讀字元串,也就意味著String引用的字元串內容是不能被改變的
StringBuffer/StringBuilder類表示的字元串對象可以直接進行修改。 區別為StringBuffer為線程安全
重載和重寫的區別
重載發生在一個類中,而重寫發生在子類和父類中
重寫要求返回值和參數類型相同,修飾符可以和父父類中的相同或者更廣泛,不能聲明比父類更多的異常
重載要求參數個數或者類型或者順序不同,返回值沒有要求.
描述一下JVM載入class文件的
- 首先將.class文件加入到記憶體,然後進入連接階段,這個階段主要驗證,準備(靜態變數初始化和賦值),解析(符號引用解析為直接引用) 最後初始化,當這個類有父類的時候先去初始化父類,然後初始化這個類.
抽象類和介面有什麼異同
介面和抽象類都不能被實例化,但可以定義引用.
介面中所有的方法必須都是抽象方法,而抽象類中可以有普通方法,
介面中的修飾符全是public,而抽象類都可以
有抽象類的方法必須聲明為抽象類,而抽象類中不一定有抽象方法
Java有記憶體泄露嗎,
- 理論上沒有,因為有垃圾回收機制,但實際中可能存在無用但可達的對象,這些對象不能被GC回收, 例如Hibernate一級緩存中的對象.
靜態變數和實例變數
靜態變數static修飾,他屬於類的,不屬於任何一個方法,所以一個靜態變數只會有一個拷貝.靜態變數可以實現讓多個對象共用記憶體。
實例變數必須依存於某一實例,需要先創建對象然後通過對象才能訪問到它。
對象克隆的實現方式
實現Cloneable介面並重寫Object類中的clone()方法;
實現Serializable介面,通過對象的序列化和反序列化實現克隆,可以實現真正的深度克隆(ObjectOutputStream)。
垃圾回收(GC)
垃圾回收機制,記憶體處理是編程人員容易出現問題的地方,忘記或者錯誤的記憶體回收會導致程式或系統的不穩定甚至崩潰
分代式垃圾收集 (伊甸園,幸存者樂園,終身頤養園)
一個java文件中可以包含多個類,但是只能有一個公開類,且名字和Java文件相同
一個內部類可以訪問他的外部類,包括私有成員
final : 類: 不能被繼承 方法: 不能被重寫 屬性: 賦值一次過後不可以再次賦值
先初始化靜態成員,然後調用父類構造器,再初始化非靜態成員,最後調用自身構造器。
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1"); GB2312編碼的字元串轉換為ISO-8859-1
try{}里有一個return語句,那麼緊跟在這個try後的finally{}里的代碼會不會被執行,
- 會執行 , 如果finally還有return 語句, 則會返回finally內的語句
異常處理: 異常都是Throwable子類
throws 聲明一個方法可能拋出的異常
throw 在方法內拋出異常
常見的異常:
ArithmeticException(算術異常)
IllegalArgumentException (非法參數異常)
IndexOutOfBoundsException (下標越界異常)
NullPointerException (空指針異常)
ClassCastException
final、finally、finalize的區別。
final : 修飾符
finally : 不管是否發生異常都會執行
finalize : GC再銷毀對象的時候調用
ArrayList使用的數組方式來進行存儲,所以查找元素快 ,LinkedList雙向鏈表,插入和刪除快,都不是線程安全,可以通過Collections中的synchronizedList方法
Collection是一個介面,它是Set、List等容器的父介面;Collections是個一個工具類,
sleep 和 wait
sleep()方法(休眠)是線程類(Thread)的靜態方法,調用此方法會讓當前線程暫停執行指定的時間,將執行機會(CPU)讓給其他線程,但是對象的鎖依然保持,因此休眠時間結束後會自動恢復(線程回到就緒狀態)
wait()是Object類的方法,調用對象的wait()方法導致當前線程放棄對象的鎖(線程暫停執行),進入對象的等待池(wait pool),只有調用對象的notify()方法(或notifyAll()方法)時才能喚醒等待池中的線程
線程的sleep()方法和yield()方法有什麼區別?
sleep()方法給其他線程運行機會時不考慮線程的優先順序,因此會給低優先順序的線程以運行的機會;yield()方法只會給相同優先順序或更高優先順序的線程以運行的機會;
線程執行sleep()方法後轉入阻塞(blocked)狀態,而執行yield()方法後轉入就緒(ready)狀態;
sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;
sleep()方法比yield()方法(跟操作系統CPU調度相關)具有更好的可移植性
請說出與線程同步以及線程調度相關的方法。
wait():使一個線程處於等待(阻塞)狀態,並且釋放所持有的對象的鎖;
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要處理InterruptedException異常;
notify():喚醒一個處於等待狀態的線程,當然在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且與優先順序無關;
notityAll():喚醒所有處於等待狀態的線程,該方法並不是將對象的鎖給所有線程,而是讓它們競爭,只有獲得鎖的線程才能進入就緒狀態;
線程同步的方式
lock()
synchronized (同步代碼塊和同步方法)
信號量機制
多線程三種實現多線程的方式
繼承Thread
實現Runable
Callable(可以拋出異常 ,可以有返回值)
線程池:需要的時候從池中獲取線程不用自行創建,使用完畢不需要銷毀線程而是放回池中,從而減少創建和銷毀線程對象的開銷。
newSingleThreadExecutor: 單線程的線程池
newFixedThreadPool: 固定大小的線程池
newCachedThreadPool:創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。
newScheduledThreadPool:創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。
newSingleThreadScheduledExecutor :創建一個單線程化的支持定時的線程池,
synchronized 和Lock的異同
都是實現線程同步功能
Lock性能更好, 需要自己手動釋放鎖
實現序列化: Serializable
XML文檔定義有幾種形式?解析XML文檔有哪幾種方式
XML文檔定義分為DTD和Schema兩種形式,二者都是對XML語法的約束,其本質區別在於Schema本身也是一個XML文件,可以被XML解析器解析,而且可以為XML承載的數據定義類型,約束能力較之DTD更強大。
DOM,SAX DOM會將整個文件裝入記憶體,當文件較大的時候會占用記憶體較多, saxAX是事件驅動型的XML解析方式,它順序讀取XML文件,不需要一次全部裝載整個文件。當遇到像文件開頭,文檔結束,或者標簽開頭與標簽結束時,它會觸發一個事件,用戶通過事件回調代碼來處理XML文件
你在項目中哪些地方用到了XML
數據交換和信息配置 (Web Service )
XML配置信息, Spring, Web
JDBC鏈接資料庫的步驟
載入驅動
創建連接
創建語句
執行語句
處理結果
關閉資源(關閉資源要和打開資源的順序相反)
Statement和PreparedStatement有什麼區別?哪個性能更好?
PreparedStatement介面代表預編譯的語句,它主要的優勢在於可以減少SQL的編譯錯誤並增加SQL的安全性(減少SQL註射攻擊的可能性)
PreparedStatement中的SQL語句是可以帶參數的,避免了用字元串連接拼接SQL語句的麻煩和不安全
當批量處理SQL或頻繁執行相同的查詢時,PreparedStatement有明顯的性能上的優勢,
使用JDBC操作資料庫時,如何提升讀取數據的性能?如何提升更新數據的性能?
要提升讀取數據的性能,可以指定通過結果集(ResultSet)對象的setFetchSize()方法指定每次抓取的記錄數(典型的空間換時間策略)
要提升更新數據的性能可以使用PreparedStatement語句構建批處理,將若幹SQL語句置於一個批處理中執行。
連接池有什麼作用
為了提升系統訪問資料庫的性能,可以事先創建若幹連接置於連接池中,需要時直接從連接池獲取,使用結束時歸還連接池而不必關閉連接,從而避免頻繁創建和釋放連接所造成的開銷,
C3P0、Proxool、DBCP、BoneCP、Druid等。
事務的屬性
原子性:事務中各項操作,要麼全做要麼全不做,任何一項操作的失敗都會導致整個事務的失敗;
一致性:事務結束後系統狀態是一致的;
隔離性:併發執行的事務彼此無法看到對方的中間狀態;
持久性:事務完成後所做的改動都會被持久化,
事務可能發生的錯誤狀態
臟讀: A事務讀取B事務尚未提交的數據併在此基礎上操作,而B事務執行回滾(A事務讀取時還沒有發生回滾,在讀取完成時出現錯誤,B事務回滾)
不可重覆讀:事務A重新讀取前面讀取過的數據,發現該數據已經被另一個已提交的事務B修改過了。
幻讀:事務A重新執行一個查詢,返回一系列符合查詢條件的行,發現其中插入了被事務B提交的行。
第一類丟失更新:事務A撤銷時,把已經提交的事務B的更新數據覆蓋了。
第二類丟失更新:事務A覆蓋事務B已經提交的數據,造成事務B所做的操作丟失。
四個隔離級別
JDBC如何進行事務處理
- 通過調用setAutoCommit(false)可以設置手動提交事務;當事務完成後用commit()顯式提交事務;如果在事務處理過程中發生異常則通過rollback()進行事務回滾。還有Savepoint(保存點)的概念,允許通過代碼設置保存點並讓事務回滾到指定的保存點
獲取一個類的類對象(class)
類型.class,
String.class
對象.getClass()
"hello".getClass()
Class.forName()
Class.forName("String")
反射創建對象
通過類對象調用newInstance()
通過類對象的getConstructor()或getDeclaredConstructor()方法獲得構造器(Constructor)對象並調用其newInstance()方法創建對象,例如:
String.class.getConstructor(String.class).newInstance("Hello");
如何通過反射獲取和設置對象私有欄位的值
可以通過類對象的
getDeclaredField()
方法欄位(Field)對象,然後再通過欄位對象的setAccessible(true)
將其設置為可以訪問
通過反射獲取對象方法
直接通過getMethod("name")獲取到方法對象,然後通過invoke方法調用
Method m = str.getClass().getMethod("toUpperCase");
m.invoke(str)
簡述一下麵向對象的"六原則一法則"。
單一職責原則:一個類只做它該做的事情,
開閉原則:軟體實體應當對擴展開放,對修改關閉。(抽象類或者介面)
依賴倒轉原則:面向介面變成, 聲明方法的參數類型、方法的返回類型、變數的引用類型時,儘可能使用抽象類型而不用具體類型,
里氏替換原則:任何時候都可以用子類型替換掉父類型(子類一定是增加父類的能力而不是減少父類的能力,因為子類比父類的能力更多,把能力多的對象當成能力少的對象來用當然沒有任何問題)。
介面隔離原則:介面要小而專,絕不能大而全。
合成聚合復用原則:優先使用聚合或合成關係復用代碼。
迪米特法則:一個對象應當對其他對象有儘可能少的瞭解(儘量用簡單的方式把業務呈現給用戶,而不讓用戶看到業務細節)
設計模式:
工廠模式:工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。
代理模式:給一個對象提供一個代理對象,並由代理對象控制原對象的引用。
適配器模式:把一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起使用的類能夠一起工作。
UML簡介
- UML是統一建模語言,為軟體開發的所有階段提供模型化和可視化支持。使用UML可以幫助溝通與交流,輔助應用設計和文檔的生成,還能夠闡釋系統的結構和行為。
UML中常用的圖
用例圖(捕獲需求,用來描述系統的功能)
類圖(描述類和類之間的關係)
時序圖(描述特定任務的執行順序以及交互關係)
冒泡排序
import java.util.Comparator;
/**
* 排序器介面(策略模式: 將演算法封裝到具有共同介面的獨立的類中使得它們可以相互替換)
* @author駱昊
*
*/
public interface Sorter {
/**
* 排序
* @param list 待排序的數組
*/
public <T extends Comparable<T>> void sort(T[] list);
/**
* 排序
* @param list 待排序的數組
* @param comp 比較兩個對象的比較器
*/
public <T> void sort(T[] list, Comparator<T> comp);
}
/**
* 冒泡排序
*/
public class BubbleSorter implements Sorter {
@Override
public <T extends Comparable<T>> void sort(T[] list) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (list[j].compareTo(list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
swapped = true;
}
}
}
}
@Override
public <T> void sort(T[] list, Comparator<T> comp) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (comp.compare(list[j], list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
swapped = true;
}
}
}
}
}
- 折半查找
public class MyUtil {
public static <T extends Comparable<T>> int binarySearch(T[] x, T key) {
return binarySearch(x, 0, x.length- 1, key);
}
// 使用迴圈實現的二分查找
public static <T> int binarySearch(T[] x, T key, Comparator<T> comp) {
int low = 0;
int high = x.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int cmp = comp.compare(x[mid], key);
if (cmp < 0) {
low= mid + 1;
}
else if (cmp > 0) {
high= mid - 1;
}
else {
return mid;
}
}
return -1;
}
// 使用遞歸實現的二分查找
private static<T extends Comparable<T>> int binarySearch(T[] x, int low, int high, T key) {
if(low <= high) {
int mid = low + ((high -low) >> 1);
if(key.compareTo(x[mid])== 0) {
return mid;
}
else if(key.compareTo(x[mid])< 0) {
return binarySearch(x,low, mid - 1, key);
}
else {
return binarySearch(x,mid + 1, high, key);
}
}
return -1;
}
}