検知可能なバグの詳細¶
このページではSpotBugsが標準で検知可能なバグを一覧で紹介しています。
バッドプラクティス (BAD_PRACTICE)¶
推奨または必須のコーディングプラクティスへの違反です。たとえば,hashCode と equals の問題,Cloneable イディオム,捨てられた例外,Serializable の問題,finalize の誤用などです。 いくつかのグループはバッドプラクティスを気にしないかもしれないが,我々はこの解析が正確になるよう努力しています。
NP: 戻り型が Boolean のメソッドが明示的に null を返している (NP_BOOLEAN_RETURN_NULL)¶
Boolean.TRUE
, Boolean.FALSE
, null
を返すメソッドはいつ事故が起きてもおかしくないです。
このメソッドは,まるで論理型の値を返すかのように呼び出されます。
コンパイラは Boolean
値のオートアンボクシングを挿入します。
null
値が返されるなら NullPointerException
が発生することになります。
SW: Swing メソッドは AWT イベントディスパッチスレッドから呼び出す必要がある (SW_SWING_METHODS_INVOKED_IN_SWING_THREAD)¶
(From JDC Tech Tip)
に解説されているとおり,Swing のメソッド, show
メソッド, setVisible
メソッド, pack
メソッドは,フレームのための関連したピアを作成します。
ピアの作成で,システムはイベントディスパッチスレッドを作成します。
これが問題になることがあります。なぜなら pack
メソッドと validate
メソッドがまだ処理中でもイベントディスパッチスレッドがリスナに通知できるからです。
この状況は,2つのスレッドが Swing コンポーネントにアクセスする可能性があり,デッドロックや,その他のスレッドに関する問題になる可能性がある重大な欠陥です。
pack
メソッドの呼び出しはコンポーネントを実体化させます。実体化しているときに,イベントディスパッチスレッドがリスナへの通知を開始する可能性があります。
FI: ファイナライザはフィールドを null にするだけ (FI_FINALIZER_ONLY_NULLS_FIELDS)¶
このファイナライザは,フィールドを null
にすること以外に何もしません。
これはまったく無意味であり,オブジェクトがガベージされ,ファイナライズされ,再びガベージされることを要求しています。
finalize
メソッドを除去すべきです。
FI: ファイナライザはフィールドを null にする (FI_FINALIZER_NULLS_FIELDS)¶
このファイナライザは,フィールドを null
にしています。
これは通常誤りでガベージコレクタを助けません。オブジェクトはいずれにしろガベージされます。
UI: クラスが拡張されるなら getResource の使い方は安全ではないかもしれない (UI_INHERITANCE_UNSAFE_GETRESOURCE)¶
このクラスが別のパッケージによって拡張されるなら, this.getClass().getResource(...)
の呼び出しは予想外の結果をもたらす可能性があります。
AM: 空の ZIP ファイルエントリの作成 (AM_CREATES_EMPTY_ZIP_FILE_ENTRY)¶
このコードは putNextEntry
メソッドを呼び出して, closeEntry
メソッドをすぐにを呼び出しています。
これは空の ZIP ファイルエントリになります。
エントリデータは putNextEntry
メソッドと closeEntry
メソッドの呼び出しの間で ZIP ファイルに書き込むべきです。
AM: 空の JAR ファイルエントリの作成 (AM_CREATES_EMPTY_JAR_FILE_ENTRY)¶
このコードは putNextEntry
メソッドを呼び出して, closeEntry
メソッドをすぐに呼び出しています。
これは空の JAR ファイルエントリになります。
エントリデータは putNextEntry
メソッドと closeEntry
メソッドの呼び出しの間で JAR ファイルに書き込むべきです。
IMSE: 疑わしい IllegalMonitorStateException のキャッチ (IMSE_DONT_CATCH_IMSE)¶
IllegalMonitorStateException
は,一般的に設計上の欠陥 (ロックを保持していないオブジェクトで wait
メソッドまたは notify
メソッドを呼び出す) の場合にだけスローされます。
CN: Cloneable を実装していないクラスが clone メソッドを定義している (CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE)¶
このクラスは, Cloneable
を実装していないのに clone
メソッドを定義しています。
これが OK (たとえば,サブクラスのクローンの実装を自分自身で制御したい場合です) という状況もありますが意図したことなのか確認してください。
CN: Cloneable を実装したクラスが clone メソッドを定義も使用もしていない (CN_IDIOM)¶
このクラスは, Cloneable
を実装していますが, clone
メソッドを定義も使用もしていません。
CN: clone メソッドが super.clone() を呼び出していない (CN_IDIOM_NO_SUPER_CALL)¶
この非 final
クラスは, super.clone()
を呼び出さない clone
メソッドを定義しています。
クラス A がサブクラス B によって拡張され,サブクラス B が super.clone()
を呼び出すなら,クラス B の clone
メソッドは,型 A のオブジェクトを返す可能性が高いです。
これは clone
のための汎用規約に違反します。
すべての clone
メソッドが super.clone()
を呼び出すなら Object.clone()
が呼び出されることが保証され,常に正しい型のオブジェクトが返されます。
DE: 例外を捨てているかもしれないメソッド (DE_MIGHT_DROP)¶
このメソッドは,例外を捨てているかもしれません。 一般的にキャッチした例外は何らかの方法で処理または報告すべきです。またはメソッドからスローすべきです。
DE: 例外を無視しているかもしれないメソッド (DE_MIGHT_IGNORE)¶
このメソッドは例外を無視しているかもしれません。 一般的に例外は何らかの方法で処理または報告すべきです。またはメソッドからスローすべきです。
Dm: System.exit(...) を呼び出しているメソッド (DM_EXIT)¶
System.exit(...)
を呼び出すことは,Java 仮想マシン全体をシャットダウンさせてしまいます。
それが適切な場合にだけ使用すべきです。
System.exit(...)
の呼び出しは,他のコードによる呼び出しを困難か不可能にします。
その代わりに RuntimeException
をスローすることを検討してください。
Nm: Java の後のバージョンのキーワードである識別子を使用している (NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER)¶
識別子は,Java の後のバージョンのキーワードとして予約されている単語です。 コードを Java の後のバージョンでコンパイルするためには変更する必要があります。
Nm: Java の後のバージョンのキーワードである識別子を使用している (NM_FUTURE_KEYWORD_USED_AS_MEMBER_IDENTIFIER)¶
この識別子は,Java の後のバージョンのキーワードとして使われます。 このコードと API を参照するどんなコードも,Java の後のバージョンでコンパイルするためには変更する必要があります。
JCIP: 不変クラスのフィールドは final にすべき (JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS)¶
クラスは, net.jcip.annotations.Immutable
または javax.annotation.concurrent.Immutable
というアノテーションが付けられています。
アノテーションのルールは,すべてのフィールドが final
であることを義務付けます。
Dm: 危険なメソッド runFinalizersOnExit を呼び出しているメソッド (DM_RUN_FINALIZERS_ON_EXIT)¶
どんな理由があるにせよ決して System.runFinalizersOnExit
と Runtime.runFinalizersOnExit
を呼び出さないでください。
Java ライブラリで最も危険なメソッドの1つです。 -- Joshua Bloch
NP: equals メソッドは null の引数をチェックしていない (NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT)¶
この equals(Object)
メソッドの実装は引数として渡されている null
をチェックしていないので, java.lang.Object.equals()
で定義された規約に違反しています。
すべての equals
メソッドは引数に null
が渡されたなら false
を返すべきです。
FI: ファイナライザはスーパークラスのファイナライザを無効にしている (FI_NULLIFY_SUPER)¶
この空の finalize
メソッドは,明示的にスーパークラスによって定義されたどんなファイナライザの効果も無効にします。
スーパークラスのために定義されたどんなファイナライザアクションも実行されません。
これが意図したことではない場合,メソッドを削除してください。
FI: ファイナライザはスーパークラスのファイナライザを呼び出しているだけ (FI_USELESS)¶
この finalize
メソッドは,スーパークラスの finalize
メソッドを呼び出しているだけです。
冗長なので削除してください。
FI: ファイナライザはスーパークラスのファイナライザを呼び出していない (FI_MISSING_SUPER_CALL)¶
この finalize
メソッドは,スーパークラスの finalize
メソッドを呼び出していません。
したがって,スーパークラスのために定義されたどんなファイナライザアクションも実行されません。
super.finalize()
の呼び出しを追加してください。
FI: ファイナライザの明示的な呼び出し (FI_EXPLICIT_INVOCATION)¶
このメソッドには明示的にオブジェクトで finalize
メソッドの呼び出しがあります。
ファイナライザは Java 仮想マシンによって1度だけ実行されることになっているので,これは間違った考えです。
参照によってつながった複数のオブジェクトがファイナライズ可能になると,Java 仮想マシンはすべてのオブジェクトの finalize
メソッドを呼び出します。
おそらく異なるスレッドで同時にです。
したがって,クラス X の finalize
メソッドの中から X によって参照されているオブジェクトの finalize
メソッドを呼び出すのは,とりわけ間違った考えです。
なぜなら,オブジェクトが既に別のスレッドによってファイナライズされているかもしれないからです。
Eq: equals メソッドは互換性のないオペランドをチェックしている (EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS)¶
この equals
メソッドは,引数が互換性のない型 (すなわちスーパタイプでもなく, equals
メソッドを定義しているクラスのスーパータイプでもサブタイプでもないクラス) なのか確かめています。
たとえば, Foo
クラスの equals
メソッドはそのように見えるかもしれません。
public boolean equals(Object o) {
if (o instanceof Foo)
return name.equals(((Foo)o).name);
else if (o instanceof String)
return name.equals(o);
else return false;
}
これは対称的で推移的である equals
メソッドを実現するのはとても難しいので,バッドプラクティスと見なされています。
プロパティがなければまったく予想していない振る舞いが起こりえます。
Eq: equals メソッドはサブタイプのために失敗する (EQ_GETCLASS_AND_CLASS_CONSTANT)¶
このクラスは,サブクラスによる継承によって壊れる equlas
メソッドがあります。
equals
メソッドは,クラスリテラルを引数のクラスと比較しています (たとえば, Foo
クラスで Foo.class == o.getClass()
のような判定を行っています)。
this.getClass() == o.getClass()
の方がより良いです。
Eq: 共変な equals メソッドの定義 (EQ_SELF_NO_OBJECT)¶
このクラスは,共変な equals
メソッドを定義しています。
java.lang.Object
の equals
メソッドを正しくオーバーライドするためには equals
メソッドのパラメータの型は, java.lang.Object
でなければなりません。
Co: 共変な compareTo メソッドの定義 (CO_SELF_NO_OBJECT)¶
このクラスは,共変な compareTo
メソッドを定義しています。
Comparable
インタフェースの compareTo
メソッドを正しく実装するためには compareTo
メソッドのパラメータの型は, java.lang.Object
でなければなりません。
Co: compareTo()/compare() は Integer.MIN_VALUE を返す (CO_COMPARETO_RESULTS_MIN_VALUE)¶
いくつかの状況下では,この compareTo
または compare
メソッドは Integer.MIN_VALUE
を返します。非常に悪いプラクティスです。
compareTo
メソッドの戻り値で重要なことは結果の符号だけです。
しかし,結果の符号を無効にすることを期待して, compareTo
メソッドの戻り値を無効にすることがあります。
返された値が Integer.MIN_VALUE
の場合を除いてです。 Integer.MIN_VALUE
よりも-1を返してください。
Co: compareTo()/compare() は間違って float または double 値を処理する (CO_COMPARETO_INCORRECT_FLOATING)¶
このメソッドはこのようなパターンを使用して double
または float
値を比較しています : val1 > val2 ? 1 : val1 < val2 ? -1 : 0
。
このパターンは,-0.0 や NaN
の値が正しく処理されたないため,不正なソート結果や破損したコレクション(比較された値がキーとして使われる場合)が生成される可能性があります。
Double.compare
または Float.compare
メソッドを使用して,すべての特殊なケースを正確に処理することを検討してください。
RV: compareTo()/compare() の結果を無効にする (RV_NEGATING_RESULT_OF_COMPARETO)¶
このコードは compareTo
または compare
メソッドの戻り値を無効にしています。
これは疑わしいかバッドプログラミングプラクティスです。戻り値が Integer.MIN_VALUE
なので,戻り値を無効にすることは結果の符号を無効にしません。
結果を無効にするのではなくオペランドの順序を逆にすることによって,同じ意図した結果を得ることができます。
ES: String オブジェクトを == や != を使用して比較している (ES_COMPARING_STRINGS_WITH_EQ)¶
このコードは参照等価性のために ==
や !=
を使用して java.lang.String
オブジェクトを比較しています。
両方の文字列がソースファイルの定数か, String.intern()
を使用して正準化されていないかぎり,同じ文字列は2つの異なる String
オブジェクトによって表されるかもしれません。
その代わりに equals(Object)
メソッドを使用することを検討してください。
ES: String パラメータを == や != を使用して比較している (ES_COMPARING_PARAMETER_STRING_WITH_EQ)¶
このコードは参照等価性のために ==
や !=
を使用して java.lang.String
パラメータを比較しています。
文字列定数または正準化された文字列だけをメソッドに渡すことを呼び出し元に要求することは,不必要に脆弱であり,測定可能な性能の向上につながることはほとんどありません。
その代わりに equals(Object)
メソッドを使用することを検討してください。
Eq: compareTo(...) メソッドを定義して Object.equals() を使用しているクラス (EQ_COMPARETO_USE_OBJECT_EQUALS)¶
このクラスは, compareTo(...)
メソッドを定義していますが, equals
メソッドは java.lang.Object
から継承しています。
一般的にequals
メソッドが true
を返す場合に限り, compareTo
メソッドは0を返すべきです。
これが違反されるなら奇妙で予測できない失敗が PriorityQueue
などのクラスで発生します。
J2SE 5.0では, PriorityQueue.remove()
は compareTo
メソッドを使用しますが,Java SE 6では, equals
メソッドを使用します。
Comparable
インタフェースの compareTo
メソッドの JavaDoc を引用します。
必須というわけではありませんが,
(x.compareTo(y)==0) == (x.equals(y))
であることが強く推奨されます。 一般的にComparable
インタフェースを実装しているクラスで,この条件に違反するクラスは明確にこの事実を示す必要があります。 「注:このクラスはequals
と一貫性のない自然順序付けを持ちます」などと明示することをお勧めします。
HE: hashCode メソッドを定義して Object.equals() を使用しているクラス (HE_HASHCODE_USE_OBJECT_EQUALS)¶
このクラスは, hashCode
メソッドを定義していますが, equals
メソッドは java.lang.Object
から継承しています (オブジェクトの参照比較で等価性を判定します)。
これは「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に従っているかもしれませんが,
おそらく, hashCode
メソッドをオーバーライドすることによって意図されたことではありません。
(hashCode
メソッドをオーバーライドすることは,オブジェクトの同一性が単純な参照等価性よりも複雑な規約に基づくことを意味します)。
このクラスのインスタンスが HashMap
/HashTable
に決して代入されるだろうと思わないなら推奨される hashCode
メソッドの実装は次のようになります。
public int hashCode() {
assert false : "hashCodeが呼び出されることは想定されていません。";
return 42; // 任意の定数
}
HE: hashCode メソッドを定義していますが equals メソッドは定義していないクラス (HE_HASHCODE_NO_EQUALS)¶
このクラスは, hashCode
メソッドを定義していますが, equals
メソッドは定義していません。
これは「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に違反するかもしれません。
HE: equals メソッドを定義して Object.hashCode() を使用しているクラス (HE_EQUALS_USE_HASHCODE)¶
このクラスは, equals(Object)
をオーバーライドしていますが, hashCode
メソッドは java.lang.Object
から継承しています (同一性ハッシュコード (Java 仮想マシンによってオブジェクトに代入された任意の値) を返します)。
したがって,「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に違反するかもしれません。
このクラスのインスタンスが HashMap
/HashTable
に決して代入されるだろうと思わないなら推奨される hashCode
メソッドの実装は次のようになります。
public int hashCode() {
assert false : "hashCodeが呼び出されることは想定されていません。";
return 42; // 任意の定数
}
HE: equals メソッドを継承して Object.hashCode() を使用しているクラス (HE_INHERITS_EQUALS_USE_HASHCODE)¶
このクラスは,抽象スーパークラスから equals(Object)
メソッドを継承して, java.lang.Object
から hashCode
メソッドを継承しています (同一性ハッシュコード (Java 仮想マシンによってオブジェクトに代入された任意の値) を返します)。
したがって,「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に違反するかもしれません。
hashCode
メソッドを定義したくないまたはオブジェクトが HashMap
/Hashtable
に決して格納されないだろうと思っているなら UnsupportedOperationException
をスローする hashCode()
メソッドを定義してください。
HE: equals メソッドは定義していますが hashCode メソッドは定義していないクラス (HE_EQUALS_NO_HASHCODE)¶
このクラスは, equals(Object)
メソッドをオーバーライドしていますが, hashCode
メソッドはオーバーライドしていません。
したがって,「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に違反するかもしれません。
Eq: 抽象クラスは共変な equals メソッドを宣言している (EQ_ABSTRACT_SELF)¶
このクラスは,共変な equals
メソッドを定義しています。
java.lang.Object
の equals
メソッドを正しくオーバーライドするためには equals
メソッドのパラメータの型は, java.lang.Object
でなければなりません。
Co: 抽象クラスは共変な compareTo メソッドを定義している (CO_ABSTRACT_SELF)¶
このクラスは,共変な compareTo
メソッドを定義しています。
Comparable
インタフェースの compareTo
メソッドを正しく実装するためには compareTo
メソッドのパラメータの型は, java.lang.Object
でなければなりません。
IC: スーパークラスは初期化中にサブクラスを使用している (IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION)¶
クラスは,初期化中にサブクラスを積極的に使用しています。サブクラスはこの時点ではまだ初期化されていません。
たとえば,次のコードにおいて, foo
は null
です。
public class CircularClassInitialization {
static class InnerClassSingleton extends CircularClassInitialization {
static InnerClassSingleton singleton = new InnerClassSingleton();
}
static CircularClassInitialization foo = InnerClassSingleton.singleton;
}
SI: スタティックイニシャライザは,すべての static final フィールドが代入される前にインスタンスを作成する (SI_INSTANCE_BEFORE_FINALS_ASSIGNED)¶
すべての static final
フィールドが初期化される前にスタティックイニシャライザがクラスのインスタンスを作成します。
It: Iterator.next() が NoSuchElementException をスローできない (IT_NO_SUCH_ELEMENT)¶
このクラスは, java.util.Iterator
を実装しています。
しかしながら, next
メソッドは java.util.NoSuchElementException
をスローできません。
next
メソッドは,それ以上要素を返すことができないときは NoSuchElementException
をスローするように変更すべきです。
ME: 列挙型フィールドは public で可変である (ME_MUTABLE_ENUM_FIELD)¶
可変 public
フィールドが public
列挙型の中に定義されています。
したがって,フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
可変列挙型フィールドが遅延初期化で使用されるかもしれないとしても外界へ暴露するバッドプラクティスです。
このメソッドを final
およびパッケージプライベートとして宣言することを考えてください。
ME: public 列挙型メソッドが無条件にフィールドを設定する (ME_ENUM_FIELD_SETTER)¶
無条件に列挙型フィールドを設定している public
列挙型で public
メソッドを宣言しています。
したがって,フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
可変列挙型フィールドが遅延初期化で使用されるかもしれないとしても外界へ暴露するバッドプラクティスです。
このメソッドを除去するかパッケージプライベートとして宣言することを考えてください。
Nm: メソッド名は小文字から始めるべき (NM_METHOD_NAMING_CONVENTION)¶
メソッド名は,最初の文字は小文字にし,それに続く各単語の最初の文字を大文字にした動詞にすべきです。
Nm: フィールド名は小文字から始めるべき (NM_FIELD_NAMING_CONVENTION)¶
final
ではないフィールドの名前は,最初の文字は小文字にし,それに続く各単語の最初の文字を大文字にすべきです。
Nm: クラス名は実装されたインタフェースの単純名を遮るべきではない (NM_SAME_SIMPLE_NAME_AS_INTERFACE)¶
このクラスまたはインタフェースは,インタフェースが異なるパッケージであるということを除いて実装された/拡張されたインタフェースと同一の単純名です (たとえば, alpha.Foo
が beta.Foo
を継承しているような状況です)。
これは非常に紛らわしく,参照関係を解決するために import
文を見なければならなかったり,スーパークラスのメソッドをオーバーライドしないで誤ってメソッドを定義する状況を作り出します。
Nm: クラス名はスーパークラスの単純名を遮るべきではない (NM_SAME_SIMPLE_NAME_AS_SUPERCLASS)¶
このクラスは,スーパークラスが異なるパッケージであるということを除いて,スーパークラスと同一の単純名です (たとえば, alpha.Foo
が beta.Foo
を拡張します)。
これは非常に紛らわしく,参照関係を解決するために import
文を見なければならなかったり,スーパークラスのメソッドをオーバーライドしないで誤ってメソッドを定義する状況を作り出します。
Nm: クラス名は大文字から始めるべき (NM_CLASS_NAMING_CONVENTION)¶
クラス名は,最初の文字とそれに続く各単語の最初の文字を大文字にした名詞にすべきです。 クラス名は単純でわかりやすいようにしてください。 頭文字や略語 (URLやHTMLなどのように略語がロング形式よりもはるかに広く使われている場合を除く) の使用は避けてください。
Nm: 非常に紛らわしい名前のメソッド (多分意図的) (NM_VERY_CONFUSING_INTENTIONAL)¶
参照されたメソッドは,大文字と小文字だけが異なる名前があります。 大文字と小文字が同一ならメソッドの1つが他のメソッドをオーバーライドするので,非常に紛らわしいです。 他のメソッドの存在から,これらのメソッドの両方の存在が意図的で,確実に混乱させていると思われます。 APIの凍結によって両方とも持たざるを得ない場合を除き,それらのうちの1つを除去しようと努力すべきです。
Nm: パラメータの間違ったパッケージのためにスーパークラスのメソッドをオーバーライドしていないメソッド (NM_WRONG_PACKAGE_INTENTIONAL)¶
パラメータの型が正確にスーパークラスで対応するパラメータの型と合致していないので,サブクラスのメソッドはスーパークラスの類似したメソッドをオーバーライドしていません。
たとえば次のようなコードです。
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
が違うパッケージだからです。
この場合,サブクラスがスーパークラスのメソッドと同一のシグネチャでメソッドを定義しているので,おそらく理解できます。 しかしながら,そのようなメソッドは非常に紛らわしいです。 類似しているが同一ではないシグネチャのメソッドを除去するか,非推奨にすることを強く検討すべきです。
Nm: 例外クラスのように命名されているが,クラスは Exception から派生されていない (NM_CLASS_NOT_EXCEPTION)¶
このクラスは,例外クラスから派生されていないのにクラス名が「Exception」で終わっています。 このクラスのユーザが混乱するでしょう。
RR: InputStream.read() の戻り値を無視しているメソッド (RR_NOT_CHECKED)¶
このメソッドは,複数バイトを返す可能性がある java.io.InputStream.read()
(またはそのバリエーション) の戻り値を無視しています。
戻り値がチェックされないと呼び出し元は要求したバイト数よりも少ないバイト数を読み出した場合,正しく処理できません。
これは潜在的なバグで,多くのプログラムでは,入力ストリームからの読み出しは,通常要求した完全なデータ量を読み出しますが,散発的に失敗することがあります。
RR: InputStream.skip() の戻り値を無視しているメソッド (SR_NOT_CHECKED)¶
このメソッドは,複数バイトをスキップする可能性がある java.io.InputStream.skip()
の戻り値を無視しています。
戻り値がチェックされないと呼び出し元は要求したバイト数よりも少ないバイト数しかスキップしなかった場合,正しく処理できません。
これは潜在的なバグで,多くのプログラムでは,入力ストリームからのスキップは,通常要求した完全なデータ量をスキップをしますが,散発的に失敗することがあります。
しかしながら,バッファードストリーム での skip
メソッドはバッファのデータをスキップするので要求されたバイト数のスキップは常に失敗します。
Se: Serializable なクラスのスーパークラスで,引数なしコンストラクタを定義していない (SE_NO_SUITABLE_CONSTRUCTOR)¶
このクラスは Serializable
インタフェースを実装していますが,そのスーパークラスは実装していません。
そのようなオブジェクトが直列化復元されるとき,スーパークラスのフィールドはスーパークラスの引数なしコンストラクタを呼び出すことによって初期化される必要があります。
スーパークラスには引数なしコンストラクタがないので,直列化と直列化復元は実行時に失敗します。
Se: Externalizable なクラスが引数なしコンストラクタを定義していない (SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION)¶
このクラスは, Externalizable
インタフェースを実装していますが,引数なしコンストラクタを定義していません。
Externalizable
オブジェクトが直列化復元されるときは,最初に引数なしコンストラクタを呼び出すことによって構築される必要があります。
このクラスには引数なしコンストラクタがないので,直列化と直列化復元は実行時に失敗します。
Se: Comparator は Serializable を実装していない (SE_COMPARATOR_SHOULD_BE_SERIALIZABLE)¶
このクラスは Comparator
インタフェースを実装しています。
Serializable
インタフェースも実装する必要があるのか検討すべきです。
コンパレータが TreeMap
のような順序付きコレクションを構築するために使われるなら,コンパレータが直列化可能な場合だけ, TreeMap
は直列化可能です。
大部分のコンパレータがほとんど状態を持たないとしても直列化可能にすることは簡単で良い防衛的なプログラミングです。
SnVI: Serializable なクラスが serialVersionUID を定義していない (SE_NO_SERIALVERSIONID)¶
このクラスは Serializable
インタフェースを実装していますが, serialVersionUID
フィールドを定義していません。
.class オブジェクトへの参照を追加するのと同じくらい簡単な変更でクラスに合成フィールドを追加します。
それは,残念ながら暗黙の serialVersionUID
を変えます (たとえば, String.class
への参照を追加すると, class$java$lang$String
という static
フィールドを生成します)。
また,バイトコードコンパイラへの異なるソースコードは,クラスオブジェクトまたは内部クラスを参照するために生成される合成変数のために異なる命名規則を使用するかもしれません。
バージョンを横断する Serializable
の相互運用性を保証するために明示的に serialVersionUID
を追加することを検討してください。
Se: readResolve メソッドの戻り値の型が Object で宣言されていない (SE_READ_RESOLVE_MUST_RETURN_OBJECT)¶
readResolve
メソッドが直列化機構で認識されるためには戻り値の型が Object
で宣言されなければなりません。
Se: 直列化復元によって設定されない transient フィールド (SE_TRANSIENT_FIELD_NOT_RESTORED)¶
このクラスには複数の場所で更新されるフィールドがあります。したがって,このクラスの状態の一部だと思われます。
しかしながら,フィールドは transient
と宣言しているので, readObject
/readResolve
で値が設定されません。
クラスの直列化復元されたインスタンスにはデフォルト値が設定されます。
Se: serialVersionUID が final ではない (SE_NONFINAL_SERIALVERSIONID)¶
このクラスは, final
ではない serialVersionUID
フィールドを定義しています。
直列化を目的としてバージョン UID を指定することを意図しているならフィールドは final
とすべきです。
Se: serialVersionUID が static ではない (SE_NONSTATIC_SERIALVERSIONID)¶
このクラスは, static
ではない serialVersionUID
フィールドを定義しています。
直列化を目的としてバージョン UID を指定することを意図しているならフィールドは static
とすべきです。
Se: serialVersionUID が long ではない (SE_NONLONG_SERIALVERSIONID)¶
このクラスは, long
ではない serialVersionUID
フィールドを定義しています。
直列化を目的としてバージョン UID を指定することを意図しているならフィールドは long
とすべきです。
Se: 直列化可能クラスの非 transient で非直列化可能なインスタンスフィールド (SE_BAD_FIELD)¶
この直列化可能クラスは, transient
, Serializable
, java.lang.Object
でもない非プリミティブ型のインスタンスフィールドを定義して,
Externalizable
インタフェースまたは readObject
メソッドと writeObject
メソッドを実装するように見えません。
また, Externalizable
インタフェースも実装していなくて, readObject
メソッドも writeObject
メソッドも定義していません。
非直列化可能オブジェクトがこのフィールドに格納されるならクラスのオブジェクトは正しく直列化復元されません。
Se: 直列化可能な内部クラス (SE_INNER_CLASS)¶
この直列化可能なクラスは内部クラスです。内部クラスを直列化しようとすると関連した外部クラスのインスタンスも直列化します。
外部クラスのインスタンスは直列化可能なので失敗しません。しかし,意図していたよりももっと多くのデータを直列化するかもしれません。
できれば,内部クラスを static
にして問題を解決すべきです。
Se: 非直列化可能クラスに直列化可能な内部クラスがある (SE_BAD_FIELD_INNER_CLASS)¶
この直列化可能クラスは,非直列化可能クラスの内部クラスです。 内部クラスを直列化しようとすると関連する外部クラスのインスタンスを結びつけようとするので,実行時エラーの原因になります。
できれば,内部クラスを static
にして問題を解決すべきです。
外部クラスの直列化は動作可能かもしれませんが,内部クラスのインスタンスを直列化することは,外部クラスのインスタンスも常に直列化することを意味します。
本当に望むことですか。
Se: 非直列化可能な値を直列化可能クラスのインスタンスフィールドに格納している (SE_BAD_FIELD_STORE)¶
非直列化可能な値を直列化可能クラスの 非 transient
フィールドに格納しています。
RV: 例外的戻り値を無視しているメソッド (RV_RETURN_VALUE_IGNORED_BAD_PRACTICE)¶
このメソッドはチェックされていない値を返しています。
戻り値は異常か予想外の実行結果を示す可能性があるのでチェックすべきです。
たとえば, File.delete()
はファイルをうまく削除できなかったなら,例外をスローするのではなく false
を返します。
結果をチェックしないなら例外的戻り値を返すメソッドの呼び出しで予想外の振る舞いの合図に気づきません。
NP: null を返すかもしれない toString メソッド (NP_TOSTRING_COULD_RETURN_NULL)¶
この toString
メソッドは,いくつかの条件で null
を返すと思われます。
仕様を寛大に読むとこれが許されると解釈できるかもしれませんが,それはおそらく間違った考えで,他のコードが壊れる原因になる可能性があります。
null
ではなく空の文字列,または,いくつかの他の適切な文字列を返してください。
NP: null を返すかもしれない clone メソッド (NP_CLONE_COULD_RETURN_NULL)¶
この clone
メソッドは,いくつかの条件で null
を返すと思われます。
しかし, clone
メソッドは決して null
を返すのは許されません。
この経路が到達できないことを確信しているなら,代わりに AssertionError
をスローしてください。
OS: ストリームのクローズに失敗するかもしれないメソッド (OS_OPEN_STREAM)¶
このメソッドは,入出力ストリームオブジェクトを作成していますが,任意のフィールドに割り当てたり,クローズする可能性のある別のメソッドに渡したり,返すことはなく,メソッドからのすべての経路でクローズするように見えません。
これはファイルディスクリプタリークの原因になることがあります。
ストリームがクローズされることを確実にするために finally
ブロックを使用することは一般的に良い考えです。
OS: 例外経路でストリームのクローズに失敗するかもしれないメソッド (OS_OPEN_STREAM_EXCEPTION_PATH)¶
このメソッドは,入出力ストリームオブジェクトを作成していますが,任意のフィールドに割り当てたり,クローズする可能性のある別のメソッドに渡したり,返すことはなく,メソッドからのすべての例外経路でクローズするように見えません。
これはファイルディスクリプターリークの原因になることがあります。
ストリームがクローズされることを確実にするために finally
ブロックを使用することは一般的に良い考えです。
RC: 定数の疑わしい参照比較 (RC_REF_COMPARISON_BAD_PRACTICE)¶
このメソッドは,参照値を ==
または !=
演算子を使用して定数と比較しています。
一般的にこの型のインスタンスを比較する正しい方法は equals
メソッドです。
等価で識別可能なインスタンスを作成する可能性がありますが異なるオブジェクトなので ==
で比較しないでください。
一般的に参照によって比較されるべきではないクラスの例は, java.lang.Integer
, java.lang.Float
などです。
RC: Boolean 値の疑わしい参照比較 (RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN)¶
このメソッドは, ==
または !=
演算子を使用して2つの Boolean
値を比較しています。
一般的には2つの Boolean
値 (Boolean.TRUE
と Boolean.FALSE
) だけですが,
new Boolean(b)
コンストラクタを使用して他の Boolean
オブジェクトを作成する可能性があります。
そのようなオブジェクトを回避することは最高です。
しかし,それらが存在するなら, Boolean
オブジェクトの等価性をチェックするために .equals(...)
ではなく ==
または !=
を使用しているなら異なる結果をもたらします。
FS: 書式文字列は n よりも %n を使用すべき (VA_FORMAT_STRING_USES_NEWLINE)¶
この書式文字列は改行文字 (\n
) が含まれています。
一般的に書式文字列には %n
を使用することがより望ましいです。%n
は,プラットフォーム特有の行セパレータを作り出します。
BIT: ビット演算の符号をチェックする (BIT_SIGNED_CHECK)¶
このメソッドは, ((event.detail & SWT.SELECTED) > 0)
のような式で比較しています。
ビット演算をより大きい演算子で比較することは,予想外の結果 (もちろん, SWT.SELECTED
の値による) の原因になる可能性があります。
SWT.SELECTED
が負数であるなら,これはバグの候補です。
SWT.SELECTED
が負ではないとしても, > 0
の代わりに != 0
を使用することが良いプラクティスであると思われます。
ODR: データベースリソースのクローズに失敗するかもしれないメソッド (ODR_OPEN_DATABASE_RESOURCE)¶
このメソッドは,データベースリソース (たとえば,データベースコネクションや行セット) を作成していますが,どんなフィールドにも代入していないか,他のメソッドにも渡していないか,戻り値にもしていません。 そして,メソッドからのすべての経路でオブジェクトをクローズするように見えません。 メソッドからのすべての経路でデータベースリソースのクローズが失敗すると性能低下になることがあります。 データベースとの通信で問題があるアプリケーションの原因になる可能性があります。
ODR: 例外経路でデータベースリソースのクローズに失敗するかもしれないメソッド (ODR_OPEN_DATABASE_RESOURCE_EXCEPTION_PATH)¶
このメソッドは,データベースリソース (たとえば,データベースコネクションや行セット) を作成していますが,どんなフィールドにも代入していないか,他のメソッドにも渡していないか,戻り値にもしていません。 そして,メソッドからのすべての例外経路でオブジェクトをクローズするように見えません。 メソッドからのすべての経路でデータベースリソースのクローズが失敗すると性能低下になることがあります。 データベースとの通信で問題があるアプリケーションの原因になる可能性があります。
ISC: static メソッドだけを提供するクラスの不必要なインスタンス化 (ISC_INSTANTIATE_STATIC_CLASS)¶
このクラスは, static
メソッドだけを提供するクラスのオブジェクトを作成しています。
このオブジェクトは作成する必要はありません。修飾子として直接クラス名を使用する static
メソッドにアクセスしてください。
DMI: Random オブジェクトが作成され1度しか使われない (DMI_RANDOM_USED_ONLY_ONCE)¶
このコードは java.util.Random
オブジェクトを作成して1つの乱数を生成するために使用して捨てています。
これはあまり良くない品質の乱数を作り出し,効率が悪いです。
できれば, Random
オブジェクトを1つだけ作成して保存されるようにコードを書き直してください。
そして,毎回新しい乱数は既存の Random
オブジェクトを呼び出して取得することが必要です。
生成された乱数が推測可能ではないことが重要なら,乱数ごとに新しい Random
オブジェクトを作成してはいけません (値はあまりに簡単に推測可能です)。
その代わりに java.security.SecureRandom
の使用を強く検討すべきです (そして必要とされる乱数ごとに新しい SecureRandom
のオブジェクトを作成することを回避します)。
BC: equals メソッドは引数の型を仮定すべきではない (BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS)¶
equals(Object o)
メソッドは, o
の型についてどんな仮定もするべきではありません。
o
が this
と同じ型ではないなら単に false
を返すべきです。
J2EE: HttpSession への非直列化可能オブジェクトの格納 (J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION)¶
このコードは HttpSession
に非直列化可能オブジェクトを格納していると思われます。
このセッションが不活性化されるか移行したなら,エラーを招きます。
GC: 検査されない型への総称呼び出し (GC_UNCHECKED_TYPE_IN_GENERIC_CALL)¶
総称型パラメータからの特定の型が予想される Object
型をコンパイルするとき,総称型コレクションメソッドへの呼び出しは引数を渡します。
したがって,標準の Java 型システムも静的解析もパラメータとして渡されているオブジェクトが適切な型かどうかに関する有用な情報を提供できません。
PZ: 繰り返しでエントリオブジェクトを再利用しない (PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS)¶
このクラスは, Iterator
と Map.Entry
で基底 Map
のビューを返すことを許可された両方の entrySet
メソッドがあります。
この巧妙なアイデアは, Map
実装で使用されましたが,厄介なコーディングミスの可能性を取り込みました。
Map m
が entrySet
のためのそのような反復子を返すならば, c.addAll(m.entrySet())
はひどく間違っているでしょう。
OpenJDK 1.7 の すべての Map
実装はこれを回避するために書き直されました。
DMI: エントリセットの要素を加えることは,Entry オブジェクトの再利用のために失敗するかもしれない (DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS)¶
entrySet
メソッドは,一つの Entry
オブジェクトを再利用し,反復中に返される基底 Map
のビューを返すことが許可されています。
Java 1.6 の時点で, IdentityHashMap
と EnumMap
の両方がそうしました。
そのような Map
を通して繰り返すとき,エントリ値は次の繰り返しへ進むまでが有効です。
たとえば, addAll
メソッドにそのような entrySet
を渡そうと試みるのは,ひどく間違っているでしょう。
DMI: コレクションを消去するために removeAll メソッドを使用しない (DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION)¶
コレクション c
からすべての要素を除去したいなら, c.removeAll(c)
ではなく c.clear
を使用してください。
コレクションを消去するために c.removeAll(c)
を呼び出すことは,それほど明確ではなく,タイプミスからの誤りに影響されやすく,効率的ではなく,いくつかのコレクションでは, ConcurrentModificationException
をスローするかもしれません。
正確性 (CORRECTNESS)¶
バグの可能性 - おそらく,開発者が意図していなかったコードになっている明らかなコーディングミスです。 我々は低い誤検出率のために努力します。
NP: Optional の戻り型を持つメソッドが明示的に null を返す (NP_OPTIONAL_RETURN_NULL)¶
Optional
の戻り型 (java.util.Optional
または com.google.common.base.Optional
) の使い方で明示的に null
を返すのは設計が望ましくないことを意味します。
null
値をこのようなケースで返すことは契約違反で,多分クライアントコードを破壊するでしょう。
NP: 非 null フィールドは初期化されていない (NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR)¶
フィールドは,非 null
としてマークされていますが,コンストラクタで書き込まれていません。
フィールドは,コンストラクタの間,ほかの場所で初期化されるか,または使用する前に常に初期化されるかもしれません。
VR: 解決できないクラス,メソッドへの参照 (VR_UNRESOLVABLE_REFERENCE)¶
このクラスは,解析されているライブラリに対して解決されないクラスまたはメソッドを参照しています。
IO: オブジェクト出力ストリームへの追加は失敗に終わる (IO_APPENDING_TO_OBJECT_OUTPUT_STREAM)¶
このコードはファイルを追加モードで開いて,オブジェクト出力ストリームの中で結果をラップしています。 これはファイルに格納された既存のオブジェクト出力ストリームに追加できないでしょう。 オブジェクト出力ストリームに追加したいなら,オブジェクト出力ストリームを開いておく必要があります。
追加モードでファイルを開き,オブジェクト出力ストリームで書き込むことができる唯一の状況は, ファイルを読み出すときにランダムアクセスモードで開き,追加を開始するところまでバイトオフセットをシークすると計画した場合です。
IL: 明らかな無限再帰ループ (IL_INFINITE_RECURSIVE_LOOP)¶
このメソッドは,無条件で自分自身を呼び出します。これはスタックオーバーフローになる無限再帰ループを示しています。
IL: コレクションは自分自身を追加している (IL_CONTAINER_ADDED_TO_ITSELF)¶
コレクションは,自分自身を追加しています。その結果,hashCode を計算すると StackOverflowException
をスローします。
RpC: 条件テストの繰り返し (RpC_REPEATED_CONDITIONAL_TEST)¶
このコードには条件テストが2回,つまり,1つめの条件テストが正しいとき,2つめの条件テストが実行されます (たとえば, x == 0 || x == 0
)。
多分,2つめの条件テストは何か他のことを意図しています (たとえば, x == 0 || y == 0
)。
FL: 浮動小数点精度を使用した計算をしている (FL_MATH_USING_FLOAT_PRECISION)¶
このメソッドは,浮動小数点精度を使用して計算をしています。浮動小数点精度は非常に不正確です。
たとえば, 16777216.0f + 1.0f = 16777216.0f
。
その代わりに double
の使用を検討してください。
CAA: おそら互換性のない要素をく共変配列に格納している (CAA_COVARIANT_ARRAY_ELEMENT_STORE)¶
配列に格納していてる値の型が配列型と一致していません。
実際の配列型が宣言された変数またはフィールドの型よりも狭くなっていて,この割り当てがオリジナルの配列型を満たしていないことが解析でわかっています。
この割り当ては実行時に ArrayStoreException
を引き起こすことがあります。
Dm: EasyMock メソッドへの役に立たない/無意味な呼び出し (DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD)¶
この呼び出しは EasyMock メソッドにどんなオブジェクトも渡さないので何もしません。
Dm: ScheduledThreadPoolExecutor の最大プールサイズを変えようとする無駄な試み (DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR)¶
ScheduledThreadPoolExecutor
は ThreadPoolExecutor
から継承されますが継承されたチューニングメソッドの一部は有用ではありません。
特に,corePoolSize スレッドとアンバウンド形式のキューを使用する固定サイズプールとして動作するので,maximumPoolSize の調整は有用な効果がありません。
(Javadoc)
DMI: 正確に表されない double から構築された BigDecimal (DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE)¶
このコードは10進数の数にうまく変換されない double
値から BigDecimal
を作成しています。
たとえば,Java で new BigDecimal(0.1)
と書くと,0.1と正確に等しい BigDecimal
(スケールが1でスケールなしの値が1) が作成されると思うかもしれませんが,
実際には0.1000000000000000055511151231257827021181583404541015625と等しくなります。
おそらく BigDecimal.valueOf(double d)
メソッドの使用が望ましいです。BigDecimal
(たとえば, BigDecimal.valueOf(0.1)
は0.1を与えます) を作成するためには double
の文字列表現を使用します。
Dm: コアプールサイズが0の ScheduledThreadPoolExecutor の作成 (DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS)¶
コアプールサイズが0の ScheduledThreadPoolExecutor
は決して何も実行しません。
最大プールサイズへの変更は無視されます。
(Javadoc)
Dm: ランタイムリテンションなしで,アノテーションの存在を調べるためにリフレクションを使用することはできない (DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION)¶
アノテーションに @Retention(RetentionPolicy.RUNTIME)
アノテーションが付いていなければ,リフレクション (たとえば, isAnnotationPresent(...)
メソッド) を使用して観測することができません。
NP: null の引数をチェックしていないメソッド (NP_ARGUMENT_MIGHT_BE_NULL)¶
このメソッドへのパラメータが null
かどうか確かめるために常にチェックされるべき値として特定されました。
しかし, null
チェックをしないで, null
値が利用されています。
RV: 符号付き整数の乱数の絶対値を計算する間違った試み (RV_ABSOLUTE_VALUE_OF_RANDOM_INT)¶
このコードは符号付き整数の乱数を生成して絶対値を計算しています。
乱数ジェネレータで返される数が Integer.MIN_VALUE
なら結果は同様に負です (Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE
なので)。
(同じ問題は long
値でも同様に起きます)。
RV: 符号付き32ビットハッシュコードの絶対値を計算する間違った試み (RV_ABSOLUTE_VALUE_OF_HASHCODE)¶
このコードはハッシュコードを生成して絶対値を計算しています。
ハッシュコードが Integer.MIN_VALUE
なら結果は同様に負です (Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE
なので)。
文字列の2^32個に1個は Integer.MIN_VALUE
のハッシュコードを持っていて,「polygenelubricants」,「GydZG_」,「DESIGNING WORKHOUSES」が該当します。
RV: 0から1の乱数値は整数値0に丸められる (RV_01_TO_INT)¶
0から1の乱数値は整数値0に丸められます。
おそらく整数に丸められる前に何か他のことによって乱数値を乗算したかったか,または Random.nextInt(n)
メソッドを使いたかったのでしょう。
Dm: Math.max と Math.min の間違った組み合わせ (DM_INVALID_MIN_MAX)¶
このコードは Math.min(0, Math.max(100, value))
のような構文を使用して境界値を制限しようとしています。
しかしながら,定数の順序が間違っています。 Math.min(100, Math.max(0, value))
とすべきです。
結果としてこのコードは常に同じ結果 (もし値が NaN
なら NaN
) を作り出します。
Eq: equals メソッドはクラスオブジェクトではなくクラス名を比較している (EQ_COMPARING_CLASS_NAMES)¶
このメソッドは,クラス名を比較することによって,2つのオブジェクトが同じクラスなのか確かめています。 異なるクラスローダによってロードされたクラスなら,同じ名前で異なるクラスがある可能性があります。 クラスオブジェクトが同じなのか確かめてください。
Eq: equals メソッドは常に true を返す (EQ_ALWAYS_TRUE)¶
このクラスは,常に true
を返す equals
メソッドを定義しています。
これは想像力に富むが,あまり良い方法とはいえません。さらに, equals
メソッドが対称的ではないことを意味します。
Eq: equals メソッドは常に false を返す (EQ_ALWAYS_FALSE)¶
このクラスでは,常に false
を返す equlas
メソッドを定義しています。
これはオブジェクトがそれ自身と等価ではないことを意味していて,このクラスの有用な Map
や Set
を作成できません。
より根本的に, equals
メソッドの要件の一つである反射性を満たしていないことになります。
おそらく意図されたことは,オブジェクトはそれ自身と等価であるというオブジェクト同一性です。
これは Object
クラスから継承される振る舞いです。
異なるスーパークラスから継承される equals
メソッドをオーバーライドする必要があるなら次のようなコードが使えます。
public boolean equals(Object o) {
return this == o;
}
Eq: equals メソッドはスーパークラスの equals メソッドをオーバーライドしているが,対称的ではないかもしれない (EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC)¶
このクラスはスーパークラスの equals
メソッドをオーバーライドする equals
メソッドを定義しています。
両方の equals
メソッドは,2つのオブジェクトが等しいかどうかの判定で, instanceof
を使用しています。
equals
メソッドは対称的 (a.equals(b) == b.equals(a)
) であることが重要なのでこれは危険を伴っています。
B が A のサブタイプなら A の equals
メソッドは引数が instanceof A
なのかチェックします。
そして, B の equals
メソッドは引数が instanceof B
なのかチェックします。
これらのメソッドによって定義された同値関係が対称的ではないということです。
Eq: 列挙型は共変な equals メソッドを定義している (EQ_DONT_DEFINE_EQUALS_FOR_ENUM)¶
このクラスは列挙を定義していて,列挙の等価性はオブジェクト同一性を使用して定義されています。
列挙値のために共変な equals
メソッドを定義することは,ひどいバッドプラクティスです。
2つの異なる列挙値が equals
メソッドでは「等価ではない」と判定され,共変な equals
メソッドでは「等価」と判定されるからです。
共変な equals
メソッドを定義しないでください。
Eq: 共変な equals メソッドを定義して,Object.equals(Object) を継承している (EQ_SELF_USE_OBJECT)¶
このクラスは,共変な equals
メソッドを定義していますが, equals(Object)
メソッドは java.lang.Object
クラスから継承しています。
クラスは, boolean equals(Object)
メソッドを定義すべきです。
Eq: Object.equals(Object) をオーバーライドしていない equals メソッドの定義 (EQ_OTHER_USE_OBJECT)¶
このクラスは, equals
メソッドを定義していますが, java.lang.Object
クラスの equals(Object)
メソッドをオーバーライドしていません。
クラスは, boolean equals(Object)
メソッドを定義すべきです。
Eq: equals(Object) メソッドをオーバーライドしていない equals メソッドの定義 (EQ_OTHER_NO_OBJECT)¶
このクラスは, equals
メソッドを定義していますが, java.lang.Object
クラスの equals(Object)
メソッドをオーバーライドしていません。
その代わりに,スーパークラスから equals(Object)
メソッドを継承して, boolean equals(Object)
メソッドを定義すべきです。
HE: ハッシュ化されたコンテキストでハッシュ化できないクラスの使用がシグネチャで宣言されている (HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS)¶
メソッド,フィールド,クラスは,ハッシュ可能なクラスが必要なコンテキストで,ハッシュ化できないクラスが使用される総称的なシグネチャを宣言しています。
クラスは, equals
メソッドを宣言していますが, hashCode
メソッドは java.lang.Object
から継承しています。
これは「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に従っていないのでハッシュ化できません。
HE: ハッシュデータ構造で hashCode メソッドのないクラスを使用している (HE_USE_OF_UNHASHABLE_CLASS)¶
このクラスは, equals(Object)
メソッドを定義していますが, hashCode
メソッドを定義していません。
これは「等価なオブジェクトは等価なハッシュコードを保持する必要がある」という hashCode
メソッドの汎用規約に従っていません。
このクラスのインスタンスはハッシュデータ構造で使われています。最重要問題を修正する必要があります。
UR: コンストラクタで初期化されていないフィールドを読み出している (UR_UNINIT_READ)¶
このコンストラクタは,まだ値が代入されていないフィールドを読み出しています。 多くの場合,プログラマがコンストラクタのパラメータの代わりに誤ってフィールドを使うときに起きます。
UR: スーパークラスのコンストラクタから呼び出されるメソッドで初期化されていないフィールドを読み出している (UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR)¶
このメソッドは,スーパークラスのコンストラクタで呼びされています。この時点では,クラスのフィールドはまだ初期化されていません。
これはたくさんの具象クラスを作るためです。次のクラスを検討してください。
abstract class A {
int hashCode;
abstract Object getValue();
A() {
hashCode = getValue().hashCode();
}
}
class B extends A {
Object value;
B(Object v) {
this.value = v;
}
Object getValue() {
return value;
}
}
B
が構築されるとき, B
のコンストラクタが value
に値を設定する前に, A
クラスのコンストラクタが呼び出されます。
したがって, A
のコンストラクタが getValue
を呼び出すとき, value
の初期化されていない値が読み出されます。
Nm: 非常に紛らわしい名前のメソッド (NM_VERY_CONFUSING)¶
参照されたメソッドは,大文字と小文字だけが異なる名前があります。 大文字と小文字が同一ならメソッドの1つが他のメソッドをオーバーライドするので,非常に紛らわしいです。
Nm: パラメータの間違ったパッケージのためにスーパークラスのメソッドをオーバーライドしていないメソッド (NM_WRONG_PACKAGE)¶
パラメータの型がスーパークラスで対応するパラメータの型と正確に合致していないので,サブクラスのメソッドはスーパークラスの類似したメソッドをオーバーライドしていません。
たとえば次のようなコードです。
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
が違うパッケージだからです。
Nm: 明らかなメソッドとコンストラクタの混乱 (NM_METHOD_CONSTRUCTOR_CONFUSION)¶
この正規のメソッドは定義しているクラスと同じ名前です。
これはコンストラクタを意図していた可能性が高いです。もしそうなら void
戻り値の宣言を除去してください。
誤ってメソッドを定義したことが間違いだと気付き,適切なコンストラクタを定義したが,後方互換性のためにこのメソッドを除去できないならメソッドを非推奨にしてください。
Nm: クラスは hashcode() を定義しています。hashCode() にすべきですか? (NM_LCASE_HASHCODE)¶
このクラスは, hashcode()
という名前のメソッドを定義しています。
このメソッドは, java.lang.Object
の hashCode
メソッドを (おそらく意図的に) オーバーライドしていません。
Nm: クラスは tostring() を定義しています。toString() にすべきですか? (NM_LCASE_TOSTRING)¶
このクラスは, tostring()
という名前のメソッドを定義しています。
このメソッドは, java.lang.Object
の toString
メソッドを (おそらく意図的に) オーバーライドしていません。
Nm: クラスは equal(Object) を定義しています。equals(Object) にすべきですか? (NM_BAD_EQUAL)¶
このクラスは, equal(Object)
という名前のメソッドを定義しています。
このメソッドは, java.lang.Object
の equals(Object)
を (おそらく意図的に) オーバーライドしていません。
Se: readResolve メソッドが static メソッドとして宣言されている (SE_READ_RESOLVE_IS_STATIC)¶
readResolve
メソッドが直列化機構で認識されるためには static
メソッドとして宣言してはいけません。
Se: 直列化機構のために private にしなければならないメソッド (SE_METHOD_MUST_BE_PRIVATE)¶
このクラスは, Serializable
インタフェースを実装して,カスタム直列化/直列化復元のためのメソッドを定義しています。
しかし,そのメソッドが private
として宣言されていないので,直列化/直列化復元 API によって無視されます。
SF: switch 文のフォールスルーのために格納が無効になっている (SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH)¶
前の case
で格納された値が switch
文のフォールスルーのためにここで上書きされています。
前の case
の終わりに break
または return
を入れるのを忘れた可能性があります。
SF: スローする switch 文のフォールスルーのために格納が無効になっている (SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW)¶
前の case
で格納された値が例外がスローされる場所で, switch
文のフォールスルーのためにここで失われています。
前の case
の終わりに break
または return
を入れるのを忘れた可能性があります。
NP: 書き込まれていないフィールドの読み出し (NP_UNWRITTEN_FIELD)¶
プログラムは,決して null
ではない値を書き込むとは思われないフィールドの値を利用しています。
フィールドが解析によって見られないメカニズムを通して初期化されないかぎり,この値を利用すると NullPointerException
が発生します。
UwF: null に設定されるだけのフィールド (UWF_NULL_FIELD)¶
このフィールドに定数値 null
を書き込みます。したがって,フィールドの読み出しは null
を返します。
誤りをチェックしてください。役に立たないなら除去してください。
UwF: 書き込まれていないフィールド (UWF_UNWRITTEN_FIELD)¶
このフィールドは決して書き込まれません。このフィールドからの読み出しはデフォルト値を返します。 誤りをチェックしてください (フィールドは初期化すべきでしたか?)。役に立たないなら除去してください。
SIC: 非 static 内部クラスとスレッドローカルのデッドロック (SIC_THREADLOCAL_DEADLY_EMBRACE)¶
このクラスは内部クラスですが,おそらく static
内部クラスにすべきです。
実際には内部クラスと外部クラスのスレッドローカルとの間にデッドロックの深刻な危険性があります。
内部クラスが static
ではないので,外部クラスへの参照を保持します。
スレッドローカルに内部クラスのインスタンスの参照があるなら,内部と外部のインスタンスの両方が到達可能になり,ガベージされません。
RANGE: 配列インデックスは範囲外 (RANGE_ARRAY_INDEX)¶
配列操作が行なわれていますが,配列インデックスが範囲外なので実行時に ArrayIndexOutOfBoundsException
が発生するでしょう。
RANGE: 配列オフセットは範囲外 (RANGE_ARRAY_OFFSET)¶
メソッドは,配列パラメータとオフセットパラメータで呼び出されていますが,オフセットは範囲外です。
実行時に IndexOutOfBoundsException
が発生するでしょう。
RANGE: 配列の長さは範囲外 (RANGE_ARRAY_LENGTH)¶
メソッドは,配列パラメータと長さパラメータで呼び出されていますが,長さは範囲外です。
実行時に IndexOutOfBoundsException
が発生するでしょう。
RANGE: 文字列インデックスは範囲外 (RANGE_STRING_INDEX)¶
文字列メソッドが呼び出されていますが,指定された文字列インデックスは範囲外です。
実行時に StringIndexOutOfBoundsException
が発生するでしょう。
RV: 戻り値を無視しているメソッド (RV_RETURN_VALUE_IGNORED)¶
このメソッドの戻り値はチェックすべきです。
この警告の共通の原因は,オブジェクトが更新されると思って不変オブジェクトのメソッドを呼び出すことです。
たとえば次のようなコードです。
String dateString = getHeaderField(name);
dateString.trim();
プログラマは, trim
メソッドが dateString
によって参照される String
オブジェクトが更新されると思っています。
しかし, String
オブジェクトは不変で, trim
メソッドが新しい String
オブジェクトを返すのに無視しています。
このコードは次のように修正すべきです。
String dateString = getHeaderField(name);
dateString = dateString.trim();
RV: 作成した例外をスローするのではなく捨てている (RV_EXCEPTION_NOT_THROWN)¶
このコードは例外 (またはエラー) オブジェクトを作成していますが,何もしていません。
たとえば次のようなコードです。
if (x < 0) {
new IllegalArgumentException("x must be nonnegative");
}
おそらくプログラマの意図は,作成した例外をスローすることでした。
if (x < 0) {
throw new IllegalArgumentException("x must be nonnegative");
}
RV: compareTo によって返された特定の値のコードチェック (RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE)¶
このコードは compareTo
または compare
メソッドを呼び出して,戻り値が特定の値(たとえば1または-1) なのか確かめています。
これらのメソッドを呼び出すときは特定のゼロ以外の値ではなく,結果の符号だけをチェックすべきです。
多数または大部分の compareTo
と比較メソッドは-1,0または1を返しますが,いくつかは他の値を返します。
NP: 常に null 値のオブジェクトで close メソッドを呼び出している (NP_CLOSING_NULL)¶
close
メソッドは,常に null
値のオブジェクトで呼び出されています。
この文が実行されるなら NullPointerException
が発生します。
ここでクローズすべき何かを決してクローズしないという大きな危険性があります。
NP: @Nonnull アノテーションが付けられたフィールドに null を格納している (NP_STORE_INTO_NONNULL_FIELD)¶
@Nonnull
アノテーションが付けられたフィールドに null
かもしれない値を格納しています。
NP: null 値を例外経路で利用している (NP_ALWAYS_NULL_EXCEPTION)¶
例外経路上のここで null
値を利用しています。コードが実行されると NullPointerException
が発生します。
現在の SpotBugs は実行不可能な例外経路を刈り取れていないので,誤検出かもしれないことに注意してください。
switch
文の default
が多くの場合実行不可能なので SpotBugs が例外経路である default
を検討することに注意して下さい。
NP: null 値を利用している可能性がある (NP_NULL_ON_SOME_PATH)¶
そこで分岐または文が実行されるなら null
値が利用されて NullPointerException
が発生します。
もちろん,問題は分岐または文が実行不可能で, NullPointerException
が決して発生する可能性がないということかもしれません。
それを決めるのは SpotBugs の能力を超えています。
NP: null 値を例外経路で利用している可能性がある (NP_NULL_ON_SOME_PATH_EXCEPTION)¶
例外経路上のここで null
値が利用されています。コードが実行されると NullPointerException
を引き起こすことがあります。
現在の SpotBugs は実行不可能な例外経路を刈り取れていないので,誤検出かもしれないことに注意してください。
switch
文の default
が多くの場合実行不可能なので SpotBugs が例外経路である default
を検討することに注意して下さい。
NP: メソッド呼び出しは非 null パラメータに null を渡している (NP_NULL_PARAM_DEREF)¶
このメソッド呼び出しは非 null
メソッドパラメータに null
値を渡しています。
パラメータは,常に非 null
とすべきパラメータとしてアノテーションが付けられていたか,解析が常に null
値を利用することを示していました。
NP: 非 null パラメータに null を渡している非仮想メソッドの呼び出し (NP_NULL_PARAM_DEREF_NONVIRTUAL)¶
null
の可能性がある値が 非 null
メソッドパラメータに渡されています。
パラメータは,常に非 null
とすべきパラメータとしてアノテーションが付けられていたか,解析が常に null
値を利用することを示していました。
NP: メソッド呼び出しは非 null パラメータに null を渡している (NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS)¶
すべての既知のターゲットメソッドが非 null
であることをパラメータに要求する呼び出し場所で,おそらく null
値を渡しています。
パラメータは,常に非 null
とすべきパラメータとしてアノテーションが付けられていたか,解析が常に null
値を利用することを示していました。
NP: メソッド呼び出しは非 null パラメータに null を渡している (NP_NONNULL_PARAM_VIOLATION)¶
このメソッドは,非 null
でなければならないメソッドのパラメータとして null
値を渡しています。
このパラメータは,明示的に @Nonnull
アノテーションが付けられていたか,解析が常に null
値を利用することを示していました。
NP: null を返すかもしれないメソッドが @Nonnull 宣言されている (NP_NONNULL_RETURN_VIOLATION)¶
このメソッドは, null
値を返すかもしれないのにメソッド (またはスーパークラスのメソッド) の戻り値に @Nonnull
が宣言されています。
NP: null 値を利用することが保証されている (NP_GUARANTEED_DEREF)¶
文または分岐が実行されるなら,この時点で値は null
であり, null
値を利用する ことが保証されています (実行時例外を含むフォワードパスを除く)。
なお, if (x == null) throw new NullPointerException();
は x
の参照解除として扱われることに注意して下さい。
NP: null 値を例外経路で利用することが保証されている (NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH)¶
例外経路上の文または分岐が実行されるなら,この時点で値は null
であり, null
値を利用することが保証されています (実行時例外を含むフォワードパスを除く)。
DMI: 逆にされたメソッド引数 (DMI_ARGUMENTS_WRONG_ORDER)¶
このメソッド呼び出しへの引数は,順序が間違っているように見えます。
たとえば,呼び出し Preconditions.checkNotNull("message", message)
は,引数を予約しました。チェックされる値は第一引数です。
RCN: 既に利用していた値の null チェック (RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE)¶
ここで値が null
なのかチェックしていますが,既に値を利用していたので null
である可能性はありません。
値が null
なら以前の利用で NullPointerException
が発生していたでしょう。
基本的に,値が null
であることを許すのかどうかに関係なく,このコードと以前の値の利用は一致しません。
チェックは冗長か,または以前の値の利用は誤りです。
RC: 疑わしい参照比較 (RC_REF_COMPARISON)¶
このメソッドは, ==
または !=
演算子を使用して2つの参照値を比較しています。
一般的にこの型のインスタンスを比較する正しい方法は equals
メソッドです。
等価で識別可能なインスタンスを作成する可能性がありますが異なるオブジェクトなので ==
で比較しないでください。
一般的に参照によって比較されるべきではないクラスの例は, java.lang.Integer
, java.lang.Float
などです。
VA: 可変長引数を期待しているメソッドにプリミティブ型の配列を渡している (VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG)¶
このコードは可変長引数をとるメソッドにプリミティブ型の配列を渡しています。 これはプリミティブ型の配列を保持するために長さが1の配列を作成してメソッドに渡します。
EC: equals メソッドを使用して配列と非配列を比較している (EC_ARRAY_AND_NONARRAY)¶
このメソッドは,配列と配列だと思われない参照を比較するために .equals(Object o)
を呼び出しています。
比較されているものが違う型なら等しくないことであることが保証されているので,比較はほぼ間違いなく誤りです。
たとえそれらが両方とも配列だったとしても,配列の equals
メソッドは2つの配列が同じオブジェクトだと決定するだけです。
配列の内容を比較するためには java.util.Arrays.equals(Object[], Object[])
を使用してください。
EC: equals(null) の呼び出し (EC_NULL_ARG)¶
このメソッドは, null
値の引数を渡して equals(Object)
を呼び出しています。
equals
メソッドの規約によると,この呼び出しは常に false
を返すはずです。
SA: フィールドへの代入ではなくローカル変数への自己代入 (SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD)¶
このメソッドにはローカル変数の自己代入があり,ローカル変数とフィールドが同じ名前です。
たとえば次のようなコードです。
int foo;
public void setFoo(int foo) {
foo = foo;
}
そのような代入は役に立ちません。そうではなく,フィールドに代入するつもりでしたか?
INT: int 値と long 定数との間違った比較 (INT_BAD_COMPARISON_WITH_INT_VALUE)¶
このコードはint 値と int 値として表される値の範囲外の long 定数を比較しています。 この比較は無意味で,おそらく間違っています。
INT: 符号付きバイトの間違った比較 (INT_BAD_COMPARISON_WITH_SIGNED_BYTE)¶
符号付バイトのとりうる値の範囲は-128~127です。その範囲外で符号付バイトを値と比較することは無意味で間違っていそうです。
符号付きバイト b
を範囲が0~255の符号なしバイトに変換するには 0xff & b
を使用してください。
INT: 負ではない値と負の定数またはゼロとの間違った比較 (INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE)¶
このコードは負ではないことが保証されている値と負の定数またはゼロとを比較しています。
BIT: 符号付きバイト値のビット加算 (BIT_ADD_OF_SIGNED_BYTE)¶
バイト値と明らかに下位8ビットがあるとわかっている値を加算しています。
ビット演算を実行する前にバイト配列からロードされた値は32ビットまで符号拡張されます。
したがって, b[0]
の値が 0xff
で, x
の初期値が 0
だとすると,
((x << 8) + b[0])
は, 0xff
が符号拡張で 0xffffffff
になるので,結果として 0xffffffff
が得られます。
特に,バイト配列を int
にパックする次のようなコードはひどく間違っています。
int result = 0;
for(int i = 0; i < 4; i++)
result = ((result << 8) + b[i]);
その代わりに次のようなイディオムは動作します。
int result = 0;
for(int i = 0; i < 4; i++)
result = ((result << 8) + (b[i] & 0xff));
BIT: 符号付きバイト値のビット論理和 (BIT_IOR_OF_SIGNED_BYTE)¶
ロードしたバイト値 (たとえば,バイト配列からロードされた値や戻り値がバイト型のメソッドから返された値) とビット論理和を実行しています。
ビット演算を実行する前にバイト値は32ビットまで符号拡張されます。
したがって, b[0]
の値が 0xff
で, x
の初期値が 0
だとすると,
((x << 8) | b[0])
は, 0xff
が符号拡張で 0xffffffff
になるので,結果として 0xffffffff
が得られます。
特に,バイト配列を int
にパックする次のようなコードはひどく間違っています。
int result = 0;
for(int i = 0; i < 4; i++) {
result = ((result << 8) | b[i]);
}
その代わりに次のようなイディオムは動作します。
int result = 0;
for(int i = 0; i < 4; i++) {
result = ((result << 8) | (b[i] & 0xff));
}
BIT: 負数を含むビット演算の符号をチェックする (BIT_SIGNED_CHECK_HIGH_BIT)¶
このメソッドは, CONSTANT
が負数のときに ((val & CONSTANT) > 0)
のようなビット演算式で比較しています。
ビット演算をより大きい演算子で比較することは,予想外の結果の原因になる可能性があります。比較は期待したようになりません。
> 0
の代わりに != 0
を使用することが良いプラクティスです。
BIT: 互換性のないビットマスク (BIT_AND)¶
このメソッドは, (e & C)
形式の式を D
と比較しています。
定数 C
の特定の値と D
ために常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
BIT: ((...) & 0) == 0 なのか確かめている (BIT_AND_ZZ)¶
このメソッドは, (e & 0)
形式の式を0と比較しています。それは,常に等価であることを比較します。論理エラーかタイプミスかもしれません。
BIT: 互換性のないビットマスク (BIT_IOR)¶
このメソッドは, (e | C)
形式の式を D
と比較しています。
定数 C
と D
の特定の値のために常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
通常,このバグは,ビットセットで帰属関係のテストを実行したいコードで発生します。
しかし,ビット論理積演算子 (&
) の代わりにビット論理和演算子 (|
) を使用しています。
こうしたバグは (e & (A | B)) == C
が意図されている間に ((e & A) | B) == C
のように解析される (e & A | B) == C
のような式で現れるかもしれません。
SA: フィールドの自己代入 (SA_FIELD_SELF_ASSIGNMENT)¶
このメソッドにはフィールドの自己代入があります。
たとえば次のようなコードです。
int x;
public void foo() {
x = x;
}
そのような代入は役に立たないので,論理エラーかタイプミスかもしれません。
SA: フィールドの無意味な自己演算 (たとえば,x & x) (SA_FIELD_SELF_COMPUTATION)¶
このメソッドは,フィールドと同じフィールドへの別の参照との無意味な計算を実行しています (たとえば, x & x
または x - x
)。
この計算の性質のため,演算は意味をなすとは思われないので,論理エラーかタイプミスかもしれません。
計算をチェックしてください。
SA: 変数の無意味な自己演算 (たとえば,x & x) (SA_LOCAL_SELF_COMPUTATION)¶
このメソッドは,ローカル変数と同じ変数への別の参照との無意味な計算を実行しています (たとえば, x & x
または x - x
)。
この計算の性質のため,演算は意味をなすとは思われないので,論理エラーかタイプミスかもしれません。
計算をダブルチェックしてください。
SA: フィールドとそれ自身との自己比較 (SA_FIELD_SELF_COMPARISON)¶
このメソッドは,フィールドをそれ自身と比較しています。論理エラーかタイプミスかもしれません。 正しいものを比較していることを確認してください。
SA: ローカル変数とそれ自身との自己比較 (SA_LOCAL_SELF_COMPARISON)¶
このメソッドは,ローカル変数をそれ自身と比較しています。論理エラーかタイプミスかもしれません。 正しいものを比較していることを確認してください。
UMAC: 呼び出し不可能なメソッドが無名クラスで定義されている (UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS)¶
この無名クラスは,直接呼び出されないスーパークラスのメソッドをオーバーライドしていないメソッドを定義しています。 他のクラスのメソッドが無名クラスで宣言されたメソッドを直接呼び出せないので,このメソッドは呼び出し不可能だと思われます。 メソッドは単にデッドコードであるかもしれません。しかし,メソッドがスーパークラスで宣言されるメソッドをオーバーライドすることを意図した可能性もあります。 そして,タイプミスまたは他の誤りのためにメソッドは,実際には意図しているメソッドをオーバーライドしません。
IJU: run メソッドでの JUnit アサーションは JUnit によって通知されない (IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD)¶
run
メソッドで JUnit アサーションが実行されています。失敗した JUnit アサーションは例外をスローします。
したがって,この例外がテストメソッドを実行したスレッド以外のスレッドで発生するなら,例外はスレッドを終了させますが,テストの失敗になりません。
IJU: TestCase は suite メソッドの間違った宣言をしている (IJU_BAD_SUITE_METHOD)¶
JUnit の TestCase
クラスで, suite
メソッドを実装しています。
しかしながら, suite
メソッドは,
public static junit.framework.Test suite()
か
public static junit.framework.TestSuite suite()
のどちらかを宣言する必要があります。
IJU: TestCase は super.setup() を呼び出さない setUp メソッドを実装している (IJU_SETUP_NO_SUPER)¶
JUnit の TestCase
クラスで, setUp
メソッドを実装しています。
setUp
メソッドは, super.setUp()
を呼び出すべきなのにそうしていません。
IJU: TestCase は super.tearDown() を呼び出さない tearDown メソッドを実装している (IJU_TEARDOWN_NO_SUPER)¶
JUnit の TestCase
クラスで, tearDown
メソッドを実装しています。
tearDown
メソッドは, super.tearDown()
を呼び出すべきなのにそうしていません。
IJU: TestCase は 非 static な suite メソッドを実装している (IJU_SUITE_NOT_STATIC)¶
JUnit の TestCase
クラスで, suite
メソッドを実装しています。
suite
メソッドは static
として宣言すべきなのにそうしていません。
BOA: スーパークラスの Adapter で実装されるメソッドを誤ってオーバーライドしているクラス (BOA_BADLY_OVERRIDDEN_ADAPTER)¶
このメソッドは,スーパークラスで実装されているメソッドをオーバーライドしています。
スーパークラスは,java.awt.event や javax.swing.event パッケージで定義されているリスナを実装する Adapter
です。
その結果,イベントが発生するときこのメソッドは呼び出されません。
SQL: インデックスが0で ResultSet にアクセスしようとしているメソッド (SQL_BAD_RESULTSET_ACCESS)¶
インデックスが0で, ResultSet
の getXXX
, updateXXX
メソッドを呼び出しています。
ResultSet
のインデックスは1から開始するので,これは常に間違いです。
SQL: インデックスが0で PreparedStatement にアクセスしようとしているメソッド (SQL_BAD_PREPARED_STATEMENT_ACCESS)¶
インデックスが0で, PreparedStatement
の setXXX
メソッドを呼び出しています。
インデックスは1から開始するので,これは常に間違いです。
SIO: instanceof 演算子を使用した不必要な型チェック (SIO_SUPERFLUOUS_INSTANCEOF)¶
オブジェクトが要求する型であるかどうかにかかわらず,静的に判定される instanceof
演算子を使用して型チェックをしています。
BAC: 初期化されていない AppletStub に依存する間違ったアプレットコンストラクタ (BAC_BAD_APPLET_CONSTRUCTOR)¶
このコンストラクタは, AppletStub
に依存する親アプレットでメソッドを呼び出しています。
このアプレットの init
メソッドが呼び出されるまで AppletStub
は初期化されないので,これらのメソッドは正しく機能しません。
EC: equals(...) メソッドを使用して互換性のない配列を比較している (EC_INCOMPATIBLE_ARRAY_COMPARE)¶
このメソッドは,互換性のない型の配列を比較するために .equals(Object o)
を呼び出しています (たとえば, String[]
と StringBuffer[]
, String[]
と int[]
) 。
それらは,決して等価ではありません。
さらに, equals(...)
を使用してが配列を比較すると,同じ配列なのか確かめるだけで,配列の内容は無視されます。
EC: 配列の equals メソッド呼び出しは == と等価である (EC_BAD_ARRAY_COMPARE)¶
このメソッドは,配列で .equals(Object o)
を呼び出しています。
配列は, Object
の equals
メソッドをオーバーライドしないので,配列で equals
メソッドを呼び出すことはアドレスを比較することと同じです。
配列の内容を比較するためには java.util.Arrays.equals(Object[], Object[])
を使用してください。
配列のアドレスを比較するために明示的に ==
を使用して参照等価性をチェックすることは,それほど紛らわしくないでしょう。
STI: interrupted メソッドを呼び出すために不要な currentThread メソッドを呼び出している (STI_INTERRUPTED_ON_CURRENTTHREAD)¶
このメソッドは, interrupted
メソッドを呼び出すために Thread.currentThread()
を呼び出しています。
interrupted
メソッドは static
メソッドなので, Thread.interrupted()
を使用するほうが単純明解です。
STI: スレッドインスタンスで static Thread.interrupted() を呼び出している (STI_INTERRUPTED_ON_UNKNOWNTHREAD)¶
このメソッドは,カレントスレッドではない Thread
オブジェクトであるように見える Thread
オブジェクトで Thread.interrupted()
を呼び出しています。
interrupted
メソッドは static
なので,作成者が意図したこととは異なるオブジェクトで呼び出されます。
DLS: return 文に無駄なインクリメントがある (DLS_DEAD_LOCAL_INCREMENT_IN_RETURN)¶
return x++;
のような return
文があります。
接頭辞インクリメント/デクリメントは 式の値に影響を与えないので,インクリメント/デクリメントは効果がありません。
この文が正しいことを確かめてください。
DLS: クラスリテラルの無効な代入 (DLS_DEAD_STORE_OF_CLASS_LITERAL)¶
この命令は変数にクラスリテラルを代入していますが,決して使われません。
The behavior of this differs in Java 1.4 and in Java 5
J2SE 1.4 およびそれ以前のバージョンでは, Foo.class
への参照は Foo
のためのスタティックイニシャライザが既に実行されていないなら実行することを強制します。
J2SE 5.0 ではそうしません。
より多くの詳細と例と J2SE 5.0 のクラスの強制的な初期化の方法の提案は Sun の article on Java SE compatibility を参照してください。
IP: メソッドで読み取られずに上書きされているパラメータ (IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN)¶
このパラメータの初期値は無視され,ここで上書きされています。 これは多くの場合,パラメータへの書き込みが呼び出し元に戻されるという誤った考えを示しています。
MF: フィールドを隠す変数を定義しているメソッド (MF_METHOD_MASKS_FIELD)¶
このメソッドは,このクラスまたはスーパークラスのフィールドと同じ名前でローカル変数を定義しています。 フィールドから初期化されていない値を読み出す,初期化されていないフィールドをそのままにしておくか,または両方を引き起こすかもしれません。
MF: スーパークラスのフィールドを隠すフィールドを定義しているクラス (MF_CLASS_MASKS_FIELD)¶
このクラスは,スーパークラスの可視インスタンスフィールドと同じ名前でフィールドを定義しています。 これは紛らわしくて,メソッドがフィールドを更新するかアクセスするなら,間違いを指摘するかもしれません。
FE: NaN への等価性のための絶望的なテスト (FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER)¶
このコードは浮動小数点が特別な非数値と等価であるか確かめています (たとえば if (x == Double.NaN)
)。
しかしながら, NaN
の特別な意味のため,値は NaN
と等価ではありません。
したがって, x == Double.NaN
は常に false
と評価します。
x
という値が特別な非数値であるかどうか確かめるためには Double.isNaN(x)
を使用します (または x
が浮動小数点精度であるなら Float.isNaN(x)
)。
ICAST: int 値を long に変換して絶対時間として使用している (ICAST_INT_2_LONG_AS_INSTANT)¶
このコードは32ビット int 値を64ビット long 値に変換して,絶対時間値を必要とするメソッドパラメータに渡しています。
絶対時間値は,「エポック」(すなわち,1970年1月1日,00:00:00 GMT)としてわかっている標準的な基準時間からのミリ秒数です。
たとえば,エポックからの秒を Date
へ変換することを意図した次のメソッド はひどく壊れています。
Date getDate(int seconds) { return new Date(seconds * 1000); }
乗算は32ビット演算を使用して,64ビット値に変換されます。 32ビット値は,64ビットに変換されて,絶対時間値を表すために使用されるとき,1969年12月と1970年1月の日付しか表せません。
上記のメソッドの正しい実装は次のとおりです。
// 失敗,2037年後の日付
Date getDate(int seconds) { return new Date(seconds * 1000L); }
// より良い,すべての日付で動作する
Date getDate(long seconds) { return new Date(seconds * 1000); }
ICAST: 整数値を double にキャストして Math.ceil() に渡している (ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL)¶
このコードは整数値 (たとえば, int
や long
) を倍精度浮動小数点に変換してから,その結果を Math.ceil()
に渡しています。
整数を double
に変換すると小数部がない数値が得られるので,この演算は常にノーオペレーションになります。
Math.ceil()
に渡される値を生成した演算が倍精度浮動小数点演算を使用して実行することを意図した可能性が高いです。
ICAST: 整数値を float にキャストして Math.round() に渡している (ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND)¶
このコードは整数値を float
精度浮動小数点に変換してから,その結果を Math.round()
に渡して引数に最も近い int
/long
を返します。
整数を float
に変換すると小数部がない数値が得られるので,この演算は常にノーオペレーションになります。
Math.round()
に渡される値を生成した演算が浮動小数点演算を使用して実行することを意図した可能性が高いです。
NP: null とわかっている値をその型のインスタンスなのか確かめている (NP_NULL_INSTANCEOF)¶
チェックされている値が null
であることが保証されているので, instanceof
は常に false
を返します。
これは安全で,誤解や論理エラーを指摘していないことを確認してください。
DMI: int に対して Double.longBitsToDouble() を呼び出している (DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT)¶
Double.longBitsToDouble()
の呼び出しで,32ビット int 値が引数として渡されています。
これはほぼ間違いなく意図したことではなく,意図した結果を出す可能性は低いです。
BC: 不可能なキャスト (BC_IMPOSSIBLE_CAST)¶
このキャストは,常に ClassCastException
をスローします。
SpotBugs は, instanceof
チェックから型情報を調査して,メソッドからの戻り値とフィールドからロードされた値の型について,より多くの正確な情報を使用します。
したがって,宣言された変数の型にはより多くの正確な情報があるかもしれないしれません。
また,キャストが常に実行時例外をスローするのかを決定するために利用する可能性があります。
BC: 不可能なダウンキャスト (BC_IMPOSSIBLE_DOWNCAST)¶
このキャストは,常に ClassCastException
をスローします。
解析は,キャストしている値の正確な型がわかっていると信じていて,サブタイプへダウンキャストしようとする試みは, ClassCastException
のスローによって常に失敗します。
BC: toArray メソッドの結果の不可能なダウンキャスト (BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY)¶
このコードは toArray
メソッドの呼び出し結果を Object[]
ではなく具体的な型のコレクションでキャストしています。
String[] getAsArray(Collection<String> c) {
return (String[]) c.toArray();
}
これは通常 ClassCastException
をスローして失敗します。
ほとんどすべてのコレクションの toArray
メソッドは, Object[]
を返します。
Collection
オブジェクトは宣言された総称型コレクションの参照がないので,本当に何もできません。
コレクションから特定の型の配列を得る正しい方法は, c.toArray(new String[]);
または c.toArray(new String[c.size()]);
(後者はわずかにより効率的です) を使用することです。
これに対する1つの共通の知られている例外があります。
Arrays.asList(...)
によって返されるリストの toArray()
メソッドは共変型配列を返します。
たとえば, Arrays.asArray(new String[] { "a" }).toArray()
は String []
を返します。
SpotBugs はそのようなケースを検出して抑止しようとしますが,見落としているかもしれません。
BC: 常に false を返す instanceof (BC_IMPOSSIBLE_INSTANCEOF)¶
この instanceof
は常に false
を返します。これは安全で,誤解や論理エラーを指摘していないことを確認してください。
RE: 正規表現のために使われている "." または "|" (RE_POSSIBLE_UNINTENDED_PATTERN)¶
String
機能が呼び出されていて, .
または |
が引数として正規表現を取るパラメータに渡されています。
これは意図したことですか?
たとえば
s.replaceAll(".", "/")
は,すべての文字が '/' 文字に置換されたString
を返すs.split(".")
は,常に長さが0のString
配列を返す"ab|cd".replaceAll("|", "/")
は,"/a/b/|/c/d/" を返す"ab|cd".split("|")
は,6個の要素がある配列を返す: [, a, b, |, c, d]
RE: 正規表現のための無効な構文 (RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION)¶
このコードは正規表現の構文によると無効である正規表現を使用しています。
この文が実行されるとき PatternSyntaxException
をスローします。
RE: 正規表現のために使われている File.separator (RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION)¶
このコードは正規表現が必要な場所で, File.separator
を使用しています。
これは File.separator
がバックスラッシュである Windows プラットフォームでは失敗します。
バックスラッシュは正規表現ではエスケープ文字として解釈されます。
その他の選択肢としては, File.separator
の代わりに File.separatorChar=='\\' ? "\\\\" : File.separator
を使用できます。
DLS: 上書きされたインクリメント (DLS_OVERWRITTEN_INCREMENT)¶
このコードはインクリメント演算 (たとえば, i++
) を実行してすぐに上書きしています。
たとえば, i = i++
は元の値をインクリメントした値で上書きします。
BSHIFT: 32ビット int の-31から31の範囲を超えた量によるシフト (ICAST_BAD_SHIFT_AMOUNT)¶
このコードは32ビット int の-31から31の範囲を超えた量でシフトを実行しています。 これの効果は,どのくらいシフトするのかを決めるために整数値の下位5ビット (32で割った余り) を使用することです (たとえば,40ビットでシフトすることは8ビットでシフトすることと同じで,32ビットでシフトすることは0ビットでシフトすることと同じです)。 これはおそらく期待されたことではなく,少なくとも紛らわしいです。
BSHIFT: シフト演算の正しくない構文解析の可能性がある (BSHIFT_WRONG_ADD_PRIORITY)¶
コードは (x << 8 + y)
のような演算を行います。
これは正しいかもしれませんが,おそらく (x << 8) + y
を行うことを意図していました。
しかし,シフト演算は優先順位が低いので,実際には x << (8 + y)
として構文解析されます。
IM: 整数剰余の結果の整数乗算 (IM_MULTIPLYING_RESULT_OF_IREM)¶
このコードは整数剰余の結果に整数定数を乗算しています。
紛らわしい演算子の優先順位がないことを確実にしてください。
たとえば, i % 60 * 1000
は, i % (60 * 1000)
ではなく (i % 60) * 1000
となります。
DMI: 配列で hashCode メソッドを呼び出している (DMI_INVOKING_HASHCODE_ON_ARRAY)¶
このコードは配列で hashCode
メソッドを呼び出しています。
配列で hashCode
メソッドを呼び出すことは, System.identityHashCode
と同じ値を返すので,内容と配列の長さを無視します。
配列 a
の内容によるハッシュコードを必要とするなら, java.util.Arrays.hashCode(a)
を使用してください。
USELESS_STRING: 配列で toString メソッドを呼び出している (DMI_INVOKING_TOSTRING_ON_ARRAY)¶
このコードは配列で toString
メソッドを呼び出しています。「[C@16f0472」のようなかなり役に立たない結果を生成します。
Arrays.toString()
を使用して,配列の内容を読み取り可能な文字列に変換することを検討してください。
『Programming Puzzlers』の第3章,パズル12を参照してください。
USELESS_STRING: 名前のない配列で toString メソッドを呼び出している (DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY)¶
このコードは無名の配列で toString
メソッドを呼び出しています。「[C@16f0472」のようなかなり役に立たない結果を生成します。
Arrays.toString()
を使用して,配列の内容を読み取り可能な文字列に変換することを検討してください。
『Programming Puzzlers』の第3章,パズル12を参照してください。
DMI: hasNext メソッドで next メソッドを呼び出している (DMI_CALLING_NEXT_FROM_HASNEXT)¶
hasNext
メソッドは, next
メソッドを呼び出しています。
hasNext
メソッドは,イテレータの状態を変更することになっていないので,ほぼ確実に間違っています。
next
メソッドがイテレータの状態を変更することになっています。
QBA: 論理式で boolean リテラル値を代入しているメソッド (QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT)¶
このメソッドは, if
または while
の式の中の boolean
変数に boolean
リテラル値 (true
または false
) を代入しています。
おそらく,これは =
による代入ではなく, ==
を使用して論理比較をすることになっていました。
DMI: コレクションへの無意味な呼び出し (DMI_VACUOUS_SELF_COLLECTION_CALL)¶
この呼び出しは意味がありません。
どんなコレクション c
も c.containsAll(c)
を呼び出すことは常に true
であるべきです。
そして, c.retainAll(c)
は効果があるはずがありません。
DMI: コレクションは自分自身を含めるべきではない (DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES)¶
この総称型コレクションメソッドへの呼び出しはコレクションに自分自身が含まれている場合 (たとえば, s.contains(s)
が true
) にだけ意味があります。
これが本当だとは思えないし,もし本当なら問題の原因になります (たとえば,無限再帰になっているハッシュコードの計算)。
間違ったパラメータが渡されている可能性が高いです。
TQ: 型修飾子がない値が修飾子を必要とする場所で使われている (TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED)¶
値が型修飾子アノテーションを必要とする方法で使われています。型修飾子は厳密なので,ツールは適切なアノテーションを指定していない値を拒絶します。
厳密なアノテーションを持つように値を矯正するには,戻り値に厳密なアノテーションを付ける識別関数を定義してください。 これはアノテーションが付けられていない値を厳密な型修飾子アノテーションを持つ値に変える唯一の方法です。
TQ: 互換性のない型修飾子による比較値 (TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS)¶
型修飾子アノテーションを指定した値がその修飾子のない値と比較しています。
より正確に, when=ALWAYS
を指定した型修飾子アノテーションが付けられた値が同じ型修飾子で when=NEVER
を指定する値と比較しています。
たとえば, @NonNegative
は型修飾子アノテーション @Negative(when=When.NEVER)
の略称とします。
次のコードは return
文が @NonNegative
値を要求するが, @Negative
としてマークされている値を受け取るのでこの警告を生成します。
public boolean example(@Negative Integer value1, @NonNegative Integer value2) {
return value1.equals(value2);
}
TQ: 型修飾子アノテーションが付けられた値がその修飾子を付けてはならない値を必要とする場所で使われている (TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED)¶
型修飾子アノテーションが付けられた値がその修飾子を付けてはならない値を必要とする場所で使われています。
より正確に, when=ALWAYS
を指定した型修飾子アノテーションが付けられた値が到達することが保証されているか同じ型修飾子で when=NEVER
を指定する場所で使用しています。
たとえば, @NonNegative
は型修飾子アノテーション @Negative(when=When.NEVER)
の略称とします。
次のコードは return
文が @NonNegative
値を要求するが @Negative
としてマークされている値を受け取るのでこの警告を生成します。
public @NonNegative Integer example(@Negative Integer value) {
return value;
}
TQ: 型修飾子アノテーションが付けられていない値がその修飾子が付けられた値を必要とする場所で使われている (TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED)¶
型修飾子アノテーションが付けられていない値がその修飾子が付けられた値を必要とする場所で使われています。
より正確に, when=NEVER
を指定した型修飾子アノテーションが付けられた値が同じ型修飾子で when=ALWAYS
を指定する場所で使用されています。
TQ: 型修飾子を付けていないかもしれない値がその型修飾子を必要とする方法で常に使われている (TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK)¶
型修飾子によって示された値のインスタンスではない可能性としてアノテーションが付けられた値です。 値は,その型修飾子によって示された値を必要とする方法で使われることが保証されています。
TQ: 型修飾子を付けているかもしれない値がその型修飾子を禁止する方法で常に使われている (TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK)¶
型修飾子によって示された値のインスタンスである可能性としてアノテーションが付けられた値です。 値は,その型修飾子によって示された値を禁止する方法で使われることが保証されています。
FB: SpotBugs からの予期しない/望ましくない警告 (FB_UNEXPECTED_WARNING)¶
SpotBugs は, @NoWarning
アノテーションが付けられたことにより予期しない/望ましくない警告を生成しました。
FB: 失われた SpotBugs からの予期した/望ましい警告 (FB_MISSING_EXPECTED_WARNING)¶
SpotBugs は, @ExpectedWarning
アノテーションが付けられたことにより予期した/望ましい警告が生成されませんでした。
実験用 (EXPERIMENTAL)¶
実験用で完全に精査されていないバグパターンです。
SKIPPED: 解析するにはあまりにも大きいクラス (SKIPPED_CLASS_TOO_BIG)¶
このクラスは効率的に処理できないほど大きいです。また,エラーのために完全に解析されませんでした。
TEST: 未知のバグパターン (UNKNOWN)¶
警告が記録されたのに SpotBugs はこのバグパターンの説明を見つけることができなかったので警告について説明できません。 これは SpotBugs かその設定のバグの場合だけで発生させるべきです。 または解析プラグインを使用して生成されるなら,プラグインは現在ロードされていません。
OBL: ストリームやリソースのクリーンアップに失敗するかもしれないメソッド (OBL_UNSATISFIED_OBLIGATION)¶
このメソッドは,ストリーム,データベースオブジェクト,またはクリーンアップ操作を明示的に必要としている他のリソースのクリーンアップ (クローズする,片付ける) に失敗するかもしれません。
一般的にメソッドがストリープや他のリソースを開いたなら,メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために try-finally
ブロックを使用すべきです。
このバグパターンは,OS_OPEN_STREAM と ODR_OPEN_DATABASE_RESOURCE と基本的に同じですが異なる (そして,うまくいけばより良い) 静的解析技術に基づいています。 私たちは,このバグパターンの有効性についてのフィードバックを得ることに関心があります。 どちらかの方法でフィードバックを送ってください。
特に,このバグパターンの誤検出抑制探索法は詳細にわたって調整されていないので,誤検出についてのレポートは我々の助けになります。
解析技術の説明は,Weimer と Necula による Finding and Preventing Run-Time Error Handling Mistakes を参照してください。
OBL: チェック例外でストリームやリソースのクリーンアップに失敗するかもしれないメソッド (OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE)¶
このメソッドは,ストリーム,データベースオブジェクト,またはクリーンアップ操作を明示的必要としている他のリソースのクリーンアップ (クローズする,片付ける) に失敗するかもしれません。
一般的にメソッドがストリープや他のリソースを開いたなら,メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために try-finally
ブロックを使用すべきです。
このバグパターンは,OS_OPEN_STREAM と ODR_OPEN_DATABASE_RESOURCE と基本的に同じですが異なる (そして,うまくいけばより良い) 静的解析技術に基づいています。 私たちは,このバグパターンの有効性についてのフィードバックを得ることに関心があります。 どちらかの方法でフィードバックを送ってください。
特に,このバグパターンの誤検出抑制探索法は詳細にわたって調整されていないので,誤検出についてのレポートは我々の助けになります。
解析技術の説明は,Weimer と Necula による Finding and Preventing Run-Time Error Handling Mistakes を参照してください。
LG: ロガーの変更は OpenJDK の弱参照が原因で潜在的に失われる (LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE)¶
OpenJDK は,潜在的非互換性を取り入れました。特に, java.util.logging.Logger
は振る舞いが変更されています。
強参照を使用する代わりに弱参照を内部的に使用しています。
これは妥当な変更ですが,残念ながらいくつかのコードは古い振る舞いに依存しています。ロガーの構成を変更すると,ロガーへの参照が削除されます。
これは,ガベージコレクタが自由にそのメモリを再利用できることを意味します。つまり,ロガーの構成が失われます。
たとえば,次を検討してください。
public static void initLogging() throws Exception {
Logger logger = Logger.getLogger("edu.umd.cs");
logger.addHandler(new FileHandler()); // ロガーの構成の変更
logger.setUseParentHandlers(false); // 別のロガーの構成の変更
}
ロガーの参照は,メソッドの終わり (メソッドは脱出しません) で失われるので,
initLogging
の呼び出しの後でガベージコレクションの循環があるなら,ロガー構成は失われます (なぜなら Logger
は弱参照を保持するだけなので)。
public static void main(String[] args) throws Exception {
initLogging(); // ファイルハンドラーをロガーに追加する
System.gc(); // ロガーの構成が失われる
Logger.getLogger("edu.umd.cs").info("Some message"); // 期待したようにファイルに記録されません
}
Ulf Ochsenfahrt と Eric Fellheimer
国際化 (I18N)¶
国際化とロケールに関係があるコードの欠陥です。
Dm: 呼び出したメソッドの Locale パラメータの使用を検討する (DM_CONVERT_CASE)¶
文字列がプラットフォームのデフォルトエンコーディングを使用して大文字,小文字に変換されています。 国際文字で使われると不適切な変換になることがあります。
- String.toUpperCase(Locale l)
- String.toLowerCase(Locale l)
Dm: デフォルトエンコーディングへの依存 (DM_DEFAULT_ENCODING)¶
byte
から String
(または String
から byte
) への変換で,デフォルトプラットフォームエンコーディングが適切だと仮定するメソッドの呼び出しを発見しました。
これはアプリケーションの振る舞いがプラットフォーム間で異なる原因となります。代替 API を使用して,文字セット名または Charset
オブジェクトを明示的に指定して下さい。
悪意のあるコード脆弱性 (MALICIOUS_CODE)¶
信頼できないコードからの攻撃に対して脆弱なコードです。
DP: doPrivileged ブロック内で呼び出すべきメソッド (DP_DO_INSIDE_DO_PRIVILEGED)¶
このコードはセキュリティ許可チェックが必要なメソッドを呼び出しています。
このコードにセキュリティ許可が与えられるとしても,セキュリティ許可を持たないコードによって呼び出されるなら doPrivileged
ブロックの中で呼び出す必要があります。
DP: doPrivileged ブロック内で作成されるべきクラスローダ (DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED)¶
このコードはクラスローダを作成していますが,セキュリティ管理がインストールされるなら許可が必要です。
このコードがセキュリティ許可がないコードによって呼び出されるなら,クラスローダの作成は doPrivileged
ブロックの中で行う必要があります。
FI: ファイナライザは public ではなく protected にすべき (FI_PUBLIC_SHOULD_BE_PROTECTED)¶
このクラスの finalize
メソッドは public
ではなく, protected
にすべきです。
MS: 配列を返すことによって内部表現を暴露するかもしれない public static メソッド (MS_EXPOSE_REP)¶
public static
メソッドは,クラスの 静的な状態の一部である配列の参照を返します。
このメソッドを呼び出すどんなコードも,基底配列を自由に変更できます。
解決策は,配列のコピーを返すことです。
EI: 可変オブジェクトへの参照を返すことによって内部表現を暴露するかもしれないメソッド (EI_EXPOSE_REP)¶
オブジェクトのフィールドに格納された可変オブジェクトの参照を返すと,オブジェクトの内部表現を暴露します。 インスタンスが信頼できないコードによってアクセスされるなら,可変オブジェクトのチェックされていない変更がセキュリティや他の重要なプロパティを危うくするでしょう。 何か違うことをする必要があります。オブジェクトの新しいコピーを返すことは,多くの状況でより良いアプローチです。
EI2: 可変オブジェクトへの参照を取り込むことによって内部表現を暴露するかもしれないメソッド (EI_EXPOSE_REP2)¶
このコードはオブジェクトの内部表現に外部の可変オブジェクトの参照を格納しています。 インスタンスが信頼できないコードによってアクセスされるなら,可変オブジェクトのチェックされていない変更がセキュリティや他の重要なプロパティを危うくするでしょう。 何か違うことをする必要があります。オブジェクトのコピーを格納することは,多くの状況でより良いアプローチです。
MS: static フィールドに可変オブジェクトを格納することによって,内部の静的状態を暴露するかもしれないメソッド (EI_EXPOSE_STATIC_REP2)¶
このコードは static
フィールドに外部の可変オブジェクトを格納しています。
可変オブジェクトのチェックされていない変更がセキュリティや他の重要なプロパティを危うくするでしょう。
何か違うことをする必要があります。オブジェクトのコピーを格納することは,多くの状況でより良いアプローチです。
MS: インタフェースから移動してパッケージプロテクテッドにすべきフィールド (MS_OOI_PKGPROTECT)¶
インタフェースに定義された static final
フィールドが配列や Hashtable
などの可変オブジェクトを参照しています。
この可変オブジェクトは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を回避するためにフィールドをクラスに移動して,パッケージプロテクテッドにする必要があります。
MS: final かつパッケージプロテクテッドにすべきフィールド (MS_FINAL_PKGPROTECT)¶
この可変 static
フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を回避するためにフィールドを final
および/またはパッケージプロテクテッドにします。
MS: final にすべきフィールド (MS_SHOULD_BE_FINAL)¶
final
ではない public static
フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を回避するためにフィールドを final
にします。
MS: final ではないフィールドはリファクタリングすべき (MS_SHOULD_BE_REFACTORED_TO_BE_FINAL)¶
final
ではない public static
フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を回避するためにフィールドを final
にします。
しかしながら,スタティックイニシャライザには複数のフィールドへの書き込みがあるので,何らかのリファクタリングを必要とするでしょう。
MS: パッケージプロテクテッドにすべきフィールド (MS_PKGPROTECT)¶
この可変 static
フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を回避するためにフィールドをパッケージプロテクテッドにします。
MS: 可変 Hashtable のフィールド (MS_MUTABLE_HASHTABLE)¶
この static final
フィールドは Hashtable
を参照しているので,悪意のあるコードや別のパッケージによって思いがけずアクセスされる可能性があります。
このコードはHashtable
の内容を自由に変更できます。
MS: 可変配列のフィールド (MS_MUTABLE_ARRAY)¶
この static final
フィールドは配列を参照しているので,悪意のあるコードや別のパッケージによって思いがけずアクセスされる可能性があります。
このコードは配列の内容を自由に変更できます。
MS: 可変コレクションのフィールド (MS_MUTABLE_COLLECTION)¶
可変コレクションのインスタンスが static final
フィールドに割り当てられています。
したがって,悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
脆弱性を避けるために Collections.unmodifiableSet/List/Map
などでこのフィールドをラップすることを検討してください。
MS: パッケージプロテクテッドにすべき可変コレクションのフィールド (MS_MUTABLE_COLLECTION_PKGPROTECT)¶
可変コレクションのインスタンスが static final
フィールドに割り当てられています。
したがって,悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
フィールドは脆弱性を避けるためにパッケージプロテクテッドにできます。
脆弱性を避けるために Collections.unmodifiableSet/List/Map
などでこのフィールドをラップすることを検討してください。
MS: final ではないフィールドは悪意のあるコードから保護できない (MS_CANNOT_BE_FINAL)¶
この可変 static
フィールドは悪意のあるコードや別のパッケージによって思いがけず変更される可能性があります。
残念ながらこのような使い方は簡単に解決できません。
マルチスレッドの正確性 (MT_CORRECTNESS)¶
スレッド,ロック,volatile に関係があるコードの欠陥です。
AT: 並行抽象の呼び出しシーケンスはアトミックではないかもしれない (AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION)¶
このコードには並行抽象化 (たとえば,並行ハッシュマップ) の呼び出しシーケンスがあります。 これらの呼び出しはアトミックに実行されません。
STCAL: static Calendar フィールド (STCAL_STATIC_CALENDAR_INSTANCE)¶
たとえ JavaDoc にそれに関する手がかりがないとしても, Calendar
はマルチスレッドでの使用は本質的に安全でありません。
正しい同期化をしないでスレッド境界の向こうで1つのインスタンスを共有することは,アプリケーションの動作が不安定になります。
JDK 5.0に比べて JDK 1.4 のほうが問題が表面化するように思われ,おそらく sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate()
の ArrayIndexOutOfBoundsExceptions
や IndexOutOfBoundsExceptions
がランダムに発生します。
直列化問題も経験するかもしれません。
インスタンスフィールドを使用することを推奨します。
詳細については, JDK Bug #6231579 や JDK Bug #6178997 を参照してください。
STCAL: static DateFormat フィールド (STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE)¶
JavaDoc に書かれているように DateFormat
はマルチスレッドでの使用は本質的に安全ではありません。
正しい同期化をしないでスレッド境界の向こうで1つのインスタンスを共有することは,アプリケーションの動作が不安定になります。
直列化問題も経験するかもしれません。
インスタンスフィールドを使用することを推奨します。
詳細については, JDK Bug #6231579 や JDK Bug #6178997 を参照してください。
STCAL: static Calendar の呼び出し (STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE)¶
たとえ JavaDoc にそれに関する手がかりがないとしても, Calendar
はマルチスレッドでの使用は本質的に安全ではありません。
ディテクタは, static
フィールドから得られた Calendar
のインスタンスの呼び出しを発見しました。
これは疑わしく見えます。
詳細については, JDK Bug #6231579 や JDK Bug #6178997 を参照してください。
STCAL: static DateFormat の呼び出し (STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE)¶
JavaDoc に書かれているように DateFormat
はマルチスレッドでの使用は本質的に安全ではありません。
ディテクタは, static
フィールドから得られた DateFormat
のインスタンスの呼び出しを発見しました。
これは疑わしく見えます。
詳細については, JDK Bug #6231579 や JDK Bug #6178997 を参照してください。
NP: 同じフィールドでの同期化と null チェック (NP_SYNC_AND_NULL_CHECK_FIELD)¶
フィールドは同期化しているので,おそらく null
ではないと思われます。
null
のフィールドを同期化すると NullPointerException
がスローされるので, null
チェックは無意味になります。
別のフィールドで同期化したほうがよいです。
VO: 配列への volatile 参照は,配列要素を volatile として扱わない (VO_VOLATILE_REFERENCE_TO_ARRAY)¶
配列に volatile
参照を宣言していますが,あなたが望むものではないかもしれません。
配列への volatile
参照は,配列への参照の読み出し,書き込みは volatile
として扱われますが,配列要素は volatile
として扱われません。
配列要素を volatile
として扱いたいのであれば,J2SE 5.0で提供された java.util.concurrent パッケージのアトミック配列クラスを使用する必要があります。
VO: volatile フィールドへのインクリメントはアトミックではない (VO_VOLATILE_INCREMENT)¶
このコードは volatile
フィールドをインクリメントしています。
volatile
フィールドのインクリメントはアトミックではありません。
複数のスレッドが同時にフィールドをインクリメントすると,インクリメントが失われる可能性があります。
Dm: Condition で呼び出された wait メソッドを監視している (DM_MONITOR_WAIT_ON_CONDITION)¶
このメソッドは, java.util.concurrent.locks.Condition
オブジェクトで wait
メソッドを呼び出しています。
Condition
オブジェクトを待機させるためには Condition
インタフェースで定義された await
メソッドを使用すべきです。
Dm: デフォルトの空の run メソッドを使用して作成されたスレッド (DM_USELESS_THREAD)¶
このメソッドは, Thread
クラスから派生した run
メソッドを指定していないか, Runnable
オブジェクトを渡すことなく,スレッドを作成しています。
このスレッドは,時間の無駄です。
DC: フィールドのダブルチェックの可能性 (DC_DOUBLECHECK)¶
このメソッドにはダブルチェックロッキングのインスタンスがあるかもしれません。このイディオムは,Java のメモリモデルでは正しくありません。
詳細は, http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html を参照してください。
DC: 部分的に初期化されたオブジェクトを暴露する可能性がある (DC_PARTIALLY_CONSTRUCTED)¶
ダブルチェックロッキングと共に遅延初期化フィールドを使用するメソッドのようです。
フィールドが正しく volatile
として宣言される間にオブジェクトの内部構造がフィールドに割り当てられた後で変更される可能性があります。
したがって,他のスレッドが部分的に初期化されたオブジェクトを見るかもしれません。
この問題を直すために,最初にローカル変数をオブジェクトに格納して,完全に構築した後で volatile
フィールドを保存することを考えてください。
DL: Boolean の同期化 (DL_SYNCHRONIZATION_ON_BOOLEAN)¶
Boolean
のようなボクシングされたプリミティブ型の定数で同期化しています。
private static Boolean inited = Boolean.FALSE;
...
synchronized(inited) {
if (!inited) {
init();
inited = Boolean.TRUE;
}
}
...
一般には2つの Boolean
オブジェクトだけが存在しています。
このコードは他の無関係なコードと同じオブジェクトで同期化している可能性があるので,無応答やデッドロックの原因になります。
CERT CON08-J. Do not synchronize on objects that may be reused を参照してください。
DL: ボクシングされたプリミティブの同期化 (DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE)¶
このコードは Integer
のようなボクシングされたプリミティブの定数で同期化しています。
private static Integer count = 0;
...
synchronized(count) {
count++;
}
...
Integer
オブジェクトはキャッシュして共有できます。
他の無関係なコードと同じオブジェクトで同期化している可能性があるので,無応答やデッドロックの原因になります。
CERT CON08-J. Do not synchronize on objects that may be reused を参照してください。
WL: クラスリテラルではなく getClass で同期化している (WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL)¶
このインスタンスメソッドは, this.getClass()
で同期化しています。
このクラスがサブクラス化されるなら,サブクラスはおそらく意図したことではないサブクラスのためにクラスオブジェクトで同期化します。
たとえば, 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++;
}
}
Jason Mehrens によって寄贈されたバグパターン
ESync: 空の synchronized ブロック (ESync_EMPTY_SYNC)¶
このコードには空の synchronized
ブロックがあります。
synchronized() {
}
空の synchronized ブロックは巧妙で正しく使用するのは困難です。 空の synchronized ブロックはわざとらしくて決して良い解決策ではありません。
MSF: 可変サーブレットフィールド (MSF_MUTABLE_SERVLET_FIELD)¶
Web サーバは,一般的にサーブレットや JSP クラスのインスタンスを1つだけ作成します (すなわち,シングルトンとして扱います)。 複数のスレッドが複数同時のリクエストに応えるためにそのインスタンスでメソッドを呼び出します。 したがって,一般に可変インスタンスフィールドは競合状態を作ります。
IS: 一貫性のない同期化 (IS2_INCONSISTENT_SYNC)¶
このクラスのフィールドは,同期化に関して一貫性なくアクセスされるように見えます。 このバグレポートは,バグパターンディテクタが次のように判断したことを示します。
- クラスは,ロックされたアクセスとアンロックされたアクセスが混在していて,
- クラスは,
javax.annotation.concurrent.NotThreadSafe
アノテーションが付けられていなくて, - 少なくとも1つのロックされたアクセスがクラス自身のメソッドの1つによって実行され,
- 読み出しの2倍の重み付けをした書き込みで,非同期フィールドのアクセス (読み出しと書き込み) 数がすべてのアクセスのわずか1/3
このバグパターンに合致する一般的なバグは,スレッドセーフを意図したクラスでメソッドを同期化させることを忘れていることです。
「非同期アクセス」というラベルがついているノードを選択すると,ディテクタが同期化しないでフィールドにアクセスしたと信じているコードの場所を表示できます。
不正確ないろいろな原因がこのディテクタにあることに注意してください。 たとえば,ディテクタはロックを保持されるすべての状況を静的に検出できるわけではありません。 また,ディテクタがロックされたアクセスとアンロックされたアクセスの区別が正確なときでも,問題のコードは依然として正しいかもしれません。
NN: 裸の notify メソッド (NN_NAKED_NOTIFY)¶
notify
メソッドまたは notifyAll
メソッドへの呼び出しは可変オブジェクト状態にどんな (明らかな) 付随的な変更ももたらされませんでした。
一般的に別のスレッドが期待しているいくつかの条件が真になったので,モニタで notify
メソッドが呼び出されます。
しかしながら,意味がある条件のために両方のスレッドに見えるヒープオブジェクトを含まなければなりません。
可変オブジェクトの状態変更が通知があるメソッドを呼び出したメソッドで起こったかもしれないので,このバグが必ずしもエラーを示すというわけではありません。
Ru: スレッドで run メソッドを呼び出している (RU_INVOKE_RUN)¶
このメソッドは,スレッドで 明示的に run
メソッドを呼び出しています。
一般的にクラスは新しいスレッドで自己の run
メソッドを呼び出してもらうために Runnable
インタフェースを実装します。
その場合は, Thread.start()
を呼び出すのが正しいです。
SP: スピンロックをしているメソッド (SP_SPIN_ON_FIELD)¶
このメソッドは,フィールドを読み出すループで回り続けます。
コンパイラがフィールドの読み出しをループの外に出すかもしれません。コードを無限ループに変えます。
正しい同期化 (wait
/notify
を呼び出すように含む) を使うようにクラスを変更すべきです。
TLW: 2つ以上のロックを保持して wait メソッドを呼び出している (TLW_TWO_LOCK_WAIT)¶
2つ以上のロックを保持して,モニタで待機させるとデッドロックを引き起こすことがあります。
wait
メソッドを呼び出すと,待機しているオブジェクトのロックを解除するだけで,その他のロックは解除しません。
これは必ずしもバグではありませんが厳密に調べる価値があります。
UW: wait メソッドの無条件呼び出し (UW_UNCOND_WAIT)¶
このメソッドには条件制御フローによってガードされない java.lang.Object.wait()
の呼び出しがあります。
このコードは wait
メソッドを呼び出す前に待機するつもりだった条件が既に満たされていないことを確かめるべきです。
どんな前の通知も無視されます。
UG: 同期化していない get メソッド,同期化している set メソッド (UG_SYNC_SET_UNSYNC_GET)¶
このクラスには類似した名前の get メソッドと set メソッドがあり,set メソッドは同期化していて,get メソッドは同期化していません。 get メソッドの呼び出し元がオブジェクトの一貫した状態を必ずしも見るというわけではないので,実行時に間違った振る舞いの原因になることがあります。 get メソッドは同期化すべきです。
IS: 並行アクセスに対してガードされていないフィールド (IS_FIELD_NOT_GUARDED)¶
このフィールドは, net.jcip.annotations.GuardedBy
または javax.annotation.concurrent.GuardedBy
というアノテーションが付けられていますが,アノテーションに違反するような方法でアクセスできます。
ML: フィールドを同期化でガードしようとする無駄な試み (ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD)¶
このメソッドは,フィールドの同時更新に対して同期化でガードしようとしています。しかし,フィールドをガードするとフィールドではなく,フィールドが参照するオブジェクトのロックを獲得します。
これはあなたが必要とする相互排除ができないかもしれません。
他のスレッドは (他の目的のための) 参照されたオブジェクトのロックを獲得するかもしれません。
このパターンの例は次のようになります。
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;
}
ML: 更新されるフィールドで同期化しているメソッド (ML_SYNC_ON_UPDATED_FIELD)¶
このメソッドは,可変フィールドから参照されたオブジェクトで同期化しています。 異なるスレッドが異なるオブジェクトで同期化しているかもしれないので,有用な意味を持っている可能性が低いです。
WS: writeObject メソッドは同期化しているがその他のメソッドは同期化していないクラス (WS_WRITEOBJECT_SYNC)¶
このクラスには同期化している writeObject
メソッドがあります。
しかしながら,クラスのその他のメソッドは同期化していません。
RS: readObject メソッドを同期化しているクラス (RS_READOBJECT_SYNC)¶
この直列化可能クラスは同期化している readObject
メソッド を定義しています。
直列化復元によって作成されるオブジェクトは1つのスレッドによってだけ到達可能です。
したがって, readObject
メソッドは同期化する必要がありません。
readObject
メソッドそのものが別のスレッドに見えるようになるオブジェクトの原因になっているなら非常に疑わしいコーディングスタイルの例です。
SC: Thread.start() を呼び出しているコンストラクタ (SC_START_IN_CTOR)¶
コンストラクタがスレッドを開始しています。クラスが拡張され,サブクラスが作られるなら間違っていそうです。 なぜなら,サブクラスのコンストラクタでスレッドが開始される前にスーパークラスのスレッドが開始されてしまうためです。
Wa: wait メソッドがループの中にない (WA_NOT_IN_LOOP)¶
このメソッドは,ループの中にない java.lang.Object.wait()
を呼び出しています。
モニタが複数の条件のために使われるなら,呼び出し元が待機するつもりだった条件は実際には発生しないかもしれません。
Wa: Condition.await() がループの中にない (WA_AWAIT_NOT_IN_LOOP)¶
このメソッドは,ループの中にない java.util.concurrent.await()
(またはそのバリエーション) を呼び出しています。
オブジェクトが複数の条件のために使われるなら,呼び出し元が待機するつもりだった条件は実際には発生しないかもしれません。
No: notifyAll メソッドではなく notify メソッドを使用している (NO_NOTIFY_NOT_NOTIFYALL)¶
このメソッドは, notifyAll
メソッドではなく notify
メソッドを呼び出しています。
モニターは,複数の条件で使われることがよくあります。
notify
メソッドの呼び出しは1つのスレッドを起こすだけで起こされたスレッドは呼び出し元が満たした待機条件の1つではないかもしれないことを意味しています。
UL: すべての経路でロックが解除されないメソッド (UL_UNRELEASED_LOCK)¶
このメソッドは,JSR-166(java.util.concurrent) のロックを獲得していますが,メソッドからのすべての経路で解除していません。 一般的に JSR-166 のロックを使用するための正しいイディオムは次のようになります。
Lock l = ...;
l.lock();
try {
// do something
} finally {
l.unlock();
}
UL: すべての例外経路でロックが解除されないメソッド (UL_UNRELEASED_LOCK_EXCEPTION_PATH)¶
このメソッドは,JSR-166(java.util.concurrent) のロックを獲得していますが,メソッドからのすべての例外経路で解除していません。 一般的に JSR-166 のロックを使用するための正しいイディオムは次のようになります。
Lock l = ...;
l.lock();
try {
// do something
} finally {
l.unlock();
}
MWN: 不整合な wait メソッド (MWN_MISMATCHED_WAIT)¶
このメソッドは,オブジェクトで明らかにロックを保持することなく, Object.wait()
を呼び出しています。
保持されるロックがない状態で, wait
メソッドを呼び出すことは, IllegalMonitorStateException
をスローすることになります。
MWN: 不整合な notify メソッド (MWN_MISMATCHED_NOTIFY)¶
このメソッドは,オブジェクトで明らかにロックを保持することなく Object.notify()
や Object.notifyAll()
を呼び出しています。
保持されるロックがない状態で, notify
メソッドや notifyAll
メソッドを呼び出すことは, IllegalMonitorStateException
をスローすることになります。
LI: static フィールドの間違った遅延初期化 (LI_LAZY_INIT_STATIC)¶
このメソッドには volatile
ではない static
フィールドの非同期な遅延初期化があります。
コンパイラやプロセッサが命令を並べ替えるかもしれないので,メソッドが複数のスレッドによって呼び出されるなら,スレッドは完全に初期化されたオブジェクトを参照することは保証されません。
この問題を修正するためにフィールドを volatile
にできます。
詳細は, Java Memory Model web site を参照してください。
LI: 更新される static フィールドの間違った遅延初期化 (LI_LAZY_INIT_UPDATE_STATIC)¶
このメソッドには static
フィールドの非同期な遅延初期化があります。
フィールドが設定されると,その場所に格納されているオブジェクトはさらに更新またはアクセスされます。フィールドの設定は,他のスレッドにすぐに見えます。
フィールドを設定するメソッドのさらなるアクセスがオブジェクトの初期化に役立つなら,
完全に初期化されるまで他のスレッドが格納されたオブジェクトにアクセスすることを防がないかぎり,非常に深刻なマルチスレッドバグがあります。
たとえメソッドが複数のスレッドによって決して呼び出されないと確信していても,
フィールドに設定する値が完全に設定/初期化されるまで, static
フィールドを設定しないほうが良いかもしれません。
JLM: java.util.concurrent のインスタンスで同期化している (JLM_JSR166_UTILCONCURRENT_MONITORENTER)¶
このメソッドは,java.util.concurrent パッケージのクラス (またはサブクラス) のインスタンスで同期化しています。
これらのクラスのインスタンスは, synchronized
による同期とは違う独自の並行制御メカニズムを持っています。
たとえば, AtomicBoolean
で同期しても,他のスレッドが AtomicBoolean
を変更することを防げません。
そのようなコードは正しいかもしれませんが,将来コードを維持しなければならない人々を混乱させるかもしれないので慎重にレビューし文書化すべきです,
JLM: util.concurrent 抽象化でモニタスタイルの wait メソッドを使用している (JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT)¶
このメソッドは, await()
メソッド, signal
メソッド, signalAll
メソッドを提供するオブジェクト
(たとえば,util.concurrent の Condition
オブジェクト) で, wait
メソッド, notify
メソッド, notifyAll
メソッドを呼び出しています。
これはおそらくあなたが望むことではなく,たとえそれを望むとしても,他の開発者が非常に混乱することを理解して,設計を変更することを検討すべきです。
JLM: Lock で同期化している (JLM_JSR166_LOCK_MONITORENTER)¶
このメソッドは, java.util.concurrent.locks.Lock
を実装したオブジェクトで同期化しています。
そのようなオブジェクトは synchronized (...)
構文よりも acquire()
/release()
を使用してロックとロックの解除をします。
SWL: ロックを保持して Thread.sleep() を呼び出しているメソッド (SWL_SLEEP_WITH_LOCK_HELD)¶
このメソッドは,ロックを保持して, Thread.sleep()
を呼び出しています。
他のスレッドがロックを獲得するために待機しているかもしれないので,ひどい性能とスケーラビリティ,またはデッドロックの原因になるかもしれません。
ロックで wait
メソッドを呼び出すことはかなり良い考えで,ロックを解除して他のスレッドが実行するのを許可します。
RV: putIfAbsent の戻り値は無視されて putIfAbsent に渡した値は再利用された (RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED)¶
putIfAbsent
メソッドは,1つの値が与えられたキー (非存在が成功するかどうかの第一の値) と関連することを確認するために使われます。
戻り値を無視して,渡された値への参照を保持するなら,マップ内のキーに関連付けられていない値を保持する危険性があります。
どれを使用するかが重要で,マップに格納されていないものを使うとプログラムは正しく動作しません。
偽のランダムノイズ (NOISE)¶
偽のランダムノイズ: ソフトウェアで実際のバグを発見するのではなく,データマイニング実験のコントロールとして役に立つことを意図しています。
NOISE: 演算に関する偽の警告 (NOISE_OPERATION)¶
偽の警告です。
性能 (PERFORMANCE)¶
必ずしも間違っているというわけではないが,効率が悪いかもしれないコードです。
Dm: URL の equals メソッドと hashCode メソッドはブロックする (DMI_BLOCKING_METHODS_ON_URL)¶
URL
の equals
メソッドと hashCode
メソッドは,ドメイン名の解決を行うので,ひどい性能になる可能性があります。
詳細は, http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html を参照してください。
その代わりに java.net.URI
を使用することを検討してください。
Dm: URL の Map や Set はひどい性能になる (DMI_COLLECTION_OF_URLS)¶
このメソッドまたはフィールドは, URL
の Map
か Set
を使用しています。
URL
の equals
と hashCode
は,ドメイン名の解決を行うので,ひどい性能になります。
詳細は, http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html を参照してください。
その代わりに java.net.URI
を使用することを検討してください。
Dm: 効率が悪い new String(String) コンストラクタを呼び出しているメソッド (DM_STRING_CTOR)¶
new String(String)
コンストラクタの使用はメモリを浪費します。
そのようにして構築されたオブジェクトと パラメータとして渡された String
は機能的に区別がつかないからです。
引数の String
をそのまま使用してください。
Dm: 効率が悪い new String() コンストラクタを呼び出しているメソッド (DM_STRING_VOID_CTOR)¶
引数がないコンストラクタを使用して,新しい java.lang.String()
オブジェクトを作成するとメモリを浪費します。
そのようにして作成されたオブジェクトと空の文字列定数 ""
は機能的に区別がつかないからです。
Javaは,同一の文字列定数が同じ String
オブジェクトによって表されることを保証します。
したがって,直接空の文字列定数を使用すべきです。
Dm: String の toString メソッドを呼び出しているメソッド (DM_STRING_TOSTRING)¶
String.toString()
を呼び出すのは冗長です。String
を使用してください。
Dm: 明示的なガベージコレクション (DM_GC)¶
明示的にガベージコレクションを呼び出しています。ベンチマークの特定の用途を除いて非常に疑わしいです。
過去に, close
メソッドや finalize
メソッドでガベージコレクタを明示的に呼び出していた状況は,巨大なパフォーマンスブラックホールの原因となりました。
ガベージコレクションは高くつきます。何百,何千ものガベージコレクションを強制する状況は,システムの停滞をもたらすでしょう。
Dm: 効率が悪い Boolean コンストラクタを呼び出しているメソッド (DM_BOOLEAN_CTOR)¶
java.lang.Boolean
の新しいインスタンスを作成するとメモリを浪費します。
Boolean
オブジェクトは不変で,2つの有用な値 (Boolean.TRUE
と Boolean.FALSE
) があります。
その代わりに Boolean.valueOf
メソッド (または J2SE 5.0 のオートボクシング) を使用して Boolean
オブジェクトを作成してください。
Bx: 効率が悪い Number コンストラクタを呼び出しているメソッド (DM_NUMBER_CTOR)¶
new Integer(int)
の使用は,常に新しいブジェクトになることが保証されています。
これに対して, Integer.valueOf(int)
は,コンパイラ,クラスライブラリ,Java 仮想マシンで値がキャッシュされます。
キャッシュに格納された値を使用することはインスタンスの作成を回避し,コードはより高速になります。
-128から127までの値は対応するキャッシュされたインスタンスを持つことが保証されています。
そして, valueOf
メソッドの使用は,コンストラクタを使用するより約3.5倍高速です。
定数範囲外の値は,両方のスタイルの性能は同じです。
クラスが J2SE 5.0より前の Java 仮想マシンとの互換性が不要なら, Long
, Integer
, Short
, Character
, Byte
のインスタンスを作成するときは,オートボクシングか valueOf
メソッドを使用してください。
Bx: 効率が悪い浮動小数点 Number コンストラクタを呼び出しているメソッド (DM_FP_NUMBER_CTOR)¶
new Double(double)
の使用は,常に新しいブジェクトになることが保証されています。
これに対して, Double.valueOf(double)
は,コンパイラ,クラスライブラリ,Java 仮想マシンで値がキャッシュされます。
キャッシュに格納された値を使用することはインスタンス生成を回避し,コードはより高速になります。
クラスが J2SE 5.0より前の Java 仮想マシンとの互換性が不要なら,オートボクシングか Double
, Float
の valueOf
メソッドを使用してください。
Bx: toString メソッドを呼び出すためだけにボクシングされたプリミティブを割り当てている (DM_BOXED_PRIMITIVE_TOSTRING)¶
ボクシングされたプリミティブが toString
メソッドを呼び出すためだけに割り当てられています。
それよりもプリミティブ値を引数にとる static
な toString
メソッドを使用したほうが効率的です。
置換前 | 置換後 |
---|---|
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) |
Bx: プリミティブを解析するためのボクシング/アンボクシング (DM_BOXED_PRIMITIVE_FOR_PARSING)¶
ボクシングされたプリミティブがボクシングされていないプリミティブ値を抽出するために String
から生成されています。
static parseXXX
メソッドを呼び出す方が効率的です。
Bx: プリミティブが比較でボクシングされている (DM_BOXED_PRIMITIVE_FOR_COMPARE)¶
ボクシングされたプリミティブが単に compareTo
メソッドを呼び出すために作られています。
直接プリミティブで働く static compare
メソッド (double
と float
は Java 1.4から,他のプリミティブ型は Java 1.7から) を使うほうがより効率的です。
Bx: プリミティブ値が3項演算子のためにアンボクシングされて,型変換される (BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR)¶
ラップされたプリミティブ値は,3項演算子 (b ? e1 : e2
) の評価の一部として,別のプリミティブ型にアンボクシングされて変換されます。
Java 言語仕様では, e1
と e2
がラップされた数値なら値はアンボクシングされ,共通の型へと変換/型変換されます
(たとえば, e1
が Integer
で, e2
が Float
なら e1
はアンボクシング (int
に変換) され, float
に変換され,ボクシング (Float
に変換) されます)。
JLS セクション15.25を参照してください。
Bx: ボクシングされた値がアンボクシングされて,すぐに再ボクシングされる (BX_UNBOXING_IMMEDIATELY_REBOXED)¶
ボクシングされた値がアンボクシングされて,すぐに再ボクシングされます。
Bx: プリミティブ値がボクシングされて,すぐにアンボクシングされる (BX_BOXING_IMMEDIATELY_UNBOXED)¶
プリミティブ値がボクシングされて,すぐにアンボクシングされます。 おそらくアンボクシングされた値が必要な場所で手動でボクシングをしているためです。 その結果,コンパイラにボクシングの機能を取り消すことを強制しています。
Bx: プリミティブ値がプリミティブ型の型変換をするためにボクシングされて,アンボクシングされる (BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION)¶
プリミティブ値がコンストラクタでボクシングされて,すぐに異なるプリミティブ型に変換されます (たとえば new Double(d).intValue()
)。
直接プリミティブ型の型変換を実行してください (たとえば (int) d
)。
Dm: クラスオブジェクトを得るためだけにインスタンスを作成しているメソッド (DM_NEW_FOR_GETCLASS)¶
メソッドは,クラスオブジェクトを得るためにインスタンスを生成して getClass
メソッドを呼び出しています。
クラスリテラル (Foo.class
) を使うほうが簡単です。
Dm: 整数の乱数を生成するためには nextDouble メソッド ではなく nextInt メソッドを使用する (DM_NEXTINT_VIA_NEXTDOUBLE)¶
java.util.Random
のインスタンス r
で, 0
から n-1
の乱数を生成したいのであれば, (int)(r.nextDouble() * n)
ではなく r.nextInt(n)
を使用します。
nextInt
メソッドへの引数は整数でなければなりません。
たとえば,-99から0までの乱数を生成したいなら, -r.nextInt(100)
を使用してください。
SS: 読み出されないフィールド (SS_SHOULD_BE_STATIC)¶
このクラスにはコンパイル時に静的な値に初期化されるインスタンス final
フィールドがあります。
static
フィールドにすることを検討してください。
SIC: static 内部クラスにすべき (SIC_INNER_SHOULD_BE_STATIC)¶
このクラスは内部クラスなのにそれを作成したオブジェクトへの埋め込まれた参照を使用していません。
この参照はより大きなクラスのインスタンスを作成して,不必要に作成オブジェクトへの参照を存続しておくことがあります。
できれば,クラスは static
にすべきです。
SIC: static 内部クラスにリファクタリングできるかもしれない (SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS)¶
このクラスは内部クラスなのにそれを作成したオブジェクトへの埋め込まれた参照を使用していません。
この参照はより大きなクラスのインスタンスを作成して,不必要に長く作成オブジェクトへの参照を存続しておくかもしれません。
できれば,クラスは static
内部クラスにすべきです。
外部オブジェクトへの参照が内部クラスのインスタンスを構築する間必要なので内部クラスのコンストラクタに外部インスタンスへの参照を渡すようにリファクタリングする必要があります。
SIC: 名前付き static 内部クラスにリファクタリングできるかもしれない (SIC_INNER_SHOULD_BE_STATIC_ANON)¶
このクラスは内部クラスなのにそれを作成したオブジェクトへの埋め込まれた参照を使用していません。
この参照はより大きなクラスのインスタンスを作成して,不必要に作成オブジェクトへの参照を存続しておくことがあります。
できれば,クラスは static
内部クラスにすべきです。
無名内部クラスは static
にできないので,名前付き内部クラスにリファクタリングする必要があります。
UPM: private メソッドは決して呼び出されない (UPM_UNCALLED_PRIVATE_METHOD)¶
この private
メソッドは,決して呼び出されません。
メソッドがリフレクションによって呼び出されるかもしれないが,決して使われないなら除去すべきです。
SBSC: ループの中で + を使用して文字列を連結しているメソッド (SBSC_USE_STRINGBUFFER_CONCATENATION)¶
このメソッドは,ループの中で + を使用して String
を構築していると思われます。
各々の繰り返しにおいて, String
は StringBuffer
/StringBuilder
に変換,追加され, String
へ変換されます。
各々の繰り返しで文字列が再コピーされ,増大すると繰り返しの数で二次コストの原因になる可能性があります。
StringBuffer
(または J2SE 5.0の StringBuilder
) を明示的に使うとより良い性能を得られます。
たとえば,
// This is bad
String s = "";
for (int i = 0; i < field.length; ++i) {
s = s + field[i];
}
// This is better
StringBuffer buf = new StringBuffer();
for (int i = 0; i < field.length; ++i) {
buf.append(field[i]);
}
String s = buf.toString();
IIL: ループの中で NodeList.getLength() を呼び出しているメソッド (IIL_ELEMENTS_GET_LENGTH_IN_LOOP)¶
メソッドは,ループの中で NodeList.getLength()
を呼び出し, NodeList
は, getElementsByTagName
の呼び出しによって作られます。
NodeList
は長さを格納しませんが,毎回とても最適ではない方法で計算されます。
ループの前に変数に長さを格納することを検討してください。
IIL: ループの中で prepareStatement を呼び出しているメソッド (IIL_PREPARE_STATEMENT_IN_LOOP)¶
メソッドは,ループの中で Connection.prepareStatement
に不変の引数を渡して呼び出しています。
PreparedStatement
を複数回実行する必要があるなら,それぞれのループの繰り返しで再生成する理由がありません。
ループの外に呼び出しを移動します。
IIL: ループの中で Pattern.compile を呼び出しているメソッド (IIL_PATTERN_COMPILE_IN_LOOP)¶
メソッドは,ループの中で Pattern.compile
に不変の定数を渡して呼び出しています。
Pattern
を複数回使用する必要があるなら,それぞれのループの繰り返しでコンパイルする理由がありません。
ループの外に呼び出しを移動するか static final
フィールドにします。
IIL: ループの中で正規表現をコンパイルしているメソッド (IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT)¶
メソッドは,ループの中で同じ正規表現を生成しているので,繰り返しごとにコンパイルされます。
ループの外で Pattern.compile
を使用して正規表現をプリコンパイルするのが最適でしょう。
IIO: String.indexOf(String) の非効率的な使用 (IIO_INEFFICIENT_INDEX_OF)¶
このコードは String.indexOf()
に長さ1の文字列定数を渡しています。String.indexOf()
の整数実装を使うほうが効率的です。
たとえば, myString.indexOf(".")
の代わりに myString.indexOf('.')
を呼び出します。
IIO: String.lastIndexOf(String) の非効率的な使用 (IIO_INEFFICIENT_LAST_INDEX_OF)¶
このコードは String.lastIndexOf()
に長さ1の文字列定数を渡しています。String.lastIndexOf()
の整数実装を使うほうが効率的です。
たとえば, myString.lastIndexOf(".")
の代わりに myString.lastIndexOf('.')
を呼び出します。
ITA: 長さが0の配列の引数で toArray メソッドを使用しているメソッド (ITA_INEFFICIENT_TO_ARRAY)¶
このメソッドは, Collection
派生クラスの toArray メソッドを使用して長さが0の配列の引数を渡しています。
myCollection.toArray(new Foo[myCollection.size()])
を使用するほうがより効率的です。
渡される配列がコレクションの要素のすべてを格納できるくらいの大きさなら,設定されて,そのまま返されます。
これは結果として返す2番目の配列 (リフレクションによって) を作成する必要を回避します。
WMI: entrySet イテレータではなく効率が悪い keySet イテレータを使用している (WMI_WRONG_MAP_ITERATOR)¶
このメソッドは, keySet
イテレータから取り出されたキーを使用して,マップエントリの値にアクセスしています。
Map
の entrySet
イテレータを使用したほうが Map.get(key)
ルックアップを回避するのでより効率的です。
UM: 定数値で Math クラスの static メソッドを呼び出しているメソッド (UM_UNNECESSARY_MATH)¶
このメソッドは,定数値で java.lang.Math
の static
メソッドを呼び出しています。
このメソッドの結果は静的に判定でき,より高速で,ときには定数を使用するほうがより正確です。
検出されるメソッドは,次のとおりです。
メソッド | パラメータ |
---|---|
abs | -any- |
acos | 0.0 or 1.0 |
asin | 0.0 or 1.0 |
atan | 0.0 or 1.0 |
atan2 | 0.0 |
cbrt | 0.0 or 1.0 |
ceil | -any- |
cos | 0.0 |
cosh | 0.0 |
exp | 0.0 or 1.0 |
expm1 | 0.0 |
floor | -any- |
log | 0.0 or 1.0 |
log10 | 0.0 or 1.0 |
rint | -any- |
round | -any- |
sin | 0.0 |
sinh | 0.0 |
sqrt | 0.0 or 1.0 |
tan | 0.0 |
tanh | 0.0 |
toDegrees | 0.0 or 1.0 |
toRadians | 0.0 |
IMA: 所有クラスの private メンバ変数にアクセスしているメソッド (IMA_INEFFICIENT_MEMBER_ACCESS)¶
この内部クラスのメソッドは,所有クラスの private
メンバ変数への読み書きか,所有クラスの private
メソッドを呼び出しています。
コンパイラはこの private
メンバにアクセスするための特別なメソッドを生成しなければなりないので,効率を悪化させる原因になります。
メンバ変数またはメソッドの保護を緩和することは,コンパイラが正常なアクセスとして扱うのを許可します。
セキュリティ (SECURITY)¶
リモートから悪用可能なセキュリティ脆弱性を引き起こすかもしれない信頼できない入力を使用しています。
XSS: 反射型クロスサイトスクリプティング脆弱性がエラーページにあるサーブレット (XSS_REQUEST_PARAMETER_TO_SEND_ERROR)¶
このコードはサーブレットのエラーページに HttpServletResponse.sendError
を使用して HTTP パラメータを直接書き込んでいます。
信頼できない入力を返すことは反射型 XSS(クロスサイトスクリプティング) 脆弱性を可能にします。
詳細は, http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。
SpotBugs は,XSS の最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 XSS を心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
XSS: 反射型クロスサイトスクリプティング脆弱性があるサーブレット (XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER)¶
このコードはサーブレットの出力に HTTP パラメータを直接書き込んでいます。これは反射型 XSS(クロスサイトスクリプティング) 脆弱性を可能にします。
詳細は, http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。
SpotBugs は,XSS の最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 XSS を心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
XSS: 反射型クロスサイトスクリプティング脆弱性がある JSP (XSS_REQUEST_PARAMETER_TO_JSP_WRITER)¶
このコードはJSP の出力に HTTP パラメータを直接書き込んでいます。これは XSS(クロスサイトスクリプティング) 脆弱性を可能にします。
詳細は, http://en.wikipedia.org/wiki/Cross-site_scripting を参照してください。
SpotBugs は,XSS の最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 XSS に関して心配しているなら商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
HRS: HTTP レスポンス分割脆弱性 (HRS_REQUEST_PARAMETER_TO_HTTP_HEADER)¶
このコードはHTTP ヘッダに HTTP パラメータを直接書き込んでいます。これは HRS(HTTP レスポンス分割) 脆弱性を可能にします。
詳細は, http://en.wikipedia.org/wiki/HTTP_response_splitting を参照してください。
SpotBugs は,HRS の最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 HRS を心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
HRS: 信頼できない入力から形成された HTTP cookie (HRS_REQUEST_PARAMETER_TO_COOKIE)¶
このコードは信頼できない HTTP パラメータを使用して HTTP クッキーを構築しています。
このクッキーが HTTP レスポンスに追加されるなら,HRS(HTTP レスポンス分割) 脆弱性を可能にします。
詳細は, http://en.wikipedia.org/wiki/HTTP_response_splitting を参照してください。
SpotBugs は,HRS の最も露骨で自明なケースだけを探します。 SpotBugs が何かを発見したなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 HRS を心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
PT: サーブレットの絶対パストラバーサル (PT_ABSOLUTE_PATH_TRAVERSAL)¶
ソフトウェアは,制限されたディレクトリ内にあるパス名を構築するためにHTTPリクエストのパラメータを使いますが,パラメータはそのディレクトリの外にある場所に解決できる「/abs/path」のような絶対パスシーケンスを適切に無効にしていません。
詳細は, http://cwe.mitre.org/data/definitions/36.html を参照してください。
SpotBugs は,絶対パストラバーサルの最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 絶対パストラバーサルを心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
PT: サーブレットの相対パストラバーサル (PT_RELATIVE_PATH_TRAVERSAL)¶
ソフトウェアは,制限されたディレクトリ内にあるパス名を構築するためにHTTPリクエストのパラメータを使いますが,パラメータはそのディレクトリの外にある場所に解決できる「.」のようなシーケンスを適切に無効にしていません。
詳細は, http://cwe.mitre.org/data/definitions/23.html を参照してください。
SpotBugs は,相対パストラバーサルの最も露骨で自明なケースだけを探します。 SpotBugs が何かを見つけたなら,ほぼ間違いなく SpotBugs が報告しない多くの脆弱性があるでしょう。 相対パストラバーサルを心配するなら,商用の静的解析ツールかペネトレーションテストツールの使用を真剣に検討すべきです。
Dm: ハードコードされた定数データベースパスワード (DMI_CONSTANT_DB_PASSWORD)¶
このコードはハードコードされた定数パスワードを使用してデータベース接続を作成しています。 ソースコードかコンパイルされたコードへアクセスできる人なら誰でも簡単にパスワードを知ることができてしまいます。
Dm: 空のデータベースパスワード (DMI_EMPTY_DB_PASSWORD)¶
このコードは空白または空のパスワードを使用してデータベース接続を作成しています。 これはデータベースがパスワードによって保護されていないことを示しています。
SQL: SQL の Statement の execute または addBatch メソッドに定数ではない文字列を渡している (SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE)¶
このメソッドは,動的に生成されるように思われる文字列で,SQL 文 の execute
または addBatch
メソッドを呼び出しています。
その代わりに PreparedStatement
を使用することを検討してください。
効率的で,SQL インジェクション攻撃に強いです。
SQL: PreparedStatement が定数ではない文字列から生成されている (SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING)¶
このコードは定数ではない文字列から SQL の PreparedStatement
を作成しています。
ユーザからのチェックされていない汚染されたデータがこの文字列を作る際に使われるなら, PreparedStatement
で予想外で望ましくない何かをするために SQL インジェクションが使われる可能性があります。
危ないコード (STYLE)¶
紛らわしいコード,異常なコード,またはエラーを引き起こす方法で書かれたコードです。 たとえば,ローカル変数への無効な代入,switch 文のフォールスルー,未確認のキャスト,null とわかっている値の冗長な null チェックなどです。 より多くの誤検出を受け入れました。 SpotBugs の以前のバージョンでは,このカテゴリは Style として知られていました。
CAA: フィールドへの共変配列代入 (CAA_COVARIANT_ARRAY_FIELD)¶
共変型の配列がフィールドに割り当てられています。
紛らわしくて,次のコードのように他の型の参照が後で配列に格納されるなら,実行時に ArrayStoreException
を引き起こすことがあります。
Number[] arr = new Integer[10];
arr[0] = 1.0;
作成した配列の型またはフィールド型を変更することを検討してください。
CAA: 共変配列がメソッドから返される (CAA_COVARIANT_ARRAY_RETURN)¶
共変型の配列がメソッドから返されます。
呼び出し元のコードが返された配列に他の型の参照を格納しようとするなら,実行時に ArrayStoreException
を引き起こすことがあります。
作成した配列の型またはメソッドの戻り型を変更することを検討してください。
CAA: ローカル変数への共変配列代入 (CAA_COVARIANT_ARRAY_LOCAL)¶
共変型の配列がローカル変数に割り当てられています。
紛らわしくて,次のコードのように他の型の参照が後で配列に格納されるなら,実行時に ArrayStoreException
を引き起こすことがあります。
Number[] arr = new Integer[10];
arr[0] = 1.0;
作成した配列の型またはローカル変数型を変更することを検討してください。
Dm: サポートされていないメソッドの呼び出し (DMI_UNSUPPORTED_METHOD)¶
このメソッド呼び出しのすべてのターゲットは UnsupportedOperationException
をスローします。
Dm: Thread オブジェクトが Runnable が期待されているところに渡されている (DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED)¶
Thread
オブジェクトが Runnable
が期待されているメソッドへのパラメータとして渡されています。
これはかなり異常で,論理エラーを示すか,予想外の振る舞いの原因になることがあります。
NP: readLine メソッドの結果が null なのか確かめないで値を利用している (NP_DEREFERENCE_OF_READLINE_VALUE)¶
readLine
メソッドの結果が null
なのか確かめないで値を利用しています。
readLine
メソッドは,それ以上読み出すテキスト行がなければ null
を返すので, NullPointerException
が発生します。
NP: readLine メソッドの結果をすぐに利用している (NP_IMMEDIATE_DEREFERENCE_OF_READLINE)¶
readLine
メソッドの結果をすぐに利用しています。
readLine
メソッドは,それ以上読み出すテキスト行がなければ null
を返すので, NullPointerException
が発生します。
RV: 符号付き32ビット整数の乱数の剰余 (RV_REM_OF_RANDOM_INT)¶
このコードは符号付き整数の乱数を生成して別の値を法とする剰余を計算しています。
乱数は負になり,剰余演算の結果も負になります。これが意図したことであることを確実にしてください。
その代わりに Random.nextInt(int)
の使用を強く検討してください。
RV: ハッシュコードの剰余は負かもしれない (RV_REM_OF_HASHCODE)¶
このコードはハッシュコードを計算して別の値を法とする剰余を計算しています。 ハッシュコードは負になり,剰余演算の結果も負なります。
計算結果が負ではないことを確認したいなら,コードを変更する必要があるかもしれません。
除数が2の累乗であることがわかっているなら,代わりにビット演算を使用できます (すなわち, x.hashCode()%n
の代わりに x.hashCode()&(n-1)
を使用してください)。
これはおそらく,剰余を計算するより高速です。
除数が2の累乗であるということをわかっていないなら,剰余演算の結果の絶対値を取得してください (すなわち Math.abs(x.hashCode()%n)
)。
Eq: 異常な equals メソッド (EQ_UNUSUAL)¶
このクラスの equals
メソッドは,引数の型が this
オブジェクトの型と互換性があるこをチェックするために我々が認識しているパターンで何もしていません。
このコードは何も間違っていないかもしれませんが,レビューする価値があります。
Eq: スーパークラスの equals メソッドをオーバーライドしていないクラス (EQ_DOESNT_OVERRIDE_EQUALS)¶
このクラスは, equals
メソッドを定義しているクラスを拡張してフィールドを追加していますが, equals
メソッドを定義していません。
したがって,このクラスのインスタンスの等価性は,サブクラスと追加されたフィールドの同一性を無視します。
これが意図したことで,しかも, equals
メソッドをオーバーライドする必要がないことを確実にしてください。
たとえ equals
メソッドをオーバーライドする必要がないとしても,サブクラスのための equals
メソッドが super.equals(o)
を呼び出して結果を返すという事実を実証するためにいずれにしろ, equals
メソッドをオーバーライドすることを検討してください。
NS: 非短絡論理の疑わしい使用 (NS_NON_SHORT_CIRCUIT)¶
このコードは短絡論理 (&&
や ||
) ではなく非短絡論理 (&
や |
) を使用していると思われます。
非短絡論理は,左辺を知ることによって結果を推論できたとしても両側の式が評価されます。
これは効率が悪く,右辺の評価でエラーが発生するケースを左辺でガードしているなら,結果としてエラーになる可能性があります。
詳細については, The Java Language Specification を参照してください。
NS: 潜在的な非短絡論理の危険な使用 (NS_DANGEROUS_NON_SHORT_CIRCUIT)¶
このコードは短絡論理 (&&
や ||
) ではなく非短絡論理 (&
や |
) を使用していると思われます。
さらに,左辺値によって右辺を評価したくない (例外のスローや演算が高くつく副作用があるため) と思っているのかもしれません。
非短絡論理は,左辺を知ることによって結果を推論できたとしても両側の式が評価されます。
これは効率が悪く,右辺の評価でエラーが発生するケースを左辺でガードしているなら,結果としてエラーになる可能性があります。
詳細については, The Java Language Specification を参照してください。
IC: 初期化が循環している (IC_INIT_CIRCULARITY)¶
バグインスタンスによって参照される2つのクラスのスタティックイニシャライザで循環が検出されました。 さまざまな予想外の振る舞いはそのような循環に起因することがあります。
IA: 潜在的な継承されたメソッドなのか外部のメソッドなのかあいまいなメソッドの呼び出し (IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD)¶
内部クラスは,継承されたメソッドか外部クラスで定義されたメソッドなのかどちらとも解釈できるメソッドを呼び出しています。
たとえば, foo(17)
を呼び出します。それはスーパークラスと外部のメソッドの両方で定義されています。
Java のセマンティックスでは,継承したメソッドを呼び出しますが,これは意図したことではないかもしれません。
本当に継承されたメソッドを呼び出すつもりなら super
を付けて (例:super.foo(17)
) 呼び出してください。
そうすれば,外部クラスのメソッドではなく継承されたメソッドを呼び出したいことがこのコードを読む人と SpotBugs に明確になります。
this.foo(17)
を呼び出す場合は,継承されたメソッドが呼び出されます。
しかしながら,SpotBugs はクラスファイルを見るだけなので, this.foo(17)
と foo(17)
の呼び出しの違いを見分けることができません。
潜在的なあいまいな呼び出しについて文句を言うでしょう。
Se: サブクラスで継承できない private な readResolve メソッド (SE_PRIVATE_READ_RESOLVE_NOT_INHERITED)¶
このクラスは, private
な readResolve
メソッドを定義しています。
そのため,このメソッドはサブクラスで継承できません。
これが意図したことなら間違っていないかもしれませんが確認するためにレビューすべきです。
Se: Serializable ではないクラスの transient フィールド (SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS)¶
フィールドは, transient
と宣言していますが,クラスは直列化可能ではないので,まったく効果がありません。
クラスが transient
だったときの名残かもしれません,または直列化機構を誤解しているのかもしれません。
SF: 1つの case が次の case へと通り抜ける switch 文を見つけた (SF_SWITCH_FALLTHROUGH)¶
このメソッドには1つの case
が次の case
へと通り抜ける switch
文があります。
通常は, break
か return
でこの case
を終わらせる必要があります。
SF: default がない switch 文を見つけた (SF_SWITCH_NO_DEFAULT)¶
このメソッドには default
がない switch
文があります。
通常は, default
を用意する必要があります。
解析は生成されたバイトコードを見るだけなので, default
が switch
文の終わりにあって,他のケースに break
文が含まれていないなら誤検出のトリガーとなります。
UuF: 未使用の public または protected フィールド (UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD)¶
このフィールドは決して使用されません。
フィールドが public
か protected
なので,多分,それは解析の一部として見えないクラスで使用されることを意図しています。
そうでなければ,クラスから除去することを検討してください。
UrF: 読み出されない public または protected フィールド (URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD)¶
このフィールドは決して読み出されません。
フィールドが public
か protected
なので,多分,それは解析の一部として見えないクラスで使用されることを意図しています。
そうでなければ,クラスから除去することを検討してください。
QF: 複雑か巧妙か間違ったインクリメントの for ループ (QF_QUESTIONABLE_FOR_LOOP)¶
本当にこの for
ループが正しい変数をインクリメントしていますか?
別の変数が for
ループによって初期化されてチェックされるように見えます。
NP: 書き込まれていない public または protected フィールドの読み出し (NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD)¶
プログラムは,決して null
値ではない値を書き込むと思われない public
または protected
フィールドの null
値を利用しています。
フィールドが解析によって見られないメカニズムを通して初期化されないかぎり,この値を利用すると NullPointerException
が発生します。
UwF: コンストラクタで初期化されていないフィールドを null チェックなしで null 値を利用している (UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR)¶
このフィールドは,どんなコンストラクタの中でも決して初期化されません。したがって,オブジェクトが構築された後, null
である可能性があります。
どこかほかで,値がロードされて, null
チェックなしで null
値が利用されます。
フィールドが初期化される前に利用されると NullPointerException
が発生するので,誤りか疑わしい設計かもしれません。
UwF: 書き込まてれいない public または protected フィールド (UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD)¶
この public
または protected
フィールドは書き込まれていません。このフィールドからの読み出しはデフォルト値を返します。
誤りをチェックしてください (フィールドは初期化すべきでしたか?)。役に立たないなら除去してください。
UC: 役に立たない空ではない void メソッド (UC_USELESS_VOID_METHOD)¶
我々の解析は,この空ではない void
メソッドが実際に有用な仕事を行わないことを示しています。
確認してください。おそらくそのコードが間違っているか,またはボディを完全に除去できます。
我々はできる限り誤検出を減らそうと努力しているが,いくつかのケースでは警告は間違っているかもしれません。 よくある誤検出例です。
- メソッドは,副作用を持つかもしれないクラスのロードをトリガーすることを意図している
- メソッドは,暗黙のわかりにくい例外をスローするように意図されている
UC: 条件は効果がない (UC_USELESS_CONDITION)¶
この条件は前に絞られた関係している変数の値と同じ結果を常に作り出します。 おそらく何かほかのことを意味していたのか,あるいは条件を除去できます。
UC: 条件は変数型のために効果がない (UC_USELESS_CONDITION_TYPE)¶
この条件は関係している変数の型範囲のために同じ結果を常に作り出します。 おそらく何かほかのことを意味していたのか,あるいは条件を除去できます。
UC: 役に立たないオブジェクトを作成した (UC_USELESS_OBJECT)¶
我々の解析でオブジェクトが役に立たないことを示しています。 作成され,変更されていますが,値はメソッドの外に出ないし,副作用をもたらしません。 間違いかオブジェクトが使われることを意図していたかのどちらか,あるいは除去できます。
この解析はめったに誤検出することはありません。よくある誤検出のケースです。
- 暗黙のうちに曖昧な例外をスローした
- コードを一般化してスタブとして使用された
- 弱/ソフト参照オブジェクトへの強い参照を持っていた
UC: 役に立たないオブジェクトをスタックで作成した (UC_USELESS_OBJECT_STACK)¶
このオブジェクトは副作用を持たない変更を行うために作成されています。 おそらく何かほかのことを意味していたのか,あるいはオブジェクトを除去できます。
RV: メソッドは戻り値を無視しています。これは間違いではないですか? (RV_RETURN_VALUE_IGNORED_INFERRED)¶
このコードはメソッドを呼び出して,戻り値を無視しています。
戻り値は,メソッドが呼び出される型と同じ型です。そして,我々の解析から戻り値が重要であるかもしれないように見えます (たとえば, String.toLowerCase()
の戻り値を無視するような)。
我々は,戻り値を無視することがメソッド本体の単純な解析から悪い考えかもしれないと推測しています。
このメソッドの戻り値を無視することが重要であるか許容できるかどうかに関して,SpotBugs に指示する @CheckReturnValue
アノテーションが使えます。
戻り値を無視することが間違いではないか決めるために厳密に調査してください。
RV: 副作用がないメソッドの戻り値は無視される (RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT)¶
このコードはメソッドを呼び出して戻り値を無視しています。 しかしながら,解析はメソッド (もしあればサブクラスの実装も含む) が戻り値以外の効果をもたらさないことを示しています。 この呼び出しは除去できます。
我々は,できる限り誤検出を減らそうとしていますが,いくつかのケースではこの警告が間違っているかもしれません。 よくある誤検出です。
- メソッドは,オーバライドされ解析対象外の他のプロジェクトで副作用がもたらされるように設計されている
- メソッドは,副作用をもたらすかもしれないクラスローダをトリガーするように呼び出されている
- メソッドは,例外を取得するために呼び出されている
我々の仮定が正しくないと感じるなら,SpotBugs にこのメソッドの戻り値が無視されることを許容するように指示する @CheckReturnValue
アノテーションを使用することができます。
RV: String.indexOf の結果が正かどうか確かめている (RV_CHECK_FOR_POSITIVE_INDEXOF)¶
このメソッドは String.indexOf
を呼び出して結果が正かどうか確かめています。
結果が負かどうか確かめるほうがはるかに一般的です。チェックされる部分文字列が String
の先頭以外の場所で出現するときだけ正になります。
RV: readLine メソッドの結果を null ではないのか確かめた後で捨てている (RV_DONT_JUST_NULL_CHECK_READLINE)¶
readLine
メソッドの戻り値を null
ではないのか確かめた後で捨てています。
ほとんどすべての状況で,結果が null
ではないなら戻り値を使用したいでしょう。
再び readLine
メソッドを呼び出すと異なる行が得られます。
NP: パラメータは 非 null でなければならないが null 可能としてマークされている (NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE)¶
このパラメータは,常に 非 null
にすることを要求する方法で使われていますが,パラメータには明示的に Nullable
アノテーションが付けられています。
パラメータかアノテーションのどちらかの使い方が間違っています。
NP: null になっている可能性があるメソッドの戻り値を利用している (NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE)¶
メソッドからの戻り値を null
チェックしないで利用しています。メソッドの戻り値は null
なのかチェックすべきです。
コードが実行されると NullPointerException
を引き起こすことがあります。
NP: null 値を実行不可能かもしれない分岐で利用している可能性がある (NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE)¶
そこで分岐または文が実行されるなら, null
値が利用されて NullPointerException
が発生します。
もちろん,問題は分岐または文が実行不可能で, NullPointerException
が決して発生する可能性がないということかもしれません。
それを決めるのは SpotBugs の能力を超えています。
この値が既に null
であることを検査したという事実からこれは明確な可能性です。
NP: null とわかっている値のロード (NP_LOAD_OF_KNOWN_NULL_VALUE)¶
ここで参照されている変数は,以前に null
なのかチェックしているため null
であることがわかっています。
これは有効ですが,間違いかもしれません (多分異なる変数を参照することを意図していました。あるいは,以前の null
チェックで null
ではないのか確かめるべきでした)。
PZLA: null ではなく長さが0の配列を返すことを検討する (PZLA_PREFER_ZERO_LENGTH_ARRAYS)¶
結果がないこと (すなわち,結果の空のリスト) を示すために null
参照ではなく長さが0の配列 を返すことは,多くの場合より良い設計です。
このように,メソッドのクライアントは明示的に null
チェックをする必要はありません。
一方,「この質問に対する答えがない」ことを示すために null
を使用することはおそらく適切です。
たとえば, File.listFiles()
は,ファイルがないディレクトリを与えられた場合は空のリストを返し,ファイルがディレクトリではないなら null
を返します。
UCF: 役に立たない制御フロー (UCF_USELESS_CONTROL_FLOW)¶
このメソッドには分岐するのかどうかに関係なく,制御フローが同じ場所へと続く,役に立たない制御フロー文があります。
たとえば,これは 空の if
文が原因になります。
if (argv.length == 0) {
// TODO: handle this case
}
UCF: 次の行へ続くだけの役に立たない制御フロー (UCF_USELESS_CONTROL_FLOW_NEXT_LINE)¶
このメソッドには分岐するのかどうかに関係なく,制御フローが同じか次の行へと続く,役に立たない制御フロー文があります。
多くの場合,不注意に if
文の本体を空文を使用したことが原因になります。
if (argv.length == 1);
System.out.println("Hello, " + argv[0]);
RCN: null とわかっている値の冗長な null チェック (RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE)¶
このメソッドには null
とわかっている値の冗長な null
チェックがあります。
RCN: null ではないことがわかっている値の冗長な null チェック (RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE)¶
このメソッドには null
ではないことがわかっている値の冗長な null
チェックがあります。
RCN: 2つの null 値の冗長な比較 (RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES)¶
このメソッドには両方とも明らかに null
とわかっている2つの参照の冗長な比較があります。
RCN: 非 null 値と null 値との冗長な比較 (RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE)¶
このメソッドには null
ではないことがわかっている参照と null
とわかっている別の参照との比較があります。
SA: ローカル変数の自己代入 (SA_LOCAL_SELF_ASSIGNMENT)¶
このメソッドにはローカル変数の自己代入があります。
たとえば次のようなコードです。
public void foo() {
int x = 3;
x = x;
}
そのような代入は役に立たないので,論理エラーかタイプミスかもしれません。
INT: 1を法とする整数の剰余 (INT_BAD_REM_BY_1)¶
どんな式 (exp % 1)
も常に0を返すことが保証されています。
そうではなく, (exp & 1)
または (exp % 2)
を意味していましたか?
INT: 整数値の無意味なビットマスク演算 (INT_VACUOUS_BIT_OPERATION)¶
どんな有用な仕事もしない整数ビット演算 (AND,OR,XOR) です (たとえば v & 0xffffffff
)。
SA: ローカル変数の二重代入 (SA_LOCAL_DOUBLE_ASSIGNMENT)¶
このメソッドにはローカル変数の二重代入があります。
たとえば次のようなコードです。
public void foo() {
int x,y;
x = x = 17;
}
変数に同じ値を2回代入することは役に立たないので,論理エラーかタイプミスかもしれません。
SA: フィールドの二重代入 (SA_FIELD_DOUBLE_ASSIGNMENT)¶
このメソッドにはフィールドの二重代入があります。
たとえば次のようなコードです。
int x,y;
public void foo() {
x = x = 17;
}
フィールドに2回代入することは役に立たないので,論理エラーかタイプミスかもしれません。
DLS: return 文に役に立たない代入がある (DLS_DEAD_LOCAL_STORE_IN_RETURN)¶
この文は, return
文でローカル変数に代入をしています。この代入は効果がありません。
この文が正しいことを確かめてください。
DLS: ローカル変数への無効な代入 (DLS_DEAD_LOCAL_STORE)¶
この命令はローカル変数に値を代入していますが,値は読み出されないか以降の命令でも使われません。 多くの場合,計算された値が決して使われないので,これは誤りを示します。
Sun の javac コンパイラが final
なローカル変数のためにしばしば無効な格納を生成することに注意してください。
SpotBugs は,バイトコードベースのツールなので誤検出をなくす簡単な方法がありません。
DLS: フィールドを遮るローカル変数への無効な代入 (DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD)¶
この命令は,ローカル変数に値を代入していますが,値は読み出されないか以降の命令でも使われません。 多くの場合,計算された値が決して使われないので,これは誤りを示します。 フィールドがローカル変数と同じ名前です。そうではなく,フィールドに代入するつもりでしたか?
DLS: ローカル変数への無効な null 代入 (DLS_DEAD_LOCAL_STORE_OF_NULL)¶
このコードはローカル変数に null
を代入していますが代入された値は読み出されていません。
この代入はガベージコレクタを手伝うために導入されたのかもしれませんが,Java SE 6 ではもはや必要とされないか有用ではありません。
REC: 例外がスローされないのに例外をキャッチしている (REC_CATCH_EXCEPTION)¶
このメソッドは,例外オブジェクトをキャッチする try-catch
ブロックを使用していますが,例外は try
ブロックの中でスローされません。また,実行時例外は明示的にキャッチされません。
それぞれの catch
ブロックが同一である多くの例外型をキャッチすることの短縮形として try { ... } catch (Exception e) { something }
を使用することが共通のバグパターンです。
しかし,この構文は誤って実行時例外も同様にキャッチするので,潜在的なバグを隠します。
より良いアプローチは,明示的にキャッチするよりもスローされる特定の例外をスローします。
または,次に示すように明示的に RuntimeException
をキャッチ,再スローして,非実行時例外をキャッチします。
try {
...
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
... deal with all non-runtime exceptions ...
}
FE: 浮動小数点の等価性のためのテスト (FE_FLOATING_POINT_EQUALITY)¶
この演算は,等価性のために2つの浮動小数点値を比較しています。
浮動小数点の計算は丸めを伴うかもしれないので計算された float
と double
の値は正確ではないかもしれません。
通貨のような正確でなければならない値のために BigDecimal
のような固定精度型を使用することを検討してください。
正確である必要がない値のためにいくつかの範囲の中で等価性のために比較することを検討してください。
たとえば, if (Math.abs(x - y) < .0000001)
。
詳細は Java 言語仕様4.2.4を参照してください。
CD: クラス間の循環依存関係のテスト (CD_CIRCULAR_DEPENDENCY)¶
このクラスは,他のクラスと循環依存関係があります。 それぞれが他のクラスの正確な構築に依存していて,クラスの構築を難しくしています。 難しい依存関係を断つためにインタフェースの使用を検討してください。
RI: スーパークラスと同じインタフェースを実装しているクラス (RI_REDUNDANT_INTERFACES)¶
このクラスは,スーパークラスによっても実装されるインタフェースを実装することを宣言しています。 スーパークラスがインタフェースを実装するので,これは冗長です。デフォルトですべてのサブクラスもこのインタフェースを実装します。 このクラスが作成されてから継承階層が変わったことを指摘するかもしれません。インタフェースの実装の所有権を考慮すべきです。
MTIA: Struts Action を拡張したクラスでのインスタンス変数の使用 (MTIA_SUSPECT_STRUTS_INSTANCE_FIELD)¶
Struts Action クラスを拡張したクラスで,インスタンス変数を使用しています。 Struts Action クラスの1つのインスタンスだけが Struts フレームワークによって作成され,マルチスレッドによって使われるので,このパラダイムは極めて問題があり,推奨できません。 ローカル変数を使用することだけを検討してください。 モニタを除いて書き込まれるインスタンスフィールドだけが報告されます。
MTIA: Servlet クラスを拡張したクラスでのインスタンス変数の使用 (MTIA_SUSPECT_SERVLET_INSTANCE_FIELD)¶
Servlet
クラスを拡張したクラスで,インスタンス変数を使用しています。
Servlet
クラスの1つのインスタンスだけが Java EE フレームワークによって作成され,マルチスレッドによって使われるので,このパラダイムは極めて問題があり,推奨できません。
ローカル変数を使用することだけを検討してください。
PS: 公開インタフェースで同期化とセマフォを暴露するクラス (PS_PUBLIC_SEMAPHORES)¶
このクラスは,自分自身 (this
参照) で, wait
メソッド, notify
メソッド, notifyAll
メソッド とともに同期化しています。
このクラスを使用するクライアントクラスは,同期化のためのオブジェクトとしてこのクラスのインスタンスをさらに使用するかもしれません。
2つのクラスが同期化のために同じオブジェクトを使用するので,マルチスレッドの正確性は疑わしいです。
公開参照で同期化もセマフォメソッドの呼び出しもすべきではありません。
同期化の制御には内部の公開されないメンバ変数を使用することを検討してください。
ICAST: 整数乗算の結果を long にキャストしている (ICAST_INTEGER_MULTIPLY_CAST_TO_LONG)¶
このコードは次のように整数の乗算を実行してから結果を long
に変換しています。
long convertDaysToMilliseconds(int days) { return 1000*3600*24*days; }
long
を使用して乗算をすれば,結果がオーバーフローするという可能性を回避できます。
たとえば次のように修正できます。
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; }
ICAST: 整数の除算の結果を double または float にキャストしている (ICAST_IDIV_CAST_TO_DOUBLE)¶
このコードは整数の除算の結果を double
または float
にキャストしています。
整数で除算をすることは,ゼロに最も近い整数値まで結果を切り捨てます。
結果が double
にキャストされたという事実は,この精度が維持されるべきだったことを示唆しています。
おそらく意味されたことは,除算を実行する前にオペランドの1つまたは両方を double
にキャストすることでした。
次に例を示します。
int x = 2;
int y = 5;
// Wrong: yields result 0.0
double value1 = x / y;
// Right: yields result 0.4
double value2 = x / (double) y;
BC: 具象コレクションへの疑わしいキャスト (BC_BAD_CAST_TO_CONCRETE_COLLECTION)¶
このコードは抽象コレクション (たとえば, Collection
, List
, Set
) を特定の具象実装 (たとえば, ArrayList
, HashSet
) にキャストしています。
これは正しくないかもしれません。そして,将来の時点で他の具象実装への切り替えをとても困難にするので,脆弱なコードになるかもしれません。
そうするための特別な理由がないかぎり抽象コレクションクラスを使用してください。
BC: 未チェック/未確認のキャスト (BC_UNCONFIRMED_CAST)¶
このキャストはチェックされていません。すべての型のインスタンスをキャストする型へキャストできるわけではありません。 プログラムのロジックがこのキャストが失敗しないことを確実に確認してください。
BC: メソッドからの戻り値の未チェック/未確認のキャスト (BC_UNCONFIRMED_CAST_OF_RETURN_VALUE)¶
このコードはメソッドの戻り値の未確認のキャストを実行しています。 コードは,キャストが安全であることが保証されるようにメソッドを呼び出しているかもしれませんが,SpotBugs はキャストが安全であることを検証できません。 プログラムのロジックがこのキャストが失敗しないことを確実に確認してください。
BC: 常に true を返す instanceof (BC_VACUOUS_INSTANCEOF)¶
この instanceof
は常に true
を返します (テストしている値が null
ではないかぎり)。
これは安全で,誤解や論理エラーを指摘していないことを確認してください。
本当に null
なのか値をテストしたいなら,多分, instanceof
ではなく null
テストをしたほうが良く,より明確になります。
BC: 抽象コレクションへの疑わしいキャスト (BC_BAD_CAST_TO_ABSTRACT_COLLECTION)¶
このコードは Collection
を抽象コレクションにキャストしています (たとえば List
, Set
, Map
)。
オブジェクトがキャストする型であるということが保証されていることを確認してください。
必要とするコレクションの反復処理ができるなら Set
または List
にキャストする必要はありません。
IM: 負数で機能しない奇数チェック (IM_BAD_CHECK_FOR_ODD)¶
このコードは x % 2 == 1
を使用して値が負数なのか確かめていますが,負数 (たとえば, (-5) % 2 == -1
) なので機能しません。
奇数チェックを意図しているなら, x & 1 == 1
または x % 2 != 0
を使用することを検討してください。
IM: 平均の計算はオーバーフローする可能性がある (IM_AVERAGE_COMPUTATION_COULD_OVERFLOW)¶
このコードは除算か符号付き右シフトを使用して2つの整数の平均を計算して,結果を配列の添字として使用しています。
平均値が非常に大きいならオーバーフローする可能性があります (結果として負の平均の計算になる)。
結果が負ではないことを意図していたなら,その代わりに符号なし右シフトを使用できます。
つまり, (low+high)/2
ではなく (low+high) >>> 1
を使用してください。
このバグは,二分探索とマージソートの多くの以前の実装で存在します。 Martin Buchholz が JDK ライブラリのバグを発見して修正しています。 Joshua Bloch が バグパターンとして公表しました。
BSHIFT: 符号なし右シフトを short/byte にキャストしている (ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT)¶
このコードは符号なしキャストの実行結果を short
または byte
にキャストしています。結果の上位ビットは捨てられます。
上位ビットが捨てられるので,符号付きと符号なし右シフト (シフトのサイズによって) との違いがないかもしれません。
DMI: ハードコードされた絶対パス名への参照がある (DMI_HARDCODED_ABSOLUTE_FILENAME)¶
このコードはハードコードされた絶対パス名を使用して File
オブジェクトを構築しています (たとえば new File("/home/dannyc/workspace/j2ee/src/share/com/sun/enterprise/deployment");
)。
DMI: substring(0) の呼び出しは元の値を返す (DMI_USELESS_SUBSTRING)¶
このコードは文字列で substring(0)
を呼び出していますが,元の値を返します。
ST: インスタンスメソッドから static フィールドへの書き込み (ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD)¶
このインスタンスメソッドは, static
フィールドに書き込みをしています。
複数のインスタンスが操作されているなら,正しくさせるのは難しいです。一般的にバッドプラクティスです。
DMI: ObjectOutput に書き込まれる非直列化可能オブジェクト (DMI_NONSERIALIZABLE_OBJECT_WRITTEN)¶
このコードは ObjectOutput.writeObject
に非直列化可能オブジェクトを渡していると思われます。
このオブジェクトが本当に非直列化可能なら,エラーを招きます。
DB: 2つの分岐のために同じコードを使用しているメソッド (DB_DUPLICATE_BRANCHES)¶
このメソッドは,条件分岐の2つの分岐を実装するために同じコードを使用しています。これがコーディングミスではないことを確認してください。
DB: switch 文の2つの case のために同じコードを使用しているメソッド (DB_DUPLICATE_SWITCH_CLAUSES)¶
このメソッドは, switch
文の2つの case
を実装するために同じコードを使用しています。
複製コードの case
かもしれないし,コーディングミスかもしれません。
XFB: XMLインタフェースの特定の実装のインスタンスを作成しているメソッド (XFB_XML_FACTORY_BYPASS)¶
このメソッドは,XMLインタフェースの特定の実装のインスタンスを作成しています。
提供されたファクトリクラスを使用してオブジェクトを作成して実行時に実装を変更できるようにすることが望ましいです。
詳細は,次を参照してください。
javax.xml.parsers.DocumentBuilderFactory
javax.xml.parsers.SAXParserFactory
javax.xml.transform.TransformerFactory
org.w3c.dom.Document.createXXXX
USM: 親クラスのメソッドに過剰に委譲しているメソッド (USM_USELESS_SUBCLASS_METHOD)¶
この派生メソッドは,単に受け取られる正確なパラメータを渡している同じスーパークラスのメソッドを呼び出すだけです。 このメソッドは,付加価値が与えられないので除去できます。
USM: 実装されたインタフェースで既に定義された抽象メソッド (USM_USELESS_ABSTRACT_METHOD)¶
この抽象メソッドは,この抽象クラスによって実装されるインタフェースで既に定義されています。 このメソッドは,付加価値が与えられないので除去できます。
CI: final なクラスが protected フィールドを宣言している (CI_CONFUSED_INHERITANCE)¶
このクラスは, final
と宣言されていますが,フィールドは protected
と宣言されています。
クラスは fainal
なので派生できません。protected
の使用は紛らわしいです。
フィールドのためのアクセス修飾子は,フィールドの真の用途を表すため, private
か public
に変更すべきです。
TQ: 値は型修飾子を必要としないが,不明としてマークされている (TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK)¶
値は,型修飾子によって示された値ではないことを必要とする方法で使われています。 しかし,値はどこでその型修飾子がいるのか禁止されていのるかわからないと述べている明示的なアノテーションがあります。 使い方かアノテーションのどちらかが間違っています。
TQ: 値は型修飾子を必要としているが,不明としてマークされている (TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK)¶
値は,常に型修飾子によって示された値であることを必要とする方法で使われています。 しかし,値はどこでその型修飾子が必要なのかわからないと述べている明示的なアノテーションがあります。 使い方かアノテーションのどちらかが間違っています。
NP: メソッドは戻り値の nullness アノテーションを緩和している (NP_METHOD_RETURN_RELAXING_ANNOTATION)¶
メソッドは,オーバーライドするメソッドの契約を常に実装すべきです。
したがって,メソッドが @Nonnull
値を返すようにアノテーションが付けられているならば,サブクラスでメソッドが @Nullable
または @CheckForNull
値を返すようにアノテーションが付けられたメソッドをオーバーライドすべきでありません。
そうすると,メソッドが null
を返すべできではないという契約に違反します。
NP: メソッドはパラメータに nullness アノテーションを強化している (NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION)¶
メソッドは,オーバーライドするメソッドの契約を常に実装すべきです。
したがって,メソッドが @Nullable
としてマークされるパラメーターを取るならば,サブクラスでパラメーターを @Nonnull
にしてメソッドをオーバーライドすべきでありません。
そうすると,メソッドが null
パラメータを処理する必要があるという契約に違反します。