Java中的时间和日期(四):与java8时间API有关的一些总结和补充
在了解完java8中新版本的时间API之后,当然,并不是全部了解,java.time包下面接近上百个类,没办法一一去了解。作为我们日常用来替换java.util.date的功能。也不需要全部了解。在看过若干代码之后,有如下总结。
1.关于Immutable对象的线程安全问题
如果在面试过程中,关于Immutable首先需要聊到的内容就是String类。String类内部是一个final修饰的字符数组。
private final char value[];
一旦创建之后,就不能对这个对象做任何修改。也不会提供任何有关的set方法。如subString等方法都是产生一个新的对象。这样来保障了线程的安全性。 不可变对象的好处就是简单,然后可以很容易的复用。但是缺点是不得不为每次操作生成一个新的对象。如果不是太大的对象,在现有GC的能力之下,一般不会有太大的问题。当然,对于频繁的改变处理,String也提供了StringBuilder、StringBuffer等专门来处理这种频繁修改操作的工具。 在Effective java这本经典的著作之中第十七条:使可变性最小化–要求每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期( lifetime )内固定不变。 实现类的不可变性要遵守如下五条规则:
- 1.不要提供任何会修改对象状态的方法(set方法)。
- 2.保证类不会被扩展。这样可以防止粗心或者恶意的子类假装对象的状态已经改变,从而破坏该类的不可变行为。
- 3.声明所有的域都是final的。通过系统的强制方式可以清楚地表明你的意图。
- 4.声明所有的域都为私有的。这样可以防止客户端获得访问被域引用的可变对象的权限,井防止客户端直接修改这些对象。
- 5.确保对子任何可变组件的互斥访问。如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用。并且,永远不要用客户端提供的对象引用来初始化这样的域,也不要从任何访问方法( accessor )中返回该对象引用。在构造器、访问方 法和readObject方法(详见第88条)中请使用保护性拷贝( defensive copy )技术(详见第50 条)。
我们可以查看所有新版本时间API相关的类。基本上全部的属性都是private final 修饰的。而且不提供任何set方法。 如在 Instant中:
/**
* Constant for the 1970-01-01T00:00:00Z epoch instant.
*/
public static final Instant EPOCH = new Instant(0, 0);
/**
* The minimum supported epoch second.
*/
private static final long MIN_SECOND = -31557014167219200L;
/**
* The maximum supported epoch second.
*/
private static final long MAX_SECOND = 31556889864403199L;
/**
* The minimum supported {@code Instant}, '-1000000000-01-01T00:00Z'.
* This could be used by an application as a "far past" instant.
* <p>
* This is one year earlier than the minimum {@code LocalDateTime}.
* This provides sufficient values to handle the range of {@code ZoneOffset}
* which affect the instant in addition to the local date-time.
* The value is also chosen such that the value of the year fits in
* an {@code int}.
*/
public static final Instant MIN = Instant.ofEpochSecond(MIN_SECOND, 0);
/**
* The maximum supported {@code Instant}, '1000000000-12-31T23:59:59.999999999Z'.
* This could be used by an application as a "far future" instant.
* <p>
* This is one year later than the maximum {@code LocalDateTime}.
* This provides sufficient values to handle the range of {@code ZoneOffset}
* which affect the instant in addition to the local date-time.
* The value is also chosen such that the value of the year fits in
* an {@code int}.
*/
public static final Instant MAX = Instant.ofEpochSecond(MAX_SECOND, 999_999_999);
/**
* Serialization version.
*/
private static final long serialVersionUID = -665713676816604388L;
/**
* The number of seconds from the epoch of 1970-01-01T00:00:00Z.
*/
private final long seconds;
/**
* The number of nanoseconds, later along the time-line, from the seconds field.
* This is always positive, and never exceeds 999,999,999.
*/
private final int nanos;
除关键的seconds和nanos之外,其他都是常量。而这两个值除了第一次赋值之后也不能修改。 另外我们再看看Instant的声明:
public final class Instant
implements Temporal, TemporalAdjuster, Comparable<Instant>, Serializable {
final修饰的类,不得继承,这也很好的符合了不可变类定义中的第二条。之后没有提供对任何属性的set方法。 其他的方法主要有两类,分别是of和with开头的获取返回结果为Instant的方法和get某个属性值的方法。 而对u有of和with方法。都是在使用的时候通过new的方式创建了一个新的Instant对象。
public static Instant ofEpochSecond(long epochSecond) {
return create(epochSecond, 0);
}
private static Instant create(long seconds, int nanoOfSecond) {
if ((seconds | nanoOfSecond) == 0) {
return EPOCH;
}
if (seconds < MIN_SECOND || seconds > MAX_SECOND) {
throw new DateTimeException("Instant exceeds minimum or maximum instant");
}
return new Instant(seconds, nanoOfSecond);
}
这个create方法也是私有的,of以静态方法的形式提供给对外使用。 这样就很好的通过不可变类的形式,实现了线程安全。这也是我们自己在写代码的过程中值得借鉴的地方。
2.java8新版本时间如何存储到mysql
我们首先需要对mysql所支持的时间类型进行梳理:
日期时间类型 |
占用空间 |
日期格式 |
最小值 |
最大值 |
零值表示 |
---|---|---|---|---|---|
DATETIME |
8 bytes |
YYYY-MM-DD HH:MM:SS |
1000-01-01 00:00:00 |
9999-12-31 23:59:59 |
0000-00-00 00:00:00 |
TIMESTAMP |
4 bytes |
YYYY-MM-DD HH:MM:SS |
19700101080001 |
2038-1-19 11:14:07 |
00000000000000 |
DATE |
4 bytes |
YYYY-MM-DD |
1000-01-01 |
9999-12-31 |
0000-00-00 |
TIME |
3 bytes |
HH:MM:SS |
-838:59:59 |
838:59:59 |
00:00:00 |
YEAR |
1 bytes |
YYYY |
1901 |
2155 |
0000 |
上述是mysql4.2以上版本都支持的5种时间类型。 我们可以看到,基本能和java新版本的LocalDate、LocatTime、LocalDateTime都能对应得上。 需要注意的是,我们系统种的LocalDate、localDateTime、LocalTime都是采用的系统本地时区。如果使用这三个字段存入mysql的时候需要考虑数据库与业务系统时区一致的问题。 另外,Instant由于包含纳秒,在使用mysql的时候,要么用两个字段来分别存储,要么就舍去纳秒。 旧的Date对象也很方便进行转换:
Instant instant = Instant.now();
System.out.println(Date.from(instant));
Date date = new Date();
System.out.println(date.toInstant());
上述代码展示了如何在Instant和Date之间的转换。 另外java8种阿里规范有规定,拒绝在任何地方使用)java.sql.Date、java.sql.Time和java.sql.Timestamp。
因此很多博客上建议将Instant转换为java.sql.Date的方案实际上并不建议使用。 我们可以看看stackoverflow上关于Instant to mysql的问题。
How to store a Java Instant in a MySQL database 正确的回答解释到,我们无法将Instant的纳秒压缩到mysql数据库中的DateTime和timeStamp。在jdbc4.2以后的版本,可以忽略纳秒。
Instant instant = Instant().now().truncatedTo( ChronoUnit.MICROSECONDS ) ;
如果不进行压缩,那么可以采用两个字段存储:
long seconds = instant.getEpochSecond() ;
long nanos = instant.getNano() ;
再或者转为String类型之后,直接字符串存储。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释