Java比较问题详细分析

Java中的比较问题是一个很基础又很容易混淆的问题。今天就几个容易出错的点作一个比较详细的归纳与整理,希望对大家的学习与面试有帮助。

一、==与equals()的区别

首先,我们需要知道==与equals()的区别,==号比较的一直是地址值,对于基本数据类型来说,==比较实际上就是变量数值是否相等,而对于引用数据类型,比较的则是地址值。这里特别需要注意的是String类型,很容易想当然的使用==,很容易出错。equals()方法是Object类里的方法,我们知道Java中一切类都会默认继承Object类,所以类对象都会有equals()方法。Object类中equals()方法如下图所示:

由源码可以看出,Object类里的equals()方法底层也是用的==,所以它比较的其实也是地址值。所以如果想用equals()方法去作其他比较,我们需要重写equals()方法。

二、基本数据类型及其包装类

我们都知道,byte、short、int、long、boolean、char、double、float这八个是基本数据类型,它们声明的变量存放在栈内存中。而它们对应的包装类型(Byte、Short、Integer、Long、Boolean、Character、Double)定义的变量则存在于堆内存中。对于基本数据类型,它们的比较相对而言较为简单,即判断是否相等用==,比较大小用<、>、<=、>=即可。而对于包装类型,却有些不同。

首先对于判断是否相等,看如下代码的执行结果:

package dailytest;
import org.junit.Test;
/**
 * Java中的比较总结
 * @author yrr
 */
public class JavaCompareTest {
  /**
   * Integer类型判断是否相等
   */
  @Test
  public void test01() {
    int n3 = 48;
    System.out.println("--------使用new对象时,当值在[-127,128]之间时---------");
    Integer n7 = new Integer(48);
    Integer n8 = new Integer(48);
    System.out.println(n7 == n8);  //false
    System.out.println(n7 == n3);  //true
    System.out.println("--------直接赋值方式,当值在[-128,127]之间时---------");
    Integer n1 = 48;
    Integer n2 = 48;
    System.out.println(n3 == n1); //true
    System.out.println(n1 == n2); //true
    System.out.println(n1.equals(n2)); //true
    System.out.println(n1.equals(n3)); //true
    System.out.println(n1.intValue() == n2.intValue()); //true
    System.out.println("--------直接赋值方式,当值不在[-127,128]之间时---------");
    Integer n4 = 128;
    Integer n5 = 128;
    int n6 = 128;
    System.out.println(n4 == n5);  //false
    System.out.println(n4 == n6);  //true
    System.out.println(n4.equals(n5)); //true
    System.out.println(n4.equals(n6)); //true
    System.out.println(n4.intValue() == n5.intValue()); //true
    //使用Integer.intValue()方法时需要注意验证是否为null,防止出现NullPointException
  }
  /**
   * Long类型判断是否相等
   */
  @Test
  public void test02() {
    //这里需要注意,使用long定义时,不需要加L或者l,而使用Long时必须加,否则会报错
    //建设都加上,以示区别
    long n3 = 48L;
    System.out.println("--------使用new对象时,当值在[-127,128]之间时---------");
    Long n7 = new Long(48);
    Long n8 = new Long(48);
    System.out.println(n7 == n8);  //false
    System.out.println(n7 == n3);  //true
    System.out.println("--------直接赋值方式,当值在[-127,128]之间时---------");
    Long n1 = 48L;
    Long n2 = 48L;
    System.out.println(n3 == n1); //true
    System.out.println(n1 == n2); //true
    System.out.println(n1.equals(n2)); //true
    System.out.println(n1.equals(n3)); //true
    System.out.println(n1.intValue() == n2.intValue()); //true
    System.out.println("--------直接赋值方式,当值不在[-127,128]之间时---------");
    Long n4 = 128L;
    Long n5 = 128L;
    long n6 = 128;
    System.out.println(n4 == n5);  //false
    System.out.println(n4 == n6);  //true
    System.out.println(n4.equals(n5)); //true
    System.out.println(n4.equals(n6)); //true
    System.out.println(n4.intValue() == n5.intValue()); //true
    //使用Long.intValue()方法时需要注意验证是否为null,防止出现NullPointException  
  }
}

针对上面的执行结果,作如下说明:

首先,对于new方法来声明一个Integer或者Long对象,因为new对象都是在堆里开辟一块空间,所以即便两者的数值相同,但对于==来说,比较的是地址值,所以会返回false。 对于基本数据类型的包装类,都重写了equals()方法,会比较数值大小,所以用equals()方法是可以根据数值大小进行判断的。 对于Integer变量与int变量比较的问题,会发现也是基于数值大小得出来的比较值,这是因为在比较时,Integer类型做了自动拆箱,转成了int类型。 前三点的解释,对所有包装类型都是适用的 对于直接赋值方式,值为48的两个Integer变量,用==号判断是true,而当值为128后,却为false。这是因为在底层,对于Integer n1 = 48;这种直接赋值的方式,其实调用了Integer.value()方法。我们可以简单看一下Integer.value()方法的源码,如下图所示:

我们可以看到,这里有个if判断,当输入的i在[-128,127]的范围内时,直接从IntegerCache数组中返回了。所以,对于在这个范围内的数值,返回的都是这个数组对应的地址值,因此用==号判断会返回true。而不在这个范围内的,是new出的对象,因此会返回false。这个结论对于Byte、Short、Integer、Long类型都成立(感兴趣的可以去看下它们对应的value()方法的源码),因为Byte类型的范围就是[-128,127],所以对于Byte类型来说,使用==与equals()没有区别。 

而对于大小比较,使用>、<、<=、>=是没有问题的,它们会进行自动拆箱。但是我们通常建议使用以下两种方式来进行大小比较:

调用xxxValue()方法转成基本数据类型进行比较 使用compareTo()方法进行比较,在包装类中,都重写了compareTo()方法。查看compareTo()源码,可以看出,其实它底层使用的也是通过自动拆箱转成了对应的基本数据类型再进行比较的。

二、Java对象的比较

有了上面的介绍之后,对象的比较就比较容易了。原理都是一样的。

1. String类型的比较

需要注意的是,String类型不能直接使用>、<=、>=、<,会报编译异常。

package dailytest;
import org.junit.Test;
/**
 * Java中的比较总结
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test03() {
    String s1 = new String("123");
    String s2 = new String("123");
    System.out.println(s1 == s2);  //false
    System.out.println(s1.equals(s2));
    String s3 = "234";
    String s4 = "234";
    System.out.println(s3 == s4);  //true
    System.out.println(s3.equals(s4));  //true
    //System.out.println(s1 <= s3); //The operator < is undefined for the argument type(s) java.lang.String,java.lang.String
    System.out.println(s1.compareTo(s3) < 0);  //true
  }
}

 2. 类对象的比较

类对象比较结论也是一样的,但是相对于基本数据类型和String类型,较为复杂一点。

根据某一规则,判断两个对象是否相等,需要在被判断类中重写equals()方法,示例代码如下:

package dailytest;
import org.junit.Test;
/**
 * Java中的比较总结
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test04() {
    Person p1 = new Person("yrr",18);
    Person p2 = new Person("yrr",18);
    System.out.println(p1 == p2);  //false
    System.out.println(p2.equals(p1)); //true
  }
}
class Person{
  private String name;  
  private Integer age;
  
  public Person() {
  }
  public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public Integer getAge() {
    return age;
  }
  @Override
  public boolean equals(Object obj) {
    Person person = (Person) obj;
    return name.equals(person.getName()) && age.equals(person.getAge());
  }
  
}

而如果要比较两个对象的大小(这也是常会问到的面试题),有两种方式:

被比较类实现Comparable接口,并重写compareTo()方法 自己定义实现了一个Comparator接口的类或者利用内部类,重写compare()方法 两者的区别:前者定义在被比较类上,而后者定义在被比较类外。通过这种区别,两者的优缺点也很明显,前者简单,但需要对被比较类进行修改,而后者则不需要修改原代码,更加灵活。

第一种方式,示例代码如下:

package dailytest;
import org.junit.Test;
/**
 * Java中的比较总结
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test5() {
    Person p1 = new Person("yrr",18);
    Person p2 = new Person("wx",19);
    System.out.println(p1.compareTo(p2) < 0);
  }
}
class Person implements Comparable<Person>{
  private String name;  
  private Integer age;
  public Person() {
  }
  public Person(String name,Integer age) {
    this.name = name;
    this.age = age;
  }
  public Integer getAge() {
    return age;
  }
  @Override
  public int compareTo(Person o) {
    return this.getAge() - o.getAge();
  }  
}

第二种方式,示例代码如下:

package comparator;
import java.util.Arrays;
import java.util.Comparator;
public class MyComparator {
  public static void main(String[] args) {
    User[] users = new User[] { new User("u1001",25),new User("u1002",20),new User("u1003",21) };
    Arrays.sort(users,new Comparator<User>() {

      @Override
      public int compare(User o1,User o2) {
        return o1.getAge() - o2.getAge();
      }
    });
    for (int i = 0; i < users.length; i++) { 
      User user = users[i]; 
      System.out.println(user.getId() + " " + user.getAge()); 
    } 
  }
}
class User { 
  private String id; 
  private int age; 
 
  public User(String id,int age) { 
    this.id = id; 
    this.age = age; 
  } 
  public int getAge() { 
    return age; 
  } 
  public void setAge(int age) { 
    this.age = age; 
  } 
  public String getId() { 
    return id; 
  } 
  public void setId(String id) { 
    this.id = id; 
  } 
}

以上就是本次给大家讲的Java中比较问题的相关内容,大家还有其他问题可以在下方留言区讨论,感谢你的支持。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 连接 连接池产生原因 连接池实现原理 小结 TEMPERANCE:Eat not to dullness;drink not to elevation.节制
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 一个优秀的工程师和一个普通的工程师的区别,不是满天飞的架构图,他的功底体现在所写的每一行代码上。-- 毕玄 1. 命名风格 【书摘】类名用 UpperCamelC
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个人在用”。哪怕只是throw了一个新的Exception。哈哈,这是我犯的错误。一、接口和抽象类类,即一个对象。先抽象类,就是抽象出类的基础部分,即抽象基类(抽象类)。官方定义让人费解,但是记忆方法是也不错的 —包含抽象方法的类叫做抽象类。接口
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket一、引子文件,作为常见的数据源。关于操作文件的字节流就是 —FileInputStream&amp;FileOutputStream。
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节。交流QQ群:【编程之美 365234583】http://qm.qq.com/cgi-bin/qm/qr?k=FhFAoaWwjP29_Aonqz
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程与多线程 线程是什么? 线程(Thread)是一个对象(Object)。用来干什么?Java 线程(也称 JVM 线程)是 Java 进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。 Ja
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket在面向对象编程中,编程人员应该在意“资源”。比如?1String hello = &quot;hello&quot;; 在代码中,我们
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第103篇原创 《程序兵法:Java String 源码的排序算法(一)》 文章工程:* JDK 1.8* 工程名:algorithm-core-le
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 一、父子类变量名相同会咋样? 有个小故事,今天群里面有个人问下面如图输出什么? 我回答:60。但这是错的,答案结果是 40 。我知错能改,然后说了下父子类变
作者:泥瓦匠 出处:https://www.bysocket.com/2021-10-26/mac-create-files-from-the-root-directory.html Mac 操作系统挺适合开发者进行写代码,最近碰到了一个问题,问题是如何在 macOS 根目录创建文件夹。不同的 ma
作者:李强强上一篇,泥瓦匠基础地讲了下Java I/O : Bit Operation 位运算。这一讲,泥瓦匠带你走进Java中的进制详解。一、引子在Java世界里,99%的工作都是处理这高层。那么二进制,字节码这些会在哪里用到呢?自问自答:在跨平台的时候,就凸显神功了。比如说文件读写,数据通信,还
1 线程中断 1.1 什么是线程中断? 线程中断是线程的标志位属性。而不是真正终止线程,和线程的状态无关。线程中断过程表示一个运行中的线程,通过其他线程调用了该线程的 方法,使得该线程中断标志位属性改变。 深入思考下,线程中断不是去中断了线程,恰恰是用来通知该线程应该被中断了。具体是一个标志位属性,
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want需求 项目在设计表的时候,要处理并发多的一些数据,类似订单号不能重复,要保持唯一。原本以为来个时间戳,精确到毫秒应该不错了。后来觉得是错了,测试环境下很多一
纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 纯技术交流群 每日推荐 - 技术干货推送 跟着泥瓦匠,一起问答交流 扫一扫,我邀请你入群 加微信:bysocket01
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocketReprint it anywhere u want.文章Points:1、介绍RESTful架构风格2、Spring配置CXF3、三层初设计,实现WebService接口层4、撰写HTTPClient 客户
Writer :BYSocket(泥沙砖瓦浆木匠)什么是回调?今天傻傻地截了张图问了下,然后被陈大牛回答道“就一个回调…”。此时千万个草泥马飞奔而过(逃哈哈,看着源码,享受着这种回调在代码上的作用,真是美哉。不妨总结总结。一、什么是回调回调,回调。要先有调用,才有调用者和被调用者之间的回调。所以在百
Writer :BYSocket(泥沙砖瓦浆木匠)一、什么大小端?大小端在计算机业界,Endian表示数据在存储器中的存放顺序。百度百科如下叙述之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加
What is a programming language? Before introducing compilation and decompilation, let&#39;s briefly introduce the Programming Language. Programming la
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:BYSocketFaceBook:BYSocketTwitter :BYSocket泥瓦匠喜欢Java,文章总是扯扯Java。 I/O 基础,就是二进制,也就是Bit。一、Bit与二进制什么是Bit(位)呢?位是CPU
Writer:BYSocket(泥沙砖瓦浆木匠)微博:BYSocket豆瓣:BYSocket一、前言 泥瓦匠最近被项目搞的天昏地暗。发现有些要给自己一些目标,关于技术的目标:专注很重要。专注Java 基础 + H5(学习) 其他操作系统,算法,数据结构当成课外书博览。有时候,就是那样你越是专注方面越