この記事では、Java 初心者からよく寄せられる質問をいくつか紹介します。% は小数で割れるかどうかに興味がある人も多いでしょう。 a += b と a = a + b の効果に違いはありますか? 配列の宣言に時間がかかるのはなぜですか? なぜ Java ライブラリはランダム ピボット クイック ソートを使用しないのですか?一連の疑問に疑問がある場合は、この記事を通して詳しく説明しましょう
この記事では、Java 初心者からよく寄せられる質問をいくつか紹介します。% は小数で割ることができますか? a += b と a = a + b の効果に違いはありますか? 配列の宣言にこんなに時間がかかるのはなぜですか? なぜ Java ライブラリはランダム ピボット クイック ソートを使用しないのですか?
基本データ型
Q. -0/3 の結果が 0 になり、-0.0/3.0 の結果が -0.0 になるのはなぜですか? (次の結果 0 には負の符号があることに注意してください)
A. Java では、整数 は 2 の補数で表されます。 2 の補数で 0 を表す方法は 1 つだけです。一方、浮動小数点数は IEEE 標準を使用して表現され、0、0、および -0 を表現するには 2 つの方法があります。
Q. % を小数で割ることはできますか?
A もちろんです。たとえば、角度が非負の数値の場合、角度 % (2 * Math.PI) は角度を 0 ~ 2 π の値に変換します。
Q. a と b が両方とも基本型変数である場合、a += b と a = a + b の効果に違いはありますか?
A. a と b のタイプが異なる場合、2 つのステートメントの効果は異なる可能性があります。 a += b は a = (int) (a + b) と同等です。この場合、a は int 型、b は float 型になります。ただし、同じ状況下では、a = a + b はコンパイルされてエラーが報告されます。
条件文とloop文
Q. 文字列の等価性を判断するために == を使用できないのはなぜですか?
A. これは、基本型 (int、double、boolean) と reference 型 (String) の違いを反映しています。
Q. ステートメントブロックの中括弧を省略できない状況はありますか?
A. 以下の例では、最初のコードは正当ですが、2 番目のコードはコンパイル エラーを引き起こします。技術的な観点から見ると、そのステートメントはステートメントではなく変数宣言であるため、エラーが報告されます。
// legal for (int i = 0; i <= N; i++) { int x = 5; } // illegal for (int i = 0; i <= N; i++) int x = 5;
Q. 次の 2 つのコードで、効果が異なる状況はありますか?
rreeA はい。ループブロック内で continue ステートメントを使用する場合。 for コードではカウンタが 1 増加しますが、while コードでは continue によってスキップされるため、カウンタは 1 増加しません。
Array
Q. 一部の Java 開発者は、配列を宣言するために int[] a の代わりに int a[] を使用します。両者の違いは何ですか?
A. どちらの使用法も Java では合法であり、機能は同じです。前者は C で配列を定義する方法です。後者は、JAVA が推奨するメソッドです。int[] の記述方法により、これが int の配列であることがより適切に示されるためです。
Q. 配列の添字はなぜ 1 ではなく 0 から始まるのですか?
A. この伝統は、機械語のプログラミング方法に由来します。機械語では、要素の位置と最初の要素の間のオフセットを計算するために配列の添え字が使用されます。 1からスタートしてしまうと、オフセットを計算する際に減算演算が必要となり、無駄が生じます。
Q. 配列の添え字として負の数値を使用するとどうなりますか?
A. 添字が 0 未満、または配列の長さ以上の場合、JAVA は実行時に ArrayIndexOutOfBoundsException 例外をスローし、プログラムを終了します。
Q. 配列を使用するときに注意する必要がある落とし穴は他にありますか?
A. JAVA は配列を作成するときに初期化するため、配列の宣言には O(N) 時間がかかることに注意してください。
Q. a[] は配列なので、System.out.println(a) は配列の要素を出力するのではなく、@f62373 のような 16 進数を出力するのはなぜですか?
A. 良い質問です。このステートメントは、メモリ内の配列のアドレスを出力します。システムは、配列の toString() メソッドを自動的に呼び出します。この問題については、toString() メソッドのソース コードを参照してください。
FunctionCall
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[]的一种子类型,但是 Stack
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 中国語 Web サイトの他の関連記事を参照してください。