zoukankan      html  css  js  c++  java
  • java8新特性简单介绍

    lambda表达式

    public class Client {
      public static void main(String[] args) {
        Arrays.asList("hello", "world").forEach(a -> {
          System.out.println(a);
          System.out.println(a.length());
        });
      }
    }
    

    lambda可以看做java的函数式编程,主要有3部分组成,参数列表、符号->、函数体,java本身提供来了一些函数式接口,Function,Consumer,Predicate,Supplier等,我们也可以定义自己的函数式接口。

    接口默认方法和静态方法

    public class Client {
    
      public static void main(String[] args) {
        MyList myList = new MyArrayList();
        myList.addAll("hello", "world");
        System.out.println(myList);
        MyList newList = MyList.of("java", "python");
        System.out.println(newList);
      }
    
    // 定义一个接口包含默认方法和静态方法
      interface MyList {
        void add(Object obj);
    
    // 默认方法
        default void addAll(Object... objs) {
          for (Object obj : objs) {
            add(obj);
          }
        }
    
    // 静态方法
        static MyList of(Object... objs) {
          MyList myList = new MyArrayList();
          myList.addAll(objs);
          return myList;
        }
      }
    
    // 接口实现类
      static class MyArrayList implements MyList {
    
        private List<Object> data = new ArrayList<>();
    
        @Override
        public void add(Object obj) {
          data.add(obj);
        }
    
        @Override
        public String toString() {
          return data.toString();
        }
      }
    }
    

    默认方法类似于抽象类中的非抽象方法,默认被所有实现类继承,java中的List接口就定义了很多默认方法,如stream方法,静态方法和类中的静态方法类似。

    方法引用

    public class Client {
      public static void main(String[] args) {
        testConstructorReference();
        testStaticMethodReference();
        testInstanceMethodReference();
        testObjectMethodReference();
      }
    
    // 构造方法引用
      private static void testConstructorReference() {
        Supplier<Date> supplier = Date::new;
        Date date = supplier.get();
        System.out.println(date);
      }
    // 静态方法引用
      private static void testStaticMethodReference() {
        List<Integer> list = Arrays.asList(82, 22, 34, 50, 9);
        list.sort(Integer::compare);
        System.out.println(list);
      }
    // 实例方法引用
      private static void testInstanceMethodReference() {
        List<Integer> list = Arrays.asList(82, 22, 34, 50, 9);
        Supplier<Integer> supplier = list::size;
        System.out.println(supplier.get());
      }
    //对象方法引用
      private static void testObjectMethodReference() {
        BiFunction<List, String, Boolean> function = List::contains;
        Boolean result = function.apply(Arrays.asList("a", "b", "c"), "a");
        System.out.println(result);
      }
    }
    

    方法引用一般和lambda表达式一起使用,共有4中类型:

    1. 构造方法引用,语法为Class::new,调用的为无参构造器
    2. 静态方法引用,语法为Class::method
    3. 实例方法引用,语法为instance::method
    4. 对象方法引用,语法为Class::method

    重复注解

    public class Client {
      public static void main(String[] args) {
        Logs logs = User.class.getAnnotation(Logs.class);
        for (Log log : logs.value()) {
          System.out.println(log.value());
        }
      }
    
    // 使用重复注解
      @Log("hello")
      @Log("world")
      static class User {
      }
    
    // 定义一个注解 Repeatable注解标识重复注解
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @Repeatable(Logs.class)
      @interface Log {
        String value();
      }
    
    // 定义一个注解容器,存放重复注解
      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      @interface Logs {
        Log[] value();
      }
    }
    

    java8新增了一个Repeatable注解来支持重复注解,如果Log注解没有重复使用,只使用了一次,那么Class中只能得到Log注解信息,如果使用多次,那么只能得到Logs注解信息,这是编译器帮我们做的。

    Optional

    ublic class Client {
      public static void main(String[] args) {
        findUserById(1).ifPresent(System.out::println);
        findUserById(2).ifPresent(System.out::println);
      }
    
      private static Optional<User> findUserById(int id) {
        if (id == 1) {
          return Optional.of(new User("lisi"));
        } else {
          return Optional.empty();
        }
      }
    
      @Setter
      @Getter
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      static class User {
        private String userName;
      }
    }
    

    Optional可以看做一个单容量的容器,可以用来防止空指针异常。

    StreamApi

    public class Client {
      public static void main(String[] args) {
    // 创建一个流
        String lan = Stream.of("java", "javascript", "python", "go", "kotlin")
    // 过滤
            .filter(language -> language.length() > 3)
    // 排序
            .sorted(Comparator.comparingInt(String::length))
    // 收集
            .collect(Collectors.joining("-"));
        System.out.println(lan);
      }
    }
    

    streamApi可以看做一连串支持映射、过滤、排序、聚集等操作的集合,通过它我们可以很方便的操作集合,数组等容器。

    Date/Time Api

    public class Client {
      public static void main(String[] args) {
    // 不带时区的时间点
        Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
        instant = instant.plus(2, ChronoUnit.DAYS);
    // 格式化
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    // 带时区的日期加时间
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        System.out.println(localDateTime);
        System.out.println(dateTimeFormatter.format(localDateTime));
      }
    }
    

    java8新增了一些关于日期时间的API,来替代之前的Date,Calendar等,新的API都是线程安全的,在易用性上有很大提高。

    Base64

    public class Client {
      private static String BLOWFISH = "Blowfish";
      private static String SECRET = "test";
    
      public static void main(String[] args) throws Exception {
    // 待处理字符串 数据多才可以看出3种编码的区别
        String source = "abcdefghijklmnopqrstuvwxyz  -+*/_= 1234567890 abcdefghijklmnopqrstuvwxyz";
        byte[] encrypt = encrypt(source);
    // 基本编码    
    System.out.println(Base64.getEncoder().encodeToString(encrypt));
        System.out.println("===============");
    // url安全的编码   
     System.out.println(Base64.getUrlEncoder().encodeToString(encrypt));
        System.out.println("===============");
    // 支持MIME类型的编码
        System.out.println(Base64.getMimeEncoder().encodeToString(encrypt));
      }
    
    // 对数据使用Blowfish算法进行加密
      private static byte[] encrypt(String data) throws Exception {
        Cipher cipher = Cipher.getInstance(BLOWFISH);
        SecretKeySpec myskeys = new SecretKeySpec(SECRET.getBytes(), BLOWFISH);
        cipher.init(Cipher.ENCRYPT_MODE, myskeys);
        return cipher.doFinal(data.getBytes());
      }
    
    }
    

    输出结果为

    FJV8zIvqmM4xP1GwLJoRATXWE2tCzicKEp8MQtIZE5RalzKc+O4DyccpbGhanbsM+tQ3TG+TIYCMipoUGRcShP09Od+f/h1GBPyLq7fDo6Q=
    ===============
    FJV8zIvqmM4xP1GwLJoRATXWE2tCzicKEp8MQtIZE5RalzKc-O4DyccpbGhanbsM-tQ3TG-TIYCMipoUGRcShP09Od-f_h1GBPyLq7fDo6Q=
    ===============
    FJV8zIvqmM4xP1GwLJoRATXWE2tCzicKEp8MQtIZE5RalzKc+O4DyccpbGhanbsM+tQ3TG+TIYCM
    ipoUGRcShP09Od+f/h1GBPyLq7fDo6Q=
    

    java共提供了3中Base64编码

    1. 基本编码,编码表为52个英文字母加上+,/两个字符,结果输出为一行
    2. url安全编码,编码表为52个英文字母加上-,_两个字符,结果输出为一行
    3. MIME类型编码,编码表为52个英文字母加上+,/两个字符,结果输出为多行

    Base64原理及实现

    自己实现如下

    public class Base64 {
    
      private Base64() {
      }
    
      private static final String toBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
      public static String encode(byte[] src) {
        StringBuilder res = new StringBuilder();
        //得到二进制字符串
        String binaryString = toEncodeBinary(src);
        //按长度6分组,最后一组补足6 以01100001为例 011000 010000
        int length = binaryString.length();
        int pageSize = 6;
        int lack = 0;
        if (length % pageSize != 0) {
          lack = pageSize - length % pageSize;
        }
        binaryString += "0".repeat(lack);
        length = binaryString.length();
        int totalPage = length / pageSize;
        for (int i = 0; i < totalPage; i++) {
          int startPos = i * pageSize;
          //将每一组的二进制字符串转为int型数字
          int index = Integer.parseInt(binaryString.substring(startPos, startPos + pageSize), 2);
          res.append(toBase64.charAt(index));
        }
        //最后一组缺少的个数用=填充
        res.append("=".repeat(lack / 2));
        return res.toString();
      }
    
      public static byte[] decode(String src) {
        String buffer = toDecodeBinary(src);
        //按长度8分组
        int pageSize = 8;
        int totalPage = buffer.length() / pageSize;
        byte[] res = new byte[totalPage];
        for (int i = 0; i < totalPage; i++) {
          int startPos = i * pageSize;
          res[i] = (byte) Integer.parseInt(buffer.substring(startPos, startPos + pageSize), 2);
        }
        return res;
      }
    
      private static String toDecodeBinary(String src) {
        int paddingLen = 0;
        //去除=填充的字符串
        for (int i = src.length() - 1; i >= 0; i--) {
          if (src.charAt(i) != '=') {
            break;
          }
          paddingLen++;
        }
        StringBuilder buffer = new StringBuilder();
        src = src.substring(0, src.length() - paddingLen);
        for (int i = 0; i < src.length(); i++) {
          String sub = Integer.toBinaryString(toBase64.indexOf(src.charAt(i)));
          //补足6位
          buffer.append("0".repeat(6 - sub.length()));
          buffer.append(sub);
        }
        //删除填充的0,个数为=的2倍
        buffer.delete(buffer.length() - paddingLen * 2, buffer.length());
        return buffer.toString();
      }
    
      /**
       * 将字节数组的二进制表示转换成字符串 以[97]为例 结果为01100001
       */
      private static String toEncodeBinary(byte[] src) {
        StringBuilder buffer = new StringBuilder();
        for (byte b : src) {
          //转成二进制
          String binary = Integer.toBinaryString(b);
          //补足8位
          buffer.append("0".repeat(8 - binary.length()));
          buffer.append(binary);
        }
        return buffer.toString();
      }
    
      public static void main(String[] args) {
        System.out
            .println(new String(Base64.decode("YXNkZmdoamtscXdlcnR5dWlvcDEyMzQ1Njc4OTB7OicvLix9")));
        System.out.println(new String(
            java.util.Base64.getDecoder().decode("YXNkZmdoamtscXdlcnR5dWlvcDEyMzQ1Njc4OTB7OicvLix9")));
        System.out.println(Base64.encode("asdfghjklqwertyuiop1234567890{:'/.,}".getBytes()));
        System.out.println(java.util.Base64.getEncoder()
            .encodeToString("asdfghjklqwertyuiop1234567890{:'/.,}".getBytes()));
      }
    }
    

    输出为

    asdfghjklqwertyuiop1234567890{:'/.,}
    asdfghjklqwertyuiop1234567890{:'/.,}
    YXNkZmdoamtscXdlcnR5dWlvcDEyMzQ1Njc4OTB7OicvLix9
    YXNkZmdoamtscXdlcnR5dWlvcDEyMzQ1Njc4OTB7OicvLix9
    

    可以看到和java内置的Base64的结果一致,以字符串a为例,来分析一下是如何编码的

    1. 转成字节数组 [97]
    2. 将每一个字节转成二进制表示,前面补足8位,01100001
    3. 按每6位分组,最后一组后面补足6位,011000 010000
    4. 每一组转成int类型的十进制表示 24 16
    5. 根据每一个索引查找编码表,转换 YQ
    6. 第3步补了4个0,表示最后要填充4/2个=号,YQ==就是最终结果

    解码过程就是反向的过程。

  • 相关阅读:
    [android] 获取系统的联系人信息
    [android] 内容观察者
    [nodejs] nodejs开发个人博客(六)数据分页
    [android] 插入一条记录到系统短信应用里
    [android] 短信的备份
    [android] 内容提供者实现
    [android] 内容提供者简介
    [nodejs] nodejs开发个人博客(五)分配数据
    [android] 常用数据适配器SimpleAdapter
    [android] 常用数据适配器ArrayAdapter
  • 原文地址:https://www.cnblogs.com/strongmore/p/13380118.html
Copyright © 2011-2022 走看看