你好呀,我是沉默王二,一个和黄家驹一样身高,和刘德华一样颜值的程序员。虽然已经写了十多年的 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@Overridepublic String toString() {return name;}
}

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

沉默王二

但是这样的结果并不会令我们满意,它有些突兀,没法表示对象的类型。更优雅的做法是这样的:

public class Cmower {private String name;// 省略构造方法和 getter/setter@Overridepublic 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对象?相关推荐

  1. 一个Java对象到底有多大?

    点击上方"方志朋",选择"置顶公众号" 技术文章第一时间送达! 出处:http://u6.gg/swLPg 编写Java代码的时候,大多数情况下,我们很少关注一 ...

  2. 一个 Java 对象到底有多大?

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 李小武 来源 | http://blog.li ...

  3. 一个Java对象到底占用多大内存?

    最近在调研MAT和VisualVM源码实现,遇到一个可疑问题,两者计算出来的对象大小不一致,才有了这样疑惑. 一个Java对象到底占用多大内存? 为了复现这个问题,准备了4个最简单类: class A ...

  4. java 如何循环执行一个对象_一个Java对象到底有多大?

    编写Java代码的时候,大多数情况下,我们很少关注一个Java对象究竟有多大(占据多少内存),更多的是关注业务与逻辑.但是殊不知,在我们不经意间,大量的内存被无形地浪费了. 一个Java对象到底有多大 ...

  5. 【趣话编程】一个Java对象的回忆录:垃圾回收

    简介:趣话编程第三期,今天让我们一起去看看一个Java对象的回忆录:垃圾回收. 原文链接 对象的诞生 "你醒啦!",迷迷糊糊中听到一个声音,我睁开了眼睛,发现一个小伙伴正看着我. ...

  6. 一个Java对象到底占多大内存?(转)

    最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...

  7. java有几大对象_一个 Java 对象到底有多大?

    阅读本文大概需要 2.8 分钟. 出处:http://u6.gg/swLPg 编写 Java 代码的时候,大多数情况下,我们很少关注一个 Java 对象究竟有多大(占据多少内存),更多的是关注业务与逻 ...

  8. 一个Java对象占用多大内存

    ​ 这个问题一般会出现在稍微高端一点的 Java 面试环节.要求面试者不仅对 Java 基础知识熟悉,更重要的是要了解内存模型. Java 对象模型 HotSpot JVM 使用名为 oops (Or ...

  9. 如何优雅的创建一个Java不可变对象类,JDK源码中也是这么干的!

    前面有篇文章当介绍了Java的不可变对象的一些特性,以及它的一些好处,但是并没有介绍如何实现一个不可变对象类.今天就来看看如何实现一个不可变对象类. Java中常用的不可变对象类 String类应该是 ...

最新文章

  1. Qt中的QWidget
  2. 34篇Java基础总结博客陪博主入门Java
  3. Golang 使用Protocol Buffer 案例
  4. python123m与n的数学运算_python小白进阶之路三——循环结构入门练习+Random库练习...
  5. python里面的报错语句翻译_翻译《Writing Idiomatic Python》(二):函数、异常
  6. gamma correct blurring
  7. 推荐一个自动破解替换密码的工具
  8. 火遍全网的 ChatGPT,给你的求职新方向
  9. 屏的像素与传输速率_HDMI线的传输速率是如何定义的
  10. 数组和链表分别比较适合用于什么场景
  11. 网络连接变成小地球,提示无法访问internet
  12. IEEE 期刊双栏模板引用文献问题
  13. python与区块链_python与区块链
  14. 安装双MeeGo系统
  15. python去掉每行前面_第一个python去掉行号
  16. 设计模式:工厂方式模式、抽象工厂模式
  17. sublime text 3开启vi编辑模式
  18. R语言T检验与Z检验
  19. BAT人工智能生态时局图:全面战争爆发前夜
  20. Matlab中十字线出不来,如何在MATLAB中实现已弃用的完整十字线指针功能?

热门文章

  1. 用ch341a刷写主板bios
  2. 【算法竞赛学习笔记】佩尔方程-数学提升计划
  3. DDR3 数据传输 (一)
  4. 寻找真实IP-子域名查找
  5. 福昕阅读器常用快捷键
  6. mp2551总线收发器芯片作用_高速CAN收发器MCP2551
  7. 在校生的实习经历怎么写
  8. 西瓜创客python在线编译器_西瓜创客在线少儿编程 - 课程
  9. 【西瓜创客】2022学年5月21日NOC大赛-初赛线上试题
  10. 基于AT89S52芯片+LCD1602液晶显示+DS12C887时钟模块的数字时钟