Una mala calidad del código lleva a un comportamiento no predecible. Desde la perspectiva de un usuario, muchas veces también supone una usabilidad limitada. Pero para un atacante es una oportunidad para atacar al sistema de formas insospechadas.
notify()
.notify()
. notifyJob()
llama notify()
.
public synchronized notifyJob() {
flag = true;
notify();
}
...
public synchronized waitForSomething() {
while(!flag) {
try {
wait();
}
catch (InterruptedException e)
{
...
}
}
...
}
wait()
, pero es posible que notify()
notifique un subproceso diferente al previsto.run()
del subproceso en lugar de llamar a start()
.run()
del objeto Thread
indica un error. El programador tenía intención de iniciar un nuevo subproceso de control, pero llamó accidentalmente a run()
en lugar de a start()
, por lo que el método run()
se ejecutará en el subproceso de control del autor de la llamada.run()
en lugar de a start()
.
Thread thr = new Thread() {
public void run() {
...
}
};
thr.run();
stop()
del subproceso, lo que puede filtrar recursos.stop()
de un objeto Thread
es un error. El programador pretende detener la ejecución de un subproceso pero no es consciente de que esa no es la mejor forma de detener un subproceso. La función stop()
en Thread
produce una excepción ThreadDeath
en cualquier lugar del objeto Thread
, lo que puede dejar a los objetos en un estado de incoherencia y filtrar recursos. Hace mucho que esta API está en desuso debido a que no es segura por naturaleza.Thread.stop()
.
...
public static void main(String[] args){
...
Thread thr = new Thread() {
public void run() {
...
}
};
...
thr.start();
...
thr.stop();
...
}
clone()
, pero no la interfaz Cloneable
.Cloneable
, ya que implementa un método llamado clone()
. Sin embargo, la clase no implementa la interfaz Cloneable
y el método clone()
no se comportará correctamente. clone()
para esta clase dará lugar a una CloneNotSupportedException.
public class Kibitzer {
public Object clone() throws CloneNotSupportedException {
...
}
}
ICloneable
especifica un contrato débil para su método Clone
, por lo que debe evitarse.ICloneable
no garantiza una clonación profunda; es posible que las clases que la implementan no se comporten en la forma prevista cuando se clonen. Las clases que implementan ICloneable
y realizan solo clonaciones superficiales (se copia solo el objeto, lo que incluye las referencias a otros objetos) pueden provocar un comportamiento inesperado. Como la clonación profunda (se copia el objeto y todos los objetos de referencia) suele ser normalmente el comportamiento previsto de un método de clonación, el uso de la interfaz ICloneable
es propenso a errores, por lo que debe evitarse.clone()
de la clase llama a una función que puede anularse.clone()
llama a una función que se puede sobrescribir, puede que el clon se quede en un estado parcialmente inicializado o que se dañe.clone()
llama a un método que se puede sobrescribir.
...
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()
y su clase envolvente no son final
, la función se puede sobrescribir, lo que podría dejar al objeto clonado clone
en un estado parcialmente inicializado, lo que podría dar lugar a errores o que funcionase en torno a la lógica de forma inesperada.equals()
, puede producirse un comportamiento inesperado.equals()
del primitivo boxed en vez de a los operadores ==
y !=
. La especificación Java establece lo siguiente sobre las conversiones boxing: Boolean
o Byte
), solo se almacenará en la caché o se memorizará un rango de valores. En el caso de un subconjunto de valores, el uso de ==
o !=
devolverá el valor correcto. Para los demás valores fuera de dicho subconjunto, se devolverá el resultado de comparar las direcciones de los objetos.
...
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
utiliza primitivos boxed Integer
para intentar comparar dos valores int
. Si mask0
y mask1
son iguales a 100
, entonces mask0 == mask1
devolverá true
. Sin embargo, cuando mask0
y mask1
son iguales a 777
, mask0 == maske1
devolverá false
, ya que dichos valores no están dentro del rango de valores almacenados en la caché para estos primitivos boxed.