目录
9.@Deprecated废弃注解追加since和forRemoval属性
自从 2017 年 9 月 21 日 Java 9 正式发布之时,Oracle 就宣布今后会按照 每六个月一次的节奏进行更新。
一、Java9
Java的更新从传统的以特性驱动的发布周期,转变为以时间驱动的 (6 个月为周期)发布模式.
针对企业客户的需求,Oracle 将以三年为周期发布长期支持版本(long term support)。
1.JDK 和 JRE 目录结构的改变
2.模块化系统
众所周知,Java 已经发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越来越暴露出一些问题:
①Java 运行环境的膨胀和臃肿。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第 一步整个jar都会被JVM加载到内存当中去。
②当代码库越来越大,创建复杂。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。
③很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
java9中将rt.jar分成了不同的模块,一个模块下可以包含多个包,模块之间存在着依赖关系,其中java.base模块是基础模块,不依赖其他模块。让jvm加载程序的必要模块,并非全部模块,达到了瘦身的效果。
模块(module)的概念,其实就是package外再裹一层,不声明默认就是隐藏。因此,模块化使得代码组织上更安全,因为它可以指定哪些部分可以暴露,哪些部分隐藏。
模块将由通常的类和新的模块声明文件(module-info.java)组成。该文件是位于java代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系, 以及哪些模块被外部使用。在exports子句中未提及的所有包默认情况下将封装在模块中,不能在外部使用。
jar包中含有.class文件,配置文件。jmod除了包含这两个文件之外,还有native library,legal licenses等,两者主要的区别是jmod主要用在编译期和链接期,并非运行期,因此对于很多开发者来说,在运行期仍然需要使用jar包。
模块化的优点:
1、精简jvm加载的class类,提升加载速度
2、对包更精细的控制,提高安全
模块与包类似,只不过一个模块下可以包含多个包。
例:
项目-----公司
模块-----部门
包名-----小组
类-------员工
演示:
1、创建项目,项目下分别创建2个模块test9和demo1
2、在模块test9下
创建com.test.bean Cat
package com.test.bean;
public class Cat {
public void eat(){
System.out.println("吃鱼");
}
}
创建com.test.bean1 Dog
package com.test.bean1;
public class Dog {
}
在src下创建module-info.java。
module test9 {//test9是本模块名
exports com.test.bean;//导出包
opens com.test.bean1;//导出包,只能通过反射的方式访问
}
3、在模块demo1下
创建com.demo Test
package com.demo;
import com.test.bean.Cat;
public class Test {
public static void main(String[] args) throws Exception {
Cat cat=new Cat();//访问不到Cat,需要配置module-info.java文件
cat.eat();
Class<?> clazz=Class.forName("com.test.bean1.Dog");
Object obj = clazz.getDeclaredConstructor().newInstance();
}
}
在src下创建module-info.java。
module demo1 {//demo1 是本模块名
requires test9;//引入test9模块requires:指明对其它模块的依赖。
}
3.Java的REPL工具: jShell命令
像Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print - loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码, 就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。
Java 9 中终于拥有了 REPL工具:jShell。
安装JDK9以上版本。
调出jShell:cmd中输入:jshell 回车
C:\Users\Administrator>jshell
| 欢迎使用 JShell -- 版本 21.0.1
| 要大致了解该版本, 请键入: /help intro
jshell> System.out.println("Hello World!")
Hello World!
jshell> int a=10;
a ==> 10
jshell> int b=20;
b ==> 20
jshell> System.out.println(a+b)
30
jshell>
获得帮助:/help intro
导入指定的包:import java.util.*;
在 JShell 环境下,语句末尾的“;” 是可选的。但推荐还是最好加上。提高代码可读性。
只需按下 Tab 键,就能自动补全代码
列出当前 session 里所有有效的代码片段: /list
查看当前 session 下所有创建过的变量:/vars
查看当前 session 下所有创建过的方法:/methods
退出jShell:/exit
4.语法改进:接口的私有方法
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。
public interface MyInterface {
void eat();
default void fly(){
sleep();
}
static void run(){
System.out.println("run");
}
private void sleep(){
System.out.println("sleep");
}
}
5.语法改进:try语句
Java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过。如下例所示:
try(InputStreamReader reader = new InputStreamReader(System.in)){
//读取数据细节省略
}catch (IOException e){
e.printStackTrace();
}
Java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的:
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
try (reader; writer) {
//reader是final的,不可再被赋值
//reader = null;
//具体读写操作省略
} catch (IOException e) {
e.printStackTrace();
}
6.String字符串的变化
6.1、String存储结构变更
以前的版本中String内部使用了char数组存储,对于使用英文的人来说,一个字符用一个字节就能存储,使用char存储字符会浪费一半的空间,因此在jdk9中将String内部的char数组改成了byte数组加上编码标记,节约了一半的内存空间(纯英文节约一半内存空间)。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
/**
* The identifier of the encoding used to encode the bytes in
* {@code value}. The supported values in this implementation are
*
* LATIN1
* UTF16
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*/
private final byte coder;
...
static final boolean COMPACT_STRINGS;
static {
COMPACT_STRINGS = true;
}
...
@Native static final byte LATIN1 = 0;
@Native static final byte UTF16 = 1;
}
String中增加了下面2个成员变量:
1、COMPACT_STRINGS:判断是否压缩,默认是true,若为false,则不压缩,使用UTF16编码
2、coder用来区分使用的字符编码,分别为LATIN1(值为0)和UTF16(值为1)
char c= 'a';//2个字节
byte b=97;//1个字节
byte数组如何存储中文呢?通过源码(StringUTF16类中的toBytes方法)得知,在使用中文字符串时,1个中文会被存储到byte数组中的两个元素上,即存储1个中文,byte数组长度为2,存储2个中文,byte数组长度为4.
例:
String str="好";
好,对应的Unicode码二进制为0101100101111101,分别取出高8位和低8位,放入byte数组中{01011001,01111101}这样就利用byte数组的2个元素保存了1个中文。
当字符串中存储了中英混合的内容时,1个英文字符会占用2个byte的数组位置,例如下面代码底层byte数组的长度为8。
String str="好hao";
在获取字符串长度时,若存储的内容存在中文,是不能直接获取byte数组的长度作为字符串长度的,String源码中有向右移动1位的操作(即除以2),这样才能获取正确的字符串长度。
打断点//F5
public class Test {
public static void main(String[] args) {
char[] name = {'好', '如'};
String str = new String(name);//F5 先进入new String()//F5
System.out.println(str);
}
}
6.2、String声明时,不允许使用_来命名变量名
String _="";//java9之后不允许使用_来命名变量名
7.集合工厂方法:快速创建只读集合
List<String> namesList = new ArrayList <>();
namesList.add("Joe");
namesList.add("Bob");
namesList.add("Bill");
namesList = Collections.unmodifiableList(namesList);
System.out.println(namesList);
//缺点:我们一下写了五行。即:它不能表达为单个表达式。
List firsnamesList = List.of(“Joe”,”Bob”,”Bill”);
调用集合中静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能可用于Set和List,也可用于Map的类似形式。此时得到的集合,是不可变的:在创建后,继续添加元素到这些集合会导致 “UnsupportedOperationException” 。 由于Java 8中接口方法的实现,可以直接在List,Set和Map的接口内定义这些方法, 便于调用。
8.InputStream 加强
InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下 示例。
ClassLoader cl = this.getClass().getClassLoader();
try (InputStream is = cl.getResourceAsStream("hello.txt");
OutputStream os = new FileOutputStream("src\\hello1.txt")) {
is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
} catch (IOException e) {
e.printStackTrace();
}
9.@Deprecated废弃注解追加since和forRemoval属性
since:标识是从哪个版本被废弃了
forRemoval:=true :标识该废弃的内容会在未来的某个版本中移除
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
/**
* Returns the version in which the annotated element became deprecated.
* The version string is in the same format and namespace as the value of
* the {@code @since} javadoc tag. The default value is the empty
* string.
*
* @return the version string
* @since 9
*/
String since() default "";
/**
* Indicates whether the annotated element is subject to removal in a
* future version. The default value is {@code false}.
*
* @return whether the element is subject to removal
* @since 9
*/
boolean forRemoval() default false;
}
例:since
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement,
TypeDescriptor.OfField<Class<?>>,
Constable {
....
@CallerSensitive
@Deprecated(since="9")
public T newInstance()
throws InstantiationException, IllegalAccessException
{
...
例:forRemoval
public class Object {
...
@Deprecated(since="9", forRemoval=true)
protected void finalize() throws Throwable { }
一个程序员最重要的能力是:写出高质量的代码!!
有道无术,术尚可求也,有术无道,止于术。
无论你是年轻还是年长,所有程序员都需要记住:时刻努力学习新技术,否则就会被时代抛弃