Equals()
se llama en un objeto que no implementa Equals()
.Equals()
en una clase (o una superclase/interfaz) que no implemente explícitamente Equals()
resulta en una llamada al método Equals()
heredado de System.Object
. En lugar de comparar campos de miembros de objetos u otras propiedades, Object.Equals()
compara dos instancias de objetos para ver si son las mismas. Aunque estos son usos legítimos de Object.Equals()
, a menudo es indicativo de que el código contiene errores.
public class AccountGroup
{
private int gid;
public int Gid
{
get { return gid; }
set { gid = value; }
}
}
...
public class CompareGroup
{
public bool compareGroups(AccountGroup group1, AccountGroup group2)
{
return group1.Equals(group2); //Equals() is not implemented in AccountGroup
}
}
equals()
se llama en un objeto que no implementa equals()
.equals()
en una clase (o una superclase/interfaz) que no implemente explícitamente equals()
resulta en una llamada al método equals()
heredado de java.lang.Object
. En lugar de comparar campos de miembros de objetos u otras propiedades, Object.equals()
compara dos instancias de objetos para ver si son las mismas. Aunque estos son usos legítimos de Object.equals()
, a menudo es indicativo de que el código contiene errores.
public class AccountGroup
{
private int gid;
public int getGid()
{
return gid;
}
public void setGid(int newGid)
{
gid = newGid;
}
}
...
public class CompareGroup
{
public boolean compareGroups(AccountGroup group1, AccountGroup group2)
{
return group1.equals(group2); //equals() is not implemented in AccountGroup
}
}
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.NaN
siempre es un error.NaN
, siempre se evalúa como false
, excepto en el caso del operador !=
, que siempre se evalúa como true
, puesto que NaN
no está ordenado.NaN
.
...
if (result == Double.NaN){
//something went wrong
throw new RuntimeException("Something went wrong, NaN found");
}
...
result
no es NaN
. Sin embargo, si se utiliza el operador ==
con NaN
siempre da como resultado un valor de false
, así que esta comprobación nunca produce la excepción.this
antes de que el objeto se inicialice por completo, lo que a su vez puede suponer vulnerabilidad.
...
class User {
private String username;
private boolean valid;
public User(String username, String password){
this.username = username;
this.valid = validateUser(username, password);
}
public boolean validateUser(String username, String password){
//validate user is real and can authenticate
...
}
public final boolean isValid(){
return valid;
}
}
validateUser
y la clase no son final
, significa que se pueden sobrescribir y, en consecuencia, al inicializar una variable en la subclase que sobrescribe dicha función, será posible eludir la funcionalidad validateUser
. Por ejemplo:
...
class Attacker extends User{
public Attacker(String username, String password){
super(username, password);
}
public boolean validateUser(String username, String password){
return true;
}
}
...
class MainClass{
public static void main(String[] args){
User hacker = new Attacker("Evil", "Hacker");
if (hacker.isValid()){
System.out.println("Attack successful!");
}else{
System.out.println("Attack failed");
}
}
}
Example 1
imprime "¡Ataque satisfactorio!" porque la clase Attacker
sobrescribe la función validateUser()
, llamada desde el constructor de la superclase User
, y Java comprobará primero la subclase en busca de funciones llamadas desde el constructor.
if (fitz == null) {
synchronized (this) {
if (fitz == null) {
fitz = new Fitzer();
}
}
}
return fitz;
Fitzer()
, pero no desea pagar el coste de la sincronización cada vez que se llame al código. A este giro se le conoce como bloqueo de doble comprobación.Fitzer()
. Consulte la declaración "El bloqueo de doble comprobación está roto" para obtener más información [1].inputReader
en función de su nombre de clase. Si un usuario malintencionado es capaz de suministrar una implementación de inputReader
que ejecute comandos maliciosos, el código no puede diferenciar entre las versiones benignas y maliciosas del objeto.
if (inputReader.GetType().FullName == "CompanyX.Transaction.Monetary")
{
processTransaction(inputReader);
}
inputReader
en función de su nombre de clase. Si un usuario malintencionado es capaz de suministrar una implementación de inputReader
que ejecute comandos maliciosos, el código no puede diferenciar entre las versiones benignas y maliciosas del objeto.
if (inputReader.getClass().getName().equals("com.example.TrustedClass")) {
input = inputReader.getInput();
...
}
inputReader
en función de su nombre de clase. Si un usuario malintencionado es capaz de suministrar una implementación de inputReader
que ejecute comandos maliciosos, el código no puede diferenciar entre las versiones benignas y maliciosas del objeto.
if (inputReader::class.qualifiedName == "com.example.TrustedClass") {
input = inputReader.getInput()
...
}
finalize()
debería llamar a super.finalize()
.finalize()
llame a super.finalize()
[1].super.finalize()
.
protected void finalize() {
discardNative();
}
x = NULL
y x != NULL
siempre serán falsas.NULL
es indeterminado. No es igual a ningún valor, ni incluso a otro valor NULL
. Además, un valor null
nunca es igual a otro valor.Ejemplo 2: la siguiente instrucción siempre será falsa.
checkNull BOOLEAN := x = NULL;
checkNotNull BOOLEAN := x != NULL;
equals()
, no con ==
o !=
.==
o !=
para comparar la igualdad de dos cadenas; es decir, compara la igualdad de dos objetos, no de sus valores. Hay muchas probabilidades de que dos referencias nunca sean iguales.
if (args[0] == STRING_CONSTANT) {
logger.info("miracle");
}
==
y !=
solo se comportarán según lo previsto cuando se utilicen para comparar cadenas dentro de objetos iguales. La forma más común para que esto ocurra es que las cadenas sean internas. De este modo, las cadenas se añaden a un grupo de objetos que la clase String
mantiene. Una vez que la cadena se interna, todos los usos de dicha cadena emplearán el mismo objeto y los operadores de igualdad se comportarán según lo previsto. Todos los literales de cadena y las constantes con valor de cadena se internan automáticamente. Otras cadenas pueden internarse manualmente llamando String.intern()
, lo que devolverá una instancia canónica de la cadena actual, creando una si fuera necesario.pthread_mutex_unlock()
antes de que otro subproceso empiece a ejecutarse. Si el subproceso que señala no puede desbloquear la exclusión mutua, la llamada pthread_cond_wait()
del segundo subproceso no volverá y el subproceso no se ejecutará. pthread_cond_signal()
, pero no puede desbloquear la exclusión mutua en la que espera el otro subproceso.
...
pthread_mutex_lock(&count_mutex);
// Signal waiting thread
pthread_cond_signal(&count_threshold_cv);
...
getChunk == NULL
siempre será falso porque getChunk
es el nombre de una función definida en el programa.
if (getChunk == NULL)
return ERR;
char* getName() {
char name[STR_MAX];
fillInName(name);
return name;
}
class AccessLevel{
public static final int ROOT = 0;
//...
public static final int NONE = 9;
}
//...
class User {
private static int access;
public User(){
access = AccessLevel.ROOT;
}
public static int getAccessLevel(){
return access;
}
//...
}
class RegularUser extends User {
private static int access;
public RegularUser(){
access = AccessLevel.NONE;
}
public static int getAccessLevel(){
return access;
}
public static void escalatePrivilege(){
access = AccessLevel.ROOT;
}
//...
}
//...
class SecureArea {
//...
public static void doRestrictedOperation(User user){
if (user instanceof RegularUser){
if (user.getAccessLevel() == AccessLevel.ROOT){
System.out.println("doing a privileged operation");
}else{
throw new RuntimeException();
}
}
}
}
getAccessLevel()
en la instancia user
y no en las clases User
o RegularUser
, la condición siempre devolverá true
y se llevará a cabo la operación restringida aunque se utilice instanceof
para entrar en esta parte del bloque if/else
.
private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;
serialPersistentFields
correctamente, debe declararse como private
, static
y final
.serialPersistentFields
. Esta característica solo funcionará si serialPersistentFields
se declara como private
, static
y final
.serialPersistentFields
no se utilizará para definir campos Serializable
porque no es private
, static
y final
.
class List implements Serializable {
public ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("myField", List.class) };
...
}
Object.equals()
en una matriz en vez de java.util.Arrays.equals().
.Object.equals()
en una matriz es casi siempre un error, ya que comprobará la igualdad de las direcciones de las matrices, en vez de la igualdad de los elementos de las matrices, y debería reemplazarse por java.util.Arrays.equals()
. Object.equals()
.
...
int[] arr1 = new int[10];
int[] arr2 = new int[10];
...
if (arr1.equals(arr2)){
//treat arrays as if identical elements
}
...