JDK1.8新特性(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/J080624/article/details/85102710

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

⑥ 时间日期校正器

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.Date Date.from(instant) date.toInstant()
java.time.Instant java.sql.Timestamp Timestamp.from(instant) timestamp.toInstant()
java.time.Zoned DateTimejava.util.GregorianCalendar GregorianCalendar.from(zonedDateTime) cal.toZonedDateTime()
java.time.LocalDate java.sql.Time Date.valueOf(localDate) date.toLocalDate()
java.time.LocalTime java.sql.Time Date.valueOf(localDate) date.toLocalTime()
java.time.LocalDateTime java.sql.Timestamp Timestamp.valueOf(localDateTime) timestamp.toLocalDateTime()
java.time.ZoneId java.util.TimeZone Timezone.getTimeZone(id) timeZone.toZoneId()
java.time.format.DateTimeFormatter java.text.DateFormat formatter.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中的注解

猜你喜欢

转载自blog.csdn.net/J080624/article/details/85102710