Java8的新特性(四)

并行流与串行流

什么是并行流

在这里插入图片描述

我们先了解一下什么是Fork/Join框架

在这里插入图片描述

Fork/Join框架和传统的线程池的区别

在这里插入图片描述


应用Fork/Join计算100000000的和:

public class ForkJoinCalculate extends RecursiveTask<Long> {
    /**
     *
     */
    private static final long serialVersionUID = 13475679780L;

    private long start;
    private long end;

    private static final long THRESHOLD = 10000L; //临界值

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;

        if(length <= THRESHOLD){
            long sum = 0;

            for (long i = start; i <= end; i++) {
                sum += i;
            }

            return sum;
        }else{
            long middle = (start + end) / 2;

            ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
            left.fork(); //拆分,并将该子任务压入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
            right.fork();

            return left.join() + right.join();
        }

    }
}

测试代码如下:

    @Test
    public void test() {
        Instant start = Instant.now();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:"+Duration.between(start, end).toMillis());
    }

执行结果如下图:
在这里插入图片描述
数值越大,它的效率体现的就越明显;
在Java8里面可以利用并行流就可以这样来写:它的底层就是Fork/Join

	@Test
    public void test3() {
        Instant start = Instant.now();
        LongStream.rangeClosed(0, 100000000L)
                .parallel()
                .reduce(0, Long::sum);
        Instant end = Instant.now();
        System.out.println("耗费时间为:"+Duration.between(start, end).toMillis());
    }

Optional容器类

什么是Optional类

在这里插入图片描述

Optional类的常用方法

一、Optional 容器类:用于尽量避免空指针异常

  • Optional.of(T t) : 创建一个 Optional 实例
  • 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

  • Optional.of(T t) : 创建一个 Optional 实例
	@Test
    public void test1() {
        Optional<Employee> op = Optional.of(new Employee());
        Employee employee = op.get();
        System.out.println(employee);
    }

运行结果如下:没有传任何的参数,这个时候,结果就是默认的值

Employee [id=0, name=null, age=0, salary=0.0, status=null]

需要注意的地方就是:
如果这里面传入了一个Null值,那么这里就是会发生空指针异常,原来的我们定位空指针异常要一行一行的去定位,非常的麻烦。它的思想就是:如果传进来了一个Null,在这一行就会发生空指针的异常,方便我们快速定位到空指针异常发生的地方:

Optional<Employee> op = Optional.of(null);

  • Optional.empty() : 创建一个空的 Optional 实例
	@Test
    public void test2() {
        Optional<Employee> op = Optional.empty();
        System.out.println(op.get());
    }

在这一行发生了空指针异常:

System.out.println(op.get());

Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例

  • 若 t 不为 null,创建 Optional 实例
	@Test
    public void test3() {
        Optional<Employee> op = Optional.ofNullable(new Employee());
        System.out.println(op.get());
    }
  • 若t为Null,那么则创建空的实例
	@Test
    public void test3() {
        Optional<Employee> op = Optional.ofNullable(null);
        System.out.println(op.get());
    }

  • isPresent() : 判断是否包含值
    我们可以进行判断:有值的时候,就正常的调用;如果没有值的话,那么就什么都不做;
	@Test
    public void test4() {
        Optional<Employee> op = Optional.ofNullable(null);
        if (op.isPresent()) {
            System.out.println(op.get());
        }
    }

  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
    如果有值的话,那么就返回值,如果没有值的话,那么我们就返回这个备用的值:T
    如果有值的话,那么就是返回了这个值:
	@Test
    public void test5() {
        Optional<Employee> op = Optional.ofNullable(new Employee());
        if (op.isPresent()) {
            System.out.println(op.get());
        }
        Employee employee = op.orElse(new Employee(1, "张三", 18, 888.88, Employee.Status.FREE));
        System.out.println(employee);
    }

结果如下:

Employee [id=0, name=null, age=0, salary=0.0, status=null]
Employee [id=0, name=null, age=0, salary=0.0, status=null]

如果没有值的话,那么就是返回我们备用的值

	@Test
    public void test5() {
        Optional<Employee> op = Optional.ofNullable(null);
        if (op.isPresent()) {
            System.out.println(op.get());
        }
        Employee employee = op.orElse(new Employee(1, "张三", 18, 888.88, Employee.Status.FREE));
        System.out.println(employee);
    }

这个时候,就是只输出一个值:也就是当为空时候,输出我们备用的值

Employee [id=1, name=张三, age=18, salary=888.88, status=FREE]

  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
    orElseGet这个方法里面的参数是一个供给型的接口,可以实现具体的功能:
	@Test
    public void test6() {
        Optional<Employee> op = Optional.ofNullable(null);
        Employee employee = op.orElseGet(() -> new Employee());
        System.out.println(employee);
    }

  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
	@Test
    public void test7() {
        Optional<Employee> op = Optional.ofNullable(new Employee(1, "张三", 18, 888.88, Employee.Status.FREE));
        Optional<String> str = op.map((e) -> e.getName());
        System.out.println(str.get());
    }

  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
    进一步避免空指针异常
    @Test
    public void test8() {
        Optional<Employee> op = Optional.ofNullable(new Employee(1, "张三", 18, 888.88, Employee.Status.FREE));
        Optional<String> opStr = op.flatMap((e) -> Optional.of(e.getName()));
        System.out.println(opStr.get());
    }

一般我们在创建可能为空的属性的时候,我们就是可以这样来写了:
我们就是可以用Optional这个类来进行封装一下:

private Optional<Godness> godness = Optional.empty();

这个时候,就是可以避免空指针异常了:
在这里插入图片描述
这个时候,就是可以避免空指针异常了;
如果,我们传了,就是我们传的值,如果没有传的话,那么也是有默认的值,就有效的避免了空指针的异常了:
在这里插入图片描述


接口中的默认方法与静态方法

接口中的默认方法

在这里插入图片描述
接口中的默认方法有一个类优先的原则:
在这里插入图片描述

  1. 有一个接口,里面有一个默认的方法:
public interface MyFun {
    default String getName() {
        return "Hello World";
    }
}
  1. 有一个类,里面也有一个与上面接口同样名字的方法:
public class MyClass {
    public String getName() {
        return "I love Java";
    }
}
  1. 此时,有一个类同时继承了上面的类和实现了上面的接口:
public class SubClass extends MyClass implements MyFun{

}
  1. 现在,我们创建SubClass这个类的实例,并且调用了实例的getName方法:
    public static void main(String[]args){
        SubClass subClass = new SubClass();
        System.out.println(subClass.getName());
    }
  1. 此时,运行的结果如下:
    在这里插入图片描述

需要注意的地方:
如果一个类里面实现了多个接口,而这多个接口里面有重名的方法,那么这个时候,在实现里面必须指定重写哪一个接口的对应的方法;
在这里插入图片描述

接口中的静态方法:

在这里插入图片描述

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

    public static void show() {
        System.out.println("接口中的静态方法");
    }
}

用法还是和类里面的静态方法一样用的:

    public static void main(String[]args){
        MyInterface.show();
    }

传统时间格式化的线程安全问题

全新的时间都在这几个包下面:
在这里插入图片描述

这些都是常用的:
在这里插入图片描述


	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("20181111");
            }
        };

        /** 创建长度为10的线程池 */
        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();
    }

如下图:
在这里插入图片描述

看执行结果,是存在线程安全的问题的:
在这里插入图片描述

传统的时间API都存在线程安全的问题;
原来,我们面对线程安全问题,我们可以对其加锁;

  • 我们可以使用ThreadLocal
public class DateFormatThreadLocal {
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMdd");
        }
    };

    public static Date convert(String source) throws ParseException {
        return df.get().parse(source);
    }
}

然后,我们就是可以这样来用:

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

        Callable<Date> task = new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return DateFormatThreadLocal.convert("20181111");
            }
        };

        /** 创建长度为10的线程池 */
        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();
    }

这个时候的执行结果就是:
在这里插入图片描述


Java8全新的API:它是线程安全的

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

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");

        Callable<LocalDate> task = new Callable<LocalDate>() {
            @Override
            public LocalDate call() throws Exception {
                return LocalDate.parse("20181111",dtf);
            }
        };

        /** 创建长度为10的线程池 */
        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();
    }
}

在这里插入图片描述
执行结果如下:
在这里插入图片描述

新时间与日期API-本地时间与时间戳

在这里插入图片描述

public class TestLocalDateTime {
    /**
     *  1.LocalDate 专门用来表示日期的
     *  2.LocalTime 专门用来表示时间的
     *  3.LocalDateTime 用来表示时间和日期
     */
    @Test
    public void test1() {
        /** 他们的使用方式大同小异,这里,我们就使用LocalDateTime来进行举例 */
        /** 1.使用他们的静态方法now()来获取实例 */
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);//2018-11-11T14:09:39.209
        /** 2.还可以使用另外一种方式来获取实例的方法 */
        LocalDateTime ldt2 = LocalDateTime.of(2018, 11, 11, 14, 06, 30);
        System.out.println(ldt2);//2018-11-11T14:06:30

        /** 对日期和时间进行一些运算 */
        LocalDateTime ldt3 = ldt.plusYears(2);
        System.out.println(ldt3);//2020-11-11T14:09:39.209

        /** 做减法的:减上两个月*/
        LocalDateTime ldt4 = ldt.minusMonths(2);
        System.out.println(ldt4);//2018-09-11T14:11:07.434

        /** 利用get方法来进行获取对应的年月日时分秒 */
        System.out.println(ldt.getYear());//2018
        System.out.println(ldt.getMonth());//NOVEMBER
        System.out.println(ldt.getMonthValue());//获取数字的月份:11

        System.out.println(ldt.getDayOfMonth());//11
        System.out.println(ldt.getDayOfWeek());//SUNDAY
        System.out.println(ldt.getDayOfYear());//315
        System.out.println(ldt.getHour());//14
        System.out.println(ldt.getMinute());//15
        System.out.println(ldt.getSecond());//28
    }
    
    /** 2.Instant:时间戳(以Unix元年,1970年1月1日 00:00:00 到某个时间之间的毫秒值)*/
    @Test
    public void test2() {
        Instant instant = Instant.now();//默认是UTC时区的时间
        System.out.println(instant);//2018-11-11T08:58:19.022Z

        //我们也可以指定一个偏移量的运算
        OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);//2018-11-11T16:58:19.022+08:00

        //转成毫秒时间
        System.out.println(instant.toEpochMilli());//1541926797229

        //在1970年1月1日 00:00:00的这个时间增加偏移量
        Instant instant1 = Instant.ofEpochSecond(60);
        System.out.println(instant1);//1970-01-01T00:01:00Z
    }

    //Duration:计算两个时间的间隔的

    @Test
    public void test3() {
        Instant ins1 = Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        Instant ins2 = Instant.now();
        Duration duration = Duration.between(ins1, ins2);
        System.out.println(duration.toMillis());//1000 得到秒的话,就是用这个方法duration.getSeconds()


        System.out.println("----------------------");
        //这个是我们的时间
        LocalTime lt1 = LocalTime.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        LocalTime lt2 = LocalTime.now();
        System.out.println(Duration.between(lt1, lt2).toMillis());//1000
    }

    //Period:计算两个日期的间隔的
    @Test
    public void test4() {

        LocalDate ld1 = LocalDate.of(2015, 9, 6);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1, ld2);
        System.out.println(period.getYears());//3年
        System.out.println(period.getMonths());//2个月
        System.out.println(period.getDays());//5天
    }
}

时间校正器

在这里插入图片描述

	//TemporalAdjusters:时间校正器
    @Test
    public void test5() {
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);//2018-11-11T17:38:37.993

        //将月中的天指定为10
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);//2018-11-10T17:38:37.993

        //时间校正器
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);//2018-11-18T17:42:38.099

        //自定义:下一个工作日是什么时候
        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);
    }

时间格式化与时区的处理

  • 时间格式化
    //DateTimeFormatter 格式化时间/日期
    @Test
    public void test6() {
        DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
        DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE;
        LocalDateTime ldt = LocalDateTime.now();
        String strDate = ldt.format(dtf);
        System.out.println(strDate);//2018-11-11T17:53:13.901

        String strDate2 = ldt.format(dtf1);
        System.out.println(strDate2);//2018-11-11

        //我们也可以指定自己的格式
        DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String strDate3 = dtf3.format(ldt);
        System.out.println(strDate3);//2018年11月11日 17:56:53

        //也可以这样来调
        String strDate4 = ldt.format(dtf3);
        System.out.println(strDate4);//2018年11月11日 17:57:37

        /** 如果把字符串解析回日期 */
        LocalDateTime newDate = LocalDateTime.parse(strDate4,dtf3);
        System.out.println(newDate);//2018-11-11T18:02:30
    }
  • 时区的处理
    在这里插入图片描述
    @Test
    public void test8() {
        //按给定的时区来获取时间
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Europe/Athens"));
        System.out.println(ldt);//2018-11-11T12:07:53.755

        LocalDateTime ldt2 = LocalDateTime.now();
        ZonedDateTime zdt = ldt2.atZone(ZoneId.of("Europe/Athens"));//这个就是带时区的日期和时间
        System.out.println(zdt);//2018-11-11T18:10:13.425+02:00[Europe/Athens]
    }

    @Test
    public void test7() {
        //获取所有的时区
        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);
    }

重复注解与类型注解

在这里插入图片描述
定义一个注解容器类:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {

    MyAnnotation[] value();
}

定义一个注解:

@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "I love Java";
}

这个时候,我们就是可以这样来用了:

public class TestAnnotation {

    @Test
    public void test1() throws NoSuchMethodException {
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method m1 = clazz.getMethod("show");

        MyAnnotation[] myAnnotations = m1.getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation myAnnotation : myAnnotations) {
            System.out.println(myAnnotation.value());//hello  world
        }
    }

    @MyAnnotation(value = "hello")
    @MyAnnotation(value = "world")
    public void show() {

    }
}

猜你喜欢

转载自blog.csdn.net/weixin_37778801/article/details/83932880