首頁  >  文章  >  Java  >  Java初學者常問的問題總結(收藏)

Java初學者常問的問題總結(收藏)

黄舟
黄舟原創
2017-03-29 10:13:091276瀏覽

本文介紹一些Java初學者常問的問題,很多朋友會對可以用%除以一個小數嗎? a += b 和 a = a + b 的效果有差嗎? 宣告一個陣列為什麼需要花費大量時間? 為什麼Java庫不用隨機pivot方式的快速排序?等等一連串問題有疑惑,以下就透過本文給大家詳細介紹下

本文介紹一些Java初學者常問的問題,可以用%除以一個小數嗎? a += b 和 a = a + b 的效果有差嗎? 聲明一個數組為什麼需要花費大量時間? 為什麼Java庫不用隨機pivot方式的快速排序?

基本資料型別

Q. 為什麼-0/3 結果是0,而-0.0/3.0 結果是-0.0? (注意後邊的結果0帶負號)

A. 在Java裡,整數是用補碼表示的。在補碼中0只有一種表示方法。另一方面,浮點數則是用 IEEE 標準表示的, 對於0有兩種表示方法, 0 和 -0。

Q. 我可以用 % 除以一個小數嗎?

A. 當然可以。例如,如果 angle 是一個非負數,那麼 angle % (2 * Math.PI) 就會把 angle 轉換到 0 到 2 π 之間。

Q. 當 a b 都是基本型別變數時,a += b 和 a = a + b 的效果有差別嗎?

A. 當 a 和 b 的型別不同時,那兩個語句的效果就可能有差別。 a += b 等同於 a = (int) (a + b),這種情況下可以是 a是int型,b是float型。但同等情況下 a = a + b 就會編譯報錯。

條件語句和循環語句

#Q. 為什麼判斷字串相等不能使用== ?

A. 這反映了基礎類型(int, double, boolean)和引用類型(String)的差異。

Q. 有沒有在什麼情況下,一條語句區塊的花括號不能省略的?

A. 在下面的範例中,第一段程式碼是合法的,第二段程式碼會引發編譯錯誤。從技術角度說,那一句語句是變數聲明,而不是語句,所以會報錯。

// legal 
for (int i = 0; i <= N; i++) { 
 int x = 5; 
} 
// illegal 
for (int i = 0; i <= N; i++) 
 int x = 5;

Q. 在下面的兩段程式碼裡,有沒有情況,它們的效果不一樣?

for ( ; ) { 
  
} 
; 
while () { 
  
  
}

A. 有的。如果在迴圈區塊裡使用 continue 語句。在for的程式碼裡,計數器會加一;而在while的程式碼裡,因為被continue略過了,計數器不加一。

 數組

#Q. 某些Java開發人員使用int a[] 而不是int[] a 去宣告一個陣列。這兩者有什麼差別?

A. 在Java中這兩種用法都是合法的,他們的作用都是一樣的。前者是在C中的定義數組的方法。後者是JAVA推薦的方法,因為它的寫法 int[] 更能顯示這是一個 int 的陣列。

Q. 為什麼陣列下標從0 開始 而不是從 1 開始?

A. 這種傳統起源於機器語言的程式設計方法。在機器語言中,陣列下標被用來計算元素位置與第一個元素之間的偏移量。如果從1開始的話,計算偏移時還需要做一次減法運算,那是種浪費。

Q. 如果我用 負數 作為陣列下標會發生什麼事?

A. 下標小於0 或 大於等於陣列長度,JAVA執行時會拋出 ArrayIndexOutOfBoundsException 例外,並且中止程式運作。

Q. 使用陣列時還有其他需要注意的陷阱嗎?

A. 需要記住,JAVA在你建立一個陣列時會去初始化它,所以宣告一個陣列需要 O(N)的時間。

Q. 既然a[] 是數組,為什麼System.out.println(a) 會印出一個16進位的數,就像@f62373 這樣,而不是印出數組的元素?

A. 好問題。這句語句印出的是 數組在記憶體中的位址,系統會自動呼叫數組的toString()方法,這個問題你可以看下toString()方法的來源碼。

函數呼叫

#Q. 當把陣列當作函數呼叫時的參數時,我常常感到疑惑?

A. 是的。你需要牢記傳值參數(參數是基本變數類型)和傳引用參數(例如陣列)之間的差異。

Q. 那为什么不把所有的参数都使用传值的方式,包括对待数组?

A. 但数组很大时,复制数组需要大量的性能开销。因为这个原因,绝大多数变成语言支持把数组传入函数但不复制一个副本——MATLAB语言除外。

递归调用

Q. 有没有只能用循环而不能用递归的情况?

A. 不可能,所有的循环都可以用递归替代,虽然大多数情况下,递归需要额外的内存。

Q. 有没有只能用递归而不能用循环的情况?

A. 不可能,所有的递归调用都可以用循环来表示。比如你可以用while的方式来实现栈。

Q. 那我应该选择哪个,递归的方式 还是 循环的方式?

A. 根据代码的可读性和效率性之间做权衡。

Q. 我担心使用递归代码时的空间开销和重复计算(例如用递归解Fibonacci)的问题。有没有其他需要担心的?
A. 在递归代码中创建大数据类型(比如数组)时需要额外注意,随着递归的推进,内存使用将会迅速增加,由于内存使用增加,操作系统管理内存的时间开销也会增加。

排序与查找

Q. 为什么我们要花大篇幅来证明一个程序是正确的?

A. 为了防止错误的结果。二分查找就是一个例子。现在,你懂得了二分查找的原理,你就能把递归形式的二分查找改写成循环形式的二分查找。Knuth 教授在 1946年就发表了二分查找的论文,但是第一个正确的二分查找的程序在 1962年在出现。

Q. 在JAVA内建库中有没有排序和查找的函数?

A. 有的。在 java.util.Arrays 中包含了 Arrays.sort() 和 Arrays.binarySearch() 方法。对于Comparable 类型它使用了 归并排序,对于基本数据类型,它使用了快速排序。因为基本类型是值传递,快速排序比归并排序更快而且不需要额外的空间。

Q. 为什么JAVA库不用 随机pivot方式的快速排序?

A. 好问题。 因为某些程序员在调试代码时,可能需要确定性的代码实现。使用随机pivot违背了这个原则。

栈和队列

Q. 在Java库中有对stacks 和 queues 的实现吗?

A. Java库中内建 java.util.Stack,但是你应该避免使用它如果你需要一个真正的栈的话。因为它是实现了额外的功能,比如访问第N个元素。另外,它也支持从栈底部插入元素,所以它看上去更像是一个队列。尽管实现了这些额外的功能对编程人员是一个加分,可是我们使用数据结构并不只是想使用所有功能,而是需要我们正好需要的那种结构。JAVA对于栈的实现就是一个典型的宽接口的例子。

Q. 我想使用数组来表示一个包含泛型的栈,但是以下代码编译报错。为什么?

private Item[] a = new Item[max]; 
oldfirst = first;

A. 不错的尝试。不幸的是,创建一个泛型数组在 Java 1.5里不支持。你可以使用cast,比如下面的写法:

private Item[] a = (Item[]) new Object[max]; 
oldfirst = first;

根本的原因是JAVA中的数组是“协变的(covariant)”,但是泛型并不是。比如, String[] 是 Object[]的一种子类型,但是 Stackf7e83be87db5cd2d9a8a0b8117b38cd4并不是 Stacka87fdacec66f0909fc0757c19f2d2b1d 的一种子类型。 许多程序员认为“协变的”数组是JAVA在数据类型方面的一个缺点。但是,如果我们不考虑泛型,“协变的”数组是有用的,比如实现 Arrays.sort(Comparable[]) 方法,然后当参数是 String[]时它也可以被正常调用。

Q. 可不可以在数组上使用 foreach 方式?

A. 可以的(虽然 数组并没有实现 Iterator 接口)。请参考下面的代码:

public static void main(String[] args) { 
  for (String s : args) 
  StdOut.println(s); 
}

Q. 在 linked list 上使用 iterator 是不是比循环或者递归更有效率?

A. 编译器在翻译时,可能把那种“尾递归”形式翻译成等价的循环形式。所以可能并没有可以被观测到的性能提升。
尾部递归是一种编程技巧。如果在递归函数中,递归调用返回的结果总被直接返回,则称为尾部递归。尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。比如f(n, sum) = f(n-1) + value(n) + sum; 会保存n个函数调用堆栈,而使用尾递归f(n, sum) = f(n-1, sum+value(n)); 这样则只保留后一个函数堆栈即可,之前的可优化删去。

Q. 自动装箱机制会怎么处理下面的情况?

Integer a = null; 
int b = a;

A.它将返回一个运行时错误。基础类型不允许它对应的装箱类型里的值是null。 

Q. 为什么第一组打印的是 true,但是后面两组打印的是 false?

Integer a1 = 100; 
Integer a2 = 100; 
System.out.println(a1 == a2);  // true 
Integer b1 = new Integer(100); 
Integer b2 = new Integer(100); 
System.out.println(b1 == b2);  // false 
Integer c1 = 150; 
Integer c2 = 150; 
System.out.println(c1 == c2);  // false

A. 第二组代码打印 false 是因为 b1 和 b2 指向不同的 Integer 对象引用。第一组和第三组依赖于自动装箱机制。 令人意外的第一组打印了 true 是因为在 -128 和 127 之间的值会自动转换成同样的immutable型的Integer 对象。对于超出那个范围的数,Java会对于每一个数创建一个新的Integer对象。

以上是Java初學者常問的問題總結(收藏)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn