JAVA中String是引用类型,为什么打印出来的是值,不是地址值?String的toString方法return this本身返回?

System.out.println表示打印输出流(输出位置为控制台由jvm指定)

调用println方法,println有10个重载方法,

第一个空参很好理解就是输出一个换行,

第二个到第七个参数都是基本数据类型都是通过String.valueOf(x)将不同数据类型转换为String,就是是调用了包装类的toString方法将值转换成byte[],再通过new String(char value[])创建String对象返回,

参数boolean的重载方法不是调用包装类的静态toString方法,其实代码体跟包装类的静态toString方法是一样的,可以认为调用包装类的静态toString方法转换为String。

 第八个重载方法参数char[]直接进行输出操作不用转换成字符串

 第九个重载方法参数String,通过上面包装类转换String大概就能理解为什么String的toString会返回this本身地址了,因为后续输出打印需要通过地址获取char[]然后输出char[];

 第10个重载方法参数Object调用了自身的toString方法

你是否似曾相识,其实这就是你在打印对象的时候输出的地址值,而这地址值也成为“逻辑地址值”,是通过调用 hashCode() 方法得来的,hashCode() 被native修饰,不必考虑所以一个普通的对象如果没有重写toString() 方法,自然会得到如下形式的输出

全类名+‘@’+hashCode

为了明白为什么打印不是地址而是值进入writeln方法,synchronized (this)表示同步代码块,线程单进单出,再进入textOut.write(s)

再进入wirte()

这个就显然易见,write就是获取String的char[]对象,返回write输出。这就解释通了为什么sout输出的不是地址而是值,值就是字节数组输出连成的字符串。

再插一句jdk9版本的String之前用的是char[]存储,jdk9版本的String之后用的是byte[]存储

这时就有一个疑问为什么getChars读取的是char[]跟String的byte[]类型也不匹配,

我们点进getChars

在点进getChars

再点进inflate,就会发现byte[] 遍历强转为了 char[];

<think>我们正在解决一个Java打印问题:当打印对象时,显示的是类似`ClassName@hashcode`的内存地址形式,而不是我们期望的实际内容。用户特别提到字符串变量也显示为地址(虽然实际上字符串打印的是,但用户可能遇到了其他对象的问题)。我们将根据用户提供的引用和需求,提供解决方案。 关键点: 1. 在Java中,直接打印对象会调用该对象的`toString()`方法(继承自Object类)。 2. Object类的`toString()`方法默认实现是:`getClass().getName() + '@' + Integer.toHexString(hashCode())`,这就是我们看到内存地址形式的原因。 3. 对于字符串,因为String类重写了`toString()`方法,所以打印的是字符串内容。如果用户打印字符串变量显示为地址,那可能实际上不是字符串类型,或者存在其他问题(如数组)。 用户引用中提到的: 引用[4]提到:所有的引用数据类型都会继承Object,所有的打印都会走Object的toString()方法,而String重写了toString()方法。但打印字符串时,实际上走的是getChars()方法(这其实是String内部实现,但最终打印的还是字符串内容)。 因此,要解决打印对象显示为地址的问题,我们需要重写该对象的`toString()`方法返回我们想要的实际字符串表示。 步骤: 1. 确认要打印的对象类型。 2. 在该对象的类中重写`toString()`方法返回一个包含对象有意义信息的字符串。 示例: 假设我们有一个Person类,直接打印Person对象时显示的是地址。我们可以在Person类中重写`toString()`方法。 重写前: ```java public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } } ``` 打印: ```java Person p = new Person("张三", 20); System.out.println(p); // 输出类似 Person@1b6d3586 ``` 重写`toString()`方法后: ```java public class Person { private String name; private int age; // 构造方法... @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } ``` 再打印: ```java System.out.println(p); // 输出:Person{name='张三', age=20} ``` 特殊情况: 1. 数组:Java数组没有重写`toString()`方法,所以直接打印数组会显示地址。应该使用`Arrays.toString()`方法。 - 对于基本类型数组:`Arrays.toString(intArray)` - 对于对象数组:`Arrays.toString(objectArray)` - 多维数组:`Arrays.deepToString(multiArray)` 2. 集合类(如ArrayList、HashMap等):这些类已经重写了`toString()`方法,所以直接打印会显示内容。但如果集合中存放的是自定义对象,那么该自定义对象也需要重写`toString()`方法,否则在集合中打印该对象时还是显示地址。 用户提到字符串变量打印地址的问题: 实际上,字符串变量打印不会显示地址,因为String类重写了`toString()`。但以下情况需注意: - 如果是一个字符数组(char[]),直接打印不会显示字符串内容,而是地址。此时应该用`new String(charArray)`或者`Arrays.toString(charArray)`。 - 如果用户误将字符串数组当作字符串打印,也会打印地址。例如: String[] arr = {"a", "b"}; System.out.println(arr); // 打印地址 因此,针对字符串数组,应该使用`Arrays.toString(arr)`。 总结解决方案: 1. 对于自定义类:重写`toString()`方法。 2. 对于数组:使用`Arrays.toString()`或`Arrays.deepToString()`。 3. 确保要打印的对象类型正确,特别是当对象是数组时。 代码示例: ```java import java.util.Arrays; public class PrintExample { public static void main(String[] args) { // 自定义对象 Person person = new Person("Alice", 30); System.out.println(person); // 调用重写的toString // 整型数组 int[] intArray = {1, 2, 3}; System.out.println(intArray); // 错误:打印地址 System.out.println(Arrays.toString(intArray)); // 正确 // 字符串数组 String[] strArray = {"Hello", "World"}; System.out.println(strArray); // 错误:打印地址 System.out.println(Arrays.toString(strArray)); // 正确 // 字符数组(注意:字符数组比较特殊,可以直接用字符串构造) char[] charArray = {'a', 'b', 'c'}; System.out.println(charArray); // 正确:打印"abc"(这是因为PrintStream的println(char[])有重载,但注意不是调用toString) // 但是,如果使用字符串拼接等方式,会调用toString,这时会打印地址 System.out.println("" + charArray); // 打印地址?实际上,在拼接时,charArray会调用Object的toString,所以是地址 // 所以,为了安全,字符数组也应该用new String(charArray)或Arrays.toString System.out.println(new String(charArray)); // 输出abc System.out.println(Arrays.toString(charArray)); // 输出[a, b, c] } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + '}'; } } ``` 注意:字符数组直接传递给`println`时,会调用PrintStream中特定的`println(char[])`方法,该方法打印字符数组的内容。但是,在其他上下文(比如字符串拼接)中,字符数组会被当作普通对象,调用其`toString()`方法,从而打印地址。因此,为了代码一致性和避免混淆,建议对字符数组也使用`new String(charArray)`或`Arrays.toString(charArray)`。 最后,如果用户打印的是字符串变量,却看到地址,请检查该变量是否真的是字符串类型,还是其他类型(比如字符串数组或字符数组)。 如果用户无法修改类的源代码(比如使用第三方库的类),那么可以: - 如果该类已经提供了获取信息的方法,可以自己拼接字符串。 - 使用反射来获取字段(不推荐,破坏封装)。 - 使用第三方库,如Apache Commons Lang的`ToStringBuilder`。 示例使用ToStringBuilder(需要引入jar包): ```java @Override public String toString() { return ToStringBuilder.reflectionToString(this); } ``` 但通常,对于自己的类,重写`toString()`是最直接的方法。 根据用户引用[3]中关于变量的说明,注意成员变量和局部变量的作用域,在重写`toString()`时,可以访问类的成员变量。 因此,针对用户的问题,我们给出以上解决方案。</think>在 Java打印对象显示为内存地址而非实际的问题,是由于 Java 默认使用 `Object.toString()` 方法实现的。以下是系统分析和解决方案: ### 问题原因分析 1. **默认 `toString()` 行为**: - 所有 Java 对象继承自 `Object` 类 - `Object.toString()` 默认返回:`类名@十六进制哈希码`(如 `Person@1b6d3586`) - 这**不是真实内存地址**,而是 JVM 内部标识(引用[1]) 2. **字符串的特殊性**: - String 类重写了 `toString()`,直接返回字符串内容 - 如果打印字符串显示"地址",实际是字符数组(`char[]`)被误判(引用[4]) ### 解决方案(打印实际) #### 方法 1:重写 `toString()` 方法(推荐) ```java class Person { private String name; private int age; // 构造方法等... @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } // 使用示例 Person p = new Person("张三", 25); System.out.println(p); // 输出:Person{name='张三', age=25} ``` #### 方法 2:使用数组专用方法打印数组时(包括 `char[]`),需特殊处理: ```java char[] charArray = {'H', 'e', 'l', 'l', 'o'}; // 错误方式(显示地址) System.out.println(charArray); // 类似 [C@1b6d3586 // 正确方式 System.out.println(Arrays.toString(charArray)); // 输出 ['H','e','l','l','o'] System.out.println(new String(charArray)); // 输出 "Hello" ``` #### 方法 3:使用 JSON 库(复杂对象) ```java import com.google.gson.Gson; class Employee { private String id; private String department; // getters/setters } Employee emp = new Employee("E1001", "IT"); System.out.println(new Gson().toJson(emp)); // 输出:{"id":"E1001","department":"IT"} ``` ### 关键注意事项 1. **字符串变量**: ```java String str = "直接显示内容"; // 无需特殊处理 System.out.println(str); // 正常输出 ``` 2. **变量作用域规则**: - 局部变量必须初始化后才能使用(引用[2]) - 成员变量有默认引用[3]): ```java class Test { int num; // 默认 0 Object obj; // 默认 null // 打印前需检查 null } ``` 3. **打印机制本质**: - `System.out.println()` 会调用对象的 `toString()` - 字符串通过 `getChars()` 输出内容(引用[4]) - 其他对象需自定义 `toString()` ### 最佳实践总结 | **场景** | **解决方案** | **示例输出** | |------------------------|----------------------------------|---------------------------| | 自定义类对象 | 重写 `toString()` | `Person{name='张三'}` | | 基本类型数组 | `Arrays.toString()` | `[1, 2, 3]` | | 对象数组 | `Arrays.deepToString()` | `[Person{...}, ...]` | | 字符数组转字符串 | `new String(charArray)` | `"Hello"` | | 第三方库对象 | 使用 Gson/Jackson 序列化 | `{"field":"value"}` | | 集合类 (List/Map) | 直接打印(已重写toString) | `[a, b, c]` | > **关键结论**:要显示对象的实际而非"地址",必须通过重写 `toString()` 方法明确指定对象的字符串表示形式[^1][^4]。对于数组等特殊类型,需使用专用工具方法处理。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值