Java 基礎面試300題 (141- 170 )
141. 編譯運行以下代碼時會發生什么?
class Mammal {}
class Cat extends Mammal { }
List<Mammal> list = new ArrayList<Cat>();
上述代碼將出現編譯錯誤。這是因為為List
指定了Mammal
哺乳動物為其元素類型,而為ArrayList
指定了Cat
為其元素類型 。在涉及到集合類型時,Java規則是,變量聲明的類型必須與其實現的類型相匹配。因此,需要在List
和 ArrayList
中指定相同的數據類型。
142.下面代碼中的第1行會編譯嗎?
class Mammal {}
class Cat extends Mammal { }
List<? super Mammal> mList = new ArrayList<Cat>(); // line 1
第1行將導致編譯錯誤。這是因為Cat
的層次結構比其超類Mammal
要低。因此,只有當<Cat>
替換為<Mammal>
或<Object>
時,上述代碼才會編譯通過。
143. 以面代碼有什么問題嗎?
List<?> mylist = new ArrayList<? extends Mammal>();
上面代碼創建了一個名為mylist
的列表, 并在列表的類型聲明中使用了通配符 (?
),會導致編譯錯誤,因為不能夠在聲明部分使用范型通配符。泛型的通配符僅允許在出現在方法參數或返回值中。
144.以面下代碼片段有效嗎?
public void getList(T t)
上述代碼使用范型參數聲明了名為getList()
的方法(范型方法)但卻沒有定義范型參數,因而是錯誤的。 下述兩種方式均可解決此問題:
//方式一,定義范型類
public class MyClass<T> {
public void getList(T t){
}
}
//方式二,定義范型方法
public class MyClass {
public <T> void getList(T t){
}
}
145. 什么是有界(Bounded)范型類型?
有界泛型類型有助于限制可用作泛型參數的類型。如下代碼示例:
public class Shape {
}
public class Circle extends Shape{
}
public class ShapeDrawer<T extends Shape> {
public void drawShape(T shape) {
System.out.println(“Drawing Shape..”);
}
}
上述代碼聲明了一個名為Shape
的超類,Circle
類是Shape
的子類。代碼還聲明了一個ShapeDrawer
類, 這是一個范型類 ,它并沒有直接定義范型類型T
,而是指定T 必須擴展Shape
類, 以對T
的類型實施了進一步限定, 這意味著范型 T
應該是Shape
的子類,如果用不是Shape
子類的參數調用drawShape()
方法 ,將發生編譯錯誤。
146. Java 集合 API中的四個主要接口是什么?
java.util.Collection
接口是Java 集合 API中的頂級接口。 它僅僅簡單表達了作為一個整體單元操作的一組值。 它有兩個主要的子接口Set
和List
。 兩者都用于存儲一組數據值; 但存儲方式略有不同。
List
允許重復的元素,同一元素可以多次出現。 此外,List
是有序的,列表中的元素按插入的順序存儲,并保持此順序。
Set
不允許元素重復,而且其中的元素是無序的,Set
不會保留 元素插入順序。
Map
接口也是一個頂級接口,用于存儲鍵值對。
147. 如何搜索數組中的特定元素?
作為集合框架的一部分,Java提供java.util.Arrays
類 , 該類有幾種實用方法, 可用于搜索數組中的特定元素。其中一種方法是Arrays.binarySearch
方法(二分搜索法), 它返回一個整數, 表示正在搜索的元素(如果存在)的在數組中的索引, 如果不存在,返回 -1
。 以下代碼演示了其使用方法:
String [] strArr = {“one”, “two”, “four”};
System.out.println(“Search index of one is:
”+Arrays.binarySearch(strArr, “one”));//輸出
Search index of one is:0
148.如果不允許元素重復,但不關心順序, 應選擇那種 集合類型?
如果想避免重復并且不關心順序,則應使用HashSet
。HashSet
是Set
接口的實現,它是未排序和無序的。TreeSet
和LinkedHashSet
也是Set
的實現。二者都不允許重復。但TreeSet
保持排序順序,而LinkedHashSet
保持插入順序。
149. 用代碼解釋如何刪除隊列頭?
Queue
接口有有一個名為poll()
的方法,可用于刪除隊列的頭部。以下代碼演示了這一點:
Queue<Integer> lList = new LinkedList<Integer>();
lList.add(100);
lList.add(200);
System.out.println(“Removed– “:lList.poll()); // Line 1
此代碼創建一個隊列,并向其添加兩個整數值。然后,它調用poll()
方法。 輸出如下:
The Element removed is: 100
因此,poll()
方法總是刪除隊列的頭部,即在隊列中添加的第一個元素。
150.哪種集合的實現允許增加或縮小其大小,并可按索引方式訪問其元素?
ArrayList
是List
接口的實現。它允許增加或縮減其尺寸。它有增加/縮減方法,可以增加或減少列表的大小。它還提供了對其元素的索引訪問。 如下代碼簡單演示了ArrayList
:
List<Integer> myList = new ArrayList<Integer>();
myList.add(2);
myList.add(4);
myList.add(6);
myList.remove(1);
Integer num = myList.get(0);
151.簡單解釋Queue
接口。
隊列接口是Java 集合 API的一部分,它擴展了java.util.Collection
接口。除了正常的集合操作外,隊列還提供了FIFO(先進先出)等隊列數據結構的功能。隊列接口有 下面幾種操作隊列的方法:
- peek():允許檢查隊列頂部的元素
- poll():刪除隊列頂部的元素
- offer():在隊列中插入一個元素
152.簡單解釋Comparator
接口 。
Comparator
是一個可用于排序的接口。自Java 8以來,它一直被指定為函數接口。它僅有單個名稱為compare
的抽象方法。該方法接受兩個對象,并返回一個布爾值,表示比較結果。比較器接口也可用于對自定義對象進行排序,為此,相應的類需要實現比較器接口,并為其compare
方法提供具體實現。如果是Java 8或更高版本, 還可以使用lambda表達式實現比較器。
153. 隊列接口主要有些什么方法?
下表是隊列主要有下面一些方法:
方法 | 描述 |
---|---|
offer(E e) | 將對象添加到隊列中。如果失敗 ,不會拋出異常。 |
peek() | 返回隊列中最后添加的對象。 |
poll() | 檢查并返回隊列中頭元素,如果存在,將其從隊列中移除。 |
remove(Object o ) | 從隊列中移除對象。 |
add(E e) | 將對象添加到隊列中。如果添加失敗,則拋出異常。 |
154.是否可以混合使用普通集合和范型集合?
可以混合范型集合和非范型集合。如下代碼片段示例:
public void insertDouble() {
List<Double> myDoubleList = new ArrayList<Double>();
myDoubleList.add(2.0); // Line 1
myDoubleList.add(4.0); // Line 2
insertSomethingElse(myDoubleList);
}
public void insertSomethingElse(List myDoubleList) {//Line 3
myDoubleList.add(“Hello World”); //Line 4
}
在上述代碼中,第1行和第2行添加兩個雙精度Double
數到列表中,該列表是一個類型為Double
的范型列表。 在第3行的insertSomethingElse()
方法聲明中,沒有為參數myDoubleList
變量指定泛型類型。因此,任何類型的值都可以添加到此列表中。第4行將一個字符串添加到該列表中。
155. Collection接口上有哪些重要方法?
以下是Collection 接口上的一些重要方法:
方法 | 描述 |
---|---|
add | 向集合中添加對象。 |
remove | 從集合中移除對象。 |
contains | 檢查集合中是否存在對象。 |
size | 返回集合的長度。 |
iterate | 在集合上進行迭代。 |
156.Java中可用的包裝類是什么?
包裝類是對應于Java基礎類型的類,其主要作用是將基礎類型轉換為對象類型,反之亦然。使用Collections
時,包裝類特別有用。由于Collections
不接受原始類型,應使用相應的包裝類型進行包裝。Java支持以下包裝類:
基礎類型 | 包裝類型 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
157. 以下代碼的輸出是什么?
public class BooleanDemo {
public static void main(String a[]){
//create Boolean using boolean primitive type
boolean boo1 = true;
Boolean booObj1 = new Boolean (boo1); //line 1
System.out.println(“Wrapper class Boolean output: “+booObj1);
Boolean booObj2 = new Boolean (“false”); //line 2
System.out.println(“Wrapper class Boolean output: “+booObj2);
System.out.println(booObj1.booleanValue());
}
}
第1行創建一個布爾包裝器類型boolObj1
,對應于bool
類型變量。第2行使用String
值創建一個布爾包裝器boolObj2
。Java自動將包裝類型轉換為基礎類型。此外,在布爾包裝類上有一個名為booleanValue()
的方法,返回原始布爾值。因此,上述代碼將打印以下輸出:
Wrapper class Boolean output: true
Wrapper class Boolean output: false
true
158. 如何將字符串“100.55”
轉換為雙精度(Double
)?如何將雙精度數100.55
轉換為字符串?
可以使用 Double.parseDouble()
方法將字符串轉換為Double
,如下所示:
Double doubleValue = Double.parseDouble(“100.55”);
可以使用Double.toString()
方法將Double
轉換為字符串,如下所示:
String stringValue = Double.toString(100.55);
159.以下代碼的輸出是什么?
Double doubleValue = Double.parseDouble(“Java”);
System.out.println(doubleValue);
上述代碼編譯沒有問題,但是在執行時會拋出NumberFormatException
,這是因為JVM無法將字符串Java
轉換為Double
值。
160.什么是Java中的包裝類?
Java是一種面向對象的語言。但是Java中的基礎數據類型不是對象類型。有時,需要這些類型的對象等價物。比如使用Collections
時, 由于集合只能包含對象,不能包含基礎類型。為了解決這個問題,Java引入了包裝類,用來將基礎類型包裝成對象。每個基礎類型都有一個相應的包裝類,可以創建該類型的對象。同樣,也可以將包裝對象轉換為基礎類型。例如,與int
基礎類型對應的包裝器類是 java.lang.Integer
。Java在基礎類型和相應的包裝類之間自動轉換。
161.什么是自動裝箱?
自動裝箱(和自動拆箱)是Java 5中引入的一項功能,它可以幫助程序員減少一些冗余代碼。
例如,為了給包裝類分配一個基礎類型的值,如果沒有自動裝箱,則需要按以下方式編寫包裝代碼:
Integer integerValue = new Integer(1000); //wrapping
通過自動裝箱,上述代碼可以重寫如下:
Integer integerValue = 1000;
同樣,為了將包裝類型的值分配給基礎類型 ,需要編寫解包代碼。如下示例:
Integer integerValue = 1000;
int iValue = integerValue.intValue(); //unwrapping
通過自動裝箱,上述代碼可以重寫如下:
Integer integerValue = 1000;
int iValue = integerValue;
162. 如何在Java中創建線程?
在Java中創建線程有兩種方法:
通過擴展Thread
類:在這種方法中, 需要創建一個擴展內置Thread
類的類,重寫其run()
方法。如下代碼示例:
public class MyThread extends Thread {
public void run(){
System.out.println(“In Thread Body..”);
}
}
通過實現Runnable
的接口:在這種方法中,需要創建一個實現內置Runnable
接口的類, 實現該接口的run()
方法。 如下代碼所示 :
public class MyThread implements Runnable{
public void run(){
System.out.println(“In Thread Body..”);
}
}
163. Java中有哪幾種不同類型的線程?
Java中有2種類型的線程: 用戶定義的線程和守護線程。
用戶定義的線程是由用戶以編程方式創建的線程, 它們的優先級較高。JVM會等待這些線程執行完成。
守護線程主要由JVM創建(雖然用戶定義的線程也可以顯式設置為守護線程),通常用于后臺進程,如垃圾收集。
一旦所有用戶線程(非Daemon線程)停止運行,JVM就會停止運行,JVM并不會等待守護線程停止,或執行完成。
164.是否可以不調用線程對象的start()
方法,直接調用其run()
方法?
可以直接調用線程對象的run()
方法,而不調用其start()
方法。但這沒有什么意義,因為,它不會產生新的線程,run()
方法中的代碼將在調用它的同一線程中執行。
165.當執行以下代碼時,幕后會發生什么?
class Test {
public static void main(String argument[]) {
System.out.println(“Thread Example...”);
}
}
執行上述代碼時,JVM會首先創建一個線程, 該線程執行main
方法中的代碼。 執行完畢,線程終止。
166. Thread
類主要有哪些方法?
方法 | 描述 |
---|---|
start() | 啟動線程并調用 run() 方法。 |
run() | 定義線程中執行的代碼。 |
sleep() | 使運行中的線程暫停指定的時間。 |
setName() | 將線程的名稱設置為指定的值。 |
join() | 在不超過指定的多時間內,等待線程死亡。 |
isAlive() | 返回一個布爾值,指示當前線程是否存活。 |
setPriority() | 將線程的優先級更改為指定的值。 |
167.線程有哪些狀態?它們之間是什么關系?
線程有下面五個狀態。如下表所示:
狀態 | 描述 |
---|---|
New (新建) | 創建了一個新的線程,但尚未調用 start() 方法時,線程處于新狀態。 |
Runnable (可運行) | 在調用 start() 方法后,但線程調度程序尚未開始執行線程之前,線程處于可運行狀態。 |
Running (正在運行) | 當線程調度程序開始執行線程時,線程處于運行狀態,執行 run() 方法體內的代碼。 |
Waiting / Blocking(等待/阻塞) | 如果線程是活動的,但不符合運行條件,則處于阻塞狀態。 |
Dead (死亡) | 線程執行完畢后,進入死亡狀態。 |
168. 如何使用Thread
類創建線程 ?
以下代碼使用“線程”類創建一個線程:
class MyThread extends Thread {
public static void main(String argument[]) {
MyThread thread = new MyThread ();
thread.start(); // Line 1
}
public void run() {
System.out.println(“Inside Run Method..”);// Line 2
}
}
上面的代碼定義了一個名為MyThread
的類,該類擴展了Thread
類。執行第1行時,會生成一個新線程,然后該線程將執行run()
方法中的代碼。
169.如何使用 Runnable
接口創建線程?
下面代碼通過實現Runnable
接口創建一個新線程:
class MyThread implements Runnable {
public static void main(String argument[]) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread); //Line 1
thread.start(); // Line 2
}
public void run() {
System.out.println(“Inside Run Method..”);
}
}
上面的代碼定義了一個名為MyThread
的類,該類實現了Runnable
接口 。然后, 創建了一個該類的對象 myThread
,并在第1行將其傳遞給Thread
類以創建一個新線程對象。 第2行調用start()
方法生成一個新線程, 并執行run()
方法中定義的代碼。
170.是否可以創建多個線程? 多線程之間如何相互通信?
可以在Java程序中創建多個線程。對于兩個線程相互通信,需要使用來自Object
類的方法wait()
、notify()
和notifyAll()
。
wait()
方法導致當前線程暫停,直到其他線程在同一對象上調用notify()
。
如果有許多對象都在等待特定對象,notify()
方法會導致任何一個線程恢復(執行)。notifyAll()
方法會使所有等待特定對象的線程恢復(執行)。