FindBugs バグ詳細FindBugs version 1.3.5 によってレポートされるバグパターンの一覧です。 サマリ詳細説明AM : 空のjarファイルエントリを作成しています。
|
| 置換前 | 置換後 |
|---|---|
| new Integer(1).toString() | Integer.toString(1) |
| new Long(1).toString() | Long.toString(1) |
| new Float(1.0).toString() | Float.toString(1.0) |
| new Double(1.0).toString() | Double.toString(1.0) |
| new Byte(1).toString() | Byte.toString(1) |
| new Short(1).toString() | Short.toString(1) |
| new Boolean(true).toString() | Boolean.toString(true) |
new Double(double)を呼び出すと、常に新たなオブジェクトが生成されます。これに対し、Double.valueOf(double)は、コンパイラ、クラスライブラリ、JVMによってキャッシュすることが可能です。キャッシュを使用することによって、余計なオブジェクトの生成を防ぎ、コードの実行効率を改善できます。
バージョン5.0より前のJava実行環境との互換性が不要であれば、オートボクシングか、Double、FloatのvalueOf()メソッドを代わりに使用してください。
new Integer(int)の呼び出しは、常に新たなオブジェクトが生成します。これに対してInteger.valueOf(int)は、コンパイラ、クラスライブラリ、JVMによってキャッシュすることが可能です。キャッシュを使用することによって、余計なオブジェクトの生成を防ぎ、コードの実行効率を改善できます。
-128から127までの値は、キャッシュされるので、valueOfを使用すると、コンストラクタを利用するよりも約3.5倍高速になります。この範囲外の場合にはパフォーマンスの差はありません。
バージョン5.0より前のJava実行環境との互換性が不要であれば、オートボクシングか、Long、Integer、Short、Character、ByteのvalueOf()メソッドを代わりに使用してください。
このクラスは、他のクラスと循環依存関係にあります。このため、それぞれクラスのコンパイルの際に別のクラスがコンパイル済である必要があり、全体のコンパイルが難しいものになっています。インターフェースを用いて強い依存関係を断ち切る事を検討してください。
このクラスはfinal宣言されていますが、protectedなフィールドが宣言されています。このクラスは継承できないのですから、protectedフィールドを宣言すると誤解を招きます。アクセス修飾はprivateかpublic(訳注:かパッケージプライベート)に変更して、正しくフィールドが利用されるようにすべきです。
クラスはCloneableを実装していますが、cloneメソッドを定義していないか使用していません。
このクラスはclone()を定義していますが、super.clone()を呼び出しておらず、finalでもありません。 もしもクラス("B")が("A")を継承しており、サブクラスBがsuper.clone()を呼び出さないと、Bのclone()メソッドはAのインスタンスを返してしまいます。これはclone()メソッドの規約を満たしていません。
全てのclone()メソッドがsuper.clone()を呼び出すようになっていれば、Object.clone()が呼び出される事が保証され、正しい型のオブジェクトが返送されます。
このクラスでは clone() メソッドを宣言していますが、Cloneable インターフェースを実装していません。このような状況が許される場合(たとえば、サブクラスのクローン動作を制御したい場合など)もありますが、これが本当に意図されたものであるかどうか、確認してください。
このクラスは、引数がjava.lang.ObjectでないcompareTo()メソッドを定義しています。
ComparableインターフェースのcompareTo()メソッドを正しく実装するには、compareTo()メソッドの引数の型は、java.lang.Objectでなければなりません。
このクラスは、引数がjava.lang.ObjectでないcompareTo()メソッドを定義しています。
ComparableインターフェースのcompareTo()メソッドを正しく実装するには、compareTo()メソッドの引数の型は、java.lang.Objectでなければなりません。
このメソッドに記述された複数の条件分岐で、同じコードが書かれています。コーディングミスでないかどうか確認してください。
このメソッドの中にあるswitch文には、同じコードがあります。単なるコードの重複かもしれませんが、コーディングミスの可能性もあります。
メソッド内にダブルチェックロッキングイディオムがあります。このイディオムはJavaのメモリーモデルでは正しく機能しません。詳細についてはhttp://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.htmlを参照してください。
このメソッドは例外を握りつぶしている可能性があります。一般に、例外が発生したら何らかの形で処理するか、呼び出し元にスローすべきです。
このメソッドは例外を無視している可能性があります。一般に、例外が発生したら何らかの形で処理するか、呼び出し元にスローすべきです。
Boolean クラスのようなボクシングされたプリミティブ値に対して同期化を行っています。
private static Boolean inited = Boolean.FALSE;
...
synchronized(inited) {
if (!inited) {
init();
inited = Boolean.TRUE;
}
}
...
このようなコードは、Boolean オブジェクトが2つのみ存在する場合は正常に終了します。しかし、関連性のない他のコードが同じオブジェクトに対して同期するため、応答性の低下やデッドロックの原因となります。
Integer クラスのようなボクシングされたプリミティブ値に対して同期化を行っています。
private static Integer count = 0;
...
synchronized(count) {
count++;
}
...
Integer オブジェクトはキャッシュされて共有されるため、このようなコードは関連性のない他のコードが同じオブジェクトに対して同期してしまいます。これは、応答性の低下やデッドロックの原因となります。
このコードでは、正規化された String オブジェクトに対して同期を行っています。
private static String LOCK = "LOCK";
...
synchronized(LOCK) { ...}
...
文字列定数は正規化され、JVM によって読み込まれたすべてのクラスの間で共有されます。このため、あるコードで文字列定数に対してロックを行っていると、同じ文字列定数でロックを行う他のコードでロック待ちが発生してしまいます。これは、非常に奇妙で解析困難なブロックとデッドロックに関する振る舞いです。 http://www.javalobby.org/java/forums/t96352.html および http://jira.codehaus.org/browse/JETTY-352 を参照してください。
このコードでは、Integer クラスのような一見共有されていないように見えるボクシングされたプリミティブ値に対して同期を行っています。
private static final Integer fileLock = new Integer(1);
...
synchronized(fileLock) {
.. do something ..
}
...
このコードは、 fileLock を以下のように宣言した方がよいでしょう。
private static final Object fileLock = new Object();
最初のコードには、ひとまず問題はありません。しかし、将来におけるリファクタリングによって、混乱を招くおそれがあります。たとえば、IntelliJ の "Remove Boxing" リファクタリング機能を使うと、これを JVM の中で共有される正規化された Integer オブジェクトを利用するように置き換えてしまい、非常に分かりにくい振る舞いとデッドロックの危険性につながってしまいます。
ローカル変数への代入が行なわれていますが、これ以降この値が読み出されることはありません。計算された値が結局は使用されないので、大抵の場合は間違いです。
ただし、Sun の提供する javac コンパイラはしばしばこのようなローカル変数に対する無意味な代入を行うコードを生成します。FindBugs はバイトコード解析に基づくツールなので、このようなコンパイラが生成したコードと本来のコードを見分けることが難しく、誤って検出してしまうことがある点に注意してください。
return 文の中でローカル変数に対して代入を行っています。この代入操作によって、ローカル変数の値が変化します。これが正当な操作であるのかどうか、確認してください。
このコードはローカル変数に null を代入していますが、この変数はその後読み出されていません。恐らく null を代入することで、ガーベージコレクタにヒントを与えることを意図したと思われますが、Java SE 6.0 からは、このような記述は不要です。
この操作ではクラス定数を変数へ代入していますが、一度も利用していません。
この振る舞いは Java 1.4 と Java 5 で異なります。
Java 1.4 およびそれ以前のバージョンでは、Foo.class への参照を行うと、Foo クラスのスタティックイニシャライザが実行されていない場合、強制的に実行されます。しかし Java 5 以降のバージョンでは、スタティックイニシャライザは実行されません。
より詳細な情報と具体例、そして Java 5 においてクラスの初期化を強制的に行う方法については、Sun によるJava SE の互換性に関する記事を参照してください。
このコードはインクリメント操作(すなわちi++)の後、ただちに内容を上書きしています。例えば、i = i++は、インクリメントされた値を元の値で上書きします。
java.lang.Booleanのインスタンスを新規に生成するのはメモリの浪費です。Booleanクラスはイミュータブルなので、TRUEとFALSEの2つのオブジェクトがあれば十分です。 Boolean.valueOf()(あるいは、Java 5以上ならばオートボクシング)を替わりに使ってください。
文字列がプラットホームのデフォルトエンコーディングで、大文字、小文字に変換されています。これらは国際文字に対して行なわれると間違った結果を招くことがあります。かわりに
| String.toUpperCase(Locale l) |
| String.toLowerCase(Locale l) |
を使用してください。
System.exit は、VM全体を終了させてしまいます。これは、本当に必要な場面に限って 使用すべきです。このようなコードは、再利用性を損なう事があります。かわりにRuntimeException をスローする事を検討してください。
明示的にガベージコレクションを呼び出しています。ベンチマークで使用されるケースを除けば、これは大変疑わしいコードです。
過去において、close()やfinalize()メソッドでのガベージコレクションを明示的に呼び出すことが、パフォーマンスのブラックホールに陥れるケースがありました。ガベージコレクションは高くつく場合があります。数百、数千のガベージコレクション呼び出しは、システムのパフォーマンスを極めて落とす事になります。
このメソッドはjava.util.concurrent.locks.Condition objectのwait()を呼び出しています。Conditionオブジェクトで待ち合わせる場合はConditionインターフェースに用意されたawait()のどれかを使用すべきです。
このメソッドは、getClass()を呼び出すためだけに、インスタンスを生成しています。あるクラスのクラスオブジェクトを得るには、.classプロパティを利用する方が簡単です。
java.util.Random のインスタンス rに対して 0 から n-1 の乱数を得たいのであれば、(int)(r.nextDouble() * n)のかわりにr.nextInt(n)を使うことができます。
System.runFinalizersOnExit、Runtime.runFinalizersOnExitをどんな理由があろうと呼び出してはならない。これらは、 最も危険なJavaクラスライブラリのメソッドである。 -- Joshua Bloch
java.lang.String(String) コンストラクタの呼び出しはメモリを浪費するだけです。このようにして生成されたオブジェクトと元の String オブジェクトは機能的に同じです。String 文字列を直接利用するようにしてください。
eauals() メソッドを使って空文字列との比較を行っています。これよりも、length() メソッドを使って長さ0かどうか調べる方が高速です。また、このようにすることでクラスファイルから無駄な文字列を削除することが出来ます。
String.toString()を明示的に呼び出すのは冗長です。
元のStringをそのまま使用してください。
java.lang.Stringの引数無しコンストラクタの呼び出しはメモリを浪費するだけです。このようにして生成されたオブジェクトと、空文字列""の間には機能的に違いがありません。Javaは、同一内容の文字列定数のインスタンスを1つにまとめます。従って単に空文字列を直接使用すべきです。
このメソッドは、Theadインスタンスを生成していますが、Runnableオブジェクトも渡していなければ、runメソッドをオーバーライドした継承クラスも生成していません。このスレッドは無駄です。
アノテーションが@Retentionアノテーションによって、デフォルトの「ソースのみ」設定以外に変更されていない場合、アノテーションはクラスファイルに格納されません。このような場合は、リフレクション(例えばisAnnotationPresentメソッドを用いて)でアノテーションを参照することはできません。
URLのquals()とhashCode()は、ドメイン名の解決を行うので、パフォーマンスが大きく損われる可能性があります。詳細はhttp://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.htmlを参照してください。
替わりにjava.net.URIを使用することを検討してください。
このフィールドあるいはメソッドは、URLのMap、Setを使用しているか、URLのMap、Set自身です。URLのequals()とhashCode()は、ドメイン名の解決を行うため、パフォーマンスが大きく損われる可能性があります。詳細については、http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.htmlを参照してください。
替わりにjava.net.URIを使用することを検討してください。
このコードは、ハードコードされた定数パスワードによってデータベースコネクションを生成しています。ソースコードやコンパイルされたバイトコードへアクセスできる人間ならば、簡単にパスワードを知ることができてしまいます。
このコードは、空白または空のパスワードによってデータベースコネクションを生成しています。これはデータベースがパスワードによって守られていないことを示しています。
引数にRunnableを期待しているメソッドに対してThreadオブジェクトが渡されています。通常はこのような書き方はしません。ロジック上の間違いを示すか、予期しない挙動を招く可能性があります。
ここでのメソッド呼び出しはすべて UnsupportedOperationException がスローされます。
このコードは月の定数として0から11の範囲を超えた値を使用しています。
hasNext() で next() を呼び出しています。hasNext()はイテレータの状態を変えるものではなく、next()メソッドがイテレータの状態を変えるはずなので、このコードは恐らくほとんどの場合間違っています。
このコードはハードコードされた絶対パスでFileオブジェクトを生成しています。
(例:new File("/home/dannyc/workspace/j2ee/src/share/com/sun/enterprise/deployment");)
このコードは、配列に対して toString() メソッドを呼び出しています。配列に対する toString() メソッドの呼び出しは、C@16f0472 のようにまったく意味のない結果をもたらします。配列をその内容を表す可読文字列に変換するには、Arrays.toString() メソッドの利用を検討してください。また、Programming Puzzlers の chapter 3、puzzle 12 も参照してみてください。
このコードは配列に対してtoStringを呼び出しています。これは"[C@16f0472"のような、あまり意味の無い結果を返します。かわりにArrays.toStringを使って配列の中身が分かるような文字列表現を得ることを検討してください。プログラミングパズルの第3章、パズル12を参照してください。
Double.longBitsToDoubleメソッドが呼び出されていますが、引数には32bit intが渡されています。これはほぼ間違いなく意図されたものではなく、期待した結果は得られないでしょう。
このコードは直列化できないオブジェクトをObjectOutput.writeObjectメソッドに渡しているようです。このオブジェクトが実際に直列化できないのであれば結果はエラーとなります。
このコードはsubstring(0)を呼び出していますが、これは元のStringと同じものを返します。
このコードはクラスローダを生成していますが、これはセキュリティマネージャを必要とします。このコード自体がセキュリティ許可を得る予定でも、セキュリティ許可を持たないコードから呼び出されるのであれば、クラスローダの生成は、doPrivilegedブロックの中で行う必要があります。
このコードはクラスローダを生成していますが、これはセキュリティマネージャを必要とします。このコード自体がセキュリティ許可を得る予定でも、セキュリティ許可を持たないコードから呼び出されるのであれば、クラスローダの生成は、doPrivilegedブロックの中で行う必要があります。
このコードはクラスローダを生成していますが、これはセキュリティマネージャを必要とします。このコード自体がセキュリティ許可を得る予定でも、セキュリティ許可を持たないコードから呼び出されるのであれば、クラスローダの生成は、doPrivilegedブロックの中で行う必要があります。
このコードはセキュリティの許可チェックが必要なメソッドを呼び出しています。もしもこのコードにセキュリティ許可が与えられていても、セキュリティ許可を持たないコードから呼び出されるのであれば、doPrivilegedブロックの中から呼び出す必要があります。
このメソッドの中で、配列オブジェクトと配列でないと思われるオブジェクトを.equals(Object o)で比較しています。双方のオブジェクトの型が異なるのであれば、比較の結果は常に偽となります。大抵は何かの間違いです。仮に配列同志の比較だとしても、配列オブジェクトのequalsによる比較は両者が同じものであるかを判定するだけです。配列の内容が等しいかどうかまで調べたいのであれば、java.util.Arrays.equals(Object[], Object[])を使用してください。
このメソッドは配列オブジェクトの.equals(Object o)メソッドを呼び出しています。配列はequalsメソッドをオーバーライドできないため、これは参照の比較しか行ないません。配列の内容を比較するならjava.util.Array.equals(Object[], Object[])を用いなければなりません。
このメソッドは、equals(Object)にnullを渡しています。正しいequals()の実装では、これは常にfalseを返します。
このメソッドでは、2つの参照をequals(Object)によって比較しています。片方はクラス、もう片方はインターフェースですが、このクラスおよび非抽象などのようなサブクラスも、このインターフェースを実装していません。このため、比較しようとしている2つのオブジェクトは実行時において同じクラスのインスタンスとはなりません(調査外となってしまったアプリケーションクラスや、実行時において動的にローディングされるクラスが当てはまる可能性は除きます)。equals() の規約によると、異なる型のオブジェクトを比較結果は不等とならなければなりません。このため、実行時におけるこの比較結果は常にfalseになるものと思われます。
このメソッドでは、 equals(Object) によって無関係なインターフェースの2つの インスタンス間を比較しようとしています。ここで無関係であるとは、互いが互いのサブインターフェースではなく、両方のインターフェースを実装する既知の非抽象クラスが存在しないことをいいます。 このため、比較しようとしている2つのオブジェクトは実行時において同じクラスのインスタンスとはなりません(調査外となってしまったアプリケーションクラスや、実行時において動的にローディングされるクラスが当てはまる可能性は除きます)。equals() の規約によると、異なる型のオブジェクトを比較結果は不等とならなければなりません。このため、実行時におけるこの比較結果は常にfalseになるものと思われます。
このメソッドでは、共通のサブクラスを持たない、異なる型のオブジェクト参照を equals(Object) によって比較しています。このため、比較しようとしている2つのオブジェクトは実行時において同じクラスのインスタンスとはなりません(調査外となってしまったアプリケーションクラスや、実行時において動的にローディングされるクラスが当てはまる可能性は除きます)。 equals() の規約によると、異なる型のオブジェクトを比較結果は不等とならなければなりません。このため、実行時におけるこの比較結果は常にfalseになるものと思われます。
このメソッドでは、異なる型に見える2つの参照を等式で比較しています。この比較の結果は、実行時においては常に false となります。
オブジェクトのフィールドに格納されたミュータブルなオブジェクトの参照を返すと、オブジェクトの内部表現を暴露してしまいます。このオブジェクトが信頼されていないコードによってアクセスされると、ミュータブルオブジェクトの予測しない変更が発生してしまいます。それがセキュリティやその他の重要な情報への脅威となる可能性があるため、このコードは修正すべきです。多くの場面では、防御的コピーを行って、オブジェクトの複製を返すのが良いやり方です。
このコードは、外部の変更可能オブジェクトの参照を自身の内部表現として格納しています。このオブジェクトが信頼されていないコードによってアクセスされると、ミュータブルオブジェクトの予測しない変更が発生してしまいます。それがセキュリティやその他の重要な情報への脅威となる可能性があるため、このコードは修正すべきです。多くの場面では、防御的コピーを行って、オブジェクトの複製を返すのが良いやり方です。
このクラスには、引数がObjectでないequals()メソッドが定義されています。
java.lang.Objectのequals()メソッドを正しくオーバーライドするには、equals()メソッドの引数は、java.lang.Objectでなければなりません。
このクラスでは、常に false を返す equlas() メソッドを宣言しています。これは、あるオブジェクトがそれ自身に対して等価ではないことを示しており、このクラスを利用した場合、有効な Map や Set を生成することができません。根本的な問題として、これは equals() メソッドの要求である反射性を満たしていないことになります。
これに似たことを表す動作として、オブジェクトの自己同一性があります。つまり、あるオブジェクトはそれ自身と等価であるということです。これは、Object クラスから継承された振る舞いです。もし、別のスーパークラスから継承された equals() メソッドをオーバーライドする必要がある場合、以下のようなコードを利用すると良いでしょう。
public boolean equals(Object o) { return this == o; }
このクラスでは、常に true を返す equals() メソッドを宣言しています。これは創意に富んでいますが、あまり良い方法とはいえません。加えて、equals() メソッドが対称性を持っていないことを示しています。
このクラスは、compareTo(...)を宣言していますがequals()をjava.lang.Objectから継承しています。一般にcompareToが0を返す条件は、equalsがtrueを返す条件と一致する必要があります。これを守らないと複雑で予測不可能な問題が、例えばPriorityQueueで発生するでしょう。Java 5では、PriorityQueue.remove()は、compareTo()を使用していますがJava 6では、equals()を使用しています。
ComparableインターフェースのcompareToメソッドのJavaDocを以下に引用します。
必須というわけではありませんが、(x.compareTo(y)==0) == (x.equals(y))であることが強く推奨されます。一般に、Comparable インターフェースを実装しているクラスで、この条件に違反するクラスはすべて、明確にこの事実を示す必要があります。「注:このクラスは equals と一貫性のない自然順序付けを持ちます」などと明示することをお勧めします。
このメソッドでは、クラス名称を比較することで、2つのオブジェクトが同じクラスであるかどうかをチェックしています。しかし、異なるクラスローダによってロードされたクラスでは、クラス名称が同じであるにもかかわらず、異なるクラスであるという現象も発生し得ます。単純に Class オブジェクトが同じであるかどうかによって比較してください。
このクラスでは、equlas() メソッドを宣言しているクラスを継承し、フィールドを追加しているにもかかわらず、自身の equals() メソッドを宣言しています。そのため、このクラスのインスタンスに対する等価性の検証では、サブクラス側の同一性と追加されたフィールドが無視されてしまいっています。これが、何を意図したものであるのかと、equlas() メソッドをオーバーライドする必要がないかどうかを確認してください。equals() メソッドをオーバーライドする必要がないにせよ、サブクラスに対してその事実を示すために、単純に super.equals(o) を返すように equlas() メソッドをオーバーライドすることを検討してください。
このクラスはタイプセーフenumを定義しており、同値性定義は、参照の一致によって決定しています。タイプセーフenumで共変なequals()メソッドを定義するのは、非常に悪い習慣です。なぜなら2つの異なる列挙定数が、通常のequals()では異なる値と判定されるのに、共変のequals()メソッドでは同値と判定されるかもしれないからです。
このクラスは、サブクラスによる継承によって破壊される可能性のある equlas() メソッドを宣言しています。このメソッドでは、クラス定数と引数で渡された Class オブジェクトを比較しています(たとえば、Foo クラスにおいて、Foo.class == o.getClass() のような判定を行っています)。これは、this.getClass() == o.getClass() のような判定に変更する方がよいでしょう。
このクラスは equals() メソッドを宣言していますが、ベースクラスである java.lang.Object で宣言された通常の equals(Object) メソッドをオーバーライドしていません。その代わりに、スーパークラスの equals(Object) メソッドを継承しています。このクラスは、boolean equals(Object) メソッドを宣言すべきです。
このクラスは equals() メソッドを宣言していますが、ベースクラスである java.lang.Object で宣言された通常の equals(Object) メソッドをオーバーライドしていません。このクラスは、boolean equals(Object) メソッドを宣言すべきです。
このクラスはスーパークラスの equals() メソッドをオーバーライドしています。両方の equals() メソッドをは2つのオブジェクトの比較に instanceof 演算子を利用しています。しかし、equlas() メソッドは対称性(a.equals(b) == b.equals(a) を満たしていること)を有していなければならないため、これは危険をはらんでいます。
もし B が A のサブタイプであって、A の equals() メソッドの中で引数が A のインスタンスであることをチェックし、B の equals() メソッドの中で引数が B のインスタンスであることをチェックしているのならば、これらのメソッドによって定義される等価性の関係が対称性を満たしていない可能性があります。
このクラスには、引数がObjectでないequals()メソッドが定義されています。
java.lang.Objectのequals()メソッドを正しくオーバーライドするには、equals()メソッドの引数は、java.lang.Objectでなければなりません。
このクラスには、引数がObjectでないequals()メソッドが定義されていますが、Objectクラスのequals(Object)も継承しています。
このクラスは、おそらく共変でないequals()を定義すべきです。
(すなわち、メソッドシグニチャを次のようにすべきです boolean equals(java.lang.Object)。
このクラスの equals() メソッドでは、this オブジェクトと引数の型の互換性をチェックするために、現在知られているいかなるパターンの処理も行っていません。このコードにはなんの不具合もないのかもしれませんが、レビューする価値があります。
このコードはjava.lang.Stringのパラメータを==、!=を使用して比較しています。これは呼び出し元が文字列定数か、internされた文字列しか渡せないことを意味しており、非常に脆いものになっています。このようにしても実行効率上のメリットはほとんどありません。equals(Object)を代わりに使用することを検討してください。
このコードはjava.lang.Stringのオブジェクトは==もしくは!=を使って参照の比較を行っています。双方のオブジェクトがソースコード上で定数となっている文字列であるか、String.intern()で得られた文字列でなければ、同じ内容でも別のオブジェクトとなる可能性があります。替わりに equals(Object)メソッドの利用を検討してください。
このコードは、以下のような空のsynchronizedブロックを含んでいます。
synchronized() {}
空のsynchronizedブロックは難解で、考えられているほど利用は容易ではありません。また大抵の場合は、すこし不格好なコードになっても空のsynchronizedブロックを使用しない方が、より優れたコードになります。
2つの浮動小数の値が同値であるか比較されています。浮動小数は桁丸めが行なわれるためfloat、doubleの値は誤差を含む場合があります。勘定系の処理のように正確な値が必要な場合には、BigDecimalのような固定小数を使用する事を検討してください。誤差があっても良いケースならば、値の差がある範囲ならば同値であると判断する事を検討してください。これは例えば次のようにします。
if ( Math.abs(x - y) < .0000001 ).
詳しくはJava言語仕様4.2.4.を参照してください。
このコードは浮動小数点数がNaN(Not A Number)と比較されています(例:if (x == Double.NaN))。しかしNaNの定義上、全てのとり得る値はNaN自身も含めてNanとは等しくなりません。このためx == Double.NaNは常にfalseとなります。値xはNot A Numberであるかどうかを調べるにはDouble.isNaN(x) (もしもxがfloatならばFloat.isNaN(x)を代わりに使用してください。
空のfinalize()メソッドは無駄です。削除するべきです。
このメソッド内でfinalize()の明示的な呼び出しが行われています。ファイナライザはVMによって一度だけ呼び出されることを意図しているため、このコードは良くありません。
参照によってつながった複数のオブジェクトがファイナライズ可能になると、それぞれのオブジェクトのfinalize()がVMから呼び出されますが、もしかすると別々のスレッドによって複数のfinalize()が同時に呼び出されるかもしれません。このため、あるクラスXの finalize() の中から、Xから参照されている別のオブジェクトのfinalize()を呼び出すのは、とりわけ間違ったやり方です。なぜなら、そのオブジェクトはすでに別のスレッドによってファイナライズされているかもしれないからです。
このファイナライザでは、フィールドに null を設定しています。これは通常ガーベジコレクションの助けとはならないため、エラーです。そして、いずれにせよガーベジコレクションによってオブジェクトは破棄されます。
このファイナライザでは、フィールドに null を設定する以外になにもしていません。これはまったく無意味であり、オブジェクトがガーベジコレクトされ、ファイナライズされ、さらにもう一度ガーベジコレクトされることを要求しています。このような finalize() メソッドは削除すべきです。
このfinalize()メソッドは、親クラスのfinalize()を呼び出していません。このため親クラスで定義されたファイナライズ処理が行われません。super.finalize()を呼び出すように変更してください。
finalize()メソッドが空なので、親クラスのファイナライザを無視することになり、親クラスで定義されたファイナライザの処理が呼び出されなくなってしまいます。そのような意図がないのであれば、このメソッドを削除してください。
このクラスの finalize() メソッドは public になっていますが、protected であるべきです。
このfinalize()メソッドは、親クラスのfinalize()を呼び出しているだけで、無駄です。削除してください。
このメソッドは float の精度で演算を行っています。floatの精度は非常に誤差の多いものです。例えば16777216.0f + 1.0f は、16777216.0fとなります。替わりに double 型の利用を検討してください。
このジェネリックメソッドの引数型は、コンテナの型パラメータの型と異なるものです。このため、コンテナが引数の型のオブジェクトを保持していることは考えにくいです。
このクラスはequals(Object)をオーバーライドしていますが、hashCode()をオーバーライドしていません。このため、このクラスは同値のオブジェクトが等しいハッシュコードを返さなければならないという規約を破る可能性があります。
このクラスはequals(Object)をオーバーライドしていますが、hashCode()をオーバーライドしていないので、java.lang.ObjectのhashCode()の実装をそのまま利用しています(これは、VMがObjectに割り当てた一意のハッシュコードを返します)。このため、同値のオブジェクトが等しいハッシュコードを返さなければならないという規約を破る可能性が非常に高いです。
もしもこのクラスのインスタンスがHashMap/HashTableに挿入されることがあり得ないと考えるのであれば、推奨されるhashCodeの実装は次のようになります。
public int hashCode() {
assert false : "hashCodeが呼び出されることは想定されていません。";
return 42; // 適当な値
}
このクラスはhashCode()メソッドを定義していますが、equals()メソッドを定義していません。このため、同値のオブジェクトは同じハッシュコードを返すべきという規約を破る可能性があります。
このクラスはhashCode()メソッドを定義していますが、equals()メソッドはjava.lang.Objectのもの(参照が同一かどうかで同値性を判定します)をそのまま利用しています。これは、同値であると判定されたオブジェクトが同じハッシュコードを返さなければならないという規約は満たしますが、恐らくhashCode()メソッドのオーバーライドは意図されたものでは無いと思われます(hashCode()のオーバーライドは、単なる参照が同一という同値性よりも複雑な基準がある事を意味します)。
もしもこのクラスのインスタンスがHashMap/HashTableに挿入されることがあり得ないと考えるのであれば、推奨されるhashCodeの実装は次のようになります。
public int hashCode() {
assert false : "hashCodeが呼び出されることは想定されていません。";
return 42; // 適当な値
}
このクラスはequals(Object)を親の抽象クラスから継承しhashCode()
は、java.lang.Objectクラスのものをそのまま使用しています(これは、VMによってアサイン
された任意の値を返送します)。このクラスは「等しいオブジェクトが、同値のハッシュコードを返さなければならない」
という規約を満たすのが非常に困難になっています。
もしも hashCode を定義する必要がない、あるいはこのオブジェクトをHashMap/Hashtableに格納する可能性がないと考えるのであれは、hashCode()メソッドをUnsupportedOperationExceptionを
スローするように実装してください。
このクラスは equals(Object) を定義していますが hashCode() を定義していません。このため、同値のオブジェクトが等しいhashCode()を返さなければいけないという規約を満たしていません。しかも、このインスタンスは実際にハッシュデータ構造に利用されています。早急にこのクラスを修正してください。
このコードでは、信頼できない HTTP パラメータを利用して HTTP クッキーを生成しています。このクッキーが HTTP レスポンスへ追加された場合、HTTP レスポンス・スプリッティングの脆弱性が生じてしまいます。HTTP レスポンス・スプリッティングの詳細については、http://en.wikipedia.org/wiki/HTTP_response_splitting をご覧ください。 FindBugs は HTTP レスポンス・スプリッティングのもっとも明白な事例しか検出しません。 もし FindBugs が一つでも事例を発見した場合、FindBugs がレポートしないより多くの脆弱性を抱えている可能性が非常に高いです。 HTTP レスポンス・スプリッティングについて心配があるのならば、FindBugs プロジェクトのスポンサーである Fortify Software が提供するような商用静的解析ツールの導入を真剣に検討すべきです。もしあなたのソフトウェアがオープンソースであるならなば、Fortify は JOR (Java Open Review) の一環としてあなたのコードを無償で調査します。
このコードでは、HTTP ヘッダに対して HTTP パラメータを直接書き込んでいます。これは HTTP レスポンス・スプリッティングの脆弱性につながります。HTTP レスポンス・スプリッティングの詳細については、http://en.wikipedia.org/wiki/HTTP_response_splitting をご覧ください。 FindBugs は HTTP レスポンス・スプリッティングのもっとも明白な事例しか検出しません。 もし FindBugs が一つでも事例を発見した場合、FindBugs がレポートしないより多くの脆弱性を抱えている可能性が非常に高いです。 HTTP レスポンス・スプリッティングについて心配があるのならば、FindBugs プロジェクトのスポンサーである Fortify Software が提供するような商用静的解析ツールの導入を真剣に検討すべきです。もしあなたのソフトウェアがオープンソースであるならなば、Fortify は JOR (Java Open Review) の一環としてあなたのコードを無償で調査します。
複数のクラスに巨大な文字列定数が重複して存在しています。これはもしかすると、finalフィールドが持つ文字列定数が、利用者側にクラスファイルに埋め込まれるJavaの特性によるものかもしれません。詳細、回避方法については、JDK bug 6447475を参照してください。これによってJDKのサイズを1MByte減らすことができました。
内部クラスの中で呼び出しているメソッドは、継承元クラスのメソッド、エンクロージングクラスのメソッドのどちらとも解釈できます。Javaの仕様上は継承元クラスのメソッドを呼び出しますが、この動作はプログラマの意図とは異なるかもしれません。もしも継承元クラスのメソッドを呼び出したいのであれば、superを付けることで(例:super.foo(17))、このコードを読む人とFindBugsに、エンクロージングクラスのメソッドではなく、継承元のメソッドを呼びたいという意図を明確にすることができます。
2つのクラスのスタティックイニシャライザの中で相互のクラスを参照しているため、循環参照となっています。このため、様々な予測出来ない挙動が起こる可能性があります。
あるクラスの初期化の間に子クラスを使用しています。子クラスはまだこの段階では初期化されていません。たとえば以下ようなの例では、foo は null となってしまいます。
public class CircularClassInitialization {
static class InnerClassSingleton extends CircularClassInitialization {
static InnerClassSingleton singleton = new InnerClassSingleton();
}
static CircularClassInitialization foo = InnerClassSingleton.singleton;
}
このコードは int 値が定数値分シフトされていますが、シフトビット数が 0 から 31 の範囲をこえています。実際のシフト量は指定量の下位 5 bit(訳注:つまり32で割った余り)が使用されますが、恐らく意図とは違うものであり、少なくとも混乱を招きます。
このコードは int の除算結果を double または float にキャストしています。整数の除算結果は 0 に近い方の整数へ切り捨てが行われます。除算結果を double へキャストしているのは、精度が維持されると考えていたことを示唆しています。恐らくコードの意図は除算の前にdoubleへのキャストを行なうことだったと考えられます。以下に例を示します。
int x = 2; int y = 5; // 間違い: 結果は0.0となる double value1 = x / y; // 正しい: 結果は0.4となる double value2 = x / (double) y;
このコードは int を double に変換した後に、値を最も近い次の整数値に丸める Math.ceil() メソッドに引き渡しています。整数から double への変換に際して、小数点以下の桁は登場しないため、この処理には意味がありません。Math.ceil() に渡している値を、その後 double 型の演算で使用するためかもしれません。
このコードは int を float に変換した後に、Math.round() メソッドに渡して丸め処理を行っています。int から floatへの変換に際して、小数点以下の桁は登場しないため、この処理には意味がありません。恐らくMath.round() に渡している値を、その後 float 型の演算で使用するためかもしれません。
このコードは int の乗算を計算してから結果を long に変換しています。
long 型を用いて乗算することで、オーバーフローしてしまう可能性を避けることができます。例えば、以下のように修正します。
long convertDaysToMilliseconds(int days) { return 1000*3600*24*days; }
あるいは、以下のようにしてもよいでしょう。
long convertDaysToMilliseconds(int days) { return 1000L*3600*24*days; }
static final long MILLISECONDS_PER_DAY = 24L*3600*1000;
long convertDaysToMilliseconds(int days) { return days * MILLISECONDS_PER_DAY; }
このコードは符号無し右シフトを行なった後、結果をshortあるいはbyteにキャストしています。これは(シフト量によりますが)上位ビットを捨ててしまうので、符号無しでも符号ありでも一緒のはずです。
runメソッドの中でJUnitのアサーションが実行されています。JUnitのアサーションは失敗すると、例外をスローします。テストメソッドを実行したスレッド以外のスレッドでこの例外がスローされると、スレッドの実行が終了するだけでテストの失敗とはなりません。
このクラスはJUnitのTestCaseクラスでsuite()メソッドを定義しています。しかし、suite()メソッドは、
public static junit.framework.Test suite()あるいは
public static junit.framework.TestSuite suite()と宣言する必要があります。
このクラスはJUnitのTestCaseクラスですが、テストメソッドが実装されていません。
このクラスは、Junit の TestCase で、setUp() メソッドを実装しています。setUp() メソッドは、super.setUp() 呼び出さなければなりませんが、ここでは呼び出していません。
このクラスは、JUnit の TestCase で、suite() メソッドを宣言しています。suite メソッドはスタティックなメソッドとしなければなりませんが、ここではスタティックメソッドになっていません。
このクラスは Junit の TestCase で、tearDown() メソッドを宣言しています。tearDown() メソッドは、super.tearDown() を呼び出さなければなりませんが、ここでは呼び出していません。
コンテナへ自身のオブジェクトを格納しています。その結果 hashCode() メソッドの呼び出しでStackOverflowException がスローされてしまいます。
このループは例外をスローする以外に終了することができません。
このメソッドは無条件に自分自身を呼び出しています。これは無限再帰ループでありスタックオーバーフローを招くと考えられます。
このコードは2つの整数の平均値を計算するのに、除算あるいは符号付き右ビットシフトを使用し、得られた結果を配列の添字に使用しています。もしも平均値計算の元となる値が非常に大きい値だと、オーバーフローして負となり、負の数の平均値計算となるかもしれません。結果が負になることを想定していないのであれば、代わりに符号無し右ビットシフトを使用することを検討してください。つまり(low+high)/2の代わりに(low+high) >>> 1を使用します。
このバグは昔の二分探索、マージソートで多く見られます。Martin Buchholz がJDKの中に、このバグを発見、修正しています。またJoshua Blochがより一般化したバグパターンの話として紹介しています。
ここで使用されている x % 2 == 1 という式は奇数であるかどうかを判定していると思われますが、これは負の数に対しては正しく動作しないことに注意してください(例:(-5) % 2 == -1)。かわりに x & 1 == 1 か x % 2 != 0を使用することを検討してください。
このコードは整数の剰余結果に整数定数を掛け合わせています。演算子の優先順位を誤解していませんか? たとえば、i % 60 * 1000 は、i % (60 * 1000) ではなく (i % 60) * 1000 となります。
この内部クラスのメソッドは、エンクロージングクラスの private フィールドに読み書きするかメソッドを呼び出しています。これを実現するため、コンパイラはフィールドにアクセスするための特別なメソッドを生成するため、コードの実行効率が犠牲になります。アクセス制限を緩めれば、コンパイラは通常のフィールドアクセス用コードを生成できます。
IllegalMonitorStateExceptionは一般にはコーディングの問題(ロックを獲得せずにwait()、notify()を呼び出した場合)によって発生するもので、キャッチすべきではありません。
このコードは、負にならないことが確実な値を、負の定数と比較しています。
byteは符号付きであり取り得る値は、-128 to 127の範囲です。byteをこの範囲外の値と比較するのは、無意味で恐らく間違いです。byte型のbを符号無しの0..255に変換したいのであれば、0xff & bとします。
任意の整数値expに対し、式 exp % 1 は常に0を返します。(exp & 1) あるいは (exp % 2) の間違いではないですか?
この整数比較は常に同じ結果になります(例:x <= Integer.MAX_VALUE)。
このコードでは、ファイルを追加モードでオープンし、その結果を ObjectOutputStream でラップしています。しかし、このようなファイルへ格納する ObjectOutputStream に対しては、追加出力することができません。ObjectOutputStream に対して追加出力したい場合、ObjectOutputStream をオープンしたままに保つ必要があります。 ファイルを追加モードでオープンし、ObjectOutputStream での出力が有効となる唯一のシチュエーションは、オープンしようとしているファイルがランダムアクセスモードによるアクセス中で、追加を開始しようとする位置にシークされている場合です。
引数で渡された値が無視され、ここで内容が上書きされています。パラメータの内容を書き変えれば、呼び出し元に変更が伝わるとプログラマが誤解している可能性があります。
このフィールドにはnet.jcip.annotations.GuardedByアノテーションが付けられていますが、そのアクセスのされ方は、アノテーションが定めたルールを破っているようです。
このクラスのフィールドは、同期化の観点から、一貫したアクセスが行われていないようです。このバグレポートは、次のようなバグパターンを示します。
このパターンに当てはまる典型的な例は、スレッドセーフを意図しているのに、メソッドを同期化するのを忘れているケースです。
このディテクタには、判断を誤る要因が幾つかあります。たとえば、ディテクタはスレッドがロックを獲得しているかどうかを、コードを見て静的に判断する事はできません。たとえディテクタが、ロックされたアクセスと、そうでないアクセスを的確に判定できたとしても、依然としてコードが正しい可能性は残ります。
このクラスのフィールドは、同期化の観点から、一貫したアクセスが行われていないようです。このバグレポートは、次のようなバグパターンを示します。
このパターンに当てはまる典型的な例は、スレッドセーフを意図しているのに、メソッドを同期化するのを忘れているケースです。
ディテクタにこのコードの意図を伝えるため「非同期アクセス」のラベルをつける事ができます。
このディテクタには、判断を誤る要因がいくつかあります。たとえば、ディテクタはスレッドがロックを獲得しているかどうかを、コードを見て静的に判断する事は出来ません。たとえディテクタが、ロックされたアクセスと、そうでないアクセスを的確に判定できたとしても、依然としてコードが正しい可能性は残ります。
ここの記述は"IS2"バージョンのパターンディテクタを参考にしています。これは、ロック・非ロックアクセスの検出に関して"IS"バージョンよりも正確です。
staticメソッドしか提供していないクラスのインスタンスの生成が行なわれています。このインスタンス生成は無駄です。クラス名を使用して直接メソッドを呼び出してください。
このクラスはjava.util.Iteratorを実装していますが、next()メソッドがjava.util.NoSuchElementExceptionをスローできないようになっています。next()メソッドは、それ以上要素を返すことができない場合にNoSuchElementExceptionをスローしなければなりません。
このメソッドでは、Collection クラスの toArray() メソッドに長さ 0 の配列を引数に渡しています。
myCollection.toArray(new Foo[myCollection.size()])
とする方が、効率の良いやり方です。もしも引数のサイズが、Collectionの中身を格納するのに十分な大きさであれば、そのまま、その配列に格納されて返送されます。さもなければリフレクションによって、新たに配列を生成しなければなりません。
このコードは直列化できないオブジェクトをHttpSessionに格納しているようです。このセッションがコンテナによって非活性化されて移動されると、正しく動作しない可能性があります。
このクラスにはnet.jcip.annotations.Immutableアノテーションが付けられており、全てのフィールドはfinal宣言しなければなりません。
このメソッドは、java.util.concurrent.locks.Lockオブジェクトで同期化を行っています。lock()とunlock()を替わりに使用すべきです。
このメソッドは、volatile でないスタティックフィールドを、同期化せずに遅延初期化しています。コンパイラや、プロセッサは、命令の並べ替えを行うかもしれないので、もしもメソッドが複数のスレッドから同時に呼び出されるのであれば、別のスレッドがこのフィールドにアクセスした際に、中途半端に初期化されたインスタンスが見えてしまう危険があります。フィールドをvolatileにする事で、この問題を解決する事が出来ます。詳細は、Javaメモリモデルを参照してください。
このメソッドは、同期化されていないスタティックフィールドに対する遅延初期化を行っています。このフィールドがセットされたオブジェクトはすぐにアクセスされるようになります。つまり、フィールドの設定とともに直ちに他のスレッドからアクセスできるようになるということです。このメソッドにおけるさらなるフィールドアクセスがオブジェクトの初期化を行うためのものであったとしたら、初期化が完全に終了するまでの間、フォールドに対する他のスレッドからのアクセスを完全に防がなければ、マルチスレッディングに関する非常に重大なバグとなります。
このクラスは、親クラスが宣言しているフィールドと同名のフィールドを宣言しています。これは混乱を招きやすく、間違えて意図しないフィールドを更新したり参照したりするプログラミングミスにつながります。
このメソッドは、このクラスもしくは親クラスが持つフィールドと同名のローカル変数を宣言しています。 これにより、フィールドが初期化されなかったり、未初期化のフィールドの値を読みこんだりする恐れがあります。
このメソッドでは、同時に更新される可能性のあるフィールドに対して同期化によるガードを行おうとしています。しかし、フィールドに対するガードは、フィールドそのものではなくフィールドが参照するオブジェクトに対してロックを取得するものです。これは、あなたが求める相互排他を提供せず、参照されたオブジェクトのロックを他のスレッドが(他の目的で)取得してしまうことがあります。このパターンの事例は以下のようになります。
private Long myNtfSeqNbrCounter = new Long(0);
private Long getNotificationSequenceNumber() {
Long result = null;
synchronized(myNtfSeqNbrCounter) {
result = new Long(myNtfSeqNbrCounter.longValue() + 1);
myNtfSeqNbrCounter = new Long(result.longValue());
}
return result;
}
このメソッドは、変更可能なフィールドが参照しているオブジェクトを同期化しています。この場合、異なるスレッドが、別々のオブジェクトに対して同期を行ってしまう可能性があるため、このコードの意図はうまく実現されないと思われます。
このコードは、外部から変更可能なオブジェクトをstaticフィールドに格納しています。ミュータブルオブジェクトの意図しない変更が、セキュリティや重要な情報を危機に晒す可能性があるなら、違うやり方を検討してください。防御的コピーによって複製されたオブジェクトを格納するのは、一つの良いアイデアです。
変更可能なstaticフィールドは、悪意を持ったコードから書き換えられたり、誤って別のパッケージのクラスから書き換えられたりすることがあります。残念ながら、このようなフィールドを簡単に保護する事はできません。
public static なメソッドが、クラスの static フィールドに保持した配列を返しています。このメソッドを呼び出して得られた配列への参照を通じて、クラスの static フィールドを変更できてしまいます。修正方法の一つとして、防御的コピーを行って配列の複製を返すことが挙げられます。
変更可能なstaticフィールドは、悪意を持ったコードから書き換えられたり、誤って別のパッケージのクラスから書き換えられたりすることがあります。このような脆弱性を防ぐために、フィールドをパッケージプライベートとするか、final 宣言して(あるいは、この両方を行って)ください。
final 宣言された static フィールドが配列の参照を保持しています。このフィールドは悪意を持ったコードや誤った別のパッケージのクラスからアクセスされ、配列の中身を自由に書き換えられてしまいます。
final 宣言された static フィールドが Hashtable の参照を保持しています。このフィールドは悪意を持ったコードや誤った別のパッケージのクラスからアクセスされ、中身を自由に書き換えられてしまいます。
インターフェースに宣言された final static フィールドが、配列 や Hashtable のようなミュータブルオブジェクトを参照しています。このオブジェクトは、悪意を持ったコードから書き換えられたり、間違って別のパッケージから書き換えられたりしてしまいます。このフィールドをクラスに移してパッケージプライベートにすることで、この問題を解決してください。
変更可能な static フィールドは、悪意を持ったコードから書き換えられたり、間違って書き換えられたりしてしまう可能性があります。フィールドをパッケージプライベートにすることで、この脆弱性を回避することができます。
変更可能なstaticフィールドは、悪意を持ったコードから書き換えられたり、誤って別のパッケージのクラスから書き換えられたりすることがあります。フィールドを final とすることで、この脆弱性を回避することができます。
一般的に、Web サーバはサーブレットやJSPクラスのインスタンスを一つしか生成しません(つまり、これらのクラスをシングルトンとして扱います)。そして、複数の同時に発生するリクエストに対処するため、複数のスレッドがこれらのインスタンスのメソッドを実行します。そして、ミュータブルなインスタンスフィールドを持つことは、一般的にリソース競合を発生させます。
このクラスはServletクラスを継承しており、インスタンスフィールドを使用しています。サーブレットクラスのインスタンスは複数のリクエストに対して1つしか生成されません。このためインスタンスフィールドは複数のスレッドから同時にアクセスされることになるので、このクラスでのインスタンスフィールドの使用は控えるべきであり、また恐らく使い方が間違っています。ローカル変数のみを使用するように変更することを検討してください。
このクラスはStruts Actionクラスを継承しており、インスタンスフィールドを使用しています。Struts Actionクラスのインスタンスは複数のリクエストに対して1つしか生成されません。このためインスタンスフィールドは複数のスレッドから同時にアクセスされることになるので、このクラスでのインスタンスフィールドの使用は控えるべきであり、また恐らく使い方が間違っています。ローカル変数のみを使用するように変更することを検討してください。
このメソッドは、ロックを獲得せずにObject.notify()あるいはObject.notifyAll()を呼び出しています。ロックを獲得せずにnotify()やnotifyAll()を呼び出すと、IllegalMonitorStateExceptionが発生します。
このメソッドは、ロックを獲得せずにObject.wait()を呼び出しています。ロックを獲得せずにwait()を呼び出すと、IllegalMonitorStateExceptionが発生します。
このクラスは equal(Object) という名称のメソッドを宣言しています。このメソッドは、java.lang.Object クラスの equals(Object) を(そのように意図したのかもしれませんが)オーバーライドしません。
クラス名には名詞を採用し、構成単語の最初の文字を大文字として、残りを小文字とすべきです。クラス名は単純明解であるよう心がけてください。頭文字や略語の使用は避けてください(ただし一般に広く用いられているURLやHTMLといったものは構いません)。
このクラスはExceptionクラス継承していないのに、クラス名が"Exception"で終了しています。これは利用者を混乱へと導きます。
これらのメソッド名は、大文字小文字が違うだけです。
finalでないフィールド名は、最初の文字以外の構成単語の最初の文字を大文字として、残りを小文字とすべきです。
この識別子は、後のバージョンの Java においてキーワードとして予約されているものです。したがって、このコードを後のバージョンの Java でコンパイルするには、修正する必要があります。
この識別子は、後のバージョンの Java においてキーワードとして利用されています。このコードおよびこのAPIを参照するすべてのコードは、後のバージョンの Java でコンパイルするために修正する必要があります。
このクラスは hashcode() という名称のメソッドを宣言しています。このメソッドは、java.lang.Object クラスの hashCode() メソッドを(そのように意図したのかもしれませんが)オーバーライドしません。
このクラスは tostring() という名称のメソッドを宣言しています。このメソッドは、java.lang.Object クラスの toString() メソッドを(そのように意図したのかもしれませんが)オーバーライドしません。
このメソッドは宣言されたクラスと同じ名称になっています。これはコンストラクタとして宣言しようとしたものだと思われます。そうであれば、void 戻り値の宣言を取り除いてください。もしも間違ってメソッドとして宣言してしまい、後から間違いに気付いて正しいコンストラクタを宣言し、後方互換性のために残されたメソッドなのであれば、メソッドの方を deprecate 宣言にしてください。
メソッド名には動詞を採用し、メソッド名の最初の文字以外の構成単語の最初の文字を大文字として、残りを小文字とすべきです。
このクラスまたはインターフェースの名称は、パッケージ名が異なることを除いて継承または実装されたインターフェースの名称と同じです(たとえば、alpha.Foo が beta.Foo を継承しているような状況です)。
これは、参照関係を知るために import 文を見なければならなかったり、スーパークラスに存在するメソッドを誤ってオーバーライドしてしまったりと、非常に混乱を招いてしまいます。
このクラスの名称は、パッケージ名が異なることを除いてスーパークラスの名称と同じです(たとえば、alpha.Foo が beta.Foo を継承しているような状況です)。
これは、参照関係を知るために import 文を見なければならなかったり、スーパークラスに存在するメソッドを誤ってオーバーライドしてしまったりと、非常に混乱を招いてしまいます。
これらのメソッド名は、大文字小文字のみが異なる名称です。これらのメソッドは大文字小文字が同じだったとすると、片方のメソッドがもう一方のメソッドをオーバーライドしてしまうため、大変まぎらわしいものとなります。
これらのメソッドは、大文字小文字のみが異なる名称です。これらのメソッドは大文字小文字が一致していれば、一方が他方をオーバーライドしていたはずで、非常にまぎらわしい命名になっています。他のメソッドの存在から、これらのメソッドの存在は意図的なものと見うけられますが、混乱を招くことには変わりありません。もしもAPIの固定化によってメソッド名が変更できないのであれば別ですが、そうでないのであれば、どちらかを削除すべきです。
スーパークラス側メソッドの引数型と、サブクラス側メソッドの引数型が正しく一致していないために、サブクラスのメソッドがスーパークラスにある似たようなメソッドをオーバーライドしていません。たとえば、以下のような状況です。
import alpha.Foo;
public class A {
public int f(Foo x) { return 17; }
}
----
import beta.Foo;
public class B extends A {
public int f(Foo x) { return 42; }
}
B クラスで宣言される f(Foo) メソッドは、A クラスの f(Foo) メソッドをオーバーライドしていません。これは、引数である Foo が異なるパッケージであるためです。
スーパークラス側メソッドの引数型と、サブクラス側メソッドの引数型が正しく一致していないために、サブクラスのメソッドがスーパークラスにある似たようなメソッドをオーバーライドしていません。たとえば、以下のような状況です。
import alpha.Foo;
public class A {
public int f(Foo x) { return 17; }
}
----
import beta.Foo;
public class B extends A {
public int f(Foo x) { return 42; }
public int f(alpha.Foo x) { return 27; }
}
B クラスで宣言される f(Foo) メソッドは、A クラスの f(Foo) メソッドをオーバーライドしていません。これは、引数である Foo が異なるパッケージであるためです。
このケースでは、サブクラスがスーパークラスとおなじシグネチャをもつメソッドを宣言しているため、理解することは可能ですが、非常に混乱を招きます。シグネチャが異なっていながら似ているメソッドを削除するか deprecated にすることを強く検討すべきです。
オブジェクトの状態を変更せずにnotify()もしくはnotifyAll()メソッドを呼び出しています。一般にはこれらのメソッドは、別のスレッドが待ち合わせている条件が成立したことを通知するために用いられます。条件の待ち合わせを行うためには、両方のスレッドから参照可能なヒープオブジェクトを使用しなければなりません。
このバグ報告は、必ずしもプログラミングエラーを意味しません。オブジェクトの状態変更がメソッド内部で行われ、そこから更に notify() / notifyall() メソッドを持ったメソッドを呼び出しているかもしれないからです。
このメソッドはnotifyAll() メソッドではなく notify() メソッドを呼び出しています。Javaのモニタは、しばしばいくつかの条件を同時に待ち合わせるために使用されますが、notify() メソッドを使って待機スレッドを起こすと、別の条件を待っているスレッドを起こすだけになるかもしれません。
null に設定された参照に対するアクセスが行われています。コードが実行されればNullPointerExceptionが発生するでしょう。
例外処理の中で null ポインタに対するアクセスが行われています。コードが実行されれば、NullPointerException が発生するでしょう。現在の FindBugs は、実行され得ない例外処理経路を考慮しません。このため、この警告は誤って報告されるかもしれません。
また、FindBugs は switch-case の default も例外経路と見なしますが、default は実行され得ない場合がしばしばありますので、注意してください。
このメソッドの引数は、nullチェックが必要と認識されていますが、チェックを行わずに引数が利用されています。
Boolean.TRUE、Boolean.FALSE、null のいずれかを返すメソッドはトラブルの元となります。 このメソッドは、boolean 型を返すメソッドとしても呼び出されることがあります。この場合、コンパイラは Boolean 型に対してオートアンボクシングを行います。もしも戻り値が null である場合、NullPointerException が発生してしまいます。
このclone() メソッドは、条件によっては null を返す場合があるようです。しかし clone() メソッドはnullを返してはいけません。もしもこのような条件が発生し得ないと確信できるのであれば、AssertionError をスローすることを検討してください。
null チェックが行われずに readLine() メソッドの結果を参照しています。読み出すべきテキストがこれ以上存在しない場合、readLine() メソッドは null を返すため、これを参照すると NullPointerException が発生してしまいます。
この equals(Object) メソッドは引数にnullが渡された場合のチェックが行なわれていないため、java.lang.Object.equals() で定義された規約を満たしていません。すべての equals() メソッドは引数に null が渡された場合に false を返さなければなりません。
この equals(Object) メソッドは引数にnullが渡された場合のチェックが行なわれていないため、java.lang.Object.equals() で定義された規約を満たしていません。すべての equals() メソッドは引数に null が渡された場合に false を返さなければなりません。
この文または分岐が実行されると、この値は必ず null となります。そしてこの値はその後必ず利用されています(RuntimeExceptionのスローを含むフォワードパスを通る場合を除きます)。
例外経路で、この文または分岐が実行されると、この値は必ず null となります。そしてこの値は必ず利用されています(RuntimeExceptionのスローを含むフォワードパスを通る場合を除きます)。
readLine() の結果をそのまま利用しています。readLine() は読み込むべきデータが無くなると null を返すので、そのまま利用すると NullPointerException をスローします。
参照されている変数は、すでに null であることのチェックが行なわれており、この地点ではnullであることが分かっています。文法としては正しいのですが、これはプログラミング上の誤りかもしれません(おそらく違う変数を参照したかったか、前のチェックは null であることではなく非 null であることを意図していたのではないかと思われます)。
このメソッドではnullをメソッドに渡しています。しかし、この引数は @NonNull と宣言されているか、解析の結果から常に利用されると判定されたため、null が許されません。
null を返す可能性があるにもかかわらず、このメソッド(あるいは、親クラスの該当メソッド)は @NonNull 宣言されています。
この instanceof は常にfalseを返します。なぜならこの値は null であることが明らかだからです。このコードは安全ではありますが、なんらかの論理の間違い、誤解を示唆しています。
この参照変数を通したアクセスは、実行時に参照変数が null となる可能性があります。これは、実行時にNullPointerExceptionを発生させる可能性があります。
幾つかの例外処理の中で、null となる参照を通してアクセスが行われています。これは、実行時にNullPointerException を発生させる可能性があります。現在の FindBugs は、実行され得ない例外処理系を考慮しません。このため、この警告は誤って報告されるかもしれません。
また、FindBugs は switch-case の default も例外処理見なしますが、しばしば default は実行され得ない場合がありますので、注意してください。
いくつかの例外パスの中で null となる参照が、ここで利用されています。これは実行時にNullPointerException を招きます。この値は null を返す可能性のあるメソッドの戻り値を使用しているため、null となるかもしれません。
このコード分岐は、もし実行されればNullPointerException の発生要因となる null 値への参照を含んでいます。 もちろん、問題はこのコード分岐が実行できず、NullPointerException が発生し得ないということかもしれません。しかし、 FindBugs の能力では、その判断をすることはできません。 この値が null とはならないというテストが既に行われているならば、これは明確なる可能性にすぎません。
メソッドに null を渡していますが、この引数はメソッドの中で無条件に利用されるようです。
引数を無条件に利用するメソッドに null となりうる値を渡しています。これは NullPointerException の発生を招く可能性があります。
引数を無条件に利用するメソッドに null となりうる値を渡しています。これは NullPointerException の発生を招きます。
このパラメータは、常に null ではないことを前提として利用されています。しかし、これに反して Nullable としてアノテートされています。パラメータの使い方かアノテーションのどちらかが誤っています。
NonNullアノテーションが付けられたフィールドに、nullになるかもしれない値が代入されています。
このフィールドは同期の対象となっているため、null となるようには見えません。 もしこれが null で同期の対象となった場合は NullPointerException がスローされ、チェックは無意味なものとなります。別なフィールドによって同期を行う方がよいでしょう。
この toString メソッドは、ある条件下で、null を返すようです。寛大に評価すれば、これは仕様を満たしていると言えますが、おそらくはこのやり方は間違いで、他のコードが正しく動作しなくなるでしょう。null の替わりに空文字列かその他の適切な文字列を返すようにしてください。
このプログラムの中で、まだ書き込まれていないと考えられるフィールドの読み出しを行なっています。これは NullPointerException のスローを招きます。
このコードは、短絡論理(&& や ||)の代わりに非短絡論理(& や |)を使用しています。更に、左側の値によっては右側の評価を(例外のスローや高コストの演算といった副作用があるために)避けたいケースがあるようです。
非短絡論理では、たとえ左側だけで結果が決定してしまう場合であっても、両側の式が評価されます。これは効率が悪く、左側がガード条件になっている場合には、右側の評価でエラーが発生するかもしれません。
詳細はJava言語仕様を参照してください。
非短絡的演算子(&あるいは|)を、短絡的演算子(&&あるいは||)と間違えて使用しているようです。非短絡的演算子は、両側の式を必ず評価します。非短絡的演算子を短絡的演算子のかわりに使うと、効率が悪いだけでなく、もしも演算子の左側の式が右側の式にアクセスするためのガード条件になっている場合には、エラーになります。
詳細については、Java言語仕様を参照してください。
このメソッドは、ストリームやデータベースオブジェクト、その他明示的なクリーンナップ(クローズやディスポーズ)操作を必要とするリソースのクリーンナップに失敗します。
一般的に、あるメソッドがストリープやリソースをオープンしたら、そのメソッドが終了する前に確実にストリームやリソースがクリーンナップされるよう、try/catchブロックを使用すべきです。
このバグ・パターンは、本質的に OS_OPEN_STREAM や ODR_OPEN_DATABASE_RESOURCE と同じですが、異なる(願わくばより良い)静的解析手法に基づいています。私たちはこのバグ・パターンの実用性に関するフィードバックを待っています。フィードバックを送信するには、以下のどちらかを利用してください。
とりわけ、このバグ・パターンの誤検知抑制アルゴリズムがまだ十分に調整されていないため、誤検知に関するレポートは非常に助かります。
この解析手法の詳細については、Weimer と Necula による Finding and Preventing Run-Time Error Handling Mistakes を参照してください。
このメソッドは、データベースリソース(例えば、コネクションや結果セット)を生成しますが、それをフィールドに代入したり、他のメソッドに渡したり、戻り値として返送したりしておらず、このメソッドを起点とする実行パスの中にクローズを行わないパスがあります。データベースリソースの解放忘れは、パフォーマンスの悪化を招いたり、アプリケーションがデータベースと通信できなくなる恐れがあります。
このメソッドは、データベースリソース(例えば、コネクションや結果セット)を生成しますが、それをフィールドに代入したり、他のメソッドに渡したり、戻り値として返送したりしておらず、このメソッドを起点とする例外処理パスの中でクローズを行わないパスがあります。データベースリソースの解放忘れは、パフォーマンスの悪化を招いたり、アプリケーションがデータベースと通信できなくなる恐れがあります。
このメソッドは入出力ストリームを生成していますが、そのインスタンスはフィールドに保持されず、close() が実行されるかもしれない別のメソッドに引き渡してもおらず、あるいはそのまま戻り値として返送されてもいません。そして、このメソッドを起点とする実行パス上では、ストリームのクローズ処理が行われないパスがあります。これは入出力ディスクリプタのリークにつながる恐れがあります。一般にはfinally ブロックを使ってリソースをクローズするのが良いやり方です。
このメソッドで入出力ストリームオブジェクトが生成されていますが、フィールドへの格納も他のメソッドへの引き渡しも、戻り値としての返送も行なわれていません。そして、このメソッドを起点とする例外パスの中にクローズが行なわれないパスがあります。これはファイルディスクリプタのリークを招きます。確実にクローズが行なわれるよう finally ブロックを使用するのが良い考えです。
このクラスは、自身のオブジェクト(this)に対する wait()、notify()、notifyAll() 呼び出しによって、同期化を行なっています。利用者は、自分が作成したクラスの中でさらにこのクラスのインスタンスを同期化のために使用してしまうかもしれません。これにより、2つのクラスが同じオブジェクトを使って同期化を行なってしまうので、マルチスレッド処理が正しく行なわれなくなる可能性があります。外部からアクセスできるオブジェクトを使って同期化、セマフォメソッド呼び出しを行なうべきではありません。private メンバフィールドを利用して同期化を行なうことを検討してください。
結果が空を表すためには、多くの場合、null を返すよりもサイズ 0 の配列を返す方が良い設計です。これにより、利用者側のコードで null との明示的な比較を行わなくてもすむようになります。
これに対して、「返すべき値が無いことを示す」たねに null を用いるのは、恐らく適切です。例えば、File.listFiles()は、ディレクトリにファイルが無い時はサイズ 0 の配列を、指定されたパスがディレクトリでなければ null を返します。
このメソッドでは、if もしくは while の条件式の中で boolean 直定数(true あるいは false)を boolean 変数に代入しています。恐らく == でbooleanの比較すべきところを、 = と間違えてしまったものと思われます。
このforループでのインクリメントは正しい変数に対して行なわれていますか? 別の変数が初期化され、チェックされているようです。
このメソッドでは、2つの参照が == あるいは != 演算子で比較されています。しかし、一般には equals() メソッドを使う方が正しいやり方です。java.lang.Integer や java.lang.Float などがこのようなクラスの例で、参照の比較を使うべきではありません。
このメソッドで非nullと分かっている参照をnullと分かっている参照と比較しています。
このメソッドでnullであることが明らかな2つの参照を無駄に比較しています。
このメソッドは null でないことが分かっている参照を無駄に null チェックしています。
このメソッドは null であることが分かっている参照を無駄に null チェックしています。
ここでは、参照が null チェックされていますが、この参照が null になることはあり得ません。この参照は以前に利用されているためです。もしも null なら前の段階で NullPointerException が発生しているはずです。そもそも、ここのコードと前の参照を利用してるコードとは、参照が null であるかどうかという点で矛盾しているのです。チェック自体が余計か、前の参照利用が間違っています。
このコードで使用されている正規表現は文法が間違っています。この文はPatternSyntaxExceptionのスローを招くでしょう。
正規表現が必要とされる場所で、File.separatorが使用されています。これはWindowsプラットフォームでは正しく動作しません。File.separatorがバックスラッシュとなるためです。これは正規表現のエスケープキャラクタです。回避方法として、File.separatorChar=='\\' & "\\\\" : File.separatorを、File.separatorの替わりに使用する方法があります。
Stringの正規表現を受け取るメソッドに"."が渡されています。これは意図通りですか?
例えば、s.replaceAll(".", "/")は、全ての文字を"/"に置き換えた文字列を返します。
この try-catch ブロックは try 節の中で例外をスローしないのに例外のキャッチ節があり、RuntimException のキャッチ節がありません。これは同じ内容のキャッチ節を書くのが面倒でtry { ... } catch (Exception e) { something }と記述してしまい、間違ってRuntimeExceptionまでキャッチしてしまうというバグパターンです。
このクラスで実装を宣言しているインターフェースは、親クラスでも実装が宣言されています。一度実装が宣言されたインターフェースは、全サブクラスで自動的に実装されることになるため、この記述は冗長です。このクラスが作成された後に継承関係が変更された可能性があります。実装するべきインターフェースについて再度検討する必要があります。
このコードに含まれる条件判定は2回、すなわち最初の式の判定ののち、ただちに次の判定が実行されます。
たとえば、x == 0 || x == 0 のような例ですが、これは恐らく x == 0 || y == 0 のように、2つ目の式が他の何かを意図したものだったのではないでしょうか。
このメソッドは、java.io.InputStream.read() の戻り値を無視しています。戻り値を無視すると、実際に何バイトのデータが読み込まれたのかわかりません。一般には、指定された長さ分完全に読み込んでしまうケースが多いために問題が顕在化せず、ごくたまに発生するやっかいなバグとなります。
このメソッドは、データの読み込みをスキップするメソッドjava.io.InputStream.skip() の戻り値を無視しています。
戻り値をチェックしないと、実際には呼び出し元が要求したバイト数よりも少ないバイト数しかスキップしなかった場合に、何バイトスキップしたのか分らなくなります。
これは潜在的なバグとなります。なぜなら、大抵の場合、要求通りのスキップが行なわれるので、まれにしか現象が起きないからです。
これに対し、バッファストリームの場合、skip()はバッファ内部のデータをスキップするだけなので、頻繁に現象が起きることになります。
この直列化可能クラスは同期化された readObject() を定義していますが、そもそもオブジェクトが直列化復元される場合、ただ1つのスレッドによって行われる事になっています。このため、readObject() を同期化する必要はありません。もしも readObject() メソッド自体が、他のオブジェクトへの他のスレッドからのアクセスを引き起こしているなら、それは非常に疑わしいコーディングです。
このメソッドは、オブジェクトのrun()メソッドを明示的に呼び出しています。一般にRunnableを実装したクラスは、新しいスレッドがrun()メソッドを呼び出す事を期待しており、この場合、Thread.start()を呼び出すのが正しいやり方です。
0から1の乱数値が生成されていますが、整数0に丸められます。恐らく丸めの前に何らかの値を掛けたかった、あるいはRandom.nextInt(n)メソッドを使用したかったのではありませんか?
このコードは符号付き整数のハッシュコードを得た後、絶対値を計算しています。もしもハッシュコードがInteger.MIN_VALUEだと、絶対値は負になります(なぜならMath.abs(Integer.MIN_VALUE) == Integer.MIN_VALUEだからです)。
このコードは符号付き整数の乱数を得た後、絶対値を計算しています。もしも生成された乱数がInteger.MIN_VALUEだと、絶対値は負になります(なぜならMath.abs(Integer.MIN_VALUE) == Integer.MIN_VALUEだからです)。
このメソッドは String.indexOf() メソッドを呼び出して、結果が正であるかどうかを調べています。通常は結果が負であるかどうかをチェックするはずです。正であることでチェックしているため、部分文字列が先頭に見つかったケースを見逃がしてしまします。
readLine() の結果が null でないことをチェックした後に単に破棄されています。ほとんどの場合、null チェックを行った後には、その値を使用するはずです。もう一度 readLine() を呼び出すと次の行が返るので、元の行の内容は取り出せません。
このコードでは、例外(またはエラー)オブジェクトを生成していますが、それに対して何の処理も行っていません。たとえば、以下のようなコードです。
if (x < 0)
new IllegalArgumentException("x must be nonnegative");
プログラマの意図は、おそらく生成した例外をスローしたかったのでしょう。
if (x < 0)
throw new IllegalArgumentException("x must be nonnegative");
このコードはハッシュコードを計算した後、剰余を計算しています。ハッシュコードは負になる場合があるため、この結果は負になるかもしれません。
計算結果が負にならないと思っているのであれば、コードを修正する必要があります。序数が2のn乗であることが明らかならば、かわりにビットANDを使用することができます(x.hashCode()%nの代わりにx.hashCode()&(n-1)を使用します)。これは恐らく剰余計算よりも高速です。それ以外のケースでは剰余に対して絶対値を算出してください(Math.abs(x.hashCode()%n)。
このコードは符号付き整数の乱数を生成し、他の値で割って剰余を求めています。乱数は負になるかもしれないので、この結果は負になるかもしれません。これが想定された動作であることを確認し、替りにRandom.nextInt(int)を使用する事を検討してください。
このメソッドの戻り値をチェックするべきです。このようなコードを書いてしまった原因の1つとして、イミュータブルなオブジェクトのメソッド呼び出しが、そのオブジェクトの状態を変化させると誤解しているケースが考えられます。例えば、
String dateString = getHeaderField(name); dateString.trim();
プログラマは trim() メソッドが dateString によって参照される Strig オブジェクトを更新すると考えているようです。しかし、String オブジェクトはイミュータブルであるため、 trim() メソッドは新しい String オブジェクトを返し、ここではそれが無視されてしまっているのです。このコードは、以下のように修正しなければなりません。
String dateString = getHeaderField(name); dateString = dateString.trim();
このメソッドはチェックされない戻り値を返しています。戻り値は機能の非正常または予測できない実行結果を表すこともあるため、確認すべきです。たとえば、File.delete() メソッドはファイルを正しく削除できなかった場合に(例外をスローするのではなく) false を返します。
もし、この結果をチェックしなかったならば、戻り値が非定型値を示すことで予期しない振る舞いをすることに気づかないでしょう。
このメソッドの戻り値を無視すべきではありません。この警告が出力される原因として、イミュータブルなオブジェクトのメソッドを呼び出して、それによりオブジェクトの状態が変化すると考えているケースが挙げられます。たとえば、以下のようなコードです。
String dateString = getHeaderField(name); dateString.trim();
このプログラマは、trim() メソッドが dateString が参照している String の状態を変えると考えているように思われます。しかし String はイミュータブルであり、trim() メソッドは新しいStringオブジェクトを返すので、これでは戻されたオブジェクトが捨てられてしまいます。このコードは、以下のように修正すべきです。
String dateString = getHeaderField(name); dateString = dateString.trim();
このメソッドでフィールドへの二重代入が行われています。
int x,y;
public void foo() {
x = x = 17;
}
フィールドに2回代入するのは無意味です。恐らく論理の誤りかタイプミスでしょう。
このメソッドは、フィールドの自己代入があります。例えば、以下のようなコードです。
int x;
public void foo() {
x = x;
}
この例のような代入は無意味です。ロジックの誤りや、タイプミスかもしれません。
メソッド内でフィールドをそれ自身と比較しています。これはタイプミスか論理の誤りと思われます。比較対象が正しいかどうか確認してください。
メソッド内でフィールドと、同じフィールド参照との間で演算を行っています(例:x&x or x-x)。この演算の特性上、意味があるとは思えず、タイプミスあるいは論理の誤りと考えられます。演算内容について、もう一度確認してください。
このメソッドでローカル変数への二重代入が行われています。
public void foo() {
int x,y;
x = x = 17;
}
変数に2回代入するのは無意味です。恐らく論理の誤りかタイプミスでしょう。
このメソッドは、ローカル変数の自己代入があります。例えば、以下のようなコードです。
public void foo() {
int x = 3;
x = x;
}
この例のような代入は、無意味です。ロジックの誤りや、タイプミスかもしれません。
メソッド内で変数をそれ自身と比較しています。これはタイプミスか論理の誤りと思われます。比較対象が正しいかどうか確認してください。
メソッド内で変数と、同じ変数参照との間で演算を行っています(例:x&x or x-x)。この演算の特性上、意味があるとは思えず、タイプミスあるいは論理の誤りと考えられます。演算内容について、もう一度確認してください。
このメソッドの中で、ループ内でStringの連結を繰り返しているようです。このためStringBuffer/StringBuilderへの変換、連結、そしてStringへの再変換という処理が何度も繰り返されます。文字列が毎回コピーし直されて長くなっていくため、繰り返し回数の二乗の処理コストが必要となる場合があります。
明示的にStringBufferあるいはStringBuilder(J2SE 1.5から導入されます)を用いる事で、パフォーマンスを改善する事が出来ます。
例)
// 悪い例
String s = "";
for (int i = 0; i < field.length; ++i) {
s = s + field[i];
}
// 良い例
StringBuffer buf = new StringBuffer();
for (int i = 0; i < field.length; ++i) {
buf.append(field[i]);
}
String s = buf.toString();
コンストラクタがスレッドを開始しています。継承が可能なクラスでは、この設計は間違いと思われます。なぜなら、サブクラスのコンストラクタが実行される前に、スレッドが開始してしまうためです。
この直列化可能クラスは、transientでもなく、直列化可能でもなく、java.lang.Objectでもないインスタンスフィールドを持っています。また、Externalizable インターフェースも実装していませんし、readObject() も writeObject() メソッドも定義していません。このフィールドに実際に非直列化可能クラスのインスタンスを保持している場合、このクラスのオブジェクトは、直列化復元を正しく行えません。
この直列化可能クラスは、直列化可能でないクラスのインナークラスとなっています。これを直列化すると、一緒にouterクラスのインスタンスも直列化しようとして、実行時にエラーとなるでしょう。
可能であれば、インナークラスをstaticとしてください。outerクラスを直列化可能とすることでも問題を解決できますが、この場合インナークラスの直列化にあたってouterクラスも直列化されることに注意してください。おそらくこれはプログラマの意図とは異なるでしょう。
直列化できない値が、直列化可能と宣言されたクラスの transient でないフィールドに格納されています。
このクラスは Comparator インターフェースを実装していますが、Serializable インターフェースも実装すべきかどうか検討すべきです。もしもTreeMap のような順序付きコレクションでこのコンパレータを使用すると、コンパレータが直列化可能である場合にのみ、TreeMap は直列化されます。大抵のコンパレータはまったく状態を持っていないか、持っていたとしてもわずかなので、直列化可能とするのは簡単であり、良い防御的プログラミングであると言えます。
この直列化可能なクラスはインナークラスです。このクラスのインスタンスを直列化しようとすると、関連する outer クラスのインスタンスも一緒に直列化されます。outer クラスが直列化可能なので、問題はありませんが、予想外に大量のデータが直列化される可能性があります。可能であれば、インナークラスをstatic としてください。
このクラスは Serializable インターフェースを実装しており、直列化、非直列化を行うためのメソッドを宣言しています。しかしメソッドが private 宣言されていないので無視されてしまいます。
このクラスは Serializable インターフェースを実装していますが、親クラスは実装していません。このクラスのオブジェクトを非直列化する場合、親クラスのフィールドの初期化は、親クラスの引数無しコンストラクタで行う必要があります。ところが、親クラスが引数無しコンストラクタを持たないので、直列化、非直列化処理は実行時に失敗します。
このクラスは Externalizable インターフェースを実装していますが、引数無しのコンストラクタを定義していません。Externalizable オブジェクトが非直列化される際、まず引数無しコンストラクタを呼んでインスタンスを生成する必要があります。このクラスには引数無しコンストラクタがないため、実行時に直列化、非直列化処理に失敗します。
このクラスは serialVersionUID フィールドを定義しますが、finalではありません。直列化のバージョンUIDのために作成したのであれば、このフィールドは、finalとすべきです。
このクラスは serialVersionUID フィールドを定義していますがlongではありません。直列化のバージョンUIDのために作成したのであれば、このフィールドは、longとすべきです。
このクラスは serialVersionUID フィールドを定義していますが、staticではありません。直列化のバージョンUIDのために作成したのであれば、このフィールドは、staticとすべきです。
このクラスはプライベートな readResolve() メソッドを宣言しています。そのため、このメソッドはサブクラスで継承できません。 これが意図したものであるならば問題ありませんが、何を意図したものか確認するために、レビューすべきでしょう。
シリアライゼーション機構が認識するためには、readResolve() メソッドはスタティックメソッドして宣言してはいけません。
直列化機構から正しく認識されるためには、readResolveメソッドの戻り値の型はObjectでなければなりません。
このクラスには、クラス内の様々な場所で更新されるフィールドを持っており、このフィールドは、このクラスの状態の一部と考えられます。しかしこのフィールドは transient と宣言されているのに、 readObject/readResolve メソッドで値が設定されません。このためこのクラスのすべてのインスタンスで、このフィールドはデフォルト値を開始値として持つことになります。
transientフィールドを持つクラスが直列化可能になっていません。このためtransientと宣言しても何の効果もありません。これは、直列化可能クラスを修正する段階で間違えたか、直列化機構に対して誤解していることが原因と思われます。
直前のcaseで値が格納されていますが、switchのフォールスルーによって、単に上書きされます。恐らくbreak、returnを直前のcaseに入れ忘れたものと思われます。
このメソッド内のswitch文の中には、breakしていないcaseがあります。この場合、制御は次のcaseにフォールスルーします。大抵の場合caseはbreakかreturnで終了する必要があります。
すべてのstatic finalフィールドの値が代入される前に、クラスのスタティックイニシャライザがインスタンス生成を行っています。
このクラスは非 static 内部クラスですが、インスタンス生成時に保持される、生成元のインスタンスへの参照を使用していません。この参照はインスタンスの大きさを増大させ、不必要に生成元への参照を長時間保持することにつながります。可能であれば、static内部クラスとすべきです。
このクラスは非 static 内部クラスですが、インスタンス生成時に保持される、生成元のインスタンスへの参照を使用していません。 この参照はインスタンスの大きさを増大させ、不必要に生成元への参照を長時間保持することにつながります。可能であれば、static内部クラスとすべきです。匿名内部クラスは static にできません。そのため、これを実現するには名前を持った static 内部クラスに変更するように、リファクタリングする必要があります。
このクラスは非static内部クラスですが、インスタンス生成時に保持される、生成元のインスタンスへの参照を使用していません。 この参照はインスタンスの大きさを増大させ、不必要に生成元への参照を長時間保持することにつながります。可能であれば、static内部クラスとすべきです。非static内部クラスの生成時には、エンクロージングクラスのインスタンスが必要となります。このため、内部クラスのコンストラクタに、エンクロージングクラスのインスタンスを渡すようにリファクタリングする必要があるかもしれません。
静的に型チェックが可能なのにinstanceof演算子を使用して型チェックを行なっています。
このクラスは大きすぎて有効に取り扱うことができません。また、エラーが発生したために完全に解析されませんでした。
このクラスは Serializable インターフェースを実装しています。しかし、serialVersionUID フィールドを定義していません。クラス参照を追加する程度の簡単な変更でも、合成フィールドを追加することになり、これは、暗黙的に生成される serialVersionUID の値を変えてしまいます(例えば、String.classへの参照を追加すると、class$java$lang$Stringというスタティックフィールドが生成されます)。また、複数のJavaコンパイラの間では、このクラス参照、インナークラス参照に対して生成される、合成フィールドの命名規則が異なる場合があります。異なるバージョン間での相互運用性を保証するため、serialVersionUID を明示的に定義することを検討してください。
このメソッドは、フィールドの値をループの終了条件としていますが、仕様上、コンパイラはフィールドの読み出しをループの外に出しても良い事になっています。このためループは無限ループとなる可能性があります。フィールド読みだしを正しく同期化(wait/notify)する事が必要です。
PreparedStatement の setXXX メソッドで、添字に0を指定しています。添字は 1 から開始するので、これは常に間違いです。
ResultSetの getXXX、updateXXX メソッド呼び出しで添字に 0 を指定しています。ResultSetの添字は 1 から開始するので、これは常に間違いです。
このメソッドは SQL の Statement の execute メソッドに動的に生成された文字列を渡しているようです。替りに PreparedStatement の使用を検討してください。この方が効率的で、SQLインジェクションによる攻撃に対してより安全です。
このコードは SQL の PreparedStatement を定数でない String から生成しています。内容をチェックしないと汚染されたユーザ入力が文字列に含まれてしまい、SQL インジェクションによって予期しない、望ましくない動作を招くかもしれません。
このクラスは、staticでないfinalフィールドがあり、コンパイル時に決定される、静的な値に初期化されています。このフィールドをstaticとすることを検討してください。
この非スタティックメソッドはスタティックフィールドに書き込みを行なっています。これは複数のインスタンスが同時に操作された時に正しく動作させるのが困難で、良くないやり方です。
JavaDoc には記述されていませんが、Calendar クラスはマルチスレッド環境において本質的にアンセーフです。 スタティックフィールドから取得された Calendar インスタンスに対するメソッドコールを検出しました。これはスレッドアンセーフであることを疑うべきコードです。 これに関するさらなる情報については、Sun Bug #6231579 や Sun Bug #6178997 をご覧ください。
JavaDoc に記述されているように、DateFormat クラスはマルチスレッド環境において本質的にアンセーフです。 スタティックフィールドから取得された DateFormat インスタンスに対するメソッドコールを検出しました。これはスレッドアンセーフであることを疑うべきコードです。 これに関するさらなる情報については、Sun Bug #6231579 や Sun Bug #6178997 をご覧ください。
JavaDoc には記述されていませんが、Calendar クラスはマルチスレッド環境において本質的にアンセーフです。 適切な排他を行わずに、1つのインスタンスをスレッドをまたがって共有すること、アプリケーションの動作が不安定になってしまいます。このような状況では、Java5 以降の環境よりも、JDK 1.4 以前の環境の方が、sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate() において ArrayIndexOutOfBoundsException や IndexOutOfBoundsExceptions がしばしばランダムに発生します。 また、シリアライゼーションに関する問題も発生するでしょう。インスタンスフィールドを利用することをお勧めします。 より詳細な情報については、Sun Bug #6231579 や Sun Bug #6178997 を参照してください。
JavaDoc に記述されているように、DateFormat クラスはマルチスレッド環境において本質的にアンセーフです。 適切な排他を行わずに、1つのインスタンスをスレッドをまたがって共有すると、アプリケーションの動作が不安定になってしまいます。このような状況では、シリアライゼーションに関する問題も発生しますので、インスタンスフィールドを利用することをお勧めします。 より詳細な情報については、Sun Bug #6231579 や Sun Bug #6178997 を参照してください。
このメソッドは、interrupted() メソッドを呼び出すためだけに Thread.currentThread() を呼び出しています。interrupted() メソッドは static メソッドなので、単に Thread.interrupted() とする方が単純明解です。
このメソッドは Thread.interrupted() が、自スレッドではないと思われるスレッドに対して呼び出されています。この interrupted() メソッドはスタティックメソッドであり、常に自スレッドに対して呼び出されるため、コード作成者の意図とは異なるスレッドに対して呼び出されることになります。
このメソッドはロックを保持したまま Thread.sleep() を呼び出しています。これはパフォーマンスとスケーラビリティを極度に悪化させ、あるいはデッドロックを引き起こします。なぜなら、他の複数のスレッドがロックの獲得のために待っているかもしれないからです。これよりも、wait() を呼び出してロックを待ち合わせる方が良い方法です。これによってロックが開放され、他のスレッドが実行できるようになります。
警告が見つかりましたが、FindBugs はこのバグパターンに対応する詳細説明を見つけることができませんでした。これは FindBugs 自身のバグか設定ミス、またはプラグインを利用している場合、プラグインが正しくロードされていない可能性があります。
2つ以上のロックを獲得した状態でモニタの上で待ち合わせを行うと、デッドロックする場合があります。wait()メソッドは、呼び出されたオブジェクトのロックのみを解放するだけで、その他のロックは解放しません。これは必ずしもバグとは限りませんが、詳しく検討すべきです。
アノテーションによってアノテートされた値が、アノテーションを利用すべきでない場所で利用されています。 より具体的な例としては、when=ALWAYS というパラメータを持つアノテーションでアノテートされた値は、when=NEVER というパラメータを持つアノテーションの影響範囲と重複してしまっています。 たとえば、@NonNegative というアノテーションが @Negative(when=When.NEVER) というアノテーションのエイリアスだとしましょう。以下のようなコードでは、return 文が @NonNegative アノテートされているにもかかわらず、引数は @Negative アノテートされてしまっています。
この値は常に型限定子が指定されているものとして利用されています。しかし、どこでこの値に型限定子が必要であるのかが不明であることを示す明示的なアノテーションが存在します。 使い方かアノテーションのどちらかが誤っています。
この値は常に型限定子が指定されていないものとして利用されています。しかし、この値は型限定子の利用禁止がどこで行われているのかわからないことを示す明示的なアノテーションが存在します。 使い方かアノテーションのどちらかが誤っています。
アノテーションを持たない値が、アノテーションを必要とする部分で利用されています。 たとえば、以下のようなメソッドを考えてください。
public @Untainted Object mustReturnUntainted(Object unknown) {
return unknown;
}
mustReturnUntainted メソッドは、@Untainted アノテートされた値を返さなければなりません。しかし、そのアノテーションを持つ値は返されていません。
何らかのアノテーションを保持する値が、そのアノテーションを利用しない場所で利用されています。
アノテーションを持たない値が、アノテーションを持つ値を必要とする箇所で利用されようとしています。 より具体的な例としては、when=NEVER というパラメータのアノテーションを保持する値が、when=ALWAYS というパラメータのアノテーションが必要とされる箇所で利用されています。
このメソッドには、無駄なフロー制御ステートメントがあります。分岐を通ったかどうかにかかわらず、制御の流れは変わりません。例えば以下のような空のifブロックを記述してしまった事が原因です。
if (argv.length == 1) {
// TODO: handle this case
}
このメソッド内には、制御フローを全く変えないか、単に次の行に移るだけの分岐が存在します。これは、不注意で以下のような空のif文を作成してしまった場合に起き得ます。
if (argv.length == 1);
System.out.println("Hello, " + argv[0]);
このクラスには似た名前の get メソッドと set メソッドがあり、set メソッドは同期化されていますが、get メソッドは同期化されていません。このため、getメソッドの呼び出しによって必ずしも最新のオブジェクトの内容が見えるとは限らず、実行時に正しくない挙動を招く場合があります。get メソッドも同期化するべきです。
this.getClass().getResource(...) の呼び出しは、別のパッケージに継承クラスがある場合には、予期しない結果をもたらす可能性があります。
このメソッドは、JSR-166 (java.util.concurrent) によるロックを獲得していますが、このメソッドを起点とした実行パスには、ロックが解放されないパスがあります。一般には、このロックを使うための正しいイディオムは次のようになります。
Lock l = ...;
l.lock();
try {
// do something
} finally {
l.unlock();
}
このメソッドは、JSR-166 (java.util.concurrent) によるロックを獲得していますが、このメソッドを起点とした例外処理パスに、解放されないパスがあります。一般には、このロックを使うための正しいイディオムは次のようになります。
Lock l = ...;
l.lock();
try {
// do something
} finally {
l.unlock();
}
このメソッド内でjava.lang.Mathのstaticメソッドを定数に対して呼び出しています。この結果は予め計算しておく事が可能であり、その方が高速で場合によってはより正確です。以下のメソッド呼び出しが検出されます。
| メソッド | パラメータ |
|---|---|
| abs | -任意- |
| acos | 0.0 か 1.0 |
| asin | 0.0 か 1.0 |
| atan | 0.0 か 1.0 |
| atan2 | 0.0 |
| cbrt | 0.0 か 1.0 |
| ceil | -任意- |
| cos | 0.0 |
| cosh | 0.0 |
| exp | 0.0 か 1.0 |
| expm1 | 0.0 |
| floor | -任意- |
| log | 0.0 か 1.0 |
| log10 | 0.0 か 1.0 |
| rint | -任意- |
| round | -任意- |
| sin | 0.0 |
| sinh | 0.0 |
| sqrt | 0.0 か 1.0 |
| tan | 0.0 |
| tanh | 0.0 |
| toDegrees | 0.0 か 1.0 |
| toRadians | 0.0 |
この匿名クラスに宣言されているメソッドは、直接呼び出されてもいなければ、継承元クラスのメソッドをオーバーライドしているわけでもありません。匿名クラスに宣言されたメソッドは、他のクラスから直接呼び出すことはできないので、結局このメソッドは呼び出すことができないように見受けられます。単に使われていないだけなのかもしれませんが、継承元のクラスのメソッドをオーバーライドしようとして失敗しているのかもしれません。もしもそうなら修正してください。
Objectのequalsメソッドをオーバーライドしていないfinalクラスの.equals(Object o)メソッドが呼び出されています。これは==による参照の比較と同値です。equalsメソッドを使うのは良いのですが、このクラスでequalsメソッドをオーバーライドすることを検討してください。
[Bill Pugh]: すみません、私はこの警告の出力には強く反対で、あなたのコードは全く正しいと思います。ユーザのコードはequals()の実装がどうなっているかを意識すべきではなく、==でのインスタンス比較に依存すべきではありません。このようにしてしまうと、ライブラリ側でオブジェクトの同値性を制御することができなくなってしまいます。
このprivateメソッドは一度も呼ばれません。リフレクションで呼び出される可能性は残りますが、このメソッドは全く使用されておらず、削除されるべきと考えるのが自然です。
このコンストラクタは、まだ何も値が代入されていないフィールドを読み込んでいます。これはしばしば、プログラマが間違えてコンストラクタのパラメータのかわりに、フィールドにアクセスしてしまった場合に起こります。
このフィールドが読み出される事はありません。クラスから削除する事を検討してください。
この抽象メソッドは、このクラスが実装しているインターフェースに宣言されています。このメソッドには何の価値もなく、削除可能です。
このメソッドは単に親クラスのメソッドを同じパラメータで呼び出しているに過ぎません。このメソッドには何の価値もなく、削除可能です。
このフィールドは利用されません。クラスから削除する事を検討してください。
このメソッドの中でjava.lang.Object.wait()を呼び出していますが、条件判断によってガードされていません。wait()を呼び出す前に、待ち条件が既に成立していないかどうか、調査する必要があります。wait()呼び出し前に行われた通知は無視されてしまいます。
このフィールドはコンストラクタで初期化されていません。このためオブジェクトが生成された後 null となっていると思われます。フィールドが初期化される前に利用されれば NullPointerExcepition のスローを招くため、これは間違いか設計不備との可能性があります。
このフィールドへのすべての書き込みは定数の null であり、このフィールドからの読み出しはすべて null になります。間違いが無いかチェックし、不要であるなら削除してください。
このフィールドに書かれる事はありません。このため読み出されるのはデフォルト値です。初期化が必要ではないですか? もしも不要なら、削除する事を検討してください。
可変引数を利用した文字列のフォーマットメソッドが利用されていますが、渡された引数の数と、フォーマット文字列中の % プレースホルダの数が一致していません。これは、設計者の意図したものではないはずです。
書式文字列のプレースホルダが関連する引数と一致していません。たとえば、以下のようなコードです。
System.out.println("%d\n", "hello");
プレースホルダ %d は数値の引数を必要としますが、文字列が渡されています。このステートメントを実行すると、実行時例外が発生してしまいます。
可変引数による文字列のフォーマットメソッドが呼び出されていますが、実際に書式文字列で必要とされる数よりも多くの引数が渡されています。これは実行時例外とはなりませんが、書式文字列に含まれるべき情報を密かに落としてしまっているかもしれません。
書式文字列の書式が誤っています。このステートメントを実行すると実行時例外が発生してしまいます。
引数の数が書式文字列のプレースホルダに比べて足りません。このステートメントを実行すると、実行時例外が発生してしまいます。
この書式文字列では、前の引数を再参照するために相対インデックスを利用しています。前の引数が存在しません。
たとえば、以下の例では実行時に MissingFormatArgumentException がスローされます。
formatter.format("%<s %s", "a", "b")
このコードは可変引数をとるメソッドにプリミティブ型の配列を渡しています。これは結局、要素型が配列である、サイズ1の配列が生成されて、その要素にこのプリミティブ型の配列が格納されてメソッドに渡されることになります。
配列への参照変数がvolatile宣言されていますが、これは、あなたが意図した動作をしない可能性があります。配列の参照変数がvolatile宣言されている場合、この参照変数自体への書き込み、読み込みはvolatileとして扱われますが、配列の各要素はvolatileではありません。もしも配列の要素へのアクセスをvolatileとして扱いたいのであれば、Java 5.0で提供されるjava.util.concurrentパッケージに含まれるアトミック配列クラスを利用する必要があります。
このクラスは解析中のライブラリでは解決できないクラスまたはメソッドへの参照を生成しています。
このメソッドは java.util.concurrent.locks.Condition.await()(あるいはそのバリエーション) を呼び出していますが、ループの中にありません。オブジェクトが複数の条件の待ち合せに使用されると、起こされた時にそれが自分の待ち合わせ条件に合致するとは限りません。
このメソッドはjava.lang.Object.wait()を呼び出していますが、ループの中にありません。幾つかの条件を待ち合わせるためにモニタを利用する場合は、wait()を抜けたからといって、自分が待っている条件が成立している保証はありません。
このインスタンスメソッドは、this.getClass() に対して同期を行っています。このクラスがサブクラス化された場合、当初の意図に反してサブクラスはサブクラスの Class オブジェクトに対して同期を行うでしょう。
たとえば、java.awt.Label 内の以下のコードを見てください。
private static final String base = "label";
private static int nameCounter = 0;
String constructComponentName() {
synchronized (getClass()) {
return base + nameCounter++;
}
}
Label のサブクラスは同じサブクラスに対して同期を行いません。
代わりに、以下のコードでは Label.class に対して同期を行っています。
private static final String base = "label";
private static int nameCounter = 0;
String constructComponentName() {
synchronized (Label.class) {
return base + nameCounter++;
}
}
Bug pattern contributed by Jason Mehrens
Mapの内容にkeySetイテレータから取り出したキーを用いてアクセスしています。このようなケースはentrySetを用いれば、無駄なMap.get(key)呼び出しを回避できます。
このクラスの writeObject() メソッドは同期化されていますが、他のメソッドは同期化されていません。
このメソッドではXML関連のインターフェースの特定の実装を直接生成していますが、これはファクトリクラスを使って生成する方が望ましいやり方です。これにによって、実行時に利用する実装を切り替えることが出来ます。詳細は以下を参照してください。
このコードでは、HTTP パラメータを JSP 出力に対して直接出力しており、クロスサイトスクリプティングの脆弱性につながります。クロスサイトスクリプティングについては、http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。 FindBugs はクロスサイトスクリプティングのもっとも明白な事例しか検出しません。 もし FindBugs が一つでも事例を発見した場合、FindBugs がレポートしないより多くの脆弱性を抱えている可能性が非常に高いです。 クロスサイトスクリプティングについて心配があるのならば、FindBugs プロジェクトのスポンサーである Fortify Software が提供するような商用静的解析ツールの導入を真剣に検討すべきです。もしあなたのソフトウェアがオープンソースであるならなば、Fortify は JOR (Java Open Review) の一環としてあなたのコードを無償で調査します。
このコードでは、HTTPパラメータを(HttpServletResponse.sendError() メソッドを利用することで)サーバエラーページへ直接出力しています。このような信頼できない入力を出力することは、クロスサイトスクリプティングの脆弱性となります。クロスサイトスクリプティングについては、http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。 FindBugs はクロスサイトスクリプティングのもっとも明白な事例しか検出しません。 もし FindBugs が一つでも事例を発見した場合、FindBugs がレポートしないより多くの脆弱性を抱えている可能性が非常に高いです。 クロスサイトスクリプティングについて心配があるのならば、FindBugs プロジェクトのスポンサーである Fortify Software が提供するような商用静的解析ツールの導入を真剣に検討すべきです。もしあなたのソフトウェアがオープンソースであるならなば、Fortify は JOR (Java Open Review) の一環としてあなたのコードを無償で調査します。
このコードでは、HTTPパラメータをサーブレットの出力へ直接書き込んでおり、クロスサイトスクリプティングの脆弱性につながります。クロスサイトスクリプティングについては、http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。 FindBugs はクロスサイトスクリプティングのもっとも明白な事例しか検出しません。 もし FindBugs が一つでも事例を発見した場合、FindBugs がレポートしないより多くの脆弱性を抱えている可能性が非常に高いです。 クロスサイトスクリプティングについて心配があるのならば、FindBugs プロジェクトのスポンサーである Fortify Software が提供するような商用静的解析ツールの導入を真剣に検討すべきです。もしあなたのソフトウェアがオープンソースであるならなば、Fortify は JOR (Java Open Review) の一環としてあなたのコードを無償で調査します。