关于判等

73 阅读4分钟

判等

1. string判等

Java的字符串存在字符串常量池机制;

设计目的:节省内存。

字符串常量池机制:当创建新的字符串时,会将引用放入字符串常量池,并返回该引用,称为是字符串驻留或池化;如果存在相同的内容的字符串对象的引用,则将这个引用返回。如果不想利用常量池,那么通过new String()创建新对象。

String a = "1";
String b = "1";
logger.info("\nString a = "1";\n" +
        "String b = "1";\n" +
        "a == b ? {}", a == b); //true

String c = new String("2");
String d = new String("2");
logger.info("\nString c = new String("2");\n" +
        "String d = new String("2");" +
        "c == d ? {}", c == d); //false

也可以使用intern()走常量池。

String e = new String("3").intern();
String f = new String("3").intern();
logger.info("\nString e = new String("3").intern();\n" +
        "String f = new String("3").intern();\n" +
        "e == f ? {}", e == f); //true

String g = new String("4");
String h = new String("4");
logger.info("\nString g = new String("4");\n" +
        "String h = new String("4");\n" +
        "g == h ? {}", g.equals(h)); //true

附: 因为如果放在字符串中将密码这一字符串存放于字符串池中,即使我们不再使用密码这一变量了,这个结果仍然会存放在字串池中一段时间,并且对于字符串来说,它是不会改变的。这样的们存储密码带来很大的安全隐患。

建议使用字符数组来存储返回的密码,因为字符数组是可以改变其中的元素的。当我们使用完这个密码之后、应该立刻用一个填充值来覆盖数组元素,这样就大大地降低了密码的安全隐患。

Java的在 Java中将字符串常量全部放在公共的存储池中,字符串变量指向存储池中相应的位置。

2. integer判等

在Java中判等可以通过equals或者==进行判等操作。其中equals为方式,==为操作符。

== 比较的是直接值,当比较基本类型时比较的是数值,因为基本类型的值就是数值。比较引用类型时,比较的是地址,因为引用类型的直接值是内存地址。

equals比较的是内容,因此比较引用类型时,比较的是对象的内容。

但在比较Integer、String等时,有时==也能使用。运行以下代码

Integer a = 127; //Integer.valueOf(127)
Integer b = 127; //Integer.valueOf(127)
logger.info("\nInteger a = 127;\n" +
        "Integer b = 127;\n" +
        "a == b ? {}",a == b);    // true

Integer c = 128; //Integer.valueOf(128)
Integer d = 128; //Integer.valueOf(128)
logger.info("\nInteger c = 128;\n" +
        "Integer d = 128;\n" +
        "c == d ? {}", c == d);   //false

查看Integer的valueOf方法,Integer在(-128,127)之间,可以通过修改参数-XX:AutoBoxCacheMax=1000修改默认缓存值。

static final int low = -128;

static final int high;

@IntrinsicCandidate
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

另外如果不想走Integer的缓存,那么可以通过new Integer()创建新对象。比较缓存对象和新对象时,结果肯定不是相同对象

    Integer e = 127; //Integer.valueOf(127)
    Integer f = new Integer(127); //new instance
    logger.info("\nInteger e = 127;\n" +
            "Integer f = new Integer(127);\n" +
            "e == f ? {}", e == f);   //false
            
    Integer g = new Integer(127); //new instance
    Integer h = new Integer(127); //new instance
    logger.info("\nInteger g = new Integer(127);\n" +
            "Integer h = new Integer(127);\n" +
            "g == h ? {}", g == h);  //false
            
    Integer i = 128; //unbox
    int j = 128;
    logger.info("\nInteger i = 128;\n" +
            "int j = 128;\n" +
            "i == j ? {}", i == j); //true
}

Integer如果和int比,Integer会拆箱后进行比较

Integer i = 128; //unbox
int j = 128;
logger.info("\nInteger i = 128;\n" +
        "int j = 128;\n" +
        "i == j ? {}", i == j); //true

自定义判等

若要实现自定义对象的equals可以按照以下顺序进行实现:

  • 如果对象同一个返回true
  • 另一对象判空,如果空对象和自身比较,那么结果false
  • 判断两个对象的类型,如果类型不同,那么直接return false
  • 对象类型相同的情况下再进行属性的类型强制转换,逐一判断所有字段。
@Override public boolean equals(Object o) { 
if (this == o) return true; 
if (o == null || getClass() != o.getClass()) return false; 
//getClass和instanceOf方法区别
// getClass()方法比较的是运行时实例的类型
// instanceof判断是否是某个类或其派生类
PointRight that = (PointRight) o; 
return x == that.x && y == that.y; 
}

还需要重写hashCode()方法。因为HashSet进行查找时,会通过hashCode()方法,如果没有自定义那么会使用超类Object的默认实现。

HashSet points = new HashSet<>(); 
points.add(p1); 
logger.info("points.contains(p2) ? {}", points.contains(p2));
//hashCode实现
@Override public int hashCode() { 
return Objects.hash(x, y); 
}

自定义类型,要实现Comparable,要记得equals、hashCode、 compareTo三者逻辑一致性。

3. if判等

在Java和C++中同样执行以下代码,输出结果不同

char a = 0xFF;
if (a == 0xFF) {
	cout << "a == 0xFF = " << a;
}

if (a == (char)0xFF) {
	cout << "a == (char)0xFF = " << a;
}

C++中仅输出第二段,强转char后判断的输出语句

image.png

Java中两段均可输出

image.png