面經彙總

轉載連接

1、Java

Java的優點

平臺無關性、垃圾回收javascript

Java有哪些特性,舉個多態的例子。

封裝、繼承、多態css

abstract interface區別

含有abstract修飾符的class即爲抽象類,abstract類不能建立的實例對象。含有abstract方法的類必須定義爲abstract class,abstract class類中的方法沒必要是抽象的。abstract class類中定義抽象方法必須在具體(Concrete)子類中實現,因此,不能有抽象構造方法或抽象靜態方法。若是的子類沒有實現抽象父類中的全部抽象方法,那麼子類也必須定義爲abstract類型。html

接口(interface)能夠說成是抽象類的一種特例,接口中的全部方法都必須是抽象的。接口中的方法定義默認爲public abstract類型,接口中的成員變量類型默認爲public static final。java

下面比較一下二者的語法區別:mysql

  1. 抽象類能夠有構造方法,接口中不能有構造方法。
  2. 抽象類中能夠有普通成員變量,接口中沒有普通成員變量
  3. 抽象類中能夠包含非抽象的普通方法,接口中的能夠有非抽象方法,好比deaflut方法
  4. 抽象類中的抽象方法的訪問類型能夠是public,protected和(默認類型,雖然
    eclipse下不報錯,但應該也不行),但接口中的抽象方法只能是public類型的,而且默認即爲public abstract類型。
  5. 抽象類中能夠包含靜態方法,接口中不能包含靜態方法
  6. 抽象類和接口中均可以包含靜態成員變量,抽象類中的靜態成員變量的訪問類型能夠任意,但接口中定義的變量只能是public static final類型,而且默認即爲public static final類型。
  7. 一個類能夠實現多個接口,但只能繼承一個抽象類。

有抽象方法必定是抽象類嗎?抽象類必定有抽象方法嗎?

有抽象方法不必定是抽象類,也多是接口。抽象類不必定有抽象方法,能夠有非抽象的普通方法。linux

Java的反射機制

在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲Java語言的反射機制。web

反射的核心是JVM在運行時才動態加載類或調用方法/訪問屬性,它不須要事先知道運行對象是誰。redis

super()和this()能不能同時使用

不能同時使用,this和super不能同時出如今一個構造函數裏面,由於this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,因此在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會經過。算法

hashcode,equals,Object的這兩個方法默認返回什麼?描述了一下爲何重寫equals方法必須重寫hashcode方法

默認的hashCode方法會利用對象的地址來計算hashcode值,不一樣對象的hashcode值是不同的。spring

public boolean equals(Object obj) { return (this == obj); }

能夠看出Object類中的equals方法與「==」是等價的,也就是說判斷對象的地址是否相等。Object類中的equals方法進行的是基於內存地址的比較。

通常對於存放到Set集合或者Map中鍵值對的元素,須要按須要重寫hashCode與equals方法,以保證惟一性。

final

  1. final關鍵字能夠用於成員變量、本地變量、方法以及類。
  2. final成員變量必須在聲明的時候初始化或者在構造器中初始化,不然就會報編譯錯誤。
  3. 你不可以對final變量再次賦值。
  4. 本地變量必須在聲明時賦值。
  5. 在匿名類中全部變量都必須是final變量。
  6. final方法不能被重寫。
  7. final類不能被繼承。
  8. 接口中聲明的全部變量自己是final的。
  9. final和abstract這兩個關鍵字是反相關的,final類就不多是abstract的。
  10. final方法在編譯階段綁定,稱爲靜態綁定(static binding)。
  11. 沒有在聲明時初始化final變量的稱爲空白final變量(blank final variable),它們必須在構造器中初始化,或者調用this()初始化。不這麼作的話,編譯器會報錯「final變量(變量名)須要進行初始化」。
  12. 將類、方法、變量聲明爲final可以提升性能,這樣JVM就有機會進行估計,而後優化。
  13. 按照Java代碼慣例,final變量就是常量,並且一般常量名要大寫。

String,StringBuffer,StringBuilder區別

  • String內容不可變,StringBuffer和StringBuilder內容可變;
  • StringBuilder非線程安全(單線程使用),String與StringBuffer線程安全(多線程使用);
  • 若是程序不是多線程的,那麼使用StringBuilder效率高於StringBuffer。

String爲何不可變

public final class String implements java.io.Serializable, Comparable<string>, CharSequence { /** 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不能夠繼承,由於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();

字符串常量池

map、list、set的區別

List:

  1. 能夠容許重複的對象。
  2. 能夠插入多個null元素。
  3. 是一個有序容器,保持了每一個元素的插入順序,輸出的順序就是插入的順序。
  4. 經常使用的實現類有 ArrayList、LinkedList 和 Vector。ArrayList 最爲流行,它提供了使用索引的隨意訪問,而 LinkedList 則對於常常須要從 List中添加或刪除元素的場合更爲合適。

Set:

  1. 不容許重複對象
  2. 無序容器,你沒法保證每一個元素的存儲順序,TreeSet經過 Comparator 或者 Comparable 維護了一個排序順序。
  3. 只容許一個 null 元素
  4. Set 接口最流行的幾個實現類是 HashSet、LinkedHashSet 以及 TreeSet。最流行的是基於 HashMap 實現的 HashSet;TreeSet 還實現了 SortedSet 接口,所以 TreeSet 是一個根據其 compare() 和 compareTo() 的定義進行排序的有序容器。

Map:

  1. Map不是collection的子接口或者實現類。Map是一個接口。
  2. Map 的 每一個 Entry 都持有兩個對象,也就是一個鍵一個值,Map 可能會持有相同的值對象但鍵對象必須是惟一的。
  3. TreeMap 也經過 Comparator 或者 Comparable 維護了一個排序順序。
  4. Map 裏你能夠擁有隨意個 null 值但最多隻能有一個 null 鍵。
  5. Map 接口最流行的幾個實現類是 HashMap、LinkedHashMap、Hashtable 和 TreeMap。(HashMap、TreeMap最經常使用)

有沒有有序的set?

有,LinkedHashSet和TreeSet

Set如何保證不重複?

HashSet中add()中調用了HashMap的put(),將一個key-value對放入HashMap中時,首先根據key的hashCode()返回值決定該Entry的存儲位置,若是兩個key的hash值相同,那麼它們的存儲位置相同。若是這個兩個key的equals比較返回true。那麼新添加的Entry的value會覆蓋原來的Entry的value,key不會覆蓋。所以,若是向HashSet中添加一個已經存在的元素,新添加的集合元素不會覆蓋原來已有的集合元素。

說一說對Java io的理解

IO,其實意味着:數據不停地搬入搬出緩衝區而已(使用了緩衝區)。

nio與bio的瞭解以及說一下區別

BIO:同步阻塞式IO,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。

NIO:同步非阻塞式IO,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。

Java併發的理解

Java是一種多線程編程語言,咱們可使用Java來開發多線程程序。 多線程程序包含兩個或多個可同時運行的部分,每一個部分能夠同時處理不一樣的任務,從而能更好地利用可用資源,特別是當您的計算機有多個CPU時。多線程使您可以寫入多個活動,能夠在同一程序中同時進行操做處理。

死鎖,死鎖緣由

兩個或者多個線程之間相互等待,致使線程都沒法執行,叫作線程死鎖。

  1. 互斥條件:使用的資源是不能共享的。
  2. 不可搶佔條件:線程持有一個資源並等待獲取一個被其餘線程持有的資源。
  3. 請求與保持條件:線程持有一個資源並等待獲取一個被其餘線程持有的資源。
  4. 循環等待條件:線程之間造成一種首尾相連的等待資源的關係。

wait和sleep的區別

  1. wait和notify方法定義在Object類中,所以會被全部的類所繼承。 這些方法都是final的,即它們都是不能被重寫的,不能經過子類覆寫去改變它們的行爲。 而sleep方法是在Thread類中是由native修飾的,本地方法。

  2. 當線程調用了wait()方法時,它會釋放掉對象的鎖。
    另外一個會致使線程暫停的方法:Thread.sleep(),它會致使線程睡眠指定的毫秒數,但線程在睡眠的過程當中是不會釋放掉對象的鎖的。

  3. 由於wait方法會釋放鎖,因此調用該方法時,當前的線程必須擁有當前對象的monitor,也即lock,就是鎖。要確保調用wait()方法的時候擁有鎖,即wait()方法的調用必須放在synchronized方法或synchronized塊中。

ArrayList和LinkedList有什麼區別?

  1. ArrayList是實現了基於動態數組的數據結構,LinkedList基於雙向鏈表的數據結構。
  2. 對於隨機訪問get和set,ArrayList優於LinkedList,由於LinkedList要移動指針。
  3. 對於新增和刪除操做add和remove,LinedList比較佔優點,由於ArrayList要移動數據。

HashMap 的原理,hashmap的擴容問題,爲何HashMap的初始容量會是16,爲何是2倍擴容,實現簡單的 get/put操做;處理哈希衝突用的哪一種方法(拉鍊),還知道什麼處理哈希衝突的方法(開放地址檢測),開放地址檢測怎麼實現的

從哈希表中刪除一個元素,再加入元素時剛好與原來那個哈希衝突,這個元素會放在哪

HashMap、Hashtable、concurrenthashmap

HashTable爲何是線程安全的?

synchronized鎖住了

HashMap,ConcurrentHashMap以及在什麼狀況下性能會很差

Thread狀態有哪些

新建、就緒、運行、阻塞、死亡

多線程實現方法

  • 繼承Thread類建立線程類,重寫run方法,run方法就是表明線程須要完成的任務,調用線程對象的start()來啓動該線程,線程類已經繼承了Thread類,因此不能再繼承其餘父類。
  • 實現Runnable接口建立線程類,定義Runnable實現類,重寫run方法
  • 實現Callable接口,重寫call()方法,call()做爲線程的執行體,具備返回值
  • 線程池,使用線程池產生線程對象java.util.concurrent.ExecutorService、java.util.concurrent.Executors;

Java如何實現線程安全

互斥同步:推薦使用 synchronized 關鍵字進行同步, 在 concurrent包中有ReentrantLock類, 實現效果差很少. 仍是推薦原生態的synchronized.

非阻塞同步:須要硬件指令完成.經常使用的指令有:

Test-and-Set

Fetch-and-Increment

Swap

Compare-and-Swap (CAS)

Load-Linked/Store-Conditional (LL/SC)

典型的應用在 AtomicInteger 中

無同步方案:將變量保存在本地線程中,就不會出現多個線程併發的錯誤了。

java中主要使用的就是ThreadLocal這個類。

Synchronized和lock區別

  • Lock提供了synchronized關鍵字所不具有的主要特性有:
    • 嘗試非阻塞地獲取鎖boolean tryLock():當前線程嘗試獲取鎖,若是這一時刻沒有被其餘線程獲取到,則成功獲取並持有鎖
    • 能被中斷地獲取鎖void lockInterruptibly():當獲取到鎖的線程被中斷時,中斷異常拋出同時會釋放鎖
    • 超時獲取鎖boolean trylock(long time, TimeUnit unit):在指定截止時間以前獲取鎖,若是在截止時間仍舊沒法獲取鎖,則返回
  • synchronized是JVM提供的加鎖,悲觀鎖;lock是Java語言實現的,並且是樂觀鎖。
  • ReentrantLock是基於AQS實現的,因爲AQS是基於FIFO隊列的實現

Java中都有什麼鎖

重量級鎖、顯式鎖、併發容器、併發同步器、CAS、volatile、AQS等

可重入鎖的設計思路是什麼

可重入公平鎖獲取流程

在獲取鎖的時候,若是當前線程以前已經獲取到了鎖,就會把state加1,在釋放鎖的時候會先減1,這樣就保證了同一個鎖能夠被同一個線程獲取屢次,而不會出現死鎖的狀況。這就是ReentrantLock的可重入性。

對於非公平鎖而言,調用lock方法後,會先嚐試搶佔鎖,在各類判斷的時候會先忽略等待隊列,若是鎖可用,就會直接搶佔使用。

樂觀鎖和悲觀鎖

悲觀鎖:假定會發生併發衝突,則屏蔽一切可能違反數據完整性的操做

樂觀鎖:假定不會發生併發衝突,只在數據提交時檢查是否違反了數據完整性(不能解決髒讀問題)

juc包內有哪些類

CountDownLatch 同步計數器,主要用於線程間的控制,但計數沒法被重置,若是須要重置計數,請考慮使用 CyclicBarrier 。

CAS如何實現

BlockQueue見過沒?

(線程池的排隊策略)

線程池原理

線程池的排隊策略和拒絕策略的試用條件和具體內容。

線程池的類型,詳細介紹cached和fixed

corePoolSize參數的意義

核心線程數

  • 核心線程會一直存活,即便沒有任務須要執行
  • 當線程數小於核心線程數時,即便有線程空閒,線程池也會優先建立新線程處理
  • 設置allowCoreThreadTimeout=true(默認false)時,核心線程會超時關閉

線程池新任務到達時會先使用空閒線程仍是加入阻塞隊列

Java併發包裏面的CountdownLatch怎麼使用

這個類是一個同步計數器,主要用於線程間的控制,當CountDownLatch的count計數>0時,await()會形成阻塞,直到count變爲0,await()結束阻塞,使用countDown()會讓count減1。CountDownLatch的構造函數能夠設置count值,當count=1時,它的做用相似於wait()和notify()的做用。若是我想讓其餘線程執行完指定程序,其餘全部程序都執行結束後我再執行,這時能夠用CountDownLatch,但計數沒法被重置,若是須要重置計數,請考慮使用 CyclicBarrier 。

volatile和synchronized區別

  • 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虛擬機會自動離開。

一致性Hash原理,實現負載均衡

異常

servlet流程

forward redirect 二次請求

序列化,以及json傳輸

tomcat均衡方式

netty

2、JVM

JVM內存劃分

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

垃圾回收器

Java對象頭

HotSpot虛擬機中,對象在內存中的佈局分爲三塊區域:對象頭、實例數據和對齊填充。

對象頭包括兩部分:Mark Word 和 類型指針。

  • Mark Word:Mark Word用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,佔用內存大小與虛擬機位長一致。

  • 類型指針:類型指針指向對象的類元數據,虛擬機經過這個指針肯定該對象是哪一個類的實例。

內存泄漏

類加載過程

雙親委派模型,爲何要使用雙親委派模型

Java虛擬機的一些參數配置

爲何jvm調優常常會將-Xms和-Xmx參數設置成同樣

3、數據結構與算法

常見的排序算法時間複雜度

快排算法 寫代碼

/** * 快速排序 * * @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); } }

堆排序怎麼實現

鏈表,數組的優缺點,應用場景,查找元素的複雜度

入棧出棧的時間複雜度,鏈表插入和刪除的時間複雜度

如何用LinkedList實現堆棧操做

Arraylist如何實現排序

利用數組,實現一個循環隊列類

兩個有序數組,有相同的元素,找出來

二叉樹怎麼實現的

二叉樹前中後序遍歷 深度 廣度

二叉樹深度

遞歸

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<TreeNode> queue = new LinkedList<>(); 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); }

給定一個2叉樹,打印每一層最右邊的結點

一棵普通樹(非二叉搜索樹),找出一條路徑和最大

一棵樹,求全部路徑之和

最長公共子序列

反轉鏈表

將當前節點和下一節點保存起來,而後將當前節點反轉。

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; }

判斷一個數是否是醜數

找出一個字符串中字符連續相同的最長子串,如aabbccc,結果就是ccc

蓄水池抽樣算法

尋找一個字符串中第一個只出現一次的字符

用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; } }

海量數據topk問題

4、操做系統

進程和線程區別

進程:進程是操做系統資源分配的基本單位。每一個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1–n個線程。

線程:線程是CPU獨立調度的基本單位。同一類線程共享代碼和數據空間,每一個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。

線程和進程的生命週期:新建、就緒、運行、阻塞、死亡

不一樣進程打開了同一個文件,那麼這兩個進程獲得的文件描述符(fd)相同嗎?

不一樣進程打開同一個文件,文件描述符可能相同可能不一樣。

操做系統如何實現輸出

進程通訊

  • 消息傳遞
    • 管道
    • 消息隊列
    • 套接字
  • 共享內存

5、網絡

OSI七層網絡模型中,你對哪層最瞭解?瞭解哪些協議?作過web開發?

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/1.0/1.1/2

HTTP/0.9只支持客戶端發送Get請求,且不支持請求頭。HTTP具備典型的無狀態性。

HTTP/1.0在HTTP/0.9的基礎上支持客戶端發送POST、HEAD。HTTP 1.0須要使用keep-alive參數來告知服務器端要創建一個長鏈接,但默認是短鏈接。

HTTP 和 HTTPS 有什麼區別?

HTTP(Hypertext Transfer Protocol)超文本傳輸協議是用來在Internet上傳送超文本的傳送協議,它可使瀏覽器更加高效,使網絡傳輸減小。但HTTP協議採用明文傳輸信息,存在信息竊聽、信息篡改和信息劫持的風險。

HTTPS(Secure Hypertext Transfer Protocol) 安全超文本傳輸協議是一個安全的通訊通道,它基於HTTP開發,用於在客戶計算機和服務器之間交換信息。HTTPS使用安全套接字層(SSL)進行信息交換,簡單來講HTTPS是HTTP的安全版,是使用TLS/SSL加密的HTTP協議。

HTTPS和HTTP的區別主要以下:

  1. https協議須要到ca申請證書,通常免費證書較少,於是須要必定費用。
  2. http是超文本傳輸協議,信息是明文傳輸,https則是具備安全性的ssl加密傳輸協議。
  3. http和https使用的是徹底不一樣的鏈接方式,用的端口也不同,前者是80,後者是443。
  4. http的鏈接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全.

知道 HTTPS 通訊過程嗎?

  • 客戶端發送請求到服務器端
  • 服務器端返回證書和公開密鑰,公開密鑰做爲證書的一部分而存在
  • 客戶端驗證證書和公開密鑰的有效性,若是有效,則生成共享密鑰並使用公開密鑰加密發送到服務器端
  • 服務器端使用私有密鑰解密數據,並使用收到的共享密鑰加密數據,發送到客戶端
  • 客戶端使用共享密鑰解密數據
  • SSL加密創建

TCP三次握手

所謂三次握手(Three-Way Handshake)即創建TCP鏈接,就是指創建一個TCP鏈接時,須要客戶端和服務端總共發送3個包以確認鏈接的創建。整個流程以下圖所示:

TCP三次握手

  1. 第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。
  2. 第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求創建鏈接,Server將標誌位SYN和ACK都置爲1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認鏈接請求,Server進入SYN_RCVD狀態。
  3. 第三次握手: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通常都會分開發送。

TCP與HTTP有什麼關係

http是要基於TCP鏈接基礎上的,簡單的說,TCP就是單純創建鏈接,不涉及任何咱們須要請求的實際數據,簡單的傳輸。http是用來收發數據,即實際應用上的。

Tcp鏈接4次揮手的緣由。Time_wait等待超時了會怎樣?

Server在LISTEN狀態下,收到創建鏈接請求的SYN報文後,能夠直接把ACK和SYN放在一個報文裏發送給Client。而關閉鏈接時,當收到對方的FIN報文時,僅僅表示對方再也不發送數據了可是還能接收數據,己方也未必所有數據都發送給對方了,因此己方能夠當即close,也能夠發送一些數據給對方後,再發送FIN報文給對方來表示贊成如今關閉鏈接,所以,己方ACK和FIN通常都會分開發送。

SSL 握手

  • 客戶端發送隨機數1,支持的加密方法(如RSA公鑰加密)
  • 服務端發送隨機數2,和服務器公鑰,並確認加密方法
  • 客戶端發送用服務器公鑰加密的隨機數3
  • 服務器用私鑰解密這個隨機數3,用加密方法計算生成對稱加密的密鑰給客戶端,
  • 接下來的報文都用雙方協定好的加密方法和密鑰,進行加密

session/cookie

經常使用的會話跟蹤技術是Cookie與Session。Cookie經過在客戶端記錄信息肯定用戶身份,Session經過在服務器端記錄信息肯定用戶身份。

聯繫:

  • Cookie與Session都是用來跟蹤瀏覽器用戶身份的會話方式。

區別:

  • Cookie數據存放在客戶的瀏覽器上,Session數據放在服務器上。
  • Cookie不是很安全,別人能夠分析存放在本地的Cookie並進行Cookie欺騙,若是主要考慮到安全應當使用加密的Cookie或者Session。
  • Session會在必定時間內保存在服務器上。當訪問增多,會比較佔用你服務器的性能,若是主要考慮到減輕服務器性能方面,應當使用Cookie。
  • 單個Cookie在客戶端的限制是4K,不少瀏覽器都限制一個站點最多保存20個Cookie。

當你在瀏覽器地址欄輸入一個URL後回車,將會發生的事情?

域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶

DNS域名解析過程

瀏覽器緩存 --> 系統緩存 --> 路由器緩存 --> ISP(互聯網服務提供商)DNS緩存 --> 根域名服務器 --> 頂級域名服務器 --> 主域名服務器 --> 保存結果至緩存

ping工做原理

Ping程序的實質是利用了ICMP請求回顯和回顯應答報文,但ARP請求和應答報文也在其中起了很是重要的做用。

Get和Post請求

GET 請求:

  • GET 請求可被緩存
  • GET 請求保留在瀏覽器歷史記錄中
  • GET 請求可被收藏爲書籤
  • GET 請求不該在處理敏感數據時使用
  • GET 請求有長度限制
  • GET 請求只應當用於取回數據

POST 請求 :

  • POST 請求不會被緩存
  • POST 請求不會保留在瀏覽器歷史記錄中
  • POST 不能被收藏爲書籤
  • POST 請求對數據長度沒有要求

HTTP狀態碼

  • 1XX 信息,服務器收到請求,須要請求者繼續執行操做
  • 2XX 成功,操做被成功接收並處理
  • 3XX 重定向,須要進一步的操做以完成請求
  • 4XX 客戶端錯誤,請求包含語法錯誤或沒法完成請求
  • 5XX 服務器錯誤,服務器在處理請求的過程當中發生了錯誤

6、數據庫

數據庫事務的四個隔離級別,MySql在哪個級別

  • 未提交讀(READ UNCOMMITTED):事務中的修改,即便沒有提交,對其它事務也是可見的。最低級別,任何狀況都沒法保證。
  • 提交讀(READ COMMITTED):一個事務只能讀取已經提交的事務所作的修改。換句話說,一個事務所作的修改在提交以前對其它事務是不可見的。可避免髒讀的發生。
  • 可重複讀(REPEATABLE READ):保證在同一個事務中屢次讀取一樣數據的結果是同樣的。可避免髒讀、不可重複讀的發生。
  • 可串行化(SERIALIXABLE):強制事務串行執行。可避免髒讀、不可重複讀、幻讀的發生。

在MySQL數據庫中,支持上面四種隔離級別,默認的爲REPEATABLE READ(可重複讀)。

數據庫死鎖/如何防止

mysql索引,索引機制,彙集索引和非彙集索引,如何建立索引,實現原理,創建準則,優缺點,注意事項,

索引在什麼狀況下失效

說一下對B+樹的瞭解

innodb創建的索引,若是字段重複率很高索引,索引是什麼樣,查找效率如何

innodb在插入的時候,是否會給行上鎖

說一下innodb的默認隔離級別

數據庫設計(訂單、購物車和商品)

sql中join的幾種操做的區別

left join / inner join / right join

union和union all的區別,誰的效率更高

用distinct和用group by去重,誰的效率更高

sql中的優化,怎麼提升查詢效率

緩存的穿透和雪崩,解決辦法

redis的排序算法?

redis集羣

redis過時策略

Redis如何解決key衝突

redis數據類型+redis是單線程的麼,爲何呢

redis和memcache區別

redis與mysql的區別以及優缺點

回答存儲機制以及持久化

7、設計模式

單例模式裏面的雙重檢查鎖定的原理,以及爲何使用volatile

確保一個類最多隻有一個實例,並提供一個全局訪問點。

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; } }

生產者消費者

工廠,說下原理和應用

策略模式

適配器模式

裝飾模式

代理模式

線程池使用了什麼設計模式

單例模式

JDK中哪些體現了命令模式

8、框架

介紹下SpringBoot

SpringBoot就是對各類框架的整合,讓框架集成在一塊兒更加簡單,簡化了開發過程、配置過程、部署過程、監控過程。

Spring IOC AOP

IOC:控制反轉也叫依賴注入,IOC利用java反射機制。所謂控制反轉是指,原本被調用者的實例是有調用者來建立的,這樣的缺點是耦合性太強,IOC則是統一交給spring來管理建立,將對象交給容器管理,你只須要在spring配置文件總配置相應的bean,以及設置相關的屬性,讓spring容器來生成類的實例對象以及管理對象。在spring容器啓動的時候,spring會把你在配置文件中配置的bean都初始化好,而後在你須要調用的時候,就把它已經初始化好的那些bean分配給你須要調用這些bean的類。

AOP是對OOP的補充和完善。AOP利用的是代理,分爲CGLIB動態代理和JDK動態代理。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構。OOP編程中,會有大量的重複代碼。而AOP則是將這些與業務無關的重複代碼抽取出來,而後再嵌入到業務代碼當中。實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立「方面」,從而使得編譯器能夠在編譯期間織入有關「方面」的代碼,屬於靜態代理。

Spring IOC有哪些好處

下降了組件之間的耦合性 ,實現了軟件各層之間的解耦

IOC涉及到的設計模式

工廠模式

AOP的應用場景,具體介紹,配置文件中須要寫什麼?具體註解須要寫啥?

權限管理、日誌、事務管理等。

切面經過帶有@Aspect註解的類實現。

Spring中定義了四個advice:BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。

Before Advice:在方法執行前執行。

AfterAdvice:在方法執行以後調用的通知,不管方法執行是否成功。

After ReturningAdvice:在方法執行後返回一個結果後執行。

After ThrowingAdvice:在方法執行過程當中拋出異常的時候執行。

說說靜態代理和動態代理

代理分爲靜態代理和動態代理,靜態代理是在編譯時就將接口、實現類、代理類所有手動完成,但若是咱們須要不少的代理,每個都這麼手動的去建立實屬浪費時間,並且會有大量的重複代碼。動態代理能夠在程序運行期間根據須要動態的建立代理類及其實例,來完成具體的功能。

Spring事務傳播,隔離級別

  • Spring事務管理高層抽象主要包括3個接口:
    • PlatformTransactionManager(事務管理器)
    • TransactionDefinition(事務定義信息,包含隔離級別、事務傳播行爲、超時、只讀)
    • TransactionStatus(事務具體運行狀態)
  • Spring事務的本質其實就是數據庫對事務的支持
  • 獲取鏈接->開啓事務 -> 執行CRUD -> 提交事務/回滾事務 -> 關閉鏈接

Spring bean初始化過程

Spring如何生成一個Bean?配置文件寫完了以後又怎麼生成?

Mybatis 傳參

  • map
  • @Param註解
  • JavaBean

Mybatis中 # 和 $ 區別

  • 至關於對數據加上雙引號, $ 至關於直接顯示數據

  • 方式可以很大程度防止sql注入

Mybatis緩存

SpringMVC的運行流程

  1. 客戶端發送HTTP請求到服務器
  2. SpringMVC的核心DispatcherServlet將請求交給HandlerMapping處理
  3. HandlerMapping經過查詢機制找處處理當前請求的Handler
  4. DispatcherServlet將請求交給這個Handler處理
  5. Handler處理完成後返回一個ModleAndView對象,這個對象包含視圖邏輯名和數據對象
  6. 返回的視圖邏輯名會經過視圖解析器解析成真正的視圖,並交給DispatcherServlet處理
  7. DispatcherServlet將請求分派給真正的視圖對象,並反映到客戶端

說幾個SpringMVC的幾個註解,都是幹啥的?

@Controller:用於標記在一個類上,使用它標記的類就是一個SpringMVC Controller 對象。

@RequestMapping:是一個用來處理請求地址映射的註解,可用於類或方法上。用於類上,表示類中的全部響應請求的方法都是以該地址做爲父路徑。

@Resource和@Autowired:@Resource和@Autowired都是作bean的注入時使用,其實@Resource並非Spring的註解,它的包是javax.annotation.Resource,須要導入,可是Spring支持該註解的注入。

@ResponseBody:返回的數據不是html標籤的頁面,而是其餘某種格式的數據時(如json、xml等)使用。

@Repository:DAO層

@Service:服務層

@autireware和@resource的區別

@Autowired註解是按類型裝配依賴對象,默認狀況下它要求依賴對象必須存在,若是容許null值,能夠設置它required屬性爲false。

@Resource註解和@Autowired同樣,也能夠標註在字段或屬性的setter方法上,但它默認按名稱裝配。名稱能夠經過@Resource的name屬性指定,若是沒有指定name屬性,當註解標註在字段上,即默認取字段的名稱做爲bean名稱尋找依賴對象,當註解標註在屬性的setter方法上,即默認取屬性名做爲bean名稱尋找依賴對象。

@Resources按名稱,是JDK的,@Autowired按類型,是Spring的。

@PathVariable是幹啥的?

@PathVariable是用來對指定請求的URL路徑裏面的變量。

說說filter、servlet、listener。

Listener我是這樣理解他的,他是一種觀察者模式的實現。

Filter的使用戶能夠改變一 個request或修改一個response。 Filter 不是一個servlet,它不能產生一個response,可是他可以在一個request到達servlet以前預先處理request,也能夠在一個響應離開 servlet時處理response。

消息隊列了解嗎?

通俗的說,就是一個容器,把消息丟進去,不須要當即處理。而後有個程序去從容器裏面把消息一條條讀出來處理。

9、分佈式

Raft協議的leader選舉,正常狀況下,網絡抖動形成follower發起leader選舉,且該follower的Term比現有leader高。集羣中全部結點的日誌信息當前一致,這種狀況下會選舉成功嗎?

分佈式框架知道哪些?

dubbo

dubbo怎麼用的,有沒有參與部署

分佈式緩存的理解

10、Linux

linux查詢Java進程

ps -ef | grep java

linux查看內存佔用狀況

  • top命令提供了實時的運行中的程序的資源使用統計。你能夠根據內存的使用和大小來進行排序。
  • vmstat命令顯示實時的和平均的統計,覆蓋CPU、內存、I/O等內容。例如內存狀況,不只顯示物理內存,也統計虛擬內存。

11、雜項

設計一個秒殺系統,如何保證不超賣,還要保證服務可用

如何設計一個定時器定時完成某個任務?

如何保證集羣環境下搶購的併發安全?

Java中你擅長的地方

多線程,JVM

若是學習一門技術,你會怎麼學習

書籍+博客+視頻

對國內互聯網公司目前的開源生態有沒有什麼瞭解

舉出三個以上的國內開源框架,越多越好,dubbo、fastjson、sharding-jdbc、Elastic-job...

你對京東的見解

電商,突出質量

說出三個京東不如淘寶或者天貓的地方

淘寶是C2C,京東和天貓是B2C,淘寶門檻低,種類,國際市場佈局

看過啥書。

深刻理解Java虛擬機&HEAD FIRST設計模式&高性能MYSQL&Java併發編程實戰,看博客比較多,感受博客更有針對性

相關文章
相關標籤/搜索