[金三銀四面試季]Java面向對象高頻面試題
金三銀四正是跳槽漲薪的好時節,波哥給大家整理了很多面試高頻的問題,助力大家找到更好的工作哦,點點關注,感謝支持。
面向對象面試題
1.談談你對面向對象思想的理解?
面向過程編程(POP):是一種以過程為中心的編程思想
面向對象編程(OOP):相對于面向過程來講的,面向對象方法,把相關的數據和方法組織為一個整體來看待,從更高的層次來進行系統建模,更貼近事物的自然運行模式
2. 談談你對面向對象的特征的理解
封裝
即把對象的屬性和方法包裹起來, 只能通過約定好的接口實現訪問
封裝是一種信息隱藏技術,在java中通過關鍵字private實現封裝。
package com.bobo.interview0001.domain; /** * 屬性+方法 */ public class User { // 將age設置為私有屬性 private int age; public String name; /** * 提供給外界設置age屬性的方法 * @param age */ public void setAge(int age){ if(age < 0 || age > 120){ System.out.println("age的設置不合法!!!"); age = 0; } this.age = age; } /** * 提供給外界獲取age屬性的方法 * @return */ public int getAge(){ return age; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
封裝的好處:可以將私有數據和公共數據分離,保護私有數據,提高了程序的安全性!!!
繼承
繼承是面向對象的最顯著的一個特征。繼承是從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行為,并能擴展新的能力。
為什么要繼承呢:
反映現實的真實關系
減少代碼冗余
對父類的屬性和方法進行擴展和重寫
繼承中,子類不可以選擇性的繼承父類的東西,而是全部繼承父類的屬性和方法。其中父類又叫超類或基類,子類又叫派生類。父類是子類的一般化,子類是父類的特化(具體化)。java中不支持多繼承,一個類最多只能有一個父類。而在java中多繼承是通過接口實現的。
public class Person { private String userName; private int age; private String sex; private String address; private String idCard; public void show(){ System.out.println("父類中的方法"); } }
1
2
3
4
5
6
7
8
9
10
11
12
public class Doctor extends Person{ @Override public void show() { System.out.println("Doctor ... show"); } }
1
2
3
4
5
6
public class Student extends Person{ private String ClassNum; private String stuNo; // .... public void learn(){ } @Override public void show() { System.out.println("Student ... show"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
多態
Java對象的多種狀態,多態主要是基于繼承和重寫,最終可以實現相同的類型調用相同的方法,結果不相同
public static void main(String[] args) { // 多態的體現 Person p1 = new Student(); p1.show(); Person p2 = new Doctor(); p2.show(); }
1
2
3
4
5
6
7
3.介紹下訪問權限修飾符
重點注意protected和default,protected相比較default多了一個不同包下面的子類可見
4. String是基本數據類型嗎?
Java中的8個基本數據類型:
byte
short
int
long
float
double
char
boolean
String類型顯然不在8個基本數據類型之中,String類型是我們講的引用類型
5. float f=6.6這樣寫是否正確?
不正確的,6.6默認是雙精度類型,將double類型賦值給float類型,屬性向下轉型,會造成精度丟失
public class Demo01 { public static void main(String[] args) { // 小數 默認的是 double類型 雙精度類型 向下轉型 精度丟失 float f2 = 1.1; float f = 6.6F; float f1 = 7.7F; } }
1
2
3
4
5
6
7
8
9
6 程序題1
short s1 = 1; s1 = s1 + 1; // 此處需要強制類型轉換
1
2
有錯嗎? – 有錯
short s1 = 1; s1 += 1; // s1 = (short) ( s1 + 1 )
1
2
有錯嗎?-- 沒有錯
7. 程序題2
以下程序的輸出結果是:
public static void main(String[] args) { Integer f1=100,f2=100,f3=150,f4=150; System.out.println(f1==f2); System.out.println(f3==f4); }
1
2
3
4
5
輸出結果:true,false
原因解釋:
IntegerCache中的定義
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; // 創建了一個緩存的數組 長度256 cache = new Integer[(high - low) + 1]; int j = low; // 循環256次 for(int k = 0; k < cache.length; k++) // 一次創建 -128 到 127 對應的Integer對象,并保存到數組中 cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
valueOf(int)中的源碼
public static Integer valueOf(int i) { // 如果 i 在 -128 到 127之間 if (i >= IntegerCache.low && i <= IntegerCache.high) // 就從緩存的數組中返回之前創建的 Integer對象 return IntegerCache.cache[i + (-IntegerCache.low)]; // 否則創建一個新的Integer對象 return new Integer(i); }
1
2
3
4
5
6
7
8
提醒:越是貌似簡單的面試題,其中的玄機就越多,要求面試者需要相對深厚的功力。
8. &和&&的區別
&:按位與
int a = 5; // 101 int b = 3; // 011 int c = a & b;// 001
1
2
3
邏輯與:都為true結果才為true,左邊如果為false,右邊表達式還是會運行
&&:短路與:都為true結果才為true,左邊如果為false,右邊表達式不會運行
public static void main(String[] args) { int a = 5; // 101 int b = 3; // 011 int c = a & b;// 001 System.out.println("c = " + c); // 邏輯與 & 左右兩邊都為true 那么結果才為true // 左邊為false,結果為false,但是右邊的表達式還是會進行運算 if(a == 0 & a ++ > 0){ System.out.println("------"); } System.out.println("a = " + a); // && 短路與 左邊如果為false,那么整體的結果也為false,而且右邊的表達式不會運算 if(a == 0 && a ++ > 0){ System.out.println("------"); } System.out.println("a = " + a); String name = null; // 短路與在實際使用的時候還是很有使用場景的 if(name != null & !name.equals("")){ } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
9. switch可以使用的數據類型
JDK5之前:byte,short,int,char
JDK5之后:Java中引入了枚舉類型,枚舉類型也可以
JDK7之后:可以使用String,但是long類型一直是不可以的
10. 用最有效的方法計算2乘以8?
2 << 3(左移3位,相當于乘以2的3次方)
11. 構造器是否可以被重寫?
構造器不能被繼承,因此是不能被重寫的,但是我們可以重載構造器
12. 是否可以繼承String類?
String類是final類型,所以不可以被繼承。
13. 重載和重寫的區別
方法的重載和重寫都是實現多態的方式, 區別在于前者實現的是編譯時的多態性, 而后者實現的是運行時的多態性。
重載:在同一個類中,同名的方法如果有不同的參數列表(參數類型,參數個數,順序)視為重載
package com.bobo.interview0006; public class Demo01 { public static void main(String[] args) { A a = new A(); a.show1(); a.show1(18); a.show1("a",18); a.show1(18,"aaa"); } } class A{ /** * 方法的重載:在同一個類中 方法名稱相同 參數列表不同 */ public void show1(){ } public void show1(String name){ } public void show1(int a){ } public void show1(String name,int a){ } public void show1(int a,String name){ } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
重寫:發生在父子關系類中,子類重寫父類的方法,有相同的返回類型或子類
class B extends A{ /** * 重寫,子類重寫父類中的方法 */ @Override public void show1() { // super.show1(); System.out.println("show1 --- B"); } /** * 重寫的返回類型必須相同或者是父類返回類型的子類 * @param n * @return */ @Override public Integer fun1(Number n) { return 666; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
14. 抽象類和接口的區別
接口和抽象類都不能實例化
抽象類相比于普通類多的限制是可以添加抽象方法
接口中只能聲明常量和抽象方法且訪問修飾符必須都為public,在JDK8之后可以定義default方法和static方法
package com.bobo.interview0007; public class Demo01 { public static void main(String[] args) { } } /** * 抽象類 * 不能被實例化 * 可以聲明普通的屬性和方法 * 還可以定義抽象方法 */ abstract class A{ private int a; public int b; public void fun1(){ } public abstract void fun2(); } /** * 接口 * 不能被實例化 * 屬性只能是常量 * 方法只能是抽象方法 * JDK1.8之后可以有 default和static方法 */ interface B{ public final int a = 9; // public abstract 方法默認的類型 可以不添加 public abstract void show1(); /** * default方法 */ public default void fun1(){ } /** * 靜態方法 */ public static void fun2(){ } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
15. 靜態內部類和內部類的區別
靜態內部類是被聲明為static的內部類,它可以不依賴外部類的實例被實例化,而通常我們的內部類是需要通過外部類的實例才能實例化。
public class OutterClass { /** * 普通內部類 */ class InnerClass{ public void show(){ System.out.println("InnerClass ..."); } } /** * 靜態內部類 */ static class StaticInnerClass{ public void show(){ System.out.println("StaticInnerClass ..."); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
測試代碼
public static void main(String[] args) { // 獲取普通內部類對象 OutterClass.InnerClass innerClass = new OutterClass().new InnerClass(); innerClass.show(); // 獲取靜態內部類對象 OutterClass.StaticInnerClass staticInnerClass = new OutterClass.StaticInnerClass(); staticInnerClass.show(); }
1
2
3
4
5
6
7
8
16. 抽象方法是否可以同時是靜態的本地方法及被synchronized修飾?
抽象方法所在的類肯定是抽象類,
不能被static修飾,因為靜態不能被重寫,而抽象方法必須要被子類重寫,所以矛盾。
不能被native修飾,本地方法是由C代碼實現的,而抽象方法沒有實現,也是矛盾的
不能被synchronized修飾,同步是和方法的實現的細節有關系的,而抽象方法沒有具體的實現,也是相互矛盾的
17. 靜態變量和實例變量的區別
靜態變量:是被static所修飾的變量,也稱為類變量,它是屬性類對象的,不屬于類的任何一個對象。靜態變量是對象間共享的
實例變量:數據具體的某個對象,必須要先創建對象,然后通過該對象來訪問實例變量,
18.靜態方法中是否可以訪問普通方法
肯定不可以,靜態方法是在類加載階段就創建的,而普通方法是屬性對象的,在類加載階段還沒有對象,所以是矛盾的,靜態方法只能訪問靜態成員,普通方法必須要先創建對象,然后通過對象來調用普通方法。
public class Person { static void show(){ // 靜態方法中不可以調用 普通方法 // speak(); } void speak(){ // 普通方法可以調用靜態方法 show(); } }
1
2
3
4
5
6
7
8
9
10
11
12
19.如何實現克隆
package com.dpb.prototype; import java.io.Serializable; import java.util.Date; /** * 原型類:被克隆的類型 * @author dengp * */ public class User implements Cloneable,Serializable{ private String name; private Date birth; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 實現克隆的方法 */ public Object clone() throws CloneNotSupportedException{ return super.clone(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
深度克隆
package com.dpb.prototype; import java.io.Serializable; import java.util.Date; /** * 原型類:被克隆的類型 * 深度克隆測試 * @author dengp * */ public class User2 implements Cloneable,Serializable{ private String name; private Date birth; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 實現克隆的方法 * 深度克隆(deep clone) */ public Object clone() throws CloneNotSupportedException{ Object object = super.clone(); // 實現深度克隆(deep clone) User2 user = (User2)object; user.birth = (Date) this.birth.clone(); return object; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
序列化和反序列化
public static void main(String[] args) throws CloneNotSupportedException, Exception { Date date = new Date(1231231231231l); User user = new User(); user.setName("波波烤鴨"); user.setAge(18); user.setBirth(date); System.out.println("-----原型對象的屬性------"); System.out.println(user); System.out.println(user.getName()); System.out.println(user.getBirth()); //使用序列化和反序列化實現深復制 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(user); byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis); //克隆好的對象! User user1 = (User) ois.readObject(); // 修改原型對象的值 date.setTime(221321321321321l); System.out.println(user.getBirth()); System.out.println("------克隆對象的屬性-------"); System.out.println(user1); System.out.println(user1.getName()); System.out.println(user1.getBirth()); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
20.介紹下final關鍵字
21.程序運行題
以下程序的輸出結果是:
public class Hello { /** * Class 對象 * Object 普通對象 * Java的類加載機制要比較清楚 * @param args */ public static void main(String[] args) { A ab = new B(); // 普通對象是根據 Class對象來創建的 1a2b ab = new B(); // 因為 B的類對象已經被加載了,這時靜態變量初始化就不會執行了 2b // 1a2b2b } } class A { static { System.out.print("1"); } public A() { System.out.print("2"); } } class B extends A{ static { System.out.print("a"); } public B() { super(); System.out.print("b"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
輸出結果為:
1a2b2b
1
程序執行的順序:
創建對象時構造器的調用順序是:先初始化靜態成員,然后調用父類構造器,再初始化非靜態成員,最后調用自身構造器。
22.數據類型之間的轉換
字符串–》基本數據類型
基本數據類型–》字符串
public static void main(String[] args) { String s1 = "99"; // 轉換為int類型 // 1.通過int對應的包裝類 Integer 中提供的 parseXXX(String) 方法 int i = Integer.parseInt(s1); // 2.通過int 對應的包裝來 Integer 中的 valueOf(String) 方法 Integer i2 = Integer.valueOf(s1); // 將一個基本數據類型轉換為 String類型 int age = 18; String s2 = age + ""; String s3 = String.valueOf(age); }
1
2
3
4
5
6
7
8
9
10
11
12
13
23.return和finally的執行順序問題
分析如下的代碼程序,給出輸出結果,并給出原因
public class Demo02 { public static void main(String[] args) { Demo02 demo02 = new Demo02(); System.out.println(demo02.getName("bobo")); } public String getName(String name){ String res = ""; try { res = name; return res; }finally { res = "波波烤鴨"; } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
輸出的結果是:bobo
原因:通過指令分析我們可以發現在 return 代碼執行的時候會將局部變量保存在 棧幀的頂部,然后在finally中修改的還是原來的棧幀位置的局部變量,最終返回的信息還是棧幀頂部的變量,所以finally代碼塊在return關鍵字之后會執行,但是不會改變棧幀頂部的信息。
還有一種情況需要注意,如果finally和try塊中都有return關鍵字會怎么樣呢?
public class Demo02 { public static void main(String[] args) { Demo02 demo02 = new Demo02(); System.out.println(demo02.getName("bobo")); } public String getName(String name){ String res = ""; try { res = name; return res; }finally { res = "波波烤鴨"; return res; // 指令中返回的就不是棧幀頂部的數據了 而是 res 對應的棧幀位置 } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
通過指令我們可以看到在finally中的return關鍵字的指令返回的就是finally中的局部變量的信息,可以理解為finally中的return會覆蓋掉try塊中的return邏輯。
Java 面向對象編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。