- Sun-Java程序員認證考試題庫 推薦度:
- 相關(guān)推薦
2017年java程序員考試試題
Sun Java認證分為兩個(gè)級別:Sun 認證Java程序員和Sun 認證Java開(kāi)發(fā)員。下面是小編整理的關(guān)于java程序員考試試題,歡迎大家參考!
1、是否可以繼承String 類(lèi)?
答:String 類(lèi)是final類(lèi),不可以被繼承。
補充:繼承String本身就是一個(gè)錯誤的行為,對String類(lèi)型最好的重用方式是關(guān)聯(lián)(HAS-A)而不是繼承(IS-A)。
2、當一個(gè)對象被當作參數傳遞到一個(gè)方法后,此方法可改變這個(gè)對象的屬性,并可返回變化后的結果,那么這里到底是值傳遞還是引用傳遞?
答:是值傳遞。Java 編程語(yǔ)言只有值傳遞參數。當一個(gè)對象實(shí)例作為一個(gè)參數被傳遞到方法中時(shí),參數的值就是對該對象的引用。對象的屬性可以在被調用過(guò)程中被改變,但對象的引用是永遠不會(huì )改變的。C++和C#中可以通過(guò)傳引用或傳輸出參數來(lái)改變傳入的參數的值。
補充:Java中沒(méi)有傳引用實(shí)在是非常的不方便,這一點(diǎn)在Java 8中仍然沒(méi)有得到改進(jìn),正是如此在Java編寫(xiě)的代碼中才會(huì )出現大量的Wrapper類(lèi)(將需要通過(guò)方法調用修改的引用置于一個(gè)Wrapper類(lèi)中,再將Wrapper對象傳入方法),這樣的做法只會(huì )讓代碼變得臃腫,尤其是讓從C和C++轉型為Java程序員的開(kāi)發(fā)者無(wú)法容忍。
3、String 和StringBuilder、StringBuffer 的區別?
答:Java 平臺提供了兩種類(lèi)型的字符串:String和StringBuffer / StringBuilder,它們可以?xún)Υ婧筒僮髯址。其中String是只讀字符串,也就意味著(zhù)String引用的字符串內容是不能被改變的。而StringBuffer和StringBuilder類(lèi)表示的字符串對象可以直接進(jìn)行修改。StringBuilder是JDK 1.5中引入的,它和StringBuffer的方法完全相同,區別在于它是在單線(xiàn)程環(huán)境下使用的,因為它的所有方面都沒(méi)有被synchronized修飾,因此它的效率也比StringBuffer略高。
補充1:有一個(gè)面試題問(wèn):有沒(méi)有哪種情況用+做字符串連接比調用StringBuffer / StringBuilder對象的append方法性能更好?如果連接后得到的字符串在靜態(tài)存儲區中是早已存在的,那么用+做字符串連接是優(yōu)于StringBuffer / StringBuilder的append方法的。
補充2:下面也是一個(gè)面試題,問(wèn)程序的輸出,看看自己能不能說(shuō)出正確答案。
package com.lovo;
public class StringEqualTest {
public static void main(String[] args) {
String a = "Programming";
String b = new String("Programming");
String c = "Program" + "ming";
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(a.intern() == b.intern());
}
}
4、重載(Overload)和重寫(xiě)(Override)的區別。重載的方法能否根據返回類(lèi)型進(jìn)行區分?
答:方法的重載和重寫(xiě)都是實(shí)現多態(tài)的方式,區別在于前者實(shí)現的是編譯時(shí)的多態(tài)性,而后者實(shí)現的是運行時(shí)的多態(tài)性。重載發(fā)生在一個(gè)類(lèi)中,同名的方法如果有不同的參數列表(參數類(lèi)型不同、參數個(gè)數不同或者二者都不同)則視為重載;重寫(xiě)發(fā)生在子類(lèi)與父類(lèi)之間,重寫(xiě)要求子類(lèi)被重寫(xiě)方法與父類(lèi)被重寫(xiě)方法有相同的返回類(lèi)型,比父類(lèi)被重寫(xiě)方法更好訪(fǎng)問(wèn),不能比父類(lèi)被重寫(xiě)方法聲明更多的異常(里氏代換原則)。重載對返回類(lèi)型沒(méi)有特殊的要求。
補充:華為的面試題中曾經(jīng)問(wèn)過(guò)這樣一個(gè)問(wèn)題:為什么不能根據返回類(lèi)型來(lái)區分重載,說(shuō)出你的答案吧!吐舌頭
5、描述一下JVM 加載class文件的原理機制?
答:JVM 中類(lèi)的裝載是由類(lèi)加載器(ClassLoader) 和它的子類(lèi)來(lái)實(shí)現的,Java中的類(lèi)加載器是一個(gè)重要的Java 運行時(shí)系統組件,它負責在運行時(shí)查找和裝入類(lèi)文件中的類(lèi)。
補充:
1.由于Java的跨平臺性,經(jīng)過(guò)編譯的Java源程序并不是一個(gè)可執行程序,而是一個(gè)或多個(gè)類(lèi)文件。當Java程序需要使用某個(gè)類(lèi)時(shí),JVM會(huì )確保這個(gè)類(lèi)已經(jīng)被加載、連接(驗證、準備和解析)和初始化。類(lèi)的加載是指把類(lèi)的.class文件中的數據讀入到內存中,通常是創(chuàng )建一個(gè)字節數組讀入.class文件,然后產(chǎn)生與所加載類(lèi)對應的Class對象。加載完成后,Class對象還不完整,所以此時(shí)的類(lèi)還不可用。當類(lèi)被加載后就進(jìn)入連接階段,這一階段包括驗證、準備(為靜態(tài)變量分配內存并設置默認的初始值)和解析(將符號引用替換為直接引用)三個(gè)步驟。最后JVM對類(lèi)進(jìn)行初始化,包括:1如果類(lèi)存在直接的父類(lèi)并且這個(gè)類(lèi)還沒(méi)有被初始化,那么就先初始化父類(lèi);2如果類(lèi)中存在初始化語(yǔ)句,就依次執行這些初始化語(yǔ)句。
2.類(lèi)的加載是由類(lèi)加載器完成的,類(lèi)加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶(hù)自定義類(lèi)加載器(java.lang.ClassLoader的子類(lèi))。從JDK 1.2開(kāi)始,類(lèi)加載過(guò)程采取了父親委托機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個(gè)父類(lèi)加載器。類(lèi)的加載首先請求父類(lèi)加載器加載,父類(lèi)加載器無(wú)能為力時(shí)才由其子類(lèi)加載器自行加載。JVM不會(huì )向Java程序提供對Bootstrap的引用。下面是關(guān)于幾個(gè)類(lèi)加載器的說(shuō)明:
a)Bootstrap:一般用本地代碼實(shí)現,負責加載JVM基礎核心類(lèi)庫(rt.jar);
b)Extension:從java.ext.dirs系統屬性所指定的目錄中加載類(lèi)庫,它的父加載器是Bootstrap;
c)System:又叫應用類(lèi)加載器,其父類(lèi)是Extension。它是應用最廣泛的類(lèi)加載器。它從環(huán)境變量classpath或者系統屬性java.class.path所指定的目錄中記載類(lèi),是用戶(hù)自定義加載器的默認父加載器。
6、char 型變量中能不能存貯一個(gè)中文漢字?為什么?
答:char類(lèi)型可以存儲一個(gè)中文漢字,因為Java中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號,這是統一的唯一方法),一個(gè)char類(lèi)型占2個(gè)字節(16bit),所以放一個(gè)中文是沒(méi)問(wèn)題的。
補充:使用Unicode意味著(zhù)字符在JVM內部和外部有不同的表現形式,在JVM內部都是Unicode,當這個(gè)字符被從JVM內部轉移到外部時(shí)(例如存入文件系統中),需要進(jìn)行編碼轉換。所以Java中有字節流和字符流,以及在字符流和字節流之間進(jìn)行轉換的轉換流,如InputStreamReader和OutputStreamReader,這兩個(gè)類(lèi)是字節流和字符流之間的適配器類(lèi),承擔了編碼轉換的任務(wù);對于C程序員來(lái)說(shuō),要完成這樣的編碼轉換恐怕要依賴(lài)于union(聯(lián)合體/共用體)共享內存的特征來(lái)實(shí)現了。
7、抽象類(lèi)(abstract class)和接口(interface)有什么異同?
答:抽象類(lèi)和接口都不能夠實(shí)例化,但可以定義抽象類(lèi)和接口類(lèi)型的引用。一個(gè)類(lèi)如果繼承了某個(gè)抽象類(lèi)或者實(shí)現了某個(gè)接口都需要對其中的抽象方法全部進(jìn)行實(shí)現,否則該類(lèi)仍然需要被聲明為抽象類(lèi)。接口比抽象類(lèi)更加抽象,因為抽象類(lèi)中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法。抽象類(lèi)中的成員可以是private、默認、protected、public的,而接口中的成員全都是public的。抽象類(lèi)中可以定義成員變量,而接口中定義的成員變量實(shí)際上都是常量。有抽象方法的類(lèi)必須被聲明為抽象類(lèi),而抽象類(lèi)未必要有抽象方法。
8、靜態(tài)嵌套類(lèi)(Static Nested Class)和內部類(lèi)(Inner Class)的不同?
答:Static Nested Class是被聲明為靜態(tài)(static)的內部類(lèi),它可以不依賴(lài)于外部類(lèi)實(shí)例被實(shí)例化。而通常的內部類(lèi)需要在外部類(lèi)實(shí)例化后才能實(shí)例化,其語(yǔ)法看起來(lái)挺詭異的,如下所示。
package com.lovo;
/**
* 撲克類(lèi)(一副撲克)
* @author 駱昊
*
*/
public class Poker {
private static String[] suites = {"黑桃", "紅桃", "草花", "方塊"};
private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
private Card[] cards;
/**
* 構造器
*
*/
public Poker() {
cards = new Card[52];
for(int i = 0; i < suites.length; i++) {
for(int j = 0; j < faces.length; j++) {
cards[i * 13 + j] = new Card(suites[i], faces[j]);
}
}
}
/**
* 洗牌 (隨機亂序)
*
*/
public void shuffle() {
for(int i = 0, len = cards.length; i < len; i++) {
int index = (int) (Math.random() * len);
Card temp = cards[index];
cards[index] = cards[i];
cards[i] = temp;
}
}
/**
* 發(fā)牌
* @param index 發(fā)牌的位置
*
*/
public Card deal(int index) {
return cards[index];
}
/**
* 卡片類(lèi)(一張撲克)
* [內部類(lèi)]
* @author 駱昊
*
*/
public class Card {
private String suite; // 花色
private int face; // 點(diǎn)數
public Card(String suite, int face) {
this.suite = suite;
this.face = face;
}
@Override
public String toString() {
String faceStr = "";
switch(face) {
case 1: faceStr = "A"; break;
case 11: faceStr = "J"; break;
case 12: faceStr = "Q"; break;
case 13: faceStr = "K"; break;
default: faceStr = String.valueOf(face);
}
return suite + faceStr;
}
}
}
測試類(lèi):
package com.lovo;
class PokerTest {
public static void main(String[] args) {
Poker poker = new Poker();
poker.shuffle(); // 洗牌
Poker.Card c1 = poker.deal(0); // 發(fā)第一張牌
// 對于非靜態(tài)內部類(lèi)Card
// 只有通過(guò)其外部類(lèi)Poker對象才能創(chuàng )建Card對象
Poker.Card c2 = poker.new Card("紅心", 1); // 自己創(chuàng )建一張牌
System.out.println(c1); // 洗牌后的第一張
System.out.println(c2); // 打印: 紅心A
}
}
9、Java 中會(huì )存在內存泄漏嗎,請簡(jiǎn)單描述。
答:理論上Java因為有垃圾回收機制(GC)不會(huì )存在內存泄露問(wèn)題(這也是Java被廣泛使用于服務(wù)器端編程的一個(gè)重要原因);然而在實(shí)際開(kāi)發(fā)中,可能會(huì )存在無(wú)用但可達的對象,這些對象不能被GC回收也會(huì )發(fā)生內存泄露。一個(gè)例子就是Hibernate的Session(一級緩存)中的對象屬于持久態(tài),垃圾回收器是不會(huì )回收這些對象的,然而這些對象中可能存在無(wú)用的垃圾對象。下面的例子也展示了Java中發(fā)生內存泄露的情況:
package com.lovo;
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack
private T[] elements;
private int size = 0;
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
}
public void push(T elem) {
ensureCapacity();
elements[size++] = elem;
}
public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
上面的代碼實(shí)現了一個(gè)棧(先進(jìn)后出(FILO))結構,乍看之下似乎沒(méi)有什么明顯的問(wèn)題,它甚至可以通過(guò)你編寫(xiě)的各種單元測試。然而其中的pop方法卻存在內存泄露的問(wèn)題,當我們用pop方法彈出棧中的對象時(shí),該對象不會(huì )被當作垃圾回收,即使使用棧的程序不再引用這些對象,因為棧內部維護著(zhù)對這些對象的過(guò)期引用(obsolete reference)。在支持垃圾回收的語(yǔ)言中,內存泄露是很隱蔽的,這種內存泄露其實(shí)就是無(wú)意識的對象保持。如果一個(gè)對象引用被無(wú)意識的保留起來(lái)了,那么垃圾回收器不會(huì )處理這個(gè)對象,也不會(huì )處理該對象引用的其他對象,即使這樣的對象只有少數幾個(gè),也可能會(huì )導致很多的對象被排除在垃圾回收之外,從而對性能造成重大影響,極端情況下會(huì )引發(fā)Disk Paging(物理內存與硬盤(pán)的虛擬內存交換數據),甚至造成OutOfMemoryError。
10、抽象的(abstract)方法是否可同時(shí)是靜態(tài)的(static),是否可同時(shí)是本地方法(native),是否可同時(shí)被synchronized修飾?
答:都不能。抽象方法需要子類(lèi)重寫(xiě),而靜態(tài)的方法是無(wú)法被重寫(xiě)的,因此二者是矛盾的。本地方法是由本地代碼(如C代碼)實(shí)現的方法,而抽象方法是沒(méi)有實(shí)現的,也是矛盾的。synchronized和方法的實(shí)現細節有關(guān),抽象方法不涉及實(shí)現細節,因此也是相互矛盾的。
11、靜態(tài)變量和實(shí)例變量的區別?
答:靜態(tài)變量是被static修飾符修飾的變量,也稱(chēng)為類(lèi)變量,它屬于類(lèi),不屬于類(lèi)的任何一個(gè)對象,一個(gè)類(lèi)不管創(chuàng )建多少個(gè)對象,靜態(tài)變量在內存中有且僅有一個(gè)拷貝;實(shí)例變量必須依存于某一實(shí)例,需要先創(chuàng )建對象然后通過(guò)對象才能訪(fǎng)問(wèn)到它。靜態(tài)變量可以實(shí)現讓多個(gè)對象共享內存。在Java開(kāi)發(fā)中,上下文類(lèi)和工具類(lèi)中通常會(huì )有大量的靜態(tài)成員。
12、是否可以從一個(gè)靜態(tài)(static)方法內部發(fā)出對非靜態(tài)(non-static)方法的調用?
答:不可以,靜態(tài)方法只能訪(fǎng)問(wèn)靜態(tài)成員,因為非靜態(tài)方法的調用要先創(chuàng )建對象,因此在調用靜態(tài)方法時(shí)可能對象并沒(méi)有被初始化。
13、如何實(shí)現對象克隆?
答:有兩種方式:
1.實(shí)現Cloneable接口并重寫(xiě)Object類(lèi)中的clone()方法;
2.實(shí)現Serializable接口,通過(guò)對象的序列化和反序列化實(shí)現克隆,可以實(shí)現真正的深度克隆,代碼如下。
package com.lovo;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
public static
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 說(shuō)明:調用ByteArrayInputStream或ByteArrayOutputStream對象的close方法沒(méi)有任何意義
// 這兩個(gè)基于內存的流只要垃圾回收器清理對象就能夠釋放資源
}
}
下面是測試代碼:
package com.lovo;
import java.io.Serializable;
/**
* 人類(lèi)
* @author 駱昊
*
*/
class Person implements Serializable {
private static final long serialVersionUID = -9102017020286042305L;
private String name; // 姓名
private int age; // 年齡
private Car car; // 座駕
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
/**
* 小汽車(chē)類(lèi)
* @author 駱昊
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高時(shí)速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
}
}
class CloneTest {
public static void main(String[] args) {
try {
Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
Person p2 = MyUtil.clone(p1); // 深度克隆
p2.getCar().setBrand("BYD");
// 修改克隆的Person對象p2關(guān)聯(lián)的汽車(chē)對象的品牌屬性
// 原來(lái)的Person對象p1關(guān)聯(lián)的汽車(chē)不會(huì )受到任何影響
// 因為在克隆Person對象時(shí)其關(guān)聯(lián)的汽車(chē)對象也被克隆了
System.out.println(p1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:基于序列化和反序列化實(shí)現的克隆不僅僅是深度克隆,更重要的是通過(guò)泛型限定,可以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時(shí)拋出異常,這種是方案明顯優(yōu)于使用Object類(lèi)的clone方法克隆對象。
14、GC 是什么?為什么要有GC?
答:GC是垃圾收集的意思,內存處理是編程人員容易出現問(wèn)題的地方,忘記或者錯誤的內存回收會(huì )導致程序或系統的不穩定甚至崩潰,Java提供的GC功能可以自動(dòng)監測對象是否超過(guò)作用域從而達到自動(dòng)回收內存的目的,Java語(yǔ)言沒(méi)有提供釋放已分配內存的顯示操作方法。Java程序員不用擔心內存管理,因為垃圾收集器會(huì )自動(dòng)進(jìn)行管理。要請求垃圾收集,可以調用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉顯示的垃圾回收調用。
垃圾回收可以有效的防止內存泄露,有效的使用可以使用的內存。垃圾回收器通常是作為一個(gè)單獨的低優(yōu)先級的線(xiàn)程運行,不可預知的情況下對內存堆中已經(jīng)死亡的或者長(cháng)時(shí)間沒(méi)有使用的對象進(jìn)行清除和回收,程序員不能實(shí)時(shí)的調用垃圾回收器對某個(gè)對象或所有對象進(jìn)行垃圾回收。在Java誕生初期,垃圾回收是Java最大的亮點(diǎn)之一,因為服務(wù)器端的編程需要有效的防止內存泄露問(wèn)題,然而時(shí)過(guò)境遷,如今Java的垃圾回收機制已經(jīng)成為被詬病的東西。移動(dòng)智能終端用戶(hù)通常覺(jué)得iOS的系統比Android系統有更好的用戶(hù)體驗,其中一個(gè)深層次的原因就在于A(yíng)ndroid系統中垃圾回收的不可預知性。
補充:垃圾回收機制有很多種,包括:分代復制垃圾回收、標記垃圾回收、增量垃圾回收等方式。標準的Java進(jìn)程既有棧又有堆。棧保存了原始型局部變量,堆保存了要創(chuàng )建的對象。Java平臺對堆內存回收和再利用的基本算法被稱(chēng)為標記和清除,但是Java對其進(jìn)行了改進(jìn),采用“分代式垃圾收集”。這種方法會(huì )跟Java對象的生命周期將堆內存劃分為不同的區域,在垃圾收集過(guò)程中,可能會(huì )將對象移動(dòng)到不同區域:
伊甸園(Eden):這是對象最初誕生的區域,并且對大多數對象來(lái)說(shuō),這里是它們唯一存在過(guò)的區域。
幸存者樂(lè )園(Survivor):從伊甸園幸存下來(lái)的對象會(huì )被挪到這里。
終身頤養園(Tenured):這是足夠老的幸存對象的歸宿。年輕代收集(Minor-GC)過(guò)程是不會(huì )觸及這個(gè)地方的。當年輕代收集不能把對象放進(jìn)終身頤養園時(shí),就會(huì )觸發(fā)一次完全收集(Major-GC),這里可能還會(huì )牽扯到壓縮,以便為大對象騰出足夠的空間。
與垃圾回收相關(guān)的JVM參數:
-Xms / -Xmx --- 堆的初始大小 / 堆的最大大小
-Xmn --- 堆中年輕代的大小
-XX:-DisableExplicitGC --- 讓System.gc()不產(chǎn)生任何作用
-XX:+PrintGCDetail --- 打印GC的細節
-XX:+PrintGCDateStamps --- 打印GC操作的時(shí)間戳
【java程序員考試試題】相關(guān)文章:
java程序員面試考試題及答案10-30
java程序員面試題10-05
java程序員面試試題11-30
Java考試格林模擬試題10-22
java考試試題及答案10-25
華為JAVA考試試題11-01
初級java程序員面試試題06-11
java認證考試試題及答案07-21
2017年java考試模擬試題05-31