你真的了解 toString() 吗?(上)

简介: 你真的了解 toString() 吗?(上)

可能有的小伙伴看到这个问题会不屑一顾,这个玩意老子天天用!


可是,相信你一定有过被 NullPointerException 支配的痛苦经历,而 toString() 方法就是其中一个罪魁祸首!


我先问大家一个问题,是不是所有的Java变量都有toString呢?


如果你觉得有,请把1打在公屏上,如果没有,就打2。(话说我在干嘛,这是博客,又不是Bilibili,emmm,不过可以考虑后期写个弹幕系统)


现在我来公布答案:基本数据类型是没有的,不能使用toString方法!


不相信你可以在Eclipse或者idea里面试一试,编译会报错的。


包装类


上面已经说了,基本数据类型是没有toString的,那么包装类有吗?

包装类,用的比较多的,那就是Integer了,看下Integer有没有toString 呢?答案肯定是有的,包装类不是基本数据类型,那就是有的。

public class StringTest1 {
    public static void main(String[] args) {
        Integer i = 1;
        System.out.println(i.toString());
    }
}

注意,Integer的toString方法用了重载(jdk1.8版本源码)

public String toString() {
   return toString(value);
}

重新调用了另一个toString,也是写在Integer类中:

public static String toString(int i) {
    if (i == Integer.MIN_VALUE)
        return "-2147483648";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}

这些代码如果看的吃力,也没有关系,我们只需要理解大概的意思就行。在调用toString方法的时候,实际上就是把Integer类内部的value属性,转换成一个String对象返回。


Integer自动装箱原理

顺便说一下Integer的自动装箱原理,有些面试题经常喜欢考察这个。

Integer作为一个典型的包装类,当你写了如下代码:


Integer i = 1;

实际上,编译器会调用Integer的valueOf方法,将原始类型值转换成Integer对象,这个过程我们是感觉不到的。Integer还维护了一个缓存IntegerCache,它会判断i是否在-128和127之间,存在则从IntegerCache中获取包装类的实例,否则new一个新实例。IntegerCache使用了享元模式,其内部维护了一个缓存池,数值范围是-128和127之间,这个缓存是静态的,类加载的时候会自动加载。因为我们正常使用Integer的时候,赋予的值一般不会太大,所以只需牺牲一点点内存,这里用享元模式可以有效地提升效率。


附上Integer的valueOf方法:

public static Integer valueOf(int i) {
    //判断i是否在-128和127之间,存在则从IntegerCache中获取包装类的实例,否则new一个新实例
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

这个功能从jdk1.5开始就有了。


Object类

在Java中,所有的类都继承自Object,所以我们有必要再来看看Object的情况。首先是Object类的toString实现:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

可以看到,Object的toString方法就是把对象的hashCode值转为16进制而已。比如:

java.lang.Object@16d3586

但是实际上,调用toString还是会根据当前对象来具体分析的,比方说下面的代码:

Object i = 1;
System.out.println(i.toString());

1明明是基本数据类型,但是赋值给Object以后,编译器就会把它看成是Integer对象。不妨做一个实验,在eclipse或者idea中打开调试模式,然后在Integer的toString方法中打上一个断点。当你调试这段代码的时候,就会发现可以进入到Integer的toString方法。


32.png

32.png

这也就印证了,这种情况编译器是会认识到,当前的1应该是一个Integer对象。

再看一个例子:

Object i = new ArrayList<>();
System.out.println(i.toString());

这时候的i其实是ArrayList的实例对象,所以实际调用toString方法的时候,也就是直接调用ArrayList的toString方法了,而ArrayList并未对toString进行重写,而是ArrayList的父类AbstractList,AbstractList的父类AbstractCollection重写了toString:

public String toString() {
    Iterator<E> it = iterator();
    if (! it.hasNext())
        return "[]";
    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            return sb.append(']').toString();
        sb.append(',').append(' ');
    }
}

这也是为什么,我们打印ArrayList的时候(这个操作实在是很普遍,懂的都懂),打印出来的是一个数组字符串。


综上,Object类调用toString,如果当前实际的对象没有重写toString,就是调用父类的toString,如果父类也没有重写toString,就层层冒泡最终到Object类。


相关文章
|
2月前
equals
==是进行对象的地址值比较,如果确实需要字符串的内容比较,可以使用两个方法 public boolean equals(0bjectobj):参数可以是任何对象,只有参数是一个字符串并且内 容相同的才会给true;否则返回false 注意事项: 1.任何对象都能用object进行接收。 2.equals方法具有对称性,也就是a.equals(b)和b.equals(a)效果一样。 3.如果比较双方一个常量一个变量,推荐把常量字符串写在前面。 推荐:"abc".equals(str) 不推荐:str.equals("abc") public boolean egualsIgnoreCas
20 4
|
9月前
|
Java
toString()方法
toString()方法
39 0
|
Apache
toString 更优雅的实现方式
toString 更优雅的实现方式
|
安全 Java 索引
Java底层源码——Arrays.toString(数组) & object.toString() & new String()
Java底层源码——Arrays.toString(数组) & object.toString() & new String()
82 0
|
Java
说说equals() (上)
说说equals() (上)
112 0
说说equals() (上)