- JEP
- JDK Enhancement Proposals :jdk 改进提案,每当需要有新的设想的时候,JEP 可以在 JCP(Java Community Process)之前或者同时提出非正式的规范(specification),被正式认可的 JEP 正式写进 JDK 的发展路线图并分配版本号
- JSR
- Java Specification Requests :Java 规范提案。新特性的规范出现在这一阶段,是指向 JCP(Java Community Process)提出新增一个 标准化技术规范的正式请求。请求可以来自于小组/项目 、JEP、JCP 成员或者 Java 社区成员的提案,每个 Java 版本都由相应的 JSR 支持。
1)、Java9 之前的目录结构
2)、Java9 之后的目录结构
说明:
在 JDK9 中
- 删除了 JRE 的子目录
bin
目录包含所有的命令conf
目录包含用户可编辑的配置文件include
目录包含编译本地代码时使用的 C/C++头文件jmods
目录包含 JMOD 格式的平台模块legal
目录包含法律声明lib
目录包含一些动态链接库
1)、模块化系统产生的背景
Java 已经发展超过 20 年(95 年发布),Java 和相关生态在不断丰富的同时也暴露出了一些问题。
- Java 运行环境的膨胀和臃肿。每次 JVM 启动的时候,至少会有 30~60M 的内存加载。主要原因是 JVM 需要加载
rt.jar
,不管其中的类是否被 classloader 加载,第一步整个 jar 都会被 JVM 加载到内存中去,而模块化可以根据模块的需要加载程序运行需要的 class 。- 很难真正的对代码进行封装,而系统并没有对不同部分(也就是 jar 文件)之间的依赖关系有个明确的概念。每一个公共的类都可以被类路径之下任何其他的公共类访问到,这样就会导致无意中使用了并不想被公开访问的 API 。
- 本质上说,模块(module)的概念,其实就是 package 外再包裹一层,也就是说,用模块来管理各个 package ,通过声明某个 package 暴露,不声明默认就是隐藏。因此模块化是的代码组织上更安全,因为它可以指定哪些部分暴露,哪些部分隐藏。
2)、代码说明
① 创建一个 java9demo
的工程,然后在这个工程下面创建 2 个 module。一个 java9A
,一个 java9B
。
② 在 java9A
中创建一个实体类 Person,然后想在 java9B
中引用它,正常情况下是引用不到的,因为是存在两个模块中。
③ 我们在 java9A
模块的 src
目录下新建 module-info.java
文件
module java9A {
// 这里 exports 后面跟具体的包名
exports com.hepingfly.bean;
}
④ 我们在 java9B
模块的 src
目录下新建 module-info.java
文件
module java9demo {
// requires 后面跟具体要引入的 module 的名字
requires java9A;
}
⑤ 这样在 java9B
模块中就能引用 java9A
模块暴露出来的实体类了。
public class ModuleTest {
// 我在 java9B 模块中想引用 java9A 模块中的 Person 类
public static void main(String[] args) {
Person person = new Person();
person.setName("hepingfly");
System.out.println(person.getName()); // 打印 hepingfly
}
}
1)、产生的背景
像 Python 和 Scala 之类的语言早就有交互式编程环境 REPL(read-evaluate-print-loop)了,以交互的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获得对程序的反馈。而之前的 Java 版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。
2)、jshell 简单介绍
在 Java9 中终于拥有了 REPL 工具:jShell 。利用 jshell 可以在没有创建类的情况下直接声明变量,计算表达式,执行语句。即开发时可以在命令行里直接运行 Java 代码,无需创建 Java 文件。
3)、基本使用
① JDK9 以上的版本如果你配置好了环境变量直接在命令行输入 jshell
即可进入交互界面(原因是 JDK
的 bin
目录下有 jshell
这个工具)
② 输入命令获得帮助信息
③ 定义变量,计算变量值
④ 定义方法,调用方法
⑤ 导入指定的包
⑥ 列出当前 session 内所有有效的代码片段
⑦ 查看当前 session 下所有创建过的变量
⑧ 查看当前 session 下所有创建过的方法
⑨ 从外部文件加载源代码
源代码:
// hello.java 文件中的内容
public void hello() {
System.out.println("helllo hepingfly");
}
hello();
⑩ 退出 jshell
注:
jshell
可以使用 tab 键进行自动补全- jshell 可以从文件中加载语句或者将语句保存到文件中
- 在 jshell 中定义相同方法名和参数列表的方法,即为对现有方法的覆盖。
- 在 jshell 中你会发现不会出现编译时异常,因为 jshell 帮你隐藏了。
在 Java9 中,接口更加灵活和强大,方法的访问权限修饰符可以声明为 private 的了,此时方法将不会成为你对外暴露的 API 的一部分。
关于接口中可以声明的内容,不同版本之间的对比:
① JDK7:
JDK7: 1)、只能声明全局常量(public static final) 2)、抽象方法(public abstract)
② JDK8:
JDK8: 1)、可以声明静态方法 2)、可以声明默认方法 静态方法和默认方法在接口中都是有方法体的, 也就意味着我们可以通过 接口名去调用这个静态方法,或者通过这个接口的实现类对象来调用默认方法
③ JDK9:
JDK9: 1)、支持声明私有方法
/**
* java9 接口
* @author hepingfly
* @date 2020/10/24 2:31 下午
*/
public interface Java9Interface {
/**
* JDK7:
* 1)、只能声明全局常量(public static final)
* 2)、抽象方法(public abstract)
* @author hepingfloy
* @date 2020/10/24 2:33 下午
*/
void hello();
/**
* JDK8:
* 1)、可以声明静态方法
* 2)、可以声明默认方法
* 静态方法和默认方法在接口中都是有方法体的, 也就意味着我们可以通过
* 接口名去调用这个静态方法,或者通过这个接口的实现类对象来调用默认方法
* @author hepingfly
* @date 2020/10/24 2:36 下午
*/
static void hello2() {
System.out.println("hepingfly");
}
default void hello3() {
System.out.println("hepingfly");
}
/**
* JDK9:
* 1)、支持声明私有方法
* @author hepingfly
* @date 2020/10/24 2:53 下午
*/
private void hello4() {
System.out.println("hepingfly");
}
}
可以与匿名实现类共同使用钻石操作符。
public class DiamondOperator {
public static void main(String[] args) {
// java8 中这么写会报错,java9 可以
Set<String> set = new HashSet<>(){};
}
}
在 Java9 中可以在 try 子句中使用已经初始化过的资源。
public class Java9Try {
/**
* Java7 中关于异常,我们需要手动去处理,去关闭
* @author hepingfly
* @date 2020/10/24 4:09 下午
*/
public void testJava7() {
InputStream is = null;
try {
is = new FileInputStream(new File("a.txt"));
System.out.println("hepingfly");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void testJava8() {
/**
* 在 try 后面的小括号中声明的一些资源,它会自动的关闭,就不需要你手动的去关了
* 在 Java8 中要求资源对象的实例化,必须放在 try 后面的小括号内
*/
try (InputStream is = new FileInputStream(new File("a.txt"))) {
System.out.println("hepingfly");
} catch (Exception e) {
e.printStackTrace();
}
}
public void testJava9() {
/**
* 可以在 try 后面的小括号中调用已经实例化好的资源对象
* 多个资源对象以分号分隔
*/
InputStreamReader is = new InputStreamReader(System.in);
OutputStreamWriter os = new OutputStreamWriter(System.out);
try (is;os) {
System.out.println(is.read());
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Java9Underline {
public static void main(String[] args) {
// _str 可以编译通过
String _str = "hello";
// 单独一个 _ 作为变量名,无法编译通过
String _ = "hello";
}
}
JDK8 之前,底层使用 char[] 存储,JDK9 底层使用 byte[] 存储。
1)、Java8 创建只读集合方式
public static void testJava8() {
// 创建一个只读的 list
List<String> list = Collections.unmodifiableList(Arrays.asList("heping","fly"));
list.forEach(System.out::println);
// 创建一个只读的 set
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("heping","fly")));
set.forEach(System.out::println);
// 创建一个只读的 map
Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {
{
put("name", "hepingfly");
put("age", "18");
}
});
map.forEach((x,y) -> System.out.println(x + ":" + y));
}
2)、Java9 创建只读集合方式
public static void testJava9() {
// 创建一个只读的 list
List<String> list = List.of("heping", "fly");
// 创建一个只读的 set
Set<String> set = Set.of("heping", "fly");
// 创建一个只读的 map
Map<String, String> map = Map.of("name", "heping", "age", "18");
Map<String, String> map2 = Map.ofEntries(Map.entry("name", "heping"), Map.entry("age", "12"));
}
在 Java9 中,Stream API 变的更好,Stream 接口中添加了 4 个新的方法,dropWhile
、 takeWhile
、ofNullable
、 还有个 iterator 方法的新重载方法。
除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个(可能是空的)Stream 对象。
① takeWhile
用于从 Stream 中获取一部分数据,接收一个 predicate 来进行选择。在有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素。
/**
* takeWhile 方法
*/
public static void test1() {
List<Integer> list = Arrays.asList(13,11,45,18,20,16,96);
Stream<Integer> stream = list.stream();
/**
* 下面这行代码的意思就是:
* 当满足 x < 20 这个条件的时候我就 take(取)
* ① 13 小于 20 满足,我进行 take 然后输出
* ② 11 小于 20 满足,我进行 take 然后输出
* ③ 45 不小于 20 ,那么就停止,即使后面有小于 20 的数,我到这也停止了,后面的就不输出了
*/
stream.takeWhile((x) -> x < 20).forEach(System.out::println);
}
② dropWhile
dropWhile 的行为与 takeWhile 相反,返回剩余的元素。
public static void test2() {
List<Integer> list = Arrays.asList(13,11,45,18,20,16,96);
Stream<Integer> stream = list.stream();
/**
* 下面这行代码的意思就是:
* 当满足 x < 20 这个条件的时候我就 drop(丢)
* ① 13 小于 20 满足,我进行 drop 丢掉
* ② 11 小于 20 满足,我进行 drop 丢掉
* ③ 45 不小于 20 ,那么就停止,后面所有的数我都不进行 drop 了,即使有小于 20 的数
*/
stream.dropWhile((x) -> x < 20).forEach(System.out::println);
}
③ ofNullable 的使用
Java8 中 Stream 不能完全为 Null ,否则会报空指针异常。而 Java9 中的 ofNullable 方法允许我们创建一个单元素 Stream ,可以包含一个非空元素,也可以创建一个空 Stream 。
public static void test3() {
Stream<Integer> stream = Stream.of(1, 2, 3, null);
stream.forEach(System.out::println);
// 如果只有单个元素,此元素不能为 null,否则报空指针异常
Stream<Object> stream1 = Stream.of(null);
// Java9 新增 ofNullable 允许单个元素为空
Stream<Object> stream2 = Stream.ofNullable(null);
System.out.println(stream2.count()); // 0
}
④ iterate 函数重载
public static void test4() {
// 需要通过 limit 来控制终止
Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::println);
// 这里就可以通过传入一个断言型函数来进行终止
Stream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::println);
}
public static void test1() {
List<String> list = Arrays.asList("a","b","c");
Optional<List<String>> optional = Optional.ofNullable(list);
// optional 类中提供了转换为 stream 的方法
Stream<List<String>> stream = optional.stream();
stream.forEach(System.out::println);
}