static變量和static方法
static變量
1.static修飾的變量:靜態(tài)變量,靜態(tài)變量在內(nèi)存中只有一個拷貝,jvm只為靜態(tài)變量分配一次內(nèi)存久妆,在加載類的過程中完成靜態(tài)變量的內(nèi)存分配窒升。可以類名直接訪問赡矢。一般在對象之間共享值時和方便訪問變量時使用靜態(tài)變量杭朱。
2.實例變量,每創(chuàng)建一個實例就會為實例變量分配一次內(nèi)存吹散,實例變量可以有多個拷貝弧械,互不影響。
靜態(tài)方法
靜態(tài)方法可以直接通過類名調(diào)用空民,實例也可調(diào)用刃唐。靜態(tài)方法中不能使用this和super關鍵字,不能直接訪問所屬類的實例變量和實例方法袭景,只能訪問所屬類的靜態(tài)成員變量和成員方法唁桩。
static代碼塊
public class Test5 {
private static int a;
private int b;
static{
Test5.a=3;
System.out.println(a);
Test5 t=new Test5();
t.f();
t.b=1000;
System.out.println(t.b);
}
在類中獨立于類成員的static語句塊,可以有多個耸棒,jvm加載類時會按順序執(zhí)行靜態(tài)代碼塊
static final
static final修飾的變量荒澡,表示一旦賦值就不可修改,并且可以通過類名訪問
static final修飾的方法与殃,不可覆蓋单山,可通過類名直接訪問
java支持的數(shù)據(jù)類型有碍现?何為自動拆裝箱?
1.byte
2.short
3.int
4.long
5.float
6.double
7.boolean
8.char
自動裝箱時java編譯器在基本數(shù)據(jù)類型和對應的對象包裝類型之間做的一個轉(zhuǎn)化米奸,比如int轉(zhuǎn)成Integer,double轉(zhuǎn)double等昼接,反之就是自動拆箱
java不支持多繼承。每個類只能繼承一個類悴晰,但可以實現(xiàn)多個接口
抽象類和抽象接口
java提供和創(chuàng)建抽象類和接口慢睡,不同點
1.接口中所有的方法隱含的都是抽象的。而抽象類則可以同時包含抽象和非抽象的方法铡溪。
2.類可以實現(xiàn)很多個接口漂辐,但是只能繼承一個抽象類
3.類如果要實現(xiàn)一個接口,它必須要實現(xiàn)接口聲明的所有方法棕硫。但是髓涯,類可以不實現(xiàn)抽象類聲明的所有方法,當然哈扮,在這種情況下纬纪,類也必須得聲明成是抽象的。
4.抽象類可以在不提供接口方法實現(xiàn)的情況下實現(xiàn)接口滑肉。
5.Java接口中聲明的變量默認都是final的包各。抽象類可以包含非final的變量。
6.Java接口中的成員函數(shù)默認是public的赦邻。抽象類的成員函數(shù)可以是private髓棋,protected或者是public。
7惶洲。接口是絕對抽象的,不可以被實例化膳犹。抽象類也不可以被實例化恬吕,但是,如果它包含main方法的話是可以被調(diào)用的须床。
創(chuàng)建線程的幾種方式*
- 繼承thread類
- 實現(xiàn)Runnable接口
- 使用Executor框架來創(chuàng)建線程池
java不支持多繼承铐料,實現(xiàn)接口的方式更受歡迎
synchronized獲取鎖,同步*
在監(jiān)視器內(nèi)部豺旬,如何做線程同步钠惩?程序應做何種級別的同步
監(jiān)視器和鎖在Java虛擬機中是一塊使用的。監(jiān)視器監(jiān)視一塊同步代碼塊族阅,確保一次只有一個線程執(zhí)行同步代碼塊篓跛。每一個監(jiān)視器
hashMap的原理
hashMap以key-value的形式進行數(shù)據(jù)存儲,本質(zhì)上是數(shù)組和鏈表的結(jié)合坦刀。
initialCapacity(初始容量)和loadFactor(加載因子)是影響hashMap性能的重要參數(shù)愧沟。默認初始容量16蔬咬,加載因子是0.75。為了保證HashMap的效率沐寺,系統(tǒng)必須要在某個臨界點進行擴容處理林艘,臨界點:當HashMap中元素的數(shù)量=數(shù)據(jù)長度length*加載因子(loadFactor).擴容是一個非常耗時的過程,需要重新計算數(shù)據(jù)在數(shù)組中的位置并進行復制混坞。
實驗表明length=2的n次方時狐援,數(shù)組中元素分布較均勻
過程:
- 利用key的hashCode重新hash計算出當前對象的元素在數(shù)組中的下標,然后找到在數(shù)組中的位置究孕。
- 如果hash值相同且key值也相同咕村,則覆蓋原始值;如果hash相同key不同(出現(xiàn)沖突)蚊俺,則將當前的key-value放入鏈表中
hashMap和hashTable懈涛、ConcurrentHashMap和synchronized Map的原理和區(qū)別(出處:http://www.importnew.com/21396.html)
HashMap中key可以為null,HashTable中key不可以為null泳猬,ConcurrentHashMap中key和value都不能為null
HashMap是非線程安全的
如何線程安全的使用hashMap
Map<String,String> hashTable = new HashTable<String,String>()
Map<String,String> synchronizedMap = Collections.synchronizedMap(new HashMap<String,String>)
Map<String,String> concurrentHashMap = new ConcurrentHashMap<String,String>();
HashMap何時會產(chǎn)生死循環(huán)批钠?
HashTable
HashTable源碼中使用synchronized來保證線程安全,如get方法和put方法
public synchronized V get(Object key){
//省略
}
public synchronized V put(Object key){
//省略
}
當一個線程使用put方法時別的線程不但不可以使用put得封,連get方法都不可以使用埋心,效率低!現(xiàn)已基本不使用
ConcurrentHashMap
ConcurrentHashMap線程安全的忙上,適用于讀者數(shù)量大于寫者數(shù)量的場景
- 允許并發(fā)讀和線程安全的更新操作
- 在執(zhí)行寫操作時拷呆,只鎖住部分map
- 高的并發(fā)級別會造成時間和空間的浪費,低的并發(fā)級別在寫線程多時會引起線程間的競爭
- 返回的迭代器是弱一致性疫粥,fail-safe并且不會拋出ConcurrentModificationException異常
- 不允許null的鍵值
- 可代替HashTable,但CHM不會鎖住整個map
java7
采用鎖分離技術茬斧,使用多個鎖來控制hash表的不同部分,每一部分相當于一個hashTable梗逮,有自己的鎖项秉。只要多個修改操作發(fā)生在不同的segment上,就可以并發(fā)執(zhí)行慷彤。
有些方法需要跨段娄蔼,如size()和containsValue(),他們可能需要鎖定整個表而不僅僅是段,這需要按順序鎖定所有段底哗,操作完畢后岁诉,按順序釋放所有段的鎖
java8
synchronizedHashMap
源碼
//synchronizedMap方法
public static <K,V> Map<K,V>synchronizedMap(Map<K,V> m){
return new SynchronizedMap<>(m);
}
//SynchronizedMap類
private static class SynchronizedMap<K,V> implements Map<K,V> Serializable{
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
this.m = Objects.requireNonNull(m);
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public int size() {
synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
synchronized (mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
synchronized (mutex) {return m.containsValue(value);}
}
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
// 省略其他方法
}
從源碼中可以看出,synchronizedMap()方法返回一個SynchronizedMap類的對象跋选,而在SynchronizedMap類中使用了synchronized同步關鍵字來保證對Map的操作是線程安全的
HashMap為什么是非線程安全涕癣?
void addEntry(int hash,K key,V value,int bucketIndex){
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash,key,value,e);
if(size++ >= threshold){
resize(2*table.length);
}
}
原因一:hashMap做put操作的時候調(diào)用addEntry方法,現(xiàn)在假如A線程和B線程同時對同一個數(shù)據(jù)位置調(diào)用該方法野建,兩個線程會同時得到頭節(jié)點属划,A寫入頭節(jié)點以后B也寫入新的頭節(jié)點恬叹,那B的寫入操作造成A的寫入操作丟失。
addEntry中當加入新的鍵值對后鍵值對總數(shù)超過門限值的時候會調(diào)用一個resize操作同眯,代碼如下:
void resize(int newCapacity){
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if(oldCapacity == MAXIMUM_CAPACITY){
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
這個操作會生成一個新的容量的數(shù)組绽昼,會對原數(shù)組的所有鍵值對重新進行計算和寫入新的數(shù)組,之后指向新的數(shù)組须蜗。
原因二:當多個線程同時檢測需要進行resize()操作硅确,各自生成新的數(shù)組并rehash后賦給該map底層的數(shù)組table,結(jié)果最后只有一個線程生成的新數(shù)組被賦給table變量明肮,其他線程的均丟失菱农。
Map testmap = Collection.synchronizedMap(new HashMap())
equals()方法和hashCode()方法
java.lang.Object類中有兩個非常重要的方法
public boolean equals(Object obj)
public int hashCode()
Object是類繼承結(jié)構(gòu)的基礎,是所有類的父類
equals()
public boolean equals(Object obj){
return (this == obj)
}
是對兩個對象的地址值進行比較柿估。但String循未、Math、Integer秫舌、Double等這些封裝類在使用equals()方法時的妖,已經(jīng)覆蓋了object類的equals()方法
如在String類中如下:
public boolean equals(Object anObject){
if(this == anObject){
return true;
}
if(anObject instanceof String){
String anotherString = (String)anObject;
int n = count;
if(n == anotherString.count){
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while(n-- != 0)//對每一位置逐一比較
{
if(v1[i++] != v2[j++]
return false;
}
}
return true;
}
}
return false;
}
類推Math、Integer足陨、Double等類都重寫了equals()方法嫂粟,還有基本類型也是進行內(nèi)容的比較
注意:當equals方法被override時,hashCode()也要被override墨缘。按照一般hashCode()方法的實現(xiàn)來說星虹,相等的對象镊讼,它們的hash code一定相等
hashCode
- 在一個java應用的執(zhí)行期間宽涌,如果一個對象提供給equals做比較的信息沒有被修改的話,該對象多次調(diào)用hashCode方法狠毯,始終返回同一個integer嚼松。
- 如果兩個對象根據(jù)equals(Object)方法是相等的,那調(diào)用二者各自的hashCode()方法必須產(chǎn)生同一個integer結(jié)果罕偎。
在Object類中甩苛,hashCode定義如下:
public native int hashCode();
說明是本地方法讯蒲,實現(xiàn)跟本地機器有關墨林,如String犯祠、Integer搔耕、Double等這些類都覆蓋了hashCode方法,String中定義的hashCode()方法如下:
public int hashCode(){
int h = hash;
if(h == 0){
int off = offset;
char val[] = value;
int len = count;
for(int i=0;i<len;i++){
h = 31*h+val[off++];
}
hash = h;
}
return h;
}
ArrayList 和 linkedList
- ArrayList實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu)猜揪,LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)而姐。
- 對于隨機訪問,ArrayList優(yōu)于LinkedList
- 對于新增和刪除操作,LinkedList優(yōu)于ArrayList
ArrayList
ArrayList公般,在聲明對象時并不需要指定它的長度瞬雹,對象的大小是按照其中存儲的數(shù)據(jù)來動態(tài)擴充和收縮的胖缤。
數(shù)組擴容是對ArrayList效率影響較大的一個元素狗唉。
每當執(zhí)行Add、AddRange、insert、insertRange等添加元素的方法讥此,都會檢查內(nèi)部數(shù)組的容量是否夠用他巨。若不夠,以當前容量的兩倍來重新構(gòu)建數(shù)組染突,將舊元素COPY到數(shù)組中份企,然后丟棄舊數(shù)組
特定類型(Object除外)的數(shù)組的性能優(yōu)于ArrayList的性能棵介,因為ArrayList的元素屬于Object類型,所以在存儲或檢索值類型時通常發(fā)生裝箱和取消裝箱的操作冰啃。
map、list扇调、set
list 有序可重復
set 無序不可重復
map 按鍵值對存儲,無放入順序
List接口有三個實現(xiàn)類:LinkedList、ArrayList瑞侮、Vector
Set接口有兩個實現(xiàn)類:HashSet(底層由HashMap實現(xiàn)),LinkedHashSet
Map接口有三個實現(xiàn)類:HashMap种玛、HashTable、LinkedHashMap
HashMap allows one null key and any number of null values.稠歉,而Hashtable則不行
HashTable是synchronized的,是線程安全的,而HashMap不是