次のような要件があるとします。現在ログインしているユーザーを特定し、それが指定したシステム管理者である場合は、送信します。 Eメール。システム管理者には特別なフィールド ID はありません。彼のユーザー ID は 888 です。この値は、開発環境、テスト環境、実稼働環境で同じです。
この要件の実装は非常に簡単です:
UserInfo userInfo = CurrentUser.getUserInfo(); if(Objects.isNull(userInfo)) { log.info("请先登录"); return; } if(Objects.equals(userInfo.getId(),888)) { sendEmail(userInfo): }
現在ログインしているユーザーのコンテキストからユーザー情報を取得し、判断し、ユーザー情報が空の場合は直接返します。
取得したユーザー情報が空でない場合は、ユーザー ID が 888 に等しいかどうかを確認します。
888 に等しい場合は、電子メールを送信します。
888 に等しくない場合は、何も行われません。
id=888 のシステム管理者アカウントでログイン後、関連する操作を行い、期待を込めてメールの受信準備をしましたが、寂しいことがわかりました。
後で、UserInfo クラスが次のように定義されていることがわかりました。
@Data public class UserInfo { private Long id; private String name; private Integer age; private String address; }
この時点で、友人の中には「何も問題ないと思う」という人もいるかもしれません。
しかし、私が言いたいのは、このコードには確かに問題があるということです。 ######どうしたの?
回答: UserInfo クラスのメンバー変数 id=888 は
Long 型ですが、Objects.equals メソッドの右側の 888 は int# 型です。 ##. この 2 つは矛盾しています。返される結果は false です。
これはどのような理由でしょうか?
int a = 1; int b = 1; byte c = 1; Integer d1 = new Integer(1); Integer d2 = new Integer(1); System.out.println(a == b); //结果:true System.out.println(a == c); //结果:true System.out.println(a == d1); //结果:true System.out.println(d2 == a); //结果:true System.out.println(d1 == d2); //结果:false
Integer と int を比較する場合、比較値が等しいかどうかを判断するために、それらは自動的にボックス化されません。
ただし、d1 と d2 などの 2 つのパッケージング クラスがある場合、== 記号を使用した結果は false になる可能性があります。
2 つの整数を比較する場合、それらが指す参照 (つまり、メモリ アドレス) が等しいかどうかが比較されます。
はすべて整数型のパラメーターであり、直接割り当てられて比較されます。 d3とd4の判定結果は等しいですが、d5とd6の判定結果は等しくありません。 友よ、びっくりしてる? 回答: Integer には定数プールがあるため、-128 から 127 までの直接の Integer データは定数プールに直接キャッシュされます。つまり、1 は定数プールにありますが、128 はありません。 ただし、新しい Integer オブジェクトは定数プールには適していません。これは、前の d1 と d2 の例の比較結果からわかります。 次に、文字列の判定を見てみましょう:もう 1 つの興味深い現象があります。
Integer d3 = 1; Integer d4 = 1; Integer d5 = 128; Integer d6 = 128; System.out.println(d3 == d4); //结果:true System.out.println(d5 == d6); //结果:falseログイン後にコピー
String e = "abc"; String f = "abc"; String g = new String("abc"); String h = new String("abc"); System.out.println(e == f); //结果:true System.out.println(e == g); //结果:false System.out.println(g == h); //结果:false
String g = new String("abc"); String h = new String("abc"); System.out.println(g == h); //结果:false
equals
メソッドを使用します。equals メソッドは、実際には Object クラスのメソッドです。
public boolean equals(Object obj) { return (this == obj); }
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
String e = "abc"; String f = "abc"; System.out.println(e.equals(f)); //结果:true
先看看==号判断的情况:
int a = 1; Integer b = new Integer(1); Integer c = null; System.out.println(a == b); //结果:true System.out.println(a == c); //结果:NullPointerException
int和Integer使用==号判断是否相等时,Integer会自动拆箱成int。
但由于c在自动拆箱的过程中,需要给它赋值int的默认值0。而给空对象,赋值0,必然会报空指针异常。
接下来,看看equals方法:
String e = null; String f = "abc"; System.out.println(e.equals(f)); //结果:NullPointerException
由于字符串对象e是空对象,直接调用它的equals方法时,就会报空指针异常。
那么,如何解决空指针问题呢?
答:在代码中判空。
String e = null; String f = "abc"; System.out.println(equals(e, f));
我们抽取了一个新的equals方法:
private static boolean equals(String e, String f) { if (e == null) { return f == null; } return e.equals(f); }
该方法可以解决空指针问题,但有没有办法封装一下,变得更通用一下,也适用于Integer或者其他类型的对象比较呢?
答:有办法,继续往下看。
Objects
类位于java.util
包下,它是里面提供了很多对象操作的辅助方法。
下面我们重点看看它的equals方法:
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }
equals方法的判断逻辑如下:
该方法先判断对象a和b的引用是否相等,如果相等则直接返回true。
如果引用不相等,则判断a是否为空,如果a为空则返回false。
如果a不为空,调用对象的equals方法进一步判断值是否相等。
该方法是如何使用的?
int a = 1; int b = 1; Integer c = null; System.out.println(Objects.equals(a, c)); //结果:false System.out.println(Objects.equals(c, a)); //结果:false System.out.println(Objects.equals(a, b)); //结果:true
从上面的列子看出,使用Objects.equals方法比较两个对象是否相等,确实可以避免空指针问题。
但这个有个疑问:前面使用a==b这种方式比较引用是否相等,当时b为空时,程序直接抛了空指针异常。
而Objects.equals方法内部也使用了a==b比较引用是否相等,为啥它没有抛异常?
答:因为而Objects类的equals方法,使用了Object类型接收参数,它的默认值是null,不用进行类型转换,也不用像int类型对象赋值默认值0。
从上面的理论可以看出,如果我们把代码改成这样,也不会抛异常:
int a = 1; Integer c = null; System.out.println(equals(a, c)); //结果:false
新定义了一个方法:
private static boolean equals(Object a, Object b) { return a == b; }
执行之后发现,确实没有抛空指针了。
所以Objects.equals方法再比较两个对象是否相等时,确实是一个不错的方法。
但它有坑,不信继续往下看。
各位小伙们看到这里,可能有点心急了,到底是什么坑?
废话不多说,直接上例子:
Integer a = 1; long b = 1L; System.out.println(Objects.equals(a, b)); //结果:false
什么?返回结果是false?
而如果你直接用==号判断:
Integer a = 1; long b = 1L; System.out.println(a == b); //结果:true
返回又是true。
a和b明明都是1,为什么使用Objects.equals方法判断不相等呢?
这就要从Integer的equals方法说起来了。
它的equals方法具体代码如下:
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
先判断参数obj是否是Integer类型,如果不是,则直接返回false。如果是Integer类型,再进一步判断int值是否相等。
而上面这个例子中b是long类型,所以Integer的equals方法直接返回了false。
也就是说,如果调用了Integer的equals方法,必须要求入参也是Integer类型,否则该方法会直接返回false。
原来坑在这里!!!
其实,如果把代码改成这样:
Integer a = 1; long b = 1L; System.out.println(Objects.equals(b, a)); //结果:false
执行结果也是false。
因为Long的equals方法代码,跟之前Integer的类似:
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
也是判断入参,如果不是Long类型,则该方法直接返回false。
除此之外,还有Byte、Short、Double、Float、Boolean和Character也有类似的equals方法判断逻辑。
由此可见,我们在使用Objects.equals方法,判断两个值是否相等时,一定要保证两个入参的类型要一致。否则即使两个值相同,但其结果仍然会返回false,这是一个大坑。
那么,如何解决上面的问题呢?
可以将参数b的类型强制转换成int。
Integer a = 1; long b = 1L; System.out.println(Objects.equals(a, (int)b)); //结果:true
或者将参数a的类型强制转换成long。
Integer a = 1; long b = 1L; System.out.println(Objects.equals(b, (long)a)); //结果:true
有些情况也可以直接用==号判断:
Integer a = 1; long b = 1L; System.out.println(a==b); //结果:true
除了Objects.equals方法在两个入参类型不同,而会直接返回false之外,java的8种基本类型包装类的equals也会有相同的问题,需要小伙们特别注意。
之前,如果直接使用java基本类型包装类的equals方法判断相等。
Integer a = new Integer(1); long b = 1L; System.out.println(a.equals(b));
在idea中,如果你将鼠标放在equals方法上,会出现下面的提示:
这时你就知道方法用错了,赶紧修正。但如果直接用包装类的equals方法,有个问题就是可能存在报空指针异常的风险。
如果你使用Objects.equals方法判断相等,在idea中就并没有错误提示。
除此之外,我还测试了findBug、sonar等工具,Objects.equals方法两个参数类型不一致的问题,也没有标识出来。
皆さん、コードをざっと見てください。間違いは見つかりましたか?
一般的な落とし穴は次のとおりです:
Long 型と Integer 型の比較 (例: ユーザー ID シナリオ)。
Byte 型と Integer 型の比較 (例: ステータス判定シナリオ)。
Double 型と Integer 型の比較。例: 金額が 0 の場合の判定シナリオ。
以上がJava における Objects.equals の落とし穴例の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。