只有光頭才能變強
前一陣子一直在學Redis,結果在黃金段位被虐了,暫時升不了段位了,天天都拿不到首勝(好煩)。java
趁着學校校運會,合理地給本身放了一個小長假,而後就回家了。回到家才發現當時618買了一堆書,這堆書還有沒撕包裝的呢....因而我翻出了最薄的一本《阿里巴巴 Java開發手冊》git
這本書一共就90多頁,一天就能夠通讀完了,看完以後我又來水博文了。程序員
注意:github
PDF官方地址:算法
POJO類中的布爾類型(Boolean)的變量都不要加is前綴,不然部分框架解析會引發序列化錯誤sql
java.util.Object#equals
工具類全部POJO類的屬性所有使用包裝數據類型,RPC的返回值和參數必須使用包裝數據類型,全部的局部變量都使用基本數據類型。定義VO/DTO/DO等POJO類時,不要設定任何屬性的默認值數據庫
構造方法禁止加入任何的業務邏輯,若是初始化邏輯能夠放在init方法中。set/get方法也不要增長業務邏輯。編程
在JDK7以及以上版本中,Comparator要知足三個條件,否則調用Arrays.sort()或者Collections.sort()
會報異常。數組
使用entrySet遍歷Map類集合K/V,而不是用keySet方式遍歷安全
SimpleDateFormat是線程不安全的類,通常不要定義爲static變量,若是定義爲static,必須加鎖,或者使用DateUtils工具類
避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed致使性能降低
/**內容*/
格式,不得使用 //xxx
方式///
來講明註釋代碼的理由1、不容許任何魔法值(未經預先定義的常量)直接出如今代碼中
例子:
Negative example: //Magic values, except for predefined, are forbidden in coding. if (key.equals("關注公衆號:Java3y")) { //... } Positive example: String KEY_PRE = "關注公衆號:Java3y"; if (KEY_PRE.equals(key)) { //... }
ps:我猜是把先常量定義出來,後續引用/修改的時候就很方便了。
2、Object的euqals方法容易拋出空指針異常,應使用常量或者有值的對象來調用equals。推薦使用java.util.Object#equals
工具類
java.util.Object#equals的源碼(已經判斷null的狀況了)
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
3、工具類Arrays.asList()把數組轉成List時,不能使用其修改集合的相關方法。
由於返回的ArrayList是一個內部類,並無實現集合的修改方法。後臺的數據還是數組,這裏體現的是適配器模式。
4、在JDK7以及以上版本中,Comparator要知足自反性,傳遞性,對稱性,否則調用Arrays.sort()或者Collections.sort()
會報異常。
The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y. (This implies that compare(x, y) must throw an exception if and only if compare(y, x) throws an exception.)The implementor must also ensure that the relation is transitive: ((compare(x, y)>0) && (compare(y, z)>0)) implies compare(x, z)>0.
Finally, the implementor must ensure that compare(x, y)==0 implies that sgn(compare(x, z))==sgn(compare(y, z)) for all z.
反例:下例中沒有處理相等的狀況,實際使用中可能會出現異常:
new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getId() > o2.getId() ? 1 : -1; } }
使用entrySet遍歷Map類集合K/V,而不是用keySet方式遍歷
首先咱們來看一下使用keySet是如何遍歷HashMap的:
public static void main(String[] args) throws InterruptedException { HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("關注公衆號:", "Java3y"); hashMap.put("堅持原創", "Java3y"); hashMap.put("點贊", "關注,轉發,分享"); // 獲得keySet,遍歷keySet獲得全部的key Set<String> strings = hashMap.keySet(); Iterator<String> iterator = strings.iterator(); while (iterator.hasNext()) { // HashMap的每一個key String key = iterator.next(); // 經過key能夠得到對應的value,若是有看過HashMap的同窗知道get方法的時間複雜度是O(1) System.out.println("key = " + key + ", value = " + hashMap.get(key)); } }
再來看一下源碼:
// 1. 獲得keySet,若是不存在,則建立 public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new KeySet(); keySet = ks; } return ks; } // 2.初始化ks (實際上就是Set集合[HashMap的內部類],在初始化時須要順便初始化iterator) ks = new AbstractSet<K>() { public Iterator<K> iterator() { return new Iterator<K>() { private Iterator<Entry<K,V>> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public K next() { return i.next().getKey(); } public void remove() { i.remove(); } }; } };
再來看一下entrySet,能夠直接拿到key和value,不用再使用get方法來獲得value,因此比keySet更加推薦使用!
public static void main(String[] args) throws InterruptedException { HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("關注公衆號:", "Java3y"); hashMap.put("堅持原創", "Java3y"); hashMap.put("點贊", "關注,轉發,分享"); // 獲得entrySet,遍歷entrySet獲得結果 Set<Map.Entry<String, String>> entrySet = hashMap.entrySet(); Iterator<Map.Entry<String, String>> iterator = entrySet.iterator(); while (iterator.hasNext()) { Map.Entry<String, String> entry = iterator.next(); System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue()); } }
若是是JDK8的話,推薦直接使用Map.forEach()
就行了,咱們也來看看用法:
public static void main(String[] args) throws InterruptedException { HashMap<String, String> hashMap = new HashMap<>(); hashMap.put("關注公衆號:", "Java3y"); hashMap.put("堅持原創", "Java3y"); hashMap.put("點贊", "關注,轉發,分享"); // forEach用法 hashMap.forEach((key, value) -> System.out.println("key = " + key + ", value = " + value)); }
其實在源碼裏邊咱們能夠發現,forEach實際上就是封裝了entrySet,提供forEach給咱們能夠更加方便地遍歷Map集合
// forEach源碼 default void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } }
5、SimpleDateFormat是線程不安全的類,通常不要定義爲static變量,若是定義爲static,必須加鎖,或者使用DateUtils工具類。
有如下的例子能夠正確使用SimpleDateFormat:
// 1. 在方法內部使用,沒有線程安全問題 private static final String FORMAT = "yyyy-MM-dd HH:mm:ss"; public String getFormat(Date date){ SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT); return dateFormat.format(date); } // 2. 每次使用的時候加鎖 private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void getFormat(){ synchronized (SIMPLE_DATE_FORMAT){ SIMPLE_DATE_FORMAT.format(new Date()); ….; } // 3. 使用ThreadLocal,每一個線程都有本身的SimpleDateFormat對象,互不干擾 private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; // 4. 使用DateTimeFormatter(This class is immutable and thread-safe.) DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println(timeFormatter.format(LocalDateTime.now()));
若是是JDK8應用,可使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat。
在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引,頁面搜索嚴禁左模糊或者全模糊,若是須要則經過搜索引擎來解決。
1、利用延遲關聯或者子查詢優化超多也分場景。
MySQL並非跳過 offset行,而是取 offset+N行,而後返回放棄前offset行,返回N行,那當 offset特別大的時候,效率就很是的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫。
例子:
// 優化前 SELECT id, cu_id, name, info, biz_type , gmt_create, gmt_modified, start_time, end_time, market_type , back_leaf_category, item_status, picuture_url FROM relation WHERE biz_type = '0' AND end_time >= '2014-05-29' ORDER BY id ASC LIMIT 149420, 20; // 優化後 SELECT a.* FROM relation a, ( SELECT id FROM relation WHERE biz_type = '0' AND end_time >= '2014-05-29' ORDER BY id ASC LIMIT 149420, 20 ) b WHERE a.id = b.id
解釋:其實這裏就是經過使用覆蓋索引查詢返回須要的主鍵,再根據主鍵關聯原表得到須要的數據。這樣就是充分利用了索引!
在看《手冊》的時候還有一些知識點沒看過、沒實踐過、涉及到的知識點比較多的,在這裏先mark一下,後續再遇到或者有空的時候再回來補坑~
AtomicInteger count = new AtomicInteger(); count.addAndGet(1);
若是是 JDK8,推薦使用 LongAdder 對象,比 AtomicLong 性能更好(減小樂觀鎖的重試次數)。固然了,若是你有比較好的資料閱讀,也能夠在評論區告訴我。我也會mark住好好看看。
好比說:「3y,我發現Optional類有篇文章寫得很不錯,url是xxxx(書籍的名稱是xxx)
因爲如今沒有必定的經驗積累,因此如下的章節得回頭看:
看我上面寫的內容就知道,除了一些規範外,還有不少實用的小技巧,這些對咱們開發是有幫助的。我這個階段也有一些沒怎麼接觸過的("日誌","設計","二方庫"),這些都須要我在成長中不斷的回看才行。
引用書上的一句話:
不少編程方式客觀上沒有對錯之分,一致性很重要,可讀性很重要,團隊溝通效率很重要。程序員天生須要團隊協做,而協做的正能量要放在問題的有效溝通上。個性化應儘可能表如今系統架構和算法效率的提高上,而不是在合做規範上進行糾纏不休的討論、爭論,最後沒有結論。
做者(孤盡)在知乎回答的一句話:
翻完了不表明記住了,記住了不表明理解了,理解了不表明可以應用上去,真正的知識是實踐,實踐,實踐。
若是你以爲我寫得還不錯,瞭解一下: