zoukankan      html  css  js  c++  java
  • Java中的数据比较(再谈==与equals的区别)

    ava中的变量与对象有区别吗?

    引子:变量与对象

    变量是Java中最基本的存储单元,为变量赋值可以使用赋值表达式。如:

    [java] view plaincopy
     
    1. int i = 10;  

    该表达式的含义是将一个字面量(literal)10赋值给一个类型为int型的变量,变量名为i。这是一个为基本数据类型的变量赋值的例子,它表达了一个非常朴素的信息,那就是变量i的值为10。
    那么这种赋值表达式引申到引用类型的变量时,其含义又有什么变化呢?再看一个赋值表达式:

    [java] view plaincopy
     
    1. String str = null;  

    该表达式的含义是将空内存地址(null)赋值给String类型的变量,变量名为str。朴素的说法是变量str的值为null。对于引用类型的变量而言,赋值操作只是将对象的内存地址保存到变量中。也就是说引用类型的变量值是对象的内存地址而不是对象的内容。如下例:

    [java] view plaincopy
     
    1. String str1 = "abc";  
    2. String str2 = new String("abc");  

    上述两种赋值操作本质上没有任何区别,最大的区别是生成对象的方法不同(这一点与赋值操作无关)。对于变量而言,其值仍然是所指对象的内存地址。

    相对于变量,对象也是存储单元的一种。对象有自己的属性与方法,其内容的表现形式由实例化该对象所用的类决定。如:

    [java] view plaincopy
     
    1. new java.sql.Time(0L);  

    要使用对象,必须将对象的内存地址指定到一个引用类型的变量中(也就是变量的赋值操作)。该变量的类型可以与对象的类型一致,也可以是对象类型的父类,或者是对象类型实现的接口。后两种是典型的多态应用。如:

    [java] view plaincopy
     
    1. java.util.Date date = new java.sql.Time(0L);  

    当然,我们只能通过变量去调用对象的方法或者设置对象的属性,其作用无非是取得或者修改对象的内容。如:

    [java] view plaincopy
     
    1. java.util.Date date = new java.sql.Time(0L);  
    2. date.setTime(3600000L);  
    3. System.out.println(date.toString());  

    注意,变量的内涵只有一个,就是它的值。我们通过变量调用对象的方法时,可以改变的也只是对象的内容。区分变量与对象是很有必要的,当我们讨论变量时总是与它们的值有关;当我们讨论对象时更多的是在讨论如何取得或修改它们的内容。记住:

    变量的值只能通过赋值表达式来改变;对象的内容只能通过自身的方法或属性来改变。

    变量值的比较

    当我们讨论变量之间是否相等时,通常使用“==”关系运算符。如:

    [java] view plaincopy
     
    1. int i = 10;  
    2. int j = 20;  
    3. if (i == j) {  
    4.     System.out.println("两个变量的值相等");  
    5. }  

    上例是基本数据类型之间的比较,本质上是变量之间值的比较。对于两个引用变量的比较,如:

    [java] view plaincopy
     
    1. String str1 = "abc";  
    2. String str2 = new String("abc");  
    3. if (str1 == str2) {  
    4.     // 判断无法成立,因为两个变量所指对象的内存地址不同。  
    5.     System.out.println("两个变量的值相等");  
    6. }  

    本质上引用类型的变量之间的比较也是值的比较,也就是内存地址的比较。上例不会打印出“两个变量的值相等”,因为两个变量指向了不同内存地址的对象。

    对象内容的比较

    对于引用变量而言,如果我们不想仅限于对内存地址的比较,而是想做更深层次的(比如对象的内容)比较。如何实现呢?Java的Object类提供了equals方法,此方法实现了对象之间内容上的比较。由于Object类是所有Java类的父类,所以我们只要在自己的类中改写equals方法,就可实现该类对象之间的内容比较。如:

    [java] view plaincopy
     
    1. String str1 = "abc";  
    2. String str2 = new String("abc");  
    3. if (str1.equals(str2)) {  
    4.     // 判断成立,因为两个对象的内容都是"abc"。  
    5.     System.out.println("两个对象的内容相等");  
    6. }  

    关于如何改写equals方法以及与之相关的hashCode方法,可以参考潘爱民翻译的《Effective Java中文版》一文中第7条:在改写equals的时候请遵守通用约定 以及 第8条:改写equals时总是要改写hashCode。

    老生常谈:==与equals的区别

    对于Java初学者而言,==与equals是容易混淆的。当然区分它们也是简单的,只要记住:

    ==只针对变量的值;equals只针对对象的内容。

    记住上句话的同时,请记住下面的一句话:

    引用类型的变量值是所指对象的内存地址。

    附1:Java函数调用中,参数的传递方式只有一种:值传递

    关于值传递的定论,网上有许多许多例子可以证明。这里也举个例子:

    [java] view plaincopy
     
    1. public class Test {  
    2.   
    3.     public static void main(String[] args){  
    4.         java.util.Date date = new java.util.Date(0);  
    5.   
    6.         System.out.println(date);  
    7.         change(date);  
    8.         System.out.println(date);  
    9.     }  
    10.   
    11.     public static void change(java.util.Date date) {  
    12.         // 此处改变了参数(变量)的值  
    13.         date = new java.util.Date(3600000L);  
    14.         System.out.println(date);  
    15.     }  
    16. }  

    上例的输出结果是:

    Thu Jan 01 08:00:00 CST 1970
    Thu Jan 01 09:00:00 CST 1970
    Thu Jan 01 08:00:00 CST 1970

    我们稍微改动一下内容:

    [java] view plaincopy
     
    1. public class Test {  
    2.   
    3.     public static void main(String[] args){  
    4.         java.util.Date date = new java.util.Date(0L);  
    5.   
    6.         System.out.println(date);  
    7.         change(date);  
    8.         System.out.println(date);  
    9.     }  
    10.   
    11.     public static void change(java.util.Date date) {  
    12.         // 此处改变了对象的内容  
    13.         date.setTime(3600000L);  
    14.         System.out.println(date);  
    15.     }  
    16. }  

    上例的输出结果是:

    Thu Jan 01 08:00:00 CST 1970
    Thu Jan 01 09:00:00 CST 1970
    Thu Jan 01 09:00:00 CST 1970

    通过这两个例子可以证明什么呢?
    当函数调用时,JVM会生成第二个引用类型的变量,并将原始引用变量的值(对象的内存地址)复制给第二个引用变量。 注意,值传递的本质是变量值的复制而不是对象内容的复制。
    第一个例子中,函数内部改变的是第二个引用变量的值,原始引用变量的值没有改变。第二个例子中,函数内部改变的是引用变量所指对象的内容,由于原始引用变量与第二个引用变量的值相等(指向同一个对象),所以导致了函数调用后对象的内容已经改变的事实。

    附2:关键字final只作用于变量的值,不作用于对象的内容。

    关键字final的作用是变量只能赋值一次。举个例子:

    [java] view plaincopy
     
    1. final int i = 10;  
    2. i = 20// 编译时报错  

    但只能赋值一次的限制只作用于变量的值,而不是对象的内容。如:

    [java] view plaincopy
     
    1. final java.util.Date date = new java.util.Date(0L);  
    2. date.setTime(3600000L); // 编译可以通过,并且运行正常。  

    如此引来了另一个问题,如何定义常量。
    定义一个常量,首先要做到使用关键字final对变量进行限制。但这样做只限制了变量的值,如果变量的类型是引用类型时,还必须保证该对象是一个值对象(value objects)。换言之值对象的类必须是一个不可变类(immutable classes)。
    String类就是一个不可变类,我们可以用String类直接定义常量。而java.util.Date类可以通过自身的方法改变其内容,所以不能用这种类型定义常量。如:

    [java] view plaincopy
     
    1. final String CHINA = "china";  

    关于如何实现不可变类的论述,可以参考《Practical Java》的第63条:审慎地定义和实现不可变类。

  • 相关阅读:
    【codecombat】 试玩全攻略 第九关 循环又循环
    【codecombat】 试玩全攻略 第十三关 已知敌人
    【codecombat】 试玩全攻略 第十一关 再次迷宫经历
    【codecombat】 试玩全攻略 第六关 cell commentary
    【codecombat】 试玩全攻略 第八关 火舞
    【codecombat】 试玩全攻略 第十二关 恐惧之门
    【codecombat】 试玩全攻略 第十四关 已知敌人
    苹果apns推送总结
    Xcode 提升速度小技巧
    UITextField 限制输入字数
  • 原文地址:https://www.cnblogs.com/yujy/p/3078466.html
Copyright © 2011-2022 走看看