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 }
}

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

  1. 首先,对于new方法来声明一个Integer或者Long对象,因为new对象都是在堆里开辟一块空间,所以即便两者的数值相同,但对于==来说,比较的是地址值,所以会返回false。
  2. 对于基本数据类型的包装类,都重写了equals()方法,会比较数值大小,所以用equals()方法是可以根据数值大小进行判断的。
  3. 对于Integer变量与int变量比较的问题,会发现也是基于数值大小得出来的比较值,这是因为在比较时,Integer类型做了自动拆箱,转成了int类型。
  4. 前三点的解释,对所有包装类型都是适用的
  5. 对于直接赋值方式,值为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中的比较总结的更多相关文章

  1. java中的锁

    java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够.于是再次翻看了一下书里的内容,突然有点打开脑门的感觉.看来确实是要学习的最好方式 ...

  2. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  3. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

  4. java中Action层、Service层和Dao层的功能区分

    Action/Service/DAO简介: Action是管理业务(Service)调度和管理跳转的. Service是管理具体的功能的. Action只负责管理,而Service负责实施. DAO只 ...

  5. Java中常用集合操作

    一.Map 名值对存储的. 常用派生类HashMap类 添加: put(key,value)往集合里添加数据 删除: clear()删除所有 remove(key)清除单个,根据k来找 获取: siz ...

  6. java中的移位运算符:<<,>>,>>>总结

    java中有三种移位运算符 <<      :     左移运算符,num << 1,相当于num乘以2 >>      :     右移运算符,num >& ...

  7. 关于Java中进程和线程的详解

    一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而 ...

  8. Java中的进程和线程

     Java中的进程与线程 一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是 ...

  9. Java中的进程与线程(总结篇)

    详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...

  10. 初探java中this的用法

    一般this在各类语言中都表示“调用当前函数的对象”,java中也存在这种用法: public class Leaf { int i = 0; Leaf increment(){ i++; retur ...

随机推荐

  1. THINKPHP中几个缓存的问题

    1.字段缓存. THINKPHP是默认开启字段缓存的.如果关闭了APPDEBUG(即在index.php中设置了这样一句话:define("APP_DEBUG","FAL ...

  2. Leetcode题解(26)

    80. Remove Duplicates from Sorted Array II 题目 分析:简单的操作,代码如下: class Solution { public: int removeDupl ...

  3. OR in Matrix

    OR in Matrix Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submi ...

  4. HDU 5783 Divide the Sequence (训练题002 B)

    Description Alice has a sequence A, She wants to split A into as much as possible continuous subsequ ...

  5. geolocation h5

    navigator. geolocation.getCurrentPosition() 触发浏览器弹窗询问用户同意访问地址.接收三个参数:成功回调函数,可选的失败回调,可选选项对象   成功回调函数接 ...

  6. CentOS7配置更新国内yum源

    备份本地yum源文件 cd /etc/yum.repo.d/ mv CentOS-Base.repo CentOS-Base.repo.bakeup 下载国内yum源 阿里云yum源 wget htt ...

  7. 面试经典算法题集锦——《剑指 offer》小结

    从今年 3 月份开始准备找实习,到现在校招结束,申请的工作均为机器学习/数据挖掘算法相关职位,也拿到了几个 sp offer.经历这半年的洗礼,自己的综合能力和素质都得到了一个质的提升. 实话说对于未 ...

  8. 和为S的两个数

    题目 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数,小的先输出. 思考 注 ...

  9. Spring Boot单元测试(Mock)

    Spring Boot单元测试(Mock) Java个人学习心得 2017-08-12 16:07 Mock 单元测试的重要性就不多说了,我这边的工程一般都是Spring Boot+Mybatis(详 ...

  10. C#三步实现标准事件处理程序

    事件,MSDN解释:类或对象可以通过事件向其他类或对象通知发生的相关事情.发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”. 有关事件的理论与好处,在这里就不再废话了,感兴趣的 ...