如何用數(shù)組實(shí)現(xiàn)隊(duì)列揪惦?
用數(shù)組實(shí)現(xiàn)隊(duì)列時(shí)要注意 溢出 現(xiàn)象,這時(shí)我們可以采用循環(huán)數(shù)組的方式來(lái)解決祥款,即將數(shù)組收尾相接清笨。使用front指針指向隊(duì)列首位,tail指針指向隊(duì)列末位刃跛。
內(nèi)部類訪問(wèn)局部變量的時(shí)候抠艾,為什么變量必須加上final修飾? {#xuan}
因?yàn)樯芷诓煌瓣肌>植孔兞吭诜椒ńY(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蝗拿。
long s = 499999999 * 499999999 在上面的代碼中,s的值是多少蒿涎?
根據(jù)代碼的計(jì)算結(jié)果哀托,s
的值應(yīng)該是-1371654655
,這是由于Java中右側(cè)值的計(jì)算默認(rèn)是int
類型同仆。
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è)通道。
反射的用途
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());
}
}
Java注解的繼承
有@Inherited | 沒(méi)有@Inherited | |
---|---|---|
子類的類上能否繼承到父類的類上的注解? | 否 | 能 |
子類實(shí)現(xiàn)了父類上的抽象方法 | 否 | 否 |
子類繼承了父類上的方法 | 能 | 能 |
子類覆蓋了父類上的方法 | 否 | 否 |
通過(guò)測(cè)試結(jié)果來(lái)看黍特,@Inherited
只是可控制對(duì)類名上注解是否可以被繼承蛙讥。不能控制方法上的注解是否可以被繼承。
非靜態(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)方法。
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è)條件變量或者鎖投票。
float 變量如何與 0 比較垃环?
folat類型的還有double類型的邀层,這些小數(shù)類型在趨近于0的時(shí)候直接等于0的可能性很小,一般都是無(wú)限趨近于0遂庄,因此不能用==來(lái)判斷寥院。應(yīng)該用|x-0|<err
來(lái)判斷,這里|x-0|
表示絕對(duì)值涛目,err
表示限定誤差秸谢。
//用程序表示就是
fabs(x) < 0.00001f
如何新建非靜態(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)部類)
Java標(biāo)識(shí)符命名規(guī)則
可以包含:字母沫换、數(shù)字臭蚁、$、_
(下劃線)讯赏,不可用數(shù)字開頭垮兑,不能是 Java 的關(guān)鍵字和保留字。
你知道哪些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
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)行嚎卫。
詳細(xì)參考:
http://www.cnblogs.com/ITtangtang/p/3948786.html
http://qifuguang.me/2015/09/10/[Java并發(fā)包學(xué)習(xí)八]深度剖析ConcurrentHashMap/
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上鎖,鎖鏈表载弄。
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同步方法法严。
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)方法
你了解哪些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á)式
你用過(guò)哪些JVM參數(shù)育特?
Xms 堆最小值
Xmx 堆最大值
Xmn: 新生代容量
XX:SurvivorRatio 新生代中Eden與Surivor空間比例
Xss 棧容量
XX:PermSize 方法區(qū)初始容量
XX:MaxPermSize 方法區(qū)最大容量
XX:+PrintGCDetails 收集器日志參數(shù)
如何打破 ClassLoader 雙親委托?
重寫loadClass()
方法先朦。
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è)籃子里”。