3分钟看完Java 8——史上最强Java 8新特性总结之第四篇 其他新特性

目录

· 默认方法和静态方法

· 初步理解

· 应用模式

· 优先级问题

· Optional

· CompletableFuture

· 基本用法

· CompletableFuture与Stream API

· 新日期和时间API

· 旧API

· 创建

· 操作

· 格式化与解析

· 时区


默认方法和静态方法

初步理解

1. 面临问题:Java 8以前,当已设计的接口被其他框架、库使用后,如果向接口添加新方法,将导致这些框架和库无法编译。

2. 解决方法:Java 8可在接口中使用静态方法和默认方法。

应用模式

1. 合并接口与辅助工具类:比如接口java.util.Collection与其辅助工具类java.util.Collections,接口支持静态方法后,可将辅助工具类的方法加入到接口。

2. 可选方法:比如JDK中的java.util.Iterator,接口提供一个默认的remove()实现,这样实体类就无需在自己的实现中显式地提供一个空方法。

 interface Iterator<T> {

     boolean hasNext();

     T next();

     default void remove() {
throw new UnsupportedOperationException();
} }

3. 行为多继承

3分钟看完Java 8——史上最强Java 8新特性总结之第四篇 其他新特性

a) Rotatable.java

 public interface Rotatable {

     void setRotationAngle(int angleInDegrees);

     int getRotationAngle();

     default void rotateBy(int angleInDegrees) {
setRotationAngle((getRotationAngle() + angleInDegrees) % 360);
} }

b) Moveable.java

 public interface Moveable {

     int getX();

     int getY();

     void setX(int x);

     void setY(int y);

     default void moveHorizontally(int distance) {
setX(getX() + distance);
} default void moveVertically(int distance) {
setY(getY() + distance);
} }

c) Resizable.java

 public interface Resizable {

     int getWidth();

     int getHeight();

     void setWidth(int width);

     void setHeight(int height);

     void setAbsoluteSize(int width, int height);

     default void setRelativeSize(int wFactor, int hFactor) {
setAbsoluteSize(getWidth() / wFactor, getHeight() / hFactor);
} }

d) Monster.java

 public class Monster implements Rotatable, Moveable, Resizable {

     @Override
public int getX() {
return 0;
} @Override
public int getY() {
return 0;
} @Override
public void setX(int x) {
} @Override
public void setY(int y) { } @Override
public int getWidth() {
return 0;
} @Override
public int getHeight() {
return 0;
} @Override
public void setWidth(int width) {
} @Override
public void setHeight(int height) {
} @Override
public void setAbsoluteSize(int width, int height) {
} @Override
public void setRotationAngle(int angleInDegrees) {
} @Override
public int getRotationAngle() {
return 0;
} }

e) Sun.java

 public class Sun implements Moveable, Rotatable {

     @Override
public int getX() {
return 0;
} @Override
public int getY() {
return 0;
} @Override
public void setX(int x) {
} @Override
public void setY(int y) {
} @Override
public void setRotationAngle(int angleInDegrees) {
} @Override
public int getRotationAngle() {
return 0;
} }

优先级问题

1. 方法执行优先级规则:

a) 距离当前类越近的方法优先级越高;

b) 显示方法近于隐式方法;

c) 距离相等时,非默认方法优先于默认方法。

2. 举例

a) hello()方法优先级:B > A。

i. A.java

 public interface A {

     default void hello() {
System.out.println("Hello from A");
} }

ii. B.java

 public interface B extends A {

     default void hello() {
System.out.println("Hello from B");
} }

iii. C.java

 public class C implements B, A {

     public static void main(String[] args) {
new C().hello(); // Hello from B
} }

b) hello()方法优先级:B > A。

i. A.java

 public interface A {

     default void hello() {
System.out.println("Hello from A");
} }

ii. B.java

 public interface B extends A {

     default void hello() {
System.out.println("Hello from B");
} }

iii. D.java

 public class D implements A{
}

iv. C.java

 public class C extends D implements B, A {

     public static void main(String[] args) {
new C().hello(); // Hello from B
} }

c) hello()方法优先级:D > B > A。

i. A.java

 public interface A {

     default void hello() {
System.out.println("Hello from A");
} }

ii. B.java

 public interface B extends A {

     default void hello() {
System.out.println("Hello from B");
} }

iii. D.java

 public class D implements A {

     @Override
public void hello() {
System.out.println("Hello from D");
} }

iv. C.java

 public class C extends D implements B, A {

     public static void main(String[] args) {
new C().hello(); // Hello from D
} }

d) hello()方法优先级:C > B = A,由于A、B优先级相等,有冲突,所以必须在C中Override hello()方法,否则将报错class C inherits unrelated defaults for hello() from types B and A。

i. A.java

 public interface A {

     default void hello() {
System.out.println("Hello from A");
} }

ii. B.java

 public interface B {

     default void hello() {
System.out.println("Hello from B");
} }

iii. C.java

 public class C implements B, A {

     @Override
public void hello() {
B.super.hello();
} public static void main(String[] args) {
new C().hello(); // Hello from B
} }

Optional

1. Optional目的:解决null带来的以下问题。

a) 错误:NullPointerException。

b) 代码膨胀:代码充斥着深度嵌套的null检查,可读性变差。

c) 毫无意义:null自身没有任何语义,它代表在静态类型语言中以一种错误的方式对缺失变量值的建模。

d) 破坏了Java的哲学:Java一直试图避免让程序员意识到指针的存在,唯一的例外是null指针。

e) 类型缺陷:null并不属于任何类型,它可以被赋值给任意引用类型的变量。这导致当这个变量被传递到系统中的另一个部分后,将无法获知null变量最初的赋值到底是什么类型。

2. java.util.Optional类的方法

方法

描述

empty

返回空Optional实例

filter

如果值存在并且满足提供的Predicate,则返回包含该值的Optional对象;否则返回空Optional对象

flatMap

如果值存在,就对该值执行提供的mapping函数,返回Optional类型的值,否则返回一个空Optional对象

get

如果值存在,将该值用Optional封装返回,否则抛出NoSuchElementException异常

ifPresent

如果值存在,则执行使用该值的方法调用,否则什么也不做

isPresent

如果值存在,则返回true,否则返回false

map

如果值存在,则对该值执行提供的mapping函数

of

将指定值用Optional封装后返回,如果该值为null,则抛出NullPointerException异常

ofNullable

将指定值用Optional封装后返回,如果该值为null,则返回空Optional对象

orElse

如果有值,则将其返回,否则返回一个默认值

orElseGet

如果有值,则将其返回,否则返回由指定Supplier接口生成的值

orElseThrow

如果有值,则将其返回,否则抛出指定的Supplier接口生成的异常

3.举例

a) 使用Optional前

i. Person.java

 public class Person {

     private Car car;

     public Car getCar() {
return car;
} }

ii. Car.java

 public class Car {

     private Insurance insurance;

     public Insurance getInsurance() {
return insurance;
} }

iii. Insurance.java

 public class Insurance {

     private String name;

     public String getName() {
return name;
} }

iv. Test.java

 public class Test {

     public static String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
} }

b) 使用Optional后

i. Person.java

 import java.util.Optional;

 public class Person {

     // 可能为null
private Car car; public Optional<Car> getCar() {
// 使用Optional封装car,允许car为null
return Optional.ofNullable(car);
} }

ii. Car.java

 import java.util.Optional;

 public class Car {

     // 可能为null
private Insurance insurance; public Optional<Insurance> getInsurance() {
return Optional.ofNullable(insurance);
} }

iii. Insurance.java

 public class Insurance {

     // 不可能为null
private String name; // 不可能为null,所以不使用Optional
public String getName() {
return name;
} }

iv. Test.java

 import java.util.Optional;

 public class Test {

     public static String getCarInsuranceName(Person person) {
Optional<Person> optPerson = Optional.ofNullable(person);
// 由于Person::getCar返回类型是Optional<Car>,
// 所以optPerson.map(Person::getCar)返回类型是Optional<Optional<Car>>,
// 因此导致map(Car::getInsurance)报错
// Optional<String> name = optPerson.map(Person::getCar)
// .map(Car::getInsurance)
// .map(Insurance::getName);
// 使用flatMap扁平化,从而避免上述报错
return optPerson.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown"); // 如果Optional结果值为空,则使用默认值
} }

c) 使用filter()方法判断Predicate

 // 不使用Optional
Insurance insurance = null;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
System.out.println("ok");
}
// 使用Optional
Optional<Insurance> optInsurance = Optional.ofNullable(null);
optInsurance.filter(ins -> "CambridgeInsurance".equals(ins.getName()))
.ifPresent(ins -> System.out.println("ok"));

CompletableFuture

基本用法

1. java.util.concurrent.Future接口

a) 起始:Java 5。

b) 使用方法:通常需要将耗时操作封装在一个Callable对象中,再将它提交给ExecutorService执行,最后通过Future获取执行结果。

2. java.util.concurrent.CompletableFuture类

a) 起始:Java 8。

b) 优点:比Future更加直观,功能更强大。

3. 举例

a) Future

i. Shop.java

 import java.util.concurrent.*;

 public class Shop {

     public double getPrice(String product) {
return calculatePrice(product);
} public Future<Double> getPriceAsync(String product) {
ExecutorService executor = Executors.newCachedThreadPool();
Future<Double> future = executor.submit(new Callable<Double>() {
public Double call() {
return getPrice(product);
}
});
return future;
} private double calculatePrice(String product) {
delay();
return Math.random() * 1000;
} private static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }

ii. Test.java

 import java.util.concurrent.*;

 public class Test {

     public static void main(String[] args) {
try {
Future<Double> future = new Shop().getPriceAsync("Candy");
Double price = future.get();
System.out.println(price);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} }

b) CompletableFuture方法一

 import java.util.concurrent.*;

 public class Shop {

     public double getPrice(String product) {
return calculatePrice(product);
} public Future<Double> getPriceAsync(String product) {
CompletableFuture<Double> future = new CompletableFuture<>();
new Thread(() -> {
try {
double price = getPrice(product);
future.complete(price); // 如果价格计算正常结束,完成Future操作并设置商品价格
} catch (Exception ex) {
future.completeExceptionally(ex); // 否则就抛出导致失败的异常,完成这次Future操作
}
}).start();
return future;
} private double calculatePrice(String product) {
delay();
return Math.random() * 1000;
} private static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }

c) CompletableFuture方法二

 import java.util.concurrent.*;

 public class Shop {

     public double getPrice(String product) {
return calculatePrice(product);
} public Future<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() -> calculatePrice(product));
} private double calculatePrice(String product) {
delay();
return Math.random() * 1000;
} private static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }

CompletableFuture与Stream API

1. 并行流与顺序流+CompletableFuture都可以实现流的并行处理,如何选取?

a) 灵活

i. 并行流:线程数由ForkJoinPool的全局属性决定。

ii. 顺序流+CompletableFuture:*创建线程池,灵活控制线程数。

b) 场景

i. 并行流:更适合计算密集型并且没有I/O的操作,因为实现简单,同时效率更高(如果所有的线程都是计算密集型,则没有必要创建比CPU核数更多的线程)。

ii. 顺序流+CompletableFuture:更适合并行工作单元涉及I/O等待(包括网络连接等待)的操作,该场景不使用并行流的另一个原因是处理流的流水线中如果发生I/O等待,流的延迟特性会让我们很难判断到底什么时候触发了等待。该场景线程数用以下公式计算(出自《Java并发编程实战》):

Nthreads = NCPU * UCPU * (1 + W/C)

NCPU为CPU核数,可通过Runtime.getRuntime().availableProcessors()获取。

UCPU为期望的CPU利用率(应介于0和1之间)。

W/C是等待时间与计算时间的比率。

2. 举例

a) Shop.java

 public class Shop {

     private String name;

     public Shop(String name) {
this.name = name;
} public String getName() {
return name;
} public double getPrice(String product) {
return calculatePrice(product);
} private double calculatePrice(String product) {
delay();
return Math.random() * 1000;
} private static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }

b) Test.java

 import java.util.*;
import java.util.concurrent.*; import static java.util.stream.Collectors.toList; public class Test { // 顺序流
public static List<String> findPrices1(List<Shop> shops, String product) {
return shops.stream()
.map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)))
.collect(toList());
} // 并行流
public static List<String> findPrices2(List<Shop> shops, String product) {
return shops.parallelStream()
.map(shop -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)))
.collect(toList());
} // 顺序流+CompletableFuture
public static List<String> findPrices3(List<Shop> shops, String product) {
// 创建一个线程池,线程池中线程的数目为100和商店数目二者中较小的一个值
Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100),
new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true); // 使用守护线程——这种方式不会阻止程序的关停
return t;
}
});
List<CompletableFuture<String>> priceFutures = shops.stream()
.map(shop -> CompletableFuture.supplyAsync(() -> String.format("%s price is %.2f", shop.getName(), shop.getPrice(product)), executor))
.collect(toList());
return priceFutures.stream()
.map(CompletableFuture::join) // 等待所有异步操作结束
.collect(toList());
} public static void printPrices(List<String> prices) {
prices.stream().forEach(System.out::println);
} public static void main(String[] args) {
List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
new Shop("LetsSaveBig"),
new Shop("MyFavoriteShop"),
new Shop("BuyItAll"));
String product = "Candy";
List<String> prices1 = findPrices1(shops, product);
printPrices(prices1);
List<String> prices2 = findPrices2(shops, product);
printPrices(prices2);
List<String> prices3 = findPrices3(shops, product);
printPrices(prices3);
} }

新日期和时间API

旧API

1. java.util.Date

a) 起始:Java 1.0。

b) 缺点:只能以毫秒精度表示时间;易用性差,比如年份从1900开始,月份从0开始。

 // 2014年3月18日
System.out.println(new Date(114, 2, 18));

2.java.util.Calendar

a) 起始:Java 1.1。

b) 缺点:易用性差,比如月份从0开始;Date和Calendar如何选择问题;格式化类DateFormat只能用于Date。

创建

1. java.time.LocalDate

a) 只存储年、月、日,不存储时区。

b) 不可变,线程安全。

2. java.time.LocalTime

a) 只存储时、分、秒,不存储时区。

b) 不可变,线程安全。

3. java.time.LocalDateTime

a) 只存储年、月、日、时、分、秒,不存储时区。

b) 不可变,线程安全。

c) LocalDate和LocalTime的合体。

4. java.time.Instant

a) Unix时间戳秒数,即与UTC时区1970-01-01 00:00:00的秒数和纳秒差值。

b) 内部存储秒数和纳秒(1 ns = 10^-9 s)两个字段。

c) 面向机器设计的类,方便机器处理。

5. java.time.temporal.ChronoField

a) 实现了java.time.temporal.TemporalFieldl接口的枚举,提供了访问java.time.temporal.Temporal接口实现类的标准字段。

b) LocalDate、LocalTime、LocalDateTime和Instant实现了Temporal接口。

c) 注意:由于Instant是面向机器的,通过ChronoField获取Instant字段时,只支持秒、毫秒(1,000毫秒=1秒)、微秒(1,000,000微秒=1秒)和纳秒(1,000,000,000纳秒=1秒)。

6. java.time.Duration

a) 表示一个时间段的持续时长。

b) 内部存储持续时长的秒和纳秒。

7. java.time.Period

a) 表示一个时间段的持续时长。

b) 内部存储持续时长的年、月、日。

8. Duration和Period的通用方法

方法

静态方法

描述

between

Y

创建两个时间点之间的间隔时长

from

Y

由一个临时时间点创建间隔时长

of

Y

由组成部分创建时长的实例

parse

Y

由字符串创建时长的实例

addTo

N

创建该时长的副本,并将其叠加到某个指定的Temporal对象

get

N

读取该时长的状态

isNegative

N

检查该时长是否为负值,不包含零

isZero

N

检查该时长的时长是否为零

minus

N

通过减去一定的时间创建该时长的副本

multipliedBy

N

将时长的值乘以某个标量创建该时长的副本

negated

N

以忽略某个时长的方式创建该时长的副本

plus

N

以增加某个指定时长的方式创建该时长的副本

subtractFrom

N

从指定的Temporal对象中减去该时长

9. 举例

a) java.time.LocalDate

 // 创建
LocalDate today = LocalDate.now();
LocalDate d = LocalDate.parse("2014-03-18");
LocalDate date = LocalDate.of(2014, 3, 18); // 2014-03-18
// 直接获取字段
int year = date.getYear(); //
Month month = date.getMonth(); // MARCH
int day = date.getDayOfMonth(); //
DayOfWeek dow = date.getDayOfWeek(); // TUESDAY
int len = date.lengthOfMonth(); // 31 (3月总天数)
boolean leap = date.isLeapYear(); // false
// 通过ChronoField获取字段
int year = date.get(ChronoField.YEAR); //
int month = date.get(ChronoField.MONTH_OF_YEAR); //
int day = date.get(ChronoField.DAY_OF_MONTH); //

b) java.time.LocalTime

 // 创建
LocalTime t = LocalTime.parse("13:45:20");
LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20
// 直接获取字段
int hour = time.getHour(); //
int minute = time.getMinute(); //
int second = time.getSecond(); //

c) java.time.LocalDateTime

 // 创建2014-03-18T13:45:20的4种方法
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
// 提取
LocalDate date1 = dt1.toLocalDate(); // 2014-03-18
LocalTime time1 = dt1.toLocalTime(); // 13:45:20

d) java.time.Instant

 // 创建1970-01-01T00:00:03Z的4种方法
Object o1 = Instant.ofEpochSecond(3);
Object o2 = Instant.ofEpochSecond(3, 0);
Object o3 = Instant.ofEpochSecond(2, 1_000_000_000); // 2秒之后再加上100万纳秒(1秒)
Object o4 = Instant.ofEpochSecond(4, -1_000_000_000); // 4秒之前的100万纳秒(1秒)
// Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth
int day = Instant.now().get(ChronoField.DAY_OF_MONTH);

e) java.time.Duration

 // 创建3秒持续时长的5种方法
Duration d1 = Duration.between(LocalTime.parse("00:00:00"), LocalTime.parse("00:00:03"));
Duration d2 = Duration.between(LocalDateTime.parse("1970-01-01T00:00:00"), LocalDateTime.parse("1970-01-01T00:00:03"));
Duration d3 = Duration.between(Instant.ofEpochSecond(0), Instant.ofEpochSecond(3));
Duration d4 = Duration.ofSeconds(3);
Duration d5 = Duration.of(3, ChronoUnit.SECONDS);

f) java.time.Period

 // 创建10天持续时长的3种方法
Period p1 = Period.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 3, 18));
Period p2 = Period.ofDays(10);
Period p3 = Period.of(0, 0, 10);

操作

1. 操作相关方法

a) with*()方法:创建某字段为新值的新Temporal对象。

b) plus*()方法:创建加上某个时长的新Temporal对象。

c) minus*()方法:创建减去某个时长的新Temporal对象。

d) java.time.temporal.TemporalAdjuster.*方法:高级日期时间操作方法

方法

描述

dayOfWeekInMonth

创建一个新日期,它的值为同一个月中每一周的第几天

firstDayOfMonth

创建一个新日期,它的值为当月的第一天

firstDayOfNextMonth

创建一个新日期,它的值为下月的第一天

firstDayOfNextYear

创建一个新日期,它的值为明年的第一天

firstDayOfYear

创建一个新日期,它的值为当年的第一天

firstInMonth

创建一个新日期,它的值为同一个月中,第一个符合星期几要求的值

lastDayOfMonth

创建一个新日期,它的值为下月的最后一天

lastDayOfNextMonth

创建一个新日期,它的值为下月的最后一天

lastDayOfNextYear

创建一个新日期,它的值为明年的最后一天

lastDayOfYear

创建一个新日期,它的值为今年的最后一天

lastInMonth

创建一个新日期,它的值为同一个月中,最后一个符合星期几要求的值

next

previous

创建一个新日期,并将其值设置为日期调整后或调整前,第一个符合指定星期几要求的日期

nextOrSame

previousOrSame

创建一个新日期,并将其值设置为日期调整后或调整前,第一个符合指定星期几要求的日期,如果该日期已经符合要求,直接返回该对象

2. 举例

a) with*()方法

 LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.withYear(2011); // 2011-03-18
LocalDate date3 = date2.withDayOfMonth(25); // 2011-03-25
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9); // 2011-09-25

b) plus*()方法和minus*()方法

 LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.plusWeeks(1); // 2014-03-25
LocalDate date3 = date2.minusYears(3); // 2011-03-25
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); // 2011-09-25

c) TemporalAdjuster.*方法

 import static java.time.temporal.TemporalAdjusters.*;
LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); // 2014-03-23
LocalDate date3 = date2.with(lastDayOfMonth()); // 2014-03-31

d) 自定义TemporalAdjuster:计算下一个工作日(仅跳过周六日)

 TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(
temporal -> {
DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
date = date.with(nextWorkingDay); // 2014-03-19

格式化与解析

1. java.time.format.DateTimeFormatter

a) 用于格式化和解析。

b) 线程安全。

2. 举例

a) 使用内置格式格式化与解析

 // 格式化
LocalDate date = LocalDate.of(2014, 3, 18);
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); //
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2014-03-18
// 解析
LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE);

b) 使用模式自定义格式式化与解析

 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
// 格式化
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
// 解析
LocalDate date2 = LocalDate.parse(formattedDate, formatter);

c) 更复杂方式自定义格式式化与解析

 DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendLiteral("年")
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral("月")
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral("日")
.parseCaseInsensitive()
.toFormatter();
// 格式化
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date1.format(formatter);
// 解析
LocalDate date2 = LocalDate.parse(formattedDate, formatter);

时区

1. java.time.ZoneId类:替换java.util.TimeZone的新时区类。

2. 举例

a) 获取时区

 // 北京时间
ZoneId beijingZone = ZoneId.of("Asia/Shanghai");
// 默认时区
ZoneId zoneId = TimeZone.getDefault().toZoneId();

b) 设置时区

 LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(beijingZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(beijingZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(beijingZone);

作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。

上一篇:史上最强Java开发环境搭建


下一篇:python基础 列表 的用法及介绍