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==就是最终结果

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

  • 相关阅读:
    [转发]深入理解git,从研究git目录开始
    iOS系统网络抓包方法
    charles抓包工具
    iOS多线程中performSelector: 和dispatch_time的不同
    IOS Core Animation Advanced Techniques的学习笔记(五)
    IOS Core Animation Advanced Techniques的学习笔记(四)
    IOS Core Animation Advanced Techniques的学习笔记(三)
    IOS Core Animation Advanced Techniques的学习笔记(二)
    IOS Core Animation Advanced Techniques的学习笔记(一)
    VirtualBox复制CentOS后提示Device eth0 does not seem to be present的解决方法
  • 原文地址:https://www.cnblogs.com/strongmore/p/13380118.html
Copyright © 2011-2022 走看看