学习日志06 java
还有四天要去比赛了,能赢吗?逼自己一把。。。!!加油!
1 对比一下java重写还是不重写tostring的区别
1. 不重写 toString()
的情况
java
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}
}public class Main {public static void main(String[] args) {Point p = new Point(10, 20);System.out.println(p); // 直接打印对象System.out.println(p.toString()); // 显式调用 toString()}
}
输出结果:
Point@1b6d3586
Point@1b6d3586
分析:
- 默认行为:所有类继承自
Object
,默认的toString()
返回类名@哈希码
。 - 问题:返回值无法提供对象的实际状态(如
x
和y
的值),对调试和日志不友好。
2. 重写 toString()
的情况
java
class Point {private int x;private int y;public Point(int x, int y) {this.x = x;this.y = y;}// 重写 toString() 方法@Overridepublic String toString() {return "Point{x=" + x + ", y=" + y + "}";}
}public class Main {public static void main(String[] args) {Point p = new Point(10, 20);System.out.println(p); // 自动调用 toString()System.out.println(p.toString()); // 显式调用}
}
输出结果:
Point{x=10, y=20}
Point{x=10, y=20}
分析:
- 自定义格式:重写后返回包含对象属性的字符串,直观展示对象状态。
- 自动调用:
System.out.println(p)
会隐式调用p.toString()
。
3. 核心区别总结
对比项 | 不重写 toString() | 重写 toString() |
---|---|---|
返回值 | 类名@哈希码 (如 Point@1b6d3586 ) | 自定义格式(如 Point{x=10, y=20} ) |
可读性 | 低(无法获取对象状态) | 高(清晰展示属性值) |
应用场景 | 无实际意义,仅用于标识对象内存地址 | 调试、日志记录、UI 展示对象信息 |
调用方式 | 手动调用 obj.toString() 才有输出 | 自动调用(如 print(obj) 或字符串拼接时) |
4. 常见误区澄清
-
误区 1:“不重写
toString()
就不能打印对象”
✅ 错误:可以打印,但输出的是无意义的哈希码。 -
误区 2:“只有
System.out.println()
会调用toString()
”
✅ 错误:所有需要字符串的场景都会自动调用,例如:java
String s = "Point: " + p; // 等价于 "Point: " + p.toString()
-
误区 3:“重写
toString()
会影响对象的其他功能”
✅ 错误:toString()
仅用于生成字符串表示,不影响对象的核心逻辑。
5. 最佳实践
- 所有自定义类都应重写
toString()
,方便调试和日志记录。 - 格式推荐:使用 JSON 风格(如
{key=value}
)或明确的描述性文本。 - IDE 自动生成:大多数 IDE(如 IntelliJ、Eclipse)可自动生成
toString()
代码。例如:java
// IntelliJ 自动生成的 toString() @Override public String toString() {return "Point{" +"x=" + x +", y=" + y +'}'; }
通过重写 toString()
,可以让对象在打印时提供有价值的信息,避免输出无意义的内存地址,这是 Java 开发中的基础规范之一。
2 对比一下我写的代码和优雅的代码,需要进步
我写的,也是敢于接受批评直接全部放上了哈。题目也在里面
package PTA_training;
/*
7-3 定义商品类,封装成员变量,输出对象
定义一个商品类。创建对象并按指定格式输出它。
商品类要求:
(1)成员变量:商品编号(String) 、商品名称(String)、商品单价(double)
(2)成员变量封装,定义为私有属性,并为每个成员变量定义getXXXX,setXXXX方法
(3)定义构造方法,要求带三个参数,参数值用于给成员变量赋值。
(4)重写toString()方法,将对象转换为字符串,格式:商品编号,商品名称,商品单价
测试类要求:
按指定的格式 输入商品信息,调用构造方法生成对象,并输出它。
例:输入:WJ002 记事本 5.5
输出:WJ002,记事本,5.5
输入商品的信息,每个属性值之间用1个空格分隔。
输出 格式,商品的每个属性值之间用逗号分隔。
输入样例:
WJ002 记事本 5.5
输出样例:
WJ002,记事本,5.5*/import java.util.Scanner;public class Demo2_3 {private String number ;private String name;private double price;public void setNumber(String number){this.number=number;}public String getNumber() {return number;}public void setName(String name) {this.name = name;}public String getName() {return name;}public void setPrice(double price) {this.price = price;}public double getPrice() {return price;}public Demo2_3(String number ,String name ,double price){this.number=number;this.name=name;this.price=price;}@Overridepublic String toString() {return number+","+name +"," +price;}public static void main(String[] args) {Scanner scanner =new Scanner(System.in);String number= scanner.next();String name= scanner.next();double price= scanner.nextDouble();Demo2_3 a = new Demo2_3(number,name,price);System.out.println(a);scanner.close();}
}
ai生成优化的优雅的代码
package PTA_training;import java.util.Scanner;public class Product {private final String id;private final String name;private final double price;public Product(String id, String name, double price) {this.id = id;this.name = name;this.price = price;}public String getId() { return id; }public String getName() { return name; }public double getPrice() { return price; }@Overridepublic String toString() {return String.format("%s,%s,%.1f", id, name, price);}public static void main(String[] args) {try (Scanner scanner = new Scanner(System.in)) {Product product = new Product(scanner.next(),scanner.next(),scanner.nextDouble());System.out.println(product);}}
}
代码对比分析
以下是你的代码与优化版本的详细对比:
对比项 | 你的代码 | 优化版本 |
---|---|---|
类命名 | Demo2_3 (无明确语义) | Product (清晰表达业务含义) |
成员变量 | number (普通描述) | id (专业术语) |
无final 修饰 | final 修饰(不可变对象) | |
构造方法 | 参数顺序与输入一致 | 保持相同参数顺序 |
getter/setter | 完整实现(有 setter) | 仅保留 getter(数据不可变) |
toString() | 简单字符串拼接 | String.format (精确控制格式) |
资源管理 | 未关闭 Scanner(原来忘记关了55) | try-with-resources(自动关闭) |
main 方法 | 多个局部变量 | 直接构造对象(减少临时变量) |
输出格式 | 直接拼接(可能出现多位小数) | %.1f (强制保留 1 位小数) |
业务封装 | 功能集中在一个类 | 职责分离(商品类专注业务) |
线程安全 | 可变对象 | 不可变对象(线程安全) |
代码行数 | 48 行 | 32 行(减少 33%) |
具体改进点解释
-
命名优化
Product
类名明确表示这是一个商品模型id
属性更符合专业领域术语(如数据库设计)
-
不可变设计
java
// 你的代码 private String number; // 可修改// 优化后 private final String id; // 不可变
不可变对象在多线程环境中更安全,且符合数据封装原则
-
格式化输出
java
// 你的代码 return number+","+name +"," +price; // 可能输出5.5000// 优化后 return String.format("%s,%s,%.1f", id, name, price); // 强制保留1位小数
-
资源管理
java
// 你的代码 Scanner scanner = new Scanner(System.in); // 需要手动关闭// 优化后 try (Scanner scanner = new Scanner(System.in)) { // 自动关闭// 使用scanner }
-
代码精简
java
// 你的getter public String getNumber() {return number; }// 优化后的getter public String getId() { return id; } // 单行格式
为什么这些改进更好
-
可维护性:语义化命名和清晰的结构使代码更易于理解和修改
-
健壮性:
- 不可变对象减少了程序状态的复杂性
- 资源自动关闭避免了内存泄漏风险
-
专业性:
- 使用领域术语(如
id
)体现专业素养 - 精确的格式控制符合企业级应用要求
- 使用领域术语(如
-
性能:
- String.format 在频繁格式化时性能更优
- 减少不必要的对象创建(如临时变量)
-
扩展性:
- 职责分离的设计更容易添加新功能
- 不可变对象支持函数式编程范式
建议在后续学习中关注:
- 面向对象设计原则(SOLID)
- Java 8+ 的函数式特性
- 单元测试与代码覆盖率
- 代码静态分析工具(如 SonarLint)的使用
================================可以了接下来就对着这个学习===================
谢谢伟大的AI!!!!
3 感受进步,初版(无法跑起来的代码)vs 满足题目要求的代码
package PTA_training;
/*
7-4 定义类与创建对象定义一个类Person,定义name和age属性,定义有参的构造方法对name和age进行初始化。
在测试类中创建该类的2个对象,姓名、年龄分别为lili、19和lucy、20,在屏幕打印出2个对象的姓名和年龄。
输入格式:
本题目无输入
输出格式:
在一行中输出一个人的姓名和年龄
输入样例:
在这里给出一组输入。例如:
输出样例:
在这里给出相应的输出。例如:
this person is lili,her age is 19
this person is lucy,her age is 20*/public class Demo2_4 {private static String name = null;public static String name1;public static String name2;public static int age = 0;public static int age1;public static int age2;public Demo2_4(String name, int age){Demo2_4.name =name;Demo2_4.age =age;name1 ="lili";name2 ="lucy";age1 = 19;age2 = 20;}@Overridepublic String toString() {return"this person is "+name+",her age is "+age;}public static void main(String[] args) {Demo2_4 a = new Demo2_4(name1,age1);Demo2_4 b = new Demo2_4(name2,age2);System.out.println(a);System.out.println(b);}public String getName() {return name;}public int getAge() {return age;}
}
//只给提示不给正确答案,这个代码有问题,我应该怎么办
提示:
你的代码有以下问题,按顺序解决它们:
- 类名错误:题目要求定义
Person
类,但你仍在使用Demo2_4
作为主类名 - 静态属性问题:
name
、age
等属性被声明为static
,导致所有对象共享同一属性- 静态属性会被最后一次赋值覆盖,无法保存多个对象的状态
- 构造函数逻辑错误:
- 在构造函数中固定设置
name1
、name2
、age1
、age2
的值 - 每次创建对象都会覆盖这些静态变量,导致数据混乱
- 在构造函数中固定设置
- 对象创建顺序问题:
- 在创建对象
a
和b
时,name1
和age1
尚未初始化(null
和0
) - 构造函数会先使用这些未初始化的值,然后再设置静态变量
- 在创建对象
提示:
- 创建
Person
类并定义非静态的name
和age
属性 - 在
Person
类的构造函数中直接初始化当前对象的属性(使用this.name
和this.age
) - 在测试类的
main
方法中创建两个Person
对象,分别传入 "lili",19 和 "lucy",20 作为参数 - 确保每个对象的属性独立保存,不使用静态变量
package PTA_training;public class Demo2_4 {String name;int age;public Demo2_4(String n, int a){this.name = n;this.age = a;}@Overridepublic String toString() {return"this person is "+name+",her age is "+age;}public static void main(String[] args) {Demo2_4 a = new Demo2_4("lili",19);Demo2_4 b = new Demo2_4("lucy",20);System.out.println(a);System.out.println(b);}}
//我改成这样了,对比一下给我进步
对比:
你的修改非常棒!现在代码已经完全符合题目要求了。主要改进包括:
-
类设计优化:
- 将
name
和age
改为实例变量(去掉了static
修饰符) - 每个对象现在拥有独立的属性值,不会相互干扰
- 将
-
构造函数修正:
- 直接通过参数初始化当前对象的属性(
this.name = n
) - 不再使用额外的静态变量(如
name1
,name2
)
- 直接通过参数初始化当前对象的属性(
-
对象创建方式:
- 在
main
方法中直接传入具体参数创建对象 - 正确初始化了两个不同的
Demo2_4
对象
- 在
-
输出格式:
- 通过重写
toString()
方法确保输出格式符合题目要求
- 通过重写
关键进步总结:
- 理解了实例变量和静态变量的区别
- 掌握了构造函数的正确使用方法
- 学会了通过对象实例化保存不同数据
最终建议:
根据题目要求,类名应该是Person
而不是Demo2_4
。虽然代码功能正确,但可能因为类名不符导致测试不通过。可以将类名改为Person
,其他部分保持不变即可:
java
public class Person {String name;int age;public Person(String n, int a) {this.name = n;this.age = a;}@Overridepublic String toString() {return "this person is " + name + ",her age is " + age;}public static void main(String[] args) {Person a = new Person("lili", 19);Person b = new Person("lucy", 20);System.out.println(a);System.out.println(b);}
}
这样就完全符合题目要求了!继续加油,你已经掌握了面向对象编程的基础概念。
4 我的最终代码和ai提供的最优雅代码
以下是优化后的代码,包含详细注释和最佳实践:
java
// 使用题目要求的类名Person
public class Person {// 使用private封装属性,提供公共访问方法private String name;private int age;// 构造方法:参数名与属性名保持一致,使用this区分public Person(String name, int age) {this.name = name; // this.name指类的属性,=右边的name是参数this.age = age;}// 提供公共的访问器方法(Getter)public String getName() {return name;}public int getAge() {return age;}// 重写toString方法,使用String.format提高可读性@Overridepublic String toString() {return String.format("this person is %s,her age is %d", name, age);}// 主方法作为程序入口public static void main(String[] args) {// 创建对象时直接传入字面量参数Person person1 = new Person("lili", 19);Person person2 = new Person("lucy", 20);// 打印对象自动调用toString()System.out.println(person1);System.out.println(person2);// 可选:演示如何通过访问器获取属性System.out.printf("Name: %s, Age: %d%n", person1.getName(), person1.getAge());}
}
关键优化点对比:
-
类设计:
- 使用
Person
作为类名(符合题目要求) - 将属性设为
private
(封装性原则) - 提供公共的
getter
方法(数据访问控制)
- 使用
-
构造方法:
java
// 原代码 public Demo2_4(String n, int a) { ... }// 优化后 public Person(String name, int age) { ... }
- 参数名与属性名保持一致(提高可读性)
- 使用
this
明确指向类属性
-
toString 方法:
java
// 原代码 return "this person is " + name + ",her age is " + age;// 优化后 return String.format("this person is %s,her age is %d", name, age);
- 使用
String.format
提高字符串拼接的可读性 - 占位符
%s
(字符串)和%d
(整数)使格式更清晰
- 使用
-
变量命名:
java
// 原代码 Demo2_4 a = new Demo2_4("lili", 19);// 优化后 Person person1 = new Person("lili", 19);
- 使用更具描述性的变量名
person1
、person2
- 类型名与变量名形成语义关联
- 使用更具描述性的变量名
-
可扩展性:
- 添加了
getName()
和getAge()
方法 - 为类未来的功能扩展预留接口
- 符合 JavaBean 规范(私有属性 + 公共访问器)
- 添加了
为什么这样设计更优雅:
-
遵循面向对象原则:
- 封装性:通过
private
属性和公共方法控制访问 - 可读性:类名、方法名、变量名均表意明确
- 可维护性:结构清晰,易于添加新功能
- 封装性:通过
-
代码健壮性:
- 避免直接访问属性(如
person.name = "xxx"
) - 通过方法可以添加逻辑校验(如年龄合法性检查)
- 避免直接访问属性(如
-
符合行业规范:
- 使用标准的 JavaBean 模式
- 字符串格式化优先使用
String.format
- 构造方法参数名与属性名一致
-
最佳实践:
- 为每个属性提供
getter
(如果需要修改,还应提供setter
) - 重写
toString()
以便调试和日志输出 - 使用有意义的变量名提高代码自解释性
- 为每个属性提供
---------------------------------------------------------------------------------------------------------------------------------这个时代有ai大模型实在是太好了!/(ㄒoㄒ)/
5 对自己所写的代码要有清晰的认知,细节明白,感谢ai注释
好的,让我们逐行分析这个代码,并解释每一步的作用:
java
public class Demo2_4 {// 定义类的属性(成员变量)String name; // 存储人的姓名,每个对象独立拥有int age; // 存储人的年龄,每个对象独立拥有// 构造方法:用于创建对象时初始化属性值public Demo2_4(String n, int a) {this.name = n; // 将参数n的值赋给当前对象的name属性this.age = a; // 将参数a的值赋给当前对象的age属性}// 重写toString()方法:控制对象打印时的输出格式@Overridepublic String toString() {return "this person is " + name + ",her age is " + age;}// 程序入口点public static void main(String[] args) {// 创建第一个对象:姓名"lili",年龄19Demo2_4 a = new Demo2_4("lili", 19);// 创建第二个对象:姓名"lucy",年龄20Demo2_4 b = new Demo2_4("lucy", 20);// 直接打印对象时,会自动调用对象的toString()方法System.out.println(a); // 输出:this person is lili,her age is 19System.out.println(b); // 输出:this person is lucy,her age is 20}
}
核心概念解释:
-
实例变量(非静态属性):
java
String name; int age;
- 每个对象独立拥有一份
name
和age
- 创建多个对象时,它们的属性值互不影响
- 每个对象独立拥有一份
-
构造方法:
java
public Demo2_4(String n, int a) {this.name = n;this.age = a; }
this.name
表示当前对象的name
属性- 参数
n
和a
是临时变量,仅在构造方法内部有效 - 作用:在创建对象时必须传入姓名和年龄,立即完成初始化
-
对象创建过程:
java
Demo2_4 a = new Demo2_4("lili", 19);
new Demo2_4("lili", 19)
:调用构造方法,创建一个新对象"lili"
和19
作为参数传递给构造方法- 构造方法将参数值赋给对象的
name
和age
属性 Demo2_4 a = ...
:将新创建的对象引用赋值给变量a
-
方法重写:
java
@Override public String toString() {return "this person is " + name + ",her age is " + age; }
@Override
:告诉编译器这是重写父类的方法toString()
是 Object 类的方法,所有对象都继承这个方法- 重写后,当你
System.out.println(a)
时,会自动调用这个方法
对比之前的错误:
你的原始代码中使用了静态变量:
java
private static String name = null;
public static int age = 0;
静态变量属于类,所有对象共享同一份数据。无论创建多少个对象,它们都会修改同一个name
和age
。
而现在的写法:
java
String name; // 实例变量
int age; // 实例变量
每个对象都有自己独立的name
和age
,互相不干扰。这是面向对象编程的基础概念。
//现在的学习方法让我感受到了进步!