static變量和static方法
static變量
1.static修飾的變量:靜態變量,靜態變量在內存中只有一個拷貝,jvm只為靜態變量分配一次內存,在加載類的過程中完成靜態變量的內存分配。可以類名直接訪問。一般在對象之間共享值時和方便訪問變量時使用靜態變量。
2.實例變量,每創建一個實例就會為實例變量分配一次內存,實例變量可以有多個拷貝,互不影響。
靜態方法
靜態方法可以直接通過類名調用,實例也可調用。靜態方法中不能使用this和super關鍵字,不能直接訪問所屬類的實例變量和實例方法,只能訪問所屬類的靜態成員變量和成員方法。
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加載類時會按順序執行靜態代碼塊
static final
static final修飾的變量,表示一旦賦值就不可修改,并且可以通過類名訪問
static final修飾的方法,不可覆蓋,可通過類名直接訪問
java支持的數據類型有?何為自動拆裝箱?
1.byte
2.short
3.int
4.long
5.float
6.double
7.boolean
8.char
自動裝箱時java編譯器在基本數據類型和對應的對象包裝類型之間做的一個轉化,比如int轉成Integer,double轉double等,反之就是自動拆箱
java不支持多繼承。每個類只能繼承一個類,但可以實現多個接口
抽象類和抽象接口
java提供和創建抽象類和接口,不同點
1.接口中所有的方法隱含的都是抽象的。而抽象類則可以同時包含抽象和非抽象的方法。
2.類可以實現很多個接口,但是只能繼承一個抽象類
3.類如果要實現一個接口,它必須要實現接口聲明的所有方法。但是,類可以不實現抽象類聲明的所有方法,當然,在這種情況下,類也必須得聲明成是抽象的。
4.抽象類可以在不提供接口方法實現的情況下實現接口。
5.Java接口中聲明的變量默認都是final的。抽象類可以包含非final的變量。
6.Java接口中的成員函數默認是public的。抽象類的成員函數可以是private,protected或者是public。
7。接口是絕對抽象的,不可以被實例化。抽象類也不可以被實例化,但是,如果它包含main方法的話是可以被調用的。
創建線程的幾種方式*
- 繼承thread類
- 實現Runnable接口
- 使用Executor框架來創建線程池
java不支持多繼承,實現接口的方式更受歡迎
synchronized獲取鎖,同步*
在監視器內部,如何做線程同步?程序應做何種級別的同步
監視器和鎖在Java虛擬機中是一塊使用的。監視器監視一塊同步代碼塊,確保一次只有一個線程執行同步代碼塊。每一個監視器
hashMap的原理
hashMap以key-value的形式進行數據存儲,本質上是數組和鏈表的結合。
initialCapacity(初始容量)和loadFactor(加載因子)是影響hashMap性能的重要參數。默認初始容量16,加載因子是0.75。為了保證HashMap的效率,系統必須要在某個臨界點進行擴容處理,臨界點:當HashMap中元素的數量=數據長度length*加載因子(loadFactor).擴容是一個非常耗時的過程,需要重新計算數據在數組中的位置并進行復制。
實驗表明length=2的n次方時,數組中元素分布較均勻
過程:
- 利用key的hashCode重新hash計算出當前對象的元素在數組中的下標,然后找到在數組中的位置。
- 如果hash值相同且key值也相同,則覆蓋原始值;如果hash相同key不同(出現沖突),則將當前的key-value放入鏈表中
hashMap和hashTable、ConcurrentHashMap和synchronized Map的原理和區別(出處: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何時會產生死循環?
HashTable
HashTable源碼中使用synchronized來保證線程安全,如get方法和put方法
public synchronized V get(Object key){
//省略
}
public synchronized V put(Object key){
//省略
}
當一個線程使用put方法時別的線程不但不可以使用put,連get方法都不可以使用,效率低!現已基本不使用
ConcurrentHashMap
ConcurrentHashMap線程安全的,適用于讀者數量大于寫者數量的場景
- 允許并發讀和線程安全的更新操作
- 在執行寫操作時,只鎖住部分map
- 高的并發級別會造成時間和空間的浪費,低的并發級別在寫線程多時會引起線程間的競爭
- 返回的迭代器是弱一致性,fail-safe并且不會拋出ConcurrentModificationException異常
- 不允許null的鍵值
- 可代替HashTable,但CHM不會鎖住整個map
java7
采用鎖分離技術,使用多個鎖來控制hash表的不同部分,每一部分相當于一個hashTable,有自己的鎖。只要多個修改操作發生在不同的segment上,就可以并發執行。
有些方法需要跨段,如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操作的時候調用addEntry方法,現在假如A線程和B線程同時對同一個數據位置調用該方法,兩個線程會同時得到頭節點,A寫入頭節點以后B也寫入新的頭節點,那B的寫入操作造成A的寫入操作丟失。
addEntry中當加入新的鍵值對后鍵值對總數超過門限值的時候會調用一個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);
這個操作會生成一個新的容量的數組,會對原數組的所有鍵值對重新進行計算和寫入新的數組,之后指向新的數組。
原因二:當多個線程同時檢測需要進行resize()操作,各自生成新的數組并rehash后賦給該map底層的數組table,結果最后只有一個線程生成的新數組被賦給table變量,其他線程的均丟失。
Map testmap = Collection.synchronizedMap(new HashMap())
equals()方法和hashCode()方法
java.lang.Object類中有兩個非常重要的方法
public boolean equals(Object obj)
public int hashCode()
Object是類繼承結構的基礎,是所有類的父類
equals()
public boolean equals(Object obj){
return (this == obj)
}
是對兩個對象的地址值進行比較。但String、Math、Integer、Double等這些封裝類在使用equals()方法時,已經覆蓋了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()方法,還有基本類型也是進行內容的比較
注意:當equals方法被override時,hashCode()也要被override。按照一般hashCode()方法的實現來說,相等的對象,它們的hash code一定相等
hashCode
- 在一個java應用的執行期間,如果一個對象提供給equals做比較的信息沒有被修改的話,該對象多次調用hashCode方法,始終返回同一個integer。
- 如果兩個對象根據equals(Object)方法是相等的,那調用二者各自的hashCode()方法必須產生同一個integer結果。
在Object類中,hashCode定義如下:
public native int hashCode();
說明是本地方法,實現跟本地機器有關,如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實現了基于動態數組的數據結構,LinkedList基于鏈表的數據結構。
- 對于隨機訪問,ArrayList優于LinkedList
- 對于新增和刪除操作,LinkedList優于ArrayList
ArrayList
ArrayList,在聲明對象時并不需要指定它的長度,對象的大小是按照其中存儲的數據來動態擴充和收縮的。
數組擴容是對ArrayList效率影響較大的一個元素。
每當執行Add、AddRange、insert、insertRange等添加元素的方法,都會檢查內部數組的容量是否夠用。若不夠,以當前容量的兩倍來重新構建數組,將舊元素COPY到數組中,然后丟棄舊數組
特定類型(Object除外)的數組的性能優于ArrayList的性能,因為ArrayList的元素屬于Object類型,所以在存儲或檢索值類型時通常發生裝箱和取消裝箱的操作。
map、list、set
list 有序可重復
set 無序不可重復
map 按鍵值對存儲,無放入順序
List接口有三個實現類:LinkedList、ArrayList、Vector
Set接口有兩個實現類:HashSet(底層由HashMap實現),LinkedHashSet
Map接口有三個實現類:HashMap、HashTable、LinkedHashMap
HashMap allows one null key and any number of null values.,而Hashtable則不行
HashTable是synchronized的,是線程安全的,而HashMap不是