这样java8新特性 更实用

java8入门

一、接口里有默认方法

Java 8使我们能够通过使用 default 关键字向接口添加非抽象方法实现。 此功能也称为虚拟扩展方法

/**
 * @author DMJ
 * @date 2020/7/6
 */
public interface Person {
    void say();

    //静态的东西是属于class
    //接口里可以定义静态的
    public static final String state = "xixi";
    //接口里定义静态的方法
    public static void play(){
        System.out.println("play");
    }
    default void run(){
        System.out.println("run");
    }
    default void eat(){
        System.out.println("eat");
    }
    
}
/**
 * @author DMJ
 * @date 2020/7/6
 */
public class Tom implements Person{
    @Override
    public void say() {
        System.out.println("say");
    }

    public static void main(String[] args) {
        Person p = new Tom();
        p.say();
        p.eat();
        p.run();
    }
}

say
eat
run

在java8以后 接口里可以写默认的方法实现

可以重写

 @Override
    public void run() {
        System.out.println("run1");
    }

say
eat
run1

不管是抽象类还是接口,都可以通过匿名内部类的方式访问。不能通过抽象类或者接口直接创建对象。对于上面通过匿名内部类方式访问接口,我们可以这样理解:一个内部类实现了接口里的抽象方法并且返回一个内部类对象,之后我们让接口的引用来指向这个对象。

public interface Person {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}



public class Tom {

    public static void main(String[] args) {
        Person person = new Person() {
            @Override
            public double calculate(int a) {
                return sqrt(a*100);
            }
        };
        System.out.println(person.calculate(100));
        System.out.println(person.sqrt(16));
    }

}


二、Lambda表达式

在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

可以看出,代码变得更段且更具有可读性,但是实际上还可以写得更短:

对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点

 public static void main(String[] args) {

        /*Person tom = new Person() {
            @Override
            public void say() {
                System.out.println("say");
            }
        };
        tom.say();*/

        
        //函数式表达方式
        Person tom = () -> System.out.println("say");
        tom.say();

    }
public interface Person {
    String say(String msg);
}

public class Tom {

    public static void main(String[] args) {

        //函数式表达方式
        Person tom = (msg) -> "jerry say:" + msg;
        System.out.println(tom.say("hello world"));

    }
}

//jerry say:hello world
public class Tom {

    public static void tomSay(Person p){
        String helloWorld = p.say("hello world");
        System.out.println(helloWorld);
    }

    public static void main(String[] args) {

        Tom.tomSay((msg) -> "jerry say:" + msg);

    }
}

//jerry say:hello world

1、函数式接口

Java 语言设计者们投入了大量精力来思考如何使现有的函数友好地支持Lambda。最终采取的方法是:增加函数式接口的概念。

“函数式接口”指仅仅只包含一个抽象方法,但是可以有多个非抽象方法(也就是默认方法)的接口。

像这样的接口,可以被隐式转换为lambda表达式。

java.lang.Runnablejava.util.concurrent.Callable 是函数式接口最典型的两个例子。(马上学)

Java 8增加了一种特殊的注解@FunctionalInterface,但是这个注解通常不是必须的(某些情况建议使用),只要接口只包含一个抽象方法,虚拟机会自动判断该接口为函数式接口。一般建议在接口上使用@FunctionalInterface 注解进行声明,这样的话,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的

@FunctionalInterface
public interface Person {
    String say(String msg);
}

2、Lamda 表达式作用域

我们可以直接在 lambda 表达式中访问外部的局部变量:

但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:

public class Tom {

    public static void tomSay(Person p){
        String helloWorld = p.say("hello world");
        System.out.println(helloWorld);
    }

    public static void main(String[] args) {

        final int tail = 10;

        Person p = (msg) -> "jerry say:" + msg + "," + tail;
        Tom.tomSay(p);
    }
}

不过这里的 num 必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:

public static void main(String[] args) {

        final int tail = 10;

        Person p = (msg) -> "jerry say:" + msg + "," + tail;
        tail = 11;
        Tom.tomSay(p);
    }

3、内置函数式接口

JDK 1.8 API包含许多内置函数式接口。 其中一些借口在老版本的 Java 中是比较常见的比如: ComparatorRunnable,这些接口都增加了@FunctionalInterface注解以便能用在 lambda 表达式上。

但是 Java 8 API 同样还提供了很多全新的函数式接口来让你的编程工作更加方便,有一些接口是来自 Google Guava 库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。

3.1 Predicates

Predicate 接口是只有一个参数的返回布尔类型值的 断言型 接口。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非):

3.2 Consumers
public class Tom {

    public static void tomSay(Consumer<String> consumer,String msg){
        consumer.accept(msg);
    }

    public static void main(String[] args) {

        /*Tom.tomSay(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s + "消费了");
            }
        } ,"jerry");

        };*/

        Tom.tomSay((msg) -> System.out.println(msg + "消费了"),"jerry");
    }
}
3.3 Functions

Function 接口接受一个参数并生成结果。默认方法可用于将多个函数链接在一起(compose, andThen):

/**
 * @author DMJ
 * @date 2020/7/6
 */
public class Tom {

    public static void tomSay(Function<String,String> function, String msg){
        String result = function.apply(msg);
        System.out.println(result);
    }

    public static void main(String[] args) {

        /*Tom.tomSay((s) -> s + "转化了" ,"jerry");*/
        
        Tom.tomSay(new jerry(),"jerry");


    }
    static class jerry implements Function<String,String>{

        @Override
        public String apply(String s) {
            return  s + "转化了";
        }
    }
    //或者
     public static void main(String[] args) {

        /*Tom.tomSay((s) -> s + "转化了" ,"jerry");*/
        
        Tom.tomSay(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s + "转化了";
            }
        }, "jerry");


    }
//最终可以简化为
 /*Tom.tomSay((s) -> s + "转化了" ,"jerry");*/

}

三、Streams(流)

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如java.util.Collection 的子类,List 或者 Set, Map 不支持。Stream 的操作可以串行执行或者并行执行。

首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

Filter(过滤)

过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

Sorted(排序)

排序是一个 中间操作,返回的是排序好后的 Stream。如果你不指定一个自定义的 Comparator 则会使用默认排序。

Map(映射)

中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。

下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

Reduce(规约)

这是一个 最终操作 ,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规约后的结果是通过Optional 接口表示的:

Count(计数)

计数是一个 最终操作,返回Stream中元素的个数,返回值类型是 long

Match(匹配)

Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是 最终操作 ,并返回一个 boolean 类型的值。

public static void main(String[] args) {

        List<Integer> list = Arrays.asList(12, 25, 30, 10, 14, 12, 21,14);
        //将list变为流
        //中间是中间操作  最后是最终操作

        Optional<Integer> reduce = list.stream()
                //map映射
                .map(item -> item * 10)
                //过滤
                .filter(item -> item > 120)
                //排序 正序
                .sorted((num1, num2) -> num1 - num2)
                //去重
                .distinct()
                //规约
                .reduce((num1, num2) -> num1 + num2);

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

    }


 public static void main(String[] args) {

        List<Integer> list = Arrays.asList(12, 25, 30, 10, 14, 12, 21,14);
        //将list变为流
        //中间是中间操作  最后是最终操作

        list.stream()
                //map映射
                .map( item -> item * 10)
                //过滤
                .filter(item -> item > 120)
                //排序 正序
                .sorted((num1,num2) -> num1 - num2)
                //去重
                .distinct()
                //将数遍历出来
                .forEach( System.out::println);

    }
/**
 * @author DMJ
 * @date 2020/7/6
 */

public class Person {
    private String name;
    private int age;
    private int height;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

/**
 * @author DMJ
 * @date 2020/7/6
 */
public class Tom {


    public static void main(String[] args) {

        List<Person> people = Arrays.asList(
                new Person("tom",10,175),
                new Person("jerry",15,160),
                new Person("xiongxiong",18,155),
                new Person("lucy",20,170),
                new Person("mark",16,165)

        );

        List<Person> collect = people.stream().filter(p -> p.getAge() > 12).collect(Collectors.toList());
        System.out.println(collect);

    }

}
//[Person{name='jerry', age=15, height=160}, Person{name='xiongxiong', age=18, height=155}, Person{name='lucy', age=20, height=170}, Person{name='mark', age=16, height=165}]


List<Person> collect = people.stream()
                //排序 正序 倒序p2-p1
                .sorted((p1,p2) -> p1.getAge()-p2.getAge())
                //获取两个
                //.limit(2);
                //判断所有的年龄是否大于50 返回false
                //.allMatch(p -> p.getAge()>50)
                //找到第一个
                //.findFirst()
        System.out.println(collect);
最优的写法
 public static void main(String[] args) {

        List<Person> people = Arrays.asList(
                new Person("tom",10,175),
                new Person("jerry",15,160),
                new Person("xiongxiong",18,155),
                new Person("lucy",20,170),
                new Person("mark",16,165)

        );

     //可以直接调用 正序
        people.sort((p1,p2) -> p1.getAge() - p2.getAge());
        //最好的写法
        people.sort(Comparator.comparingInt(Person::getAge));
        //倒序
        people.sort(Comparator.comparingInt(p -> -p.getAge()));
        people.forEach(System.out::println);
        
     
     
        //先按照身高的倒序排  在按照年龄的正序排
        people.sort(Comparator.comparingInt(Person::getHeight).reversed().thenComparingInt(Person::getAge));
        people.forEach(System.out::println);
        //Person{name='tom', age=10, height=175}
Person{name='lucy', age=20, height=170}
Person{name='mark', age=16, height=165}
Person{name='jerry', age=15, height=160}
Person{name='xiongxiong', age=18, height=155}

    }
  1. List item

四、并行流 Parallel Streams

前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

下面的例子展示了是如何通过并行Stream来提升性能:

/**
 * @author DMJ
 * @date 2020/7/6## 标题
 */
public class Tom {


    public static void main(String[] args) {

        ArrayList<Double> nums = new ArrayList<>();
        for (int i = 0; i <10000000 ; i++) {
            double random = Math.random();
            nums.add(random);
        }
        //普通流
        long start = System.currentTimeMillis();
        List<Double> collect = nums.stream().sorted().collect(Collectors.toList());
        long end = System.currentTimeMillis();
        System.out.println(end - start);


        //并行流  以多线程的形式
        long start2 = System.currentTimeMillis();
        List<Double> collect1 = nums.parallelStream().sorted().collect(Collectors.toList());
        long end2 = System.currentTimeMillis();
        System.out.println(end2 - start2);
    }

}

4240
2540
//冒泡形式很慢
public static void main(String[] args) {

        ArrayList<Double> nums = new ArrayList<>();
        for (int i = 0; i <100000 ; i++) {
            double random = Math.random();
            nums.add(random);
        }
        long start = System.currentTimeMillis();
        long start2 = System.currentTimeMillis();
        for (int i = 0; i <nums.size()-1 ; i++) {
            for (int j = 0; j < nums.size()-1-i; j++) {
                if (nums.get(j) > nums.get(j + 1)) {
                    double temp = nums.get(j);
                    nums.set(j, nums.get(j + 1));
                    nums.set(j + 1, temp);
                }
            }

        }
        //普通流

        List<Double> collect = nums.stream().sorted().collect(Collectors.toList());
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        //并行流  以多线程的形式

        List<Double> collect1 = nums.parallelStream().sorted().collect(Collectors.toList());
        long end2 = System.currentTimeMillis();
        System.out.println(end2 - start2);


    }

五、Data API

Java 8在 java.time 包下包含一个全新的日期和时间API。新的Date API与Joda-Time库相似,但它们不一样。以下示例涵盖了此新 API 的最重要部分。译者对这部分内容参考相关书籍做了大部分修改。

译者注(总结):

Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用 Instant 类来表示,Instant 类也可以用来创建旧版本的java.util.Date 对象。

在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds的静态方法,它返回所有区域标识符。

jdk1.8中新增了 LocalDate 与 LocalDateTime等类来解决日期处理方法,同时引入了一个新的类DateTimeFormatter 来解决日期格式化问题。可以使用Instant代替 Date,LocalDateTime代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。

和 SimpleDateFormat 相比,DateTimeFormatter 是线程安全的。

Instant 的精确度更高,可以精确到纳秒级。

Duration 可以便捷得到时间段内的天数、小时数等。

LocalDateTime 能够快速地获取年、月、日、下一月等。

TemporalAdjusters 类中包含许多常用的静态方法,避免自己编写工具类

Clock

Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用 Instant 类来表示,Instant 类也可以用来创建旧版本的java.util.Date 对象。

Timezones(时区)

在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类ZoneId(在java.time包中)表示一个区域标识符。 它有一个名为getAvailableZoneIds的静态方法,它返回所有区域标识符。

LocalTime(本地时间)

LocalTime 定义了一个没有时区信息的时间,例如 晚上10点或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:

LocalDate(本地日期)

LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。

LocalDateTime(本地日期时间)

LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime 和 LocalTime还有 LocalDate 一样,都是不可变的。LocalDateTime 提供了一些能访问具体字段的方法。

 public static void main(String[] args) {

        //获取时区
        Clock clock = Clock.systemDefaultZone();
        System.out.println(clock.getZone());

        //获取当前的时间
        LocalDate localDate = LocalDate.now();
        System.out.println(localDate);
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDateTime);

        //获取其他时区的时间 以美国纽约为例
        LocalDate localDate1 = LocalDate.now(ZoneId.of("America/New_York"));
        System.out.println(localDate1);

        //格式化时间对象
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        String format = localDate.format(dateTimeFormatter);
        System.out.println(format);

        LocalDateTime localDateTime1 = LocalDateTime.now();
        String time = localDateTime1.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(time);
    }
    
//Asia/Shanghai
2020-07-07
2020-07-07T01:14:05.449
2020-07-06
2020-07-07
2020-07-07 01:14:05
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值