Q:JDK和JRE區別?html
JDK是整個JAVA的核心,包括了Java運行環境JRE,一堆Java工具和Java基 礎的類庫。經過JDK開發人員將源碼文件(java文件)編譯成字節碼文件(class文 件)。JRE是Java運行環境,不含開發環境,即沒有編譯器和調試器。將class文件 加載到內存準備運行。
Q:final關鍵字,抽象類可使用final修飾嗎?java
1.用來修飾數據,包括成員變量和局部變量,該變量只能被賦值一次且它的 值沒法被改變。對於成員變量來說,必須在聲明時或者構造方法中對它賦值; 2.修飾方法,表示該方法沒法被重寫; 3.修飾類,表示該類沒法被繼承。抽象類是被用於繼承的,final修飾表明不可修改、不可繼承的。因此不能用 final修飾抽象類。
Q:JAVA容器程序員
ArrayList底層數組實現,封裝了常見的增刪改查操做,而且支持動態擴容。適 合查找多的場合。LinkedList基於鏈表實現的列表。適合增刪狀況較多的場合。TreeSet,基於二叉排序樹(紅黑樹)實現的。TreeSet裏最典型的就是它用到 了兩種排序方式,即基於元素對象自身的實現的Comparable接口的天然排序, 以及基於更爲靈活不與單個元素綁定的Comparator接口的客戶化排序。本身在 構造的時候傳入一個比較器便可。HashMap是用來存儲鍵值對的映射關係,底層是用數組+鏈表實現的。結合put 操做講一下。HashSet其實就是基於HashMap實現的,只不過將值固定爲一個固定的值。LinkedHashMap,支持按照插入順序排序。PriorityQueue優先級隊列,一個基於優先級堆的無界優先級隊列
Q:線程安全體如今哪些方面?JAVA怎麼保證線程安全?鎖在項目中具體怎麼使用?算法
1、線程安全在三個方面體現 1.原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操做,(atomic,synchronized); 2.可見性:一個線程對主內存的修改能夠及時地被其餘線程看到,(synchronized,volatile); 3.有序性:一個線程觀察其餘線程中的指令執行順序,因爲指令重排序,該觀察結果通常雜亂無序,(happens-before原則)。 2、如何保證線程安全 1.保證原子性: 鎖和同步 經常使用的保證Java操做原子性的工具是鎖和同步方法(或者同步代碼塊)。使 用鎖,能夠保證同一時間只有一個線程能拿到鎖,也就保證了同一時間只有一個 線程能執行申請鎖和釋放鎖之間的代碼。與鎖相似的是同步方法或者同步代碼塊。使用非靜態同步方法時,鎖住的是 當前實例;使用靜態同步方法時,鎖住的是該類的Class對象;使用靜態代碼塊 時,鎖住的是synchronized關鍵字後面括號內的對象。不管使用鎖仍是synchronized,本質都是同樣,經過鎖來實現資源的排它 性,從而實際目標代碼段同一時間只會被一個線程執行,進而保證了目標代碼段 的原子性。這是一種以犧牲性能爲代價的方法。 2.保證可見性: Java提供了volatile關鍵字來保證可見性。因爲JMM是基於共享內存實現線 程通訊的,因此會存在緩存一致性的問題。當使用volatile修飾某個變量時,它 會保證對該變量的修改會當即被更新到內存中,而且將其它緩存中對該變量的緩 存設置成無效,所以其它線程須要讀取該值時必須從主內存中讀取,從而獲得最 新的值。 3.保證有序性: 編譯器和處理器對指令進行從新排序時,會保證從新排序後的執行結果和代 碼順序執行的結果一致,因此從新排序過程並不會影響單線程程序的執行,卻可 能影響多線程程序併發執行的正確性。Java中可經過volatile在必定程序上保證順序性,另外還能夠經過 synchronized和鎖來保證順序性。synchronized和鎖保證順序性的原理和保證原子性同樣,都是經過保證同 一時間只會有一個線程執行目標代碼段來實現的。除了從應用層面保證目標代碼段執行的順序性外,JVM還經過被稱爲 happens-before原則隱式地保證順序性。兩個操做的執行順序只要能夠經過 happens-before推導出來,則JVM會保證其順序性,反之JVM對其順序性不做 任何保證,可對其進行任意必要的從新排序以獲取高效率。 4.其它保證線程安全的方法: (1)儘量避免引發非線程安全的條件——共享變量。若是能從設計上避免 共享變量的使用,便可避免非線程安全的發生,也就無須經過鎖或者 synchronized以及volatile解決原子性、可見性和順序性的問題。 (2)不可變對象 可使用final修飾的對象保證線程安全,因爲final修飾的引用型變量(除String外)不 可變是指引用不可變,但其指向的對象是可變的,因此此類必須安全發佈,即不能對外提供 能夠修改final對象的接口。
Q:JAVA怎麼避免死鎖?數據庫
參考:http://ifeve.com/deadlock-prevention/ 致使死鎖的緣由: 1、加鎖順序 當多個線程須要相同的一些鎖,可是按照不一樣的順序加鎖,死鎖就很容易發生。若是能確保全部的線程都是按照相同的順序得到鎖,那麼死鎖就不會發生。 2、加鎖時限 另一個能夠避免死鎖的方法是在嘗試獲取鎖的時候加一個超時時間,這也就意味着在 嘗試獲取鎖的過程當中若超過了這個時限該線程則放棄對該鎖請求。若一個線程沒有在給定的 時限內成功得到全部須要的鎖,則會進行回退並釋放全部已經得到的鎖,而後等待一段隨機 的時間再重試。這段隨機的等待時間讓其它線程有機會嘗試獲取相同的這些鎖,而且讓該應 用在沒有得到鎖的時候能夠繼續運行(譯者注:加鎖超時後能夠先繼續運行乾點其它事情, 再回頭來重複以前加鎖的邏輯)。 3、死鎖檢測 死鎖檢測是一個更好的死鎖預防機制,它主要是針對那些不可能實現按序加鎖而且鎖超時也不可行的場景。每當一個線程得到了鎖,會在線程和鎖相關的數據結構中(map、graph等等)將其記 下。除此以外,每當有線程請求鎖,也須要記錄在這個數據結構中。當一個線程請求鎖失敗時,這個線程能夠遍歷鎖的關係圖看看是否有死鎖發生。那麼當檢測出死鎖時,這些線程該作些什麼呢?一個可行的作法是釋放全部鎖,回退,而且等待一段隨機的時間後重試。這個和簡單的 加鎖超時相似,不同的是隻有死鎖已經發生了纔回退,而不會是由於加鎖的請求超時了。雖然有回退和等待,可是若是有大量的線程競爭同一批鎖,它們仍是會重複地死鎖(編者 注:緣由同超時相似,不能從根本上減輕競爭)。一個更好的方案是給這些線程設置優先級,讓一個(或幾個)線程回退,剩下的線程就 像沒發生死鎖同樣繼續保持着它們須要的鎖。若是賦予這些線程的優先級是固定不變的,同 一批線程老是會擁有更高的優先級。爲避免這個問題,能夠在死鎖發生的時候設置隨機的優先級。 順便複習一下操做系統死鎖: 死鎖預防:限制申請方式; 互斥:原來獨佔的資源變得共享,可能會形成程序不肯定性。 佔用並等待:必須保證當一個進程請求一個資源的時候,它不持有任何其餘資源。(要麼所有拿到,要麼一點也不佔有)它開始執行以前須要進程請求並分配其全部的資源,容許進程請求資源當且僅當進程沒有佔有任何資源的時候資源利用率低,可能發生飢餓無搶佔若是進程佔有某些資源,並請求其餘不能被當即分配的資源,則釋放當前正佔有的資源,被搶佔資源添加到資源列表中,只有當它可以得到舊的資源以及它請求的新的資源,進程能夠獲得執行。 循環等待:對全部資源類型進行排序,並要求每一個進程按照資源的順序進行申請 死鎖避免:銀行家算法,若是發現分配了資源以後就可能死鎖,就不分配資源了。 死鎖檢測:容許進入死鎖狀態,主要是經過檢測算法看看是否產生了死鎖,而後讓相應線程進行回滾。 死鎖恢復:殺死全部進程,或者根據優先級殺死部分進程,從而解除死鎖。
Q:ThreadLocal具體怎麼使用?使用在什麼場景?編程
參考: (1)https://www.cnblogs.com/ldq2016/p/9041856.html (2)https://www.jianshu.com/p/98b68c97df9b 當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供 獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其 它線程所對應的副本。從線程的角度看,目標變量就象是線程的本地變量,這也是類名 中「Local」所要表達的意思。ThreadLocal是如何作到爲每個線程維護變量的副本的呢?其實實現的思 路很簡單:在ThreadLocal類中有一個Map,用於存儲每個線程的變量副本, Map中元素的鍵爲線程對象,而值對應線程的變量副本。ThreadLocal則從另外一個角度來解決多線程的併發訪問。ThreadLocal會爲每 一個線程提供一個獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。因 爲每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,能夠把不安全的變量封裝進ThreadLocal。我的理解:每個ThreadLocal內部有一個靜態內部 類:ThreadLocalMap,Map裏面存儲線程本地線程對象(key)和線程的變量副 本(value)可是,Thread內部的Map是由ThreadLocal維護的,由 ThreadLocal負責向map獲取和設置線程的變量值。因此對於不一樣的線程,每次 獲取副本值時,別的線程並不能獲取到當前線程的副本值,造成了副本的隔離, 互不干擾。 使用場景:還記得Hibernate的session獲取場景嗎? private static final ThreadLocal<Session> threadLocal = new ThreadLocal<S ession>(); 23 //獲取Session 4 public static Session getCurrentSession(){ 5 Session session = threadLocal.get(); 6 //判斷Session是否爲空,若是爲空,將建立一個session,並設置到本地線程變量中 7 try { 8 if(session ==null&&!session.isOpen()){ 9 if(sessionFactory==null){ 10 rbuildSessionFactory(); // 建立Hibernate的SessionFactory 11 }else{ 12 session = sessionFactory.openSession(); 13 } 14 } 15 threadLocal.set(session); 16 } catch (Exception e) { 17 // TODO: handle exception18 } 19 20 return session; 21 } 爲何每一個線程訪問數據庫都應當是一個獨立的Session會話?若是多個線 程共享同一個Session會話,有可能其餘線程關閉鏈接了,當前線程再執行提交 時就會出現會話已關閉的異常,致使系統異常。此方式能避免線程爭搶 Session,提升併發下的安全性。使用ThreadLocal的典型場景正如上面的數據庫鏈接管理,線程會話管理等 場景,只適用於獨立變量副本的狀況,若是變量爲全局共享的,則不適用在高並 發下使用。本身使用的一個場景: @Component public class HostHolder { private static ThreadLocal<User> users = new ThreadLocal<User>(); public User getUser() { return users.get(); } public void setUser(User user) { users.set(user); } public void clear() { 11 users.remove();; 12 } 13 } 主要是用來判斷當前用戶是否登陸。即在某些頁面好比發帖等頁面須要判斷 當前用戶是否登陸,若沒有登陸則須要跳轉到登陸頁面。 總結 每一個ThreadLocal只能保存一個變量副本,若是想要上線一個線程能 夠保存多個副本以上,就須要建立多個ThreadLocal。ThreadLocal內部的ThreadLocalMap鍵爲弱引用,會有內存泄漏的 風險。適用於無狀態,副本變量獨立後不影響業務邏輯的高併發場景。若是 若是業務邏輯強依賴於副本變量,則不適合用ThreadLocal解決,須要另尋解決方案。
Q:瞭解反射嗎?怎麼用?用在哪裏?設計模式
Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬 性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它 的屬性。總結說:反射就是把java類中的各類成分映射成一個個的Java對象,並 且能夠進行操做。Java反射的原理:java類的執行須要經歷如下過程:編譯:.java文件編譯後生成.class字節碼文件 加載:類加載器負責根據一個類的全限定名來讀取此類的二進制字節流到 JVM內部,並存儲在運行時內存區的方法區,而後將其轉換爲一個與目標類型對 應的java.lang.Class對象實例 連接 驗證:格式(class文件規範) 語義(final類是否有子類) 操做 準備:靜態變量賦初值和內存空間,final修飾的內存空間直接賦 原值,此處不是用戶指定的初值。解析:符號引用轉化爲直接引用,分配地址 初始化:根據程序員經過程序指定的主觀計劃去初始化類變量和其餘資源, 或者能夠從另外一個角度來表達:初始化階段是執行類構造器<clinit>()方法的過 程。執行靜態方法代碼塊爲靜態變量賦值。Java的反射就是利用上面第二步加載到jvm中的.class文件來進行操做 的。.class文件中包含java類的全部信息,當你不知道某個類具體信息時,能夠 使用反射獲取class,而後進行各類操做。 怎麼用: 1、經過class.forName(),加載某個類。 2、在運行時構造任意一個類的對象。1 Class cls = Class.forName("com.jdk"); 2 jdk jdkobj = cls.newInstance(); 3、在運行時判斷任意一個類所具備的成員變量和方法。 1 Class cls = Class.forName("com.jdk"); 2 Methods methods[]= cls.getDecliedMethod(); 3 Fields fields[] = cls.getDeclieredFields(); 使用場景: Class.forName();數據庫註冊驅動的時候。編譯器智能提示該類有哪些方法可供調用。AOP動態代理。經典的就是在xml文件或者properties裏面寫好了配置,而後在Java類裏面解析 xml或properties裏面的內容,獲得一個字符串,而後用反射機制,根據這個字 符串得到某個類的Class實例,這樣就能夠動態配置一些東西,不用每一次都要 在代碼裏面去new或者作其餘的事情,之後要改的話直接改配置文件,代碼維護 起來就很方便了,同時有時候要適應某些需求,Java類裏面不必定能直接調用另 外的方法,這時候也能夠經過反射機制來實現。註解(Annontation)是Java5引入的一種代碼輔助工具,它的核心做用是對類、方法、變 量、參數和包進行標註,經過反射來訪問這些標註信息,以此在運行時改變所註解對象的行爲。
Q:動態代理爲何使用反射而不使用繼承?數組
這個問題有坑啊,動態代理能夠用繼承和反射均可以實現。 JDK動態代理:利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用 InvokeHandler來處理。 CGlib動態代理:利用ASM(開源的Java字節碼編輯庫,操做字節碼)開源包,將代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。 區別:JDK代理只能對實現接口的類生成代理;CGlib是針對類實現代理,對指定的類生成 一個子類,並覆蓋其中的方法,這種經過繼承類的實現方式,不能代理final修飾的類。 總結: 1.JDK代理使用的是反射機制實現aop的動態代理,CGLIB代理使用字節碼處理框架asm, 經過修改字節碼生成子類。因此jdk動態代理的方式建立代理對象效率較高,執行效率較 低,cglib建立效率較低,執行效率高; 2.JDK動態代理機制是委託機制,具體說動態實現接口類,在動態生成的實現類裏面委託 hanlder去調用原始實現類方法,CGLIB則使用的繼承機制,具體說被代理類和代理類是繼 承關係,因此代理類是能夠賦值給被代理類的,若是被代理類有接口,那麼代理類也能夠賦值給接口。 參考:https://blog.csdn.net/lz1170063911/article/details/79835248 JDK的動態代理(依賴於接口) 1. 在Java的動態代理機制中,有兩個重要的類或接口,一個是InvocationHandler接 口,另外一個是Proxy類。 2. InvocationHandler接口是給動態代理類實現的,負責處理被代理對象的操做。 3. Proxy類是用來建立動態代理類實例對象的,只有獲得這個對象,才能調用須要代 理的方法。 4. 動態代理的代理類是在靜態代理類上進行修改,將動態代理類實現 InvocationHandler接口,重寫Invoke方法,Invoke方法經過傳入的被代理類方法和 參數來執行。 JDK動態代理和Cglib動態代理的區別: 1. JDK動態代理是實現了被代理對象的接口,Cglib是繼承了被代理對象。 2. Cglib由於是繼承機制,因此沒法代理被final修飾的方法。 3. JDK和Cglib都是在運行期間生產字節碼,JDK是直接寫class字節碼,Cglib使用 ASM框架寫class字節碼;cglib代理實現更復雜,生成代理類比JDK效率低。 4. JDK調用代理方法,是經過反射實現機制調用,cglib是經過Fashclass機制直接調 用方法,效率更高。Fastcalss機制:爲代理類和被代理類個生成一個class,這個class會爲代理類或被代理類的方法分配一個 index。這個index當作一個入參,Fashclass就能夠直接定位要調用的方法,並直接進行調用。這樣省去了反射調用,因此效率高。
Q:設計模式中簡單工廠和抽象工廠的區別?緩存
簡單工廠模式:
雖然某種程度不符合開閉原則,可是實際實用不少。工廠方法模式:不修改已有類的狀況下,經過增長新的工廠類實現擴展。可是容易致使工廠 類氾濫。
抽象工廠模式:
不能夠增長產品,能夠增長產品族。增長新產品須要修改不少地方。
Q:HTTP狀態碼?3XX和4XX區別,404是啥?安全
3XX重定向:客戶端須要作些額外工做才能獲得所須要的資源。它們一般用於 GET請求。他們一般告訴客戶端須要向另外一個URI發送GET請求,才能獲得所需 的表示。那個URI就包含在Location響應報頭裏。 301:永久重定向,好比更換了新的IP,服務端就就告訴客戶端之後你訪問個人 新IP 302:暫時重定向 4XX客戶端錯誤:這些響應代碼代表客戶端出現錯誤。不是認證信息有問題,就 是表示格式或HTTP庫自己有問題。客戶端須要自行改正。 400: 包含語法錯誤,沒法被服務器解析 403: 服務器已經接收請求,可是拒絕執行 404: 請求失敗,請求所但願獲得的資源未在服務器上發現 5XX服務端錯誤:這些響應代碼代表服務器端出現錯誤。通常來講,這些代碼意味着服務器處於不能執行客戶端請求的狀態,此時客戶端應稍後重試。 500: 服務器內部錯誤,沒法處理請求
Q:數據庫爲何創建索引?如何編程實現數據庫搶鎖?
這是由於,建立索引能夠大大提升系統的性能。 優勢: 第一,經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。 第二,能夠大大加快數據的檢索速度,這也是建立索引的最主要的緣由。 第三,能夠加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別 有意義。 第四,在使用分組和排序子句進行數據檢索時,一樣能夠顯著減小查詢中分組 和排序的時間。 第五,經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。也許會有人要問:增長索引有如此多的優勢,爲何不對錶中的每個列建立一個索引呢?這種想法當然有其合理性,然而也有其片面性。雖然,索引有許多優勢,可是,爲表中的每個列都增長索引,是很是不明智的。這是由於,增長索引也有許多不利的一個方面, 缺點: 第一,建立索引和維護索引要耗費時間,這種時間隨着數據量的增長而增長。 第二,索引須要佔物理空間,除了數據表佔數據空間以外,每個索引還要佔一 定的物理空間,若是要創建聚簇索引,那麼須要的空間就會更大。 第三,當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,這 樣就下降了數據的維護速度。 哪些適合創建索引哪些不適合? 適合的:常常須要搜索的列上,常常須要範圍查詢的,主鍵等。 不適合的:常常不用來查詢的,大字段的好比text段等。 如何編程實現數據庫搶鎖? 行鎖的釋放是須要事務提交以後自動釋放 共享鎖:SELECT ... LOCK IN SHARE MODE; 解釋:MySQL會對查詢結果集中每行都添加共享鎖。鎖申請前提:當前沒有線程對該結果集中的任何行使用排他鎖,不然申請會阻塞。 排他鎖:SELECT ... FOR UPDATE; MySQL會對查詢結果集中每行都添加排他鎖,在事物操做中,任何對記錄的更新與刪除操 做會自動加上排他鎖。鎖申請前提:當前沒有線程對該結果集中的任何行使用排他鎖或共享鎖,不然申請會阻塞。 表鎖:lock tables ... read/write 釋放表鎖:對應線程執行 unlock tables 便可 參考:https://www.cnblogs.com/zqyanywn/p/5922656.html
Q:Redis數據結構基礎,項目中具體怎麼用的?若是把數據都存儲在Redis中會不會丟失數據?Redis分佈式鎖瞭解嗎?
單機Redis會丟失,集羣的話不會。 分佈式鎖:請參考https://www.cnblogs.com/seesun2012/p/9214653.html
Q:大數據問題:硬盤裏一個50G大小的文件和另外一個100G文件,裏面存儲着不一樣的名 字,如何在一個內存很小的電腦上實現兩個文件的交集運算?
方法一: 分桶+組內Hash索引或者組內使用位圖 O(n) 使用哈希切分的方法,將一個大文件裏的數據使用一個哈希函數進行切分爲 許多小的文件,這樣相同的數據必定會進入同一個文件當中去,並進行文件編 號。對另一個文件也是用相同的哈希函數進行切分爲相同數目的小文件,這樣 咱們只須要將相同編號裏的文件進行比較。這樣其時間複雜度就會下降爲 O(n)。相同的文件查找時能夠先對一個文件創建hash索引(桶+鏈表),而後對另 一個文件依次按照索引進行查找。若hash值相同在進行進一步比較便可。 方法二: 位圖 O(n) 這有個前提是文件中必須存儲的是數字。那麼根據位圖,咱們能夠將第一個 文件中全部數據映射到位圖中去。而後再不斷導入第二個文件,若是發現某個數 字已經存儲在位圖中,就說明這是兩個文件的交集。 方法三: 近似解-布隆過濾器 O(n) 將A文件每一個數據通過多個Hash函數映射到一個位圖上,而後第二個文件同 樣的作法,若是所有命中,說明相同。不然說明不存在。可是這個有必定的錯誤 率。方法四:多路歸併排序 Onlog(n)+O(n) 先將文件劃分爲不少等量的小文件。而後對每一個小文件導入內存進行內部排 序。這樣就有了不少有序的小文件。而後對不少有序的小文件進行多路歸併排序,而後不斷寫入大文件便可。(Onlog(n))最終就獲得了一個有序的大文件。最後對兩個有序的大文件進行 查找相同的值便可(O(n))。
Q:大數據問題:BBS上不少帖子,發帖最多的人被刪除掉了,剩下3我的的帖子數目均超 過1/4,如何找出這三我的?
1.首先統計出數據出現的次數。這個能夠採用hash。而後用最小堆即可以求出 出現次數最高的N個數。 2.若是這個文件很是大。能夠採起分治法。假設此文件大小爲n。而內存中能處 理的爲k。則分m次讀取。m=n/k+1。每次讀取k大小。而後採用1的方法能夠 得出k大小中的N個頻率最高的。m次後獲得m個堆。將此m個堆合併。即可得 到頻率最高的N個。 3.堆的維護的代價爲lgN;我想仍是比較快的。思路大體以下:每個帖子和用戶是一一對應的關係。先根據HashMap<用戶ID,帖子數 目>遍歷每個帖子,獲得每一個用戶的發帖子的數目。假設此時用戶數目爲m, 即Map的大小。而後進行堆排序,因爲咱們只須要排名前三的人,全部只需維 護一個大小爲3的小頂堆便可。時間複雜度:遍歷全部帖子時間複雜度爲O(n), 堆排序時間複雜度爲O(mlog3)。因爲三我的的帖子數目均超過了總帖子的 1/4。能夠認爲m的數目遠遠小於n。最終複雜度約等於O(n)。