JDK7~13的新特性


 

jdk7

新的资源关闭方式 try-with-resources

以前的资源关闭方式 try…finally

OutputStream os = null;
try {
    os = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
    os.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
} finally {
    if (os != null) {
        try {
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

需要在finally中关闭,且在finally中还可能需要嵌套try,代码冗杂;如果需要关闭多个资源,关闭顺序还可能写错。
 

jdk7新增的资源关闭方式 try-with-resources

/**
 * 在try()中定义要关闭的资源,try代码块执行完毕后会自动关闭该资源,无需手动关闭。关闭时会自动flush刷出数据,无需手动调用flush()。
 * 相当于自动提供了一个关闭资源的finally代码块,如果执行try代码块时发生异常,资源也能正常关闭。
 * 如果有手动写的finally代码块,会在手动写的finally代码块之前执行。
 */
try (OutputStream os = new FileOutputStream("C:/Users/chy/Desktop/1.txt")) {
    os.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
}


/**
 * 如果有多个要关闭的资源,分号隔开。关闭顺序与声明顺序相反,后声明的先关闭。
 * 只要实现了AutoCloseable接口的类,都可以使用此种方式关闭资源。
 * IO流的4个抽象基类InputStream、OutputStream、Reader、Writer都实现了AutoCloseable接口,IO流基本都可以使用此方式关闭
 */
try (
    OutputStream os1 = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
    OutputStream os2 = new FileOutputStream("C:/Users/chy/Desktop/2.txt")
) {
    os1.write("hello".getBytes());
} catch (FileNotFoundException e) {
    //...
} catch (IOException e) {
    //...
}

自动关闭资源,方便,代码也简洁,尽量用try-with-resources代替try…finally来关闭资源。

 

jdk8

接口新增的默认方法、静态方法
public interface Animal {

    void run();

    void eat();

    default void breath() {
        System.out.println("默认方法实现...");
    }

    static void test() {
        System.out.println("静态⽅法...");
    }
    
}

jdk8以前:接口中的方法只能是抽象方法,不能有方法体(实现)

jdk8

  • 接口新增了默认方法:方法使用default修饰,必须提供方法体(实现)。实现类会继承默认方法,可根据需要重写默认方法,实现类调用默认方法时,如果进行了重写则默认调用本类中的默认方法,否则调用接口中的默认方法。①默认访问权限不需要任何访问权限关键字修饰,default并不是默认访问权限,接口中方法的访问权限只能是public;②默认方法只能存在于接口中,不能存在于抽象类、具体实现类中。
  • 接口新增了静态方法:方法使用static修饰,必须提供方法体(实现),通过接口名调用。关于静态方法,注意:子类会继承父类的静态方法,但子接口、实现类不会继承父接口的静态方法。

接口新增了默认方法、静态方法,但和抽象类相比,接口依然不能表达对象状态的变化(接口中不能定义对象的成员字段),接口不能完全替代抽象类。

 

新增的base64加解密api

base64常用于数据的加解密,比如登录使用http+密码明文很不安全,最好使用https,如果坚持要用http,最好加密敏感数据后再进行传输。
 

jdk7的base64加解密方式

//原文本
String text = "年年有鱼";
//编码后的文本
String encodedText;

//编码
BASE64Encoder encoder = new BASE64Encoder();
encodedText = encoder.encode(text.getBytes("UTF-8"));
System.out.println("编码后得到的文本:" + encodedText);

//解码
BASE64Decoder decoder = new BASE64Decoder();
text = new String(decoder.decodeBuffer(encodedText), "UTF-8");
System.out.println("解码后得到的原文本:" + text);

编解码性能差,据说会在将来的版本中删除。
 

jdk8提供了性能更好的base64加解密方式

//原文本
String text = "年年有鱼";
//编码后的文本
String encodedText;

//编码
Base64.Encoder encoder = Base64.getEncoder();
encodedText = encoder.encodeToString(text.getBytes("UTF-8"));
System.out.println("编码后得到的文本:" + encodedText);

//解码
Base64.Decoder decoder = Base64.getDecoder();
text = new String(decoder.decode(encodedText), "UTF8");
System.out.println("解码后得到的原文本:" + text);

 

新增的时间日期类、时间日期处理类
  • jdk7的时间日期类:Date、Calendar,非线程安全,api设计差、不好使用。
  • jdk8新增的时间日期类:LocalDate 日期、LocalTime 时间、LocalDateTime 日期+时间,api众多,操作方便。
     
  • jdk7的时间日期处理类:DateFormat、SimpleDateFormat,SimpleDateFormat简单易用,但非线程安全
  • jdk8新增的时间日期处理类:DateTimeFormatter,同样好用,但线程安全
//新增的三个时间日期类,创建对象的方法都是静态方法,通过类名直接调用
LocalDateTime today = LocalDateTime.now();
LocalDateTime dt = LocalDateTime.of(2020, 1, 1, 1, 1, 1);

//使用新增的时间日期处理类进行格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDT = dtf.format(today);
System.out.println("格式化后得到的时间日期:" + formatDT);

//计算2个时间日期的差值。第二个参数减第一个参数
Duration duration = Duration.between(dt, today);
//Duration包含了多种差值表示方式
System.out.println("dt距today多少天:" + duration.toDays());
System.out.println("dt距today多少小时:" + duration.toHours());

 

新增的NPE处理类Optional

使用大段大段的if…else…判空,可读性差,可以用Optional替代对象成员变量的if判空。

User user = null;


//user可以为null
Optional<User> optional = Optional.ofNullable(user);

//user不能为null,为null时会发生NPE
Optional<User> optional = Optional.of(user);


//Optional可以看做对对象的包装,判断内部对象是否为null
boolean present = optional.isPresent();

//获取内部对象,内部对象为null时会发生NPE
User user = optional.get();



//可以对optional中的对象进行过滤,满足条件才保留在optional中,以新对象的形式返回
//user可以为null,为null时不会发生NPE
optional = optional.filter(user -> StringUtils.isBlank(user.getUsername()) && user.getUsername().length() < 10);


String defaultHeadImg = "/xxx/xxx.png";

//user对象的headImg属性不为null,则返回headImg,为null则返回设置的默认值
String headImg = optional.map(user -> user.getHeadImg()).orElse(defaultHeadImg);

//可以把lambda表达式的返回值作为默认值
String headImg = optional.map(user -> user.getHeadImg()).orElseGet(() -> defaultHeadImg);

//也可以直接抛出异常
String headImg = optional.map(user -> user.getHeadImg()).orElseThrow(() -> new RuntimeException("未选择头像"));

//以上三句代码,user为null时也不会发生NPE


//也可以先进行判断,内部对象不为null才进行操作
optional.ifPresent(user -> {
    //传入的是对象地址,对形参的操作就是对实参的操作
    //...
});
  • 创建Optional对象,对内部对象进行判空,再进行操作,这样使用Optional没有意义,比原先的if判空还写得复杂。
  • Optional适合在对象可能为null(需要判空)时单次获取对象的属性,不是每种判空都适合使用Optional,比如对包装类、String这些简单对象的判空,比如要多次获取对象的(多个)属性进行操作。高频使用map()方法会显得很繁琐,最好先对对象进行判空,再进行后续操作,有时候使用if直接判空更好。

 

新增的函数式接口、lambda表达式
函数式接口

jdk8新增了函数式接口,以支持函数式编程

@FunctionalInterface  //标识为函数式接口
public interface MyStringUtils {

    String join(String str1, String str2);

}

函数式接口中的抽象方法有且只能有1个,可以有静态方法、默认实现的方法,但抽象方法必须有、且只能有1个。

 

lambda表达式

lambda表达式可以实现只有一个抽象方法的接口,不局限于函数式接口,只要接口中只有一个抽象方法,就可以使用lambda表达式实现。

//原来的写法:使用匿名内部类
MyStringUtils myStringUtils1 = new MyStringUtils() {
    @Override
    public String join(String str1, String str2) {
        return str1 + str2;
    }
};


//使用lambda表达式。lambda表达式实质是使用匿名内部类,语法糖,代码更加简洁
MyStringUtils myStringUtils2 = (str1, str2) -> str1 + str2;


//相比于匿名内部类,lambda表达式除了简洁之外,还可以直接访问外部变量,但只能访问、不能修改
String username = "chy";
userlist.forEach(user -> {
	//可以直接使用外部变量username
    if (username.equals(user.getUsername())) {
        System.out.println(user);
    }
});
  • 只有一个参数时,可以省略()
  • 方法体中只有一条语句时,可以省略{ }
  • 方法体中只有一条语句,且方法有返回值时,可以缺省return关键字

 

jdk8内置的函数式接口

jdk、各种框架都内置了多个函数式接口,jdk8常见的内置函数式接口如下

  • Consumer:void accept(T t); 消费型接口,有入参、无返回值,用于处理传入的参数。
  • Supplier:T get(); 供给型接口,无入参、有返回值,用于提供某个类型的实例。
  • Function<T, R>:R apply(T t); 函数型接口,有入参、有返回值,用于处理传入的数据,并返回结果。
  • BiFunction:R apply(T t, U u); Function只能接收⼀个参数,BiFunction可以接收2个参数
  • Predicate: boolean test(T t); 断⾔型接口,有入参、有返回值,返回值为布尔类型,用于校验传入的数据。

 

示例

public class Test {

    /**
     * 过滤list中的元素,返回满足条件的元素
     */
    public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
        List<T> resultList = new ArrayList<>();
        for (T ele : list) {
            //调用predicate的test()进行判断
            if (predicate.test(ele)) {
                resultList.add(ele);
            }
        }
        return resultList;
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu");
        //条件:字符串长度>5
        List<String> resultList = filterList(list, ele -> ele.length() > 5);
        System.out.println("满足条件的元素:" + resultList);
    }

}

 

新增的方法引用方式::

在lambda表达式中,可以使用双冒号引用方法

User user = new User();
Optional<User> optional = Optional.ofNullable(user);

//原来的调用方式:u.getHeadImg()
String headImg1 = optional.map(u->u.getHeadImg()).orElse("/xxx/defaultHeadImg.png");
//新的调用方式:User::getHeadImg
String headImg2 = optional.map(User::getHeadImg).orElse("/xxx/defaultHeadImg.png");

这种方法引用方式只能在lambda表达式中使用

 

新增的集合操作方式Stream

参考:https://blog.csdn.net/chy_18883701161/article/details/124169038

 

新的内存空间Matespace

jdk、jvm都只是一个标准、规范,有多种实现,此处以主流的jvm实现Hotspot为准。

jdk7用永久代(permanent generation)来此存储类的元数据,永久代使用的是jvm的内存空间,可能发生OOM。

jdk8使用元空间(Metaspace)代替永久代,元空间使用本地内存,受制于机器物理内存的大小,基本不会发生OOM。

 

jdk9

接口新增的私有方法
public interface Animal {

    private void m1() {
        System.out.println("私有方法");
    }

    default void m2() {
        System.out.println("默认方法");
        m1();
    }

    static void m3() {
        System.out.println("静态方法");
    }

}

私有方法使用private修饰,自然不会被继承,只能在当前接口中调用,且必须提供方法体(实现),不然没有意义。
 

说明

  • 接口中的私有方法、默认方法、静态方法都必须提供方法体(实现)。
  • 私有方法不能被继承,默认方法可以被继承,接口中的静态方法不能被继承,但类中的静态方法可以被继承。

 

增强的try-with-resource
OutputStream os1 = new FileOutputStream("C:/Users/chy/Desktop/1.txt");
OutputStream os2 = new FileOutputStream("C:/Users/chy/Desktop/2.txt");

//可以将资源声明放在try()外面,()中指定资源变量即可,有多个时分号隔开 try (os1; os2)
try (os1) {
    os1.write("hello".getBytes());
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

 

新增的创建只读集合的api

之前创建只读集合的方式

List<String> list = new ArrayList<>();
list.add("chy");
//设置为只读集合。原集合不变,返回一个只读集合
list = Collections.unmodifiableList(list);


Set<String> set = new HashSet<>();
set.add("chy");
//设置为只读集合
set = Collections.unmodifiableSet(set);


Map<String, String> map = new HashMap<>();
map.put("name", "chy");
//设置为只读集合
map = Collections.unmodifiableMap(map);


//Arrays.asList()创建的也是只读集合
List<String> list = Arrays.asList("chy1", "chy2");

 

jdk9新增的方式

List<String> list = List.of("chy1", "chy2");

Set<String> set = Set.of("chy1", "chy2");

Map<String, Object> map = Map.of("name", "chy", "age", 20);

 

模块化

jdk9将把jdk、jre、jar等分成较小的模块,使用时无需全部引入,只引入所需的模块即可,模块化降低了代码耦合度、大大缩小了应用体积。

 

jdk10

新增的创建只读集合的api
ArrayList<String> list1 = new ArrayList<>();
list1.add("chy");

//jdk10的List、Set、Map都提供了静态方法copyOf()创建对应的只读集合,原集合不变,以新集合的方式返回
List<String> list2 = List.copyOf(list1);

jdk9、10的这2个api实质都是调用 ImmutableCollections 类的方法创建只读集合。immutable 只读的、不可变更的。

 

新增的局部变量类型推断var
//声明时可以声明为var类型,声明时就必须初始化,初始化时不能赋null,会自动根据赋的值推断变量类型
var a = 1;
var b = 2;
System.out.println(a + b);

var只能用于声明局部变量,不能用于声明方法形参,不能作为方法的返回值类型。

 

jdk11

新增的http客户端

基本使用

//要请求的uri
URI uri = URI.create("http://www.baidu.com");


//创建httpClient。可以HttpClient.newHttpClient()直接创建HttpClient对象,但连接最好都设置超时时间
HttpClient httpClient = HttpClient.newBuilder()
        .connectTimeout(Duration.ofMillis(5000))
        .build();


//创建请求。get请求方式
HttpRequest request = HttpRequest.newBuilder()
        .timeout(Duration.ofMillis(5000))
        .uri(uri)
        // 默认就是get方式,可省略
        // .GET()
        // 配置项
        .header("key1", "value1")
        .header("key2", "value2")
        .build();


//执行请求
try {
    HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
    //使用响应中的数据
    System.out.println(response.body());
} catch (IOException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}

 

发送post方式的请求

//post请求方式传递表单数据
String formData = "username=chy&password=1234";
HttpRequest request1 = HttpRequest.newBuilder()
        .uri(uri)
        .header("Content-Type", "application/x-www-formurlencoded")
        .POST(HttpRequest.BodyPublishers.ofString(formData))
        .build();


//post方式传递json数据
String jsonData = "{\"username\":\"chy\",\"password\":\"abcd\"}";
HttpRequest request2 = HttpRequest.newBuilder()
        .uri(uri)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(jsonData))
        .build();

 

发送异步请求

// 同步请求
// HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// System.out.println(response.body());


// 异步请求
CompletableFuture<String> completableFuture = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(response -> response.body());

//要执行的其它代码
//......

//使用响应。completableFuture.get()会阻塞当前线程,直到请求完成返回了响应,或者超时
String resposeBody = completableFuture.get();
System.out.println(resposeBody);

 

发送http2请求

HttpRequest request = HttpRequest.newBuilder()
        .timeout(Duration.ofMillis(5000))
        .uri(uri)
        //指定httpClient使用的http协议版本
        .version(HttpClient.Version.HTTP_2)
        .header("key1", "value1")
        .header("key2", "value2")
        .build();

目前http协议的主流版本是http1.1,http2是下一代版本。http2强制使用https,需要服务器支持http2。

 

javac、java命令的优化

以前编译执行:先javac编译,再java执行,本地会生成.class文件。

jdk11:无需javac编译,直接java执行即可,本地不会生成.class文件。

 

jdk13

jdk13新增了文本块、增强了switch表达式,但这2个特性在jdk13中都是预览状态、没有正式发布。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值