第1章 JavaSE 面試題一、自增變量二、編程題:寫一個 Singleton 示例三、類初始化和實例初始化四、方法的參數傳遞機制五、遞歸與迭代六、成員變量與局部變量第2章 SSM 面試題一、Spring Bean 的做用域之間有什麼區別?二、Spring 支持的經常使用數據庫事務傳播屬性和事務隔離級別三、SpringMVC 中如何解決 POST 請求中文亂碼問題四、SpringMVC 中如何解決 GET 請求中文亂碼問題五、簡單的談一下 SpringMVC 的工做流程六、MyBatis 中當實體類中的屬性名和數據庫表中的字段名不同怎麼辦?第3章 Java 高級面試題一、Linux 經常使用服務類相關命令二、git 分支相關命令和實際應用三、redis 持久化四、Mysql 何時建索引五、JVM 垃圾回收機制第5章 Java 項目面試題一、redis 在項目中的使用場景二、Elasticsearch 與 solr 的區別三、單點登陸實現過程四、購物車實現過程五、消息隊列在項目中的使用javascript
public static void main(String[] args) {
int i = 1;
i = i++; // i=1
int j = i++; // j=1 i=2
int k = i + ++i * i++; // 2 + 3 * 3 = 11
System.out.println("i=" + i); // i=4
System.out.println("j=" + j); // j=1
System.out.println("k=" + k); // 11
}
(1)css
要點:
一、某個類只能有一個實例;
構造器私有化
二、它必須自行建立這個實例;
含有一個該類的靜態變量來保存這個惟一的實例
三、它必須自行向整個系統提供這個實例;
對外提供獲取該實例對象的方式:
(1)直接暴露
(2)用靜態變量的get方法獲取java
幾種常見形式:
餓漢式:在類初始化時直接建立對象,不存在線程安全問題
直接實例化餓漢式(簡潔直觀)
枚舉式(最簡潔)
靜態代碼塊餓漢式(適合複雜實例化)
示例代碼以下:mysql
package com.atguigu.singleton;
/*
* 餓漢式:
* 在類初始化時直接建立實例對象,無論你是否須要這個對象都會建立
*
* (1)構造器私有化
* (2)自行建立,而且用靜態變量保存
* (3)向外提供這個實例
* (4)強調這是一個單例,咱們能夠用 final 修飾
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
}
示例代碼以下:linux
package com.atguigu.singleton;
/*
* 枚舉類型:表示該類型的對象是有限的幾個
* 咱們能夠限定爲一個,就成了單例
*/
public enum Singleton2 {
INSTANCE
}
示例代碼以下:git
package com.atguigu.singleton;
import java.io.IOException;
import java.util.Properties;
public class Singleton3 {
public static final Singleton3 INSTANCE;
private String info;
static {
try {
Properties properties = new Properties();
properties.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
INSTANCE = new Singleton3(properties.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Singleton3(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Singleton3 [info=" + info + "]";
}
}
懶漢式:延遲建立對象
線程不安全(適用於單線程)
線程安全(適用於多線程)
靜態內部類形式(適用於多線程)
示例代碼以下:web
package com.atguigu.singleton;
/*
* 懶漢式:
* 延遲建立這個實例對象
* 線程不安全(適用於單線程)
*
* (1)構造器私有化
* (2)用一個靜態變量保存這個惟一的實例
* (3)提供一個靜態方法,獲取這個實例對象
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
public static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
示例代碼以下:面試
package com.atguigu.singleton;
/*
* 懶漢式:
* 延遲建立這個實例對象
* 線程安全(適用於多線程)
*
* (1)構造器私有化
* (2)用一個靜態變量保存這個惟一的實例
* (3)提供一個靜態方法,獲取這個實例對象
*/
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (instance == null) { // 優化性能
synchronized (Singleton5.class) { // 線程安全
if (instance == null) {
instance = new Singleton5();
}
}
}
return instance;
}
}
示例代碼以下:redis
package com.atguigu.singleton;
/* 懶漢式:
* 延遲建立這個實例對象
* 靜態內部類形式(適用於多線程)
*
* 在內部類被加載和初始化時,才建立 INSTANCE 實例對象
* 靜態內部類不會自動隨着外部類的加載和初始化而初始化,它是要單獨去加載和初始化的。(是最懶的)
* 由於是在內部類加載和初始化時,建立的,所以是線程安全的
*/
public class Singleton6 {
private Singleton6() {
}
private static class Inner {
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() {
return Inner.INSTANCE;
}
}
考點
類初始化過程
實例初始化過程
方法的重寫算法
類初始化過程
一個類要建立實例須要先加載並初始化該類
main 方法所在的類須要先加載和初始化
一個子類要初始化須要先初始化父類
一個類初始化就是執行 <clinit>() 方法
<clinit>() 方法由靜態類變量顯示賦值代碼和靜態代碼塊組成
類變量顯示賦值代碼 和 靜態代碼塊代碼 從上到下順序執行
<clinit>() 方法只執行一次
實例初始化過程
實例初始化就是執行 <init>() 方法
<init>() 方法可能重載有多個,有幾個構造器就有幾個 <init> 方法
<init>() 方法由非靜態實例變量顯示賦值代碼和非靜態代碼塊、對應構造器代碼組成
非靜態實例變量顯示賦值代碼和非靜態代碼塊代碼從上到下順序執行,而對應構造器的代碼最後執行
每次建立實例對象,調用對應構造器,執行的就是對應的 <init> 方法
<init> 方法的首行是 super() 或 super(實參列表),即對應父類的 <init> 方法
方法的重寫
哪些方法不能夠被重寫
final 方法
靜態方法
private 等子類中不可見方法
對象的多態性
子類若是重寫了父類的方法,經過子類對象調用的必定是子類重寫過的代碼
非靜態方法默認的調用對象是 this
this 對象在構造器或者說 <init> 方法中就是正在建立的對象
Override 和 Overload 的區別?
Override 重寫的要求?
方法名
形參列表
返回值類型
拋出的異常列表
修飾符
瞭解《JVM 虛擬機規範》中關於 <clinit> 和 <init> 方法的說明、invokespecial 指令
示例代碼以下:
package com.atguigu.init;
/*
* 父類的初始化 <clinit>:
* (1)父類的靜態方法 j = method();
* (2)父類的靜態代碼塊
*
*
*
* -----------------------------------------
* 父類的實例化 <init>:
* (1)super()(最前)
* (2)i = test();
* (3)父類的非靜態代碼塊
* (4)父類的無參構造(最後)
*
* 非靜態方法前面其實有一個默認的對象 this
* this 在構造器(或 <init>)它表示的是正在建立的對象,
* 由於這裏是在建立 Son 對象,因此 test() 執行的是子類重寫的代碼(面向對象+多態)
*
* 這裏 i=test() 執行的是子類重寫的 test() 方法
*/
public class Father {
private int i = test();
private static int j = method();
static {
System.out.print("(1)");
}
Father() {
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test() {
System.out.print("(4)");
return 1;
}
public static int method() {
System.out.print("(5)");
return 1;
}
}
示例代碼以下:
package com.atguigu.init;
/*
* 子類的初始化 <clinit>:
* (1)子類的靜態方法 j = method();
* (2)子類的靜態代碼塊
*
* 先初始化父類的靜態方法和靜態代碼塊:(5)(1)
* 再初始化子類的靜態方法和靜態代碼塊:(10)(6)
* -----------------------------------------
* 子類的實例化 <init>:
* (1)super()(最前) (9)(3)(2)
* (2)i = test(); (9)
* (3)子類的非靜態代碼塊 (8)
* (4)子類的無參構造(最後) (7)
*
* 由於建立了兩個 Son 對象,所以實例化方法 <init> 執行了兩次
* 即又出現了一次 (9)(3)(2)(9)(8)(7)
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.print("(6)");
}
Son() {
// super(); // 寫或不寫都在,在子類構造器中必定會調用父類的構造器
System.out.print("(7)");
}
{
System.out.print("(8)");
}
public int test() {
System.out.print("(9)");
return 1;
}
public static int method() {
System.out.print("(10)");
return 1;
}
public static void main(String[] args) {
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
}
考點
方法的參數傳遞機制
String、包裝類等對象的不可變性
方法的參數傳遞機制
形參是基本數據類型時
傳遞數據值
形參是引用數據類型時
傳遞地址值,本質也是值
特殊的類型:String、包裝類等對象不可變性
示例代碼以下:
package com.atguigu.passvalue;
import java.util.Arrays;
public class Exam4 {
public static void main(String[] args) {
int i = 1;
String str = "hello"; // 在常量池中(jdk1.8 中,常量池在元空間中;jdk1.7 中,常量池在堆中)
Integer num = 200; // 值大於 127,對象在堆中
int[] arr = { 1, 2, 3, 4, 5 }; // 堆中
MyData my = new MyData(); // 堆中
change(i, str, num, arr, my);
System.out.println("i = " + i); // i = 1
System.out.println("str = " + str); // str = hello
System.out.println("num = " + num); // num = 200
System.out.println("arr = " + Arrays.toString(arr)); // arr = [2, 2, 3, 4, 5]
System.out.println("my.a = " + my.a); // my.a = 11
}
public static void change(int j, String s, Integer n, int[] a, MyData m) { // 方法在棧中,執行完畢後銷燬
j += 1; // j = 1
s += "world"; // 產生新的對象
n += 1; // 產生新的對象
a[0] += 1; // a[0] = 2
m.a += 1; // 11
}
}
class MyData {
int a = 10;
}
編程題:有 n 步臺階,一次只能上 1 步或 2 步,共有多少種走法?
遞歸(理解上直觀,效率上可能會差些(針對某些狀況下))
package com.atguigu.step;
import org.junit.Test;
public class TestStep {
@Test
public void test() {
long start = System.currentTimeMillis();
System.out.println(f(40)); // 165580141
long end = System.currentTimeMillis();
System.out.println(end - start); // 308ms
}
// 實現 f(n):求 n 步臺階,一共有幾種走法
public int f(int n) {
if (n < 1) {
throw new IllegalArgumentException(n + "不能小於1");
}
if (n == 1 || n == 2) {
return n;
}
return f(n - 2) + f(n - 1);
}
}
循環迭代
package com.atguigu.step;
import org.junit.Test;
public class TestStep2 {
@Test
public void test() {
long start = System.currentTimeMillis();
System.out.println(loop(40)); // 165580141
long end = System.currentTimeMillis();
System.out.println(end - start); // <1ms
}
public int loop(int n) {
if (n < 1) {
throw new IllegalArgumentException(n + "不能小於1");
}
if (n == 1 || n == 2) {
return n;
}
int one = 2; // 初始化 走到第二級臺階的走法
int two = 1; // 初始化 走到第一級臺階的走法
int sum = 0;
for (int i = 3; i <= n; i++) {
// 最後跨2步 + 最後跨1步的走法
sum = two + one;
two = one;
one = sum;
}
return sum;
}
}
小總結
考點
就近原則
變量的分類
成員變量:類變量、實例變量
局部變量
非靜態代碼塊的執行:每次建立實例對象都會執行
方法的調用規則:調用一次執行一次
局部變量與成員變量的區別:
聲明的位置
局部變量:方法體 {} 中,形參,代碼塊 {} 中
成員變量:類中方法外
類變量:有 static 修飾
實例變量:沒有 static 修飾
修飾符
局部變量:final
成員變量:public、protected、private、final、static、volatile、transient
值存儲的位置
局部變量:棧
實例變量:堆
類變量:方法區
做用域
局部變量:從聲明處開始,到所屬的 } 結束
實例變量:在當前類中 「this.」 (有時 this. 能夠缺省),在其餘類中 「對象名.」 訪問
類變量:在當前類中 「類名.」(有時類名.能夠省略),在其餘類中 「類名.」 或 「對象名.」 訪問
生命週期
局部變量:每個線程,每一次調用執行都是新的生命週期
實例變量:隨着對象的建立而初始化,隨着對象的被回收而消亡,每個對象的實例變量是獨立的
類變量:隨着類的初始化而初始化,隨着類的卸載而消亡,該類的全部對象的類變量是共享的
當局部變量與 xx 變量重名時,如何區分:
局部變量與實例變量重名
在實例變量前面加 「this.」
局部變量與類變量重名
在類變量前面加 「類名.」
Java 運行時數據區
示例代碼以下:
package com.atguigu.variable;
public class Exam5 {
static int s; // 成員變量,類變量
int i; // 成員變量,實例變量
int j; // 成員變量,實例變量
{
int i = 1; // 非靜態代碼塊中的局部變量 i
i++; // i = 2
j++; // j = 1
s++; //
}
public void test(int j) { // 形參,局部變量 j
j++; //
i++; // i = 1
s++; //
}
public static void main(String[] args) { // 形參,局部變量 args
Exam5 obj1 = new Exam5(); // 局部變量 obj1
Exam5 obj2 = new Exam5(); // 局部變量 obj2
obj1.test(10);
obj1.test(20);
obj2.test(30);
System.out.println(obj1.i + "," + obj1.j + "," + obj1.s); // 2,1,5
System.out.println(obj2.i + "," + obj2.j + "," + obj2.s); // 1,1,5
}
}
代碼內存圖解以下:
在 Spring 中,能夠在 <bean>
元素的 scope
屬性裏設置bean的做用域,以決定這個 bean 是單實例的仍是多實例的。
默認狀況下,Spring 只爲每一個在 IOC 容器裏聲明的 bean 建立惟一一個實例(單實例),整個 IOC 容器範圍內都能共享該實例:全部後續的 getBean() 調用和 bean 引用都將返回這個惟一的 bean 實例。該做用域被稱爲 singleton,它是全部 bean 的默認做用域。
當 bean 的做用域爲單例時,Spring 會在 IOC 容器對象建立時就建立 bean 的對象實例。而當 bean 的做用域爲 prototype 時,IOC 容器在獲取 bean 的實例時建立 bean 的實例對象。
當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在本身的事務中運行。
事務的傳播行爲能夠由傳播屬性指定。Spring 定義了 7 種類傳播行爲。
事務的屬性:
1.★ propagation:用來設置事務的傳播行爲
事務的傳播行爲:一個方法運行在了一個開啓了事務的方法中時,當前方法是使用原來的事務仍是開啓一個新的事務
-Propagation.REQUIRED:默認值,使用原來的事務
-Propagation.REQUIRES_NEW:將原來的事務掛起,開啓一個新的事務
2.★ isolation:用來設置事務的隔離級別
-Isolation.REPEATABLE_READ:可重複讀,MySQL默認的隔離級別
-Isolation.READ_COMMITTED:讀已提交,Oracle默認的隔離級別,開發時一般使用的隔離級別
數據庫事務併發問題
假設如今有兩個事務:Transaction01 和 Transaction02 併發執行。1) 髒讀(讀取到了未提交的數據)
① Transaction01 將某條記錄的 AGE 值從 20 修改成 30。
② Transaction02 讀取了 Transaction01 更新後的值:30。
③ Transaction01 回滾,AGE 值恢復到了20。
④ Transaction02 讀取到的 30 就是一個無效的值。2) 不可重複讀(讀取到了已提交的數據)
① Transaction01 讀取了 AGE 值爲 20。
② Transaction02 將 AGE 值修改成 30。
③ Transaction01 再次讀取 AGE 值爲 30,和第一次讀取不一致。3) 幻讀
① Transaction01 讀取了 STUDENT 表中的一部分數據。
② Transaction02 向 STUDENT 表中插入了新的行。
③ Transaction01 讀取了 STUDENT 表時,多出了一些行。
事務的隔離級別
數據庫系統必須具備隔離併發運行各個事務的能力,使它們不會相互影響,避免各類併發問題。一個事務與其餘事務隔離的程度稱爲隔離級別。SQL 標準中規定了多種事務隔離級別,不一樣隔離級別對應不一樣的干擾程度,隔離級別越高,數據一致性就越好,但併發性越弱。1) 讀未提交:READ UNCOMMITTED
容許 Transaction01 讀取 Transaction02 未提交的修改。2) 讀已提交:READ COMMITTED
要求 Transaction01 只能讀取 Transaction02 已提交的修改。3) 可重複讀:REPEATABLE READ
確保 Transaction01 能夠屢次從一個字段中讀取到相同的值,即 Transaction01 執行期間禁止其它事務對這個字段進行更新。4) 串行化:SERIALIZABLE
確保 Transaction01 能夠屢次從一個表中讀取到相同的行,在 Transaction01 執行期間,禁止其它事務對這個表進行添加、更新、刪除操做。能夠避免任何併發問題,但性能十分低下。5) 各個隔離級別解決併發問題的能力見下表
6) 各類數據庫產品對事務隔離級別的支持程度
在 web.xml 中配置一個過濾器
<filter>
<!-- SpringMVC 中如何解決 POST 請求中文亂碼問題 -->
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!-- POST 請求中文亂碼問題 -->
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<!-- POST 響應中文亂碼問題 -->
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
方法一:稍微修改 tomcat 的配置文件 server.xml,修改第 65 行代碼
方法二:參考百度
第一種解決方案:寫 sql 語句時起別名。別名和數據庫表中的字段名同樣便可。
第二種解決方案:在 MyBatis 的全局配置文件中開啓駝峯命名規則。能夠將數據庫中下劃線映射爲駝峯命名。(有侷限性)
第三種解決方案:在 Mapper 的映射文件中使用 resultMap 來自定義映射規則。
CentOS 6
運行級別
CentOS 7
Git 工做流
Redis 提供了 2 種不一樣形式的持久化方式:
RDB(Redis Database):全量替換
AOF(Append Of File):增量追加
GC 發生在 JVM 哪個部分,有幾種 GC,它們的算法是什麼
答:堆中。2 種:Minor GC(新生代)、 Full GC(老年代)。引用計數算法、複製算法、標記清除算法、標記壓縮算法、標記清除壓縮算法。
引用計數算法(已經淘汰了,由於沒有辦法處理循環引用)
複製算法(新生代中使用的是 Minor GC)
標記清除算法(老年代通常是由標記清除或者標記清除與標記整理的混合實現)
標記壓縮算法(老年代通常是由標記清除或者標記清除與標記整理的混合實現)
標記清除壓縮算法
背景:它們都是基於 Lucene 搜索服務器基礎之上開發,一款優秀的,高性能
的企業級搜索服務器。【由於它們都是基於分詞技術構建的倒排索引的方式進行查詢
】
開發語言:java 語言開發
誕生時間:
Solr:2004年誕生。
Elasticsearch:2010年誕生。更新、功能更強大。
區別:
一、當實時創建索引的時候,solr 會產生 io 阻塞,而 es 則不會,es 查詢性能要高於 solr。
二、在不斷動態添加數據的時候,solr 的檢索效率會變的低下,而 es 則沒有什麼變化。
三、Solr 利用 zookeeper 進行分佈式管理,而 es 自身帶有分佈式系統管理功能。Solr 通常都要部署到 web 服務器上,好比 tomcat。啓動 tomcat 的時候須要配 tomcat 與 solr 的關聯。【由於 Solr 的本質 是一個動態 web 項目
】
四、Solr 支持更多的格式數據 [xml,json,csv等],而 es 僅支持 json 文件格式。
五、Solr 是傳統搜索應用的有力解決方案,可是 es 更適用於新興的實時搜索應用。
a) 若是單純的對已有數據進行檢索的時候,solr 效率更好,高於 es。
六、Solr 官網提供的功能更多,而 es 自己更注重於核心功能(即檢索),高級功能多有第三方插件。
Solr 利用 zookeeper 進行分佈式管理,部署在 tomcat 上
SolrCloud:集羣圖
Elasticsearch:集羣圖
單點登陸:一處登陸多處使用!
前提:單點登陸多使用在分佈式系統中。
Demo:
參觀動物園流程:
檢票員=認證中心模塊
一、我直接帶着你們進動物園,則會被檢票員攔住【看咱們是否有門票】,沒有[售票處買票]
登陸=買票
二、我去買票【帶着票,帶着你們一塊兒準備進入動物園】檢票員 check【有票】
Token=piao
3.咱們手中有票就能夠任意觀賞動物的每處景點。
京東:單點登陸,是將 token 放入到 cookie 中的。
案例:若是將瀏覽器的 cookie 禁用,則在登陸京東後就失敗!即不管如何登陸不了!
購物車:
1. 購物車跟用戶的關係?
a) 一個用戶必須對應一個購物車【一個用戶無論買多少商品,都會存在屬於本身的購物車中。】
b) 單點登陸的開發必定在開發購物車以前。
2. 跟購物車有關的操做有哪些?
a) 添加購物車
i.用戶未登陸狀態
1. 添加到什麼地方?未登陸將數據保存到什麼地方?
a) Redis ? --> 京東使用
b) Cookie ? --> 本身開發項目的時候【若是瀏覽器禁用 cookie,則京東登陸不上】
ii.用戶登陸狀態
1. Redis 緩存中【讀寫速度快】
a) Hash:hset(key,field,value)
i. key=user:userId:cart
ii. Hset(key,skuId,value);
2. 存在數據庫中【oracle、mysql,保證數據安全性】
b) 展現購物車
i.未登陸狀態展現
1.直接從 cookie 中取得數據進行展現便可
ii.登陸狀態
1.用戶一旦登陸:必須顯示數據庫 【redis】+【cookie】 中的購物車的數據
a) Cookie 中有三條記錄
b) Redis 中有五條記錄
c) 合併後,真正展現的時候應該是小於或等於八條記錄
背景:在分佈式系統中是如何處理高併發的。
因爲在高併發的環境下,來不及同步處理用戶發送的請求,則會致使請求發生阻塞。好比說,大量的 insert,update 之類的請求同時到達數據庫 MySQL,直接致使無數的行鎖表鎖,甚至會致使請求堆積不少。從而觸發 too many connections 錯誤。使用消息隊列能夠解決【異步通訊】
一、異步
二、並行
三、排隊
消息隊列電商使用場景:
消息隊列的弊端: 解決消息的不肯定性:延遲隊列 和 輪詢技術來解決,好比:訂單模塊 定時向 支付寶 查詢當前訂單是否支付成功。 推薦你們使用 activemq!由於環境都是 java。