面試答辯

qq_43207114發表於2020-10-27

面試介紹

專案

我最近做了一個cms內容管理系統的專案,他是一個位於Web前端與創作或編輯的後端之間的軟體系統,這裡的內容可以指創作人員,編輯人員,釋出人員來提交、修改、審批、釋出內容,這裡的“內容”可能包括檔案、圖片、表格、資料庫中的資料甚至可以是視訊等一切你想要傳送到Internet以及Extranet網站的資訊。

面試可能遇到的問題

Spring常用註解

例項化物件的: @Controller,@Service, @Repository[它用於將資料訪問層 (DAO 層 ) 的類標識為 Spring Bean], @Component(當類不屬於任何歸類的時候可以使用它,可以認為是你不認識是什麼類的時候,但又需要將此類例項化為bean物件可以使用它)
注:以上註解的使用條件是需要掃描報的路徑,在xml配置檔案裡寫入<context:component-scan/>可以實現

  1. 注入註解:@Autowired[SpringMVC的註解]
    @Resource[Javaee的註解,對spring也可支援]

  2. 匹配請求的註解:@RequestMapping 通過它來指定控制器可以處理哪些URL請求 (需要開啟Spring對Mvc的支援)

  3. 響應ajax請求的註解:@ResponseBody 將響應的資料轉換成json格式,並且使用它標註的方法返回值不會經過試圖解析器

SpringMVC的執行流程

所有的請求 -> 前端控制器 -> HandlerMapping[找到處理器Handler] -> HandlerApadtor[找到處理Handler的介面卡] -> 執行Handler處理請求【返回ModelAndView物件】 -> 檢視解析器 -> 響應給客戶端

xml中的大致配置

web.xml: 前端控制器+過濾器處理post請求中文亂碼【get請求不需要處理,tomcat8預設使用UTF-8進行編碼,tomcat7及其他之前版本需要配置】+監聽器【載入spring配置檔案】
spring配置檔案:管理屬性檔案jdbc.properties+管理連線池+管理SessionFactory+管理Mapper介面
SpringMVC配置檔案:掃描包路徑+開啟Spring對MVC的支援+檢視解析器+靜態資源放行

#與$的區別

#將傳入的資料都當成一個字串,會對自動傳入的資料加一個雙引號;
$將傳入的資料直接顯示生成在sql中;

  1. #方式能夠很大程度防止sql注入,$方式無法防止Sql注入。
  2. $方式一般用於傳入資料庫物件,例如傳入表名。
  3. 從安全性上考慮,能使用#儘量使用#來傳參,因為這樣可以有效防止SQL隱碼攻擊的問題。

get請求,post請求區別

主要通過看請求的引數在哪判斷
在位址列就是get請求,不安全,有大小限制
在請求資料包的實體內容中,相對安全,資料大小無限制,所以上傳檔案要用post請求

Cookie和Session的區別

主要是看資料儲存在哪啊
Cookie:資料儲存在瀏覽器 不安全,資料型別和大小都有限制,但是減輕了伺服器的壓力
Session:資料儲存在伺服器 安全,資料型別和大小沒有限制,資料量過多會影響伺服器的壓力

頁面靜態化技術的原理

見名知意就是將動態頁面靜態化的一個技術
使用模板技術freemarker,基於資料和模板.ftl生成靜態頁面

分頁如何實現的

使用gm提供的方法setQuery()將gm自帶的分頁引數和高階查詢的資料傳遞到後臺,後臺通過動態sql實現動態查詢,再使用limit對查詢到的資料進行分頁查詢,然後將查詢到的資料使用gm的屬性顯示再表格中;
limit a,b 引數a表示當前頁面顯示資料的下標或索引可以使用(當前頁 - 1)* pageSize計算得到
引數b表示每一頁顯示的條數

動態sql用過哪些

if,where,sql,include

基礎面試題

StringBuilder和StringBuffer區別

StringBuilder是執行緒不安全的,而StringBuffer的執行緒安全。

為什麼StringBuilder是執行緒不安全,StringBuffer是執行緒安全?

StringBuilder與StringBuffer的內部實現與String一樣,都是通過char型別的陣列來儲存字串。StringBuilder和StringBuffer都繼承了AbstractStringBuilder,現做一個測試:

import org.junit.Test;
 
/**
 * stringbuilder test
 *
 * @author admin
 */
public class StringBuilderTest {
 
    @Test
    public void test() throws InterruptedException {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    stringBuilder.append("a");
                }
            }).start();
        }
 
        Thread.sleep(500L);
        System.out.println(stringBuilder.length());
    }
}

輸出結果:

Exception in thread "Thread-84" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.String.getChars(String.java:826)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
	at java.lang.StringBuilder.append(StringBuilder.java:136)
	at test.wby.dingtalk.StringBuilderTest.lambda$test$0(StringBuilderTest.java:19)
	at java.lang.Thread.run(Thread.java:748)
94465

我們的期望值是100000但是輸出的卻比期望值小而且還出了異常;

為什麼值不一樣,先看一看StringBuilder的append方法

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

可知他是整合了父類AbstractStringBuilder的append()方法

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

可以看到count += len不是一個原子操作(所謂原子操作是指不會被執行緒排程機制打斷的操作;這種操作一旦開始,就一直執行到結束,中間不會有任何 context switch (切換到另一個執行緒)),所以假設這時count的值為10,len的值為1,兩個執行緒同時執行到這兒的時候拿到的count值都是10,執行完賦值給count那麼兩個都是11,而不會是12,所以輸出值小的;

至於為啥拋異常,我們繼續看AbstractStringBuilder的append()方法原始碼,ensureCapacityInternal()方法是檢查StringBuilder物件的原char陣列的容量能不能盛下新的字串,如果盛不下就呼叫expandCapacity()方法對char陣列進行擴容。

private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
    if (minimumCapacity - value.length > 0)
        expandCapacity(minimumCapacity);
}

擴容機制就是new一個新的陣列將長度設定為原來陣列的兩倍加2,然後將原陣列資料複製到這個新的陣列當中。

拷貝流程:

假設現在有兩個執行緒同時執行了StringBuilder的append方法,兩個執行緒執行到line5的ensureCapacityInternal()方法,此時count=5
假設現在有兩個執行緒同時執行了StringBuilder的append方法,兩個執行緒執行到line5的ensureCapacityInternal()方法,此時count=5
這時候執行緒1的時間片用完了,執行緒2繼續執行,當執行緒2執行完整個append()方法的時候count就變成6了
這時候執行緒1的時間片用完了,執行緒2繼續執行,當執行緒2執行完整個append()方法的時候count就變成6了然後執行緒1再繼續執行第六行的資料的時候再拿到的count的值已經是6了,執行char陣列拷貝的時候就會丟擲ArrayIndexOutOfBoundsException異常。

再來看看StringBuffer的append()方法,其中toStringCache就是當改變就清空,接下來看呼叫了父類的append的方法【注意用synchronized修飾了,執行緒安全】這就是與StringBuilder的區別

 @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

看到他呼叫了父類的append()方法

   public AbstractStringBuilder append(CharSequence s) {
        if (s == null)
            return appendNull();
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof AbstractStringBuilder)
            return this.append((AbstractStringBuilder)s);

        return this.append(s, 0, s.length());
    }

如果引數s為null返回appendnull();該方法最終返回return this.append(s, 0, s.length());接下來我們看到當拼接的物件為String型別的時候就走到append((String)s)方法

      public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

如果引數s為空返回appendNull(); 該方法最終返回return this;接下來看ensureCapacityInternal()方法,傳入的count+len是拼接字串的總長度

 private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

我們可以看到 ,copyOf(char[] original, int newLength)的方法查JDK幫助文件可知: 複製指定的陣列,複製具有指定的長度。也就是說 ensureCapacityInternal()方法其實是重新複製了一個新陣列,將長度擴大成傳入的引數長度,並重新賦值給value

繼續看到String的getChars()方法

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {//0,len=5,value=[hello],count=5
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

最終呼叫的是System.arraycopy的方法:將指定源陣列中的陣列從指定位置複製到目標陣列的指定位置。

    public static void arraycopy(Object src,
                                 int srcPos,
                                 Object dest,
                                 int destPos,
                                 int length)
    /*src - 源陣列。
     srcPos - 源陣列中的起始位置。
     dest - 目標陣列。
     destPos - 目的地資料中的起始位置。
     length - 要複製的陣列元素的數量。 
     */
    System.arraycopy([world], 0, [hello], 5, 5);

以上可知,append方法其實是建立了一個新的陣列,擴大了長度,將需要新增的字串給複製到這個新的陣列中去。

八個基本型別

整數:byte,short,int,long
小數:float,double
字元類:char
布林型別:boolean

處理中文亂碼

專案一中使用過濾器(request的請求的都攔下來先強制設定統一的字符集編碼)處理post請求的中文引數亂碼問題,get請求器不用處理,因為用的是tomcat8,預設使用UTF-8過濾器原理

過濾器原理

過濾器實際上就是對web資源進行攔截,做一些處理後再交給下一個過濾器或servlet處理
通常都是用來攔截request進行處理的,也可以對返回的response進行攔截處理

SpringMvc攔截器

先來一張簡明知意的圖片
攔截器流程SpringMVC攔截器實現的兩種方式
第一種是要定義Interceptor類要實現了Spring的HandlerInterceptor介面
第二種是繼承實現了HaandlerInterceptor介面的類

HandlerInterceptor介面中定義了三個方法,我們通過這三個方法來對使用者請求進行攔截處理,在我的cms專案中只使用了preHandle()方法
preHandle(): 這個方法在業務處理器處理請求之前被呼叫;
postHandle():這個方法在當前請求進行處理之後;
afterCompletion():該方法也是需要當前對應的Interceptor 的preHandle 方法的返回值為true 時才會執行。顧名思義,該方法將在整個請求結束之後,也就是在DispatcherServlet 渲染了對應的檢視之後執行。

MVC思想

model 模型,view檢視,controller控制
任何語言的設計都需要這三個部分,使用者接觸的軟體就是通過試圖(頁面或客戶端)
檢視中的功能需要實現,必須使用功能model實現,但是model和viwe不能直接互動,必須通過controller。三者缺一不可。

三層架構

controller表現層或控制層【以前的servlet+jsp,現在的SpringMVC】
service層業務層,處理業務邏輯判斷的
持久化層(以前的dao用jdbc,現在用mybatis,mapper)

常見的響應狀態碼有哪些,各是什麼原因

① 200:請求成功,瀏覽器會把響應體內容(通常是html)顯示在瀏覽器中;
② 404:(客戶端問題)請求的資源沒有找到,說明客戶端錯誤的請求了不存在的資源;
③ 500:(服務端問題)請求資源找到了,但伺服器內部發生了不可預期的錯誤,相當於後臺程式碼錯誤;
④ 301/302/303:(網站搬家了,跳轉)重定向
⑤ 304: Not Modified,代表上次的文件已經被快取了,還可以繼續使用。如果你不想使用本地快取可以用Ctrl+F5 強制重新整理頁面

常識:前臺?後臺?前端?後端?

前臺 - 直接給使用者使用者操作的
後臺- 對前臺資訊的維護,後臺管理系統
不管是前臺還是後臺都有前端和後端之分
	前端:html,css,js,jquery,和其他前端框架和外掛
	後端:三層程式碼 + domain+entity等後端java程式碼

相關文章