`

equals专题

    博客分类:
  • J2SE
阅读更多

    先看几个例子

    int a=1;
    int b=1;
    System.out.println(a==b);

    这个大家都知道吧,结果是true,那下面的呢


    String a="1";
    String b="1";
    System.out.println(a==b);  [color=#FF0000]true[/color]
    System.out.println(a.equals(b));  [color=#FF0000]true[/color]

    String a=new String("1");
    String b="1";
    System.out.println(a==b);  [color=#FF0000]false[/color]
    System.out.println(a.equals(b));  [color=#FF0000]true[/color]

    StringBuffer a=new StringBuffer("1");
    StringBuffer b=new StringBuffer("1");
    System.out.println(a==b);  [color=#FF0000]false[/color]
    System.out.println(a.equals(b));  [color=#FF0000]false[/color] 

    List a=new ArrayList();
    a.add("0");       
    List b=new ArrayList();
    b.add(new String("0"));
    System.out.println(a1==b1);  [color=#FF0000]false[/color]
    System.out.println(a1.equals(b1));  [color=#FF0000]true[/color]


    大家知道为什么结果为是这样吗,知道的就不必往下看了

    预备知识:
    Java中把存储区分为6类。
    分别为寄存器(register)、栈(stack)、堆(heap)、静态存储区(static storage)、常量存储区(constant storage)以及非随机存取存储区(Non-RAM)。
    1. 寄存器(register).寄存器与其他的存储区不同,它位于CPU中,所以它是存取速度最快的存储区.但是由于CPU中寄存器的数量是非常有限的,所以寄存器的使用是由CPU按需进行分配.编程的时候我们没有必要去对它进行直接控制.并且在程序中也没办法找到到底什么时候使用了寄存器的依据.
    2. 栈(stack).它位于随机存取存储器(RAM)中,并通过由CPU直接支持的栈指针(stack pointer)来进行操作.栈指针通过向下移动来分配新的存储空间,并通过向上移动来释放存储空间.这是仅次于寄存器的另一种高速有效地分配存储空间的方式.在创建程序期间,Java编译器必须知道存储在栈上的所有数据的准确大小和它们的生命周期.这样它才能够产生代码来让栈指针上下地移动.但这种约束限制了代码的灵活性,所以当某些Java存储区在栈上的时候——非凡是,对象引用(object reference)――而Java对象本身并不被存储在栈上.
    3. 堆(heap).这是存储Java所有对象的内存池(同样在RAM区域中).堆的优点是,它不像栈一样,编译器并不需要知道它应该从堆中分配多大的存储空间,或者存储空间应该在堆上存在多久.因此,在堆上分配多大的存储空间具有很大的灵活性.无论你何时需要创建对象,你只需要简单的在代码中使用关键字new来创建它就行了.在这段代码被执行时候就在堆(heap)上面分配相应的存储空间.当然,为这样的灵活性也需要付出一定的代价:在堆上分配存储空间比在栈上分配存储空间要更加耗时(假如你能够在Java中像在C++一样,在stack上创建对象的话).
    4. 静态存储区(static storage).”静态(static)”在这里表示”在一个固定的位置”(尽管它同样在RAM中).静态存储区包括:对于程序的整个运行期间都有效的数据.你能够使用关键字static来指定非凡的对象为静态的.但是Java对象自身却从来不放置在静态存储区中. 5. 常量存储区(constant storage).常量通常被直接放置在程序代码当中.这是安全的,因为它们永远不能被改变.在某些时候多个常量能够自己分隔开,这样在嵌入式系统中可以被任意地放置在只读存储器(ROM)中.
    6. 非RAM存储区(non-RAM storage).假如数据完全存在于程序之外,在程序未被运行时数据也能存在,而不受程序的控制.对于此类情况,有两个主要的例子来说明:一,对象流(streamed objects),这种对象通常以字节流的形式被送到其他的机器去.二,持久化对象(persistent objects),这种对象存放在磁盘上,即便是程序中止以后它们仍然保持原有状态.这些存储器都是将对象以某种形式保存在其他的介质中,然后在需要的时候再把它恢复为常规的基于RAM的对象.Java提供了对轻量级持久性的支持.在未来的Java版本中,Java将提供更加完备的持久性的支持.

    先从对象的创建说起
    int a=1;在编译时就创建了变量a存储在stack中,1就是它的值,它在内存中存在唯一的地址。
    Integer a=new Integer(1);编译时创建了变量a,类型是Integer,存储在stack中,有一个唯一地址,而1则是在运行时创建存储在heap中,也有一个唯一地址,类型是Integer,并且变量a指向1的内存地址,a里面放的就是1的内存地址。

    八大基本数据类型boolean,byte,char,short,int,long,float,double只能用==,比较的就是值,直接从stack中取出来比就是了。


    引用类型的比较有==和equals(),==比较的是两个对象所指向的内存地址,equals()比较两个对象所指向的内存地址的值,比如说

    Java codeString a=new String("1");
    String b=new String("1");
    System.out.println(a==b);  [color=#FF0000]false[/color]
    System.out.println(a.equals(b));  [color=#FF0000]true[/color]


    a.equals(b)比的就是a的值(相当于"1"的内存地址)和b,a本身也占有一个地址的。
    你不用new它就在编译时创建,存储在stack、静态存储区、常量存储区中
    一旦你new了,new后面的东西要等到运行时在heap中创建,new前面的东西还是在编译时创建存储在stack中
    所以a和b是指向不同的内存地址的,==就返回false,equals就返回true


    Java codeStringBuffer a=new StringBuffer("1");
    StringBuffer b=new StringBuffer("1");
    System.out.println(a==b);  [color=#FF0000]false[/color]
    System.out.println(a.equals(b));  [color=#FF0000]false[/color]

    而StringBuffer为什么会不行呢?
    因为StringBuffer没有重写equals()方法,所以它的equals还是Object.equals(Object o),比较的还是他们所指向的对象的内存地址,也就是他们是否指向同一个对象,与==号一样。

    大多数类都重写了equals()方法,Integer、Float、Date、String、ArrayList、HashMap等,没有重写equals()的方法也有很多,比如StringBuffer、StringBuilder等。
    哪个类有没有重写equals(),API上都有,看它的方法中有没有equals()就是了。

    还有个比较特殊的String,有着值类型的特征,但是本身却是引用类型
    JVM专门为String在内存中分配了一个String常量池,每次创建String类型的变量时申明部分在stack中创建,只要你不new,=号后面的它会从String常量池中查找是否已经存在要创建的值,存在就直接指向它,否则就在stack中创建一个新的对象,如果new了那就跟引用类型一样。那下面的例子也不难理解了。


    Java codeString a="1";
    String b="1";
    System.out.println(a==b);  [color=#FF0000]true[/color]
    System.out.println(a.equals(b));  [color=#FF0000]true[/color]

    申明部分a首先在stack中创建,不存在"1",在String常量池中创建"1",b再次创建时发现String常量池中有了"1",直接指向它,所以他们指向同一个对象,也就是指向的对象的地址相同。

     

     

    常量前置的优点:

    在许多代码中都能见到比较对象/变量是否为空,代码如下:
    if (obj   !=   null)
            ...
    但是常常我们可以看到这样的代码:
    if (null   !=   obj)
            ...
    把null写在前面有什么样的好处呢?
    这个习惯是这样来的。最早学C编程的时候,像if(a=b)这样的条件是编译通过的,尽管你的意思是if(a==b)。
    为了避免出现这种低级错误,写判断的时候习惯把常量写在前边,因为即使出现了if(null=a)这样的错误,也会被编译器提示错误,因为你不能给常量再赋值。
     
     
    另外,我们知道equals方法判断的时候把常量放在前面可以防止空指针异常:
    if ("".equals(obj))
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics