文章目錄
- 前言
- 一、java基礎
- 1 java基礎知識
- 2 集合(必會)
- 3多執行緒(必會)
- 4執行緒池(高薪必問)
- 5JVM(高薪必問)
- 總結
前言
提示:回答面試題時最好面試官問一個點以后,我們自己延伸到其他問題,比如樂觀鎖、悲觀鎖,這種問題回答完以后,延伸到CAS機制,或者執行緒、多執行緒、執行緒池,
一、java基礎
1 java基礎知識
??1.1:面向物件的特征(必會)
面向物件的特征:封裝、繼承、多型、抽象,
? ?封裝:就是把物件的屬性和行為(資料)結合為一個獨立的整體,并盡可能隱藏物件的內部實作細節,就是把不想告訴或者不該告訴別人的東西隱藏起來,把可以告訴別人的公開,別人只能用我提供的功能實作需求,而不知道是如何實作的,增加安全性,
? ?繼承:子類繼承父類的資料屬性和行為,并能根據自己的需求擴展出新的行為,提高了代碼的復用性,
? ?多型:指允許不同的物件對同一訊息做出相應,即同一訊息可以根據發送物件的不同而采用多種不同的行為方式(發送訊息就是函式呼叫),封裝和繼承幾乎都是為多型而準備的,在執行期間判斷參考物件的實際型別,根據其實際的型別呼叫其相應的方法,
? ?抽象:表示對問題領域進行分析、設計中得出的抽象的概念,是對一系列看上去不同,但是本質上相同的具體概念的抽象,在Java中抽象用 abstract 關鍵字來修飾,用 abstract 修飾類時,此類就不能被實體化,從這里可以看出,抽象類(介面)就是為了繼承而存在的,
??1.2:基本資料型別有哪些(了解)

??1.3:JDK JRE JVM 的區別(必會)

? ?JDK(Java Development Kit)是整個 Java 的核心,是java開發工具包,包括了 Java 運行環境 JRE、Java 工具和 Java 基礎類別庫,
? ?JRE(Java Runtime Environment)是運行 JAVA 程式所必須的環境的集合,包含java虛擬機和java程式的一些核心類別庫,
? ?JVM 是 Java Virtual Machine(Java 虛擬機)的縮寫,是整個 java 實作跨平臺的最核心的部分,能夠運行以 Java 語言寫作的軟體程式,
??1.4:多載和重寫的區別(了解)
? ?多載: 發生在同一個類中,方法名必須相同,引數型別不同.個數不同.順序不同,方法回傳值和訪問修飾符可以不同,發生在編譯時,(同類同名不同引數)
? ?重寫: 發生在父子類中,方法名.引數串列必須相同,回傳值范圍小于等于父類,拋出的例外范圍小于等于父類, 訪問修飾符范圍大于等于父類;如果父類方法訪問修飾符為 private 則子類就不能重寫該方法,(同名同參不同類)
??1.5:java中==和equals的區別(必會)
== 的作用:
? ? 基本型別:比較的就是值是否相同
? ? 參考型別:比較的就是地址值是否相同
? equals 的作用:
? ?參考型別:默認情況下,比較的是地址值,
? ?特殊情況:String、Integer、Date這些類別庫中equals被重寫,比較的是內容而不是地址!
面試題:請解釋字串比較之中 “ == ” 和 equals() 的區別?
答: ==:比較的是兩個字串記憶體地址(堆記憶體)的數值是否相等,屬于數值比較; equals():比較的是兩個字串的內容,屬于內容比較,
??1.6:String 、StringBuffer、StringBuilder三者之間的區別(必會)
? ??? ?String 字串常量
? ??? ?StringBuffer 字串變數(執行緒安全)
? ??? ?StringBuilder 字串變數(非執行緒安全)
? ?String 中的String 類中使用 final 關鍵字修飾字符陣列來保存字串,private final char value[] ,String物件是不可變的,也就可以理解為常量,執行緒安全,
? ?AbstractStringBuilder是 StringBuilder 與 StringBuffer 的公共父類,定義了一些字串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法,
? ?StringBuffer 對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的,
? ?StringBuilder 并沒有對方法進行加同步鎖,所以是非執行緒安全的,
小結:
??1)如果要操作少量的資料用 String;
??(2)多執行緒操作字串緩沖區下操作大量資料用 StringBuffer;
??(3)單執行緒操作字串緩沖區下操作大量資料用 StringBuilder,
??1.7:介面和抽象類的區別
? ?實作:抽象類的子類使用 extends 來繼承;介面必須使用 implements 來實作介面,
? ?建構式:抽象類可以有建構式*;介面不能有,
? ?main 方法:抽象類可以有 main 方法,并且我們能運行它;介面不能有 main 方法,
? ?實作數量:類可以實作很多個介面;但是只能繼承一個抽象類,
? ?訪問修飾符:介面中的方法默認使用 public 修飾;抽象類中的方法可以是任意訪問修飾符.
??1.8:String常用的方法都有哪些(了解)
? ??? ?indexOf():回傳指定字符的索引,
? ??? ?charAt():回傳指定索引處的字符,
? ??? ?replace():字串替換,
? ??? ?trim():去除字串兩端空白,
? ??? ?split():分割字串,回傳一個分割后的字串陣列,
? ??? ?getBytes():回傳字串的 byte 型別陣列,
? ??? ?length():回傳字串長度,
? ??? ?toLowerCase():將字串轉成小寫字母,
? ??? ?toUpperCase():將字串轉成大寫字符,
? ??? ?substring():截取字串,
? ??? ?equals():字串比較,
? ??? ?compareTo():比較兩個字串的字典
? ??? ?hashCode:回傳此字串的哈希代碼
? ??? ?toCharArray():將此字串轉換為一個新的字符陣列
? ??? ?toString():這個物件(這已經是一個字串!)是自己回來了,
? ??? ?trim():回傳一個字串,去掉前后空格,
? ??? ?lastIndexOf(();回傳在指定字符的最后一個發生的字串內的索引
??1.9:反射(必會)
? ?在 Java 中的反射機制是指在運行狀態中,對于任意一個類都能夠知道這個類所有的 屬性和方法;并且對于任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取資訊 以及動態呼叫物件方法的功能成為 Java 語言的反射機制,通俗點說就是解耦合,
? ??1):獲取 Class 物件的 3 種方法 :
? ??? ?1.1):呼叫某個物件的 getClass()方法
? ??? ?Person p=new Person();
? ??? ?Class clazz=p.getClass();
? ??? ?1.2):呼叫某個類的 class 屬性來獲取該類對應的 Class 物件
? ??? ?Person p=new Person();
? ??? ?Class clazz=p.class();
? ??? ?1.3):使用 Class 類中的 forName()靜態方法(最安全/性能最好)
? ??? ?Class clazz=Class.forName(“類的全路徑”); (最常用)
? ??2):獲取 Class 物件的構造方法 :->如果沒有權限,可以使用SetAccessible
? ??? ?2.1):批量獲取
? ??? ?獲取這個類中的所有“構造方法”,包括私有、受保護的,
? ??? ?public Constructor<?>[] getDeclaredConstructors()
? ??? ?獲取這個類中的所有“公有構造方法”
? ??? ?public Constructor<?>[] getConstructors()
? ??? ?2.2):單個獲取
? ??? ?獲取這個類中的某個“構造方法”,包括私有、受保護的,
? ??? ?public Constructor<?>[] getDeclaredConstructor()
? ??? ?獲取這個類中的某個“共有構造方法”
? ??? ?public Constructor<?>[] getConstructor()
? ??3):獲取 Class 物件的成員屬性并賦值和取值 :->如果沒有權限,可以使用SetAccessible
? ??? ?3.1):批量獲取
? ??? ?獲取這個類中所有的公有成員屬性
? ??? ?public Field[] getFields()
? ??? ?獲取這個類中所有的成員屬性
? ??? ?public Field[] getDeclaredFields()
? ??? ?3.2):單個獲取
? ??? ?獲取這個類中某個公有成員屬性
? ??? ?public Field getField(String name)
? ??? ?獲取這個類中的某個成員屬性
? ??? ?public Field getDeclaredField(String name)
? ??? ?設定屬性的值:獲取到成員屬性的回傳值.set(Object target,Object value).
? ??? ?獲取屬性的值:獲取到成員屬性的回傳值.get(Object target,Object value).
? ??4):獲取 Class 物件的成員方法 :->如果沒有權限,可以使用SetAccessible
? ??? ?4.1):批量獲取
? ??? ?獲取這個類中所有的公有成員方法
? ??? ?public 方法[] getMethods()
? ??? ?獲取這個類中所有的成員方法
? ??? ?public 方法[] getDeclaredMethods()
? ??? ?4.2):單個獲取
? ??? ?獲取這個類中某個公有成員方法
? ??? ?public 方法 getMethod(String name,
? ??? ?獲取這個類中的某個成員方法
? ??? ?public 方法 getDeclaredMethod(String name)
? ??? ?呼叫方法:獲取成員方法后回傳值.inuoke(Object targetObj,Object…實參列);
??1.10:什么是單例模式,有哪幾種,
? ?單例模式:某個類的實體在多執行緒的環境下只會被創建一次出來
? ?單例模式有餓漢式單例模式、懶漢式模式、雙檢鎖模式三種
??1.11:jdk1.8的新特性
? ?1:Lambda運算式
? ??Lambda允許把函式作為一個方法的引數,

? ?2:方法參考
? ??方法參考允許直接參考已有的java類或物件的方法或構造方法,

? ?3:函式式介面
? ?有且僅有一個抽象方法的介面叫做函式式介面,函式式介面可以被隱式轉換為 Lambda 運算式,
? ?通常函式式介面上會添@FunctionalInterface 注解,
? ?介面允許定義默認方法和靜態方法
? ?從 JDK8 開始,允許介面中存在一個或多個默認非抽象方法和靜態方法,
? ?4:Strieam API
? ?新添加的 Stream API(java.util.stream)把真正的函式式編程風格引入到 Java 中,這種風格將要處理的元素集合看作一種流,流在管道中傳輸,并且可以在管道的節點上進行處理,比如篩選,排序,聚合等,
? ?常用方法有
???1):參考型別陣列流 Stream.of();
???2):List集合中:
???? 遍歷:forEach()終結方法,沒有回傳值
???? 遍歷:過濾 filter() 拼接方法,回傳Stream流
???? 取出前n個元素 Iimit(n) 回傳一個新流物件
????跳過前n個元素 skip(n)回傳一個新流物件
???? 轉換泛型 map() 回傳一個新流物件
???? 合并流 concat(Stream s1.Stream s2)
? ?5:日期/時間類改進
? ?之前的 JDK 自帶的日期處理類非常不方便,我們處理的時候經常是使用的第三方工具包,比如 commons-lang包等,不過 JDK8 出現之后這個改觀了很多,比如日期時間的創建、比較、調整、格式化、時間間隔等,
這些類都在 java.time 包下,LocalDate/LocalTime/LocalDateTime,
? ?6:Optional 類
? ?Optional 類是一個可以為 null 的容器物件,如果值存在則 isPresent()方法會回傳 true,呼叫 get()方法會回傳該物件,
??1.12:java的例外(了解)

??Throwable是所有Java程式中錯誤處理的父類,有兩種資類:Error和Exception,
??Error:表示由JVM所偵測到的無法預期的錯誤,由于這是屬于JVM層次的嚴重錯誤,導致JVM無法繼續執行,因此,這是不可捕捉到的,無法采取任何恢復的操作,頂多只能顯示錯誤資訊,
??Exception:表示可恢復的例外,這是可捕捉到的,
??1.非運行時例外 (編譯例外):是RuntimeException以外的例外** 型別上都屬于Exception類及其子類,從程式語法角度講是必須進行處理的例外,如果不處理,程式就不能編譯通過,如IOException、SQLException等以及用戶自定義的Exception例外,一般情況下不自定義檢查例外,
??2.運行時例外:都是RuntimeException類及其子類例外
???NullPointerException(空指標例外)
???IndexOutOfBoundsException(下標越界例外),
? ? 這些例外是不檢查例外,程式中可以選擇捕獲處理,也可以不處理,這些例外一般是由程式邏輯錯誤引起的,程式應該從邏輯角度盡可能避免這類例外的發生,運行時例外的特點是Java編譯器不會檢查它,也就是說,當程式中可能出現這類例外,即使沒有用try-catch陳述句捕獲它,也沒有用throws子句宣告拋出它,也會編譯通過,
常見的RunTime例外幾種如下:
??NullPointerException - 空指標參考例外
??ClassCastException - 型別強制轉換例外,
??IllegalArgumentException - 傳遞非法引數例外,
??ArithmeticException - 算術運算例外
??ArrayStoreException - 向陣列中存放與宣告型別不兼容物件例外
??IndexOutOfBoundsException - 下標越界例外
? ????
??1.13:BIO、NIO、AIO 有什么區別?
? ????
??BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,并發處理能力低,
??New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實作了多路復用,
??Asynchronous IO 是 NIO 的升級,也叫 NIO2,實作了異步非堵塞 IO ,異步 IO 的操作基于事件和回呼機制,
? ????
??1.14:Threadloal的原理(高薪常問)
? ????
??ThreadLocal: 為共享變數在每個執行緒中創建一個副本,每個執行緒都可以訪問自己內部的副本變數,通過threadlocal保證執行緒的安全性,
??ThreadLocal :類中有一個靜態內部類ThreadLocalMap(其類似于Map),用鍵值對的形式存盤每一個執行緒的變數副本,ThreadLocalMap中元素的key為當前ThreadLocal物件,而value對應執行緒的變數副本,
? ????
??1.15:同步鎖、死鎖、樂觀鎖、悲觀鎖(高薪常問)
? ????
??同步鎖:當多個執行緒同時訪問同一個資料時,很容易出現問題,為了避免這種情況出現,我們要保證執行緒同步互斥,就是指并發執行的多個執行緒,在同一時間內只允許一個執行緒訪問共享資料,Java 中可以使用 synchronized 關鍵字來取得一個物件的同步鎖,
??死鎖:多個執行緒同時被阻塞,他們中的一個或者全部都在等待某個資源被釋放,
??樂觀鎖: 總是假設最好的情況,每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,在Java中java.util.concurrent.atomic包下面的原子變數類就是使用了樂觀鎖的一種實作方式CAS實作的,(在這里可以和面試官延伸到CAS機制)
??悲觀鎖: 總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖(共享資源每次只給一個執行緒使用,其它執行緒阻塞,用完后再把資源轉讓給其它執行緒),傳統的關系型資料庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖,Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實作,(這里就可以和面試官延伸到synchronized的實作原理、和Lock的區別,和volatile的區別,或者直接點就是多執行緒)
? ????
??1.16:說一下 synchronized 底層實作原理?
? ????
??synchronized可以保證方法或者代碼塊在運行時,同一時刻只有一個方法可以進入到臨界區,同時它還可以保證共享變數的記憶體可見性,
??java中每一個物件都可以作為鎖,這是synchronized實作同步的基礎,
??普通同步方法,鎖是當前實體物件,
??靜態同步方法,鎖是當前類的class物件
??同步方法塊,鎖是括號里面的物件
? ????
??1.17:synchronized 和 volatile 的區別是什么?
? ????
??volatile本質是在告訴JVM當前變數是在暫存器(作業記憶體)中的值是不確定的,需要從主記憶體中讀取;synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞,
??synchronized通過加鎖的方式,使得其在需要原子性、可見性和有序性三個特性的時候都可以作為其中一種解決方案,看起來是萬能的,volatile通過在volatile變數的操作前后插入記憶體屏障的方式,保證了變數在并發場景下的可見性和有序性,
??volatile僅能使用在變數級別;synchronized則可以使用在變數、方法、和類級別的,
??volatile不會造成執行緒的阻塞;synchronized可能會造成執行緒的阻塞,
? ????
??1.18:synchronized 和 Lock 有什么區別?
? ????
??首先,synchronized是java中的關鍵字,而Lock是一個類
??synchronized無法判斷是否獲取鎖的狀態,但是Lock可以通過lock()和unlock()方法來判斷鎖的狀態,
??synchronized會自動釋放鎖(執行完相應的代碼塊或者出現例外就會釋放鎖),Lock需在finally中手工釋放鎖(unock()方法),否則就會容易造成執行緒死鎖,
??用synchronized關鍵字的倆個執行緒,執行緒1和執行緒2,如果執行緒1獲得鎖,執行緒2等待,,如果執行緒一堵塞,執行緒2就會一直等待下去,而這個Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,執行緒可以不用一直等待就結束了,
??synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可)
??synchronized鎖適合代碼少量的同步問題,而Lock鎖適合大量同步的代碼的同步問題,
? ????
??1.19:手寫冒泡排序!(必會)
? ????


? ????
??1.20:final、finally、finalize的區別
? ????
final:最終的意識,可以修飾類,成員變數,成員方法,
??修飾類,類不能被繼承
??修飾變數,變數是常量
??修飾方法,方法不能被重寫,
finally:是例外處理的一部分,用來釋放資源,一般來說,代碼肯定會執行,特殊情況:在執行到finally之前JVM退出了
finalize:是Object類的一個方法,用于垃圾回收,
? ????
??1.21:如果catch陳述句里面有return,請問finally里面的代碼還會執行嘛,如果會,請問是return前,還是return后,
?? ??會執行,在return前,
? ????
2 集合(必會)
??2.1:常見的陣列結構
? ????
?? ??常見的陣列結構有:陣列、堆疊、佇列、鏈表、樹、散列、堆、圖等

?? ??陣列是最常見的資料結構,陣列的特點是長度固定,陣列的大小固定后就沒有辦法擴容,陣列只能存盤一種型別的資料,添加洗掉的操作慢,因為要移動其他的元素
?? ??堆疊是一種基于先進后出的資料結構,是一種只能在一段進行插入和洗掉操作的特殊線性表,它按照先進后出的原值存入資料,先進入的資料被壓在堆疊底,最后的資料在堆疊頂,需要讀資料的時候從堆疊頂開始彈出資料(最后一個資料被第一個彈出來)
?? ??佇列是一種基于先進先出的資料結構,是一種只能在一端進行插入,在另一端進行洗掉操作的特殊性線表,它按照先進先出的原則存盤資料,先進的資料,在讀取資料時先被讀取出來,
?? ??鏈表是一種物理存盤單元上非連續,非順序的存盤結構,其物理結構不能只表示資料元素的邏輯順序,資料元素的邏輯順序是通過鏈表中的指標鏈接次序實作的,鏈表是有一系列的節點(鏈表中的每一個元素稱為節點)組成,節點可以在運行時動態生成,根據指標的指向,鏈表能形成不同的結構,例如單鏈表、雙向鏈表、回圈串列等
?? ??樹是我們計算機中非常重要的一種資料結構,同時使用樹這種資料結構,可以描述現實生活中的很多事物,例如家譜、單位的組織架構等,有二叉樹、平衡樹、紅黑樹、B樹、B+樹
?? ??散串列,也叫哈利表,是根據關鍵碼和值直接進行訪問的資料結構,通過key和value來映射到集合中的一個位置,這樣就可以很快找到集合中的對應元素,
?? ??堆是計算機學科中一類特殊的資料結構的同城,堆通常可以被看作是一顆完全二叉樹的陣列物件,
?? ??圖是由一組頂點和一組能夠將倆個頂點相連的邊組成的,
? ????
??2.2:集合和陣列的區別
? ????
??區別:陣列長度固定,集合長度可變
??陣列中存盤的是同一種資料型別的元素,可以存盤基本資料型別,也可以存盤參考資料型別
??集合村粗的都是物件,而且物件的資料型別可以不一致,在開發中一般當物件比較多的時候,使用集合來存盤物件,
? ????
??2.3:java中集合的體系
? ????

??Connection介面:
?? ?1):List:有序,可重復
?? ??1.1)ArrayList
?? ??優點:底層資料結構是陣列,查詢快,增刪慢,
?? ??缺點:執行緒不安全,效率高
?? ??1.2):Vector
?? ??優點:底層資料結構是陣列查詢快,增刪慢,
?? ??缺點:執行緒安全,效率低(已經舍棄了),
?? ??1.3):LinkedList
?? ??優點:底層資料結構是鏈表,查詢慢,增刪快,
?? ??缺點:縣城不安全,效率高
?? ?2):Set 無序,唯一
?? ??2.1):HashSet
?? ??底層資料結構是哈希表,(無序,唯一)
?? ??如何來保證元素的唯一性?
?? ??依賴倆個方法 hashCode()和equals()
?? ??2.2):LinkedHashSet
?? ??底層資料結構是鏈表和哈希表,
?? ??由鏈表保證元素有序
?? ??由哈希表保證元素唯一
?? ??2.3):TreeSet
?? ??底層資料結構是紅黑樹,(唯一,有序)
?? ??1):如何保證元素排序的呢?
?? ??自然排序
?? ??比較器排序
?? ??2):如何保證元素的唯一性的呢?
?? ??根據比較的回傳值是否是0來決定
??Map介面:
?? ??1):HashMap
?? ??基于hash表的Map介面實作,非執行緒安全、高效、支持null值和null鍵,執行緒不安全
?? ??2):HashTable
?? ??執行緒安全、低效、不支持null鍵和null值
????3):LinkedHashMap
?? ??執行緒不安全,是HashMap的一個子類,保存了已記錄的插入順序,
?? ??4):TreeMap
?? ??能夠把它保存的記錄根據鍵排序,默認是鍵值的升序排序,執行緒不安全
? ????
??2.4:List和Map、Set的區別
? ????
??List和set是存盤單列資料的集合,Map是存盤鍵值對這樣的雙列資料的集合;
??List中存盤的資料是有順序的,并且值允許重復
??Map中存盤的資料是無序的,它的鍵是不允許重復的,但是值是允許重復的;
??Set中存盤的資料是無順序的,并且并不允許重復,但元素在集合中的位置是由元素的hashcode決定,即位置是固定的(set集合是根據hashcode來進行資料存盤的,所以位置是固定的,但是這個位置不是用戶可以控制的,所以對于用戶來說set中的元素還是無序的),
? ????
??2.5:HashMap的底層原理
? ????
??HashMap在JDK1.8以前的實作方式是陣列+鏈表,
??但是在JDK1.8后對HashMap進行可底層優化,改為了由陣列+鏈表+紅黑樹實作,只要的目的是提高查找效率


??1. Jdk8陣列+鏈表或者陣列+紅黑樹實作,當鏈表中的元素超過了 8 個以后, 會將鏈表轉換為紅黑樹,當紅黑樹節點 小于 等于6 時又會退化為鏈表,
??2. 當new HashMap():底層沒有創建陣列,首次呼叫put()方法示時,底層創建長度為16的陣列,jdk8底層的陣列是:Node[],而非Entry[],用陣列容量大小乘以加載因子得到一個值,一旦陣列中存盤的元素個數超過該值就會呼叫rehash方法將陣列容量增加到原來的兩倍,專業術語叫做擴容,在做擴容的時候會生成一個新的陣列,原來的所有資料需要重新計算哈希碼值重新分配到新的陣列,所以擴容的操作非常消耗性能.
默認的負載因子大小為0.75,陣列大小為16,也就是說,默認情況下,那么當HashMap中元素個數超過160.75=12的時候,就把陣列的大小擴展為216=32,即擴大一倍,
??在我們Java中任何物件都有hashcode,hash演算法就是通過hashcode與自己進行向右位移16的異或運算,這樣做是為了計算出來的hash值足夠隨機,足夠分散,還有產生的陣列下標足夠隨機,
??map.put(k,v)實作原理
????(1)首先將k,v封裝到Node物件當中(節點),
????(2)先呼叫k的hashCode()方法得出哈希值,并通過哈希演算法轉換成陣列的下標,
???? (3)下標位置上如果沒有任何元素,就把Node添加到這個位置上,如果說下標對應的位置上有鏈表,此時,就會拿著k和鏈表上每個節點的k進行equal(),如果所有的equals()方法回傳都是false,那么這個新的節點將被添加到鏈表的末尾,如其中有一個equals回傳了true,那么這個節點的value將會被覆寫,
??map.get(k)實作原理
????(1)先呼叫k的hashCode()方法得出哈希值,并通過哈希演算法轉換成陣列的下標,
????(2)在通過陣列下標快速定位到某個位置上,重點理解如果這個位置上什么都沒有,則回傳null,如果這個位置上有單向鏈表,那么它就會拿著引數K和單向鏈表上的每一個節點的K進行equals,如果所有equals方法都回傳false,則get方法回傳null,如果其中一個節點的K和引數K進行equals回傳true,那么此時該節點的value就是我們要找的value了,get方法最侄訓傳這個要找的value,
? ????
??2.6:HashMap和HashTable、ConcurrentHashMap區別
? ????
??區別對比一(HashMap和HashTable區別)
1):HashMap是非執行緒安全的,HashTable是執行緒安全的,
2):HashMap的鍵和值都允許有null值存在,而Table不行,
3):因為執行緒安全的問題,HashMap效率比HashTable的要高,
4):HashTable是同步的,而HashMap不是,因此HashMap適合于單執行緒環境,而HashTable適合多執行緒環境,
5):一般不建議使用HashTable,因為是遺留類,內部實作很多沒優化,即使在多執行緒環境下,現在也有同步的ConcurrentHashMap替代,沒有必要因為是多執行緒而使用HashTable,
??區別對比二(HashTable和CincurrenHashMap區別):
1):HashTable 使用的是 Synchronized 關鍵字修飾,ConcurrentHashMap 是JDK1.7使用了鎖分段技術來保證執行緒安全的,JDK1.8ConcurrentHashMap取消了Segment分段鎖,采用CAS和synchronized來保證并發安全,資料結構跟HashMap1.8的結構類似,陣列+鏈表/紅黑二叉樹,
2):synchronized只鎖定當前鏈表或紅黑二叉樹的首節點,這樣只要hash不沖突,就不會產生并發,效率又提升N倍,
? ????
3多執行緒(必會)
??3.1:什么事執行緒?執行緒和行程的區別
? ????
??執行緒:是行程的一個物體,是cpu調度和分配的基本單位,是比行程更少的可以獨立運行的基本單位
??行程:具有一定獨立功能的程式關于某個資料集合上的例外運行活動,是作業系統進行資源分配和調度的一個獨立單位,
??特點:執行緒的劃分尺度小于行程,這使多執行緒程式擁有高并發性,行程在運行時各自記憶體單元相互獨立,執行緒之間,記憶體共享,這使多執行緒編程可以擁有更好的性能和用戶體驗,
? ????
??3.2:創造執行緒有幾種方式
? ????
??1):繼承Thread類并重寫run方法,實作簡單但是不可以繼承其他類
??2):實作Runnable介面并重寫run方法,避免了單繼承局限性,編程更加靈活,實作解耦,
??3):實作Callable介面并重寫Call方法,創建執行緒,可以獲取執行緒執行結果的回傳值,并且拋出例外
??4):使用執行緒池創建(Executor介面)
? ????
??3.3:Runnable和Callable的區別
? ????
??Runnable介面run方法沒有回傳值,Callable介面call方法有回傳值,支持泛型
??Runnable介面run方法只能拋出運行時例外,且無法捕獲處理;Callable介面Call方法允許拋出例外,可以獲取例外資訊,
? ????
??3.4:如何啟動一個新執行緒、呼叫start和run方法的區別?
? ????

??執行緒物件呼叫run方法不開啟執行緒,僅是物件呼叫方法,
??執行緒物件呼叫start開啟執行緒,并讓jvm呼叫run方法在開啟的執行緒中執行
??呼叫start方法可以啟動執行緒,并且使得執行緒進入就緒狀態,而run方法只是thread的一個普通方法,還是在主執行緒中執行,
? ????
??3.5:執行緒有哪幾種狀態,以及各種狀態之間的轉換?
? ????

??1. 第一是new->新建狀態,在生成執行緒物件,并沒有呼叫該物件的start方法,這是執行緒處于創建狀態,
??2. 第二是Runnable->就緒狀態,當呼叫了執行緒物件的start方法之后,該執行緒就進入了就緒狀態,但是此時執行緒調度程式還沒有把該執行緒設定為當前執行緒,此時處于就緒狀態,
??3. 第三是Running->運行狀態,執行緒調度程式將處于就緒狀態的執行緒設定為當前執行緒,此時執行緒就進入了運行狀態,開始運行run函式當中的代碼,
??4. 第四是阻塞狀態,阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止運行,直到執行緒進入就緒狀態,才有機會轉到運行狀態,阻塞的情況分三種:
????(1)等待 – 通過呼叫執行緒的wait() 方法,讓執行緒等待某作業的完成,
????(2)超時等待 – 通過呼叫執行緒的sleep() 或join()或發出了I/O請求時,執行緒會進入到阻塞狀態,當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態,
????(3)同步阻塞 – 執行緒在獲取synchronized同步鎖失敗(因為鎖被其它執行緒所占用),它會進入同步阻塞狀態,
??5. 第五是dead->死亡狀態: 執行緒執行完了或者因例外退出了run()方法,該執行緒結束生命周期.
? ????
??3.6:執行緒相關的基本方法
? ????
??執行緒相關的基本方法有 wait,notify,notifyAll,sleep,join,yield 等
1.執行緒等待(wait)
??呼叫該方法的執行緒進入 WAITING 狀態,只有等待另外執行緒的通知或被中
斷才會回傳,需要注意的是呼叫 wait()方法后,會釋放物件的鎖,因此,wait 方
法一般用在同步方法或同步代碼塊中,
2.執行緒睡眠(sleep)
??sleep 導致當前執行緒休眠,與 wait 方法不同的是 sleep 不會釋放當前占
有的鎖,sleep(long)會導致執行緒進入 TIMED-WATING 狀態,而 wait()方法
會導致當前執行緒進入 WATING 狀態.
3.執行緒讓步(yield)
??yield 會使當前執行緒讓出 CPU 執行時間片,與其他執行緒一起重新競爭
CPU 時間片,一般情況下,優先級高的執行緒有更大的可能性成功競爭得到 CPU
時間片,但這又不是絕對的,有的作業系統對 執行緒優先級并不敏感,
4.執行緒中斷(interrupt)
??中斷一個執行緒,其本意是給這個執行緒一個通知信號,會影響這個執行緒內部的
一個中斷標識位,這個執行緒本身并不會因此而改變狀態(如阻塞,終止等)
5.Join 等待其他執行緒終止
??join() 方法,等待其他執行緒終止,在當前執行緒中呼叫一個執行緒的 join() 方
法,則當前執行緒轉為阻塞狀態,回到另一個執行緒結束,當前執行緒再由阻塞狀態變
為就緒狀態,等待 cpu 的寵幸.
6.執行緒喚醒(notify)
??Object 類中的 notify() 方法,喚醒在此物件監視器上等待的單個執行緒,如果所有執行緒都在此物件上等待,則會選擇喚醒其中一個執行緒,選擇是任意的,并在對實作做出決定時發生,執行緒通過呼叫其中一個 wait() 方法,在物件的監視 器上等待,直到當前的執行緒放棄此物件上的鎖定,才能繼續執行被喚醒的執行緒,
被喚醒的執行緒將以常規方式與在該物件上主動同步的其他所有執行緒進行競爭,類 似的方法還有 notifyAll() ,喚醒再次監視器上等待的所有執行緒,
? ????
??3.7:wait()和sleep()的區別
? ????
1:來自不同的類
??wait來自Object類
??sleep來自Thread類
2:關于鎖的釋放
??wait在等待的程序中會釋放鎖
??sleep在等待的程序中不會釋放鎖
3:使用的范圍
??wait必須是在同步代碼塊中使用
??sleep可以再任何地方使用
4:是否需要捕獲例外
??wait不需要捕獲例外;
??sleep需要捕獲例外
? ????
4執行緒池(高薪必問)
??4.1:為什么需要執行緒池
? ????
??在實際使用中,執行緒是很占用系統資源的,如果對執行緒管理不完善的話很容易導致系統問題,因此,在大多數并發框架中都會使用執行緒池來管理執行緒,使用執行緒池管理執行緒主要有如下好處:
1、使用執行緒池可以重復利用已有的執行緒繼續執行任務,避免執行緒在創建和銷毀時造成的消耗
2、由于沒有執行緒創建和銷毀時的消耗,可以提高系統回應速度
3、通過執行緒可以對執行緒進行合理的管理,根據系統的承受能力調整可運行執行緒數量的大小等
? ????
??4.2:執行緒池的分類
? ????

??1):newCachedThreadPool:創建一個可進行快取重復利用的執行緒池
??2):newFixedThreadPool:創建一個可重用固定執行緒數的執行緒池,以共享的無界佇列方式來運行這些執行緒,執行緒池中的執行緒處于一定的量,可以很好的控制執行緒的并發量
??3):newSingleThreadExecutor:創建一個使用單個 worker 執行緒的Executor ,以無界佇列方式來運行該執行緒,執行緒池中最多執行一個執行緒,之后提交的執行緒將會排在佇列中以此執行
??4):newSingleThreadScheduledExecutor:創建一個單執行緒執行程式,它可安排在給定延遲后運行命令或者定期執行
??5):newScheduledThreadPool:創建一個執行緒池,它可安排在給定延遲后運行命令或者定期的執行
??6):newWorkStealingPool:創建一個帶并行級別的執行緒池,并行級別決定了同一時刻最多有多少個執行緒在執行,如不傳并行級別引數,將默認為當前系統的CPU個數
? ????
??4.3:核心引數
? ????
??corePoolSize:核心執行緒池的大小
??maximumPoolSize:執行緒池能創建執行緒的最大個數
??keepAliveTime:空閑執行緒存活時間
??unit:時間單位,為keepAliveTime指定時間單位
??workQueue:阻塞佇列,用于保存任務的阻塞佇列
??threadFactory:創建執行緒的工程類
??handler:飽和策略(拒絕策略)
? ????
??4.4:執行緒池的原理
? ????

執行緒池的作業程序如下:
當一個任務提交至執行緒池之后,
- 執行緒池首先判斷核心執行緒池里的執行緒是否已經滿了,如果不是,則創建一個新的作業執行緒來執行任務,否則進入2.
- 判斷作業佇列是否已經滿了,倘若還沒有滿,將執行緒放入作業佇列,否則進入3.
- 判斷執行緒池里的執行緒是否都在執行任務,如果不是,則創建一個新的作業執行緒來執行,如果執行緒池滿了,則交給飽和策略來處理任務,
- ? ????
??4.5:拒絕策略
? ????
??ThreadPoolExecutor.AbortPolicy(系統默認): 丟棄任務并拋出RejectedExecutionException例外,讓你感知到任務被拒絕了,我們可以根據業務邏輯選擇重試或者放棄提交等策略
??ThreadPoolExecutor.DiscardPolicy: 也是丟棄任務,但是不拋出例外,相對而言存在一定的風險,因為我們提交的時候根本不知道這個任務會被丟棄,可能造成資料丟失,
??ThreadPoolExecutor.DiscardOldestPolicy: 丟棄佇列最前面的任務,然后重新嘗試執行任務(重復此程序),通常是存活時間最長的任務,它也存在一定的資料丟失風險
??ThreadPoolExecutor.CallerRunsPolicy:既不拋棄任務也不拋出例外,而是將某些任務回退到呼叫者,讓呼叫者去執行它,
? ????
??4.6:執行緒池的關閉
? ????
關閉執行緒池,可以通過shutdown和shutdownNow兩個方法
原理:遍歷執行緒池中的所有執行緒,然后依次中斷
??1、shutdownNow首先將執行緒池的狀態設定為STOP,然后嘗試停止所有的正在執行和未執行任務的執行緒,并回傳等待執行任務的串列;
??2、shutdown只是將執行緒池的狀態設定為SHUTDOWN狀態,然后中斷所有沒有正在執行任務的執行緒
? ????
5JVM(高薪必問)
虛擬機,一種能夠運行java位元組碼的虛擬機,
? ????
??5.1:JDK1.8 JVM運行時記憶體

程式計數器:
執行緒私有的(每個執行緒都有一個自己的程式計數器), 是一個指標. 代碼運行, 執行命令. 而每個命令都是有行號的,會使用程式計數器來記錄命令執行到多少行了.
Java虛擬機堆疊:
執行緒私有的(每個執行緒都有一個自己的Java虛擬機堆疊). 一個方法運行, 就會給這個方法創建一個堆疊幀, 堆疊幀入堆疊執行代碼, 執行完畢之后出堆疊(彈堆疊)
本地方法堆疊:
執行緒私有的(每個執行緒都有一個自己的本地方法堆疊), 和Java虛擬機堆疊類似, Java虛擬機堆疊加載的是普通方法,本地方法加載的是native修飾的方法.
native:在java中有用native修飾的,表示這個方法不是java原生的.
堆:
執行緒共享的(所有的執行緒共享一份). 存放物件的,new的物件都存盤在這個區域.還有就是常量池.
元空間: 存盤.class 資訊, 類的資訊,方法的定義,靜態變數等.而常量池放到堆里存盤
JDK1.8和JDK1.7的jvm記憶體最大的區別是, 在1.8中方法區是由元空間(元資料區)來實作的, 常量池.
1.8不存在方法區,將方法區的實作給去掉了.而是在本地記憶體中,新加入元資料區(元空間).
? ????
??5.2:JDK1.8堆記憶體結構

Young 年輕區(代): Eden+S0+S1, S0和S1大小相等, 新創建的物件都在年輕代
Tenured 年老區: 經過年輕代多次垃圾回收存活下來的物件存在年老代中.
Jdk1.7和Jdk1.8的區別在于, 1.8將永久代中的物件放到了元資料區, 不存永久代這一區域了.
? ????
5.3:Gc垃圾回收**
??JVM的垃圾回收動作可以大致分為兩大步,首先是「如何發現垃圾」,然后是「如何回收垃圾」,說明一點, 執行緒私有的不存在垃圾回收, 只有執行緒共享的才會存在垃圾回收, 所以堆中存在垃圾回收.
??5.3.1 如何發現垃圾
??Java語言規范并沒有明確的說明JVM使用哪種垃圾回收演算法,但是常見的用于「發現垃圾」的演算法有兩種,參考計數演算法和根搜索演算法,
1.參考計數演算法
??該演算法很古老(了解即可),核心思想是,堆中的物件每被參考一次,則計數器加1,每減少一個參考就減1,當物件的參考計數器為0時可以被當作垃圾收集,
??優點:快,
??缺點:無法檢測出回圈參考,如兩個物件互相參考時,他們的參考計數永遠不可能為0,
2.根搜索演算法(也叫可達性分析)
??根搜索演算法是把所有的參考關系看作一張圖,從一個節點GC ROOT開始,尋找對應的參考節點,找到這個節點以后,繼續尋找這個節點的參考節點,當所有的參考節點尋找完畢之后,剩余的節點則被認為是沒有被參考到的節點,即可以當作垃圾,
??Java中可作為GC Root的物件有
??1.虛擬機堆疊中參考的物件
??2.本地方法堆疊參考的物件
??2.方法區中靜態屬性的參考物件
??3.方法區中常量的參考物件
??5.3.2 如何回收垃圾
??Java中用于「回收垃圾」的常見演算法有4種:
1.標記-清除演算法(mark and sweep)
??分為“標記”和“清除”兩個階段:首先標記出所有需要回收的物件,在標記完成之后統一回收掉所有被標記的物件,
??缺點:首先,效率問題,標記和清除效率都不高,其次,標記清除之后會產生大量的不連續的記憶體碎片,
2.標記-整理演算法
??是在標記-清除演算法基礎上做了改進,標記階段是相同的,但標記完成之后不是直接對可回收物件進行清理,而是讓所有存活的物件都向一端移動,在移動程序中清理掉可回收的物件,這個程序叫做整理,
??優點:記憶體被整理后不會產生大量不連續記憶體碎片,
3.復制演算法(copying)
??將可用記憶體按容量分成大小相等的兩塊,每次只使用其中一塊,當這塊記憶體使用完了,就將還存活的物件復制到另一塊記憶體上去,然后把使用過的記憶體空間一次清理掉,
??缺點:可使用的記憶體只有原來一半,
4.分代收集演算法(generation)
當前主流JVM都采用分代收集(Generational Collection)演算法, 這種演算法會根據物件存活周期的不同將記憶體劃分為年輕代、年老代、永久代,不同生命周期的物件可以采取不同的回收演算法,以便提高回收效率,

(1)年輕代(Young Generation)
??1.所有新生成的物件首先都是放在年輕代的,
??2.新生代記憶體按照8:1:1的比例分為一個eden區和兩個Survivor(survivor0,survivor1)區,大部分物件在Eden區中生成,回收時先將eden區存活物件復制到一個survivor0區,然后清空eden區,當這個survivor0區也存放滿了時,則將eden區和survivor0區存活物件復制到另一個survivor1區,然后清空eden和這個survivor0區,此時survivor0區是空的,然后將survivor0區和survivor1區交換,即保持survivor1區為空, 如此往復,
??3.當survivor1區不足以存放eden和survivor0的存活物件時,就將存活物件直接存放到老年代,若是老年代也滿了就會觸發一次Full GC,也就是新生代、老年代都進行回收,
??4.新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)
(2)年老代(Old Generation)
??1.在年輕代中經歷了N次垃圾回收后仍然存活的物件,就會被放到年老代中,因此,可以認為年老代中存放的都是一些生命周期較長的物件,
??2.記憶體比新生代也大很多(大概是2倍),當老年代記憶體滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代物件存活時間比較長,存活率比較高,
(3)持久代(Permanent Generation)
??用于存放靜態檔案,如Java類、方法等,持久代對垃圾回收沒有顯著影響,從JDK8以后已經廢棄, 將存放靜態檔案,如Java類、方法等這些存盤到了元資料區.
??5.4:JVM調優引數
??這里只給出一些常見的性能調優的引數及其代表的含義,(大家記住5.6個就行, 并不需要都記住.)

??-Xmx3550m:設定JVM最大可用記憶體為3550M,
??-Xms3550m:設定JVM初始記憶體為3550m,注意:此值一般設定成和-Xmx相同,以避免每次垃圾回收完成后JVM重新分配記憶體,
??-Xmn2g:設定年輕代大小為2G,整個JVM記憶體大小=年輕代大小 + 年老代大小 + 持久代大小,此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8,
??-Xss256k:設定每個執行緒的堆疊大小,JDK5.0以后每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K,根據應用的執行緒所需記憶體大小進行調整,在相同物理記憶體下,減小這個值能生成更多的執行緒,

??-XX:NewRatio=4:設定年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代),設定為4,則年輕代與年老代所占比值為1:4,(該值默認為2)
??-XX:SurvivorRatio=4:設定年輕代中Eden區與Survivor區的大小比值,設定為4,則兩個Survivor區與一個Eden區的比值為2:4,
總結
這里是最基本的java基礎面試題,下一章Web面試題
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258716.html
標籤:java
