1、Java
Java的優點
Java有哪些特性,舉個多態的例子。
abstract interface區別
有抽象方法必定是抽象類嗎?抽象類必定有抽象方法嗎?
Java的反射機制
super()和this()能不能同時使用
hashcode,equals,Object的這兩個方法默認返回什麼?描述了一下爲何重寫equals方法必須重寫hashcode方法
final
String,StringBuffer,StringBuilder區別
String爲何不可變
String,是否能夠繼承,「+」怎樣實現
字符串常量池
map、list、set的區別
有沒有有序的set?
Set如何保證不重複?
說一說對Java io的理解
nio與bio的瞭解以及說一下區別
Java併發的理解
死鎖,死鎖緣由
wait和sleep的區別
ArrayList和LinkedList有什麼區別?
HashMap 的原理,hashmap的擴容問題,爲何HashMap的初始容量會是16,爲何是2倍擴容,實現簡單的 get/put操做;處理哈希衝突用的哪一種方法(拉鍊),還知道什麼處理哈希衝突的方法(開放地址檢測),開放地址檢測怎麼實現的
從哈希表中刪除一個元素,再加入元素時剛好與原來那個哈希衝突,這個元素會放在哪
HashMap、Hashtable、concurrenthashmap
HashTable爲何是線程安全的?
HashMap,ConcurrentHashMap以及在什麼狀況下性能會很差
Thread狀態有哪些
多線程實現方法
Java如何實現線程安全
Synchronized和lock區別
Java中都有什麼鎖
可重入鎖的設計思路是什麼
樂觀鎖和悲觀鎖
juc包內有哪些類
CAS如何實現
BlockQueue見過沒?
線程池原理
線程池的排隊策略和拒絕策略的試用條件和具體內容。
線程池的類型,詳細介紹cached和fixed
corePoolSize參數的意義
線程池新任務到達時會先使用空閒線程仍是加入阻塞隊列
Java併發包裏面的CountdownLatch怎麼使用
volatile和synchronized區別
線程池使用時通常要考慮哪些問題
通常線程和守護線程的區別
一致性Hash原理,實現負載均衡
異常
servlet流程
forward redirect 二次請求
序列化,以及json傳輸
tomcat均衡方式
netty
2、JVM
JVM內存劃分
GC
垃圾回收器
Java對象頭
內存泄漏
類加載過程
雙親委派模型,爲何要使用雙親委派模型
Java虛擬機的一些參數配置
爲何jvm調優常常會將-Xms和-Xmx參數設置成同樣
3、數據結構與算法
常見的排序算法時間複雜度
快排算法 寫代碼
堆排序怎麼實現
鏈表,數組的優缺點,應用場景,查找元素的複雜度
入棧出棧的時間複雜度,鏈表插入和刪除的時間複雜度
如何用LinkedList實現堆棧操做
Arraylist如何實現排序
利用數組,實現一個循環隊列類
兩個有序數組,有相同的元素,找出來
二叉樹怎麼實現的
二叉樹前中後序遍歷 深度 廣度
二叉樹深度
層序遍歷二叉樹
樹的中序遍歷,除了遞歸和棧還有什麼實現方式
二叉搜索樹轉換成一個排好序的雙向鏈表
判斷平衡二叉樹
給定一個2叉樹,打印每一層最右邊的結點
一棵普通樹(非二叉搜索樹),找出一條路徑和最大
一棵樹,求全部路徑之和
最長公共子序列
反轉鏈表
判斷一個數是否是醜數
找出一個字符串中字符連續相同的最長子串,如aabbccc,結果就是ccc
蓄水池抽樣算法
尋找一個字符串中第一個只出現一次的字符
給定一個數組,裏面只有一個數出現了一次,其餘都出現了兩次。怎麼獲得這個出現了一次的數?
給定一個數組,若是有兩個不一樣數的出現了一次,其餘出現了兩次,怎麼獲得這兩個數?
海量數據topk問題
4、操做系統
進程和線程區別
不一樣進程打開了同一個文件,那麼這兩個進程獲得的文件描述符(fd)相同嗎?
操做系統如何實現輸出
進程通訊
5、網絡
OSI七層網絡模型中,你對哪層最瞭解?瞭解哪些協議?作過web開發?
HTTP 0.9/1.0/1.1/2
HTTP 和 HTTPS 有什麼區別?
知道 HTTPS 通訊過程嗎?
TCP三次握手
爲何三次握手和四次揮手
TCP與HTTP有什麼關係
Tcp鏈接4次揮手的緣由。Time_wait等待超時了會怎樣?
SSL 握手
session/cookie
當你在瀏覽器地址欄輸入一個URL後回車,將會發生的事情?
DNS域名解析過程
ping工做原理
Get和Post請求
HTTP狀態碼
6、數據庫
數據庫事務的四個隔離級別,MySql在哪個級別
數據庫死鎖/如何防止
mysql索引,索引機制,彙集索引和非彙集索引,如何建立索引,實現原理,創建準則,優缺點,注意事項,
索引在什麼狀況下失效
說一下對B+樹的瞭解
innodb創建的索引,若是字段重複率很高索引,索引是什麼樣,查找效率如何
innodb在插入的時候,是否會給行上鎖
說一下innodb的默認隔離級別
數據庫設計(訂單、購物車和商品)
sql中join的幾種操做的區別
union和union all的區別,誰的效率更高
用distinct和用group by去重,誰的效率更高
sql中的優化,怎麼提升查詢效率
緩存的穿透和雪崩,解決辦法
redis的排序算法
redis集羣
redis過時策略
Redis如何解決key衝突
redis數據類型+redis是單線程的麼,爲何呢
redis和memcache區別
redis與mysql的區別以及優缺點
7、設計模式
單例模式裏面的雙重檢查鎖定的原理,以及爲何使用volatile
生產者消費者
工廠,說下原理和應用
策略模式
適配器模式
裝飾模式
代理模式
線程池使用了什麼設計模式
JDK中哪些體現了命令模式
8、框架
介紹下SpringBoot
Spring IOC AOP
Spring IOC有哪些好處
IOC涉及到的設計模式
AOP的應用場景,具體介紹,配置文件中須要寫什麼?具體註解須要寫啥?
說說靜態代理和動態代理
Spring事務傳播,隔離級別
Spring bean初始化過程
Spring如何生成一個Bean?配置文件寫完了以後又怎麼生成?
Mybatis 傳參
Mybatis中 # 和 $ 區別
Mybatis緩存
SpringMVC的運行流程
說幾個SpringMVC的幾個註解,都是幹啥的?
@autireware和@resource的區別
@PathVariable是幹啥的?
說說filter、servlet、listener。
消息隊列了解嗎?
9、分佈式
Raft協議的leader選舉,正常狀況下,網絡抖動形成follower發起leader選舉,且該follower的Term比現有leader高。集羣中全部結點的日誌信息當前一致,這種狀況下會選舉成功嗎?
分佈式框架知道哪些?
dubbo怎麼用的,有沒有參與部署
分佈式緩存的理解
10、Linux
linux查詢Java進程
linux查看內存佔用狀況
11、雜項
設計一個秒殺系統,如何保證不超賣,還要保證服務可用
如何設計一個定時器定時完成某個任務?
如何保證集羣環境下搶購的併發安全?
Java中你擅長的地方
若是學習一門技術,你會怎麼學習
對國內互聯網公司目前的開源生態有沒有什麼瞭解
你對京東的見解
說出三個京東不如淘寶或者天貓的地方
看過啥書。css
平臺無關性、垃圾回收html
封裝、繼承、多態java
含有abstract修飾符的class即爲抽象類,abstract類不能建立的實例對象。含有abstract方法的類必須定義爲abstract class,abstract class類中的方法沒必要是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,因此,不能有抽象構造方法或抽象靜態方法。若是的子類沒有實現抽象父類中的全部抽象方法,那麼子類也必須定義爲abstract類型。node
接口(interface)能夠說成是抽象類的一種特例,接口中的全部方法都必須是抽象的。接口中的方法定義默認爲public abstract類型,接口中的成員變量類型默認爲public static final。mysql
下面比較一下二者的語法區別:linux
抽象類能夠有構造方法,接口中不能有構造方法。
抽象類中能夠有普通成員變量,接口中沒有普通成員變量
抽象類中能夠包含非抽象的普通方法,接口中的能夠有非抽象方法,好比deaflut方法
抽象類中的抽象方法的訪問類型能夠是public,protected和(默認類型,雖然
eclipse下不報錯,但應該也不行),但接口中的抽象方法只能是public類型的,而且默認即爲public abstract類型。
抽象類中能夠包含靜態方法,接口中不能包含靜態方法
抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任意,但接口中定義的變量只能是public static final類型,而且默認即爲public static final類型。
一個類能夠實現多個接口,但只能繼承一個抽象類。web
有抽象方法不必定是抽象類,也多是接口。抽象類不必定有抽象方法,能夠有非抽象的普通方法。redis
在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲Java語言的反射機制。算法
反射的核心是JVM在運行時才動態加載類或調用方法/訪問屬性,它不須要事先知道運行對象是誰。spring
不能同時使用,this和super不能同時出如今一個構造函數裏面,由於this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,因此在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會經過。
默認的hashCode方法會利用對象的地址來計算hashcode值,不一樣對象的hashcode值是不同的。
public boolean equals(Object obj) {
return (this == obj);
}
能夠看出Object類中的equals方法與「==」是等價的,也就是說判斷對象的地址是否相等。Object類中的equals方法進行的是基於內存地址的比較。
通常對於存放到Set集合或者Map中鍵值對的元素,須要按須要重寫hashCode與equals方法,以保證惟一性。
final關鍵字能夠用於成員變量、本地變量、方法以及類。
final成員變量必須在聲明的時候初始化或者在構造器中初始化,不然就會報編譯錯誤。
你不可以對final變量再次賦值。
本地變量必須在聲明時賦值。
在匿名類中全部變量都必須是final變量。
final方法不能被重寫。
final類不能被繼承。
接口中聲明的全部變量自己是final的。
final和abstract這兩個關鍵字是反相關的,final類就不多是abstract的。
final方法在編譯階段綁定,稱爲靜態綁定(static binding)。
沒有在聲明時初始化final變量的稱爲空白final變量(blank final variable),它們必須在構造器中初始化,或者調用this()初始化。不這麼作的話,編譯器會報錯「final變量(變量名)須要進行初始化」。
將類、方法、變量聲明爲final可以提升性能,這樣JVM就有機會進行估計,而後優化。
按照Java代碼慣例,final變量就是常量,並且一般常量名要大寫。
String內容不可變,StringBuffer和StringBuilder內容可變;
StringBuilder非線程安全(單線程使用),String與StringBuffer線程安全(多線程使用);
若是程序不是多線程的,那麼使用StringBuilder效率高於StringBuffer。
public final class String
implements java.io.Serializable, Comparable
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */ private int hash; // Default to 0</string>
String 的底層實現是依靠 char[] 數組,既然依靠的是基礎類型變量,那麼他必定是可變的, String 之因此不可變,是由於 Java 的開發者經過技術實現,隔絕了使用者對 String 的底層數據的操做。
String不能夠繼承,由於String被final修飾,而final修飾的類是不能被繼承的。
String爲不可變的,每次String對象作累加時都會建立StringBuilder對象。
// 程序編譯期即加載完成對象s1爲"ab"
String s1 = "a" + "b";
// 這種方式,JVM會先建立一個StringBuilder,而後經過其append方法完成累加操做
String s1 = "a";
String s2 = "b";
String s3 = s1 + s2; // 等效於 String s3 = (new StringBuilder(s1)).append(s2).toString();
List:
能夠容許重複的對象。
能夠插入多個null元素。
是一個有序容器,保持了每一個元素的插入順序,輸出的順序就是插入的順序。
經常使用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最爲流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於常常須要從 List中添加或刪除元素的場合更爲合適。
Set:
不容許重複對象
無序容器,你沒法保證每一個元素的存儲順序,TreeSet經過 Comparator 或者 Comparable 維護了一個排序順序。
只容許一個 null 元素
Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 接口,所以 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。
Map:
Map不是collection的子接口或者實現類。Map是一個接口。
Map 的 每一個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是惟一的。
TreeMap 也經過 Comparator 或者 Comparable 維護了一個排序順序。
Map 裏你能夠擁有隨意個 null 值但最多隻能有一個 null 鍵。
Map 接口最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最經常使用)
有,LinkedHashSet和TreeSet
HashSet中add()中調用了HashMap的put(),將一個key-value對放入HashMap中時,首先根據key的hashCode()返回值決定該Entry的存儲位置,若是兩個key的hash值相同,那麼它們的存儲位置相同。若是這個兩個key的equals比較返回true。那麼新添加的Entry的value會覆蓋原來的Entry的value,key不會覆蓋。所以,若是向HashSet中添加一個已經存在的元素,新添加的集合元素不會覆蓋原來已有的集合元素。
IO,其實意味着:數據不停地搬入搬出緩衝區而已(使用了緩衝區)。
BIO:同步阻塞式IO,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。
NIO:同步非阻塞式IO,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。
Java是一種多線程編程語言,咱們可使用Java來開發多線程程序。 多線程程序包含兩個或多個可同時運行的部分,每一個部分能夠同時處理不一樣的任務,從而能更好地利用可用資源,特別是當您的計算機有多個CPU時。多線程使您可以寫入多個活動,能夠在同一程序中同時進行操做處理。
兩個或者多個線程之間相互等待,致使線程都沒法執行,叫作線程死鎖。
互斥條件:使用的資源是不能共享的。
不可搶佔條件:線程持有一個資源並等待獲取一個被其餘線程持有的資源。
請求與保持條件:線程持有一個資源並等待獲取一個被其餘線程持有的資源。
循環等待條件:線程之間造成一種首尾相連的等待資源的關係。
wait和notify方法定義在Object類中,所以會被全部的類所繼承。 這些方法都是final的,即它們都是不能被重寫的,不能經過子類覆寫去改變它們的行爲。 而sleep方法是在Thread類中是由native修飾的,本地方法。
當線程調用了wait()方法時,它會釋放掉對象的鎖。
另外一個會致使線程暫停的方法:Thread.sleep(),它會致使線程睡眠指定的毫秒數,但線程在睡眠的過程當中是不會釋放掉對象的鎖的。
由於wait方法會釋放鎖,因此調用該方法時,當前的線程必須擁有當前對象的monitor,也即lock,就是鎖。要確保調用wait()方法的時候擁有鎖,即wait()方法的調用必須放在synchronized方法或synchronized塊中。
ArrayList是實現了基於動態數組的數據結構,LinkedList基於雙向鏈表的數據結構。
對於隨機訪問get和set,ArrayList優於LinkedList,由於LinkedList要移動指針。
對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。
synchronized鎖住了
新建、就緒、運行、阻塞、死亡
繼承Thread類建立線程類,重寫run方法,run方法就是表明線程須要完成的任務,調用線程對象的start()來啓動該線程,線程類已經繼承了Thread類,因此不能再繼承其餘父類。
實現Runnable接口建立線程類,定義Runnable實現類,重寫run方法
實現Callable接口,重寫call()方法,call()做爲線程的執行體,具備返回值
線程池,使用線程池產生線程對象java.util.concurrent.ExecutorService、java.util.concurrent.Executors
互斥同步:推薦使用 synchronized 關鍵字進行同步, 在 concurrent包中有ReentrantLock類, 實現效果差很少. 仍是推薦原生態的synchronized.
非阻塞同步:須要硬件指令完成.經常使用的指令有:
Test-and-Set
Fetch-and-Increment
Swap
Compare-and-Swap (CAS)
Load-Linked/Store-Conditional (LL/SC)
典型的應用在 AtomicInteger 中
無同步方案:將變量保存在本地線程中,就不會出現多個線程併發的錯誤了。
java中主要使用的就是ThreadLocal這個類。
Lock提供了synchronized關鍵字所不具有的主要特性有:
嘗試非阻塞地獲取鎖boolean tryLock():當前線程嘗試獲取鎖,若是這一時刻沒有被其餘線程獲取到,則成功獲取並持有鎖
能被中斷地獲取鎖void lockInterruptibly():當獲取到鎖的線程被中斷時,中斷異常拋出同時會釋放鎖
超時獲取鎖boolean trylock(long time, TimeUnit unit):在指定截止時間以前獲取鎖,若是在截止時間仍舊沒法獲取鎖,則返回
synchronized是JVM提供的加鎖,悲觀鎖;lock是Java語言實現的,並且是樂觀鎖。
ReentrantLock是基於AQS實現的,因爲AQS是基於FIFO隊列的實現
重量級鎖、顯式鎖、併發容器、併發同步器、CAS、volatile、AQS等
可重入公平鎖獲取流程
在獲取鎖的時候,若是當前線程以前已經獲取到了鎖,就會把state加1,在釋放鎖的時候會先減1,這樣就保證了同一個鎖能夠被同一個線程獲取屢次,而不會出現死鎖的狀況。這就是ReentrantLock的可重入性。
對於非公平鎖而言,調用lock方法後,會先嚐試搶佔鎖,在各類判斷的時候會先忽略等待隊列,若是鎖可用,就會直接搶佔使用。
悲觀鎖:假定會發生併發衝突,則屏蔽一切可能違反數據完整性的操做
樂觀鎖:假定不會發生併發衝突,只在數據提交時檢查是否違反了數據完整性(不能解決髒讀問題)
CountDownLatch 同步計數器,主要用於線程間的控制,但計數沒法被重置,若是須要重置計數,請考慮使用 CyclicBarrier 。
(線程池的排隊策略)
核心線程數
核心線程會一直存活,即便沒有任務須要執行
當線程數小於核心線程數時,即便有線程空閒,線程池也會優先建立新線程處理
設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉
這個類是一個同步計數器,主要用於線程間的控制,當CountDownLatch的count計數>0時,await()會形成阻塞,直到count變爲0,await()結束阻塞,使用countDown()會讓count減1。CountDownLatch的構造函數能夠設置count值,當count=1時,它的做用相似於wait()和notify()的做用。若是我想讓其餘線程執行完指定程序,其餘全部程序都執行結束後我再執行,這時能夠用CountDownLatch,但計數沒法被重置,若是須要重置計數,請考慮使用 CyclicBarrier 。
volatile是變量修飾符,其修飾的變量具備可見性,Java的作法是將該變量的操做放在寄存器或者CPU緩存上進行,以後纔會同步到主存,使用volatile修飾符的變量是直接讀寫主存,volatile不保證原子性,同時volatile禁止指令重排
synchronized做用於一段代碼或者方法,保證可見性,又保證原子性,可見性是synchronized或者Lock能保證通一個時刻只有一個線程獲取鎖而後執行不一樣代碼,而且在釋放鎖以前會對變量的修改刷新到主存中去,原子性是指要麼不執行,要執行就執行到底
java中的線程分爲兩種:守護線程(Daemon)和用戶線程(User)。
任何線程均可以設置爲守護線程和用戶線程,經過方法Thread.setDaemon(bool on);true則把該線程設置爲守護線程,反之則爲用戶線程。Thread.setDaemon()必須在Thread.start()以前調用,不然運行時會拋出異常。
惟一的區別是判斷虛擬機(JVM)什麼時候離開,Daemon是爲其餘線程提供服務,若是所有的User Thread已經撤離,Daemon 沒有可服務的線程,JVM撤離。也能夠理解爲守護線程是JVM自動建立的線程(但不必定),用戶線程是程序建立的線程;好比JVM的垃圾回收線程是一個守護線程,當全部線程已經撤離,再也不產生垃圾,守護線程天然就沒事可幹了,當垃圾回收線程是Java虛擬機上僅剩的線程時,Java虛擬機會自動離開。
JVM內存模型
程序計數器:記錄正在執行的虛擬機字節碼指令的地址(若是正在執行的是本地方法則爲空)。
Java虛擬機棧:每一個 Java 方法在執行的同時會建立一個棧幀用於存儲局部變量表、操做數棧、常量池引用等信息。每個方法從調用直至執行完成的過程,就對應着一個棧幀在 Java 虛擬機棧中入棧和出棧的過程。
本地方法棧:與 Java 虛擬機棧相似,它們之間的區別只不過是本地方法棧爲本地方法服務。
Java堆:幾乎全部對象實例都在這裏分配內存。是垃圾收集的主要區域("GC 堆"),虛擬機把 Java 堆分紅如下三塊:
新生代
老年代
永久代
新生代又可細分爲Eden空間、From Survivor空間、To Survivor空間,默認比例爲8:1:1。
方法區:方法區(Method Area)與Java堆同樣,是各個線程共享的內存區域。Object Class Data(類定義數據)是存儲在方法區的,此外,常量、靜態變量、JIT編譯後的代碼也存儲在方法區。
運行時常量池:運行時常量池是方法區的一部分。Class 文件中的常量池(編譯器生成的各類字面量和符號引用)會在類加載後被放入這個區域。除了在編譯期生成的常量,還容許動態生成,例如 String 類的 intern()。這部分常量也會被放入運行時常量池。
直接內存:直接內存(Direct Memory)並非虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,可是這部份內存也被頻繁地使用,並且也可能致使OutOfMemoryError 異常出現。避免在Java堆和Native堆中來回複製數據。
垃圾回收算法包括:標記-清除算法,複製算法,標記-整理算法,分代收集算法。
標記—清除算法:
標記/清除算法,分爲「標記」和「清除」兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收全部被標記的對象。
標記階段:標記的過程其實就是前面介紹的可達性分析算法的過程,遍歷全部的GC Roots對象,對從GC Roots對象可達的對象都打上一個標識,通常是在對象的header中,將其記錄爲可達對象;
清除階段:清除的過程是對堆內存進行遍歷,若是發現某個對象沒有被標記爲可達對象,則將其回收。
標記-清除
複製算法:
將內存劃分爲大小相等的兩塊,每次只使用其中一塊,當這一塊內存用完了就將還存活的對象複製到另外一塊上面,而後再把使用過的內存空間進行一次清理。
將內存分爲一塊較大的 Eden 空間和兩塊較小的 Survior 空間,每次使用 Eden 空間和其中一塊 Survivor。在回收時,將 Eden 和 Survivor 中還存活着的對象一次性複製到另外一塊 Survivor 空間上,最後清理 Eden 和 使用過的那一塊 Survivor。HotSpot 虛擬機的 Eden 和 Survivor 的大小比例默認爲 8:1,保證了內存的利用率達到 90 %。若是每次回收有多於 10% 的對象存活,那麼一塊 Survivor 空間就不夠用了,此時須要依賴於老年代進行分配擔保,也就是借用老年代的空間。
複製
標記—整理算法:
標記—整理算法和標記—清除算法同樣,可是標記—整理算法不是把存活對象複製到另外一塊內存,而是把存活對象往內存的一端移動,而後直接回收邊界之外的內存,所以其不會產生內存碎片。標記—整理算法提升了內存的利用率,而且它適合在收集對象存活時間較長的老年代。
標記—整理
分代收集算法:
分代回收算法其實是把複製算法和標記整理法的結合,並非真正一個新的算法,通常分爲:老年代和新生代,老年代就是不多垃圾須要進行回收的,新生代就是有不少的內存空間須要回收,因此不一樣代就採用不一樣的回收算法,以此來達到高效的回收算法。
新生代:因爲新生代產生不少臨時對象,大量對象須要進行回收,因此採用複製算法是最高效的。
老年代:回收的對象不多,都是通過幾回標記後都不是可回收的狀態轉移到老年代的,因此僅有少許對象須要回收,故採用標記清除或者標記整理算法。
HotSpot虛擬機中,對象在內存中的佈局分爲三塊區域:對象頭、實例數據和對齊填充。
對象頭包括兩部分:Mark Word 和 類型指針。
Mark Word:Mark Word用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,佔用內存大小與虛擬機位長一致。
類型指針:類型指針指向對象的類元數據,虛擬機經過這個指針肯定該對象是哪一個類的實例。
類加載的過程主要分爲三個部分:
加載:指的是把class字節碼文件從各個來源經過類加載器裝載入內存中。
連接
初始化:對類變量初始化,是執行類構造器的過程。
連接又能夠細分爲
驗證:爲了保證加載進來的字節流符合虛擬機規範,不會形成安全錯誤。
準備:爲類變量(注意,不是實例變量)分配內存,而且賦予初值。
解析:將常量池內的符號引用替換爲直接引用的過程。
/** * 快速排序 * * @param array * @param _left * @param _right */ private static void quickSort(int[] array, int _left, int _right) { int left = _left;// int right = _right; int pivot;//基準線 if (left < right) { pivot = array[left]; while (left != right) { //從右往左找到比基準線小的數 while (left < right && pivot <= array[right]) { right--; } //將右邊比基準線小的數換到左邊 array[left] = array[right]; //從左往右找到比基準線大的數 while (left < right && pivot >= array[left]) { left++; } //將左邊比基準線大的數換到右邊 array[right] = array[left]; } //此時left和right指向同一位置 array[left] = pivot; quickSort(array, _left, left - 1); quickSort(array, left + 1, _right); } }
public class HeapSort {
/**
* 構建大頂堆
/
public static void adjustHeap(int[] a, int i, int len) {
int temp, j;
temp = a[i];
for (j = 2 i; j < len; j *= 2) {// 沿關鍵字較大的孩子結點向下篩選
if (j < len && a[j] < a[j + 1])
++j; // j爲關鍵字中較大記錄的下標
if (temp >= a[j])
break;
a[i] = a[j];
i = j;
}
a[i] = temp;
}
public static void heapSort(int[] a) { int i; for (i = a.length / 2 - 1; i >= 0; i--) {// 構建一個大頂堆 adjustHeap(a, i, a.length - 1); } for (i = a.length - 1; i >= 0; i--) {// 將堆頂記錄和當前未經排序子序列的最後一個記錄交換 int temp = a[0]; a[0] = a[i]; a[i] = temp; adjustHeap(a, 0, i - 1);// 將a中前i-1個記錄從新調整爲大頂堆 } }
}
遞歸
public int TreeDepth(TreeNode root) { if (root == null) { return 0; } return Math.max(TreeDepth(root.left) + 1, TreeDepth(root.right) + 1); }
非遞歸,層次遍歷
public int TreeDepth_2(TreeNode root) { if (root == null) { return 0; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); int start = 0; int end = 1; int depth = 0; while (!queue.isEmpty()) { TreeNode temp = queue.poll(); start++; if (temp.left != null) { queue.offer(temp.left); } if (temp.right != null) { queue.offer(temp.right); } if (start == end) { start = 0; end = queue.size(); depth++; } } return depth; }
思路:
訪問根節點,並將根節點入隊。
當隊列不空的時候,重複如下操做。
一、彈出一個元素。做爲當前的根節點。
二、若是根節點有左孩子,訪問左孩子,並將左孩子入隊。
三、若是根節點有右孩子,訪問右孩子,並將右孩子入隊。
public void levelOrder(TreeNode root) {
//使用隊列,先進先出
Queue
queue.add(root);
while (!queue.isEmpty()) {
TreeNode temp = queue.poll();
System.out.print(temp.val + " ");
if (temp.left != null) {
queue.offer(temp.left);
}
if (temp.right != null) {
queue.offer(temp.right);
}
}
}
從下往上遍歷,若是子樹是平衡二叉樹,則返回子樹高度,不然返回-1
public boolean IsBalanced_Solution(TreeNode root) { return MaxDepth(root) != -1; } public int MaxDepth(TreeNode root) { if (root == null) { return 0; } int leftHeight = MaxDepth(root.left); if (leftHeight == -1) { return -1; } int rightHeight = MaxDepth(root.right); if (rightHeight == -1) { return -1; } return Math.abs(leftHeight - rightHeight) > 1 ? -1 : 1 + Math.max(leftHeight, rightHeight); }
將當前節點和下一節點保存起來,而後將當前節點反轉。
public ListNode ReverseList(ListNode head) { //head爲當前節點,若是當前節點爲空的話,那就什麼也不作,直接返回null ListNode pre = null;//pre爲當前節點的前一節點 ListNode next = null;//next爲當前節點的下一節點 //須要pre和next的目的是讓當前節點從pre.head.next1.next2變成pre<-head next1.next2 //即pre讓節點能夠反轉所指方向,但反轉以後若是不用next節點保存next1節點的話,此單鏈表就此斷開了 //因此須要用到pre和next兩個節點 //1.2.3.4.5 //1<-2<-3 4.5 //作循環,若是當前節點不爲空的話,始終執行此循環,此循環的目的就是讓當前節點從指向next到指向pre while (head != null) { //先用next保存head的下一個節點的信息,保證單鏈表不會由於失去head節點的原next節點而就此斷裂 next = head.next; //保存完next,就可讓head從指向next變成指向pre了 head.next = pre; //head指向pre後,就繼續依次反轉下一個節點 //讓pre,head,next依次向後移動一個節點,繼續下一次的指針反轉 pre = head; head = next; } //若是head爲null的時候,pre就爲最後一個節點了,可是鏈表已經反轉完畢,pre就是反轉後鏈表的第一個節點 //直接輸出pre就是咱們想要獲得的反轉後的鏈表 return pre; }
利用遞歸走到鏈表的末端,而後再更新每個節點的next值 ,實現鏈表的反轉。
public ListNode ReverseList(ListNode head) { //若是鏈表爲空或者鏈表中只有一個元素 if (head == null || head.next == null) return head; //先遞歸找到到鏈表的末端結點,從後依次反轉整個鏈表 ListNode reverseHead = ReverseList(head.next); //再將當前節點設置爲後面節點的後續節點 head.next.next = head; head.next = null; return reverseHead; }
用LinkedHashMap記錄字符出現的次數
public Character firstNotRepeating(String str){ if(str == null) return null; char[] strChar = str.toCharArray(); LinkedHashMap<Character,Integer> hash = new LinkedHashMap<Character,Integer>(); for(char item:strChar){ if(hash.containsKey(item)) hash.put(item, hash.get(item)+1); else hash.put(item, 1); } for(char key:hash.keySet()) { if(hash.get(key)== 1) return key; } return null; }
利用HashSet的元素不能重複,若是有重複的元素,則刪除重複元素,若是沒有則添加,最後剩下的就是隻出現一次的元素
public void FindNumsAppearOnce(int[] array, int num[]) { HashSet<Integer> set = new HashSet<>(); for (int i = 0; i < array.length; i++) { if (!set.add(array[i])) { set.remove(array[i]); } } Iterator<Integer> iterator = set.iterator(); num[0] = iterator.next(); }
用HashMap<K,V>保存數組的值,key爲數組值,value爲布爾型表示是否有重複
public void FindNumsAppearOnce_2(int[] array, int num[]) { HashMap<Integer, Boolean> map = new HashMap<>(); for (int i = 0; i < array.length; i++) { if (!map.containsKey(array[i])) { map.put(array[i], true); } else { map.put(array[i], false); } } for (int i = 0; i < array.length; i++) { if (map.get(array[i])) { num[0] = array[i]; } } }
利用HashSet的元素不能重複,若是有重複的元素,則刪除重複元素,若是沒有則添加,最後剩下的就是隻出現一次的元素
public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { HashSet<Integer> set = new HashSet<>(); for (int i = 0; i < array.length; i++) { if (!set.add(array[i])) { set.remove(array[i]); } } Iterator<Integer> iterator = set.iterator(); num1[0] = iterator.next(); num2[0] = iterator.next(); }
用HashMap<K,V>保存數組的值,key爲數組值,value爲布爾型表示是否有重複
public void FindNumsAppearOnce_2(int[] array, int num1[], int num2[]) { HashMap<Integer, Boolean> map = new HashMap<>(); for (int i = 0; i < array.length; i++) { if (!map.containsKey(array[i])) { map.put(array[i], true); } else { map.put(array[i], false); } } int index = 0;//區分是第幾個不重複的值 for (int i = 0; i < array.length; i++) { if (map.get(array[i])) { index++; if (index == 1) { num1[0] = array[i]; } else { num2[0] = array[i]; } } } }
位運算 異或,兩個不相等的元素在位級表示上一定會有一位存在不一樣。
public void FindNumsAppearOnce_3(int[] array, int num1[], int num2[]) { int diff = 0; for (int num : array) diff ^= num; // 獲得最右一位 diff &= -diff; for (int num : array) { if ((num & diff) == 0) num1[0] ^= num; else num2[0] ^= num; } }
進程:進程是操做系統資源分配的基本單位。每一個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1–n個線程。
線程:線程是CPU獨立調度的基本單位。同一類線程共享代碼和數據空間,每一個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。
線程和進程的生命週期:新建、就緒、運行、阻塞、死亡
不一樣進程打開同一個文件,文件描述符可能相同可能不一樣。
消息傳遞
管道
消息隊列
套接字
共享內存
image
OSI七層網絡模型 對應網絡協議
應用層 HTTP、TFTP、FTP、NFS、WAIS、SMTP
表示層 Telnet、Rlogin、SNMP、Gopher
會話層 SMTP、DNS
傳輸層 TCP、UDP
網絡層 IP、ICMP、ARP、RARP、AKP、UUCP
數據鏈路層 FDDI、Ethernet、Arpanet、PDN、SLIP、PPP
物理層 IEEE 802.1A、IEEE 802.2到IEEE 802.11
HTTP/0.9只支持客戶端發送Get請求,且不支持請求頭。HTTP具備典型的無狀態性。
HTTP/1.0在HTTP/0.9的基礎上支持客戶端發送POST、HEAD。HTTP 1.0須要使用keep-alive參數來告知服務器端要創建一個長鏈接,但默認是短鏈接。
HTTP(Hypertext Transfer Protocol)超文本傳輸協議是用來在Internet上傳送超文本的傳送協議,它可使瀏覽器更加高效,使網絡傳輸減小。但HTTP協議採用明文傳輸信息,存在信息竊聽、信息篡改和信息劫持的風險。
HTTPS(Secure Hypertext Transfer Protocol) 安全超文本傳輸協議是一個安全的通訊通道,它基於HTTP開發,用於在客戶計算機和服務器之間交換信息。HTTPS使用安全套接字層(SSL)進行信息交換,簡單來講HTTPS是HTTP的安全版,是使用TLS/SSL加密的HTTP協議。
HTTPS和HTTP的區別主要以下:
https協議須要到ca申請證書,通常免費證書較少,於是須要必定費用。
http是超文本傳輸協議,信息是明文傳輸,https則是具備安全性的ssl加密傳輸協議。
http和https使用的是徹底不一樣的鏈接方式,用的端口也不同,前者是80,後者是443。
http的鏈接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全.
客戶端發送請求到服務器端
服務器端返回證書和公開密鑰,公開密鑰做爲證書的一部分而存在
客戶端驗證證書和公開密鑰的有效性,若是有效,則生成共享密鑰並使用公開密鑰加密發送到服務器端
服務器端使用私有密鑰解密數據,並使用收到的共享密鑰加密數據,發送到客戶端
客戶端使用共享密鑰解密數據
SSL加密創建
所謂三次握手(Three-Way Handshake)即創建TCP鏈接,就是指創建一個TCP鏈接時,須要客戶端和服務端總共發送3個包以確認鏈接的創建。整個流程以下圖所示:
TCP三次握手
第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認鏈接請求,Server進入SYN_RCVD狀態。
第三次握手:Client收到確認後,檢查ack是否爲J+1,ACK是否爲1,若是正確則將標誌位ACK置爲1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否爲K+1,ACK是否爲1,若是正確則鏈接創建成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間能夠開始傳輸數據了。
Server在LISTEN狀態下,收到創建鏈接請求的SYN報文後,能夠直接把ACK和SYN放在一個報文裏發送給Client。而關閉鏈接時,當收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,己方也未必所有數據都發送給對方了,因此己方能夠當即close,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACK和FIN通常都會分開發送。
http是要基於TCP鏈接基礎上的,簡單的說,TCP就是單純創建鏈接,不涉及任何咱們須要請求的實際數據,簡單的傳輸。http是用來收發數據,即實際應用上的。
Server在LISTEN狀態下,收到創建鏈接請求的SYN報文後,能夠直接把ACK和SYN放在一個報文裏發送給Client。而關閉鏈接時,當收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,己方也未必所有數據都發送給對方了,因此己方能夠當即close,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACK和FIN通常都會分開發送。
客戶端發送隨機數1,支持的加密方法(如RSA公鑰加密)
服務端發送隨機數2,和服務器公鑰,並確認加密方法
客戶端發送用服務器公鑰加密的隨機數3
服務器用私鑰解密這個隨機數3,用加密方法計算生成對稱加密的密鑰給客戶端,
接下來的報文都用雙方協定好的加密方法和密鑰,進行加密
經常使用的會話跟蹤技術是Cookie與Session。Cookie經過在客戶端記錄信息肯定用戶身份,Session經過在服務器端記錄信息肯定用戶身份。
聯繫:
Cookie與Session都是用來跟蹤瀏覽器用戶身份的會話方式。
區別:
Cookie數據存放在客戶的瀏覽器上,Session數據放在服務器上。
Cookie不是很安全,別人能夠分析存放在本地的Cookie並進行Cookie欺騙,若是主要考慮到安全應當使用加密的Cookie或者Session。
Session會在必定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能,若是主要考慮到減輕服務器性能方面,應當使用Cookie。
單個Cookie在客戶端的限制是4K,不少瀏覽器都限制一個站點最多保存20個Cookie。
域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶
瀏覽器緩存 --> 系統緩存 --> 路由器緩存 --> ISP(互聯網服務提供商)DNS緩存 --> 根域名服務器 --> 頂級域名服務器 --> 主域名服務器 --> 保存結果至緩存
Ping程序的實質是利用了ICMP請求回顯和回顯應答報文,但ARP請求和應答報文也在其中起了很是重要的做用。
GET 請求:
GET 請求可被緩存
GET 請求保留在瀏覽器歷史記錄中
GET 請求可被收藏爲書籤
GET 請求不該在處理敏感數據時使用
GET 請求有長度限制
GET 請求只應當用於取回數據
POST 請求 :
POST 請求不會被緩存
POST 請求不會保留在瀏覽器歷史記錄中
POST 不能被收藏爲書籤
POST 請求對數據長度沒有要求
1XX 信息,服務器收到請求,須要請求者繼續執行操做
2XX 成功,操做被成功接收並處理
3XX 重定向,須要進一步的操做以完成請求
4XX 客戶端錯誤,請求包含語法錯誤或沒法完成請求
5XX 服務器錯誤,服務器在處理請求的過程當中發生了錯誤
未提交讀(READ UNCOMMITTED):事務中的修改,即便沒有提交,對其它事務也是可見的。最低級別,任何狀況都沒法保證。
提交讀(READ COMMITTED):一個事務只能讀取已經提交的事務所作的修改。換句話說,一個事務所作的修改在提交以前對其它事務是不可見的。可避免髒讀的發生。
可重複讀(REPEATABLE READ):保證在同一個事務中屢次讀取一樣數據的結果是同樣的。可避免髒讀、不可重複讀的發生。
可串行化(SERIALIXABLE):強制事務串行執行。可避免髒讀、不可重複讀、幻讀的發生。
在MySQL數據庫中,支持上面四種隔離級別,默認的爲REPEATABLE READ(可重複讀)。
left join / inner join / right join
惰性刪除+按期刪除
惰性刪除
在進行get或set等操做時,先檢查key是否過時,
若過時,刪除key,而後執行相應操做;
若沒過時,直接執行相應操做
按期刪除
遍歷每一個數據庫(就是redis.conf中配置的"database"數量,默認爲16)
檢查當前庫中的指定個數個key(默認是每一個庫檢查20個key,注意至關於該循環執行20次,循環體時下邊的描述)
若是當前庫中沒有一個key設置了過時時間,直接執行下一個庫的遍歷
隨機獲取一個設置了過時時間的key,檢查該key是否過時,若是過時,刪除key
判判定期刪除操做是否已經達到指定時長,若已經達到,直接退出按期刪除。
回答存儲機制以及持久化
確保一個類最多隻有一個實例,並提供一個全局訪問點。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() { } /** * 當第一次調用getInstance()方法時,instance爲空,同步操做,保證多線程實例惟一 * 當第一次後調用getInstance()方法時,instance不爲空,不進入同步代碼塊,減小了沒必要要的同步 */ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }
}
單例模式
SpringBoot就是對各類框架的整合,讓框架集成在一塊兒更加簡單,簡化了開發過程、配置過程、部署過程、監控過程。
IOC:控制反轉也叫依賴注入,IOC利用java反射機制。所謂控制反轉是指,原本被調用者的實例是有調用者來建立的,這樣的缺點是耦合性太強,IOC則是統一交給spring來管理建立,將對象交給容器管理,你只須要在spring配置文件總配置相應的bean,以及設置相關的屬性,讓spring容器來生成類的實例對象以及管理對象。在spring容器啓動的時候,spring會把你在配置文件中配置的bean都初始化好,而後在你須要調用的時候,就把它已經初始化好的那些bean分配給你須要調用這些bean的類。
AOP是對OOP的補充和完善。AOP利用的是代理,分爲CGLIB動態代理和JDK動態代理。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構。OOP編程中,會有大量的重複代碼。而AOP則是將這些與業務無關的重複代碼抽取出來,而後再嵌入到業務代碼當中。實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼,屬於靜態代理。
下降了組件之間的耦合性 ,實現了軟件各層之間的解耦
工廠模式
權限管理、日誌、事務管理等。
切面經過帶有@Aspect註解的類實現。
Spring中定義了四個advice:BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。
Before Advice:在方法執行前執行。
AfterAdvice:在方法執行以後調用的通知,不管方法執行是否成功。
After ReturningAdvice:在方法執行後返回一個結果後執行。
After ThrowingAdvice:在方法執行過程當中拋出異常的時候執行。
代理分爲靜態代理和動態代理,靜態代理是在編譯時就將接口、實現類、代理類所有手動完成,但若是咱們須要不少的代理,每個都這麼手動的去建立實屬浪費時間,並且會有大量的重複代碼。動態代理能夠在程序運行期間根據須要動態的建立代理類及其實例,來完成具體的功能。
Spring事務管理高層抽象主要包括3個接口:
PlatformTransactionManager(事務管理器)
TransactionDefinition(事務定義信息,包含隔離級別、事務傳播行爲、超時、只讀)
TransactionStatus(事務具體運行狀態)
Spring事務的本質其實就是數據庫對事務的支持
獲取鏈接->開啓事務 -> 執行CRUD -> 提交事務/回滾事務 -> 關閉鏈接
map
@Param註解
JavaBean
客戶端發送HTTP請求到服務器
SpringMVC的核心DispatcherServlet將請求交給HandlerMapping處理
HandlerMapping經過查詢機制找處處理當前請求的Handler
DispatcherServlet將請求交給這個Handler處理
Handler處理完成後返回一個ModleAndView對象,這個對象包含視圖邏輯名和數據對象
返回的視圖邏輯名會經過視圖解析器解析成真正的視圖,並交給DispatcherServlet處理
DispatcherServlet將請求分派給真正的視圖對象,並反映到客戶端
@Controller:用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。
@RequestMapping:是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。
@Resource和@Autowired:@Resource和@Autowired都是作bean的注入時使用,其實@Resource並非Spring的註解,它的包是javax.annotation.Resource,須要導入,可是Spring支持該註解的注入。
@ResponseBody:返回的數據不是html標籤的頁面,而是其餘某種格式的數據時(如json、xml等)使用。
@Repository:DAO層
@Service:服務層
@Autowired註解是按類型裝配依賴對象,默認狀況下它要求依賴對象必須存在,若是容許null值,能夠設置它required屬性爲false。
@Resource註解和@Autowired同樣,也能夠標註在字段或屬性的setter方法上,但它默認按名稱裝配。名稱能夠經過@Resource的name屬性指定,若是沒有指定name屬性,當註解標註在字段上,即默認取字段的名稱做爲bean名稱尋找依賴對象,當註解標註在屬性的setter方法上,即默認取屬性名做爲bean名稱尋找依賴對象。
@Resources按名稱,是JDK的,@Autowired按類型,是Spring的。
@PathVariable是用來對指定請求的URL路徑裏面的變量。
Listener我是這樣理解他的,他是一種觀察者模式的實現。
Filter的使用戶能夠改變一 個request或修改一個response。 Filter 不是一個servlet,它不能產生一個response,可是他可以在一個request到達servlet以前預先處理request,也能夠在一個響應離開 servlet時處理response。
通俗的說,就是一個容器,把消息丟進去,不須要當即處理。而後有個程序去從容器裏面把消息一條條讀出來處理。
dubbo
ps -ef | grep java
top命令提供了實時的運行中的程序的資源使用統計。你能夠根據內存的使用和大小來進行排序。
vmstat命令顯示實時的和平均的統計,覆蓋CPU、內存、I/O等內容。例如內存狀況,不只顯示物理內存,也統計虛擬內存。
多線程,JVM
書籍+博客+視頻
舉出三個以上的國內開源框架,越多越好,dubbo、fastjson、sharding-jdbc、Elastic-job...
電商,突出質量
淘寶是C2C,京東和天貓是B2C,淘寶門檻低,種類,國際市場佈局
深刻理解Java虛擬機&HEAD FIRST設計模式&高性能MYSQL&Java併發編程實戰,看博客比較多,感受博客更有針對性