Java实体类中使用GregorianCalendar设置指定日期并在toString()中正确格式化显示的问题咨询
先给你捋捋当前代码里的几个小问题,这就是为啥toString输出Date Of Publish=1/2/5这种奇怪结果的原因:
1. 为什么toString显示错误的日期值?
你在toString里用的dateOfPublish.YEAR、dateOfPublish.MONTH、dateOfPublish.DATE其实是Calendar类的静态常量,它们的固定值分别是1、2、5,根本不是你实例里的实际日期!要获取Calendar对象的真实年月日,得用get()方法,比如dateOfPublish.get(Calendar.YEAR)才行。
2. 正确创建包含特定日期的Book对象
首先要注意:GregorianCalendar的月份参数是从0开始的(0对应1月,11对应12月),所以你写的new GregorianCalendar(2018,3,20)实际创建的是2018年4月20日,不是3月哦。如果要创建2018年3月20日,推荐用Calendar的月份常量,可读性更强:
new Book(new GregorianCalendar(2018, Calendar.MARCH, 20));
另外你的setDateOfPublish方法完全没生效,现在的代码只是把参数赋值给自己,得改成这样:
public void setDateOfPublish(int day, int month, int year) { // 如果外部传入的月份是1-12(符合日常习惯),这里要转成0起始的Calendar月份 this.dateOfPublish = new GregorianCalendar(year, month - 1, day); }
3. 在实体类中使用SimpleDateFormat格式化日期
当然可以用!不过要注意SimpleDateFormat不是线程安全的,所以分两种场景处理:
场景一:单线程/低并发环境
直接在toString里每次创建新的实例就行,简单方便:
@Override public String toString() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); // 把Calendar转成Date对象再格式化 return "Date Of Publish=" + sdf.format(dateOfPublish.getTime()); }
场景二:多线程环境
用ThreadLocal来封装SimpleDateFormat,避免线程安全问题:
// 静态ThreadLocal,每个线程持有自己的格式化实例 private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = ThreadLocal.withInitial( () -> new SimpleDateFormat("yyyy/MM/dd") ); @Override public String toString() { return "Date Of Publish=" + DATE_FORMATTER.get().format(dateOfPublish.getTime()); }
4. 更推荐的日期类:Java 8+的LocalDate
GregorianCalendar和Date都是老旧API,坑特别多(比如月份0起始、对象可变、线程不安全)。Java 8及以后强烈推荐用java.time.LocalDate,这是不可变类,线程安全,API设计更符合直觉:
改造后的Book类示例
import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class Book { private LocalDate dateOfPublish; // 直接接受LocalDate的构造方法 public Book(LocalDate dateOfPublish) { this.dateOfPublish = dateOfPublish; } // 提供接受年月日的构造方法,月份直接用1-12(符合日常习惯) public Book(int year, int month, int day) { this.dateOfPublish = LocalDate.of(year, month, day); } public LocalDate getDateOfPublish() { return dateOfPublish; } public void setDateOfPublish(int day, int month, int year) { this.dateOfPublish = LocalDate.of(year, month, day); } @Override public String toString() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd"); return "Date Of Publish=" + dateOfPublish.format(formatter); } }
现在创建对象直接写new Book(2018, 3, 20)就代表2018年3月20日,不用再纠结月份偏移的问题,而且DateTimeFormatter是线程安全的,比SimpleDateFormat靠谱多了。
内容的提问来源于stack exchange,提问作者AhChing11

