每日十道面試題(七)

每日十道面試題(七)

每天看一看小知識增加一下

1. 談一談Springboot的啓動類註解

@springbootApplication其實是一個複合註解,它其實裏面由==@SpringbootConfiguration+@EnableAutoConfiguration+@ComponentScan==三大打手,第一個其實就是標明主啓動類是一個配置類,因爲點進去你會發現它就是一個@Configuration的註解,這裏先要聲明瞭解一個javaConfig配置的形式@Configuration+@Bean可以配置到ioc容器中

然後我們接下來重點說剩下兩個,@ComponentScan的功能是掃描符合條件的組件把,具體我們點進去看其實它的功能就是自動掃描並且加載符合條件的組件,講那些@Component或者那些bean加載到ioc容器中,當然我們可以通過basePackges來特定一些包來掃描,如果不指定的話就默認掃描==@ComponentScan該類所在的package進行掃描==

然後講一下@EnableAutoConfiguration,這個最重要,它了。內部有一個@AutoConfigurationPackage+@import,一個是自動配置包,一個是導入自動配置的組件

@AutoConfigurationPackage它裏面註冊了一個bean然後它==new packageImport(metadata).getPackageName(),==這一句話是說明返回了當前主程序的同級和子級的包組件,這也是爲什麼我們一般那些包都和啓動類同級

接下來說@import它導入的是一個AutoConfigurationImportSelector.class這個類又繼承了ImportSelector,這個裏面它加載了WEB/INF/spring.factories外部文件,裏面有很多自動配置的類信息,然後這個自動導入選擇器會選擇符合條件的也就是你需要用到的然後它會自動配置到ioc容器中

它爲什麼能自動配置到ioc去,因爲SpringFactoriesLoader工具類,它是來幫助從外部文件加載配置的,將那個org.springfamework.boot.autofigure.EnabletoConfiguration對應的配置項通過反射實例化爲javaConfig形式注入到ioc容器中,後面開啓了自動配置的功能,幫助尋找配置文件然後加載到ioc容器

2. 你瞭解反射,那你知道動態代理嗎?spring中是用哪一種,爲什麼不用其它的

實際上這裏挖了個坑,就是看你對源碼了不瞭解,

動態代理可以在運行期間動態的獲取目標源的信息,從而執行它的方法,靜態代理是編譯期就確定好的,實質是代碼改變的話,動態代理比較好,不經常變需求的話,靜態代理比較好

下面是jdk動態代理

public class ProxyHandller implements InvocationHandler {
    private Object target;
    public  ProxyHandller(Object target){
        this.target=target;
    }
    public Object getProxy() throws Exception {

        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result=method.invoke(target,args);
        return result;
    }
}

實際jdk是需要代理接口的,通過獲取代理的接口類加載器和接口,來獲取接口類的實例,從而再利用實例調用invoke反射執行接口的方法

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理

而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理

  1. 如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP

  2. 如果目標對象實現了接口,可以強制使用CGLIB實現AOP

  3. 如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

//Cglib動態代理,實現MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
    private Object target;//需要代理的目標對象
    
    //重寫攔截方法
    @Override
    public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {
        System.out.println("Cglib動態代理,監聽開始!");
        Object invoke = method.invoke(target, arr);//方法執行,參數:target 目標對象 arr參數數組
        System.out.println("Cglib動態代理,監聽結束!");
        return invoke;
    }
    //定義獲取代理對象方法
    public Object getCglibProxy(Object objectTarget){
        //爲目標對象target賦值
        this.target = objectTarget;
        Enhancer enhancer = new Enhancer();
        //設置父類,因爲Cglib是針對指定的類生成一個子類,所以需要指定父類
        enhancer.setSuperclass(objectTarget.getClass());
        enhancer.setCallback(this);// 設置回調 
        Object result = enhancer.create();//創建並返回代理對象
        return result;
    }

大致流程和jdk動態代理沒差,只是他有用到enhancer,這只是個工具類方便用來實例成子類對象的,然後繼承父類

spring其實兩種都可以使用,沒有說固定了哪一種,但是有接口的話一般都是默認jdk,因爲強制使用cglib的話還要加一串代碼,所以一般沒有用cglib,而且cglib重寫父類會覆蓋方法,通過修改其字節碼生成子類來處理,個人感覺效率上會比jdk慢很多,畢竟涉及到了字節碼,但是沒有接口的話,spring會強制轉換成cglib代理

有接口但想強制使用CGLIB實現AOP:
(1)添加CGLIB庫,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>

3.講一下SpringMVC的攔截器

實際上就是講HandlerInterceptor這個類,我們可以通過實現這個接口,然後重寫裏面的

preHandle:調用Controller之前執行,可以利用這個做權限啥的攔截比如shiro和Sercurity,

當定義多個攔截器的時候,只要一個返回爲false,Controller就會不執行,

posthandler: 調用Controller之後執行

afterCompletion: 無視異常照常執行,且在在preHandle返回爲true的時候可以執行,比如頁面渲染後

preHandle(true)->preHandle(false)仍然會執行返回true的afterCompletion

這個實現原理和Aop一樣織入日誌和事務,在執行方法前後加上攔截器

4. SpringMVC的攔截器和Filter過濾器的區別

  • 功能相同: 兩者無太大差別,都是先實現攔截,再做自己的操作
  • 容器不同: 攔截器是構建在SpringMVC裏面,Filter是構建在Servlet容器上
  • 便利性不同:攔截器有三個時機,而過濾器僅提供一個方法,如果要實現其它的還要自己去寫,
  • 如果純粹的攔截一些東西的話我覺得過濾器比較好,要是需要返回值或者判斷什麼東西,加上一些操作的話,比如動態代理一樣,加入日誌等我還是推薦攔截器

比如Cloud裏面網關的過濾阿,shiro,springSecuritty的攔截,靜態資源的放行,過濾請求等很多地方都用到

5. 反射中,Class.forName 和ClassLoader區別?

這兩者,都可用來對類進行加載。差別在於:

●Class#forName(…) 方法,除了將類的.class文件加載到JVM中之外,還會對類進行解釋,執行類中的static 塊。

●ClassLoader只幹一-件事情,就是將 .class文件加載到JVM中,不會執行static 中的內容,只有在newInstance纔會去執行static塊。

Class.forName(name, initialize, loader)方法,帶參函數也可控制是否加載static 塊,並且只有調用了newInstance方法才能調用構造函數,創建類的對象。

6. 迭代器Iterator是什麼?和ListIterator有什麼區別

Iterator 接口提供遍歷任何 Collection 的接口。我們可以從一個 Collection 中使用迭代器方法來獲取迭代器實例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允許調用者在迭代過程中移除元素。
Iterator:它是遍歷集合的工具,即我們通常通過Iterator迭代器來遍歷集合。我們說Collection依賴於Iterator,是因爲Collection的實現類都要實現iterator()函數,返回一個Iterator對象。ListIterator是專門爲遍歷List而存在的

而ListIterator繼承了Iterator,擴展了更多關於list的方法

使用範圍不同,Iterator可以迭代所有集合;ListIterator 只能用於List及其子類

APi比較:

1add(E e)  將指定的元素插入列表,插入位置爲迭代器當前位置之前
2set(E e)  迭代器返回的最後一個元素替換參數e
3hasPrevious()  迭代器當前位置,反向遍歷集合是否含有元素
4previous()  迭代器當前位置,反向遍歷集合,下一個元素
5previousIndex()  迭代器當前位置,反向遍歷集合,返回下一個元素的下標
6nextIndex()  迭代器當前位置,返回下一個元素的下標
//使用範圍不同,Iterator可以迭代所有集合;ListIterator 只能用於List及其子類
ListIterator 有 add 方法,可以向 List 中添加對象;Iterator 不能
ListIterator 有 hasPrevious()previous() 方法,可以實現逆向遍歷;Iterator不可以
ListIterator 有 nextIndex()previousIndex() 方法,可定位當前索引的位置;Iterator不可以
ListIterator 有 set()方法,可以實現對 List 的修改;Iterator 僅能遍歷,不能修改

ps:如果map要使用lterator,首先要轉成set,再使用

 Set<Map.Entry<Integer,String>> set = map.entrySet();
 Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
 while (iterator.hasNext()){
     Map.Entry<Integer,String> entry = iterator.next();
     System.out.println(entry.getKey()+"="+entry.getValue());
 }

7. 什麼是守護線程?常見的有哪些?

分爲(用戶)前臺線程和後臺線程,只有前臺線程都結束了,後臺線程纔會慢慢結束,後臺線程就叫做守護線程,比如我們經常用的docker可以後臺運行鏡像,包括服務器掛載jar包可以用後臺線程執行,

Java中把線程設置爲守護線程的方法:在 start 線程之前調用線程的 setDaemon(true) 方法。

  • setDaemon(true) 必須在 start() 之前設置,否則會拋出IllegalThreadStateException異常,該線程仍默認爲用戶線程,繼續執行
  • 守護線程創建的線程也是守護線程
  • 守護線程不應該訪問、寫入持久化資源,如文件、數據庫,因爲它會在任何時間被停止,導致資源未釋放、數據寫入中斷等問題

一般來說常見都有垃圾回收線程也就是gc線程是守護線程

8 . 動態規劃(dp)和遞歸的區別

比如一個例子:斐波那契數列fn=fn-1+fn-2

如果我們使用遞歸也能循環調用自身,通過f2=1,f1=1這出口完成計算,但是每一步都是要算到出口的,就是沒有記錄狀態和結果,過程就會可能計算重複的步驟,dp正是記錄了每一步的結果和狀態,減少了重複計算的時間複雜度

那麼遞歸爲什麼時間比較久?

原因是遞歸調用方法的話,會不停的出棧入棧,這個可想而知

ps:遞歸是通過棧中子幀返回結果,然後出棧返回上一級父幀,個人感覺遞歸還是有點自上向下的,因爲不停往下找,而dp記錄了每一步結果,算下步的話,直接自下而上就可以返回了,這些是個人感覺,可不作討論

9 .闡述靜態變量和實例變量的區別,還有局部變量?

不管創建多少個對象,靜態變量在內存中有且僅有一個;實例變量必須依存於某一實例,需要先創建對象然後通過對象才能訪問到它。靜態變量可以實現讓多個對象共享內存。,局部變量在執行完方法後,局部變量會被清除

靜態變量是類鏈接的時候就開始賦值初值了,實例變量和局部變量在初始化過程,按順序執行

10 .說下原生JDBC操作數據庫流程?

● 第一步:Class.forName()加載數據庫連接驅動

● 第二步:DriverManager.getConnection()獲取數據連接對象;

● 第三步:根據SQL獲取sql會話對象,有2種方式 Statement、PreparedStatement ;

● 第四步:執行SQL,執行SQL前如果有參數值就設置參數值setXXX();

● 第五步:處理結果集;

● 第六步:關閉結果集、關閉會話、關閉連接

public class JdbcTest {
    public static void main(String[] args) throws Exception {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //註冊驅動
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/mybatis";
            String user = "root";
            String password = "root";
            //獲取連接
            con = DriverManager.getConnection(url, user, password);
            String sql = "select * from tb_user where id = ? ";
            //獲取數據庫操作對象
            ps = con.prepareStatement(sql);
            //設置參數
            ps.setLong(1, 1);
            rs = ps.executeQuery();
            //處理結果集
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
                System.out.println(rs.getInt("sex"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //釋放資源
            try {
                if (rs!=null) {
                    rs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (ps!=null) {
                    ps.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (con!=null) {
                    con.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

ps:jdbc這個流程和消息中間件的流程代碼很像,比如我之前學的ActiveMQ,學習方式學會遷移哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章