package test; import java.util.ArrayList; public class A implements Cloneable { public ArrayList b = new ArrayList(); // 淺克隆 // @Override // protected Object clone() throws CloneNotSupportedException { // A a = (A) super.clone(); // return a; // } // // 深克隆 // public Object clone() throws CloneNotSupportedException { // A a = (A) super.clone(); // a.b = (ArrayList) b.clone(); // return a; // } public static void main(String[] args) throws CloneNotSupportedException { A a = new A(); a.b.add("s"); A b = (A) a.clone(); a.b.add("b"); System.out.println(b.b.size()); } }
Object obj=new Object(); ReferenceQueue refQueue=new ReferenceQueue(); SoftReference softRef=new SoftReference(obj,refQueue);
String str=new String("abc"); // 強引用 SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
軟引用通常是當內存不足的時候纔會清除,內存充足的時候調用system.gc()不會清除掉軟引用。(生活無關緊要品,家滿了的時候清除,平時不動)html
弱引用:也是配合一個引用隊列使用,當被清理的時候,將其放入引用隊列中。當gc掃描到該對象的時候,不管內存是否已滿,都會回收。是一種比軟引用生命力更弱的引用。可是因爲gc的優先級較低,因此被清理的String str=new String("abc"); WeakReference<String> abcWeakRef = new WeakReference<String>(str);
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
一、重寫equals方法時須要重寫hashCode方法,主要是針對Map、Set等集合類型的使用;java
a: Map、Set等集合類型存放的對象必須是惟一的;node
b: 集合類判斷兩個對象是否相等,是先判斷equals是否相等,若是equals返回TRUE,還要再判斷HashCode返回值是否ture,只有二者都返回ture,才認爲該兩個對象是相等的。算法
二、因爲Object的hashCode返回的是對象的hash值,因此即便equals返回TRUE,集合也可能斷定兩個對象不等,因此必須重寫hashCode方法,以保證當equals返回TRUE時,hashCode也返回Ture,這樣才能使得集合中存放的對象惟一。spring
1. Interface Iterable 迭代器接口,這是Collection類的父接口。實現這個Iterable接口的對象容許使用foreach進行遍歷,也就是說,全部的Collection集合對象都具備"foreach可遍歷性"。這個Iterable接口只
有一個方法: iterator()。它返回一個表明當前集合對象的泛型<T>迭代器,用於以後的遍歷操做 1.1 Collection Collection是最基本的集合接口,一個Collection表明一組Object的集合,這些Object被稱做Collection的元素。Collection是一個接口,用以提供規範定義,不能被實例化使用 1) Set Set集合相似於一個罐子,"丟進"Set集合裏的多個對象之間沒有明顯的順序。Set繼承自Collection接口,不能包含有重複元素(記住,這是整個Set類層次的共有屬性)。 Set判斷兩個對象相同不是使用"=="運算符,而是根據equals方法。也就是說,咱們在加入一個新元素的時候,若是這個新元素對象和Set中已有對象進行注意equals比較都返回false,
則Set就會接受這個新元素對象,不然拒絕。 由於Set的這個制約,在使用Set集合的時候,應該注意兩點:1) 爲Set集合裏的元素的實現類實現一個有效的equals(Object)方法、2) 對Set的構造函數,傳入的Collection參數不能包
含重複的元素 1.1) HashSet HashSet是Set接口的典型實現,HashSet使用HASH算法來存儲集合中的元素,所以具備良好的存取和查找性能。當向HashSet集合中存入一個元素時,HashSet會調用該對象的
hashCode()方法來獲得該對象的hashCode值,而後根據該HashCode值決定該對象在HashSet中的存儲位置。 值得主要的是,HashSet集合判斷兩個元素相等的標準是兩個對象經過equals()方法比較相等,而且兩個對象的hashCode()方法的返回值相等 1.1.1) LinkedHashSet LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但和HashSet不一樣的是,它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。
當遍歷LinkedHashSet集合裏的元素時,LinkedHashSet將會按元素的添加順序來訪問集合裏的元素。 LinkedHashSet須要維護元素的插入順序,所以性能略低於HashSet的性能,但在迭代訪問Set裏的所有元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷) 1.2) SortedSet 此接口主要用於排序操做,即實現此接口的子類都屬於排序的子類 1.2.1) TreeSet TreeSet是SortedSet接口的實現類,TreeSet能夠確保集合元素處於排序狀態 1.3) EnumSet EnumSet是一個專門爲枚舉類設計的集合類,EnumSet中全部元素都必須是指定枚舉類型的枚舉值,該枚舉類型在建立EnumSet時顯式、或隱式地指定。EnumSet的集合元素也是有序的,
它們以枚舉值在Enum類內的定義順序來決定集合元素的順序 2) List List集合表明一個元素有序、可重複的集合,集合中每一個元素都有其對應的順序索引。List集合容許加入重複元素,由於它能夠經過索引來訪問指定位置的集合元素。List集合默認按元素
的添加順序設置元素的索引 2.1) ArrayList ArrayList是基於數組實現的List類,它封裝了一個動態的增加的、容許再分配的Object[]數組。 2.2) Vector Vector和ArrayList在用法上幾乎徹底相同,但因爲Vector是一個古老的集合,因此Vector提供了一些方法名很長的方法,但隨着JDK1.2之後,java提供了系統的集合框架,就將
Vector改成實現List接口,統一納入集合框架體系中 2.2.1) Stack Stack是Vector提供的一個子類,用於模擬"棧"這種數據結構(LIFO後進先出) 2.3) LinkedList implements List<E>, Deque<E>。實現List接口,能對它進行隊列操做,便可以根據索引來隨機訪問集合中的元素。同時它還實現Deque接口,即能將LinkedList看成雙端隊列
使用。天然也能夠被看成"棧來使用" 3) Queue Queue用於模擬"隊列"這種數據結構(先進先出 FIFO)。隊列的頭部保存着隊列中存放時間最長的元素,隊列的尾部保存着隊列中存放時間最短的元素。新元素插入(offer)到隊列的尾部,
訪問元素(poll)操做會返回隊列頭部的元素,隊列不容許隨機訪問隊列中的元素。結合生活中常見的排隊就會很好理解這個概念 3.1) PriorityQueue PriorityQueue並非一個比較標準的隊列實現,PriorityQueue保存隊列元素的順序並非按照加入隊列的順序,而是按照隊列元素的大小進行從新排序,這點從它的類名也能夠
看出來 3.2) Deque Deque接口表明一個"雙端隊列",雙端隊列能夠同時從兩端來添加、刪除元素,所以Deque的實現類既能夠當成隊列使用、也能夠當成棧使用 3.2.1) ArrayDeque 是一個基於數組的雙端隊列,和ArrayList相似,它們的底層都採用一個動態的、可重分配的Object[]數組來存儲集合元素,當集合元素超出該數組的容量時,系統會在底層重
新分配一個Object[]數組來存儲集合元素 3.2.2) LinkedList 1.2 Map Map用於保存具備"映射關係"的數據,所以Map集合裏保存着兩組值,一組值用於保存Map裏的key,另一組值用於保存Map裏的value。key和value均可以是任何引用類型的數據。Map的key不允
許重複,即同一個Map對象的任何兩個key經過equals方法比較結果老是返回false。 關於Map,咱們要從代碼複用的角度去理解,java是先實現了Map,而後經過包裝了一個全部value都爲null的Map就實現了Set集合 Map的這些實現類和子接口中key集的存儲形式和Set集合徹底相同(即key不能重複) Map的這些實現類和子接口中value集的存儲形式和List很是相似(即value能夠重複、根據索引來查找) 1) HashMap 和HashSet集合不能保證元素的順序同樣,HashMap也不能保證key-value對的順序。而且相似於HashSet判斷兩個key是否相等的標準也是: 兩個key經過equals()方法比較返回true、
同時兩個key的hashCode值也必須相等 1.1) LinkedHashMap LinkedHashMap也使用雙向鏈表來維護key-value對的次序,該鏈表負責維護Map的迭代順序,與key-value對的插入順序一致(注意和TreeMap對全部的key-value進行排序進行區
分) 2) Hashtable 是一個古老的Map實現類 2.1) Properties Properties對象在處理屬性文件時特別方便(windows平臺上的.ini文件),Properties類能夠把Map對象和屬性文件關聯起來,從而能夠把Map對象中的key-value對寫入到屬性文
件中,也能夠把屬性文件中的"屬性名-屬性值"加載到Map對象中 3) SortedMap 正如Set接口派生出SortedSet子接口,SortedSet接口有一個TreeSet實現類同樣,Map接口也派生出一個SortedMap子接口,SortedMap接口也有一個TreeMap實現類 3.1) TreeMap TreeMap就是一個紅黑樹數據結構,每一個key-value對即做爲紅黑樹的一個節點。TreeMap存儲key-value對(節點)時,須要根據key對節點進行排序。TreeMap能夠保證全部的
key-value對處於有序狀態。一樣,TreeMap也有兩種排序方式: 天然排序、定製排序 4) WeakHashMap WeakHashMap與HashMap的用法基本類似。區別在於,HashMap的key保留了對實際對象的"強引用",這意味着只要該HashMap對象不被銷燬,該HashMap所引用的對象就不會被垃圾回收。
但WeakHashMap的key只保留了對實際對象的弱引用,這意味着若是WeakHashMap對象的key所引用的對象沒有被其餘強引用變量所引用,則這些key所引用的對象可能被垃圾回收,當垃
圾回收了該key所對應的實際對象以後,WeakHashMap也可能自動刪除這些key所對應的key-value對 5) IdentityHashMap IdentityHashMap的實現機制與HashMap基本類似,在IdentityHashMap中,當且僅當兩個key嚴格相等(key1 == key2)時,IdentityHashMap才認爲兩個key相等 6) EnumMap EnumMap是一個與枚舉類一塊兒使用的Map實現,EnumMap中的全部key都必須是單個枚舉類的枚舉值。建立EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap根據key的天然順序
(即枚舉值在枚舉類中的定義順序)
Entry對象惟一表示一個鍵值對,有四個屬性:數據庫
-K key 鍵對象
-V value 值對象
-int hash 鍵對象的hash值
-Entry entry 指向鏈表中下一個Entry對象,可爲null,表示當前Entry對象在鏈表尾部編程
內部有一個指向entry的數組,數組的每一個元素都是一個entry,每一個entry結構又是個鏈表結構,指向下一個entry。windows
1.8以前當發生hash衝突的時候可使用entry的鏈表來解決衝突;可是很難保證hashmap的均勻性,很容易出現很長的鏈表,影響查詢效率。設計模式
1.8以後使用了紅黑樹解決hash衝突,具體的能夠看:http://www.javashuo.com/article/p-majqhclb-n.htmlapi
以上兩者是相同的瞭解了HashMap爲何線程不安全,那如今看看如何線程安全的使用HashMap。這個無非就是如下三種方式:
例子:
//Hashtable Map<String, String> hashtable = new Hashtable<>(); //synchronizedMap Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>()); //ConcurrentHashMap Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
依次來看看。
先稍微吐槽一下,爲啥命名不是HashTable啊,看着好難受,無論了就裝做它叫HashTable吧。這貨已經不經常使用了,就簡單說說吧。HashTable源碼中是使用synchronized
來保證線程安全的,好比下面的get方法和put方法:
public synchronized V get(Object key) { // 省略實現 } public synchronized V put(K key, V value) { // 省略實現 }
因此當一個線程訪問HashTable的同步方法時,其餘線程若是也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用put方法時,另外一個線程不但不可使用put方法,連get方法都不能夠,好霸道啊!!!so~~,效率很低,如今基本不會選擇它了。
ConcurrentHashMap(如下簡稱CHM)是JUC包中的一個類,Spring的源碼中有不少使用CHM的地方。以前已經翻譯過一篇關於ConcurrentHashMap的博客,如何在java中使用ConcurrentHashMap,裏面介紹了CHM在Java中的實現,CHM的一些重要特性和什麼狀況下應該使用CHM。須要注意的是,上面博客是基於Java 7的,和8有區別,在8中CHM摒棄了Segment(鎖段)的概念,而是啓用了一種全新的方式實現,利用CAS算法,有時間會從新總結一下。
看了一下源碼,SynchronizedMap的實現仍是很簡單的。
// 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的操做是線程安全的。
這是要靠數聽說話的時代,因此不能只靠嘴說CHM快,它就快了。寫個測試用例,實際的比較一下這三種方式的效率(源碼來源),下面的代碼分別經過三種方式建立Map對象,使用ExecutorService
來併發運行5個線程,每一個線程添加/獲取500K個元素。
public class CrunchifyConcurrentHashMapVsSynchronizedMap { public final static int THREAD_POOL_SIZE = 5; public static Map<String, Integer> crunchifyHashTableObject = null; public static Map<String, Integer> crunchifySynchronizedMapObject = null; public static Map<String, Integer> crunchifyConcurrentHashMapObject = null; public static void main(String[] args) throws InterruptedException { // Test with Hashtable Object crunchifyHashTableObject = new Hashtable<>(); crunchifyPerformTest(crunchifyHashTableObject); // Test with synchronizedMap Object crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>()); crunchifyPerformTest(crunchifySynchronizedMapObject); // Test with ConcurrentHashMap Object crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>(); crunchifyPerformTest(crunchifyConcurrentHashMapObject); } public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException { System.out.println("Test started for: " + crunchifyThreads.getClass()); long averageTime = 0; for (int i = 0; i < 5; i++) { long startTime = System.nanoTime(); ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE); for (int j = 0; j < THREAD_POOL_SIZE; j++) { crunchifyExServer.execute(new Runnable() { @SuppressWarnings("unused") @Override public void run() { for (int i = 0; i < 500000; i++) { Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000); // Retrieve value. We are not using it anywhere Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber)); // Put value crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber); } } }); } // Make sure executor stops crunchifyExServer.shutdown(); // Blocks until all tasks have completed execution after a shutdown request crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); long entTime = System.nanoTime(); long totalTime = (entTime - startTime) / 1000000L; averageTime += totalTime; System.out.println("2500K entried added/retrieved in " + totalTime + " ms"); } System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n"); } }
測試結果:
這個就不用廢話了,CHM性能是明顯優於Hashtable和SynchronizedMap的,CHM花費的時間比前兩個的一半還少,哈哈,之後再有人問就能夠甩數據了。
Test started for: class java.util.Hashtable 2500K entried added/retrieved in 2018 ms 2500K entried added/retrieved in 1746 ms 2500K entried added/retrieved in 1806 ms 2500K entried added/retrieved in 1801 ms 2500K entried added/retrieved in 1804 ms For class java.util.Hashtable the average time is 1835 ms Test started for: class java.util.Collections$SynchronizedMap 2500K entried added/retrieved in 3041 ms 2500K entried added/retrieved in 1690 ms 2500K entried added/retrieved in 1740 ms 2500K entried added/retrieved in 1649 ms 2500K entried added/retrieved in 1696 ms For class java.util.Collections$SynchronizedMap the average time is 1963 ms Test started for: class java.util.concurrent.ConcurrentHashMap 2500K entried added/retrieved in 738 ms 2500K entried added/retrieved in 696 ms 2500K entried added/retrieved in 548 ms 2500K entried added/retrieved in 1447 ms 2500K entried added/retrieved in 531 ms For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms
通常狀況下,咱們用的最多的是HashMap,HashMap裏面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
TreeMap取出來的是排序後的鍵值對。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。
LinkedHashMap 是HashMap的一個子類,若是須要輸出的順序和輸入的相同,那麼用LinkedHashMap能夠實現,它還能夠按讀取順序來排列,像鏈接池中能夠應用。
什麼是反射:
一、在運行狀態中,對於任意一個類,都可以知道這個類的屬性和方法。
二、對於任意一個對象,都可以調用它的任何方法和屬性。
這種動態獲取信息以及動態調用對象的方法的功能稱爲JAVA的反射。
反射的做用:
在JAVA中,只有給定類的名字,就能夠經過反射機制來獲取類的全部信息,能夠動態的建立對象和編譯。
實現原理:
JAVA語言編譯以後會生成一個.class文件,反射就是經過字節碼文件找到某一個類、類中的方法以及屬性等。
反射的實現主要藉助如下四個類:
Class:類的對象
Constructor:類的構造方法
Field:類中的屬性對象
Method:類中的方法對象
根據類的名字獲取類:Class<T> c = Class.forName("類名");
根據類c獲取對象:Object o = c.newInstance();
根據類c獲取實現的接口:Class<?> interfaces = c.getInterfaces();
獲取有參構造器:Constructor<?> con = c.getConstructor(String.class,int.class);
獲取屬性:Field f2 = c.getField("age");
方法調用:
public class ReflectCase { public static void main(String[] args) throws Exception { Proxy target = new Proxy(); Method method = Proxy.class.getDeclaredMethod("run"); method.invoke(target); } static class Proxy { public void run() { System.out.println("run"); } } }
AOP是面向切面編程,是spring中的一個特性,能夠增長業務的重用性,一旦某一功能出現問題直接修改該功能所在模塊就好。隔離業務,下降耦合度,提升開發效率。經常使用來處理日誌記錄,性能統計,安全分析等。
OOP是java的基本編程思想,面向對象編程。針對業務處理過程當中的實體以及屬性和行爲進行封裝,以得到更加清晰的邏輯劃分。
區別:對於某一類物體,其所具備的屬性和動做方法可使用oop來進行封裝;而對於一個行爲片斷咱們只能使用aop進行隔離,最經常使用的AOP應用在數據庫鏈接以及事務處理上,實現模式爲代理模式和工廠模式。