コードの質が低いと、予測できない動作につながります。ユーザーの視点には、それがしばしば使い勝手の悪さとなって現れます。攻撃者にとっては、予期せぬ方法でシステムにストレスを与える機会となります。
notify()
がコールされるときにどのスレッドが起動されるのかは、あいまいです。notify()
のコールで、起動されるスレッドを指定する方法はありません。 notifyJob()
が notify()
をコールします。
public synchronized notifyJob() {
flag = true;
notify();
}
...
public synchronized waitForSomething() {
while(!flag) {
try {
wait();
}
catch (InterruptedException e)
{
...
}
}
...
}
wait()
をコールするスレッドを起動しようとしていますが、notify()
が意図しているスレッドとは別のスレッドを通知する可能性があります。start()
をコールする代わりに、スレッドの run()
メソッドをコールします。Thread
オブジェクトの run()
メソッドの直接コールはバグです。プログラマは制御の新規スレッドを開始しようとして、誤って start()
の代わりに run()
をコールしたため、run()
メソッドがコーラーの支配下のスレッドで実行されます。start()
の代わりに誤って run()
をコールしています。
Thread thr = new Thread() {
public void run() {
...
}
};
thr.run();
stop()
メソッドというリソースをコールしています。Thread
オブジェクトの stop()
メソッドの直接コールはバグです。プログラマーはスレッドの実行を中止しようとするかもしれませんが、スレッドの停止は適切な方法ではありません。Thread
の stop()
関数は ThreadDeath
例外を Thread
オブジェクトのどこかで引き起こす可能性があり、オブジェクトは不整合な状態になり、漏えいの可能性のあるリソースになります。API は本質的に安全ではないため、かなり前に廃止されています。Thread.stop()
をコールしている Java プログラムです。
...
public static void main(String[] args){
...
Thread thr = new Thread() {
public void run() {
...
}
};
...
thr.start();
...
thr.stop();
...
}
clone()
メソッドを実装しますが、Cloneable
インターフェイスは実装しません。clone()
と名付けられたメソッドを実装するため、プログラマはこのクラスで Cloneable
インターフェイスの実装を意図したようです。しかし、クラスは Cloneable
インターフェイスを実装せず、clone()
メソッドは正しく動作しません。 clone()
をコールすると、結果として CloneNotSupportedException.
になります。
public class Kibitzer {
public Object clone() throws CloneNotSupportedException {
...
}
}
ICloneable
インターフェイスでは、その Clone
メソッドに脆弱なコントラクトが指定されており、その使用を回避する必要があります。ICloneable
インターフェイスでは、ディープコピーが保証されず、このインターフェイスを実装するクラスはクローンが作成されるときに予想される通りに動作しない可能性があります。ICloneable
を実装し、シャローコピー (別のオブジェクトへの既存の参照を含む、オブジェクトのみをコピーする方法) のみを行うクラスでは、予期せぬ動作が発生する可能性があります。ディープコピー (オブジェクトとすべての参照オブジェクトをコピーする方法) が一般にクローンメソッドの動作として仮定されているため、ICloneable
インターフェイスを使用するとエラーが発生しやすくなります。このインターフェイスの使用は避ける必要があります。clone()
メソッドは、オーバーライド可能な関数を呼び出します。clone()
関数がオーバーライド関数をコールする場合、クローンが部分的に初期化された状態で、あるいは壊れた状態で残ることがあります。clone()
関数は、オーバーライドされる可能性のあるメソッドをコールしています。
...
class User implements Cloneable {
private String username;
private boolean valid;
public Object clone() throws CloneNotSupportedException {
final User clone = (User) super.clone();
clone.doSomething();
return clone;
}
public void doSomething(){
...
}
}
doSomething()
と括弧で囲まれたクラスは final
ではないため、関数がオーバーライドされる可能性があります。その結果、クローン オブジェクト clone
が部分的に初期化された状態で残され、ロジックを対処しなかった場合、予期しない方法でエラーが起こる可能性があります。equals()
メソッドではなく等価演算子を使用して比較すると、予期しない動作を引き起こす場合があります。==
や !=
ではなくボクシングされたプリミティブ型の equals()
メソッドをコールするべきです。Java 仕様は、ボクシング変換について次のように記述しています。 Boolean
または Byte
以外)、特定の範囲の値のみがキャッシュまたはメモリに保存されることを意味します。値のサブセットでは、==
または !=
を使用して正しい値を返します。このサブセットに含まれないすべての値では、オブジェクト アドレスを比較した結果を返します。
...
Integer mask0 = 100;
Integer mask1 = 100;
...
if (file0.readWriteAllPerms){
mask0 = 777;
}
if (file1.readWriteAllPerms){
mask1 = 777;
}
...
if (mask0 == mask1){
//assume file0 and file1 have same permissions
...
}
...
Example 1
のコードでは、Integer
のボクシングされたプリミティブ型を使用して、2 つの int
値を比較しようとしています。mask0
と mask1
が両方とも 100
と等しい場合は、mask0 == mask1
は true
を返します。しかし、mask0
と mask1
が両方とも 777
に等しい場合は、これらの値がボクシングされたプリミティブ型のキャッシュされた値の範囲内にないため、mask0 == maske1
は false
を返します。