如何优雅地打印一个Java对象?

你好呀,我是沉默王二,一个和黄家驹同样身高,和刘德华同样颜值的程序员。虽然已经写了十多年的 Java 代码,但仍然以为本身是个菜鸟(请容许我惭愧一下)。css

在一个月黑风高的夜晚,我思前想后,以为不再能这么蹉跎下去了。因而痛下决心,准备经过输出的方式倒逼输入,以此来修炼本身的内功,从而进阶成为一名真正意义上的大神。与此同时,但愿这些文章可以帮助到更多的读者,让你们在学习的路上再也不寂寞、空虚和冷。java

为了更好的输入,我选择 Stack Overflow 做为战斗的第一线,毕竟不少前辈都在强烈推荐。本篇文章,咱们来探讨一下如何优雅地打印一个Java对象。程序员

真没想到,这个问题的访问量像阿尔泰山同样高,访问量足足有 29+ 万次,这不得了啊!说明有不少不少的程序员被这个问题困扰过。web

来回顾一下提问者的问题吧。数组

提问者定义了这样一个类:bash

public class Cmower {
    private String name;

    public Cmower(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
复制代码

而后建立了一个该类的对象,并尝试打印它:app

Cmower cmower = new Cmower("沉默王二");
System.out.println(cmower);
复制代码

可是输出的结果并非他想要的:ide

com.cmower.java_demo.stackoverflow.printObject.Cmower@355da254
复制代码

除此以外,他在打印数组的时候也出现了类似的问题:学习

Cmower [] cmowers = {new Cmower("沉默王二"), new Cmower("沉默王三")};
System.out.println(cmowers);
复制代码

输出结果为:ui

[Lcom.cmower.java_demo.stackoverflow.printObject.Cmower;@4dc63996
复制代码

Cmower@355da254[LCmower;@4dc63996 这样的输出结果表明着什么意思呢?怎么样才能把 Cmower 类的 name 打印出来呢?以及如何打印一个对象的列表(数组或者集合)呢?

若是你们也被这样的问题困扰过,或者正在被困扰,就请随来,我们肩并肩手拉手一块儿梳理一下这个问题,并找出最佳答案。Duang、Duang、Duang,打怪进阶喽!

0一、究竟发生了什么?

全部的 Java 对象都默认附带了一个 toString() 的方法,当咱们尝试打印这个对象的时候,该方法就会被调用。

System.out.println(object);  // 调用 object.toString()
复制代码

toString() 方法由 Object 类(全部 Java 对象的超类)定义,该方法会返回一个看起来晦涩难懂的字符串:

1)Class 名,由包名和类名组成,好比 com.Cmower
2)@ 链接符;
3)十六进制的哈希码。

来看一下该方法的源码:

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

数组和普通的 Java 对象相似,只有一点点不一样——追踪 Class 类的 getName() 方法就能够印证这一点。

If this class object represents a class of arrays, then the internal form of the name consists of the name of the element type preceded by one or more '[' characters representing the depth of the array nesting.

大体的意思就是,若是是一个数组的话,Class 名的前面会有一个或者多个英文中括号“[”,表示数组的维度(一维数组为一个“[”,二维数组为两个“[”),而后再紧跟一个元素的类型首字母。

这就是为何对象数组的前缀是“[L”的缘由。是否是有一种恍然大悟的感受?

0二、自定义输出

若是想在打印的时候输出本身预期的结果,就必须在自定义类中重写 toString() 方法,来看例子。

public class Cmower {
    private String name;
    // 省略构造方法和 getter/setter

    @Override
    public String toString() {
        return name;
    }
}
复制代码

当咱们再次打印 Cmower 对象时,输出结果就再也不是 com.Cmower@355da254 了。

沉默王二
复制代码

可是这样的结果并不会令咱们满意,它有些突兀,无法表示对象的类型。更优雅的作法是这样的:

public class Cmower {
    private String name;
    // 省略构造方法和 getter/setter

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[name=" + name + "]";
    }
}
复制代码

再次打印 Cmower 对象,输出结果为:

Cmower[name=沉默王二]
复制代码

这样的形式不只看起来美观,还可以在调试的时候给出有用的信息。可是,有时候咱们不想重写 toString() 方法(想保留原有的打印格式 ClassType@123121),又想打印该对象的信息,那么最好定义一个新的方法,好比说 toMyString() 方法。

0三、自动化输出

IDE(Eclipse 或者 Intellj IDEA) 一般会提供一种针对类的字段的输出格式,用来覆盖 toString() 方法。

@Override
public String toString() {
    return "Cmower{" +
            "name='" + name + '\'' +
            '}';
}
复制代码

另外,一些开源的第三方类库也会提供这样的功能,好比说:

1)Apache Commons Lang 的 ToStringBuilder。

使用方法:

@Override
public String toString() {
    return ToStringBuilder.reflectionToString(this);
}
复制代码

输出结果:

com.cmower.printObject.Cmower@355da254[name=沉默王二]
复制代码

2)Google Guava 的 MoreObjects

使用方法:

@Override
public String toString() {
    return MoreObjects.toStringHelper(this)
            .add("name", getName())
            .toString();
}
复制代码

输出结果:

Cmower{name=沉默王二}
复制代码

3)Lombok 的 @toString 注解(IDE 须要先安装 Lombok 的插件)

使用方法:

@ToString
public class Cmower {

    private String name;

    // 省略构造方法和 getter/setter
}
复制代码

只须要一个 @toString 注解,不须要覆盖 toString() 方法。

输出结果:

Cmower(name=沉默王二)
复制代码

0四、打印对象列表(数组或者集合)

上述内容已经把打印单个对象的事情唠明白了,are you ok?接下来,咱们来讲道说道打印对象列表的事儿。

1)数组

Arrays.toString() 能够将任意类型的数组转成字符串,包括基本类型数组和引用类型数组。代码示例以下。

Cmower[] cmowers = {new Cmower("沉默王二"), new Cmower("沉默王三")};
System.out.println(Arrays.toString(cmowers));
复制代码

输出结果:

[Cmower{name='沉默王二'}, Cmower{name='沉默王三'}]
复制代码

2)集合

对于集合来讲,能够直接打印就能输出咱们预期的结果。代码示例以下。

List<Cmower> list = new ArrayList<>();
list.add(new Cmower("沉默王二"));
list.add(new Cmower("沉默王三"));
System.out.println(list);
复制代码

输出结果:

[Cmower{name='沉默王二'}, Cmower{name='沉默王三'}]
复制代码

0五、鸣谢

好了,我亲爱的读者朋友,以上就是本文的所有内容了。能在疫情期间坚持看技术文,二哥必需要伸出大拇指为你点个赞👍。

原创不易,若是以为有点用的话,请不要吝啬你手中点赞的权力——由于这将是我写做的最强动力。

相关文章
相关标签/搜索