2019年Java面試題基礎系列228道(2),查漏補缺!

2019年Java面試題基礎系列228道java

上一篇更新1~20題的答案解析程序員

2019年Java面試題基礎系列228道(1),快看看哪些你還不會?
面試


v2-51deefa1c229044d15241a72995f96ff_hd.png


本次更新Java 面試題(一)的21~50題答案算法

2一、描述一下 JVM 加載 class 文件的原理機制?編程

JVM 中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java 中的類加載器是一個重要的 Java 運行時系統組件,它負責在運行時查找和裝入類文件中的類。數組

因爲 Java 的跨平臺性,通過編譯的 Java 源程序並非一個可執行程序,而是一個或多個類文件。當 Java 程序須要使用某個類時,JVM 會確保這個類已經被加載、鏈接(驗證、準備和解析)和初始化。類的加載是指把類的.class 文件中的數據讀入到內存中,一般是建立一個字節數組讀入.class 文件,而後產生與所加載類對應的 Class 對象。加載完成後,Class 對象還不完整,因此此時的類還不可用。當類被加載後就進入鏈接階段,這一階段包括驗證、準備(爲靜態變量分配內存並設置默認的初始值)和解析(將符號引用替換爲直接引用)三個步驟。最後 JVM 對類進行初始化,包括:1)若是類存在直接的父類而且這個類尚未被初始化,那麼就先初始化父類;2)若是類中存在初始化語句,就依次執行這些初始化語句。瀏覽器

類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader 的子類)。從 Java 2(JDK 1.2)開始,從 Java 2(JDK 1.2)開始,類加載過程採起了父親委託機制(PDM)。PDM 更好的保證了 Java 平臺的安全性,在該機制中,JVM 自帶的 Bootstrap 是根加載器,其餘的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能爲力時才由其子類加載器自行加載。JVM 不會向 Java 程序提供對 Bootstrap 的引用。下面是關於幾個類加載器的說明:緩存

(1) Bootstrap:通常用本地代碼實現,負責加載 JVM 基礎核心類庫(rt.jar);安全

(2) Extension:從 java.ext.dirs 系統屬性所指定的目錄中加載類庫,它的父加載器是 Bootstrap;服務器

(3) System:又叫應用類加載器,其父類是 Extension。它是應用最普遍的類加載器。它從環境變量 classpath 或者系統屬性 java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認父加載器。

2二、char 型變量中能不能存貯一箇中文漢字,爲何?

char 類型能夠存儲一箇中文漢字,由於 Java 中使用的編碼是 Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號,這是統一的惟一方法),一個 char 類型佔 2 個字節(16 比特),因此放一箇中文是沒問題的。

補充:使用 Unicode 意味着字符在 JVM 內部和外部有不一樣的表現形式,在 JVM內部都是 Unicode,當這個字符被從 JVM 內部轉移到外部時(例如存入文件系統中),須要進行編碼轉換。因此 Java 中有字節流和字符流,以及在字符流和字節流之間進行轉換的轉換流,如 InputStreamReader 和 OutputStreamReader,這兩個類是字節流和字符流之間的適配器類,承擔了編碼轉換的任務;對於 C 程序員來講,要完成這樣的編碼轉換恐怕要依賴於 union(聯合體/共用體)共享內存的特徵來實現了。

2三、抽象類(abstract class)和接口(interface)有什麼異同?

抽象類和接口都不可以實例化,但能夠定義抽象類和接口類型的引用。一個類若是繼承了某個抽象類或者實現了某個接口都須要對其中的抽象方法所有進行實現,不然該類仍然須要被聲明爲抽象類。接口比抽象類更加抽象,由於抽象類中能夠定義構造器,能夠有抽象方法和具體方法,而接口中不能定義構造器並且其中的方法所有都是抽象方法。抽象類中的成員能夠是 private、默認、protected、public 的,而接口中的成員全都是 public 的。抽象類中能夠定義成員變量,而接口中定義的成員變量實際上都是常量。有抽象方法的類必須被聲明爲抽象類,而抽象類未必要有抽象方法。

2四、靜態嵌套類(Static Nested Class)和內部類(Inner Class)的不一樣?

Static Nested Class 是被聲明爲靜態(static)的內部類,它能夠不依賴於外部類實例被實例化。而一般的內部類須要在外部類實例化後才能實例化,其語法看起來挺詭異的,以下所示。

/**
* 撲克類(一副撲克)
* @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;
		}
	}
	/**
* 發牌
* @param index 發牌的位置
*
*/
	public Card deal(int index) {
		return cards[index];
	}
	/**
* 卡片類(一張撲克)
* [內部類]
* @author 駱昊
*
*/
	public class Card {
		private String suite;
		// 花色
		private int face;
		// 點數
		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;
		}
	}
}
測試代碼:
class PokerTest {
	public static void main(String[] args) {
		Poker poker = new Poker();
		poker.shuffle();
		// 洗牌
		Poker.Card c1 = poker.deal(0);
		// 發第一張牌
		// 對於非靜態內部類 Card
		// 只有經過其外部類 Poker 對象才能建立 Card 對象
		Poker.Card c2 = poker.new Card("紅心", 1);
		// 本身建立一張牌
		System.out.println(c1);
		// 洗牌後的第一張
		System.out.println(c2);
		// 打印: 紅心 A
	}
}

面試題 - 下面的代碼哪些地方會產生編譯錯誤?

class Outer {
	class Inner {
	}
	public static void foo() {
		new Inner();
	}
	public void bar() {
		new Inner();
	}
	public static void main(String[] args) {
		new Inner();
	}
}

注意:Java 中非靜態內部類對象的建立要依賴其外部類對象,上面的面試題中 foo和 main 方法都是靜態方法,靜態方法中沒有 this,也就是說沒有所謂的外部類對象,所以沒法建立內部類對象,若是要在靜態方法中建立內部類對象,能夠這樣作:

new Outer().new Inner();

2五、Java 中會存在內存泄漏嗎,請簡單描述。

理論上 Java 由於有垃圾回收機制(GC)不會存在內存泄露問題(這也是 Java 被普遍使用於服務器端編程的一個重要緣由);然而在實際開發中,可能會存在無用但可達的對象,這些對象不能被 GC 回收,所以也會致使內存泄露的發生。例如Hibernate 的 Session(一級緩存)中的對象屬於持久態,垃圾回收器是不會回收這些對象的,然而這些對象中可能存在無用的垃圾對象,若是不及時關閉(close)或清空(flush)一級緩存就可能致使內存泄露。下面例子中的代碼也會致使內存泄露。

import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
	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);
		}
	}
}

上面的代碼實現了一個棧(先進後出(FILO))結構,乍看之下彷佛沒有什麼明顯的問題,它甚至能夠經過你編寫的各類單元測試。然而其中的 pop 方法卻存在內存泄露的問題,當咱們用 pop 方法彈出棧中的對象時,該對象不會被看成垃圾回收,即便使用棧的程序再也不引用這些對象,由於棧內部維護着對這些對象的過時引 用(obsolete reference)。在支持垃圾回收的語言中,內存泄露是很隱蔽的,這種內存泄露其實就是無心識的對象保持。若是一個對象引用被無心識的保留起來了,那麼垃圾回收器不會處理這個對象,也不會處理該對象引用的其餘對象,即便這樣的對象只有少數幾個,也可能會致使不少的對象被排除在垃圾回收以外,從而對性能形成重大影響,極端狀況下會引起 Disk Paging(物理內存與硬盤的虛擬內存交換數據),甚至形成 OutOfMemoryError。

v2-4e34799896cba40dead4dfb71fd1cbad_hd.png

2六、抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被 synchronized修飾?

都不能。抽象方法須要子類重寫,而靜態的方法是沒法被重寫的,所以兩者是矛盾的。本地方法是由本地代碼(如 C 代碼)實現的方法,而抽象方法是沒有實現的,也是矛盾的。synchronized 和方法的實現細節有關,抽象方法不涉及實現細節,所以也是相互矛盾的。

2七、闡述靜態變量和實例變量的區別。

靜態變量是被 static 修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類無論建立多少個對象,靜態變量在內存中有且僅有一個拷貝;實例變量必須依存於某一實例,須要先建立對象而後經過對象才能訪問到它。靜態變量能夠實現讓多個對象共享內存。

補充:在 Java 開發中,上下文類和工具類中一般會有大量的靜態成員。

2八、是否能夠從一個靜態(static)方法內部發出對非靜態(non-static)方法的調用?

不能夠,靜態方法只能訪問靜態成員,由於非靜態方法的調用要先建立對象,在調用靜態方法時可能對象並無被初始化。

2九、如何實現對象克隆?

有兩種方式:

1). 實現 Cloneable 接口並重寫 Object 類中的 clone()方法;

2). 實現 Serializable 接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆,代碼以下。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
	private MyUtil() {
		throw new AssertionError();
	}
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> T clone(T obj) throws
	Exception {
		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();
		// 說明:調用 ByteArrayInputStream 或 ByteArrayOutputStream
		對象的 close 方法沒有任何意義
		// 這兩個基於內存的流只要垃圾回收器清理對象就可以釋放資源,這
		一點不一樣於對外部資源(如文件流)的釋放
	}
}

下面是測試代碼:

import java.io.Serializable;
/**
* 人類
* @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 + "]";
	}
}
/**
* 小汽車類
* @author 駱昊
*
*/
class Car implements Serializable {
	private static final long serialVersionUID = -5713945027627603702L;
	private String brand;
	// 品牌
	private int maxSpeed;
	// 最高時速
	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 關聯的汽車對象的品牌屬性
			// 原來的 Person 對象 p1 關聯的汽車不會受到任何影響
			// 由於在克隆 Person 對象時其關聯的汽車對象也被克隆了
			System.out.println(p1);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
	}
}

注意:基於序列化和反序列化實現的克隆不只僅是深度克隆,更重要的是經過泛型限定,能夠檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用 Object 類的 clone 方法克隆對象。讓問題在編譯的時候暴露出來老是好過把問題留到運行時。

30、GC 是什麼?爲何要有 GC?

GC 是垃圾收集的意思,內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會致使程序或系統的不穩定甚至崩潰,Java 提供的 GC 功能能夠自動監測對象是否超過做用域從而達到自動回收內存的目的,Java 語言沒有提供釋放已分配內存的顯示操做方法。Java 程序員不用擔憂內存管理,由於垃圾收集器會自動進行管理。要請求垃圾收集,能夠調用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但 JVM 能夠屏蔽掉顯示的垃圾回收調用。垃圾回收能夠有效的防止內存泄露,有效的使用可使用的內存。垃圾回收器一般是做爲一個單獨的低優先級的線程運行,不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。在 Java 誕生初期,垃圾回收是 Java最大的亮點之一,由於服務器端的編程須要有效的防止內存泄露問題,然而時過境遷,現在 Java 的垃圾回收機制已經成爲被詬病的東西。移動智能終端用戶一般以爲 iOS 的系統比 Android 系統有更好的用戶體驗,其中一個深層次的緣由就在於 Android 系統中垃圾回收的不可預知性。

補充:垃圾回收機制有不少種,包括:分代複製垃圾回收、標記垃圾回收、增量垃圾回收等方式。標準的 Java 進程既有棧又有堆。棧保存了原始型局部變量,堆保存了要建立的對象。Java 平臺對堆內存回收和再利用的基本算法被稱爲標記和清除,可是 Java 對其進行了改進,採用「分代式垃圾收集」。這種方法會跟 Java對象的生命週期將堆內存劃分爲不一樣的區域,在垃圾收集過程當中,可能會將對象移動到不一樣區域:

(1)伊甸園(Eden):這是對象最初誕生的區域,而且對大多數對象來講,這裏是它們惟一存在過的區域。

(2)倖存者樂園(Survivor):從伊甸園倖存下來的對象會被挪到這裏。

(3)終身頤養園(Tenured):這是足夠老的倖存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次徹底收集(Major-GC),這裏可能還會牽扯到壓縮,以便爲大對象騰出足夠的空間。

與垃圾回收相關的 JVM 參數:

-Xms / -Xmx — 堆的初始大小 / 堆的最大大小
 -Xmn — 堆中年輕代的大小
 -XX:-DisableExplicitGC — 讓 System.gc()不產生任何做用
 -XX:+PrintGCDetails — 打印 GC 的細節
 -XX:+PrintGCDateStamps — 打印 GC 操做的時間戳
 -XX:NewSize / XX:MaxNewSize — 設置新生代大小/新生代最大大小
 -XX:NewRatio — 能夠設置老生代和新生代的比例
 -XX:PrintTenuringDistribution — 設置每次新生代 GC 後輸出倖存者
樂園中對象年齡的分佈
 -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:設置老
年代閥值的初始值和最大值
 -XX:TargetSurvivorRatio:設置倖存區的目標使用率

3一、String s = new String(「xyz」);建立了幾個字符串對象?

兩個對象,一個是靜態區的」xyz」,一個是用 new 建立在堆上的對象。

3二、接口是否可繼承(extends)接口?抽象類是否可實現(implements)接口?抽象類是否可繼承具體類(concreteclass)?

接口能夠繼承接口,並且支持多重繼承。抽象類能夠實現(implements)接口,抽象類可繼承具體類也能夠繼承抽象類。

3三、一個」.java」源文件中是否能夠包含多個類(不是內部類)?有什麼限制?

能夠,但一個源文件中最多隻能有一個公開類(public class)並且文件名必須和公開類的類名徹底保持一致。

3四、Anonymous Inner Class(匿名內部類)是否能夠繼承其它類?是否能夠實現接口?

能夠繼承其餘類或實現其餘接口,在 Swing 編程和 Android 開發中經常使用此方式來實現事件監聽和回調。

3五、內部類能夠引用它的包含類(外部類)的成員嗎?有沒有什麼限制?

一個內部類對象能夠訪問建立它的外部類對象的成員,包括私有成員。

3六、Java 中的 final 關鍵字有哪些用法?

(1)修飾類:表示該類不能被繼承; (2)修飾方法:表示方法不能被重寫; (3)修飾變量:表示變量只能一次賦值之後值不能被修改(常量)。

3七、指出下面程序的運行結果

class A {
	static {
		System.out.print("1");
	}
	public A() {
		System.out.print("2");
	}
}
class B extends A{
	static {
		System.out.print("a");
	}
	public B() {
		System.out.print("b");
	}
}
public class Hello {
	public static void main(String[] args) {
		A ab = new B();
		ab = new B();
	}
}

執行結果:1a2b2b。建立對象時構造器的調用順序是:先初始化靜態成員,而後調用父類構造器,再初始化非靜態成員,最後調用自身構造器。

提示:若是不能給出此題的正確答案,說明以前第 21 題 Java 類加載機制尚未徹底理解,趕忙再看看吧。

3八、數據類型之間的轉換:

(1) 如何將字符串轉換爲基本數據類型?

(2) 如何將基本數據類型轉換爲字符串?

答:

(1)調用基本數據類型對應的包裝類中的方法 parseXXX(String)或valueOf(String)便可返回相應基本類型;

(2)一種方法是將基本數據類型與空字符串(」「)鏈接(+)便可得到其所對應的字符串;另外一種方法是調用 String 類中的 valueOf()方法返回相應字符串

3九、如何實現字符串的反轉及替換?

方法不少,能夠本身寫實現也可使用 String 或 StringBuffer/StringBuilder 中的方法。有一道很常見的面試題是用遞歸實現字符串反轉,代碼以下所示:

public static String reverse(String originStr) {
	if(originStr == null || originStr.length() <= 1)
	return originStr;
	return reverse(originStr.substring(1)) + originStr.charAt(0);
}

40、怎樣將 GB2312 編碼的字符串轉換爲 ISO-8859-1 編碼的字符串?

代碼以下所示:

String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

4一、日期和時間:

(1)如何取得年月日、小時分鐘秒?

(2) 如何取得從 1970 年 1 月 1 日 0 時 0 分 0 秒到如今的毫秒數?

(3) 如何取得某月的最後一天?

(4)如何格式化日期?

答:

問題 1:建立 java.util.Calendar 實例,調用其 get()方法傳入不一樣的參數便可得到參數所對應的值。Java 8 中可使用 java.time.LocalDateTimel 來獲取,代碼以下所示。

public class DateTimeTest {
	public static void main(String[] args) {
		Calendar cal = Calendar.getInstance();
		System.out.println(cal.get(Calendar.YEAR));
		System.out.println(cal.get(Calendar.MONTH));
		// 0 - 11
		System.out.println(cal.get(Calendar.DATE));
		System.out.println(cal.get(Calendar.HOUR_OF_DAY));
		System.out.println(cal.get(Calendar.MINUTE));
		System.out.println(cal.get(Calendar.SECOND));
		// Java 8
		LocalDateTime dt = LocalDateTime.now();
		System.out.println(dt.getYear());
		System.out.println(dt.getMonthValue());
		// 1 - 12
		System.out.println(dt.getDayOfMonth());
		System.out.println(dt.getHour());
		System.out.println(dt.getMinute());
		System.out.println(dt.getSecond());
	}
}

問題 2:如下方法都可得到該毫秒數。

Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis();
// Java 8

問題 3:代碼以下所示。

Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH

問題 4:利用 java.text.DataFormat 的子類(如 SimpleDateFormat 類)中的format(Date)方法可將日期格式化。Java 8 中能夠用java.time.format.DateTimeFormatter 來格式化時間日期,代碼以下所示。

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;
class DateFormatTest {
	public static void main(String[] args) {
		SimpleDateFormat oldFormatter = new
		SimpleDateFormat("yyyy/MM/dd");
		Date date1 = new Date();
		System.out.println(oldFormatter.format(date1));
		// Java 8
		DateTimeFormatter newFormatter =
		DateTimeFormatter.ofPattern("yyyy/MM/dd");
		LocalDate date2 = LocalDate.now();
		System.out.println(date2.format(newFormatter));
	}
}

補充:Java 的時間日期 API 一直以來都是被詬病的東西,爲了解決這一問題,Java8 中引入了新的時間日期 API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等類,這些的類的設計都使用了不變模式,所以是線程安全的設計。

4二、打印昨天的當前時刻。

import java.util.Calendar;
class YesterdayCurrent {
	public static void main(String[] args){
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.DATE, -1);
		System.out.println(cal.getTime());
	}
}

在 Java 8 中,能夠用下面的代碼實現相同的功能。

import java.time.LocalDateTime;
class YesterdayCurrent {
	public static void main(String[] args) {
		LocalDateTime today = LocalDateTime.now();
		LocalDateTime yesterday = today.minusDays(1);
		System.out.println(yesterday);
	}
}

4三、比較一下 Java 和 JavaSciprt。

JavaScript 與 Java 是兩個公司開發的不一樣的兩個產品。Java 是原 SunMicrosystems 公司推出的面向對象的程序設計語言,特別適合於互聯網應用程序開發;而 JavaScript 是 Netscape 公司的產品,爲了擴展 Netscape 瀏覽器的功能而開發的一種能夠嵌入 Web 頁面中運行的基於對象和事件驅動的解釋性語言。JavaScript 的前身是 LiveScript;而 Java 的前身是 Oak 語言。

下面對兩種語言間的異同做以下比較:

(1)基於對象和麪向對象:Java 是一種真正的面向對象的語言,即便是開發簡單的程序,必須設計對象;JavaScript 是種腳本語言,它能夠用來製做與網絡無關的,與用戶交互做用的複雜軟件。它是一種基於對象(Object-Based)和事件驅動(Event-Driven)的編程語言,於是它自己提供了很是豐富的內部對象供設計人員使用。

(2)解釋和編譯:Java 的源代碼在執行以前,必須通過編譯。JavaScript 是一種解釋性編程語言,其源代碼不需通過編譯,由瀏覽器解釋執行。(目前的瀏覽器幾乎都使用了 JIT(即時編譯)技術來提高 JavaScript 的運行效率)

(3)強類型變量和類型弱變量:Java 採用強類型變量檢查,即全部變量在編譯以前必須做聲明;JavaScript 中變量是弱類型的,甚至在使用變量前能夠不做聲明,JavaScript 的解釋器在運行時檢查推斷其數據類型。

(4)代碼格式不同。

補充:上面列出的四點是網上流傳的所謂的標準答案。其實 Java 和 JavaScript最重要的區別是一個是靜態語言,一個是動態語言。目前的編程語言的發展趨勢是函數式語言和動態語言。在 Java 中類(class)是一等公民,而 JavaScript 中函數(function)是一等公民,所以 JavaScript 支持函數式編程,可使用 Lambda函數和閉包(closure),固然 Java 8 也開始支持函數式編程,提供了對 Lambda表達式以及函數式接口的支持。對於這類問題,在面試的時候最好仍是用本身的語言回答會更加靠譜,不要背網上所謂的標準答案。

4四、何時用斷言(assert)?

斷言在軟件開發中是一種經常使用的調試方式,不少開發語言中都支持這種機制。通常來講,斷言用於保證程序最基本、關鍵的正確性。斷言檢查一般在開發和測試時開啓。爲了保證程序的執行效率,在軟件發佈後斷言檢查一般是關閉的。斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲 true;若是表達式的值爲 false,那麼系統會報告一個 AssertionError。斷言的使用以下面的代碼所示:

assert(a > 0); // throws an AssertionError if a <= 0

斷言能夠有兩種形式:

assert Expression1;

assert Expression1 : Expression2 ;

Expression1 應該老是產生一個布爾值。

Expression2 能夠是得出一個值的任意表達式;這個值用於生成顯示更多調試信息的字符串消息。

要在運行時啓用斷言,能夠在啓動 JVM 時使用-enableassertions 或者-ea 標記。要在運行時選擇禁用斷言,能夠在啓動 JVM 時使用-da 或者-disableassertions標記。要在系統類中啓用或禁用斷言,可以使用-esa 或-dsa 標記。還能夠在包的基礎上啓用或者禁用斷言。

注意:斷言不該該以任何方式改變程序的狀態。簡單的說,若是但願在不知足某些條件時阻止代碼的執行,就能夠考慮用斷言來阻止它。歡迎你們關注個人公種浩【程序員追風】,2019年多家公司java面試題整理了1000多道400多頁pdf文檔,文章都會在裏面更新,整理的資料也會放在裏面。

v2-c721b18b5db88d560222f8e41110884f_hd.png

4五、Error 和 Exception 有什麼區別?

Error 表示系統級的錯誤和程序沒必要處理的異常,是恢復不是不可能但很困難的狀況下的一種嚴重問題;好比內存溢出,不可能期望程序能處理這樣的狀況;

Exception 表示須要捕捉或者須要程序進行處理的異常,是一種設計或實現問題;也就是說,它表示若是程序運行正常,從不會發生的狀況。

4六、try{}裏有一個 return 語句,那麼緊跟在這個 try 後的finally{}裏的代碼會不會被執行,何時被執行,在 return前仍是後?

會執行,在方法返回調用者前執行。

注意:在 finally 中改變返回值的作法是很差的,由於若是存在 finally 代碼塊,try中的 return 語句不會立馬返回調用者,而是記錄下返回值待 finally 代碼塊執行完畢以後再向調用者返回其值,而後若是在 finally 中修改了返回值,就會返回修改後的值。顯然,在 finally 中返回或者修改返回值會對程序形成很大的困擾,C#中直接用編譯錯誤的方式來阻止程序員幹這種齷齪的事情,Java 中也能夠經過提高編譯器的語法檢查級別來產生警告或錯誤,Eclipse 中能夠在如圖所示的地方進行設置,強烈建議將此項設置爲編譯錯誤。

4七、Java 語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally 分別如何使用?

Java 經過面向對象的方法進行異常處理,把各類不一樣的異常進行分類,並提供了良好的接口。在 Java 中,每一個異常都是一個對象,它是 Throwable 類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法能夠捕獲到這個異常並能夠對其進行處理。Java 的異常處理是經過 5 個關鍵詞來實現的:try、catch、throw、throws 和 finally。通常狀況下是用 try 來執行一段程序,若是系統會拋出(throw)一個異常對象,能夠經過它的類型來捕獲(catch)它,或經過老是執行代碼塊(finally)來處理;try 用來指定一塊預防全部異常的程序;catch 子句緊跟在 try 塊後面,用來指定你想要捕獲的異常的類型;throw 語句用來明確地拋出一個異常;throws 用來聲明一個方法可能拋出的各類異常;finally 爲確保一段代碼無論發生什麼異常情況都要被執行;try 語句能夠嵌套,每當遇到一個 try 語句,異常的結構就會被放入異常棧中,直到全部的 try 語句都完成。若是下一級的try 語句沒有對某種異常進行處理,異常棧就會執行出棧操做,直到遇到有處理這種異常的 try 語句或者最終將異常拋給 JVM。

4八、運行時異常與受檢異常有何異同?

異常表示程序運行過程當中可能出現的非正常狀態,運行時異常表示虛擬機的一般操做中可能遇到的異常,是一種常見運行錯誤,只要程序設計得沒有問題一般就不會發生。受檢異常跟程序運行的上下文環境有關,即便程序設計無誤,仍然可能因使用的問題而引起。Java 編譯器要求方法必須聲明拋出可能發生的受檢異常,可是並不要求必須聲明拋出未被捕獲的運行時異常。異常和繼承同樣,是面向對象程序設計中常常被濫用的東西,在 Effective Java 中對異常的使用給出瞭如下指導原則:

(1)不要將異常處理用於正常的控制流(設計良好的 API 不該該強迫它的調用者爲了正常的控制流而使用異常)

(2)對能夠恢復的狀況使用受檢異常,對編程錯誤使用運行時異常

(3)避免沒必要要的使用受檢異常(能夠經過一些狀態檢測手段來避免異常的發生)

(4)優先使用標準的異常

(5)每一個方法拋出的異常都要有文檔

(6)保持異常的原子性

(7)不要在 catch 中忽略掉捕獲到的異常

4九、列出一些你常見的運行時異常?

(1)ArithmeticException(算術異常)

(2) ClassCastException (類轉換異常)

(3) IllegalArgumentException (非法參數異常)

(4) IndexOutOfBoundsException (下標越界異常)

(5) NullPointerException (空指針異常)

(6) SecurityException (安全異常)

50、闡述 final、finally、finalize 的區別。

(1) final:修飾符(關鍵字)有三種用法:若是一個類被聲明爲 final,意味着它不能再派生出新的子類,即不能被繼承,所以它和 abstract 是反義詞。將變量聲明爲 final,能夠保證它們在使用中不被改變,被聲明爲 final 的變量必須在聲明時給定初值,而在之後的引用中只能讀取不可修改。被聲明爲 final 的方法也一樣只能使用,不能在子類中被重寫。

(2)finally:一般放在 try…catch…的後面構造老是執行代碼塊,這就意味着程序不管正常執行仍是發生異常,這裏的代碼只要 JVM 不關閉都能執行,能夠將釋放外部資源的代碼寫在 finally 塊中.

(3)finalize:Object 類中定義的方法,Java 中容許使用 finalize()方法在垃圾收集器將對象從內存中清除出去以前作必要的清理工做。這個方法是由垃圾收集器在銷燬對象時調用的,經過重寫 finalize()方法能夠整理系統資源或者執行其餘清理工做。


最後

歡迎你們一塊兒交流,喜歡文章記得點個贊喲,感謝支持!

相關文章
相關標籤/搜索