JDK1.8新特性(三)使用详情

JDK1.8新特性(一)JDK1.8新特性(二)后继续学习JDK1.8新特性。

【7】Optional 类

final修饰的Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null 表示一个值不存在,现在Optional 可以更好的表达这个概念。并且可以避免空指针异常。

常用方法:

  • Optional.of(T t) : 创建一个Optional 实例,如果T为null,则会抛出空指针异常。
  • Optional.empty() : 创建一个空的Optional 实例
  • Optional.ofNullable(T t):若t 不为null,创建Optional 实例,否则创建空实例
  • isPresent() : 判断是否包含值
  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t.
  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回s 获取的值
  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map 类似,要求返回值必须是Optional

测试代码如下:

@Test
public void test4(){
	Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
	
	Optional<String> op2 = op.map(Employee::getName);
	System.out.println(op2.get());
	
	Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
	System.out.println(op3.get());
}

@Test
public void test3(){
	Optional<Employee> op = Optional.ofNullable(new Employee());
	
	if(op.isPresent()){
		System.out.println(op.get());
	}
	//orElse
	Employee emp = op.orElse(new Employee("张三"));
	System.out.println(emp);
	//orElseGet
	Employee emp2 = op.orElseGet(() -> new Employee());
	System.out.println(emp2);
}

@Test
public void test2(){
	Optional<Employee> op = Optional.of(new Employee());
	Employee emp = op.get();
	System.out.println(emp);
	//将会抛出空指针
//	Optional<Employee> op1 = Optional.ofNullable(null);
//	System.out.println(op1.get());
	
	//java.util.NoSuchElementException: No value present
	Optional<Employee> op2 = Optional.empty();
	System.out.println(op2.get());
}

【8】接口中的默认方法与静态方法

① 接口中的默认方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用default关键字修饰。而且Default 方法只在接口中被允许使用。

如下所示:

public interface MyFun {
	//必须显示用default修饰
	default String getName(){
		return "哈哈哈";
	}
}

② 接口默认方法的”类优先”原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时,情况如下:

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

  • 第一种情况测试

如下所示,类MyClass 拥有和接口同名同参不同实现方法:

public class MyClass {
	public String getName(){
		return "嘿嘿嘿";
	}
}

类SubClass 继承MyClass 并实现MyFun

public class SubClass extends MyClass implements MyFun{
	//...
}

测试如下:

public static void main(String[] args) {
		SubClass sc = new SubClass();
		System.out.println(sc.getName());
		//嘿嘿嘿
}
  • 第二种情况测试

另外一个接口如下所示:

public interface MyInterface {
	
	default String getName(){
		return "呵呵呵";
	}
}

如果SubClass此时实现两个接口,则提示如下:
在这里插入图片描述

修改SubClass如下所示:

public class SubClass /*extends MyClass*/ implements MyFun, MyInterface{
	@Override
	public String getName() {
		//注意,必须有 .super 
		return MyInterface.super.getName();
	}
}

③ 接口中的静态方法

Java8 中,接口中允许添加静态方法。

修改MyInterface ,在其中添加静态方法

public interface MyInterface {
	
	default String getName(){
		return "呵呵呵";
	}
	//public, abstract, default, static and strictfp are permitted
	public static void show(){
		System.out.println("接口中的静态方法");
	}
}

可以直接调用,如下所示:

public static void main(String[] args) {
	SubClass sc = new SubClass();
	System.out.println(sc.getName());
	
	MyInterface.show();
}

【9】新时间日期API

① 传统时间日期api线程安全问题

传统的时间日期类分别在java.util、java.sql和java.text包下。如java.uti.Date、java.sql.Date和进行格式化的java.text.DateFormat。这些是非线程安全的。

测试如下:

public static void main(String[] args) throws Exception {
		
	SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
	
	Callable<Date> task = new Callable<Date>() {

		@Override
		public Date call() throws Exception {
			return sdf.parse("20181121");
		}
		
	};
	//创建固定大小线程池
	ExecutorService pool = Executors.newFixedThreadPool(10);
	
	List<Future<Date>> results = new ArrayList<>();
	
	for (int i = 0; i < 10; i++) {
		results.add(pool.submit(task));
	}
	//打印结果
	for (Future<Date> future : results) {
		System.out.println(future.get());
	}
	//关闭线程池
	pool.shutdown();
}

测试结果如下:

在这里插入图片描述

那么为了解决这个多线程问题,我们可能采取方法如下:

public class DateFormatThreadLocal {
	
	private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
		
		protected DateFormat initialValue(){
			return new SimpleDateFormat("yyyyMMdd");
		}
		
	};
	
	public static final Date convert(String source) throws ParseException{
		return df.get().parse(source);
	}

}

创建一个DateFormatThreadLocal 类里面定义一个常量ThreadLocal<DateFormat> df。这里使用到ThreadLocal来解决多线程安全问题。

public static void main(String[] args) throws Exception {

	//解决多线程安全问题
	Callable<Date> task = new Callable<Date>() {

		@Override
		public Date call() throws Exception {
			//这里修改
			return DateFormatThreadLocal.convert("20181121");
		}
		
	};

	ExecutorService pool = Executors.newFixedThreadPool(10);
	
	List<Future<Date>> results = new ArrayList<>();
	
	for (int i = 0; i < 10; i++) {
		results.add(pool.submit(task));
	}
	
	for (Future<Date> future : results) {
		System.out.println(future.get());
	}
	
	pool.shutdown();
}

② jdk1.8新的时间日期api

jdk1.8中新的时间日期在java.time.*相关的包下,如下所示:
在这里插入图片描述

java.time – 包含值对象的基础包
java.time.chrono – 提供对不同的日历系统的访问
java.time.format – 格式化和解析时间和日期
java.time.temporal – 包括底层框架和扩展特性
java.time.zone – 包含时区支持的类

jdk1.8中新的时间日期类是不可变的,是线程安全的。不会存在①中多线程安全问题:

public static void main(String[] args) throws Exception {
	//格式化器
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
	
	//使用LocalDate
	Callable<LocalDate> task = new Callable<LocalDate>() {

		@Override
		public LocalDate call() throws Exception {
			LocalDate ld = LocalDate.parse("20181121", dtf);
			return ld;
		}
		
	};

	ExecutorService pool = Executors.newFixedThreadPool(10);
	
	List<Future<LocalDate>> results = new ArrayList<>();
	
	for (int i = 0; i < 10; i++) {
		results.add(pool.submit(task));
	}
	
	for (Future<LocalDate> future : results) {
		System.out.println(future.get());
	}
	
	pool.shutdown();
}

③ LocalDate、LocalTime、LocalDateTime

LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象(类使用final修饰),分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

三个类使用方式是一样的,分别是日期、时间和日期+时间。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法。

LocalDate部分源码如下所示:

public final class LocalDate
        implements Temporal, TemporalAdjuster, ChronoLocalDate, Serializable {

    /**
     * The minimum supported {@code LocalDate}, '-999999999-01-01'.
     * This could be used by an application as a "far past" date.
     */
    public static final LocalDate MIN = LocalDate.of(Year.MIN_VALUE, 1, 1);
    /**
     * The maximum supported {@code LocalDate}, '+999999999-12-31'.
     * This could be used by an application as a "far future" date.
     */
    public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31);

    /**
     * Serialization version.
     */
    private static final long serialVersionUID = 2942565459149668126L;
    /**
     * The number of days in a 400 year cycle.
     */
    private static final int DAYS_PER_CYCLE = 146097;
    /**
     * The number of days from year zero to year 1970.
     * There are five 400 year cycles from year zero to 2000.
     * There are 7 leap years from 1970 to 2000.
     */
    static final long DAYS_0000_TO_1970 = (DAYS_PER_CYCLE * 5L) - (30L * 365L + 7L);

    /**
     * The year.
     */
    private final int year;
    /**
     * The month-of-year.
     */
    private final short month;
    /**
     * The day-of-month.
     */
    private final short day;
    //...
 }

测试代码如下:

//1. LocalDate、LocalTime、LocalDateTime
@Test
public void test1(){
	//获取当前系统时间
	LocalDateTime ldt = LocalDateTime.now();
	System.out.println(ldt);
	
	//使用参数创建时间日期对象
	LocalDateTime ld2 = LocalDateTime.of(2018, 11, 21, 10, 10, 10);
	System.out.println(ld2);
	
	//+20年 产生新的实例对象
	LocalDateTime ldt3 = ld2.plusYears(20);
	System.out.println(ldt3);
	
	//减20月 产生新的实例对象
	LocalDateTime ldt4 = ld2.minusMonths(2);
	System.out.println(ldt4);
	
	System.out.println(ldt.getYear());
	System.out.println(ldt.getMonthValue());
	System.out.println(ldt.getDayOfMonth());
	System.out.println(ldt.getHour());
	System.out.println(ldt.getMinute());
	System.out.println(ldt.getSecond());
}

测试结果如下:

2018-12-20T14:28:37.550
2018-11-21T10:10:10
2038-11-21T10:10:10
2018-09-21T10:10:10
2018
12
20
14
28
37

常见方法如下表所示

方法描述
now()静态方法,根据当前时间创建对象
of()静态方法,根据指定日期/时间创建对象
plusDays,plusWeeks,plusMonths,plusYears向当前LocalDate对象添加几天、几周、几个月、几年
minusDays,minusWeeks,minusMonths,minusYears从当前LocalDate对象减去几天、几周、几个月、几年
plus,minus添加或减少一个Duration或Period
withDayOfMonth,withDayOfYear,withMonth,withYear将月份天数、年份天数、月份、年份修改为指定的值并返回新的LocalDate对象
getDayOfMonth获得月份天数(1-31)
getDayOfYear获得年份天数(1-366)
getDayOfWeek获得星期几(返回一个DayOfWeek枚举值)
getMonth获得月份,返回一个Month枚举对象
getMonthValue获得月份(1-12)
getYear获得年份
until获得两个日期之间的Period对象,或者指定ChronoUnits的数字
isBefore,isAfter比较两个LocalDate
isLeapYear判断是否是闰年

④ Instant 时间戳

用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算。

测试如下:

@Test
public void test2(){
	Instant ins = Instant.now();  //默认使用 UTC 时区
	System.out.println(ins);
	
	OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
	System.out.println(odt);
	//获取毫秒
	System.out.println(ins.toEpochMilli());
	//获取纳秒
	System.out.println(ins.getNano());
	
	//改变秒
	Instant ins2 = Instant.ofEpochSecond(5);
	System.out.println(ins2);
}

测试结果如下:

2018-12-20T06:59:28.547Z
2018-12-20T14:59:28.547+08:00
1545289168547
547000000
1970-01-01T00:00:05Z


⑤ Duration 和Period

Duration:用于计算两个“时间”间隔,Period:用于计算两个“日期”间隔。二者均是使用final修饰。

测试代码如下:

@Test
public void test3(){
	Instant ins1 = Instant.now();
	
	System.out.println(ins1+"--------------------");
	try {
		Thread.sleep(1000);
	} catch (InterruptedException e) {
	}
	
	Instant ins2 = Instant.now();
	
	System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
	
	System.out.println("----------------------------------");
	
	LocalDate ld1 = LocalDate.now();
	LocalDate ld2 = LocalDate.of(2011, 1, 1);
	
	Period pe = Period.between(ld2, ld1);
	System.out.println(pe.getYears());
	System.out.println(pe.getMonths());
	System.out.println(pe.getDays());
}

测试结果如下:

2018-12-20T07:04:09.595Z--------------------
所耗费时间为:PT1.048S
----------------------------------
7
11
19

获取两个LocalDateTime 时间之间分钟数:

LocalDateTime beginTime = LocalDateTime.now();
LocalDateTime endTime = beginTime.plusHours(4);
Duration duration = Duration.between(beginTime, endTime);
long toMinutes = duration.toMinutes(); //240

根据localDate和localTime获取一个localDateTime:

LocalDateTime beginTime = LocalDateTime.now();
LocalDate beginDate = beginTime.toLocalDate();
LocalTime localTime=LocalTime.parse("08:00:00");
LocalDateTime checkBegin = LocalDateTime.of(beginDate, localTime);
System.out.println(checkBegin); //2022-04-05T08:00

⑥ 时间日期校正器

TemporalAdjuster ,时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。TemporalAdjusters --该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。

TemporalAdjuster是一个函数式接口,我们可以使用lambda表达式自定义实现。

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}

获取下个周日测试如下:

@Test
public void test4(){
LocalDateTime ldt = LocalDateTime.now();
	System.out.println(ldt);
	
	LocalDateTime ldt2 = ldt.withDayOfMonth(10);
	System.out.println(ldt2);
	
	LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
	System.out.println(ldt3);
	
	//自定义:下一个工作日
	LocalDateTime ldt5 = ldt.with((l) -> {
		LocalDateTime ldt4 = (LocalDateTime) l;
		
		DayOfWeek dow = ldt4.getDayOfWeek();
		
		if(dow.equals(DayOfWeek.FRIDAY)){
			return ldt4.plusDays(3);
		}else if(dow.equals(DayOfWeek.SATURDAY)){
			return ldt4.plusDays(2);
		}else{
			return ldt4.plusDays(1);
		}
	});
	
	System.out.println(ldt5);
	
}

测试结果如下:

2018-12-20T15:17:55.714
2018-12-10T15:17:55.714
2018-12-23T15:17:55.714
2018-12-21T15:17:55.714

TemporalAdjusters常用方法如下所示:
在这里插入图片描述


⑦ 时间日期解析与格式化

jdk1.8中提供了类似于SimpleDateFormat功能的java.time.format.DateTimeFormatter。该类提供了三种格式化方法:

  • 预定义的标准格式
  • 语言环境相关的格式
  • 自定义的格式

如下所示:

@Test
public void test5(){
	//使用类中的格式
//     DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
	//自定义格式
	DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
	
	LocalDateTime ldt = LocalDateTime.now();
	//格式化
	String strDate = ldt.format(dtf);
	
	System.out.println(strDate);
	//解析
	LocalDateTime newLdt = ldt.parse(strDate, dtf);
	System.out.println(newLdt);
}

测试代码如下:

2018年12月20日 15:32:46 星期四
2018-12-20T15:32:46

DateTimeFormatter中已经提供的格式如下:
在这里插入图片描述


⑧ 时区的处理

Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime。

其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:Asia/Shanghai 等。

ZoneId:该类中包含了所有的时区信息
	- getAvailableZoneIds() : 可以获取所有时区时区信息
	- of(id) : 用指定的时区信息获取ZoneId 对象

//ZoneId中主要的时区信息
 static {
        Map<String, String> map = new HashMap<>(64);
        map.put("ACT", "Australia/Darwin");
        map.put("AET", "Australia/Sydney");
        map.put("AGT", "America/Argentina/Buenos_Aires");
        map.put("ART", "Africa/Cairo");
        map.put("AST", "America/Anchorage");
        map.put("BET", "America/Sao_Paulo");
        map.put("BST", "Asia/Dhaka");
        map.put("CAT", "Africa/Harare");
        map.put("CNT", "America/St_Johns");
        map.put("CST", "America/Chicago");
        map.put("CTT", "Asia/Shanghai");
        map.put("EAT", "Africa/Addis_Ababa");
        map.put("ECT", "Europe/Paris");
        map.put("IET", "America/Indiana/Indianapolis");
        map.put("IST", "Asia/Kolkata");
        map.put("JST", "Asia/Tokyo");
        map.put("MIT", "Pacific/Apia");
        map.put("NET", "Asia/Yerevan");
        map.put("NST", "Pacific/Auckland");
        map.put("PLT", "Asia/Karachi");
        map.put("PNT", "America/Phoenix");
        map.put("PRT", "America/Puerto_Rico");
        map.put("PST", "America/Los_Angeles");
        map.put("SST", "Pacific/Guadalcanal");
        map.put("VST", "Asia/Ho_Chi_Minh");
        map.put("EST", "-05:00");
        map.put("MST", "-07:00");
        map.put("HST", "-10:00");
        SHORT_IDS = Collections.unmodifiableMap(map);
    }

测试代码如下:

//ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
@Test
public void test7(){
	LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
	System.out.println(ldt);
	
	//带时区的时间日期
	ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
	System.out.println(zdt);
}

测试结果如下:

2018-12-20T15:52:49.718
2018-12-19T23:52:49.720-08:00[US/Pacific]

⑨ 与传统日期处理的转换

如下表所示:

To 遗留类From 遗留类
java.time.Instant java.util.DateDate.from(instant)date.toInstant()
java.time.Instant java.sql.TimestampTimestamp.from(instant)timestamp.toInstant()
java.time.Zoned DateTimejava.util.GregorianCalendarGregorianCalendar.from(zonedDateTime)cal.toZonedDateTime()
java.time.LocalDate java.sql.TimeDate.valueOf(localDate)date.toLocalDate()
java.time.LocalTime java.sql.TimeDate.valueOf(localDate)date.toLocalTime()
java.time.LocalDateTime java.sql.TimestampTimestamp.valueOf(localDateTime)timestamp.toLocalDateTime()
java.time.ZoneId java.util.TimeZoneTimezone.getTimeZone(id)timeZone.toZoneId()
java.time.format.DateTimeFormatter java.text.DateFormatformatter.toFormat()

【10】重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

如下所示:

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
	
	MyAnnotation[] value();

}

@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String value();
}


测试如下:

public class TestAnnotation {
	
	@MyAnnotation("haha")
	@MyAnnotation("hello")
	public void show(@MyAnnotation("jane")String  string){
		System.out.println(string);
	}
	
	public static void main(String[] args) throws Exception {
		Class<TestAnnotation> class1 = TestAnnotation.class;
		Method method = class1.getMethod("show", String.class);
		MyAnnotation[] annotations =  method.getAnnotationsByType(MyAnnotation.class);
		Arrays.stream(annotations).map((e)->e.value()).forEach(System.out::println);
	}

}

测试结果如下:

haha
hello

这里没有过多讲解,具体注解的讲解参考博文:一文读懂Java中的注解

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值