Java基礎(chǔ)知識(shí)整理
- [x] java中==和equals和hashCode的區(qū)別
- [x] int、char掀宋、long各占多少字節(jié)數(shù)
- [x] int與integer的區(qū)別
- [x] 談?wù)剬?duì)java多態(tài)的理解
- [x] String、StringBuffer擎椰、StringBuilder區(qū)別始腾?
- [x] 什么是內(nèi)部類??jī)?nèi)部類的作用辕狰?成員內(nèi)部類、靜態(tài)內(nèi)部類控漠、局部?jī)?nèi)部類和匿名內(nèi)部類的理解蔓倍,以及項(xiàng)目中的應(yīng)用?
- [x] 靜態(tài)內(nèi)部類的設(shè)計(jì)意圖盐捷?
- [x] 抽象類和接口區(qū)別
- [x] 抽象類的意義
- [x] 抽象類與接口的應(yīng)用場(chǎng)景
- [x] 抽象類是否可以沒(méi)有方法和屬性偶翅?
- [x] 接口的意義
- [x] 泛型中extends和super的區(qū)別
- [x] 父類的靜態(tài)方法能否被子類重寫
- [x] 進(jìn)程和線程的區(qū)別
- [x] final,finally碉渡,finalize的區(qū)別
- [x] 序列化的方式
- [x] Serializable 和Parcelable 的區(qū)別
- [x] 靜態(tài)屬性和靜態(tài)方法是否可以被繼承聚谁?是否可以被重寫?以及原因滞诺?
- [x] 談?wù)剬?duì)kotlin的理解
- [x] 閉包和局部?jī)?nèi)部類的區(qū)別
- [x] string 轉(zhuǎn)換成 integer的方式及原理
Java深入點(diǎn)的知識(shí)點(diǎn)
- [ ]哪些情況下的對(duì)象會(huì)被垃圾回收機(jī)制處理掉形导?
- [ ]講一下常見(jiàn)編碼方式?
- [ ]utf-8編碼中的中文占幾個(gè)字節(jié)习霹;int型幾個(gè)字節(jié)朵耕?
- [ ]靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別,什么場(chǎng)景使用淋叶?
- [ ]Java的異常體系
- [ ]談?wù)勀銓?duì)解析與分派的認(rèn)識(shí)憔披。
- [ ]修改對(duì)象A的equals方法的簽名,那么使用HashMap存放這個(gè)對(duì)象實(shí)例的時(shí)候爸吮,會(huì)調(diào)用哪個(gè)equals方法?
- [ ]Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么望门?
- [ ]如何將一個(gè)Java對(duì)象序列化到文件里形娇?
- [ ]說(shuō)說(shuō)你對(duì)Java反射的理解
- [ ]說(shuō)說(shuō)你對(duì)Java注解的理解
- [ ]說(shuō)說(shuō)你對(duì)依賴注入的理解
- [ ]說(shuō)一下泛型原理,并舉例說(shuō)明
- [ ]Java中String的了解
- [ ]String為什么要設(shè)計(jì)成不可變的筹误?
- [ ]Object類的equal和hashCode方法重寫桐早,為什么?
問(wèn)題解答部分
1. java中==和equals和hashCode的區(qū)別?
- == 是判斷相等運(yùn)算符哄酝,用于比較基本數(shù)據(jù)類型的值是否相等友存,或者引用類型對(duì)象的內(nèi)存地址是否相等。
- equals 是Object類的一個(gè)方法陶衅,子類可以重寫這個(gè)方法屡立,實(shí)現(xiàn)特定類的equals方法。常用于判斷兩個(gè)對(duì)象的內(nèi)容是否一致(實(shí)際上如果你關(guān)注的僅僅是人的年齡搀军,你可以讓一個(gè)男人equals一個(gè)女人膨俐。是否equal的規(guī)則是由自己定義的,但是一般情況下重寫equals方法還是有一些要求的罩句。)焚刺。Object類的equals方法的默認(rèn)實(shí)現(xiàn)只是比較了對(duì)象的內(nèi)存地址是否一樣(obj1==obj2),所以我們?cè)谛枰臅r(shí)候需要自己重寫equals方法來(lái)判斷對(duì)象內(nèi)容是否一致门烂。equals方法的重寫原則是:
- 自反性 x.equals(x) = true
- 對(duì)稱性 x.eqlas(y) = y.equals(x)
- 傳遞性 x.eqlas(y) = true && y.equals(z)=true 則 x.equals(z)=true
- 一致性 每次調(diào)用返回的結(jié)果應(yīng)該一致(如果參與equals判斷的字段沒(méi)有改變)
- x.equals(null) = false
- x==y 則x.equals(y)=true
- equal objects must have equal hash codes
- 如果x.equals(y),如果x和y的類型不一致乳愉,應(yīng)該返回false
- hashCode 是對(duì)象的一個(gè)標(biāo)示值,是根據(jù)對(duì)象內(nèi)的值進(jìn)行運(yùn)算得到的結(jié)果。把一個(gè)需要占據(jù)更多內(nèi)存的對(duì)象用一個(gè)占用更少內(nèi)存的HashCode值來(lái)表示屯远,因此肯定會(huì)有信息損失蔓姚,導(dǎo)致的結(jié)果是不同的對(duì)象的HashCode值可能會(huì)相等。常常用在HashMap或者HashTable中氓润,hashCode方法的規(guī)則如下:
- 一個(gè)應(yīng)用程序一個(gè)執(zhí)行片段(線程)中HashCode值應(yīng)該不變赂乐,但是并不需要保證在兩個(gè)不同的執(zhí)行片段中HashCode值也要相等
- equal的兩個(gè)對(duì)象的HashCode值需要保證相等
- not equal的兩個(gè)對(duì)象的HashCode值不需要保證不相等,但是如果可以不一致能夠提高HashMap等的效率(防止碰撞的發(fā)生)
- Object類的hashCode方法是根據(jù)對(duì)象在內(nèi)存中的地址轉(zhuǎn)換出的
2. int咖气、char挨措、long各占多少字節(jié)數(shù)?
Java中有八種基本數(shù)據(jù)類型:
- byte 1個(gè)字節(jié)
- char 2個(gè)字節(jié)
- boolean java沒(méi)有說(shuō)明一定是幾個(gè)字節(jié),可認(rèn)為是一個(gè)字節(jié)崩溪,畢竟只要一個(gè)bit就可以表示true和false兩種情況了
- short 短整型 2個(gè)字節(jié)
- int 整型 4個(gè)字節(jié)
- long 長(zhǎng)整型 8個(gè)字節(jié)
- float 浮點(diǎn)數(shù)類型 4個(gè)字節(jié)
- double 雙精度浮點(diǎn)數(shù)類型 8個(gè)字節(jié)
3. int與integer的區(qū)別浅役?
- Java中的數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型,所以有人說(shuō)Java不是一門純粹的面向?qū)ο笳Z(yǔ)言伶唯。int是基本數(shù)據(jù)類型觉既,Integer是int的包裝類。講到這個(gè)不得不說(shuō)一說(shuō)自動(dòng)裝箱和自動(dòng)拆箱乳幸。自動(dòng)裝箱和自動(dòng)拆箱是Java5新增的概念瞪讼,Java5以前需要使用手動(dòng)裝箱和拆箱。
- 自動(dòng)裝箱指的是基本數(shù)據(jù)類型自動(dòng)轉(zhuǎn)換成想對(duì)應(yīng)的包裝類型粹断,如int轉(zhuǎn)換成Integer會(huì)自動(dòng)調(diào)用Integer.valueOf(int a)方法將int值轉(zhuǎn)換成對(duì)應(yīng)的Integer包裝類符欠。(這里有一點(diǎn)值得注意的是:-128~127之間的int值裝箱時(shí)指向的是緩存對(duì)象,不在這里范圍內(nèi)則會(huì)新new一個(gè)Integer對(duì)象)
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
- 自動(dòng)拆箱指的是包裝類型自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的基本數(shù)據(jù)類型的過(guò)程瓶埋。如Integer自動(dòng)轉(zhuǎn)換成int希柿、Character自動(dòng)轉(zhuǎn)換成char,分別調(diào)用integer.intValue()诊沪、character.charValue()。
- 自動(dòng)裝箱和自動(dòng)拆箱的發(fā)生時(shí)機(jī):當(dāng)需要int值曾撤,但是傳入的參數(shù)是Integer類型時(shí)會(huì)發(fā)生自動(dòng)拆箱端姚,相反則會(huì)發(fā)生自動(dòng)裝箱
- 其他需要額外注意的點(diǎn):
- integer += 2,會(huì)進(jìn)行一次自動(dòng)拆箱和一次自動(dòng)裝箱,使用時(shí)需要注意挤悉。
- 發(fā)生重載時(shí)不會(huì)發(fā)生自動(dòng)裝箱和拆箱操作
- int == int ,int == Integer 比較的是值渐裸。Integer == Integer比較的是對(duì)象地址.
- 前面說(shuō)的那個(gè)-128~127的問(wèn)題。貼一段源碼自己看哦尖啡,小伙伴們
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
4.談?wù)剬?duì)java多態(tài)的理解橄仆。
- 是什么。多態(tài)是面向?qū)ο蟮娜筇匦灾恍普叮嫦驅(qū)ο蟮娜筇匦允牵豪^承盆顾、封裝和多態(tài)。多態(tài)指的是:父類的引用變量 指向子類對(duì)象畏梆,當(dāng)調(diào)用父類中存在的方法時(shí)您宪,實(shí)際上會(huì)調(diào)用子類重寫之后的方法(如果有重寫。否則調(diào)用父類的方法奠涌。)
- 為什么宪巨。在開(kāi)發(fā)中會(huì)經(jīng)常遇到設(shè)計(jì)一定的準(zhǔn)則A,大家都來(lái)遵循這個(gè)準(zhǔn)則A來(lái)辦事溜畅。準(zhǔn)則A1捏卓、A2...的具體實(shí)行留給實(shí)施該準(zhǔn)則的地方來(lái)實(shí)現(xiàn),但是怎么調(diào)用到某一個(gè)特定的準(zhǔn)則A1呢慈格,有兩個(gè)方法怠晴,一個(gè)是強(qiáng)制類型轉(zhuǎn)換之后將A轉(zhuǎn)換成A1,然后調(diào)用A1重寫的規(guī)則浴捆,再有就是創(chuàng)造一個(gè)方法可以使用A直接調(diào)用A1代表的準(zhǔn)則蒜田,就是多態(tài)了。
- 怎么做选泻。多態(tài)的實(shí)現(xiàn)需要三個(gè)條件:
- 繼承+重寫:子類A1繼承自父類A冲粤,并且重寫父類A的方法function1();
- 向上轉(zhuǎn)型:使用父類A的引用變量指向A1對(duì)象,如<code>A a = new A1();</code>
- 使用a.function1()页眯;
當(dāng)使用a調(diào)用function1的時(shí)候梯捕,實(shí)際上調(diào)用的是A1重寫的function1方法。
- 來(lái)個(gè)實(shí)例:例子是網(wǎng)上流傳比較廣的例子窝撵,找不到出處了科阎,誰(shuí)知道出處告訴我一下,我把原文鏈接貼出來(lái)忿族。
public class Main {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
public static class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public static class B extends A {
public String show(B obj) {
return ("B and B");
}
public String show(A obj) {
return ("B and A");
}
}
public static class C extends B {}
public static class D extends B {}
/**
*
* 下面這是正確答案.你對(duì)了幾個(gè)锣笨?
* 1--A and A
* 2--A and A
* 3--A and D
* 4--B and A
* 5--B and A
* 6--A and D
* 7--B and B
* 8--B and B
* 9--A and D
*
*/
}
下來(lái)做下簡(jiǎn)單說(shuō)明:
根據(jù)繼承關(guān)系我們知道: C/D->B->A
根據(jù)繼承關(guān)系關(guān)系,我們把類A和類B的方法做一下簡(jiǎn)化道批。
class A{
代號(hào)F1:show(A) 輸出 A and A
代號(hào)F2:show(D) 輸出 A and D
}
class B extends A{
代號(hào)F3:show(A) 輸出 B and A **重寫的方法错英,覆蓋父類的show(A)方法
代號(hào)F4:show(B) 輸出 B and B **新增加的方法
代號(hào)F5:show(D) 輸出 A and D **繼承自A類的方法,沒(méi)有重寫隆豹。
}
再看一下引用對(duì)象:
A a1->A 引用變量和實(shí)際對(duì)象一致椭岩,可以調(diào)用類A中的方法(F1,F2)
A a2->B 引用變量和實(shí)際對(duì)象不一致,做了向上轉(zhuǎn)型璃赡,此時(shí)a2只能調(diào)用繼承自A類的方法或者重寫了A類的方法判哥,不能調(diào)用B類自己新增的方法。(F3,F5)
B b ->B 引用變量和實(shí)際對(duì)象一致碉考,可以調(diào)用類B中的所有方法塌计,包括繼承來(lái)的、重寫的侯谁、新增的锌仅。(F3,F4,F5)
在進(jìn)行方法的選擇時(shí),首先看能調(diào)用到哪些方法墙贱,然后在可以調(diào)用的方法里尋找是否能完美匹配實(shí)參真實(shí)類型的热芹,如果沒(méi)有,向上轉(zhuǎn)型找到最近的一個(gè)父類惨撇。
1-3都是a1調(diào)用方法伊脓,a1的變量類型和實(shí)際類型一致,可以調(diào)用F1,F2
System.out.println("1--" + a1.show(b)); //b是B的實(shí)例魁衙,但是A類沒(méi)有show(B)方法报腔,所以需要對(duì)b向上轉(zhuǎn)型,然后b是A的子類的實(shí)例纺棺,所以調(diào)用F1,輸出-> A and A
System.out.println("2--" + a1.show(c));//c跟B一樣需要做向上轉(zhuǎn)型榄笙,調(diào)用F1,輸出-> A and A
System.out.println("3--" + a1.show(d));//d是D的實(shí)例,可以直接調(diào)用F2,輸出A and D
4-6 都是a2調(diào)用方法祷蝌,可以調(diào)用F3,F5
System.out.println("4--" + a2.show(b));//b是A的子類的實(shí)例茅撞,調(diào)用F3,輸出B and A
System.out.println("5--" + a2.show(c));//c是A的子類的實(shí)例,調(diào)用F3,輸出B and A
System.out.println("6--" + a2.show(d));//d是D的實(shí)例巨朦,調(diào)用F5,輸出A and D
7-9 都是a2調(diào)用方法米丘,可以調(diào)用F3,F4,F5
System.out.println("7--" + b.show(b));//b是A的子類的實(shí)例,實(shí)際調(diào)用時(shí)調(diào)用F4,輸出B and B
System.out.println("8--" + b.show(c));//c是B的子類的實(shí)例糊啡,調(diào)用F4,輸出B and B
System.out.println("9--" + b.show(d));//d是D的實(shí)例拄查,調(diào)用F5,輸出A and D
這樣一加分析,是不是就很清楚明了了呢棚蓄。
5.String堕扶、StringBuffer碍脏、StringBuilder區(qū)別?
相同點(diǎn):String、StringBuilder稍算、StringBuffer都是使用一個(gè)char[]保存字符串?dāng)?shù)據(jù)典尾。
不同點(diǎn):String 是不可變的,StringBuilder是可變的線程不安全的糊探,StringBuffer是在StringBuilder的基礎(chǔ)上加上了線程安全控制钾埂,所以是可變的線程安全的。
面試官可能會(huì)深入到下面的問(wèn)題
- 字符串池:String是不可變的科平,共享使用可以提高效率褥紫。不可變,所以所有改變字符串的方法都是new了一個(gè)新String(可能會(huì)借助SB)瞪慧。contact用的是把原來(lái)的數(shù)據(jù)和新增的數(shù)據(jù)copy到另一個(gè)char[]中髓考,
- 內(nèi)存模型:常量池放在棧內(nèi)存區(qū),new對(duì)象放在堆內(nèi)存區(qū)汞贸。<code>String s0 = "abc";String s1 = "abc";String s2 = new String("abc");</code>s0和s1指向的是同一個(gè)對(duì)象(常量池中的對(duì)象)绳军;s1和s2指向的不是同一個(gè)對(duì)象。
- 線程安全:這是另一個(gè)話題
- new String矢腻,String+String门驾,String.replace,String.concat等方法的內(nèi)部實(shí)現(xiàn)
- 或者其他的
6.什么是內(nèi)部類,內(nèi)部類的作用多柑?成員內(nèi)部類奶是、靜態(tài)內(nèi)部類、局部?jī)?nèi)部類和匿名內(nèi)部類的理解竣灌,以及項(xiàng)目中的應(yīng)用?
定義:把一個(gè)類定義在另外一個(gè)類中聂沙,這個(gè)類就是內(nèi)部類。
作用(意義):內(nèi)部類可以實(shí)現(xiàn)更好的封裝初嘹;內(nèi)部類的使用可以實(shí)現(xiàn)多重繼承及汉;如果一個(gè)類需要實(shí)現(xiàn)的兩個(gè)接口中有同名同參數(shù)列表的方法(或者父類和接口),也需要借助內(nèi)部類來(lái)實(shí)現(xiàn)屯烦;內(nèi)部是面向?qū)ο笾械拈]包坷随。
內(nèi)部類可以分為下面幾種:
成員內(nèi)部類包含非靜態(tài)內(nèi)部類和靜態(tài)內(nèi)部類
局部?jī)?nèi)部類包含一般局部?jī)?nèi)部類和匿名內(nèi)部類
下面的很多內(nèi)容都涉及了成員間的相互訪問(wèn)能力,這里有一條準(zhǔn)則是一定要牢記的:
靜態(tài)成員不能訪問(wèn)非靜態(tài)成員驻龟!
- 非靜態(tài)內(nèi)部類:
- 定義:類定義在外部類的大括號(hào)里温眉,作為外部類的成員。非靜態(tài)內(nèi)部類不能擁有靜態(tài)成員(靜態(tài)方法翁狐、靜態(tài)成員类溢、靜態(tài)初始化塊)。
- 訪問(wèn)外部類:內(nèi)部類可以直接訪問(wèn)外部類的所有靜態(tài)成員和非靜態(tài)成員(內(nèi)部類作為外部類的成員露懒,成員間的相互訪問(wèn)是不受訪問(wèn)控制符的限制的)闯冷。
- 被外部類訪問(wèn):外部類需要新建一個(gè)內(nèi)部類的實(shí)例才能訪問(wèn)內(nèi)部類的非靜態(tài)成員(根據(jù)第二條規(guī)則砂心,事實(shí)上也不會(huì)有靜態(tài)成員),不受訪問(wèn)控制符的限制。
- 被其他類訪問(wèn):在外部類以外訪問(wèn)內(nèi)部類首先需要確定是否有內(nèi)部類的訪問(wèn)權(quán)限蛇耀,如果可以訪問(wèn)內(nèi)部類计贰,則需要新建一個(gè)外部類的實(shí)例,然后用外部類的實(shí)例創(chuàng)建內(nèi)部類對(duì)象進(jìn)行訪問(wèn)蒂窒。
- 作用與應(yīng)用:因?yàn)榉庆o態(tài)內(nèi)部類不能脫離離開(kāi)外部類實(shí)例單獨(dú)存在,所以特別適用于內(nèi)部類是外部類不可分割的一部分荞怒,內(nèi)部類實(shí)例離開(kāi)外部類實(shí)例就沒(méi)有意義的那種情況(如響尾蛇是一個(gè)實(shí)例洒琢,響尾蛇的尾巴是一個(gè)實(shí)例,響尾蛇的尾巴離開(kāi)響尾蛇就不會(huì)響了褐桌,這個(gè)尾巴的成員變量和成員方法就失去了意義)衰抑。或者添加上訪問(wèn)控制符荧嵌,不想讓內(nèi)部類脫離外部類使用的情況呛踊,如一個(gè)RecyclerView的Adapter是一個(gè)外部類,Adapter中的ViewHolder是一個(gè)內(nèi)部類啦撮,這個(gè)ViewHolder脫離里這個(gè)Adapter就沒(méi)有了意義谭网,所以可以給這個(gè)ViewHolder添加private訪問(wèn)控制符不讓外部類以外的類訪問(wèn)。
- 靜態(tài)內(nèi)部類:
- 定義:類定義在外部類的大括號(hào)里赃春,作為外部類的成員愉择,并且增加Static關(guān)鍵字修飾。
- 訪問(wèn)外部類:靜態(tài)內(nèi)部類可以訪問(wèn)外部類的靜態(tài)成員變量和靜態(tài)成員方法织中,不能訪問(wèn)外部類的非靜態(tài)成員锥涕。
- 被外部類訪問(wèn):外部類需要新建一個(gè)內(nèi)部類的實(shí)例才能訪問(wèn)內(nèi)部類的非靜態(tài)成員,或者直接使用內(nèi)部類類名訪問(wèn)內(nèi)部類的靜態(tài)成員狭吼。
- 被其他類訪問(wèn):在外部類以外可以直接使用外部類名.內(nèi)部類名訪問(wèn)內(nèi)部類的靜態(tài)成員层坠,或者需要新建一個(gè)內(nèi)部類的實(shí)例來(lái)訪問(wèn)內(nèi)部類的非靜態(tài)成員。
- 作用與應(yīng)用:<code>//todo 這個(gè)我不知道誒</code>我也還不是很明白靜態(tài)內(nèi)部類的應(yīng)用場(chǎng)景呢刁笙。
- 局部?jī)?nèi)部類:
- 定義:類定義在外部類的方法中破花,并且有自己的類名。局部?jī)?nèi)部類相當(dāng)于一個(gè)局部變量采盒,所以不能有訪問(wèn)控制符以及static等修飾符旧乞。局部?jī)?nèi)部類的訪問(wèn)(包括對(duì)象的創(chuàng)建、方法的調(diào)用)局限在外部類的方法內(nèi)部磅氨。
- 訪問(wèn)限制:局部?jī)?nèi)部類可以訪問(wèn)方法內(nèi)的局部變量尺栖,但是局部變量需要是final類型的(Java8以前)
- 作用與應(yīng)用: 一般的局部?jī)?nèi)部類確實(shí)用的比較少誒
- 匿名內(nèi)部類
- 定義:匿名內(nèi)部類繼承自一個(gè)類或者實(shí)現(xiàn)一個(gè)接口,使用方法是直接使用new 父類名稱或者接口名稱就可以創(chuàng)建自己匿名內(nèi)部類的對(duì)象烦租。
- 內(nèi)部類的構(gòu)造器:匿名內(nèi)部類沒(méi)有類名延赌,所以無(wú)法定義構(gòu)造器除盏。如果是繼承父類的匿名內(nèi)部類,新建對(duì)象是直接使用父類的構(gòu)造器挫以。
- 作用與應(yīng)用: 最常見(jiàn)的創(chuàng)建匿名內(nèi)部類的方式是在回調(diào)時(shí)需要某個(gè)接口類型的對(duì)象者蠕。
7.靜態(tài)內(nèi)部類的設(shè)計(jì)意圖?
如果有一個(gè)內(nèi)部類并不需要保存一個(gè)外部類的引用時(shí)掐松,可以設(shè)計(jì)成靜態(tài)類踱侣;
如果外部類有一個(gè)靜態(tài)方法需要使用到內(nèi)部類中的某個(gè)成員(成員變量或者成員方法),那么也需要把內(nèi)部類設(shè)置成靜態(tài)的大磺。
8.抽象類和接口區(qū)別?
- 設(shè)計(jì)目的:接口體現(xiàn)的是一種規(guī)范和實(shí)現(xiàn)相分離的設(shè)計(jì)哲學(xué)抡句,接口定義了一系列的準(zhǔn)則供實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)。實(shí)現(xiàn)此接口的所有類都遵循了接口所定義的一系列準(zhǔn)則杠愧,他們內(nèi)部實(shí)現(xiàn)不一樣待榔,但是對(duì)于調(diào)用者而言他們可以提供相同的對(duì)外服務(wù)。面向接口的耦合是一種低層次的耦合流济,可以提供更好的拓展性和可維護(hù)性锐锣。
抽象類則提現(xiàn)的是一種模板模式的設(shè)計(jì)。抽象類定義了一種模板绳瘟,完成模板的一些基礎(chǔ)實(shí)現(xiàn)雕憔,其他的細(xì)節(jié)交給子類去延遲實(shí)現(xiàn)。- 用法(包含的內(nèi)容):
- 變量:抽象類可以包含任何類型的成員變量稽荧;接口只能包含public static final類型的變量橘茉。
- 方法:抽象類可以包含抽象方法或者非抽象方法(可以有方法的實(shí)現(xiàn));接口只能包含抽象實(shí)例方法(java8以后可以包含類方法和默認(rèn)方法)姨丈。
- 初始化塊和構(gòu)造器:抽象類可以包含畅卓,接口則不能包含。
- 其他:抽象類的關(guān)鍵字abstract class,接口的關(guān)鍵字interface蟋恬。
9.抽象類的意義?
抽象類是值用abstract關(guān)鍵字修飾的類翁潘,抽象類中可以包含抽象方法并且不能被實(shí)例化,除此之外歼争,抽象類和其他的類并沒(méi)有本質(zhì)性的區(qū)別(甚至可以把抽象類看成普通類和接口的復(fù)合體拜马,或者說(shuō)是把抽象類看成接口和具體實(shí)現(xiàn)類之間的一個(gè)過(guò)渡,比如說(shuō)List接口--->AbstractList抽象類--->ArrayList具體實(shí)現(xiàn)類)沐绒。
- 抽象類提取了子類中的公有方法俩莽,可以提供方法級(jí)別的代碼復(fù)用;
- 同時(shí)抽象類的實(shí)現(xiàn)類必須實(shí)現(xiàn)抽象類中定義的抽象方法乔遮,向外提供一致的調(diào)用方法扮超。
- 抽象類將子類的公共部分進(jìn)行提取,將可變部分留給子類去實(shí)現(xiàn),子類可同時(shí)擁有父類的能力出刷,也擁有自己新的能力璧疗。
- 抽象類是用于繼承的,可以實(shí)現(xiàn)多態(tài)馁龟。
10.抽象類與接口的應(yīng)用場(chǎng)景?
- 從意義上說(shuō):抽象類是對(duì)一組具有關(guān)聯(lián)(有共同的屬性崩侠、方法或者意義上有關(guān)聯(lián)等)的類的抽象;接口則會(huì)是對(duì)一組執(zhí)行相同標(biāo)準(zhǔn)(如都有List接口的子類都有插入坷檩、刪除等功能)的類的抽象却音。從這個(gè)層面來(lái)說(shuō),抽象類適用于子類之間有一些共同的屬性或者方法矢炼,不需要子類去單獨(dú)實(shí)現(xiàn)(但是子類也可以有自己獨(dú)特的屬性或方法)的時(shí)候僧家;而接口適用于子類可能只是需要實(shí)現(xiàn)特定的一組功能,對(duì)其他的并沒(méi)有約束裸删。
- 從類之間通訊:接口更加適合用于兩個(gè)類之間需要通過(guò)一個(gè)標(biāo)準(zhǔn)進(jìn)行通訊,具體實(shí)現(xiàn)并不關(guān)心(面向接口編程)阵赠;抽象類則會(huì)在如果只靠純粹的接口無(wú)法滿足通訊需求(有狀態(tài)等信息需要保存)涯塔,可以考慮使用抽象類。
- 從功能上來(lái)區(qū)分:值得注意的是:
- 接口不能擁有實(shí)例變量清蚀,如果子類之間需要共同的變量匕荸,只能使用抽象類
- 接口的所有方法都必須要被子類實(shí)現(xiàn),如果你設(shè)計(jì)的方法只想被需要的時(shí)候?qū)崿F(xiàn)枷邪,你應(yīng)該使用抽象類(Java8以前榛搔,Java8以后接口也可以有默認(rèn)方法 default method)。
- 你只需要一組標(biāo)識(shí)东揣,不需要任何方法践惑,應(yīng)該使用接口(只是使用了接口的變量都是<code>public static final</code> 的這一特性)
11.抽象類是否可以沒(méi)有方法和屬性?
可以嘶卧。抽象類和實(shí)現(xiàn)類之間的唯一區(qū)別是抽象類中允許存在抽象方法尔觉。
12.接口的意義?
接口,在Java中叫Interface芥吟,在Object_C中叫protocol(協(xié)議)侦铜,在維基百科上接口的解釋是用于溝通的中介物的抽象。
我們?cè)谏钪薪?jīng)常會(huì)遇到一種情況就是我們希望這個(gè)東西可以做什么钟鸵,但是并不想了解具體是怎么做的钉稍,比如說(shuō)電飯鍋可以燒飯,不管什么品牌什么型號(hào)的電飯鍋都可以燒飯棺耍,我們并不需要知道電飯鍋怎么燒飯的贡未,不同品牌的電飯鍋燒飯有什么區(qū)別,但是我們都知道只要是電飯鍋是肯定是可以燒飯的(如果不能燒飯我們就要去12315投訴了)。
在軟件開(kāi)發(fā)中我們也需要對(duì)一些共通的內(nèi)容進(jìn)行抽象羞秤,如果我們經(jīng)常需要比較兩個(gè)對(duì)象的大小缸托,任意的對(duì)象都應(yīng)該有比較大小的方法,比較的過(guò)程由具體的對(duì)象施行瘾蛋。如果我們有一個(gè)規(guī)范A(或者協(xié)議)規(guī)定了比較大小的方法a俐镐,那么我們只需要知道某個(gè)類B是否實(shí)現(xiàn)了這個(gè)接口A,如果實(shí)現(xiàn)了哺哼,那我們知道B肯定有一個(gè)方法B.a是用來(lái)比較對(duì)象B1佩抹、B2的大小的,我們直接調(diào)用就好了取董。
13.泛型中extends和super的區(qū)別
extends 確定了泛型類型的上限棍苹。
super 確定了泛型類型的下限。
假如現(xiàn)在有現(xiàn)在的繼承關(guān)系:
- Animal extends Object
- Person extends Animal
- Students extends Person
- CollegeStudents extends Student
現(xiàn)在假設(shè)有一個(gè)List:
List<? extends Person> personList = new ArrayList<>();
//那么List可接受類型只能是Person茵汰、Student枢里、CollegeStudent,
//不能是Animal,不能是Object
List<? extends Person> personList = new ArrayList<>();
//那么List可接受類型只能是Animal或者Object
//不能是Person、Student蹂午、CollegeStudent
14.父類的靜態(tài)方法能否被子類重寫栏豺?
不能。這個(gè)問(wèn)題可能是想問(wèn)你類加載以及對(duì)象創(chuàng)建和方法調(diào)用的過(guò)程豆胸,這是一大串內(nèi)容啊奥洼,回頭有時(shí)間再專門寫則這個(gè)話題吧。
簡(jiǎn)單點(diǎn)就可以直接回答:我試了晚胡,加上@override之后編譯不通過(guò)灵奖。
15.進(jìn)程和線程的區(qū)別
進(jìn)程: Process,進(jìn)程是操作系統(tǒng)進(jìn)行資源分配(內(nèi)存等)的單位。一個(gè)應(yīng)用至少擁有一個(gè)進(jìn)程(一個(gè)應(yīng)用也可以開(kāi)啟兩個(gè)進(jìn)程)估盘,一個(gè)進(jìn)程默認(rèn)擁有一個(gè)線程瓷患。
線程: Thread,線程是CPU進(jìn)行調(diào)度的單位。隸屬于同一個(gè)進(jìn)程的不同線程共享該進(jìn)程的系統(tǒng)資源遣妥,共享一塊內(nèi)存尉尾,并發(fā)執(zhí)行的效率更高。線程比進(jìn)程更加輕量燥透,多個(gè)線程之間進(jìn)行交互更加方便沙咏。
16.final,finally班套,finalize的區(qū)別
final: 用于聲明屬性,方法和類, 分別表示屬性不可變, 方法不可覆蓋, 類不可繼承.
finally: 是異常處理語(yǔ)句結(jié)構(gòu)的一部分肢藐,表示總是執(zhí)行.
finalize: 是Object類的一個(gè)方法,在垃圾收集器執(zhí)行的時(shí)候會(huì)調(diào)用被回收對(duì)象的此方法吱韭,可以覆蓋此方法提供垃圾收集時(shí)的其他資源回收吆豹,例如關(guān)閉文件等. JVM不保證此方法總被調(diào)用.
17.序列化的方式
18.Serializable 和Parcelable 的區(qū)別
Java自定義了兩種序列化方式鱼的,實(shí)現(xiàn)Serializable接口或者Externalizable接口。
Android提供了一種序列化的方式痘煤,實(shí)現(xiàn)Parcelable接口凑阶。
區(qū)別是:
- 實(shí)現(xiàn)Serializable接口不需要寫任何額外的代碼;實(shí)現(xiàn)Parcelable接口則需要實(shí)現(xiàn)接口里的方法衷快,以及創(chuàng)建一個(gè)Creator宙橱,來(lái)完成格式化。
- 實(shí)現(xiàn)Serializable接口序列化的對(duì)象適合硬盤存儲(chǔ)蘸拔、網(wǎng)絡(luò)存儲(chǔ)等师郑;實(shí)現(xiàn)Parcelable接口適合于在內(nèi)存中傳輸。
- 實(shí)現(xiàn)Serializable接口實(shí)現(xiàn)序列化的過(guò)程運(yùn)用了反射技術(shù)调窍,所以效率比較低宝冕;實(shí)現(xiàn)Parcelable接口則序列化的速度比較快。
19.靜態(tài)屬性和靜態(tài)方法是否可以被繼承邓萨?是否可以被重寫地梨?以及原因?
可以被繼承缔恳,不能被重寫湿刽。
Java中的綁定指的是將方法的調(diào)用與具體的類和類中的方法做一個(gè)連接,綁定分為靜態(tài)綁定和動(dòng)態(tài)綁定褐耳。static靜態(tài)方法的綁定屬于靜態(tài)綁定,在程序執(zhí)行前已經(jīng)由Java編譯器或者其他連接器進(jìn)行了綁定渴庆,并不是在運(yùn)行時(shí)進(jìn)行的綁定铃芦。
重寫指的是Java子類重寫(@Override)和覆蓋父類的方法,是多態(tài)的基礎(chǔ)襟雷,多態(tài)是發(fā)生在運(yùn)行時(shí)的刃滓。子類可以創(chuàng)建和父類一樣的靜態(tài)方法,但是這時(shí)候不是重寫耸弄,只是子類也建立了一個(gè)和父類一樣的靜態(tài)方法而已咧虎,引用變量是什么類型,就調(diào)用哪個(gè)類型的靜態(tài)方法计呈。
更嚴(yán)格意義上說(shuō)砰诵,我覺(jué)得Java對(duì)靜態(tài)成員的調(diào)用就不該使用實(shí)例.靜態(tài)成員,而應(yīng)該只允許使用類名.靜態(tài)成員捌显,這樣才更符合靜態(tài)成員屬于類本身而不屬于類的實(shí)例這一特性茁彭。
20.談?wù)剬?duì)kotlin的理解
我的理解是這玩意兒挺好的,但是也不是特別需要急著學(xué)扶歪,但是會(huì)這個(gè)肯定是好的理肺,畢竟是趨勢(shì)。
說(shuō)說(shuō)我用的時(shí)候感觸比較深的幾點(diǎn)。
- 與Java的互通性
- 空安全妹萨,再也不用擔(dān)心空指針了
- 字符串的連接更爽年枕,尤其是多個(gè)固定字符串和多個(gè)變量的連接,超爽
- 類型推斷乎完,定義并且初始化時(shí)不需要顯式指明變量類型
- 方法可以設(shè)置默認(rèn)參數(shù)熏兄,挺實(shí)用的
- public 變量不需要寫getter和setter方法了,雖然以前也可以用工具生成囱怕,但是代碼總是顯得有些臃腫霍弹。
- 更好的Lambda表達(dá)式
21.閉包和局部?jī)?nèi)部類的區(qū)別
先說(shuō)說(shuō)閉包,可以先參考下這里,評(píng)論也看一下
閉包的概念:外部環(huán)境持有內(nèi)部函數(shù)所使用的自由變量娃弓,由此對(duì)內(nèi)部函數(shù)形成了閉包典格。一個(gè)函數(shù)的自由變量就是既不是局部變量也不是方法參數(shù)的變量。
在Java中台丛,函數(shù)只能在類中定義耍缴。所以這里的內(nèi)部函數(shù)一定是在一個(gè)類中的。
個(gè)人認(rèn)為Java中的幾種閉包的常見(jiàn)形式:
- 類直接作為外部環(huán)境: 如果這個(gè)函數(shù)直接使用了類中的實(shí)例變量挽霉,那么函數(shù)的返回值既依賴于函數(shù)的參數(shù)防嗡,同時(shí)也依賴于類的實(shí)例變量,這也是一種閉包侠坎。
- 類作為包裝蚁趁,放在另外的類里:這是外部類-非靜態(tài)內(nèi)部類的一種閉包形式,內(nèi)部類持有外部類的引用实胸,如果此時(shí)內(nèi)部類的成員方法調(diào)用了外部類的實(shí)例變量他嫡,則外部類就對(duì)該函數(shù)實(shí)現(xiàn)了簡(jiǎn)潔的閉包。
- 類作為包裝庐完,放在其他的方法里:這是一種方法-局部?jī)?nèi)部類的閉包形式钢属,局部?jī)?nèi)部類引用了方法中的局部變量,也就形成了閉包门躯。
局部?jī)?nèi)部類的概念:將類定義在方法淆党、代碼塊的內(nèi)部就形成了局部?jī)?nèi)部類,局部?jī)?nèi)部類的作用范圍只在方法或者代碼塊的作用范圍內(nèi)有效讶凉。
根據(jù)前面列出的幾種形成閉包的形式可以看出染乌,局部?jī)?nèi)部類可以構(gòu)成閉包,閉包的形成并不一定需要局部?jī)?nèi)部類懂讯。局部?jī)?nèi)部類是形成閉包的非充分非必要條件慕匠。
22.string 轉(zhuǎn)換成 integer的方式及原理。
String轉(zhuǎn)換成Integer需要使用Integer類的Integer.parseInt(String)方法域醇。
把String轉(zhuǎn)換成Integer的方法需要考慮以下幾點(diǎn):
我覺(jué)得1-4都比較容易判斷台谊,第5點(diǎn)稍微有點(diǎn)麻煩蓉媳,看源碼看了半天才明白意思。
- String的是否為null锅铅,總長(zhǎng)度大于零
- 數(shù)字部分的長(zhǎng)度是否大于0
- 字符串的第一為是否標(biāo)示正負(fù)酪呻,以及數(shù)字的正負(fù)
- 除正負(fù)位以外的其他位是否都是“0-9”之間的數(shù)字
- 數(shù)字大小是否超過(guò)Integer能表示的范圍
下面說(shuō)一下源碼中轉(zhuǎn)換的流程。(以轉(zhuǎn)換成10進(jìn)制為例子盐须,暫時(shí)不考慮其他進(jìn)制)
- 判斷條件1玩荠,是null或者長(zhǎng)度為0,直接拋出異常贼邓。
- 判斷條件3阶冈,并且標(biāo)記是正數(shù)還是負(fù)數(shù),同時(shí)把正數(shù)當(dāng)做負(fù)數(shù)來(lái)處理塑径,最后再做符號(hào)的轉(zhuǎn)換女坑。然后判斷條件2,數(shù)字部分長(zhǎng)度小于1直接拋異常统舀。
- 循環(huán)
- 判斷條件4匆骗,同時(shí)拿到當(dāng)前位代表的數(shù)字digit;
- 用方法一判斷5誉简;(方法一的具體邏輯稍后再說(shuō))
- 然后將digit乘以10存為結(jié)果result碉就。<code>result *= 10</code>
- 用方法二判斷5;(方法二的具體邏輯稍后再說(shuō))
- 將原來(lái)的result和當(dāng)前位進(jìn)行合并闷串。<code>result -= digit</code>瓮钥。因?yàn)榍懊嬲f(shuō)了當(dāng)成負(fù)數(shù)處理的,所以這里result = result-digit烹吵。
- 根據(jù)之前標(biāo)記的符號(hào)位的正負(fù)轉(zhuǎn)換成真實(shí)的數(shù)字碉熄。<code>return 是負(fù)數(shù)?result:-result</code>
下面來(lái)說(shuō)一下前面預(yù)留的幾個(gè)沒(méi)有解決的問(wèn)題:
- 為什么轉(zhuǎn)換成負(fù)數(shù)處理不轉(zhuǎn)換成正數(shù)處理年叮,或者分開(kāi)處理:
- 負(fù)數(shù)的標(biāo)示范圍比正數(shù)大,所以可以把正數(shù)轉(zhuǎn)換成負(fù)數(shù)來(lái)進(jìn)行運(yùn)算玻募,都轉(zhuǎn)成正數(shù)會(huì)出問(wèn)題.
說(shuō)一個(gè)常見(jiàn)的數(shù)字Integer的范圍是-2147483648~2147483647,在判斷是否超過(guò)最大值的時(shí)候需要使用只损,所以我們用同一個(gè)變量來(lái)標(biāo)示邊界值(最大或者最小)如果用<code>int boundary = Integer.MAX_VALUE</code>是沒(méi)有問(wèn)題的七咧,但是如果使用<code>int boundary = -Integer.MIN_VALUE</code>這就出問(wèn)題了跃惫,因?yàn)閕nt類型變量不能表示2147483648。(我當(dāng)時(shí)也弄不明白為什么不轉(zhuǎn)成正數(shù)艾栋,后來(lái)才發(fā)現(xiàn)原來(lái)最小的負(fù)數(shù)的絕對(duì)值比正數(shù)大1爆存。)- 個(gè)人感覺(jué)如果是我自己寫代碼,我就分開(kāi)寫了蝗砾,雖然有很多代碼是重復(fù)的先较,但是感覺(jué)邏輯性和可讀性會(huì)好很多携冤。但是源碼沒(méi)有這么做。
我來(lái)貼一段源代碼吧
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;//用來(lái)保存結(jié)算結(jié)果
boolean negative = false;//是否是負(fù)數(shù)
int i = 0, len = s.length();//String下標(biāo)闲勺;數(shù)組長(zhǎng)度
int limit = -Integer.MAX_VALUE;//邊界值,此處是最小值。把正數(shù)的最大值也用負(fù)數(shù)表示
int multmin;// 在這里等于limit/10毕荐,limit值的除了最后一位以外的幾位的值在這里應(yīng)該是214748364
int digit;//用來(lái)保存String的每一位代表的數(shù)字
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
//如果是負(fù)數(shù)
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;//這個(gè)值 后面有用添祸。
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
//Character.digit獲取到字符代表的數(shù)字,這里也有一個(gè)小算法
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
//如果不是0-9之間的數(shù)字 直接拋異常
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
//這里用的是一種提前判斷防止越界的辦法癌幕,如果在result *= radix之后判斷時(shí)候比最小值小衙耕,則可能存在數(shù)值越界的問(wèn)題,為了避免數(shù)值越界的問(wèn)題勺远,可以直接提前進(jìn)行比較橙喘,就是比較計(jì)算之前的數(shù)值書(shū)否會(huì)越界。
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
//這里也是一種提前判斷的方法,如果result-digit已經(jīng)比limit還小谚中,就肯定已經(jīng)超過(guò)int能表示的最小的數(shù)了渴杆。所以要提前做一下比較(提前比較的時(shí)候沒(méi)有賦值,result-digit的類型巴拉巴拉)宪塔,避免賦值的時(shí)候出現(xiàn)越界處理磁奖。
throw NumberFormatException.forInputString(s);
}
result -= digit;//如果沒(méi)有越界,就把后面的數(shù)值加到結(jié)果里某筐。
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;//根據(jù)正負(fù)號(hào)返回最終結(jié)果比搭。
}
所以這道題,簡(jiǎn)單描述的答案就是:
把字符串的第一個(gè)數(shù)字拿出來(lái)乘以十得到一個(gè)數(shù)字南誊,再把得到的數(shù)字加上第二個(gè)數(shù)字得到一個(gè)數(shù)字身诺;再上一輪拿得到的數(shù)字乘以十,再加上第三個(gè)數(shù)字得到一個(gè)結(jié)果抄囚,這個(gè)結(jié)果作為下一輪的初始值霉赡,以此類推。這中間可能會(huì)穿插一些其他細(xì)節(jié)的判斷和處理幔托。