初心者 Java プログラマーの間違い。パート1
著者: A.Grasoff™ 出典へのリンク:初心者 Java プログラマーの間違い
9. main() メソッドからの非静的クラス メソッドの呼び出し
Java プログラムのエントリ ポイントは静的メソッドである必要がありますmain
。
public static void main(String[] args) {
...
}
このメソッドは静的であるため、そこから非静的クラス メソッドを呼び出すことはできません。学生はこのことを忘れて、クラスのインスタンスを作成せずにメソッドを呼び出そうとすることがよくあります。この間違いは通常、トレーニングの最初の段階、つまり学生が小さなプログラムを作成するときに発生します。 間違った例:
public class DivTest {
boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
// на следующие строки компилятор выдаст ошибку
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
このエラーを修正するには 2 つの方法があります。目的のメソッドを静的にするか、クラスのインスタンスを作成します。適切なメソッドを選択するには、そのメソッドがフィールドを使用するか、他のクラス メソッドを使用するかを自問してください。「はい」の場合は、クラスのインスタンスを作成し、そのインスタンスでメソッドを呼び出す必要があります。そうでない場合は、メソッドを静的にする必要があります。 修正例 1:
public class DivTest {
int modulus;
public DivTest(int m) {
modulus = m;
}
boolean divisible(int x) {
return (x % modulus == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
DivTest tester = new DivTest(v2);
if (tester.divisible(v1) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
修正例 2:
public class DivTest {
static boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
10. String クラス オブジェクトをメソッドのパラメータとして使用します。
Java では、クラスにjava.lang.String
文字列データが格納されます。ただし、Java の文字列
- 永続性があります (つまり、変更できません)。
- オブジェクトです。
public static void main(String args[]) {
String test1 = "Today is ";
appendTodaysDate(test1);
System.out.println(test1);
}
/* прим. редактора: закомментированный метод должен иметь модификатор
static (здесь автором допущена ошибка №9)
public void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
*/
public static void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
上の例では、学生はメソッドのtest1
パラメータに新しい値を代入して ローカル変数の値を変更したいと考えています。当然、これではうまくいきません。意味は変わりますが、意味は変わりません。このエラーは、(1) Java オブジェクトは常に参照によって渡され、(2) Java の文字列は不変であるという誤解により発生します。文字列オブジェクトの値は決して変更されず、文字列に対するすべての操作は新しいオブジェクトを作成することを理解する必要があります。上記の例のエラーを修正するには、メソッドから文字列を返すか、の代わりにオブジェクトをパラメータとしてメソッドに渡す必要があります。 修正例 1:line
appendTodaysDate
line
test1
StringBuffer
String
public static void main(String args[]) {
String test1 = "Today is ";
test1 = appendTodaysDate(test1);
System.out.println(test1);
}
public static String appendTodaysDate(String line) {
return (line + (new Date()).toString());
}
修正例 2:
public static void main(String args[]) {
StringBuffer test1 = new StringBuffer("Today is ");
appendTodaysDate(test1);
System.out.println(test1.toString());
}
public static void appendTodaysDate(StringBuffer line) {
line.append((new Date()).toString());
}
約。翻訳 |
11. コンストラクターをメソッドとして宣言する
Java のオブジェクト コンストラクターは、通常のメソッドと外観が似ています。唯一の違いは、コンストラクターが戻り値の型を指定しないことと、名前がクラス名と同じであることです。残念ながら、Java ではメソッド名をクラス名と同じにすることができます。以下の例では、学生はVector list
クラスの作成時にクラス フィールドを初期化したいと考えています。メソッドは'IntList'
コンストラクターではないため、これは起こりません。 間違った例です。
public class IntList {
Vector list;
// Выглядит How конструктор, но на самом деле это метод
public void IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
NullPointerException
このコードは、フィールドに初めてアクセスしたときに 例外をスローしますlist
。このエラーは簡単に修正できます。メソッド ヘッダーから戻り値を削除するだけです。 修正された例:
public class IntList {
Vector list;
// Это конструктор
public IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
12. オブジェクトを必要な型にキャストするのを忘れた
他のすべてのオブジェクト指向言語と同様、Java ではオブジェクトをそのスーパークラスとして参照できます。これは と呼ばれ'upcasting'
、Java で自動的に行われます。ただし、変数、クラス フィールド、またはメソッドの戻り値がスーパークラスとして宣言されている場合、サブクラスのフィールドとメソッドは非表示になります。スーパークラスをサブクラスとして参照するには'downcasting'
、 と呼ばれます。これを自分で登録する (つまり、オブジェクトを目的のサブクラスに移動する) 必要があります。学生はオブジェクトのサブクラス化を忘れてしまうことがよくあります。これは、オブジェクトの配列とパッケージjava.util
( Collection Frameworkを意味します) のコレクションを使用するときに最もよく発生します。以下の例では、String
オブジェクトを配列に入れてから、それを配列から削除して、別の文字列と比較します。コンパイラはエラーを検出し、型キャストが明示的に指定されるまでコードをコンパイルしません。 間違った例です。
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if (arr[0].compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
型キャストの意味を理解するのが難しい人もいます。動的メソッドは特に問題を引き起こすことがよくあります。equals
上の例では、の代わりにメソッド が使用されていた場合、クラスのcompareTo
メソッドが呼び出されるため、コンパイラはエラーをスローせず、コードは正しく動作したでしょう。動的リンクは とは異なることを理解する必要があります。 修正された例:equals
String
downcasting
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if ( ((String) arr[0]).compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
13. インターフェースの使用。
多くの学生にとって、クラスとインターフェイスの違いは完全には明確ではありません。したがって、一部の学生は、 implements の代わりにextendsキーワードを使用してObserver
、 またはなどのインターフェイスを実装しようとします。エラーを修正するには、キーワードを正しいものに修正するだけです。 間違った例:Runnable
public class SharkSim extends Runnable {
float length;
...
}
修正された例:
public class SharkSim implements Runnable {
float length;
...
}
関連エラー: extends ブロックとimplementsブロックの順序が正しくありません。Java 仕様によれば、クラス拡張宣言はインターフェイス実装宣言の前に置く必要があります。また、インターフェイスの場合、 implementsキーワードは1 回だけ記述する必要があり、複数のインターフェイスはカンマで区切られます。 さらに間違った例をいくつか挙げます:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
float length;
...
}
// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
int airLeft;
...
}
修正された例:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
float length;
...
}
// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
int airLeft;
...
}
14. スーパークラスメソッドの戻り値を使い忘れた
Java では、キーワードキーワードを使用して、サブクラスから同様のスーパークラス メソッドを呼び出すことができます。場合によっては、学生はスーパークラス メソッドを呼び出す必要がありますが、戻り値の使用を忘れることがよくあります。これは、メソッドとその戻り値をまだ理解していない学生の間で特によく起こります。以下の例では、学生がtoString()
スーパークラス メソッドの結果をサブクラス メソッドの結果に挿入したいと考えていますtoString()
。ただし、スーパークラスメソッドの戻り値は使用しません。 間違った例:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
super();
return("color=" + fillColor + ", beveled=" + beveled);
}
}
エラーを修正するには、通常、戻り値をローカル変数に割り当て、サブクラス メソッドの結果を計算するときにその変数を使用するだけで十分です。 修正された例:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
String rectStr = super();
return(rectStr + " - " +
"color=" + fillColor + ", beveled=" + beveled);
}
}
15. AWT コンポーネントを追加するのを忘れた
AWT は単純な GUI 設計モデルを使用します。各インターフェイス コンポーネントは、最初に独自のコンストラクターを使用して作成され、次にadd()
親コンポーネント メソッドを使用してアプリケーション ウィンドウに配置される必要があります。したがって、AWT 上のインターフェイスは階層構造を受け取ります。学生はこれら 2 つのステップを忘れることがあります。コンポーネントを作成しましたが、それを拡大ウィンドウに配置するのを忘れました。これによってコンパイル段階でエラーが発生することはなく、コンポーネントがアプリケーション ウィンドウに表示されないだけです。 間違った例です。
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
}
}
このエラーを修正するには、コンポーネントをその親に追加するだけです。以下の例は、これを行う方法を示しています。コンポーネントをアプリケーション ウィンドウに追加することを忘れた学生は、そのコンポーネントにイベント リスナーを割り当てることも忘れることがよくあることに注意してください。 修正された例:
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
Panel controlPanel = new Panel();
controlPanel.add(exit);
add("Center", controlPanel);
exit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
17. ストリームを開始するのを忘れた
Java のマルチスレッドは、 を使用して実装されますjava.lang.Thread
。スレッドのライフサイクルは、初期化、開始、ブロック、停止の 4 つの段階で構成されます。新しく作成されたスレッドは初期化された状態です。実行状態にするには、 を呼び出す必要がありますstart()
。学生がスレッドを作成しても、開始するのを忘れてしまう場合があります。通常、このエラーは、学生が並列プログラミングとマルチスレッドに関する知識が不十分な場合に発生します。(おおよその翻訳: 接続が表示されません) エラーを修正するには、スレッドを開始するだけです。以下の例では、学生がインターフェースを使用して画像のアニメーションを作成したいと考えていますRunnable
が、スレッドを開始するのを忘れていました。 間違った例
public class AnimCanvas extends Canvas implements Runnable {
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
}
// метод run() не будет вызван,
// потому что поток не запущен.
public void run() {
for(int n = 0; n < 10000; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
修正された例:
public class AnimCanvas extends Canvas implements Runnable {
static final int LIMIT = 10000;
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
myThread.start();
}
public void run() {
for(int n = 0; n < LIMIT; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
スレッドのライフサイクルと、スレッドとインターフェイスを実装するクラス間の関係Runnable
Java プログラミングの非常に重要な部分であり、これに焦点を当てることは有益です。
18. java.io.DataInputStreamクラスの禁止されているreadLine()メソッドの使用
Java バージョン 1.0 では、テキスト文字列を読み取るにはreadLine()
クラスメソッドを使用する必要がありましたjava.io.DataInputStream
。Java バージョン 1.1 では、テキストの I/O 操作を提供する I/O クラスのセット全体 ( と ) が追加されましReader
たWriter
。したがって、バージョン 1.1 以降、テキスト行を読み取るには、readLine()
クラスメソッドを使用する必要がありますjava.io.BufferedReader
。学生は、特に古い本から教えられた場合、この変化に気づかない可能性があります。(おおよその訳:実際にはもう意味がありません。今では10年前の本から勉強する人はいないでしょう)。古いメソッドはreadLine()
JDK に残っていますが、違法であると宣言されており、学生を混乱させることがよくあります。理解する必要があるのは、readLine()
クラスメソッドの使用はjava.io.DataInputStream
間違っているわけではなく、単に時代遅れであるということです。クラスを使用する必要がありますBufferedReader
。 間違った例:
public class LineReader {
private DataInputStream dis;
public LineReader(InputStream is) {
dis = new DataInputStream(is);
}
public String getLine() {
String ret = null;
try {
ret = dis.readLine(); // Неправильно! Запрещено.
} catch (IOException ie) { }
return ret;
}
}
修正された例:
public class LineReader {
private BufferedReader br;
public LineReader(InputStream is) {
br = new BufferedReader(new InputStreamReader(is));
}
public String getLine() {
String ret = null;
try {
ret = br.readLine();
} catch (IOException ie) { }
return ret;
}
}
1.0 以降のバージョンでは他にも禁止されているメソッドがありますが、これが最も一般的です。
19. double を float として使用する
他のほとんどの言語と同様、Java は浮動小数点数 (小数) の演算をサポートします。Java には、浮動小数点数用の 2 つのプリミティブ型があります。IEEEdouble
64 ビット精度とfloat
IEEE 32 ビット精度です。問題は、1.75、12.9e17、または -0.00003 などの 10 進数を使用する場合であり、コンパイラはそれらを type に割り当てますdouble
。Java は、精度の損失が発生する可能性のある操作では型キャストを実行しません。この型キャストはプログラマが行う必要があります。たとえば、Java では、以下の例に示すように、型キャストを行わずに 型値を型int
変数に割り当てることはできません。byte
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
小数は type で表されdouble
、double
type の変数への代入はfloat
精度の損失につながる可能性があるため、コンパイラは小数を として使用しようとするとエラーを出しますfloat
。したがって、以下の代入を使用すると、クラスがコンパイルできなくなります。
float realValue1 = -1.7; /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
この割り当ては C または C++ では機能しますが、Java ではさらに厳密になります。このエラーを解決するには 3 つの方法があります。double
の代わりにtype を使用できますfloat
。これが最も簡単な解決策です。実際、64 ビットの代わりに 32 ビット演算を使用することにほとんど意味はありません。速度の違いは依然として JVM によって消費されます (さらに、最新のプロセッサでは、すべての小数が 80 ビット プロセッサの形式に変換されます)。操作の前に登録してください)。これらを使用する唯一の利点float
は、使用するメモリが少なくなることです。これは、多数の小数変数を扱う場合に役立ちます。数値型修飾子を使用して、数値を格納する方法をコンパイラーに指示できます。type の修飾子float - 'f'
。したがって、コンパイラは型 1.75 をdouble
とに割り当てます1.75f - float
。例えば:
float realValue1 = 1.7; /* неправильно! */
float realValue2 = 1.9f; /* правильно */
明示的な型キャストを使用できます。これは最も洗練されていない方法ですが、型変数をdouble
type に変換するときに便利ですfloat
。例:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
浮動小数点数の詳細については、こことここを参照してください。
-- 訳者コメント -- |
GO TO FULL VERSION