美文网首页
Java中常见的坑

Java中常见的坑

作者: IT程序狮 | 来源:发表于2016-07-25 09:32 被阅读421次

    译文地址:Java中常见的坑

    概述

    Java是门极简风格的语言,比其它语言相比,它故意保持较少的特性,不仅在有些不常见的情况下会出些奇奇怪怪的错误,即使很一般的情况下也有可能让人栽根头。如果你习惯了别的语言,你读 Java 的代码很容易搞错一些概念。

    变量要么是引用 ,要么是基础类型

    这是对的,变量不是对象。也就是说在下面这个例子里,s不是个对象,也不是字符串,它只是一个字符串的引用。

    String s = "Hello";
    

    这个能解释很多的问题,比如: 如果说字符串是不可变的,为什么我能修改它? s+=“!” 回答:在Java里是不可变的,你改变的只是引用而已。

    ==比较的是引用,不是内容

    让人更混乱的是,有时候用==是能比较内容的。如果你有两个一样的不可变值,JVM会尝试引用同一个对象 。

    String s1 = "Hi", s2 = "Hi";
    Integer a = 12, b = 12;
    

    这两个例子中用到了对象池,所以最后引用的是同样的对象。s1==s2a==b都是返回true,JVM已经把两个引用都指向了同一个对象。然而,如果稍微改下代码,JVM没有把对象放到池里的话,==就会返回false,可能会让你意想不到。这个时候你得用equals了。

    String s3 = new String(s1);
       Integer c = -222, d = -222;
    s1 == s2      // is true
      s1 == s3      // is false
      s1.equals(s3) // is true
      a == b        // is true
      c == d        // is false (different objects were created)
      c.equals(d)   // is true
    

    对于整型来说,对象池缓存的范围是-128到127(还有可能更高)。

    Java通过传值进行引用传递

    所有的变量都是传值,包括引用。这就是说如果你有个变量,它是一个对象的引用,这个引用会被拷贝后再传参,而不是传递的对应的那个对象。

    public static void addAWord(StringBuilder sb) {
         sb.append(" word");
         sb = null;
    }  
    
    
    StringBuilder sb = new StringBuilder("first ");
    addWord(sb);
    addWord(sb);
    System.out.println(sb); // prints "first word word"
    

    引用的对象可以改变,不过如果修改拷贝的这个引用,对调用方是没有影响的。

    在大多数JVM实现里,Object.hashCode和内存地址无关

    hashCode必须是保持不变的。不然的话HashSet或者ConcurrentHashMap就没法玩了。然而对象可以在内存的任何地方,并且它的位置还可能不断变化,而这个对你的程序来说是透明的。使用内存地址来当做hashCode是不可行的(除非你自己有一个JVM,对象是固定不动的)。 对于 OpenJDK 和Hotspot JVM来说,hashCode是按需生成的,并存储在对象的头部。使用Unsafe API你看到hashCode是否已经生成了,甚至还可以修改它。

    Object.toString那些不为人知的事情

    toString的默认行为是打印类的的内部名称还有对象的hashCode。 上面已经提到,hashCode并不是内存地址,尽管它是用16进制打印的。同样的,类名,尤其是数组的类名,更容易让人头晕。比如说String[]的名称是[Ljava.lang.String;这个[表明它是个数组,L说明它是Java语言(Language)创建的类,并不是基础类型比如byte这些,顺便提一下byte内部名称是B。;号标识类的结束。比如你有个这样的数组:

    String[] words = { “Hello”, “World” };
    System.out.println(words);
    

    输出会是这样:

    [Ljava.lang.String;@45ee12a7
    

    很不幸你只知道这是个对象数组。如果你只有一个Object实例words,这样是不够的,你得调用下Arrays.toString(words)。这极其恶劣地破坏了封装的原则,在StackOverflow上面这也是最常见的一类问题。

    我问过Oracle的好些个工程师,从他们的反馈感觉,这个问题目前很难解决。

    相关文章

      网友评论

          本文标题:Java中常见的坑

          本文链接:https://www.haomeiwen.com/subject/bcygjttx.html