積累Java基礎知識
面向物件特徵
封裝,繼承,多型
封裝:將類的某些資訊隱藏在類內部,不允許外部程式直接訪問,而是通過該類提供的方法來實現對隱藏資訊的操作和訪問。
繼承:這是類與類之間的關係,子類繼承父類,表明子類是一種特殊的父類,並且具有父類所不具有的一些屬性或方法,並且只有單繼承
多型:物件擁有多種形態,即引用多型和方法多型;引用多型中,父類的引用可以指向本類物件,也可以指向子類的物件;方法多型中,建立本類物件時,呼叫的方法為本類的方法,建立子類物件時,呼叫的方法為子類重寫的方法;多型的前提是繼承和重寫
基本資料型別
整數值型:byte,short,int,long
浮點型:float,double
字元型:char
布林型:boolean
整數預設int型,小數預設是double型。Float和long型別的必須加字尾。
String屬於引用型別,不是基本型別
平時說的拆箱和裝箱就是引用型別和基本型別的轉換,基本型別轉換成引用型別後就可以呼叫其包裝類的方法進行型別轉換
Final關鍵字
#####final修飾類
該類不能被繼承
#####final修飾屬性
該屬性一旦賦值就不能被更改,即此時該屬性為常量
#####final修飾方法
該方法可以被呼叫,不能重寫
static關鍵字
#####static方法
static方法就是沒有this的方法。在static方法內部不能呼叫非靜態方法,因為非靜態方法是在物件建立的時候初始化的,靜態方法在類載入的時候就初始化了;反過來是可以的。而且可以在沒有建立任何物件的前提下,僅僅通過類本身來呼叫static方法。
static方法不能被覆蓋,因為方法覆蓋是基於執行時動態繫結的,而static方法是編譯時靜態繫結的
#####static變數
static變數也稱作靜態變數,靜態變數和非靜態變數的區別是:靜態變數被所有的物件所共享,在記憶體中只有一個副本,它當且僅當在類初次載入時會被初始化。而非靜態變數是物件所擁有的,在建立物件的時候被初始化,存在多個副本,各個物件擁有的副本互不影響。
static成員變數的初始化順序按照定義的順序進行初始化
#####static程式碼塊
static程式碼塊也叫靜態程式碼塊,通常是把只進行一次的初始化操作放在程式碼塊中以優化程式效能,避免重複開闢記憶體。static塊可以置於類中的任何地方,類中可以有多個static塊。在類初次被載入的時候,會按照static塊的順序來執行每個static塊,並且只會執行一次
#####static類
static可以用來修飾內部類,也就是靜態內部類;可以避免非靜態內部類持有外部類引用導致記憶體洩漏
volatile
一旦一個共享變數(類的成員變數、類的靜態成員變數)被volatile修飾之後,那麼就具備了兩層語義:
- 保證了不同執行緒對這個變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的
- 禁止進行指令重排序,保證有序性
finalize和finally
finalize 方法是在物件被回收之前呼叫的方法,給物件自己最後一個復活的機會,但是什麼時候呼叫 finalize 沒有保證。
finally 是一個關鍵字,與 try 和 catch 一起用於異常的處理。finally 塊一定會被執行,無論在 try 塊中是否有發生異常
抽象類和介面區別
介面是對動作的抽象,抽象類是對根源的抽象
- 抽象類和介面都不能直接例項化,如果要例項化,抽象類變數必須指向實現所有抽象方法的子類物件,介面變數必須指向實現所有介面方法的類物件
- 抽象類要被子類繼承,介面要被類實現
- 介面只能做方法申明,抽象類中可以做方法申明,也可以做方法實現
- 接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數
- 抽象類裡的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那麼該子類只能是抽象類。同樣,一個實現介面的類,如不能全部實現介面方法,那麼該類也只能為抽象類
- 抽象方法只能申明,不能實現,介面是設計的結果 ,抽象類是重構的結果
- 抽象類裡可以沒有抽象方法
- 如果一個類裡有抽象方法,那麼這個類只能是抽象類
- 抽象方法要被實現,所以不能是靜態的,也不能是私有的
- 介面可繼承介面,並可多繼承介面,但類只能單繼承
重寫和過載
- 重寫是作用於父類的方法。過載是作用於自己的方法
- 方法重寫要求引數列表必須一致,而方法過載要求引數列表必須不一致
- 方法重寫要求返回型別必須一致(或為其子型別),方法過載對此沒有要求
- 方法重寫只能用於子類重寫父類的方法,方法過載用於同一個類中的所有方法
- 方法重寫對方法的訪問許可權和丟擲的異常有特殊的要求,而方法過載在這方面沒有任何限制
- 父類的一個方法只能被子類重寫一次,而一個方法可以在所有的類中可以被過載多次
- 過載是編譯時多型,因為編譯器可以根據引數的型別來選擇使用哪個方法;重寫是執行時多型,因為編譯期編譯器不知道並且沒辦法確定該去呼叫哪個方法,JVM會在程式碼執行的時候作出決定
Runnable和Callable的區別
Runnable介面中的run()方法的返回值是void,它做的事情只是純粹地去執行run()方法中的程式碼而已;Callable介面中的call()方法是有返回值的,是一個泛型,和Future、FutureTask配合可以用來獲取非同步執行的結果。
這其實是很有用的一個特性,因為多執行緒相比單執行緒更難、更復雜的一個重要原因就是因為多執行緒充滿著未知性,某條執行緒是否執行了?某條執行緒執行了多久?某條執行緒執行的時候我們期望的資料是否已經賦值完畢?無法得知,我們能做的只是等待這條多執行緒的任務執行完畢而已。而Callable+Future/FutureTask卻可以方便獲取多執行緒執行的結果,可以在等待時間太長沒獲取到需要的資料的情況下取消該執行緒的任務。
equals和==的區別
最大的區別是:equals是方法,==是運算子
等號在比較基本資料型別時比較的是值,而用等號比較兩個物件時比較的是兩個物件的地址值
類如果沒有重寫equals()方法,呼叫equals()方法其實和使用等號的效果一樣,可檢視Object類的equals方法原始碼實現,也是使用等號比較兩個物件的地址值;
重寫了equals()方法,就具體看重寫邏輯,通常是比較兩個物件的值,可檢視String類的equals方法原始碼,就是比較每個字元是否相等
Java中的HashMap的工作原理是什麼?
Java中的HashMap是以鍵值對(key-value)的形式儲存元素的。HashMap需要一個hash函式,它使用hashCode()和equals()方法來從集合新增和檢索元素。當呼叫put()方法的時候,HashMap會計算key的hash值,然後把鍵值對儲存在集合中合適的索引上。如果key已經存在了,value會被更新成新值。
HashMap和Hashtable有什麼區別?
HashMap和Hashtable都實現了Map介面,因此很多特性非常相似。但是,他們有以下不同點:
HashMap允許鍵和值是null,而Hashtable不允許鍵或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更適合於單執行緒環境,而Hashtable適合於多執行緒環境。
ArrayList和LinkedList不同點:
ArrayList是基於索引的資料介面,它的底層是陣列。它可以以O(1)時間複雜度對元素進行隨機訪問。
與此對應,LinkedList是以元素列表的形式儲存它的資料,每一個元素都和它的前一個和後一個元素連結在一起,
在這種情況下,查詢某個元素的時間複雜度是O(n)。
b相對於ArrayList,LinkedList的插入,新增,刪除操作速度更快,因為當元素被新增到集合任意位置的時候,
不需要像陣列那樣重新計算大小或者是更新索引。
LinkedList比ArrayList更佔記憶體,因為LinkedList為每一個節點儲存了兩個引用,一個指向前一個元素,一個指向下一個元素。
大O符號描述了當資料結構裡面的元素增加的時候,演算法的規模或者是效能在最壞的場景下有多麼好。
大O符號也可用來描述其他的行為,比如:記憶體消耗。因為集合類實際上是資料結構,我們一般使用大O符號基於時間,
記憶體和效能來選擇最好的實現。大O符號可以對大量資料的效能給出一個很好的說明。
String StringBuffer StringBuild
String類中使用字元陣列儲存字串, 有“final”修飾符,所以可以知道string物件是不可變的,在執行時儲存了一個字串池(String pool)
字串池的實現可以在執行時節約很多heap空間,不同的字串僅僅只儲存一個
可以避免網路安全問題,多執行緒併發安全問題,資料庫的使用者名稱、密碼都是以字串的形式傳入
socket程式設計中,主機名和埠都是以字串的形式傳入。因為字串是不可變的,所以它的值是不可改變的,
否則黑客們可以鑽到空子,改變字串指向的物件的值,造成安全漏洞。
StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元陣列儲存字串,
沒有用final修飾符,可知這兩種物件都是可變的。
String中的物件是不可變的,也就可以理解為常量,顯然執行緒安全。
對 String 型別進行改變的時候其實都等同於生成了一個新的 String 物件,然後將指標指向新的 String 物件
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的
StringBuilder並沒有對方法進行加同步鎖,所以是非執行緒安全的。
如果程式不是多執行緒的,那麼使用StringBuilder效率高於StringBuffer。
在大部分情況下StringBuilder> StringBuffer > String
List遍歷
第一種:
for(object o :list)
{
}
第二種:
Iterator iter = list.iterator();
while(iter.hasNext()){
Object o = iter.next();
}
第三種:
int size = list.size();
for(int i=0; i< size; i++){
Object o= list.get(i);
}
在資料量達到百萬級甚至千萬級的時候,第三種是最快的,第一種和第二種差不多,第二種稍微好點;資料量小的時候沒有明顯差別
Map遍歷
第一種:
//遍歷key 通過key獲取value
for(Integer key:map.keySet()){
System.out.println(key);
String value = map.get(key);
}
第二種:
//遍歷value 只能獲取value
for(String value:map.values()){
System.out.println(value);
}
第三種:使用的較多
for(Map.Entry< Integer, String> entry : map.entrySet()){
entry.getKey();
entry.getValue();
}
第四種:可以在遍歷的時候刪除key,其它方式不行
Iterator< Map.Entry< Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry< Integer, String> entry = iterator.next();
entry.getKey();
entry.getValue();
iterator.remove();
}
同樣的在資料量小的時候,第二種和第三種速度差不多,第四種慢
在資料量在百萬級和千萬級的時候,第四種就很慢了,第三種最快,第二種次之