微信公眾號(hào):慕容千語(yǔ)的架構(gòu)筆記。歡迎關(guān)注一起進(jìn)步。

          1. 如何用數(shù)組實(shí)現(xiàn)隊(duì)列?

          用數(shù)組實(shí)現(xiàn)隊(duì)列時(shí)要注意 溢出 現(xiàn)象,這時(shí)我們可以采用循環(huán)數(shù)組的方式來(lái)解決,即將數(shù)組收尾相接。使用front指針指向隊(duì)列首位,tail指針指向隊(duì)列末位。

          2. 內(nèi)部類訪問(wèn)局部變量的時(shí)候,為什么變量必須加上final修飾?

          因?yàn)樯芷诓煌?。局部變量在方法結(jié)束后就會(huì)被銷毀,但內(nèi)部類對(duì)象并不一定,這樣就會(huì)導(dǎo)致內(nèi)部類引用了一個(gè)不存在的變量。

          所以編譯器會(huì)在內(nèi)部類中生成一個(gè)局部變量的拷貝,這個(gè)拷貝的生命周期和內(nèi)部類對(duì)象相同,就不會(huì)出現(xiàn)上述問(wèn)題。

          但這樣就導(dǎo)致了其中一個(gè)變量被修改,兩個(gè)變量值可能不同的問(wèn)題。為了解決這個(gè)問(wèn)題,編譯器就要求局部變量需要被final修飾,以保證兩個(gè)變量值相同。


          在JDK8之后,編譯器不要求內(nèi)部類訪問(wèn)的局部變量必須被final修飾,但局部變量值不能被修改(無(wú)論是方法中還是內(nèi)部類中),否則會(huì)報(bào)編譯錯(cuò)誤。利用javap查看編譯后的字節(jié)碼可以發(fā)現(xiàn),編譯器已經(jīng)加上了final。

          3. long s = 499999999 * 499999999 在上面的代碼中,s的值是多少?

          根據(jù)代碼的計(jì)算結(jié)果,s的值應(yīng)該是-1371654655,這是由于Java中右側(cè)值的計(jì)算默認(rèn)是int類型。

          4. NIO相關(guān),Channels、Buffers、Selectors

          NIO(Non-blocking IO)為所有的原始類型提供(Buffer)緩存支持,字符集編碼解碼解決方案。 Channel :一個(gè)新的原始I/O 抽象。
          支持鎖和內(nèi)存映射文件的文件訪問(wèn)接口。提供多路(non-bloking) 非阻塞式的高伸縮性網(wǎng)絡(luò)I/O 。

          IO NIO
          面向流 面向緩沖
          阻塞IO 非阻塞IO
          無(wú) 選擇器
          流與緩沖

          Java NIO和IO之間第一個(gè)最大的區(qū)別是,IO是面向流的,NIO是面向緩沖區(qū)的。 Java
          IO面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),它們沒(méi)有被緩存在任何地方。此外,它不能前后移動(dòng)流中的數(shù)據(jù)。如果需要前后移動(dòng)從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)。

          Java
          NIO的緩沖導(dǎo)向方法略有不同。數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動(dòng)。這就增加了處理過(guò)程中的靈活性。但是,還需要檢查是否該緩沖區(qū)中包含所有您需要處理的數(shù)據(jù)。而且,需確保當(dāng)更多的數(shù)據(jù)讀入緩沖區(qū)時(shí),不要覆蓋緩沖區(qū)里尚未處理的數(shù)據(jù)。

          阻塞與非阻塞IO

          Java IO的各種流是阻塞的。這意味著,當(dāng)一個(gè)線程調(diào)用read() 或
          write()時(shí),該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。 Java
          NIO的非阻塞模式,是線程向某通道發(fā)送請(qǐng)求讀取數(shù)據(jù),僅能得到目前可用的數(shù)據(jù),如果目前沒(méi)有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取,當(dāng)然它不會(huì)保持線程阻塞。所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。
          非阻塞寫也是如此。所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道。

          選擇器(Selectors)

          Java NIO 的
          選擇器允許一個(gè)單獨(dú)的線程來(lái)監(jiān)視多個(gè)輸入通道,你可以注冊(cè)多個(gè)通道使用一個(gè)選擇器,然后使用一個(gè)單獨(dú)的線程來(lái)“選擇”通道:這些通道里已經(jīng)有可以處理的輸入,或者選擇已準(zhǔn)備寫入的通道。這種選擇機(jī)制,使得一個(gè)單獨(dú)的線程很容易來(lái)管理多個(gè)通道。

          5. 反射的用途

          Java反射機(jī)制可以讓我們?cè)诰幾g期(Compile
          Time)之外的運(yùn)行期(Runtime)檢查類,接口,變量以及方法的信息。反射還可以讓我們?cè)谶\(yùn)行期實(shí)例化對(duì)象,調(diào)用方法,通過(guò)調(diào)用get/set方法獲取變量的值。同時(shí)我們也可以通過(guò)反射來(lái)獲取泛型信息,以及注解。還有更高級(jí)的應(yīng)用–動(dòng)態(tài)代理和動(dòng)態(tài)類加載(ClassLoader.loadclass())。

          下面列舉一些比較重要的方法:

          * getFields:獲取所有 public 的變量。
          * getDeclaredFields:獲取所有包括 private , protected 權(quán)限的變量。
          * setAccessible:設(shè)置為 true 可以跳過(guò)Java權(quán)限檢查,從而訪問(wèn)private權(quán)限的變量。
          * getAnnotations:獲取注解,可以用在類和方法上。
          獲取方法的泛型參數(shù):
          method = Myclass.class.getMethod("setStringList", List.class); Type[]
          genericParameterTypes = method.getGenericParameterTypes(); for(Type
          genericParameterType : genericParameterTypes){ if(genericParameterType
          instanceof ParameterizedType){ ParameterizedType aType = (ParameterizedType)
          genericParameterType; Type[] parameterArgTypes =
          aType.getActualTypeArguments(); for(Type parameterArgType : parameterArgTypes){
          Class parameterArgClass = (Class) parameterArgType;
          System.out.println("parameterArgClass = " + parameterArgClass); } } }
          動(dòng)態(tài)代理:
          //Main.java public static void main(String[] args) { HelloWorld helloWorld=new
          HelloWorldImpl(); InvocationHandler handler=new HelloWorldHandler(helloWorld);
          //創(chuàng)建動(dòng)態(tài)代理對(duì)象 HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance(
          helloWorld.getClass().getClassLoader(), helloWorld.getClass().getInterfaces(),
          handler); proxy.sayHelloWorld(); } //HelloWorldHandler.java public Object
          invoke(Object proxy, Method method, Object[] args) throws Throwable { Object
          result = null; //調(diào)用之前 doBefore(); //調(diào)用原始對(duì)象的方法 result=method.invoke(obj, args);
          //調(diào)用之后 doAfter(); return result; }
          通過(guò)反射獲取方法注解的參數(shù):
          Class aClass = TheClass.class; Annotation[] annotations =
          aClass.getAnnotations(); for(Annotation annotation : annotations){
          if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation =
          (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name());
          System.out.println("value: " + myAnnotation.value()); } }
          非靜態(tài)內(nèi)部類能定義靜態(tài)方法嗎?
          public class OuterClass{ private static float f = 1.0f; class InnerClass{
          public static float func(){return f;} } }
          以上代碼會(huì)出現(xiàn)編譯錯(cuò)誤,因?yàn)橹挥徐o態(tài)內(nèi)部類才能定義靜態(tài)方法。

          6. Lock 和 Synchronized 有什么區(qū)別?

          使用方法的區(qū)別

          * Synchronized:在需要同步的對(duì)象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號(hào)中表示需要鎖的對(duì)象。
          *
          Lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個(gè)線程中必須要使用一個(gè)ReentrantLock類做為對(duì)象才能保證鎖的生效。且在加鎖和解鎖處需要通過(guò)lock()和unlock()顯示指出。所以一般會(huì)在finally塊中寫unlock()以防死鎖。
          性能的區(qū)別


          synchronized是托管給JVM執(zhí)行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因?yàn)檫@是一個(gè)重量級(jí)操作,需要調(diào)用操作接口,導(dǎo)致有可能加鎖消耗的系統(tǒng)時(shí)間比加鎖以外的操作還多。相比之下使用Java提供的Lock對(duì)象,性能更高一些。但是到了Java1.6,發(fā)生了變化。synchronize在語(yǔ)義上很清晰,可以進(jìn)行很多優(yōu)化,有適應(yīng)自旋,鎖消除,鎖粗化,輕量級(jí)鎖,偏向鎖等等。導(dǎo)致在Java1.6上synchronize的性能并不比Lock差。

          * Synchronized:采用的是CPU悲觀鎖機(jī)制,即線程獲得的是獨(dú)占鎖。獨(dú)占鎖意味著
          其他線程只能依靠阻塞來(lái)等待線程釋放鎖。而在CPU轉(zhuǎn)換線程阻塞時(shí)會(huì)引起線程上下文切換,當(dāng)有很多線程競(jìng)爭(zhēng)鎖的時(shí)候,會(huì)引起CPU頻繁的上下文切換導(dǎo)致效率很低。
          *
          Lock:用的是樂(lè)觀鎖方式。所謂樂(lè)觀鎖就是,每次不加鎖而是假設(shè)沒(méi)有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試,直到成功為止。樂(lè)觀鎖實(shí)現(xiàn)的機(jī)制就是CAS操作。我們可以進(jìn)一步研究ReentrantLock的源代碼,會(huì)發(fā)現(xiàn)其中比較重要的獲得鎖的一個(gè)方法是compareAndSetState。這里其實(shí)就是調(diào)用的CPU提供的特殊指令。
          ReentrantLock:具有更好的可伸縮性:比如時(shí)間鎖等候、可中斷鎖等候、無(wú)塊結(jié)構(gòu)鎖、多個(gè)條件變量或者鎖投票。

          7. float 變量如何與 0 比較?

          folat類型的還有double類型的,這些小數(shù)類型在趨近于0的時(shí)候直接等于0的可能性很小,一般都是無(wú)限趨近于0,因此不能用==來(lái)判斷。應(yīng)該用|x-0|
          //用程序表示就是 fabs(x) < 0.00001f
          8. 如何新建非靜態(tài)內(nèi)部類?

          內(nèi)部類在聲明的時(shí)候必須是 Outer.Inner a,就像int a 一樣,至于靜態(tài)內(nèi)部類和非靜態(tài)內(nèi)部類new的時(shí)候有點(diǎn)區(qū)別:

          * Outer.Inner a = new Outer().new Inner()(非靜態(tài),先有Outer對(duì)象才能 new 內(nèi)部類)
          * Outer.Inner a = new Outer.Inner()(靜態(tài)內(nèi)部類)
          9. Java標(biāo)識(shí)符命名規(guī)則

          可以包含:字母、數(shù)字、$、_(下劃線),不可用數(shù)字開頭,不能是 Java 的關(guān)鍵字和保留字。

          11. 你知道哪些JDK中用到的設(shè)計(jì)模式?

          * 裝飾模式:java.io
          * 單例模式:Runtime類
          * 簡(jiǎn)單工廠模式:Integer.valueOf方法
          * 享元模式:String常量池、Integer.valueOf(int i)、Character.valueOf(char c)
          * 迭代器模式:Iterator
          * 職責(zé)鏈模式:ClassLoader的雙親委派模型
          * 解釋器模式:正則表達(dá)式j(luò)ava.util.regex.Pattern
          12. ConcurrentHashMap如何保證線程安全

          JDK 1.7及以前:


          ConcurrentHashMap允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù)。它使用了多個(gè)鎖來(lái)控制對(duì)hash表的不同部分進(jìn)行的修改。ConcurrentHashMap內(nèi)部使用段(Segment)來(lái)表示這些不同的部分,每個(gè)段其實(shí)就是一個(gè)小的hash
          table,它們有自己的鎖。只要多個(gè)修改操作發(fā)生在不同的段上,它們就可以并發(fā)進(jìn)行。

          JDK 1.8:

          Segment雖保留,但已經(jīng)簡(jiǎn)化屬性,僅僅是為了兼容舊版本。

          插入時(shí)使用CAS算法:unsafe.compareAndSwapInt(this, valueOffset, expect, update)。
          CAS(Compare And
          Swap)意思是如果valueOffset位置包含的值與expect值相同,則更新valueOffset位置的值為update,并返回true,否則不更新,返回false。插入時(shí)不允許key或value為null

          與Java8的HashMap有相通之處,底層依然由“數(shù)組”+鏈表+紅黑樹;

          底層結(jié)構(gòu)存放的是TreeBin對(duì)象,而不是TreeNode對(duì)象;

          CAS作為知名無(wú)鎖算法,那ConcurrentHashMap就沒(méi)用鎖了么?當(dāng)然不是,當(dāng)hash值與鏈表的頭結(jié)點(diǎn)相同還是會(huì)synchronized上鎖,鎖鏈表。

          13. i++在多線程環(huán)境下是否存在問(wèn)題,怎么解決?


          雖然遞增操作++i是一種緊湊的語(yǔ)法,使其看上去只是一個(gè)操作,但這個(gè)操作并非原子的,因而它并不會(huì)作為一個(gè)不可分割的操作來(lái)執(zhí)行。實(shí)際上,它包含了三個(gè)獨(dú)立的操作:讀取count的值,將值加1,然后將計(jì)算結(jié)果寫入count。這是一個(gè)“讀取
          - 修改 - 寫入”的操作序列,并且其結(jié)果狀態(tài)依賴于之前的狀態(tài)。所以在多線程環(huán)境下存在問(wèn)題。

          要解決自增操作在多線程環(huán)境下線程不安全的問(wèn)題,可以選擇使用Java提供的原子類,如AtomicInteger或者使用synchronized同步方法。

          14. new與newInstance()的區(qū)別

          new是一個(gè)關(guān)鍵字,它是調(diào)用new指令創(chuàng)建一個(gè)對(duì)象,然后調(diào)用構(gòu)造方法來(lái)初始化這個(gè)對(duì)象,可以使用帶參數(shù)的構(gòu)造器


          newInstance()是Class的一個(gè)方法,在這個(gè)過(guò)程中,是先取了這個(gè)類的不帶參數(shù)的構(gòu)造器Constructor,然后調(diào)用構(gòu)造器的newInstance方法來(lái)創(chuàng)建對(duì)象。


          Class.newInstance不能帶參數(shù),如果要帶參數(shù)需要取得對(duì)應(yīng)的構(gòu)造器,然后調(diào)用該構(gòu)造器的Constructor.newInstance(Object
          … initargs)方法

          15. 你了解哪些JDK1.8的新特性?

          * 接口的默認(rèn)方法和靜態(tài)方法,JDK8允許我們給接口添加一個(gè)非抽象的方法實(shí)現(xiàn),只需要使用default關(guān)鍵字即可。也可以定義被static修飾的靜態(tài)方法。
          * 對(duì)HashMap進(jìn)行了改進(jìn),當(dāng)單個(gè)桶的元素個(gè)數(shù)大于6時(shí)就會(huì)將實(shí)現(xiàn)改為紅黑樹實(shí)現(xiàn),以避免構(gòu)造重復(fù)的hashCode的攻擊
          * 多并發(fā)進(jìn)行了優(yōu)化。如ConcurrentHashMap實(shí)現(xiàn)由分段加鎖、鎖分離改為CAS實(shí)現(xiàn)。
          * JDK8拓寬了注解的應(yīng)用場(chǎng)景,注解幾乎可以使用在任何元素上,并且允許在同一個(gè)地方多次使用同一個(gè)注解
          * Lambda表達(dá)式
          16. 你用過(guò)哪些JVM參數(shù)?

          * Xms 堆最小值
          * Xmx 堆最大值
          * Xmn: 新生代容量
          * XX:SurvivorRatio 新生代中Eden與Surivor空間比例
          * Xss 棧容量
          * XX:PermSize 方法區(qū)初始容量
          * XX:MaxPermSize 方法區(qū)最大容量
          * XX:+PrintGCDetails 收集器日志參數(shù)
          17. 如何打破 ClassLoader 雙親委托?

          重寫loadClass()方法。

          18. hashCode() && equals()

          hashcode() 返回該對(duì)象的哈希碼值,支持該方法是為哈希表提供一些優(yōu)點(diǎn),例如,java.util.Hashtable 提供的哈希表。

          在 Java 應(yīng)用程序執(zhí)行期間,在同一對(duì)象上多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù),前提是對(duì)象上 equals
          比較中所用的信息沒(méi)有被修改(equals默認(rèn)返回對(duì)象地址是否相等)。如果根據(jù)
          equals(Object)方法,兩個(gè)對(duì)象是相等的,那么在兩個(gè)對(duì)象中的每個(gè)對(duì)象上調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果。

          以下情況不是必需的:如果根據(jù) equals(java.lang.Object) 方法,兩個(gè)對(duì)象不相等,那么在兩個(gè)對(duì)象中的任一對(duì)象上調(diào)用 hashCode
          方法必定會(huì)生成不同的整數(shù)結(jié)果。但是,程序員應(yīng)該知道,為不相等的對(duì)象生成不同整數(shù)結(jié)果可以提高哈希表的性能。

          實(shí)際上,由 Object 類定義的 hashCode 方法確實(shí)會(huì)針對(duì)不同的對(duì)象返回不同的整數(shù)。(這一般是通過(guò)將該對(duì)象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)來(lái)實(shí)現(xiàn)的,但是
          JavaTM 編程語(yǔ)言不需要這種實(shí)現(xiàn)技巧I。)

          hashCode的存在主要是用于查找的快捷性,如 Hashtable,HashMap等,hashCode 是用來(lái)在散列存儲(chǔ)結(jié)構(gòu)中確定對(duì)象的存儲(chǔ)地址的;

          如果兩個(gè)對(duì)象相同,就是適用于 equals(java.lang.Object) 方法,那么這兩個(gè)對(duì)象的 hashCode 一定要相同;

          如果對(duì)象的 equals 方法被重寫,那么對(duì)象的 hashCode 也盡量重寫,并且產(chǎn)生 hashCode 使用的對(duì)象,一定要和 equals
          方法中使用的一致,否則就會(huì)違反上面提到的第2點(diǎn);

          兩個(gè)對(duì)象的hashCode相同,并不一定表示兩個(gè)對(duì)象就相同,也就是不一定適用于equals(java.lang.Object)
          方法,只能夠說(shuō)明這兩個(gè)對(duì)象在散列存儲(chǔ)結(jié)構(gòu)中,如Hashtable,他們“存放在同一個(gè)籃子里”。

          19. Thread.sleep() & Thread.yield()

          sleep()和yield()都會(huì)釋放CPU。


          sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài),所以執(zhí)行sleep()的線程在指定的時(shí)間內(nèi)肯定不會(huì)執(zhí)行;yield()只是使當(dāng)前線程重新回到可執(zhí)行狀態(tài),所以執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行。

          sleep()可使優(yōu)先級(jí)低的線程得到執(zhí)行的機(jī)會(huì),當(dāng)然也可以讓同優(yōu)先級(jí)和高優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì);yield()只能使同優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì)。

          20. #{}和${}的區(qū)別是什么?

          {}是預(yù)編譯處理,${}是字符串替換。

          Mybatis在處理#{}時(shí),會(huì)將sql中的#{}替換為?號(hào),調(diào)用PreparedStatement的set方法來(lái)賦值;
          Mybatis在處理${}時(shí),就是把${}替換成變量的值。
          使用#{}可以有效的防止SQL注入,提高系統(tǒng)安全性。

          21. 通常一個(gè)Xml映射文件,都會(huì)寫一個(gè)Dao接口與之對(duì)應(yīng),請(qǐng)問(wèn),這個(gè)Dao接口的工作原理是什么?Dao接口里的方法,參數(shù)不同時(shí),方法能重載嗎?


          Dao接口,就是人們常說(shuō)的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法內(nèi)的參數(shù),就是傳遞給sql的參數(shù)。Mapper接口是沒(méi)有實(shí)現(xiàn)類的,當(dāng)調(diào)用接口方法時(shí),接口全限名+方法名拼接字符串作為key值,可唯一定位一個(gè)MappedStatement,舉例:com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace為com.mybatis3.mappers.StudentDao下面id
          = findStudentById的MappedStatement。在Mybatis中,每一個(gè)、、、標(biāo)簽,都會(huì)被解析為一個(gè)MappedStatement對(duì)象。

          Dao接口里的方法,是不能重載的,因?yàn)槭侨廾?方法名的保存和尋找策略。


          Dao接口的工作原理是JDK動(dòng)態(tài)代理,Mybatis運(yùn)行時(shí)會(huì)使用JDK動(dòng)態(tài)代理為Dao接口生成代理proxy對(duì)象,代理對(duì)象proxy會(huì)攔截接口方法,轉(zhuǎn)而執(zhí)行MappedStatement所代表的sql,然后將sql執(zhí)行結(jié)果返回。

          歡迎關(guān)注微信公眾號(hào):慕容千語(yǔ)的架構(gòu)筆記 一起學(xué)習(xí)提升

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問(wèn)題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                九色|PORNY|蝌蚪视频 | 欧美日韩国产伦理 | 按摩高潮japanesevideo | 乱伦一级毛片免费播放 | 欧美日韩一区精品 |