用數(shù)組實(shí)現(xiàn)隊(duì)列時(shí)要注意 溢出 現(xiàn)象,這時(shí)我們可以采用循環(huán)數(shù)組的方式來解決,即將數(shù)組收尾相接。使用front指針指向隊(duì)列首位,tail指針指向隊(duì)列末位。
我們是2013年至今的成都網(wǎng)站建設(shè)公司,提供網(wǎng)站建設(shè),電商網(wǎng)站設(shè)計(jì)開發(fā),成都外貿(mào)網(wǎng)站制作,響應(yīng)式網(wǎng)頁設(shè)計(jì),小程序設(shè)計(jì)、等服務(wù)。為客戶創(chuàng)造有價(jià)值的品牌營銷體驗(yàn),讓互聯(lián)網(wǎng)提升企業(yè)的競爭力!
因?yàn)樯芷诓煌>植孔兞吭诜椒ńY(jié)束后就會被銷毀,但內(nèi)部類對象并不一定,這樣就會導(dǎo)致內(nèi)部類引用了一個(gè)不存在的變量。
所以編譯器會在內(nèi)部類中生成一個(gè)局部變量的拷貝,這個(gè)拷貝的生命周期和內(nèi)部類對象相同,就不會出現(xiàn)上述問題。
但這樣就導(dǎo)致了其中一個(gè)變量被修改,兩個(gè)變量值可能不同的問題。為了解決這個(gè)問題,編譯器就要求局部變量需要被final修飾,以保證兩個(gè)變量值相同。
在JDK8之后,編譯器不要求內(nèi)部類訪問的局部變量必須被final修飾,但局部變量值不能被修改(無論是方法中還是內(nèi)部類中),否則會報(bào)編譯錯(cuò)誤。利用javap查看編譯后的字節(jié)碼可以發(fā)現(xiàn),編譯器已經(jīng)加上了final。
根據(jù)代碼的計(jì)算結(jié)果,s的值應(yīng)該是-1371654655,這是由于Java中右側(cè)值的計(jì)算默認(rèn)是int類型。
NIO(Non-blocking IO)為所有的原始類型提供(Buffer)緩存支持,字符集編碼解碼解決方案。Channel :一個(gè)新的原始I/O 抽象。支持鎖和內(nèi)存映射文件的文件訪問接口。提供多路(non-bloking) 非阻塞式的高伸縮性網(wǎng)絡(luò)I/O 。
I
Java NIO和IO之間第一個(gè)最大的區(qū)別是,IO是面向流的,NIO是面向緩沖區(qū)的。Java IO面向流意味著每次從流中讀一個(gè)或多個(gè)字節(jié),直至讀取所有字節(jié),它們沒有被緩存在任何地方。此外,它不能前后移動流中的數(shù)據(jù)。如果需要前后移動從流中讀取的數(shù)據(jù),需要先將它緩存到一個(gè)緩沖區(qū)。
Java NIO的緩沖導(dǎo)向方法略有不同。數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū),需要時(shí)可在緩沖區(qū)中前后移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區(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ā)送請求讀取數(shù)據(jù),僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會獲取,當(dāng)然它不會保持線程阻塞。所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情。非阻塞寫也是如此。所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道。
選擇器(Selectors)
Java NIO 的 選擇器允許一個(gè)單獨(dú)的線程來監(jiān)視多個(gè)輸入通道,你可以注冊多個(gè)通道使用一個(gè)選擇器,然后使用一個(gè)單獨(dú)的線程來“選擇”通道:這些通道里已經(jīng)有可以處理的輸入,或者選擇已準(zhǔn)備寫入的通道。這種選擇機(jī)制,使得一個(gè)單獨(dú)的線程很容易來管理多個(gè)通道。
Java反射機(jī)制可以讓我們在編譯期(Compile Time)之外的運(yùn)行期(Runtime)檢查類,接口,變量以及方法的信息。反射還可以讓我們在運(yùn)行期實(shí)例化對象,調(diào)用方法,通過調(diào)用get/set方法獲取變量的值。同時(shí)我們也可以通過反射來獲取泛型信息,以及注解。還有更高級的應(yīng)用–動態(tài)代理和動態(tài)類加載(ClassLoader.loadclass())。
下面列舉一些比較重要的方法:
getFields:獲取所有 public 的變量。
getDeclaredFields:獲取所有包括 private , protected 權(quán)限的變量。
setAccessible:設(shè)置為 true 可以跳過Java權(quá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);
}
}
}
動態(tài)代理:
//Main.java
public static void main(String[] args) {
HelloWorld helloWorld=new HelloWorldImpl();
InvocationHandler handler=new HelloWorldHandler(helloWorld);
//創(chuàng)建動態(tài)代理對象
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)用原始對象的方法
result=method.invoke(obj, args);
//調(diào)用之后
doAfter();
return result;
}
通過反射獲取方法注解的參數(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;}
}
}
以上代碼會出現(xiàn)編譯錯(cuò)誤,因?yàn)橹挥徐o態(tài)內(nèi)部類才能定義靜態(tài)方法。
使用方法的區(qū)別
Synchronized:在需要同步的對象中加入此控制,synchronized可以加在方法上,也可以加在特定代碼塊中,括號中表示需要鎖的對象。
Lock:需要顯示指定起始位置和終止位置。一般使用ReentrantLock類做為鎖,多個(gè)線程中必須要使用一個(gè)ReentrantLock類做為對象才能保證鎖的生效。且在加鎖和解鎖處需要通過lock()和unlock()顯示指出。所以一般會在finally塊中寫unlock()以防死鎖。
性能的區(qū)別
synchronized是tuo管給JVM執(zhí)行的,而lock是java寫的控制鎖的代碼。在Java1.5中,synchronize是性能低效的。因?yàn)檫@是一個(gè)重量級操作,需要調(diào)用操作接口,導(dǎo)致有可能加鎖消耗的系統(tǒng)時(shí)間比加鎖以外的操作還多。相比之下使用Java提供的Lock對象,性能更高一些。但是到了Java1.6,發(fā)生了變化。synchronize在語義上很清晰,可以進(jìn)行很多優(yōu)化,有適應(yīng)自旋,鎖消除,鎖粗化,輕量級鎖,偏向鎖等等。導(dǎo)致在Java1.6上synchronize的性能并不比Lock差。
Synchronized:采用的是CPU悲觀鎖機(jī)制,即線程獲得的是獨(dú)占鎖。獨(dú)占鎖意味著 其他線程只能依靠阻塞來等待線程釋放鎖。而在CPU轉(zhuǎn)換線程阻塞時(shí)會引起線程上下文切換,當(dāng)有很多線程競爭鎖的時(shí)候,會引起CPU頻繁的上下文切換導(dǎo)致效率很低。
Lock:用的是樂觀鎖方式。所謂樂觀鎖就是,每次不加鎖而是假設(shè)沒有沖突而去完成某項(xiàng)操作,如果因?yàn)闆_突失敗就重試,直到成功為止。樂觀鎖實(shí)現(xiàn)的機(jī)制就是CAS操作。我們可以進(jìn)一步研究ReentrantLock的源代碼,會發(fā)現(xiàn)其中比較重要的獲得鎖的一個(gè)方法是compareAndSetState。這里其實(shí)就是調(diào)用的CPU提供的特殊指令。
ReentrantLock:具有更好的可伸縮性:比如時(shí)間鎖等候、可中斷鎖等候、無塊結(jié)構(gòu)鎖、多個(gè)條件變量或者鎖投票。
folat類型的還有double類型的,這些小數(shù)類型在趨近于0的時(shí)候直接等于0的可能性很小,一般都是無限趨近于0,因此不能用==來判斷。應(yīng)該用|x-0| //用程序表示就是 fabs(x) < 0.00001f 內(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對象才能 new 內(nèi)部類) 可以包含:字母、數(shù)字、$、_(下劃線),不可用數(shù)字開頭,不能是 Java 的關(guān)鍵字和保留字。 裝飾模式:java.io JDK 1.7及以前: ConcurrentHashMap允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù)。它使用了多個(gè)鎖來控制對hash表的不同部分進(jìn)行的修改。ConcurrentHashMap內(nèi)部使用段(Segment)來表示這些不同的部分,每個(gè)段其實(shí)就是一個(gè)小的hash table,它們有自己的鎖。只要多個(gè)修改操作發(fā)生在不同的段上,它們就可以并發(fā)進(jìn)行。 JDK 1.8: Segment雖保留,但已經(jīng)簡化屬性,僅僅是為了兼容舊版本。 插入時(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對象,而不是TreeNode對象; CAS作為知名無鎖算法,那ConcurrentHashMap就沒用鎖了么?當(dāng)然不是,當(dāng)hash值與鏈表的頭結(jié)點(diǎn)相同還是會synchronized上鎖,鎖鏈表。 雖然遞增操作++i是一種緊湊的語法,使其看上去只是一個(gè)操作,但這個(gè)操作并非原子的,因而它并不會作為一個(gè)不可分割的操作來執(zhí)行。實(shí)際上,它包含了三個(gè)獨(dú)立的操作:讀取count的值,將值加1,然后將計(jì)算結(jié)果寫入count。這是一個(gè)“讀取 - 修改 - 寫入”的操作序列,并且其結(jié)果狀態(tài)依賴于之前的狀態(tài)。所以在多線程環(huán)境下存在問題。 要解決自增操作在多線程環(huán)境下線程不安全的問題,可以選擇使用Java提供的原子類,如AtomicInteger或者使用synchronized同步方法。 new是一個(gè)關(guān)鍵字,它是調(diào)用new指令創(chuàng)建一個(gè)對象,然后調(diào)用構(gòu)造方法來初始化這個(gè)對象,可以使用帶參數(shù)的構(gòu)造器 newInstance()是Class的一個(gè)方法,在這個(gè)過程中,是先取了這個(gè)類的不帶參數(shù)的構(gòu)造器Constructor,然后調(diào)用構(gòu)造器的newInstance方法來創(chuàng)建對象。 Class.newInstance不能帶參數(shù),如果要帶參數(shù)需要取得對應(yīng)的構(gòu)造器,然后調(diào)用該構(gòu)造器的Constructor.newInstance(Object … initargs)方法 接口的默認(rèn)方法和靜態(tài)方法,JDK8允許我們給接口添加一個(gè)非抽象的方法實(shí)現(xiàn),只需要使用default關(guān)鍵字即可。也可以定義被static修飾的靜態(tài)方法。 Xms 堆最小值 重寫loadClass()方法。 hashcode() 返回該對象的哈希碼值,支持該方法是為哈希表提供一些優(yōu)點(diǎn),例如,java.util.Hashtable 提供的哈希表。 在 Java 應(yīng)用程序執(zhí)行期間,在同一對象上多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù),前提是對象上 equals 比較中所用的信息沒有被修改(equals默認(rèn)返回對象地址是否相等)。如果根據(jù) equals(Object)方法,兩個(gè)對象是相等的,那么在兩個(gè)對象中的每個(gè)對象上調(diào)用 hashCode 方法都必須生成相同的整數(shù)結(jié)果。 以下情況不是必需的:如果根據(jù) equals(java.lang.Object) 方法,兩個(gè)對象不相等,那么在兩個(gè)對象中的任一對象上調(diào)用 hashCode 方法必定會生成不同的整數(shù)結(jié)果。但是,程序員應(yīng)該知道,為不相等的對象生成不同整數(shù)結(jié)果可以提高哈希表的性能。 實(shí)際上,由 Object 類定義的 hashCode 方法確實(shí)會針對不同的對象返回不同的整數(shù)。(這一般是通過將該對象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)來實(shí)現(xiàn)的,但是 JavaTM 編程語言不需要這種實(shí)現(xiàn)技巧I。) hashCode的存在主要是用于查找的快捷性,如 Hashtable,HashMap等,hashCode 是用來在散列存儲結(jié)構(gòu)中確定對象的存儲地址的; 如果兩個(gè)對象相同,就是適用于 equals(java.lang.Object) 方法,那么這兩個(gè)對象的 hashCode 一定要相同; 如果對象的 equals 方法被重寫,那么對象的 hashCode 也盡量重寫,并且產(chǎn)生 hashCode 使用的對象,一定要和 equals 方法中使用的一致,否則就會違反上面提到的第2點(diǎn); 兩個(gè)對象的hashCode相同,并不一定表示兩個(gè)對象就相同,也就是不一定適用于equals(java.lang.Object) 方法,只能夠說明這兩個(gè)對象在散列存儲結(jié)構(gòu)中,如Hashtable,他們“存放在同一個(gè)籃子里”。 sleep()和yield()都會釋放CPU。 sleep()使當(dāng)前線程進(jìn)入停滯狀態(tài),所以執(zhí)行sleep()的線程在指定的時(shí)間內(nèi)肯定不會執(zhí)行;yield()只是使當(dāng)前線程重新回到可執(zhí)行狀態(tài),所以執(zhí)行yield()的線程有可能在進(jìn)入到可執(zhí)行狀態(tài)后馬上又被執(zhí)行。 sleep()可使優(yōu)先級低的線程得到執(zhí)行的機(jī)會,當(dāng)然也可以讓同優(yōu)先級和高優(yōu)先級的線程有執(zhí)行的機(jī)會;yield()只能使同優(yōu)先級的線程有執(zhí)行的機(jī)會。 {}是預(yù)編譯處理,${}是字符串替換。 Mybatis在處理#{}時(shí),會將sql中的#{}替換為?號,調(diào)用PreparedStatement的set方法來賦值; Dao接口,就是人們常說的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法內(nèi)的參數(shù),就是傳遞給sql的參數(shù)。Mapper接口是沒有實(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è) Dao接口里的方法,是不能重載的,因?yàn)槭侨廾?方法名的保存和尋找策略。 Dao接口的工作原理是JDK動態(tài)代理,Mybatis運(yùn)行時(shí)會使用JDK動態(tài)代理為Dao接口生成代理proxy對象,代理對象proxy會攔截接口方法,轉(zhuǎn)而執(zhí)行MappedStatement所代表的sql,然后將sql執(zhí)行結(jié)果返回。 關(guān)于Java后端面試題和答案就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果喜歡這篇文章,不如把它分享出去讓更多的人看到。
Outer.Inner a = new Outer.Inner()(靜態(tài)內(nèi)部類)
單例模式:Runtime類
簡單工廠模式:Integer.valueOf方法
享元模式:String常量池、Integer.valueOf(int i)、Character.valueOf(char c)
迭代器模式:Iterator
職責(zé)鏈模式:ClassLoader的雙親委派模型
解釋器模式:正則表達(dá)式j(luò)ava.util.regex.Pattern
對HashMap進(jìn)行了改進(jìn),當(dāng)單個(gè)桶的元素個(gè)數(shù)大于6時(shí)就會將實(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)用場景,注解幾乎可以使用在任何元素上,并且允許在同一個(gè)地方多次使用同一個(gè)注解
Lambda表達(dá)式
Xmx 堆最大值
Xmn: 新生代容量
XX:SurvivorRatio 新生代中Eden與Surivor空間比例
Xss 棧容量
XX:PermSize 方法區(qū)初始容量
XX:MaxPermSize 方法區(qū)最大容量
XX:+PrintGCDetails 收集器日志參數(shù)
Mybatis在處理{}替換成變量的值。
使用#{}可以有效的防止SQL注入,提高系統(tǒng)安全性。
當(dāng)前名稱:Java后端面試題和答案
URL網(wǎng)址:http://weahome.cn/article/jiggjg.html