題目整理
Java基礎進階階段
基礎概念類
1.JDK1.8新特性?
2.面向對象和面向過程的區(qū)別?
3.什么是值傳遞和引用傳遞?
4.什么是不可變對象?
5.講講類的實例化順序?
6.java 創(chuàng)建對象的幾種方式
7.Java訪問修飾符的作用域
8.switch中能否使用string作為參數?
9.switch中能否作用在byte,long上?
10.什么是自動拆裝箱?
11.如何正確的退出多層嵌套循環(huán)?
繼承
1.Java支持多繼承么?
2.父類的靜態(tài)方法能否被子類重寫?
3.繼承的好處和壞處?
接口抽象類
1.接口的意義?
2.抽象類的意義?
3.抽 象 的 (abstract) 方 法 是 否 可 同 時 是 靜 態(tài) 的 (static), 是 否 可 同 時 是 本 地 方 法(native)?
4.抽象類和接口區(qū)別?
5.Java中接口可不可以繼承一般類,為什么?
6.重載與重寫區(qū)別?
7.final有哪些用法?
多態(tài)
1.多態(tài)的好處和弊端?
2.代碼中如何實現多態(tài)?
3.Java 中實現多態(tài)的機制是什么?
內部類Lambda
1.內部類的作用?
2.一個java文件內部可以有類?(非內部類)
3.Lambda的使用前提是什么?
4.Lambda與匿名內部類區(qū)別?
static關鍵字
1.是否可以在static環(huán)境中訪問非static變量?
2.static都有哪些用法?
3.靜態(tài)變量和實例變量的區(qū)別?
4.static特點?
數據類型
1.String s1=”ab”, String s2=”a”+”b”, String s3=”a”, String s4=”b”, s5=s3+s4請問s5==s2返回什么?
2.3*0.1==0.3返回值是什么?
3.基本數據類型的默認值?基本數據類型所占的字節(jié)以及封裝他們的類?
4.String屬于那個類,以及常用的方法?
5.String, StringBuffer和StringBuilder區(qū)別?
異常類
1.error和exception有什么區(qū)別?
2.運行時異常和一般異常有何不同?
3.Java中異常處理機制的原理?
4.你平時在項目中是怎樣對異常進行處理的?
5.throw和throws有什么區(qū)別?
6.異常處理的時候,finally代碼塊的重要性是什么?
7.請列出 5 個運行時異常?
8.try catch finally,try里有return,finally還執(zhí)行么?
集合
1、List、Map、Set三個接口,存取元素時,各有什么特點?
2、ArrayList和LinkedList的底層實現原理?他們?yōu)槭裁淳€程不安全?在多線程并發(fā)操作下,我們應該用什么替代?
3、HashMap和HashTable有什么區(qū)別?其底層實現是什么?CurrentHashMap的鎖機制又是如何?如果想將一個Map變?yōu)橛行虻?該如何實現?
4.什么是迭代器(Iterator)?
5.Arraylist 與 LinkedList 區(qū)別?
6.Arraylist 與 LinkedList 應用場景?
7.Collection 和 Collections的區(qū)別?
8.為何Map接口不繼承Collection接口?
9.當兩個對象的hashcode相同會發(fā)生什么?
10.HashMap和Hashtable有什么區(qū)別?
11.List 和 Set 區(qū)別?
12.Set和List對比?
13.當兩個對象的hashcode相同會發(fā)生什么?
14.如果兩個鍵的hashcode相同,你如何獲取值對象?
15.有沒有可能兩個不相等的對象有相同的hashcode?
16.HashMap、LinkedHashMap、TreeMap的區(qū)別?
17.HashMap、LinkedHashMap、ConcurrentHashMap、ArrayList、LinkedList的底層實現。
18.==和 equals hashCode 的區(qū)別?
19.自然排序Comparble和比較器排序Comparator的異同點?
泛型
1.為什么使用泛型?
2.泛型用在什么地方?
3.如何使用泛型類?
樹
1.什么是二叉樹?
2.什么是二叉查找樹?
3.什么是平衡二叉樹?
序列化
1.什么是 Java 序列化?
2.如何實現 Java 序列化?
3.Java 序列話中,如果有些字段不想進行序列化怎么辦?
4.對象操作流是字符流還是字節(jié)流?
5.如何在讀寫文件時指定字符集?
6.字符緩沖流特有方法?
7.為什么使用對象流?
多線程
1.什么是線程?
2.線程和進程有什么區(qū)別?
3.如何在Java中實現線程?
4.用Runnable還是Thread?
5.Thread 類中的start() 和 run() 方法有什么區(qū)別?
6.Java中Runnable和Callable有什么不同?
7.Java內存模型是什么?
8.Java中的volatile 變量是什么?
9.什么是線程安全?Vector是一個線程安全類嗎?
10.Java中如何停止一個線程?
11.Java中notify 和 notifyAll有什么區(qū)別?
12. 什么是線程池? 為什么要使用它?
13.如何寫代碼來解決生產者消費者問題?
14.Java多線程中的死鎖?
15.Java中synchronized 和 ReentrantLock 有什么不同?
16.詳談Synchronized?
17.在Java中Lock接口與synchronized塊的區(qū)別是什么?
18.synchronized 的原理是什么?有什么不足?
19.關于成員變量和局部變量?
20. 如果你提交任務時,線程池隊列已滿。會時發(fā)會生什么?
21.volatile關鍵字的作用是?
22.守護線程和非守護線程有什么區(qū)別?
23.線程的生命周期?
24.wait和sleep,notify()鎖方面區(qū)別?
25.什么情況下會出現線程安全問題?
26.Java中規(guī)定了線程有哪幾種狀態(tài)?
27.什么是原子性?
28.Java中哪些操作是原子操作?
29.什么是CAS算法?
30.synchronized和CAS的區(qū)別?
31.并發(fā)容器Hashtable和ConcurrentHashMap特點?
反射
1.Java反射機制的作用?
2.什么是反射機制?
3.哪里用到反射機制?
4.反射機制的優(yōu)缺點?
5.反射中,Class.forName 和 ClassLoader 區(qū)別
6.什么是雙親委派模型?
7.為什么要有雙親委派模型?
8.怎么利用反射使用私有成員?
網絡通信
1.什么是三次握手?
2.什么是四次揮手?
3.TCP通信注意事項?
web階段
jsp相關
1.jsp內置對象和EL內置對象的區(qū)別與聯系?
2.說一下 jsp 的 4 種作用域?
3.ServletContext 與application的異同?
4.jsp 有哪些內置對象?作用分別是什么?
概念相關
1.post和get區(qū)別?
2.簡單闡述相對路徑和絕對路徑?
3.Cookie和session的區(qū)別?
4.servlet四大域對象的區(qū)別?
5.什么是活化與鈍化?
6.EL內置對象有哪些?
7.如果有大量的網站訪問量。那么會產生很多的session,該怎么解決?
8.頁面?zhèn)鬟f對象的方法?
9.session 和 application的區(qū)別?
servlet相關
1.解釋一下什么是servlet?
2.servlet的生命周期?
3.servlet生命周期方法有哪些?
4.servlet過濾器的作用?
5.servlet監(jiān)聽器的作用?
6.web.xml中組件的加載順序?
7.如何確保servlet在應用啟動之后被加載到內存?
8.HttpServlet為什么聲明為抽象類?
9.redirect(重定向)和forward(請求轉發(fā))區(qū)別?
10.sevlet中的屬性域有哪些?
11.Servlet是否線程安全?
12.如何創(chuàng)建線程安全的servlet?(SingleThreadModel方法不算)
13.是否有必要重寫service方法?
14.servlet包裝類有什么用?
15.在servlet中能否產生類似死鎖情況?
16.Servlet API中forward()與redirect()的區(qū)別?
17.ServletContext對象和ServletConfig對象的區(qū)別?
18.PrintWriter和ServletOutPutStream類有什么區(qū)別?
19.在一個servlet能否同時獲取PrintWriter和ServletOutputStream對象?
20.Request對象的主要方法有哪些?
21.jsp和servlet的異同點以及聯系是什么?
數據庫階段
索引相關
1.什么是索引?
2.索引是個什么樣的數據結構呢?
3.在建立索引的時候,都有哪些需要考慮的因素呢?
4.關心過業(yè)務系統里面的sql耗時嗎?統計過慢查詢嗎?對慢查詢都怎么優(yōu)化過?
5.區(qū)別B樹,B-,B+,B*?
6.MySQL優(yōu)化策略?
7.key和index的區(qū)別?
8.怎么驗證 mysql 的索引是否滿足需求?
事務相關
1.ACID是什么?可以詳細說一下嗎?
2.同時有多個事務在進行會怎么樣呢?
3.怎么解決這些問題呢?MySQL的事務隔離級別了解嗎?
4.Innodb使用的是哪種隔離級別呢?
5.對MySQL的鎖了解嗎?
6.MySQL都有哪些鎖呢?像上面那樣子進行鎖定豈不是有點阻礙并發(fā)效率了?
7.行級鎖定的優(yōu)點缺點?
8.說一下 mysql 的行鎖和表鎖?
表設計相關
1. 為什么要盡量設定一個主鍵?
2.主鍵使用自增ID還是UUID?
3. 字段為什么要求定義為not null?
4.varchar(10)和int(10)代表什么含義?
5.建表策略?
存儲引擎相關
1. MySQL支持哪些存儲引擎?
2.InnoDB和MyISAM有什么區(qū)別?
3.什么是存儲過程?有哪些優(yōu)缺點?
4.說一說三個范式?
答案整理
Java基礎進階階段
基礎概念類
1.JDK1.8新特性?
提供lambda表達式極大地減少了代碼的冗余; 在接口中可以使用default和static關鍵字來修飾接口中的普通方法; 提供新的API LocalDate | LocalTime | LocalDateTime
- Java.util.Date和SimpleDateFormatter線程上都不安全,而LocalDate和LocalTime和 String一樣都是不可改變類,線程上比較安全,還不能修改;
- Java.util.Date月份從0開始,12月是11,而java.time.LocalDate月份和星期都改成了 enum, 就不可能出錯了;
2.面向對象和面向過程的區(qū)別?
面向過程
- 優(yōu)點:性能比面向對象高,因為類調用時需要實例化,開銷比較大,比較消耗資源。比如,單片機、嵌入式開發(fā)、Linux/Unix 等一般采用面向過程開發(fā),性能是最重要的因素。
- 缺點:沒有面向對象易維護、易復用、易擴展。
面向對象
- 優(yōu)點:易維護、易復用、易擴展,由于面向對象有封裝、繼承、多態(tài)性的特性,可以設計出低耦合的系統,使系統更加靈活、更加易于維護。
- 缺點:性能比面向過程低。
3.什么是值傳遞和引用傳遞?
- 值傳遞,是對基本型變量而言的,傳遞的是該變量的一個副本,改變副本不影響原變量。
- 引用傳遞,一般是對于對象型變量而言的,傳遞的是該對象地址的一個副本,并不是原對象本身。
一般認為,Java 內的傳遞都是值傳遞,Java 中實例對象的傳遞是引用傳遞。
4.什么是不可變對象
- 不可變對象指對象一旦被創(chuàng)建,狀態(tài)就不能再改變。任何修改都會創(chuàng)建一個新的對象,如 String、Integer及其它包裝類。
5.講講類的實例化順序?
初始化順序如下:
- 父類靜態(tài)變量
- 父類靜態(tài)代碼塊
- 子類靜態(tài)變量、
- 子類靜態(tài)代碼塊
- 父類非靜態(tài)變量(父類實例成員變量)
- 父類構造函數
- 子類非靜態(tài)變量(子類實例成員變量)
- 子類構造函數
6.java 創(chuàng)建對象的幾種方式
- 采用new
- 通過反射
- 采用clone
- 通過序列化機制
前2者都需要顯式地調用構造方法。造成耦合性最高的恰好是第一種,因此你發(fā)現無論什么框架,只要涉及到解耦必先減少new的使用
7.Java訪問修飾符的作用域
作用域 當前類 同包 子類 其它
- public Y Y Y Y
- protected Y Y Y N
- default Y Y N N
- private Y N N N
8.switch中能否使用string作為參數?
- 在jdk1.7之前,switch只能支持byte,short,char,int或者其他對應的封裝類以及Enum類型.jdk1.7之后開始支持String
9.switch中能否作用在byte,long上?
10.什么是自動拆裝箱?
- 自動裝箱和拆箱,就是基本類型和引用類型之間的轉換。
- 把基本數據類型轉換成包裝類的過程就是打包裝,為裝箱。
- 把包裝類轉換成基本數據類型的過程就是拆包裝,為拆箱。
11.如何正確的退出多層嵌套循環(huán)?
使用標號和break;
繼承
-
Java支持多繼承么?
- Java類中不支持多繼承,但是可以多實現,所以接口的擴展性比較好,實際開發(fā)中盡量避免繼承的使用
-
父類的靜態(tài)方法能否被子類重寫
- 不能。重寫只適用于實例方法,不能用于靜態(tài)方法,而子類當中含有和父類相同簽名的靜態(tài)方法,我們一般稱之為隱藏。
-
繼承的好處和壞處
- 好處:
- 子類能自動繼承父類的對象 2、創(chuàng)建子類的對象時,無須創(chuàng)建父類的對象
- 壞處:
- 破壞封裝,子類與父類之間緊密耦合,子類依賴于父類的實現,子類缺乏獨立性。
- 支持擴展,但是往往以增強系統結構的復雜度為代價
- 不支持動態(tài)繼承。在運行時,子類無法選擇不同的父類
- 子類不能改變父類的接口
接口抽象類
1.接口的意義
2.抽象類的意義
- 為其他子類提供一個公共的類型
- 封裝子類中重復定義的內容
- 定義抽象方法,子類雖然有不同的實現,但是定義時一致的
3.抽 象 的 (abstract) 方 法 是 否 可 同 時 是 靜 態(tài) 的 (static), 是 否 可 同 時 是 本 地 方 法(native)
- abstract關鍵字不能同時與static或private或final同時修飾一個方法;
4.抽象類和接口區(qū)別?
-
語法區(qū)別:
-
抽象類可以有構造方法,接口不能有構造方法
-
抽象類中可以有普通成員變量,接口中沒有普通成員變量;
-
抽象類中可以有非抽象的方法,接口中的方法都必須是抽象的;
-
抽象類中的方法可以是public,protected類型,接口中的方法只能是public類型的,切 默認為public abstract類型;
-
抽象類中可以有靜態(tài)方法,接口中不能有靜態(tài)方法;
-
抽象類中的靜態(tài)變量訪問類型可以是任意的,但接口中的靜態(tài)變量只能是public static final 類型。
-
.一個類可以實現多個接口,但一個類只能繼承一個抽象類;
-
應用區(qū)別:
5.Java中接口可不可以繼承一般類,為什么?
不可以因為接口中只能出現3種成員:
-
公共的靜態(tài)常量
-
公共的抽象方法
-
靜態(tài)內部類
而一個類中,就算什么都不寫,也必須帶一個構造方法,在extends時就會被子類繼承,如果是接口也會 繼承這個構造方法,很明顯構造方法不在上面三項之列 而如果類中有一般的方法和成員變量,也會被子類全部繼承,這些更不能出現在接口中了,所以接口是絕 對不可能繼承一個類的
6.重載與重寫區(qū)別
override(重寫)
- 方法名、參數、返回值相同。
- 子類方法不能縮小父類方法的訪問權限。
- 子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。
- 存在于父類和子類之間。
- 被final修飾的方法,不能被重寫。
overload(重載)
- 參數類型、個數、順序至少有一個不相同。
- 不能重載只有返回值不同的方法名。
- 存在于父類和子類、同類中。
7.final有哪些用法?
- 被final修飾的類不可以被繼承
- 被final修飾的方法不可以被重寫
- 被final修飾的變量不可以被改變。如果修飾引用,那么表示引用不可變,引用指向的內容可變。
注:修飾變量, final 數據類型 變量名=數據值; 如果該變量是基本數據類型,則值不能修改,如果該變量是引用數據類型,則地址值不能改(既只能new一次);
- 被final修飾的方法,JVM會嘗試將其內聯,以提高運行效率
- 被final修飾的常量,在編譯階段會存入常量池中。
回答出編譯器對final域要遵守的兩個重排序規(guī)則更好:
- 在構造函數內對一個final域的寫入,與隨后把這個被構造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序。
- 初次讀一個包含final域的對象的引用,與隨后初次讀這個final域,這兩個操作之間不能重排序。
多態(tài)
1.多態(tài)的好處和弊端
許不同類對象對同一消息做出響應,即同一消息可以根據發(fā)送對象的不同而采用多種不同的行為方式(發(fā)送消息就是函數調用)。即父類型的引用指向子類型的對象。
- 優(yōu)點:
- 可替換性:多態(tài)對已存在代碼具有可替換性
- 可擴充性:增加新的子類不影響已經存在的類結構
- 更加靈活
- 弊端:
2.代碼中如何實現多態(tài)
實現多態(tài)主要有以下三種方式:
3.Java 中實現多態(tài)的機制是什么?
內部類Lambda
1.內部類的作用?
- 內部類可以有多個實例,每個實例都有自己的狀態(tài)信息,并且與其他外圍對象的信息相互獨立.在單個外圍類當中,可以讓多個內部類以不同的方式實現同一接口,或者繼承同一個類.創(chuàng)建內部類對象的時刻不依賴于外部類對象的創(chuàng)建。
- 內部類提供了更好的封裝,除了該外圍類,其他類都不能訪問。
2.一個java文件內部可以有類?(非內部類)
- 只能有一個public公共類,但是可以有多個default修飾的類。
3.Lambda的使用前提是什么?
- 當需要一個接口的實現類對象,且接口中有且僅有一個抽象方法的時候,可以使用lambda完成這個實現類要做的事情;(替代匿名內部類)
4.Lambda與匿名內部類區(qū)別
- lambda表達式編譯后并不會生成.class文件,而匿名內部類編譯后會產生單獨的class文件;
- 匿名內部類可以用在類,抽象類,接口中,而lambda表達式只能用在有且僅有一個抽象方法的接口中;
static關鍵字
1.是否可以在static環(huán)境中訪問非static變量?
- static變量在Java中是屬于類的,它在所有的實例中的值是一樣的。當類被Java虛擬機載入的時候,會對static變量進行初始化。如果你的代碼嘗試不用實例來訪問非static的變量,編譯器會報錯,因為這些變量還沒有被創(chuàng)建出來,還沒有跟任何實例關聯上。
2.static都有哪些用法?
- 被static所修飾的變量/方法都屬于類的靜態(tài)資源,類實例所共享.
- static也用于靜態(tài)塊,多用于初始化操作.
- 此外static也多用于修飾內部類,此時稱之為靜態(tài)內部類.
3.靜態(tài)變量和實例變量的區(qū)別?
- 靜態(tài)變量存儲在方法區(qū),屬于類所有。實例變量存儲在堆當中,其引用存在當前線程棧。
4.static特點
- 如果修飾構造代碼塊,僅在類第一次加載的時候,執(zhí)行一次;
- 如果修飾成員變量,這個變量的值屬于類;可以被所有的對象共享;
- 如果修飾成員方法,在方法中不能使用this,super;
- 靜態(tài)的內容優(yōu)先于對象存在!
數據類型
1.String s1=”ab”, String s2=”a”+”b”, String s3=”a”, String s4=”b”, s5=s3+s4請問s5==s2返回什么?
- 返回false。在編譯過程中,編譯器會將s2直接優(yōu)化為”ab”,會將其放置在常量池當中,s5則是被創(chuàng)建在堆區(qū),相當于s5=new String(“ab”);
2.3*0.1==0.3返回值是什么
3.基本數據類型的默認值?基本數據類型所占的字節(jié)以及封裝他們的類?
4.String屬于那個類,以及常用的方法?
5.String, StringBuffer和StringBuilder區(qū)別
- String的值是不可改變的,這就導致每次對String的操作都會生成新的String對象,不禁效率底下, 而且浪費大量的內存空間;
- StringBuilder是可變類,任何對他指向的字符串的操作都不會產生新的對 象,但單線程不安全;
- StringBuffer底層方法使用了synchronized關鍵字,線程比較安全,但效率 較StringBuilder慢
異常相關
1.error和exception有什么區(qū)別
- error表示系統級的錯誤,是java運行環(huán)境內部錯誤或者硬件問題,不能指望程序來處理這樣的問題,除了退出運行外別無選擇,它是Java虛擬機拋出的。
- exception 表示程序需要捕捉、需要處理的異常,是由與程序設計的不完善而出現的問題,程序必須處理的問題
2.運行時異常和一般異常有何不同
- Java提供了兩類主要的異常:runtimeException和checkedException
- 一般異常(checkedException)主要是指IO異常、SQL異常等。對于這種異常,JVM要求我們必須對其進行cathc處理,所以,面對這種異常,不管我們是否愿意,都是要寫一大堆的catch塊去處理可能出現的異常。
- 運行時異常(runtimeException)我們一般不處理,當出現這類異常的時候程序會由虛擬機接管。比如,我們從來沒有去處理過NullPointerException,而且這個異常還是最常見的異常之一。
- 出現運行時異常的時候,程序會將異常一直向上拋,一直拋到遇到處理代碼,如果沒有catch塊進行處理,到了最上層,如果是多線程就有Thread.run()拋出,如果不是多線程那么就由main.run()拋出。拋出之后,如果是線程,那么該線程也就終止了,如果是主程序,那么該程序也就終止了。
- 其實運行時異常的也是繼承自Exception,也可以用catch塊對其處理,只是我們一般不處理罷了,也就是說,如果不對運行時異常進行catch處理,那么結果不是線程退出就是主程序終止。
- 如果不想終止,那么我們就必須捕獲所有可能出現的運行時異常。如果程序中出現了異常數據,但是它不影響下面的程序執(zhí)行,那么我們就該在catch塊里面將異常數據舍棄,然后記錄日志。如果,它影響到了下面的程序運行,那么還是程序退出比較好些。
3.Java中異常處理機制的原理
Java通過面向對象的方式對異常進行處理,Java把異常按照不同的類型進行分類,并提供了良好的接口。當一個方法出現異常后就會拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常并對異常進行處理。Java的異常處理是通過5個關鍵詞來實現的:try catch throw throws finally。
一般情況下是用try來執(zhí)行一段程序,如果出現異常,系統會拋出(throws),我們可以通過它的類型來捕捉它,或最后由缺省處理器來處理它(finally)。
- try:用來指定一塊預防所有異常的程序
- catch:緊跟在try后面,用來捕獲異常
- throw:用來明確的拋出一個異常
- throws:用來標明一個成員函數可能拋出的各種異常
- finally:確保一段代碼無論發(fā)生什么異常都會被執(zhí)行的一段代碼。
4.你平時在項目中是怎樣對異常進行處理的。
- 盡量避免出現runtimeException 。例如對于可能出現空指針的代碼,帶使用對象之前一定要判斷一下該對象是否為空,必要的時候對runtimeException
也進行try catch處理。
- 進行try catch處理的時候要在catch代碼塊中對異常信息進行記錄,通過調用異常類的相關方法獲取到異常的相關信息,返回到web端,不僅要給用戶良好的用戶體驗,也要能幫助程序員良好的定位異常出現的位置及原因。例如,以前做的一個項目,程序遇到異常頁面會顯示一個圖片告訴用戶哪些操作導致程序出現了什么異常,同時圖片上有一個按鈕用來點擊展示異常的詳細信息給程序員看的。
5.throw和throws有什么區(qū)別?
- throw關鍵字用來在程序中明確的拋出異常,相反,throws語句用來表明方法不能處理的異常。每一個方法都必須要指定哪些異常不能處理,所以方法的調用者才能夠確保處理可能發(fā)生的異常,多個異常是用逗號分隔的。
6.異常處理的時候,finally代碼塊的重要性是什么?
- 無論是否拋出異常,finally代碼塊總是會被執(zhí)行。就算是沒有catch語句同時又拋出異常的情況下,finally代碼塊仍然會被執(zhí)行。最后要說的是,finally代碼塊主要用來釋放資源,比如:I/O緩沖區(qū),數據庫連接。
7.請列出 5 個運行時異常?
- NullPointerException 空指針
- IndexOutOfBoundsException 索引越界
- ClassCastException 類型轉換異常
- ArrayStoreException 當你試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常
- BufferOverflowException 寫入的長度超出了允許的長度
- IllegalArgumentException 方法的參數無效
- NoClassDefFoundException - JAVA運行時系統找不到所引用的類
8.try catch finally,try里有return,finally還執(zhí)行么?**
- finally語句總會執(zhí)行即使try里包含continue,break,return,try塊結束后,finally塊也會執(zhí)行
- 如果try、catch中有return語句,finally中沒有return,那么在finally中修改除包裝類型和靜態(tài)變量、全局變量以外的數據都不會對try、catch中返回的變量有任何的影響(包裝類型、靜態(tài)變量會改變、全局變量)
- 盡量不要在finally中使用return語句,如果使用的話,會忽略try、catch中的返回語句,也會忽略try、catch中的異常,屏蔽了錯誤的發(fā)生。
- finally中避免再次拋出異常,一旦finally中發(fā)生異常,代碼執(zhí)行將會拋出finally中的異常信息,try、catch中的異常將被忽略
集合部分
1、List、Map、Set三個接口,存取元素時,各有什么特點?
- Set集合的add有一個boolean類型的返回值,當集合中沒有某個元素時,則可以成功加入該 元素,返回結果為true;當集合中存在與某個元素equals方法相等 的元素時,則無法加入該元素, 取元素時只能用Iterator接口取得所有元素,在逐一遍歷各個元素;
- List表示有先后順序的集合,調用add()方法,指定當前對象在集合中的存放位置;一個對象可 以被反復存進集合中;每調用一次add()方法,該對象就會被插入集合中一次,其實,并不是把對 象本身存進了集合中,而是在集合中使用一個索引變量指向了該對象,當一個對象被add多次時, 即有多個索引指向了這個對象。List去元素時可以使用Iterator取出所有元素,在逐一遍歷,還可 以使用get(int index)獲取指定下表的元素;
- Map是雙列元素的集合,調用put(key,value),要存儲一對key/value,不能存儲重復的key, 這個是根據eauals來判斷;取元素時用get(key)來獲取key所對 應的value,另外還可以獲取 全部key,全部value
2、ArrayList和LinkedList的底層實現原理?他們?yōu)槭裁淳€程不安全?在多線程并發(fā)操作下,我們應該用什么替代?
- ArrayList底層通過數組實現,ArrayList允許按序號索引元素,而插入元素需要對數組進行移位等內存操作,所以索引快插入較慢;(擴容方式)一旦我們實例化了ArrayList 無參構造函數默認數組長度為10。add方法底層如 果增加的元素超過了10個,那么ArrayList底層會生成一個新的數組,長度為原來數組長度的1.5倍+1,然后將原數組內容復制到新數組中,并且后續(xù)加的內容都會放到新數組中。當新數組無法容納增加元素時,重復該過程;
- LinkedList底層通過雙向鏈表實現,取元素時需要進行前項或后項的遍歷,插入元素時只需要記錄本項的前后 項即可,所以插入快查詢慢;
- ArrayList和LinkedList底層方法都沒有加synchronized關鍵詞,多線程訪問時會出現多個線程先后更改數據造成得到的數據是臟數據;多線程并發(fā)操作下使用Vector來代替,Vector底層也是數組,但底層方法都加synchronized關鍵字使線程安全,效率較ArrayList差;
3、HashMap和HashTable有什么區(qū)別?其底層實現是什么?CurrentHashMap的鎖機制又是如何?如果想將一個Map變?yōu)橛行虻?該如何實現?
- 區(qū)別:
- HashMap沒有實現synchronized線程非安全,HashTable實現了synchronized線程安全;
- HashMap允許key和value為null,而HashTable不允許
- 底層原理:數組+鏈表實現
- ConcurrentHashMap鎖分段技術:HashTable效率低下的原因,是因為所訪問HashTable的線程都必須競爭同一把鎖,那假如容器中有多把鎖,每一把鎖用于鎖住容器中的一部分數據,那么當多線程訪問容器中不同的數據時,線程間就不會存在鎖競爭,從而提高并發(fā)訪問率;ConcurrentHashMap使用的就是鎖分段技術,首先將數據分成一段一段的存儲,然后給每一段數據配一把鎖,當一個線程占用鎖訪問其中一個數據時,其他段的數據也能被其他線程訪問;
- 實現TreeMap
4.什么是迭代器(Iterator)?
- Iterator接口提供了很多對集合元素進行迭代的方法。每一個集合類都包含了可以返回迭代器實例的迭代方法。迭代器可以在迭代的過程中刪除底層集合的元素,但是不可以直接調用集合的remove(Object Obj)刪除,可以通過迭代器的remove()方法刪除
5.Arraylist 與 LinkedList 區(qū)別
- Arraylist:
- 優(yōu)點:ArrayList是實現了基于動態(tài)數組的數據結構,因為地址連續(xù),一旦數據存儲好了,查詢操作效率會比較高(在內存里是連著放的)。
- 缺點:因為地址連續(xù), ArrayList要移動數據,所以插入和刪除操作效率比較低。
- LinkedList:
- 優(yōu)點:LinkedList基于鏈表的數據結構,地址是任意的,所以在開辟內存空間的時候不需要等一個連續(xù)的地址,對于新增和刪除操作add和remove,LinedList比較占優(yōu)勢。LinkedList 適用于要頭尾操作或插入指定位置的場景
- 缺點:因為LinkedList要移動指針,所以查詢操作性能比較低。
6.Arraylist 與 LinkedList 應用場景?
- 當需要對數據進行對此訪問的情況下選用ArrayList,當需要對數據進行多次增加刪除修改時采用LinkedList。
7.Collection 和 Collections的區(qū)別
- Collection是集合類的上級接口,繼承與他的接口主要有Set 和List.Collections是針對集合類的一個幫助類,他提供一系列靜態(tài)方法實現對各種集合的搜索、排序、線程安全化等操作(帶s的基本都是工具類,如Arrays)
8.為何Map接口不繼承Collection接口?
- 盡管Map接口和它的實現也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map繼承Collection毫無意義,反之亦然。
- 如果Map繼承Collection接口,那么元素去哪兒?Map包含key-value對,它提供抽取key或value列表集合的方法,但是它不適合“一組對象”規(guī)范。
10.HashMap和Hashtable有什么區(qū)別?
- HashMap是非線程安全的,HashTable是線程安全的。
- HashMap的鍵和值都允許有null值存在,而HashTable則不行。
- 因為線程安全的問題,HashMap效率比HashTable的要高。
- Hashtable是同步的,而HashMap不是。因此,HashMap更適合于單線程環(huán)境,而Hashtable適合于多線程環(huán)境。
一般現在不建議用HashTable
- 是HashTable是遺留類,內部實現很多沒優(yōu)化和冗余
- 即使在多線程環(huán)境下,現在也有同步的ConcurrentHashMap替代,沒有必要因為是多線程而用HashTable。
11.List 和 Set 區(qū)別?
List,Set都是繼承自Collection接口
(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的,加入Set 的Object必須定義equals()方法 ,另外list支持for循環(huán),也就是通過下標來遍歷,也可以用迭代器,但是set只能用迭代,因為他無序,無法用下標來取得想要的值。)
12.Set和List對比?
- Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。
- List:和數組類似,List可以動態(tài)增長,查找元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。
13.當兩個對象的hashcode相同會發(fā)生什么?
- 因為hashcde相同,所以它們的bucket位置相同,'碰撞’會發(fā)生。因為HashMap使用鏈表存儲對象,這個Entry(包含有鍵值對的Map.Entry對象)會存儲在鏈表中。
14.如果兩個鍵的hashcode相同,你如何獲取值對象?
- 當我們調用get()方法,HashMap會使用鍵對象的hashcode找到bucket位置,然后會調用keys.equals()方法去找到鏈表中正確的節(jié)點,最終找到要找的值對象。
15.有沒有可能兩個不相等的對象有相同的hashcode?
- 有可能,(通話,重地)兩個不相等的對象可能會有相同的 hashcode 值,這就是為什么在 hashmap 中會有沖突。如果兩個對象相等,必須有相同的hashcode 值,反之不成立。
16.HashMap、LinkedHashMap、TreeMap的區(qū)別?
- HashMap是根據鍵的hashcode值存儲數據,根據鍵可以直接獲取它的值,具有很快的訪問速度,取得的數據完全是隨機的
- LinkedHashMap保存了記錄的插入順序,在使用Iterator進行遍歷的時候,先得到的肯定是先插入的數據,可以在構造時帶參數,按照應用次數來進行排序
- TreeMap實現SortMap接口,能夠把它保存的記錄根據鍵排序。默認的是升序排序,也可以指定排序的比較器,進行遍歷的時候得到的是排序過的記錄。
17.HashMap、LinkedHashMap、ConcurrentHashMap、ArrayList、LinkedList的底層實現。**
- HashMap是java數據結構中兩大結構數組和鏈表的組合。HashMap底層數組,數組中的每一項又是一個鏈表。程序會先根據key的hashcode()方法返回值決定該Entry在數組中的
- 存儲位置,如果該位置上沒有元素,就會將元素放置在此位置上,如果兩個Entry的key相同,會調用equals,返回值是true則覆蓋原來的value值,返回false則會形成Entry鏈,位于頭部。
- ArrrayList的底層實現是數組,在執(zhí)行add操作時,會先檢查數組 大小是否可以容納新的元素,如果不夠就會進行擴容。然后會將原來的數據拷貝到新的數組中。
- LinkedList底層是一個鏈表,其實現增刪改查和數據結構中的操作完全相同,而且插入是有序的。
- LinkedHashMap的底層結構式是雙鏈表,其他的邏輯處理與HashMap一致,同樣沒有鎖保護,多線程使用時存在風險。
- ConcurrentHashMap是segment數組結構和HashEntry數組結構組成的,segment在ConcurrentHashMap中充當鎖的角色,HashEntry用于存儲鍵值對數據。segment的結構是數組和鏈表,一個segment中有一個HashEntry,每個HashEntry是一個鏈表結構的元素。對HashEntry中的數據進行修改時,需要先獲得它所對應的segment鎖。每個ConcurrentHashMap默認有16個segment。
18.==和 equals hashCode 的區(qū)別?
- 基本數據類型: ==比較的是內容 引用數據類型: ==比的是地址值,equals默認比地址值,重寫按照規(guī)則比較,hashCode
19.自然排序Comparble和比較器排序Comparator的異同點?
相同點:
返回值的規(guī)則:
-
如果返回值為負數,表示當前存入的元素是較小值,存左邊
-
如果返回值為0,表示當前存入的元素跟集合中元素重復了,不存
-
如果返回值為正數,表示當前存入的元素是較大值,存右邊
不同點:
1.用到的接口不同
-
自然排序: 自定義類實現Comparable接口,重寫compareTo方法,根據返回值進行排序
-
比較器排序: 創(chuàng)建TreeSet對象的時候傳遞Comparator的實現類對象,重寫compare方法,根據返回值進行排序
2.使用場景不同
-
自然排序能滿足大部分情況
-
存儲沒有修改權限的類時可以使用
20.Iterator 和 ListIterator 有什么區(qū)別?
-
Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。
-
Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以后向。
-
ListIterator實現了Iterator接口,并包含其他的功能,比如:增加元素,替換元素,獲取前一個和后一個元素的索引,等等。
泛型
1.為什么使用泛型?
- 它提供了編譯時類型安全檢測機制,把運行時期的問題提前到了編譯期間
- 避免了強制類型轉換
2.泛型用在什么地方?
3.如何使用泛型類?
- 創(chuàng)建泛型類對象時,必須要給這個泛型確定具體的數據類型
樹
1.什么是二叉樹?
2.什么是二叉查找樹?
- 每個節(jié)點最多有兩個子節(jié)點,左邊比當前節(jié)點小,右邊比當前節(jié)點大
3.什么是平衡二叉樹?
- 二叉樹左右子樹的樹高差不超過1,任意節(jié)點的左右子樹都是平衡二叉樹
- 通過左旋右旋保持樹的平衡
序列化
1.什么是 Java 序列化?
- 序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。
- 可以對流化后的對象進行讀寫操作,也可將流化后的對象傳輸于網絡之間。
- 序列化是為了解決在對對象流進行讀寫操作時所引發(fā)的問題。
- 反序列化的過程,則是和序列化相反的過程。
- 另外,我們不能將序列化局限在 Java 對象轉換成二進制數組,例如說,我們將一個 Java 對象,轉換成 JSON 字符串,或者 XML 字符串,這也可以理解為是序列化。
2.如何實現 Java 序列化?
將需要被序列化的類,實現 Serializable 接口,該接口沒有需要實現的方法,implements Serializable 只是為了標注該對象是可被序列化的。
序列化
- 然后,使用一個輸出流(如:FileOutputStream)來構造一個 ObjectOutputStream(對象流)對象,接著,使用 ObjectOutputStream 對象的 #writeObject(Object obj) 方法,就可以將參數為 obj 的對象寫出(即保存其狀態(tài))。
反序列化
3.Java 序列話中,如果有些字段不想進行序列化怎么辦?
- 對于不想進行序列化的變量,使用 transient 關鍵字修飾。
- 當對象被序列化時,阻止實例中那些用此關鍵字修飾的的變量序列化。
- 當對象被反序列化時,被 transient 修飾的變量值不會被持久化和恢復。
- transient 只能修飾變量,不能修飾類和方法。
4.對象操作流是字符流還是字節(jié)流?
5.如何在讀寫文件時指定字符集?
jdk11之前:
- 使用轉換流InputStreamReader(輸入轉換流)字節(jié)轉換字符橋梁/OutputStreamWriter(輸出轉換流)字符轉字節(jié)橋梁
jdk11之后
6.字符緩沖流特有方法?
- readLine():讀取一整行,到達尾處為null
- newLine():跨平臺換行
7.為什么使用對象流?
- 在開發(fā)中,經常需要將對象的信息保存到磁盤中,如果使用前面所學的知識來實現,會非常的繁瑣。使用對象流就非常的方便
- 對象操作流可以將對象以字節(jié)的形式寫到本地文件中,直接打開是看不懂的,需要時可以再次用對象操作流讀到內存中
多線程
1.什么是線程?
- 線程是操作系統能夠進行運算調度的最小單位,它被包含在進程之中,是進程中的實際運作單位。線程是進程的一部分,是進程中的單個控制流,是一條執(zhí)行路徑
2.線程和進程有什么區(qū)別?
- 線程是進程的子集,一個進程可以有很多線程,每條線程并行執(zhí)行不同的任務。不同的進程使用不同的內存空間,而所有的線程共享一片相同的內存空間。
3.如何在Java中實現線程?
- 繼承Thread:
- 實現Runnable接口:
- 實現Runnable接口,將實現類作為參數傳遞給Thread對象
- 實現Callable接口:
- 實現Callabale接口,創(chuàng)建FutureTask對象,將Callable作為參數傳遞給FutureTask對象,再將FutureTask對象傳遞給Thread類
4.用Runnable還是Thread?
Java不支持類的多重繼承,但允許你調用多個接口。所以如果你要繼承其他類,當然是調用Runnable接口好了。
- Thread:
- Runnable,Callable:
- 擴展性比較強,優(yōu)先使用Runnable接口,需要執(zhí)行完有返回值可以選擇Callable接口
5.Thread 類中的start() 和 run() 方法有什么區(qū)別?
- start()方法被用來啟動新創(chuàng)建的線程,而且start()內部調用了run()方法,這和直接調用run()方法的效果不一樣。
當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啟動,start()方法才會啟動新線程。
6.Java中Runnable和Callable有什么不同?
- Runnable和Callable都代表那些要在不同的線程中執(zhí)行的任務。Runnable從JDK1.0開始就有了,Callable是在JDK1.5增加的。它們的主要區(qū)別是Callable的 call() 方法可以返回值和拋出異常,而Runnable的run()方法沒有這些功能。Callable可以返回裝載有計算結果的Future對象。
7.Java內存模型是什么?
Java內存模型規(guī)定和指引Java程序在不同的內存架構、CPU和操作系統間有確定性地行為。它在多線程的情況下尤其重要。Java內存模型對一個線程所做的變動能被其它線程可見提供了保證,它們之間是先行發(fā)生關系。這個關系定義了一些規(guī)則讓程序員在并發(fā)編程時思路更清晰。比如,先行發(fā)生關系確保了:
-
線程內的代碼能夠按先后順序執(zhí)行,這被稱為程序次序規(guī)則。
-
對于同一個鎖,一個解鎖操作一定要發(fā)生在時間上后發(fā)生的另一個鎖定操作之前,也叫做管程鎖定規(guī)則。
-
前一個對volatile的寫操作在后一個volatile的讀操作之前,也叫volatile變量規(guī)則。
-
一個線程內的任何操作必需在這個線程的start()調用之后,也叫作線程啟動規(guī)則。
-
一個線程的所有操作都會在線程終止之前,線程終止規(guī)則。
-
一個對象的終結操作必需在這個對象構造完成之后,也叫對象終結規(guī)則。
-
可傳遞性
8.Java中的volatile 變量是什么?
- volatile是一個特殊的修飾符,只有成員變量才能使用它。在Java并發(fā)程序缺少同步類的情況下,多線程對成員變量的操作對其它線程是透明的。volatile變量可以保證下一個讀取操作會在前一個寫操作之后發(fā)生,就是上一題的volatile變量規(guī)則。
9.什么是線程安全?Vector是一個線程安全類嗎?
- 多個線程可能會同時運行同一段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。一個線程安全的計數器類的同一個實例對象在被多個線程使用的情況下也不會出現計算失誤。很顯然你可以將集合類分成兩組,線程安全和非線程安全的。Vector 是用同步方法來實現線程安全的, 而和它相似的ArrayList不是線程安全的。
10.Java中如何停止一個線程?
- 當run() 或者 call() 方法執(zhí)行完的時候線程會自動結束,如果要手動結束一個線程,你可以用volatile 布爾變量或設置某個變量達到一定值的時候,來退出run()方法的循環(huán)或者是取消任務來中斷線程。
11.Java中notify 和 notifyAll有什么區(qū)別?
- notify()方法不能喚醒某個具體的線程,所以只有一個線程在等待的時候它才有用武之地。
- notifyAll()喚醒所有線程并允許他們爭奪鎖確保了至少有一個線程能繼續(xù)運行
12. 什么是線程池? 為什么要使用它?
- 創(chuàng)建線程要花費昂貴的資源和時間,如果任務來了才創(chuàng)建線程那么響應時間會變長,而且一個進程能創(chuàng)建的線程數有限。為了避免這些問題,在程序啟動的時候就創(chuàng)建若干線程來響應處理,它們被稱為線程池,里面的線程叫工作線程。從JDK1.5開始,Java API提供了Executor框架讓你可以創(chuàng)建不同的線程池。比如單線程池,每次處理一個任務;數目固定的線程池或者是緩存線程池(一個適合很多生存期短的任務的程序的可擴展線程池)
13.如何寫代碼來解決生產者消費者問題?
- 在現實中你解決的許多線程問題都屬于生產者消費者模型,就是一個線程生產任務供其它線程進行消費,你必須知道怎么進行線程間通信來解決這個問題。比較低級的辦法是用wait和notify來解決這個問題,比較贊的辦法是用Semaphore 或者 BlockingQueue來實現生產者消費者模型
14.Java多線程中的死鎖
死鎖是指兩個或兩個以上的進程在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。這是一個嚴重的問題,因為死鎖會讓你的程序掛起無法完成任務,死鎖的發(fā)生必須滿足以下四個條件:
-
互斥條件:一個資源每次只能被一個進程使用。
-
請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
-
不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
-
循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關系。
避免死鎖最簡單的方法就是阻止循環(huán)等待條件,將系統中所有的資源設置標志位、排序,規(guī)定所有的進程申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。
15.Java中synchronized 和 ReentrantLock 有什么不同?
- 這兩種方式最大區(qū)別就是對于Synchronized來說,它是java語言的關鍵字,是原生語法層面的互斥,需要jvm實現。而ReentrantLock它是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成。
16.詳談Synchronized
當Synchronized關鍵字修飾一個方法的時候,該方法叫做同步方法:java中的每個對象都有一個鎖(lock)或者叫做監(jiān)視器(monitor),當訪問某個對象的synchronized方法的時候,表示將對象上鎖,此時其它任何線程都無法再去訪問synchronized方法了,直到之前的那個線程執(zhí)行方法完畢后(或者是拋出了異常),那么將該對象的鎖釋放掉,其他線程才有可能再去訪問該synchronized方法。
- 注意1:
- 如果一個對象有多個synchronized方法,某一個時刻某個線程已經進入到了某個synchronized方法,那么在該方法沒有執(zhí)行完畢前,其它線程是無法訪問該對象的任何synchronzed方法的。
- 注意2:
- 如果某個Synchronized方法是static的,那么當線程訪問該方法時,它鎖的并不是Synchronized方法所在的對象,而是Synchronized方法所在的對象所對象的Class對象,因為java中無論一個類有多少個對象,這些對象會對應唯一一個class對象,因此當線程分別訪問同一個類的兩個對象的兩個static Synchronized方法的時候,他們執(zhí)行的順序也是順序的,也就是說一個線程先去執(zhí)行方法,執(zhí)行完畢后另一個線程才開始執(zhí)行。
- 注意3:
- jdk1.6之后對synchronized(偏向鎖(根本就不加鎖)、輕量級鎖(CAS),重量級鎖(悲觀鎖))進行了大量的優(yōu)化
17.在Java中Lock接口與synchronized塊的區(qū)別是什么?
- 用法不一樣。synchronized既可以加在方法上,也可以加載特定的代碼塊上,括號中表示需要鎖的對象。而Lock需要顯示地指定起始位置和終止位置。synchronzied是托管給jvm執(zhí)行的,Lock鎖定是通過代碼實現的。
- 在性能上來說,如果競爭資源不激烈,兩者的性能是差不多的,而當競爭資源非常激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優(yōu)于synchronized。所以說,在具體使用時要根據適當情況選擇。
- 鎖的機制不一樣。synchronized獲得鎖和釋放的方式都是在塊結構中,而且是自動釋放鎖。而Lock則需要開發(fā)人員手動去釋放,并且必須在finally塊中釋放,否則會引起死鎖問題的發(fā)生。
- Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
- synchronized在發(fā)生異常時,會自動釋放線程占有的鎖,因此不會導致死鎖現象發(fā)生;而Lock在發(fā)生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖
18.synchronized 的原理是什么?有什么不足?
- 原理:
- synchronized是 Java 內置的關鍵字,它提供了一種獨占的加鎖方式。
- synchronized的獲取和釋放鎖由JVM實現,用戶不需要顯示的釋放鎖,非常方便。
- 不足:
- 當線程嘗試獲取鎖的時候,如果獲取不到鎖會一直阻塞。
- 如果獲取鎖的線程進入休眠或者阻塞,除非當前線程異常,否則其他線程嘗試獲取鎖必須一直等待。
19.關于成員變量和局部變量
- 如果一個變量是成員變量,那么多個線程對同一個對象的成員變量進行操作的時候,他們對該成員變量是彼此影響的,也就是說一個線程對成員變量的改變會影響到另外一個線程;如果一個變量是局部變量,那么每個線程都會有一個該局部變量的拷貝,一個線程對該局部變量的改變不會影響到其它的線程。
20. 如果你提交任務時,線程池隊列已滿。會時發(fā)會生什么?
- 如果一個任務不能被調度執(zhí)行那么ThreadPoolExecutor’s submit()方法將會拋出一個RejectedExecutionException異常。
21.volatile關鍵字的作用是?
- 保證變量的可見性。
- 在java內存結構中,每個線程都是有自己獨立的內存空間(此處指的線程棧)。當需要對一個共享變量操作時,線程會將這個數據從主存空間復制到自己的獨立空間內進行操作,然后在某個時刻將修改后的值刷新到主存空間。這個中間時間就會發(fā)生許多奇奇怪怪的線程安全問題了,volatile就出來了,它保證讀取數據時只從主存空間讀取,修改數據直接修改到主存空間中去,這樣就保證了這個變量對多個操作線程的可見性了。換句話說,被volatile修飾的變量,能保證該變量的 單次讀或者單次寫 操作是原子的。
- 但是線程安全是兩方面需要的 原子性(指的是多條操作)和可見性。volatile只能保證可見性,synchronized是兩個均保證的。
- volatile輕量級,只能修飾變量;synchronized重量級,還可修飾方法。
- volatile不會造成線程的阻塞,而synchronized可能會造成線程的阻塞。
22.守護線程和非守護線程有什么區(qū)別?
- 程序運行完畢,JVM 會等待非守護線程完成后關閉,但是 JVM 不會等待守護線程
23.線程的生命周期?
線程的生命周期包含5個階段,包括:新建、就緒、運行、阻塞、銷毀。
-
新建:就是剛使用new方法,new出來的線程;
-
就緒:就是調用的線程的start()方法后,這時候線程處于等待CPU分配資源階段,誰先搶的CPU資源,誰開始執(zhí)行;
-
運行:當就緒的線程被調度并獲得CPU資源時,便進入運行狀態(tài),run方法定義了線程的操作和功能;
-
阻塞:在運行狀態(tài)的時候,可能因為某些原因導致運行狀態(tài)的線程變成了阻塞狀態(tài),比如sleep()、wait()之后線程就處于了阻塞狀態(tài),這個時候需要其他機制將處于阻塞狀態(tài)的線程喚醒,比如調用notify或者notifyAll()方法。喚醒的線程不會立刻執(zhí)行run方法,它們要再次等待CPU分配資源進入運行狀態(tài);
-
銷毀:如果線程正常執(zhí)行完畢后或線程被提前強制性的終止或出現異常導致結束,那么線程就要被銷毀,釋放資源;
24.wait和sleep,notify()鎖方面區(qū)別?
- wait:讓線程等待,同時立即釋放鎖
- sleep():讓線程休眠,但是不會釋放鎖
- notify()或notifyAll(): 喚醒等待的線程,但是不會立即釋放鎖
25.什么情況下會出現線程安全問題?
- 多線程環(huán)境
- 有共享數據
- 有對共享數據的操作
26.Java中規(guī)定了線程有哪幾種狀態(tài)?
27.什么是原子性?
- 所謂的原子性就是完成功能的所有操作要么都執(zhí)行,要么都不執(zhí)行
28.Java中哪些操作是原子操作?
- 除了long和double之外的所有原始類型的賦值
- 所有volatile變量的賦值
- java.concurrent.Atomic *類的所有操作
29.什么是CAS算法?
- 當預期值E==主內存中的值V,此時可以進行修改,將V改成新值
- 當預期值E!=主內存中的值V時,將主內存中的已經改變的值更新到自己的工作內存中,再次嘗試比較,直到預期值E等于主內存中的值V,才可以修改。這個過程稱為自旋
30.synchronized和CAS的區(qū)別?
- 相同點:
- 不同點:
- synchronized總是從最壞的角度出發(fā),認為每次獲取數據的時候,別人都有可能修改。所以在每 次操作共享數據之前,都會上鎖。(悲觀鎖)
- CAS是從樂觀的角度出發(fā),假設每次獲取數據別人都不會修改,所以不會上鎖。只不過在修改共享數據的時候,會檢查一下,別人有沒有修改過這個數據。如果別人修改過,那么我再次獲取現在最新的值。如果別人沒有修改過,那么我現在直接修改共享數據的值.(樂觀鎖)
31.并發(fā)容器Hashtable和ConcurrentHashMap特點?
- Hashtable:
- ConcurrentHashMap:
反射類加載器
1.Java反射機制的作用?
- 在運行時判斷任意一個對象所屬的類。
- 在運行時判斷任意一個類所具有的成員變量和方法。
- 在運行時任意調用一個對象的方法
- 在運行時構造任意一個類的對象
2.什么是反射機制?
- 簡單說,反射機制值得是程序在運行時能夠獲取自身的信息。在java中,只要給定類的名字,那么就可以通過反射機制來獲得類的所有信息。
3.哪里用到反射機制?
- Spring 框架的 IoC 基于反射創(chuàng)建對象和設置依賴屬性。
- Spring MVC 的請求調用對應方法,也是通過反射。
- JDBC 的 Class#forName(String className) 方法,也是使用反射。
4.反射機制的優(yōu)缺點?
- 靜態(tài)編譯:
- 動態(tài)編譯:
- 運行時確定類型,綁定對象。動態(tài)編譯最大限度的發(fā)揮了java的靈活性,體現了多態(tài)的應用,有利于降低類之間的耦合性。一句話,反射機制的優(yōu)點就是可以實現動態(tài)創(chuàng)建對象和編譯,體現出很大的靈活性,特別是在J2EE的開發(fā)中它的靈活性就表現的十分明顯。比如,一個大型的軟件,不可能一次就把把它設計的很完美,當這個程序編譯后,發(fā)布了,當發(fā)現需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。采用靜態(tài)的話,需要把整個程序重新編譯一次才可以實現功能的更新,而采用反射機制的話,它就可以不用卸載,只需要在運行時才動態(tài)的創(chuàng)建和編譯,就可以實現該功能。它的缺點是對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執(zhí)行相同的操作
5.反射中,Class.forName 和 ClassLoader 區(qū)別
- java中class.forName()和classLoader都可用來對類進行加載。
- class.forName()前者除了將類的.class文件加載到jvm中之外,還會對類進行解釋,執(zhí)行類中的static塊。
- 而classLoader只干一件事情,就是將.class文件加載到jvm中,不會執(zhí)行static中的內容,只有在newInstance才會去執(zhí)行static塊。
6.什么是雙親委派模型?
- 如果一個類加載器收到了類加載請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器去執(zhí)行,如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸,請求最終將到達頂層的啟動類加載器,如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器才會嘗試自己去加載,這就是雙親委派模式
7.為什么要有雙親委派模型?
- 為了保證類的全局唯一性,如果自定義類與Java核心類庫中的類重名了,會被編譯但不會不執(zhí)行
8.怎么利用反射使用私有成員?
- setAccessible(boolean flag)
網絡通信
1.什么是三次握手?
-
客戶端向服務器發(fā)出取消連接請求
-
服務器向客戶端返回一個響應,告訴客戶端收到了請求
-
客戶端向服務器端再次發(fā)出確認信息建立連接
2.什么是四次揮手?
3.TCP通信注意事項?
-
accept方法是阻塞的,作用就是等待客戶端連接
-
客戶端創(chuàng)建對象并連接服務器,此時是通過三次握手協議,保證跟服務器之間的連接
-
針對客戶端來講,是往外寫的,所以是輸出流 針對服務器來講,是往里讀的,所以是輸入流
-
read方法也是阻塞的
-
客戶端在關流的時候,還多了一個往服務器寫結束標記的動作
-
最后一步斷開連接,通過四次揮手協議保證連接終止
web階段
jsp相關
1.jsp內置對象和EL內置對象的區(qū)別與聯系
jsp內置對象:
EL表達式內置對象:
pageContext對象是二者唯一相同的對象,其他都是各自獨立的對象
2.說一下 jsp 的 4 種作用域?
JSP中的四種作用域包括page、request、session和application,具體來說:
-
page代表與一個頁面相關的對象和屬性。
-
request代表與Web客戶機發(fā)出的一個請求相關的對象和屬性。一個請求可能跨越多個頁面,涉及多個Web組件;需要在頁面顯示的臨時數據可以置于此作用域。
-
session代表與某個用戶與服務器建立的一次會話相關的對象和屬性。跟某個用戶相關的數據應該放在用戶自己的session中。
-
application代表與整個Web應用程序相關的對象和屬性,它實質上是跨越整個Web應用程序,包括多個頁面、請求和會話的一個全局作用域。
3.ServletContext 與application的異同
- 相同:
- 其實servletContext和application 是一樣的,就相當于一個類創(chuàng)建了兩個不同名稱的變量。在 servlet中ServletContext就是application對象。大家只要打開jsp編譯過后生成的Servlet中 jspService()方法就可以看到如下的聲明: ServletContextapplication = null;application= pageContext.getServletContext();
- 不同:
- 兩者的區(qū)別就是application用在jsp中,servletContext用在servlet中。application和page requestsession 都是JSP中的內置對象,在后臺用ServletContext存儲的屬性數據可以用 application對象獲得。 而且application的作用域是整個Tomcat啟動的過程。 例如:ServletContext.setAttribute("username",username); 則在JSP網頁中可以使用 application.getAttribute("username"); 來得到這個用戶名。
4.jsp 有哪些內置對象?作用分別是什么?
JSP有9個內置對象:
-
request:封裝客戶端的請求,其中包含來自GET或POST請求的參數;
-
response:封裝服務器對客戶端的響應;
-
pageContext:通過該對象可以獲取其他對象;
-
session:封裝用戶會話的對象;
-
application:封裝服務器運行環(huán)境的對象;
-
out:輸出服務器響應的輸出流對象;
-
config:Web應用的配置對象;
-
page:JSP頁面本身(相當于Java程序中的this);
-
exception:封裝頁面拋出異常的對象。
概念相關
1.post和get區(qū)別?
-
-
get:
-
數據顯示在地址欄
-
不安全
-
get方式提交有大小限制(約4kb)
2.相對路徑和絕對路徑?
-
相對路徑
概念:
寫法:
-
絕對路徑
概念:
寫法:
3.Cookie和session的區(qū)別?
session是基于cookie
多次請求之間共享數據
- cookie:
-
數據存儲于客戶端--不安全
-
只能存字符串
-
大小有限制
- session:
-
數據存儲于服務器端--安全
-
類型無限制
-
大小無限制
4.servlet四大域對象的區(qū)別?

5.什么是活化與鈍化?
服務器自動完成(注意使用本地Tomcat才行)
- 鈍化:
- 活化:
-
概念:相反的狀態(tài),從磁盤讀取到內存
-
時機:tomcat重新啟動時會將會話加載到內存
6.EL內置對象有哪些?

注意:EL 表達式內置對象和,JSP 內置對象不是一回事,el表達式中想要使用jsp 中的對象需要使用pageContext 獲取
7.如果有大量的網站訪問量。那么會產生很多的session,該怎么解決?
session默認保存在內存中,內存資源寶貴,session數據量大導致內存利用率高,以下方案解決session內存存儲問題:
- 可以設置session超時時間,達到超時時間session自動清空
<session-config>
<session-timeout>20</session-timeout>
</session-config>
- 將session中的數據序列化到硬盤中
- 不使用session,使用cookie(此方法存在安全性問題)
8.頁面?zhèn)鬟f對象的方法?
- Request、session、application、cookie等
9.session 和 application的區(qū)別?
- 兩者的作用范圍不同:
- Session對象是用戶級的,而Application是應用程序級別的
- 一個用戶一個session對象,每個用戶的session對象不同,在用戶所訪問的網站多個頁面之間共享同一個session對象
- 一個Web應用程序一個application對象,每個Web應用程序的application對象不同,但一個Web應用程序的多個用戶之間共享同一個application對象。
- 兩者的生命周期不同:
- session對象的生命周期:用戶首次訪問網站創(chuàng)建,用戶離開該網站 (不一定要關閉瀏覽器) 消亡。
- application對象的生命周期:啟動Web服務器創(chuàng)建,關閉Web服務器銷毀。
Servlet相關
1.解釋一下什么是servlet
- Servlet有良好的生存期的定義,包括加載和實例化、初始化、處理請求以及服務結束。這個生存期由javax.servlet.Servlet 接口的init,service 和destroy方法表達。
- Web容器加載Servlet,Servlet被服務器實例化后,生命周期開始。通過調用servlet的init()方法進行servlet的初始化。通過調用service()方法實現,根據請求的不同調用不同的doXXX方法(doGet,doPost)方法。結束服務,web容器調用servlet的destroy()方法。
2.servlet的生命周期?
servlet容器負責管理servlet的生命周期,servlet生命周期如下:
- 加載和實例化 Servlet 容器裝載和實例化一個 Servlet。創(chuàng)建出該 Servlet 類的一個實例。
- 初始化 在 Servlet 實例化完成之后,容器負責調用該 Servlet 實例的 init() 方法,在處理用戶請求之前,來做一些額外的初始化工作。
- 處理請求 當 Servlet 容器接收到一個 Servlet 請求時,便運行與之對應的 Servlet 實例的 service() 方法,service() 方法再派遣運行與請求相對應的 doXX(doGet,doPost) 方法來處理用戶請求。
- 銷毀 當 Servlet 容器決定將一個 Servlet 從服務器中移除時 ( 如 Servlet 文件被更新 ),便調用該 Servlet 實例的 destroy() 方法,在銷毀該 Servlet 實例之前, 來做一些其他的工作。
加載實例化,初始化,銷毀,在整個生命周期中只會執(zhí)行一次
補充:
3.servlet生命周期方法有哪些?
共有3個方法:
- public void init(ServletConfig config):
- 這個方法是由servlet容器調用用來初始化servlet的,這個方法在servlet生命周期中僅被調用一次。
- public void service(ServletRequest request, ServletResponse response):
- servlet容器為每個客戶端創(chuàng)建線程,然后都會執(zhí)行service方法,但執(zhí)行此方法前init方法必須已經被執(zhí)行了。
- public void destroy():
- servlet從servlet容器中被移除時會調用此方法,僅被調用一次。
4.servlet過濾器的作用?
- 打印一些請求參數到日志中
- 授權請求訪問資源
- 在將請求提交給servlet之前,對request請求頭或請求體進行一些格式化的處理
- 將響應數據進行壓縮再返回給瀏覽器。
- 解決亂碼問題。
5.servlet監(jiān)聽器的作用?
- 監(jiān)聽客戶端的請求,服務器端的操作等 。通過監(jiān)聽器,可以自動激發(fā)一些操作,比如監(jiān)聽在線的用戶數量( 當增加一個HttpSession時,就自動觸發(fā)sessionCreated(HttpSessionEvent se)方法,在這個方法中就可以統計在線人數了 ),另外還可以用來初始化一些資源,比如數據庫連接池等(web.xml中配置的context-param只能是字符串不能使對象,這時就得使用ServletContextListener了,注意,有讀者可能說ServletConext的setAttribute不是可以設置對象嗎?但是這是在servlet創(chuàng)建之后才能調用的方法,如果希望web應用一啟動就產生初始參數必須使用監(jiān)聽器)。
6.web.xml中組件的加載順序?
- context-param -> listener -> filter -> servlet
- 而同個類型之間的實際程序調用的時候的順序是根據對應的 mapping 的順序進行調用的
7.如何確保servlet在應用啟動之后被加載到內存?
- 通常情況下都是客戶端請求一個servlet,這個servlet才會被加載到內存,但對于某些很大加載很耗時的servlet我們希望應用啟動時就加載它們,這時我們可以在web.xml文件中配置或者使用webServlet注解(servlet3.0)告訴容器系統一啟動就加載:
<servlet> <servlet-name>foo</servlet-name> <servlet-class>com.foo.servlets.Foo</servlet-class> <load-on-startup>5</load-on-startup> </servlet>
- load-on-startup節(jié)點中必須配置一個整數,整數代表應用一啟動就會被加載,負數表示當客戶端請求之后才加載,正數的值越小,說明越先被加載。
8.HttpServlet為什么聲明為抽象類?
- httpServlet類雖然是抽象類但卻沒有抽象方法,之所以這樣設計,是因為doget,dopost等方法并沒有業(yè)務邏輯,開發(fā)者至少應該重寫一個service中的方法,這就是我們不能實例化HttpServlet的原因。
9.redirect(重定向)和forward(請求轉發(fā))區(qū)別?
重定向:
-
兩次請求
-
地址欄發(fā)生變化
-
不可以使用request域的共享數據
-
不可以訪問/WEB_INF下的資源
轉發(fā):
-
一次請求
-
地址欄不發(fā)生變化
-
可以使用request域的共享數據
-
可以訪問/WEB_INF下的資源
補充:
servlet中怎么定義forward 和redirect?
- 轉發(fā):request.getRequestDispatcher (“demo.jsp"). forward(request, response);
- 重定向:response.sendRedirect(“demo.jsp");
10.sevlet中的屬性域有哪些?
servlet提供了一些域對象方便內部servlet之間的通信,我們可以通過set/get方法為web應用設置或取出屬性值。servlet提供3個域(和jsp區(qū)分開來):
- request scope
- session scope
- application scope
分別由ServletRequest,HttpSession,ServletContext對象提供對應的set/get/remove方法去操作者三個域。
注:這跟web.xml中為servletConfig(針對單個servlet)和servletContext(針對web應用)定義的初始化參數不一樣。
11.Servlet是否線程安全?
- HttpServlet的init和destroy方法在servlet聲明周期中僅 調用一次,所以不用擔心它們的線程安全。但是service方法以及doget,dopost等方法是存在線程安全問題的,因為servlet容器為每一個客戶端請求都創(chuàng)建一個線程,這些線程在同一時刻可能訪問同一個servlet的service方法,所以我們在使用這些方法時務必小心。
12.如何創(chuàng)建線程安全的servlet?(SingleThreadModel方法不算)
- 盡量使用局部變量,減少全局變量的使用。
- 對于共享變量,加上關鍵字synchronized。
注:servlet中常見線程安全與不安全的對象
- 線程安全:ServletRequest,ServletResponse
- 線程不安全:ServletContext,HttpSession。
對于 ServletContext,我們應盡量減少該對象中屬性的修改。
而HttpSession對象在用戶會話期間存在,只能在處理屬于同一個Session的請求的線程中被訪問,因此Session對象的屬性訪問理論上是線程安全的。但是當用戶打開多個同屬于一個進程的瀏覽器窗口,在這些窗口的訪問屬于同一個Session,會出現多次請求,需要多個工作線程來處理請求,可能造成同時多線程讀寫屬性
13.是否有必要重寫service方法?
- 一般情況下是沒有必要的,因為service方法會根據請求的類型(get、post等)將請求分發(fā)給doxxx方法去執(zhí)行。即使我們需要在處理請求之前需要做一些額外的事,我們也可以通過過濾器或監(jiān)聽器完成。
14.servlet包裝類有什么用?
- servletAPI提供了兩個包裝類:HttpServletRequestWrapper類和HttpServletResponseWrapper類,這些包裝類幫助開發(fā)者給出request和response的一般實現。我們可以繼承它們并選擇我們需要復寫的方法進行復寫(包裝設計模式),而不用復寫所有的方法。
15.在servlet中能否產生類似死鎖情況?
- 可以的,你在doPost方法中調用doGet方法,在doGet方法中調用doPost方法,將產生死鎖(最終會拋出stackoverflow異常)。
16.Servlet API中forward()與redirect()的區(qū)別?
- forward 是服務器轉發(fā),一次請求和響應,瀏覽器地址欄不會顯示出轉發(fā)后的地址;forward比較高效,而且有助于隱藏實際地址。
- eg: getServletContext().getRequest Dispatcher(“/servlet/secondservlet”).forward(request, response);
- redirect 是重定向,兩次請求和響應,瀏覽器會得到跳轉地址,對新地址重新發(fā)送請求。
17.ServletContext對象和ServletConfig對象的區(qū)別?
- 每個servlet都會有自己獨有的servletConfig對象而servletContext對象是整個web應用共享的。
- servletConfig提供servlet的初始化參數(init-param),僅該servlet可以訪問。而servletContext提供的初始化參數整個web應用的所有servlet都可以訪問。
- servletContext對象提供了setAttribute方法設置共享參數,而servletConfig并沒有對應的set方法。
18.PrintWriter和ServletOutPutStream類有什么區(qū)別?
- PrintWriter是字符流,ServletOutputStream是字節(jié)流??梢酝ㄟ^ PrintWriter向瀏覽器輸出字符數組或者是字符串。也可以通過ServletOutPutStream向瀏覽器端輸出字節(jié)數組。
- PrintWriter對象在servlet中可以通過response.getWriter()方法獲取
- ServletOutputStream對象通過response.getOutputStream方法獲取。
19.在一個servlet能否同時獲取PrintWriter和ServletOutputStream對象?
- 不可以,如果同時獲取,將會拋出java.lang.IllegalStateException異常。
20.Request對象的主要方法有哪些?
- setAttribute(String name,Object):設置名字為name的request 的參數值
- getAttribute(String name):返回由name指定的屬性值
- getAttributeNames():返回request對象所有屬性的名字集合,結果是一個枚舉的實例
- getCookies():返回客戶端的所有Cookie對象,結果是一個Cookie數組
- getCharacterEncoding():返回請求中的字符編碼方式
- getContentLength():返回請求的Body的長度
- getHeader(String name):獲得HTTP協議定義的文件頭信息
- getHeaders(String name):返回指定名字的request Header的所有值,結果是一個枚舉的實例
- getHeaderNames():返回所以request Header的名字,結果是一個枚舉的實例
- getInputStream():返回請求的輸入流,用于獲得請求中的數據
- getMethod():獲得客戶端向服務器端傳送數據的方法
- getParameter(String name):獲得客戶端傳送給服務器端的有name指定的參數值
- getParameterNames():獲得客戶端傳送給服務器端的所有參數的名字,結果是一個枚舉的實例
- getParametervalues(String name):獲得有name指定的參數的所有值
- getProtocol():獲取客戶端向服務器端傳送數據所依據的協議名稱
- getQueryString():獲得查詢字符串
- getRequestURI():獲取發(fā)出請求字符串的客戶端地址
- getRemoteAddr():獲取客戶端的IP 地址
- getRemoteHost():獲取客戶端的名字
- getSession([Boolean create]):返回和請求相關Session
- getServerName():獲取服務器的名字
- getServletPath():獲取客戶端所請求的腳本文件的路徑
- getServerPort():獲取服務器的端口號
- removeAttribute(String name):刪除請求中的一個屬性
21.jsp和servlet的異同點以及聯系是什么?
-
jsp經編譯后就變成了servlet(jsp本質就是servlet,jvm只能識別java的類,不能識別jsp代碼,web容器將jsp的代碼編譯成jvm能夠識別的java類)
-
jsp更擅長表現于頁面顯示,servlet更擅長于邏輯控制
-
setvlet中沒有內置對象,jsp中的內置對象都是必須通過HttpServletRequest對象,HttpServletResponse對象及HttpServlet對象得到
-
jsp是servlet的一種簡化,使用jsp只需要完成程序員需用輸出到客戶端的內容,jsp中的java腳本如何鑲嵌到一個類中,由jsp容器完成,而servlet則是個完整的java類,這個類的service方法用于生成對客戶端的響應
數據庫階段
索引相關
1.什么是索引?
- 索引是一種數據結構,可以幫助我們快速的進行數據的查找
2.索引是個什么樣的數據結構呢?
- 索引的數據結構和具體存儲引擎的實現有關, 在MySQL中使用較多的索引有Hash索引,B+樹索引等,而我們經常使用的InnoDB存儲引擎的默認索引實現為:B+樹索引.
3.在建立索引的時候,都有哪些需要考慮的因素呢?
- 建立索引的時候一般要考慮到字段的使用頻率,經常作為條件進行查詢的字段比較適合.如果需要建立聯合索引的話,還需要考慮聯合索引中的順序.此外也要考慮其他方面,比如防止過多的所有對表造成太大的壓力.這些都和實際的表結構以及查詢方式有關.
4.關心過業(yè)務系統里面的sql耗時嗎?統計過慢查詢嗎?對慢查詢都怎么優(yōu)化過?
在業(yè)務系統中,除了使用主鍵進行的查詢,其他的我都會在測試庫上測試其耗時,慢查詢的統計主要由運維在做,會定期將業(yè)務中的慢查詢反饋給我們.
慢查詢的優(yōu)化首先要搞明白慢的原因是什么? 是查詢條件沒有命中索引?是load了不需要的數據列?還是數據量太大?
所以優(yōu)化也是針對這三個方向來的,
-
首先分析語句,看看是否load了額外的數據,可能是查詢了多余的行并且拋棄掉了,可能是加載了許多結果中并不需要的列,對語句進行分析以及重寫.
-
分析語句的執(zhí)行計劃,然后獲得其使用索引的情況,之后修改語句或者修改索引,使得語句可以盡可能的命中索引.
-
如果對語句的優(yōu)化已經無法進行,可以考慮表中的數據量是否太大,如果是的話可以進行橫向或者縱向的分表.
5.區(qū)別B樹,B-,B+,B*?
- B樹:二叉樹,每個結點只存儲一個關鍵字,等于則命中,小于走左結點,大于走右結點;
- B-樹:多路搜索樹,每個結點存儲M/2到M個關鍵字,非葉子結點存儲指向關鍵字范圍的子結點; 所有關鍵字在整顆樹中出現,且只出現一次,非葉子結點可以命中;
- B+樹:在B-樹基礎上,為葉子結點增加鏈表指針,所有關鍵字都在葉子結點中出現,非葉子結點作為葉子結點的索引;B+樹總是到葉子結點才命中;
- B*樹:在B+樹基礎上,為非葉子結點也增加鏈表指針,將結點的最低利用率從1/2提高到2/3;
6.MySQL優(yōu)化策略?
- 開啟查詢緩存,優(yōu)化查詢
- explain你的select查詢,這可以幫你分析你的查詢語句或是表結構的性能瓶頸。EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的
- 當只要一行數據時使用limit 1,MySQL數據庫引擎會在找到一條數據后停止搜索,而不是繼續(xù)往后查少下一條符合記錄的數據
- 為搜索字段建索引
- 使用 ENUM 而不是 VARCHAR,如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態(tài)”或“部門”,你知道這些字段的取值是有限而且固定的,那么,你應該使用 ENUM 而不是VARCHAR。
- Prepared StatementsPrepared Statements很像存儲過程,是一種運行在后臺的SQL語句集合,我們可以從使用 prepared statements 獲得很多好處,無論是性能問題還是安全問題。Prepared Statements 可以檢查一些你綁定好的變量,這樣可以保護你的程序不會受到“SQL注入式”攻擊
- 垂直分表
- 選擇正確的存儲引擎
7.key和index的區(qū)別?
- key 是數據庫的物理結構,它包含兩層意義和作用,一是約束(偏重于約束和規(guī)范數據庫的結構完整性),二是索引(輔助查詢用的)。包括primary key, unique key, foreign key 等
- index是數據庫的物理結構,它只是輔助查詢的,它創(chuàng)建時會在另外的表空間(mysql中的innodb表空間)以一個類似目錄的結構存儲。索引要分類的話,分為前綴索引、全文本索引等;
8.怎么驗證 mysql 的索引是否滿足需求?
- 使用 explain 查看 SQL 是如何執(zhí)行查詢語句的,從而分析你的索引是否滿足需求。
- explain 語法:explain select * from table where type=1。
事務相關
1.ACID是什么?可以詳細說一下嗎?
- A=Atomicity
- 原子性,就是上面說的,要么全部成功,要么全部失敗.不可能只執(zhí)行一部分操作.
- C=Consistency
- 系統(數據庫)總是從一個一致性的狀態(tài)轉移到另一個一致性的狀態(tài),不會存在中間狀態(tài).
- I=Isolation
- 隔離性: 通常來說:一個事務在完全提交之前,對其他事務是不可見的.注意前面的通常來說加了紅色,意味著有例外情況.
- D=Durability
- 持久性,一旦事務提交,那么就永遠是這樣子了,哪怕系統崩潰也不會影響到這個事務的結果.
2.同時有多個事務在進行會怎么樣呢?
多事務的并發(fā)進行一般會造成以下幾個問題:
-
臟讀: A事務讀取到了B事務未提交的內容,而B事務后面進行了回滾.
-
不可重復讀: 當設置A事務只能讀取B事務已經提交的部分,會造成在A事務內的兩次查詢,結果竟然不一樣,因為在此期間B事務進行了提交操作.
-
幻讀: A事務讀取了一個范圍的內容,而同時B事務在此期間插入了一條數據.造成"幻覺".
3.怎么解決這些問題呢?MySQL的事務隔離級別了解嗎?
MySQL的四種隔離級別如下:
-
- 這就是上面所說的例外情況了,這個隔離級別下,其他事務可以看到本事務沒有提交的部分修改.因此會造成臟讀的問題(讀取到了其他事務未提交的部分,而之后該事務進行了回滾).
- 這個級別的性能沒有足夠大的優(yōu)勢,但是又有很多的問題,因此很少使用.
-
READ COMMITTED(已提交讀)
- 其他事務只能讀取到本事務已經提交的部分.這個隔離級別有 不可重復讀的問題,在同一個事務內的兩次讀取,拿到的結果竟然不一樣,因為另外一個事務對數據進行了修改.
-
REPEATABLE READ(可重復讀)
- 可重復讀隔離級別解決了上面不可重復讀的問題(看名字也知道),但是仍然有一個新問題,就是 幻讀,當你讀取id> 10 的數據行時,對涉及到的所有行加上了讀鎖,此時例外一個事務新插入了一條id=11的數據,因為是新插入的,所以不會觸發(fā)上面的鎖的排斥,那么進行本事務進行下一次的查詢時會發(fā)現有一條id=11的數據,而上次的查詢操作并沒有獲取到,再進行插入就會有主鍵沖突的問題.
-
SERIALIZABLE(可串行化)
- 這是最高的隔離級別,可以解決上面提到的所有問題,因為他強制將所以的操作串行執(zhí)行,這會導致并發(fā)性能極速下降,因此也不是很常用.
4.Innodb使用的是哪種隔離級別呢?
5.對MySQL的鎖了解嗎?
- 當數據庫有并發(fā)事務的時候,可能會產生數據的不一致,這時候需要一些機制來保證訪問的次序,鎖機制就是這樣的一個機制.
6.MySQL都有哪些鎖呢?像上面那樣子進行鎖定豈不是有點阻礙并發(fā)效率了?
從鎖的類別上來講,有共享鎖和排他鎖.
-
共享鎖: 又叫做讀鎖. 當用戶要進行數據的讀取時,對數據加上共享鎖.共享鎖可以同時加上多個.
-
排他鎖: 又叫做寫鎖. 當用戶要進行數據的寫入時,對數據加上排他鎖.排他鎖只可以加一個,他和其他的排他鎖,共享鎖都相斥.
用上面的例子來說就是用戶的行為有兩種,一種是來看房,多個用戶一起看房是可以接受的. 一種是真正的入住一晚,在這期間,無論是想入住的還是想看房的都不可以.
鎖的粒度取決于具體的存儲引擎,InnoDB實現了行級鎖,頁級鎖,表級鎖.
他們的加鎖開銷從大到小,并發(fā)能力也是從大到小.
7.行級鎖定的優(yōu)點缺點?
- 優(yōu)點:
-
當在許多線程中訪問不同的行時只存在少量鎖定沖突。
-
回滾時只有少量的更改
-
可以長時間鎖定單一的行。
缺點:
-
-
比頁級或表級鎖定占用更多的內存。
-
當在表的大部分中使用時,比頁級或表級鎖定速度慢,因為你必須獲取更多的鎖。
-
如果你在大部分數據上經常進行GROUP BY操作或者必須經常掃描整個表,比其它鎖定明顯慢很多。
-
用高級別鎖定,通過支持不同的類型鎖定,你也可以很容易地調節(jié)應用程序,因為其鎖成本小于行級鎖定。
8.說一下 mysql 的行鎖和表鎖?
- MyISAM 只支持表鎖,InnoDB 支持表鎖和行鎖,默認為行鎖。
表設計相關
1. 為什么要盡量設定一個主鍵?
- 主鍵是數據庫確保數據行在整張表唯一性的保障,即使業(yè)務上本張表沒有主鍵,也建議添加一個自增長的ID列作為主鍵.設定了主鍵之后,在后續(xù)的刪改查的時候可能更加快速以及確保操作數據范圍安全.
2.主鍵使用自增ID還是UUID?
- 推薦使用自增ID,不要使用UUID.
- 因為在InnoDB存儲引擎中,主鍵索引是作為聚簇索引存在的,也就是說,主鍵索引的B+樹葉子節(jié)點上存儲了主鍵索引以及全部的數據(按照順序),如果主鍵索引是自增ID,那么只需要不斷向后排列即可,如果是UUID,由于到來的ID與原來的大小不確定,會造成非常多的數據插入,數據移動,然后導致產生很多的內存碎片,進而造成插入性能的下降.
補充:
- 關于主鍵是聚簇索引,如果沒有主鍵,InnoDB會選擇一個唯一鍵來作為聚簇索引,如果沒有唯一鍵,會生成一個隱式的主鍵.
3. 字段為什么要求定義為not null?
- null值會占用更多的字節(jié),且會在程序中造成很多與預期不符的情況.
4.varchar(10)和int(10)代表什么含義?
- varchar的10代表了申請的空間長度,也是可以存儲的數據的最大長度,而int的10只是代表了展示的長度,不足10位以0填充.也就是說,int(1)和int(10)所能存儲的數字大小以及占用的空間都是相同的,只是在展示時按照長度展示.
5.建表策略?
-
對于大數據字段,獨立表進行存儲,以便提高性能(例如:簡介字段);
-
使用varchar類型代替char,因為varchar會動態(tài)分配長度,char指定長度是固定的;
-
給表創(chuàng)建主鍵,對于沒有主鍵的表,在查詢和索引定義上有一定的影響;
-
避免表字段運行為null,建議設置默認值(例如:int類型設置默認值為0)在索引查詢上,效率立顯;
-
建立索引,最好建立在唯一和非空的字段上,建立太多的索引對后期插入、更新都存在一定的影響(考慮實際情況來創(chuàng)建);
存儲引擎相關
1. MySQL支持哪些存儲引擎?
- MySQL支持多種存儲引擎,比如InnoDB,MyISAM,Memory,Archive等等.在大多數的情況下,直接選擇使用InnoDB引擎都是最合適的,InnoDB也是MySQL的默認存儲引擎.
2.InnoDB和MyISAM有什么區(qū)別?
-
InnoDB支持事物,而MyISAM不支持事物
-
InnoDB支持行級鎖,而MyISAM支持表級鎖
-
InnoDB支持MVCC, 而MyISAM不支持
-
InnoDB支持外鍵,而MyISAM不支持
-
InnoDB不支持全文索引,而MyISAM支持。
3.什么是存儲過程?有哪些優(yōu)缺點?
存儲過程是一些預編譯的SQL語句。1、更加直白的理解:存儲過程可以說是一個記錄集,它是由一些T-SQL語句組成的代碼塊,這些T-SQL語句代碼像一個方法一樣實現一些功能(對單表或多表的增刪改查),然后再給這個代碼塊取一個名字,在用到這個功能的時候調用他就行了。2、存儲過程是一個預編譯的代碼塊,執(zhí)行效率比較高,一個存儲過程替代大量T_SQL語句 ,可以降低網絡通信量,提高通信速率,可以一定程度上確保數據安全
但是,在互聯網項目中,其實是不太推薦存儲過程的,比較出名的就是阿里的《Java開發(fā)手冊》中禁止使用存儲過程,我個人的理解是,在互聯網項目中,迭代太快,項目的生命周期也比較短,人員流動相比于傳統的項目也更加頻繁,在這樣的情況下,存儲過程的管理確實是沒有那么方便,同時,復用性也沒有寫在服務層那么好.
4.說一說三個范式?
- 第一范式: 每個列都不可以再拆分.
- 第二范式: 非主鍵列完全依賴于主鍵,而不能是依賴于主鍵的一部分.
- 第三范式: 非主鍵列只依賴于主鍵,不依賴于其他非主鍵.
在設計數據庫結構的時候,要盡量遵守三范式,如果不遵守,必須有足夠的理由.比如性能. 事實上我們經常會為了性能而妥協數據庫的設計.
|