코드 품질이 낮으면 예측할 수 없는 동작이 발생합니다. 사용자 입장에서는 사용 편의성이 떨어지는 것으로 나타나는 경우가 많습니다. 공격자에게는 예상치 못한 방법으로 시스템에 부담을 줄 수 있는 기회가 됩니다.
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()
함수는 Thread
개체 내의 모든 위치에서 ThreadDeath
예외를 발생시켜 개체가 일치하지 않는 상태가 되고 리소스가 누출될 수 있습니다. 이 API는 본질적으로 안전하지 않으므로 오래 전부터 더 이상 사용되지 않습니다.Thread.stop()
을 잘못 호출합니다.
...
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
인터페이스는 복제되면 정상적으로 작동하지 않을 수 있는 클래스의 완전한 복제(deep cloning)를 보장하지 않습니다. ICloneable
을 구현하고 다른 개체에 대한 기존의 참조를 포함하는 개체만 복사하는 단순 복제(shallow-cloning)만을 구현하는 클래스는 비정상적으로 작동할 수 있습니다. 개체 및 모든 참조된 개체를 복제하는 완전한 복제(deep cloning)가 clone 메서드의 일반적으로 예상되는 작동이므로 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 사양에는 boxing 변환이 다음과 같이 설명되어 있습니다. 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
상자 표시 기본 형식을 사용하여 두 int
값을 비교합니다. mask0
및 mask1
이 모두 100
이면 mask0 == mask1
은 true
를 반환합니다. 그러나 mask0
및 mask1
이 모두 777
이면 mask0 == maske1
은 false
를 반환합니다. 해당 값은 해당 상자 표시 기본 형식의 캐시된 값 범위 내에 있지 않기 때문입니다.