如何优雅地打印一个Java对象?
你好呀,我是沉默王二,一个和黄家驹一样身高,和刘德华一样颜值的程序员。虽然已经写了十多年的 Java 代码,但仍然觉得自己是个菜鸟(请允许我惭愧一下)。
在一个月黑风高的夜晚,我思前想后,觉得再也不能这么蹉跎下去了。于是痛下决心,准备通过输出的方式倒逼输入,以此来修炼自己的内功,从而进阶成为一名真正意义上的大神。与此同时,希望这些文章能够帮助到更多的读者,让大家在学习的路上不再寂寞、空虚和冷。

为了更好的输入,我选择 Stack Overflow 作为战斗的第一线,毕竟很多前辈都在强烈推荐。本篇文章,我们来探讨一下如何优雅地打印一个Java对象。
真没想到,这个问题的访问量像阿尔泰山一样高,访问量足足有 29+ 万次,这不得了啊!说明有很多很多的程序员被这个问题困扰过。
来回顾一下提问者的问题吧。
提问者定义了这样一个类:
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;
}
}
然后创建了一个该类的对象,并尝试打印它:
Cmower cmower = new Cmower("沉默王二");
System.out.println(cmower);
但是输出的结果并不是他想要的:
com.cmower.java_demo.stackoverflow.printObject.Cmower@355da254
除此之外,他在打印数组的时候也出现了相似的问题:
Cmower [] cmowers = {new Cmower("沉默王二"), new Cmower("沉默王三")};
System.out.println(cmowers);
输出结果为:
[Lcom.cmower.java_demo.stackoverflow.printObject.Cmower;@4dc63996
Cmower@355da254
和 [LCmower;@4dc63996
这样的输出结果代表着什么意思呢?怎么样才能把 Cmower 类的 name 打印出来呢?以及如何打印一个对象的列表(数组或者集合)呢?
如果大家也被这样的问题困扰过,或者正在被困扰,就请随我来,咱们肩并肩手拉手一起梳理一下这个问题,并找出最佳答案。Duang、Duang、Duang,打怪进阶喽!
01、究竟发生了什么?
所有的 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”的原因。是不是有一种恍然大悟的感觉?

02、自定义输出
如果想在打印的时候输出自己预期的结果,就必须在自定义类中重写 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()
方法。
03、自动化输出
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=沉默王二)
04、打印对象列表(数组或者集合)
上述内容已经把打印单个对象的事情唠明白了,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='沉默王三'}]
05、鸣谢
好了,我亲爱的读者朋友,以上就是本文的全部内容了。能在疫情期间坚持看技术文,二哥必须要伸出大拇指为你点个赞
如何优雅地打印一个Java对象?的更多相关文章
- 一个Java对象到底占用多大内存?
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个Java对象到底占用多大内存
在网上搜到了一篇博客讲的非常好,里面提供的这个类也非常实用: import java.lang.instrument.Instrumentation; import java.lang.reflect ...
- 一个Java对象到底占多大内存
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个Java对象到底占多大内存?(转)
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- java.lang.instrument: 一个Java对象占用多少字节?
一.对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针) 1. Mark Word 用于存储对象自身的运行时数据,如哈希码(HashCode).GC分代年 ...
- 【转】一个Java对象到底占多大内存?
最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...
- 一个 Java 对象到底有多大?
阅读本文大概需要 2.8 分钟. 出处:http://u6.gg/swLPg 编写 Java 代码的时候,大多数情况下,我们很少关注一个 Java 对象究竟有多大(占据多少内存),更多的是关注业务与逻 ...
- 高端面试必备:一个Java对象占用多大内存
这个问题一般会出现在稍微高端一点的 Java 面试环节.要求面试者不仅对 Java 基础知识熟悉,更重要的是要了解内存模型. Java 对象模型 HotSpot JVM 使用名为 oops (Ordi ...
- 如何复制一个java对象(浅克隆与深度克隆)
在项目中,有时候有一些比较重要的对象经常被当作参数传来传去,和C语言的值传递不同,java语言的传递都是引用传递,在任何一个地方修改了这个对象的值,就会导致这个对象在内存中的值被彻底改变.但是很多时候 ...
随机推荐
- Git安装--Windows
Git安装配置 在使用Git前我们需要先安装 Git.Git 目前支持 Linux/Unix.Solaris.Mac和 Windows 平台上运行. Git 各平台安装包下载地址为:http://gi ...
- Prometheus + Grafana 部署说明之「安装」
说明 在前面的Prometheus学习系列文章里,大致介绍说明了Prometheus和Grafana的一些使用,现在开始介绍如何从头开始部署Prometheus+Grafana,来监控各个相关的指标数 ...
- $POJ$3252 $Round\ Numbers$ 数位$dp$
正解:数位$dp$ 解题报告: 传送门$w$ 沉迷写博客,,,不想做题,,,$QAQ$口胡一时爽一直口胡一直爽$QAQ$ 先港下题目大意嗷$QwQ$大概就说,给定区间$[l,r]$,求区间内满足二进制 ...
- Linux常用命令大全(二)
Linux常用命令大全(二) cp命令 将源文件或目录复制到目标文件或目录中 注:如果是目录,需要使用-r选项 -d 复制时保留文件链接 -f 如果现存的目标文件不能打开,则删除并重试 -i 在覆盖目 ...
- 机器学习之路--KNN算法
机器学习实战之kNN算法 机器学习实战这本书是基于python的,如果我们想要完成python开发,那么python的开发环境必不可少: (1)python3.52,64位,这是我用的python ...
- 01_垂直居中body中的应用
1: 应用场景 在body中书写一个代码块, 使其相对于body垂直居中 <!DOCTYPE html> <html lang="en"> <head ...
- makefile个人理解
makefile makefile抽象层面的理解 学习某一样东西之前一定要明确学习的目的,即学习了这项工具能解决一些什么问题,其优势是什么? makefile的优势就是能够动态根据文件的新旧来决定是否 ...
- MongoDB高级用法
MongoDB高级查询用法大全 转载 http://blog.163.com/lgh_2002/blog/static/440175262012052116455/ 详见官方的手册:http://ww ...
- 常用crud
增:@Insert("insert into t_user (`last_name`, `sex`) values(#{lastName}, #{sex})") 删:@Del ...
- js css html加载顺序
1.js放在head中会立即执行,阻塞后续的资源下载与执行.因为js有可能会修改dom,如果不阻塞后续的资源下载,dom的操作顺序不可控.正常的网页加载流程是这样的.浏览器一边下载HTML网页,一边开 ...