面向對象的特征有哪些方面?
原來學的時候說是三種特征,即封裝、繼承和多態。
現在一般說面向對象有四大特性,即抽象、封裝、繼承和多態。
1.抽象:將同類對象的共同特征提取出來構造類。
2.封裝:將數據隱藏起來,對數據的訪問只能通過特定接口。
3.繼承:基於基類創建新類。
4.多態:不同子類型對象對相同消息做出不同響應。
訪問修飾符public,private,protected以及不寫(默認)的區別?
訪問權限逐級遞減,public>protected>default(不寫)>private
public最高可以被其他包訪問
protected最高可以被子類訪問
default(默認,不寫)最高可以被同包訪問
private只能在當前類訪問
String是基本數據類型嗎?
不是。Java只有8個基本數據類型,分別是byte(字節類型)、short(短整型)、int(整型)、long(長整型)、float(浮點型)、double(雙精度浮點型)、char(字符型)、boolean(布爾類型);除了基本類型(Primitive Type),剩下的都是引用類型(Reference Type),Java5以后引入的枚舉類型也算是一種比較特殊的引用類型。
float f = 3.4;這個表達式是否正確?
不正確。在Java里面,沒有小數點的默認是int類型,有小數點的默認是double類型。因此3.4是雙精度數,將雙精度浮點型(double)賦值給浮點型(float)屬於下轉型(down-casting,也稱為窄化),會造成精度損失,因此需要強制類型轉換 float f = (float)3.4 或者寫成 float f = 3.4f 。
short s1 = 1; s1 = s1 + 1; 這個表達式有錯嗎?short s1 = 1; s1 += 1; 這個表達式有錯嗎?
前面這個表達式,由於1是int類型,因此 s1 + 1 的運算結果也是int類型,再把int類型的結果賦值給short類型的s1是會報錯的,需要對結果進行強制類型轉換才能通過編譯。
后面這個表達式, s1 += 1; 相當於 s1 = (short)(s1 + 1); 表達式,其中會有隱含的強制類型轉換,可以正常通過編譯。
Java中有沒有goto?
goto是Java中的保留字,但是在目前版本的Java中沒有使用,即沒有goto語句。
int和Integer有什么區別?
Java是一個近乎純潔的面向對象編程的語言,但是為了編程的方便還是引入了基本數據類型。為了能夠將這些基本數據類型當作對象操作,Java為每一個基本數據類型都引入了相應的包裝類型(Wrapper Class),int類型的包裝類型就是Integer。從Java5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。
Java為每個原始類型(基本數據類型)提供了相應的包裝類型:
原始類型:byte,short,int,long,float,double,char,boolean
包裝類型:Byte,Short,Integer,Long,Float,Double,Character,Boolean
Integer a = new Integer(3); Integer b = 3; // 將3自動裝箱成Integer類型 int c = 3; System.out.println(a == b); // false 兩個引用沒有引用同一對象 System.out.println(a == c); // true a自動拆箱成int類型再和c比較
下面這段代碼也和自動裝箱/拆箱有關系:
Integer f1 = 100, f2 = 100, f3 = 128, f4 = 128; System.out.println(f1 == f2); // true System.out.println(f3 == f4); // false
為什么第一個運算的結果是true,第二個卻是false呢?
首先注意,f1、f2、f3和f4都是Integer對象引用,所以下面的==運算比較的不是值而是引用。
裝箱的本質是什么呢?是當我們給一個Integer對象賦一個int類型值的時候,會調用Integer類的靜態方法valueOf(),看看valueOf()方法的源代碼就知道為什么會產生這樣的結果。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
IntegerCache是Interger類的內部類,其代碼如下:(好好研究)
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() {} }
簡單得說,如果整型字面量的值的范圍在-128到127之間,那么就不會new一個新的Integer對象,而是直接引用常量池中的Integer對象。
&和&&的區別?
&運算符有兩種用法,一是按位與,二是邏輯與。&&運算符是短路與。
邏輯與跟短語與的差別是非常大的,雖然二者都要求運算符左右兩邊的布爾值都是true整個表達式的值才是true,但是如果&&運算符左邊的表達式是false的話,右邊的表達式會直接被短路掉,不會進行運算,這就是&&運算符被稱為短路運算的原因。
很多時候我們需要用到的可能都是&&而不是&,例如在驗證用戶登錄的時候,判定用戶名不是null而且不是空字符串,應當寫為:userName != null && !userName.equals(""),二者的順序不能交換,更不能使用&運算符,因為第一個條件如果不成立,根本不能進行字符串的equals比較,否則會產生NullPointerException異常。
JavaScript中的短路運算更是強大,因為運算符兩邊不僅僅能是表達式,還能是值。
解釋內存中的棧(Stack)、堆(Heap)和方法區(Method Area)的用法。
通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用JVM中的棧空間;
而通過new關鍵字和構造器創建的對象則放在堆空間,堆是垃圾收集器管理的主要區域,由於現在的垃圾收集器都采用分代收集算法,所以堆空間還可以細分為新生代和老生代,再具體一點可以分為Eden、Survivor(又可分為From Survivor和To Survivor)、Tenured;
方法區和堆都是各個線程共享的內存區域,用於存儲已經被JVM加載的類信息、常量、靜態變量、JIT編譯器編譯后的代碼等數據;程序中的字面量(Literal),如直接書寫的100、"hello"和常量都是放在常量池中,常量池是方法區的一部分。
棧空間操作起來最快但是棧很小,通常大量的對象都是放在堆空間,棧和堆的大小都可以通過JVM的啟動參數來進行調整,棧空間用光了會引發StackOverflowError,而堆和常量池空間不足則會引發OutOfMemoryError。
String str = new String("hello");
上面的語句中,變量str放在棧區,用new關鍵字創建出來的字符串對象放在堆區,而"hello"這個字面量是放在方法區的。
較新版本的Java(Java6的某個更新開始)中,由於JIT編譯器的發展和"逃逸分析"技術的逐漸成熟,棧上分配、標量替換等優化技術使得對象一定分配再堆上這件事情已經變得不那么絕對了。
運行時常量池相當於Class文件,因此常量池具有動態性。即Java語言並不要求常量一定只有編譯期間才能產生,運行期間也可以將新的常量放入池中,String類的intern()方法就是這樣的。
String s1 = new StringBuilder("go").append("od").toString(); System.out.println(s1.intern() == s1); // true String s2 = new StringBuilder("ja").append("va").toString(); System.out.println(s2.intern() == s2); // false
為什么會產生這樣的結果,要好好思考一下。
Math.round(11.5)等於多少?Math.round(-11.5)等於多少?
Math.round(11.5)的結果是12,Math.round(-11.5)的結果是-11。
四舍五入的原理是在參數上加0.5,然后進行向下取整。
那么Math.round(11.6)和Math.round(-11.6)的結果又是什么呢?
答案是12和-12。
11.6 + 0.5 = 12.1,然后向下取整為12;
-11.6 + 0.5 = -11.1,然后向下取整為-12。
要特別理解向下取整是什么意思。
switch是否能作用在byte上,是否能作用在long上,是否能作用在String上?
在Java5以前,switch(expr)中,expr只能是byte、short、char、int。
從Java5開始,Java中引入了枚舉類型,expr也可以是enum類型,從Java7開始,expr還可以是字符串類型(String)。
但是長整型(long)在目前的所有Java版本中都是不可以的。
用最有效率的方法計算2乘以8?
2<<3(左移三位相當於乘以2的三次方,右移三位相當於除以2的三次方)。
我們為編寫的類重寫hashCode()方法的時候,可能會看到如下所示的代碼,其實我們不太理解為什么要使用這樣的乘法運算來產生哈希碼(散列碼),而且為什么這個數是素數,為什么通常選擇31這個數?
選擇31,是因為可以用移位和減法運算來代替乘法,從而得到更好的性能。說到這里你可能已經想到了:31 * num等價於(num << 5) - num,左移5位相當於乘以2的5次方再減去自身就相當於乘以31,現在的VM都能自動完成這個優化。
public class PhoneNumber { private int areaCode; private String prefix; private String lineNumber; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + areaCode; result = prime * result + ((lineNumber == null) ? 0 : lineNumber.hashCode()); result = prime * result + ((prefix == null) ? 0 : prefix.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; PhoneNumber other = (PhoneNumber) obj; if (areaCode != other.areaCode) return false; if (lineNumber == null) { if (other.lineNumber != null) return false; } else if (!lineNumber.equals(other.lineNumber)) return false; if (prefix == null) { if (other.prefix != null) return false; } else if (!prefix.equals(other.prefix)) return false; return true; } }
數組中有沒有length()方法?String有沒有length()方法?
數組沒有length()方法,但是有length的屬性。
String[] stringArr = {"1", "2", "3"}; System.out.println(stringArr.length); // 3
String有length()方法,沒有length屬性。
System.out.println("12345".length); // 5
在JavaScript中,獲得字符串的長度是通過length屬性得到的,這一點容易和Java混淆。
在Java中,如何跳出當前的多重嵌套循環?
在最外層循環前加一個標記如A,然后用break A;可以跳出多重循環。(Java中支持帶標簽的break和continue語句,作用有點類似於C和C++中的goto語句,但是就像要避免使用goto一樣,應該避免使用帶標簽的break和continue,因為它不會讓你的程序變得更優雅,很多時候甚至有相反的作用,所以這種語法非常不建議使用)。
構造器(Constructor)是否可以被重寫(Override)?
構造器不能被繼承,因此不能被重寫(子類),但是可以被重載(同個類)。
兩個對象值相同(x.equals(y) == true)但是卻可有不同的hashCode,這句話對不對?
不對。如果兩個對象x和y滿足equals()結果為true,那么它們的哈希碼(hashCode)應當相同。
Java對於equals)(方法和hashCode()方法是這樣規定的:
1.如果兩個對象相同(equals()方法返回true),那么它們的hashCode值一定要相同;
2.如果兩個對象的hashCode相同,它們並不一定相同。
當然,你未必要按照要求去做,但是如果你違背了上述原則,就會發現在使用容器時,相同的對象可以出現在Set集合中,同時增加新元素的效率會大大下降(對於使用哈希存儲的系統,如果哈希碼頻繁地沖突將會造成存取性能急劇下降)。
是否可以繼承String類?
String類是final類,不可以被繼承。
繼承Stirng本身就是一個錯誤的行為,對String類型最好的重用方式是關聯關系(Has-A)和依賴關系(Use-A)而不是繼承關系(Is-A)。
"如果你想去遠方,就要先想辦法離開你現在站的位置。"