jdk1.8新特性总结

jdk1.8和之前的版本有较大的改动,HashMap新增红黑树的实现,ConcurrentHashMap使用cas算法重新实现。并且新增了几个新特性。

1. 二进制变量的表示,支持将整数用二进制来表示,用0b开头。
  • 所有整数int,short,long,byte都可以用二进制表示。
byte b= (byte)0b10000101;
int i = 0b10001010; 
long l = 0b10010101;
short s = 0b10000001;
2. switch语法支持String类型。
        String str = null;
        switch (str) {
            case "1": {
                break;
            }
            case "2": {
                break;
            }
            case "3": {
                break;
            }
            default: {
                System.out.println("没了");
            }
        }

3. 新增Try-with-resource语句。

try-with-resource语句是一种声明了一种或多种资源的try语句,所谓资源:是指在使用完成之后需要关闭的对象(例如io流)。try-with-resource语句保证了每个资源在语句结束时都将被关闭。任何实现了java.lang.AutoCloseable接口和实现了java.lang.closeable接口的对象,都可以当做资源来使用。

        //之前使用
        BufferedWriter bufferedWriter = null;
        try {
            bufferedWriter = new BufferedWriter(new FileWriter(new File("D://123.txt")));
            bufferedWriter.write("我是测试");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //需要在finally代码块中关闭
            try {
                //而且自己捕获的话还会多出一层异常
                bufferedWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

       //Try-with-resources语句(Try括号中可以声明多个资源对象)
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File("D://123.txt")));
             BufferedReader bufferedReader = new BufferedReader((new FileReader(new File("D://456.txt"))))
        ) {
            bufferedWriter.write("我是测试");
            bufferedReader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }


try-with-resource语句,我们可以看到将对象声明写在了try()中的括号中。当代码执行完毕之后java会确保外部资源的close方法被调用。cath只是捕获在读写时的异常,并没有捕获关闭时的异常。这种行为叫做异常抑制,我们可以通过e.getSuppressed()方法来展示关闭时的异常。

4. catch多个异常
public static void main(String[] args) throws Exception {
        try {
            testthrows();
        } catch (IOException | SQLException ex) {
              throw ex;
            }
        }
    public static void testthrows() throws IOException, SQLException {
    }


5. 数字类型的变量可以添加下划线,更友好的显示方式。下划线只能在数字中间在开始或结束一定不要使用下划线。
int i = 1_000_000;
double d=1_000_00.75;
float f=1_000.12F;
long l=1_000_000_000L;
System.out.println("i=:"+i);
System.out.println("d=:"+d);
System.out.println("f=:"+f);
System.out.println("l=:"+l);

//输出结果
i=:1000000
d=:100000.75
f=:1000.12
l=:1000000000
6. 泛型实例的创建可以通过类型推断来简化,也就是说在new的时候可以不用写new后面<>中的类型。只用<>就可以了。
//之前
List list  = new ArrayList();
//之后,省略<>中的
List list  = new ArrayList<>();
7. default关键字

jdk1.8添加了default关键字,用作在接口中。被该关键字修饰的方法不是必须被实现的

编写一个interface

/**
 * @author [email protected]
 * @date 2019/5/20.
 */
public interface Test {

    /**
     * 正常方法
     */
    void test();

    /**
     * default修饰的
     */
    default void test1() {
        System.out.println("默认不需要实现的方法");
    }

}

实现类

/**
*可以看到不需要实现test1方法了
*/
class testImpl implements Test{

        @Override
        public void test() {
            
        }

        public static void main(String[] args) {
            Test test = new Test.testImpl();
            test.test1();
        }
    }

我们可以看到test1方法不需要被实现也可以,test1就作为接口中的默认类,如果需要重写在进行重写。在开发中我们可以根据自己的业余来编写这样公共的默认方法。大大减少的代码量。

8. Lambda表达式

Lambda表达式是jdk1.8一个重要的里程碑,代表java也开启了函数时编程。

函数式编程:函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

最直观的应该就是集合的排序了

public static void main(String[] args) {
            List list = new ArrayList();
            list.add(12);
            list.add(98102);
            list.add(928);
            list.add(120);


            /**
             * 常规写法
             */
            list.sort(new Comparator() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2 - o1;
                }
            });

            /**
             * lambda表达式写法
             */
            list.sort((o1, o2) -> o2 - o1);

}

基本语法
(formal parameter list) -> { expression or statements }
参数列表

  • 参数列表是一个逗号分隔的形式参数列表,这些参数与功能接口中单一方法的形式参数相对应。
  • 参数列表中的参数类型是可选项,如果未指定参数类型,将从上下文推断。
  • 参数列表必须用小括号括起来,但如果只有一个参数且不带参数类型时小括号可以省略。
  • 参数列表如果为空(即:功能接口方法没有形式参数),则必须指定空括号。

Lambda 主体根据以下选项之一返回结果:

  • 如果 lambda 主体是单一表达式,则返回表达式的值(如果有的话)。
  • 如果功能接口方法的结果是 void,可以提供一个 return 语句,但这不是必需的。
  • 如果功能接口方法具有返回类型,且 lambda 主体不是单一表达式,则 lambda 主体必须使用 return 语句返回匹配的值。

语句块必须包含在大括号内,除非语句块是一个方法调用语句,且功能接口方法的返回结果是void。
Lambda 表达式实际上是一种匿名方法实现,指定形式参数,并使用 return 语句返回值。匿名方法必须按照以下规则所规定的与其实现的功能接口方法兼容。

  • Lambda 表达式返回的结果必须与功能接口方法的结果兼容。返回值的类型可以是功能接口方法声明中返回类型的子类型。
  • Lambda 表达式签名必须与功能接口方法的签名相同。
  • Lambda 表达式只能抛出那些在功能接口方法的 throws 子句中声明了异常类型或异常超类型的异常。

lambda用一行代码就解决排序问题。但是并不是所有的方法都可以使用lambda表达式,只有函数式接口才行。

9. 函数式接口

函数式接口是指:仅仅只包含一个抽象方法的接口。java8提供了一个@FunctionalInterface注解来定义函数式接口,不符合规范会报错。

/**
 * @author [email protected]
 * @date 2019/5/20.
 */
@FunctionalInterface
public interface FirstLambda {

    /**
     * 典型的,就一个抽象方法
     */
    Integer test(Integer s, Integer s1);

    default void test1(){
        System.out.println("test1");
    }


    class Test {
        public static void main(String[] args) {
            test((s, s1) -> {
                return s + s1;
            });
        }

        /**
         * 定义一个符合lambda使用的方法
         */
        public static void test(FirstLambda firstLambda) {
            firstLambda.test1();
            System.out.println("把接口作为参数");
        }
    }
}
10. 方法与构造器参数引用

java8提供了一种调用方法对象::属性 使用::符号。
最简单的使用,用的最多的还是在stream流中。这里只做一个写法的演示

    /**
     * 定义一个内部类
     */
    class User {
        String age;
        String name;

        public User(String age, String name) {
        }
        public User() {
        }

        public User(Integer integer, Integer integer1) {

        }

        public String getAge() {
            return age;
        }

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

        public String getName() {
            return name;
        }

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

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

    }

    class Test {
        public static void main(String[] args) {
            List list = new ArrayList();
            User user = new User();
            user.setAge("1");
            list.add(user);
            //print方法用这种方式调用
            list.forEach(System.out::print);
        }
    }

11. Stream流(重要)

Stream的加入可以说是对集合一大加强。大大减少了我们对集合的操作,并且又添加了很多方法。而且和lambda可以一起使用。贴代码:

   /**
     * 定义一个内部类
     */
    class User {
        String age;
        String name;

        public User(String age, String name) {
        }
        public User() {
        }

        public User(Integer integer, Integer integer1) {

        }

        public String getAge() {
            return age;
        }

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

        public String getName() {
            return name;
        }

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

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

class Test {
    
        public static void main(String[] args) {
            List list = new ArrayList();
            User user = new User();
            user.setAge("1");
            list.add(user);

            /**
             * 遍历
             */
            list.stream().forEach(us -> {
                //在这里可以进行操作
                System.out.println(us.getAge());
            });

            /**
             *寻找第一条
             */
            Optional first = list.stream().findFirst();
            //得到User对象
            User user1 = first.get();

            /**
             * 转换为set
             */
            Set collect = list.stream().collect(Collectors.toSet());

            /**
             * 默认排序
             */
            list.stream().sorted();


            /**
             * filter 根据条件过滤,可以多个条件
             * collect 最后返回过滤好的对象
             */list.stream()
                    .filter(user2 -> user2.getAge().equals("1"))
                    .filter(user2 -> user2.getName().equals("张三"))
                    .collect(Collectors.toList());

            /**
             * 读取0-10 条数据
             */
            list.stream().limit(10).collect(Collectors.toList());

            /**
             *  map方法用于映射每个元素到对应的结果, 可以对age进行操作,改变类型或者进行操作等等。
             *  我们通过foreach可以看到 list对象已经转换为age对应类型的集合,也就是说map是吧原集合转换为对应类型的集合。
             */
            list.stream().map(user2 -> user2.getAge() + 1).forEach(s -> System.out.println(s));
        }
           /**
             * 划重点,groupingby分组 直接根据属性分组
             * 返回结果 key=age value=List
             */
            Map> collect1 = list.stream().collect(Collectors.groupingBy(User::getAge));

   
    }


12. Optional 优雅的判断空指针。

我们都知道在java中调用空对象去进行一些操作就会抛出空指针异常。
String age = user.getCode().getAddress().getAge() 一直调用的话如果address为空那么就会出现空指针异常,那么我们通常这样子去调整代码

if(null!=user){
       Code code = user.getCode();
       if(null!=code){
          Address address=code.getAddress();
          if(null!=address){
             String age =address.getAge();
           }
       }
   }
}

可以看出来我们为了得到一个age属性进行了一系列的非空判断,代码很冗余,难以维护。为了简化代码并且让代码可读性更强,我们引入Optional类

首先我们先这样子写

//声明一个user对象
User user = new User();
//如果user不为空那么调用map方法获取Code对象
Optional code = Optional.ofNullable(user).map(u -> u.getCode());
//获取Address对象
Optional address = code.map(c -> c.getAddress());
//获取Age属性
Optional s = address.map(a -> a.getAge());

通过上述代码我们可以看到代码量并没有减少,但是可以清楚的看到每一步的返回值,所以我们可以这样优化代码

Optional s1 = Optional.ofNullable(user)//user不为空返回Optional
                    .map(u -> u.getCode())//返回Optional
                    .map(c -> c.getAddress())//返回Optional
.map(a -> a.getAge()) .orElse("我是空的");//如果为空怎么办,我们可以自己默认一个值

这样子代码简洁了很多,可读性和可塑性更强了。
简单介绍一下Optional提供的几个方法

方法 简单描述
of 把指定的值封装为Optional对象,如果指定的值为null,则抛出NullPointerException
empty 创建一个空的Optional对象
ofNullable 把指定的值封装为Optional对象,如果指定的值为null,则创建一个空的Optional对象
get 如果创建的Optional中有值存在,则返回此值,否则抛出NoSuchElementException
orElse 如果创建的Optional中有值存在,则返回此值,否则返回一个默认值
orElseGet 如果创建的Optional中有值存在,则返回此值,否则返回一个由Supplier接口生成的值
orElseThrow 如果创建的Optional中有值存在,则返回此值,否则抛出一个由指定的Supplier接口生成的异常
filter 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象
map 如果创建的Optional中的值存在,对该值执行提供的Function函数调用
flagMap 如果创建的Optional中的值存在,就对该值执行提供的Function函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象
isPresent 如果创建的Optional中的值存在,返回true,否则返回false
ifPresent 如果创建的Optional中的值存在,则执行该方法的调用,否则什么也不做
13. Date Time日期,时间处理
  • 我们一直使用的是java.util.Date来处理时间,但是它是线程不安全的。在多线程下会出现问题
  • java.sql.Date,java.util.Date 两个包很相似。从设计上来说很糟糕了。
  • 新加入的API在java.time包下。重点说两个Local(本地),Zoned(时区)

LocalDate基本用法

 public static void main(String[] args) {
            //获取当前日期
            LocalDate now = LocalDate.now();
            System.out.println(now);
            //获取年,月,日
            int year = now.getYear();
            int monthValue = now.getMonthValue();
            int dayOfMonth = now.getDayOfMonth();
            System.out.println("年"+year+"月"+monthValue+"日"+dayOfMonth);
            //通过指定日期生成localDate对象
            LocalDate localDate = LocalDate.of(2019, 05, 01);
            System.out.println("生成的日期"+localDate);
            //判断两个日期是否相等,equals方法被重写了。所以直接可以判断
            boolean equals = localDate.equals(now);
            System.out.println("两个日期是否相等"+equals);


        }

相关的一些API

方法 描述
now 静态方法,返回今天的日期
of 从指定年份,月份和日期创建LocalDate的静态方法
getDayOfMonth, getMonthValue, getYear 以int形式返回此LocalDate的日,月或年
getMonth 以Month枚举常量返回此LocalDate的月份
plusDays, minusDays 给LocalDate添加或减去指定的天数
plusWeeks, minusWeeks 给LocalDate添加或减去指定的星期数
plusMonths, minusMonths 给LocalDate添加或减去指定的月份数
plusYears, minusYears 给LocalDate添加或减去指定的年数
isLeapYear 检查LocalDate指定的年份是否为闰年
isAfter, isBefore 检查此LocalDate是在给定日期之后还是之前
lengthOfMonth 返回此LocalDate中月份的天数
withDayOfMonth 返回此LocalDate的拷贝,将月份中的某天设置为给定值
withMonth 返回此LocalDate的拷贝,其月份设置为给定值
withYear 返回此LocalDate的拷贝,并将年份设置为给定值

LocalDateTime
LocalDateTime相关API

方法 描述
now 返回当前日期和时间的静态方法。
of 从指定年份,月份,日期,小时,分钟,秒和毫秒创建LocalDateTime的静态方法。
getYear, getMonthValue, getDayOfMonth, getHour, getMinute, getSecond 以int形式返回此LocalDateTime的年,月,日,小时,分钟或秒部分。
plusDays, minusDays 给当前LocalDateTime添加或减去指定的天数。
plusWeeks, minusWeeks 给当前LocalDateTime添加或减去指定的周数。
plusMonths, minusMonths 给当前LocalDateTime添加或减去指定的月数。
plusYears, minusYears 给当前LocalDateTime添加或减去指定的年数。
plusHours, minusHours 给当前LocalDateTime添加或减去指定的小时数
plusMinutes, minusMinutes 给当前LocalDateTime添加或减去指定的分钟数
plusSeconds, minusSeconds 给当前LocalDateTime添加或减去指定的秒数
IsAfter, isBefore 检查此LocalDateTime是否在指定的日期时间之后或之前
withDayOfMonth 返回此LocalDateTime的拷贝,并将月份中的某天设置为指定值
withMonth, withYear 返回此LocalDateTime的拷贝,其月或年设置为指定值
withHour, withMinute, withSecond 返回此LocalDateTime的拷贝,其小时/分钟/秒设置为指定值

ZonedDateTime和LocalDateTime用法差不多,只不过ZonedDateTime多了一个时区的概念。

你可能感兴趣的:(jdk1.8新特性总结)