Jdk8 新特性
Jdk8 的新特性:方法引用、Lambda 表达式、Stream 流...
# 1、Lambda
表达式
概述:
Lambda
表达式是函数式编程思想的体现函数式编程思想是尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
是匿名内部类的一种优化写法
可以根据上下文代码环境==“可推导可省略”==
格式:
(形式参数) -> {代码块} //组成Lambda表达式的三要素
1- 形式参数:可以有多个形式参数,之间用
,
隔开,如果没有参数,留空即可 ->
:由英文中划线和大于符号组成,固定写法,表示指向动作- 代码块:使我们具体要做的事情,也就是以前写的方法体
- 形式参数:可以有多个形式参数,之间用
==使用前提==:
- 有一个接口
- 接口中有且只有一个抽象方法
- 要有上下文环境
省略规则:
- 参数类型可以省略,但在有多个参数时,要省略参数类型需全部省略
- 如果参数有且仅有一个,小括号
()
可以省略 - 如果代码块的语句只有一条,可以省略大括号
{}
和语句体后面的分号;
,若此时的语句体中有return
,则return
也必须省略掉
注意事项:
使用
Lambda
表达式要满足其使用前提必须有上下文环境,才能使用,即能够通过代码推导出对应的接口
根据局部变量的赋值得知Lambda对应的接口
Runnable r = () -> System.out.println("Lambda表达式");
1根据调用方法的参数得知Lambda对应的接口
new Thread(() -> System.out.println("Lambda表达式")).start();
1
与匿名内部类的区别:
所需类型不同
- 匿名内部类:可以是接口、抽象类、具体类
Lambda
表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,此时可以使用
Lambda
表达式,也可以使用匿名内部类 - 如果接口中多于一个抽象方法,则只能使用匿名内部类
- 如果接口中有且仅有一个抽象方法,此时可以使用
实现原理不同
匿名内部类:编译之后会产生一个单独的
.class
字节码文件Lambda
表达式:编译之后不会产生单独的.class
字节码文件,对应的字节码会在运行的时候动态生成。由于Lambda的这一编译特性,会提高代码的运行效率及减少资源占用。
因为字节码文件在运行的时候需要加载到内存导致。
# 2、方法引用
概述:
- 在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作,如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,没有必要再写重复逻辑,可以通过方法引用来使用已经存在的方案。
方法引用符:
:: //该符号为引用运算符,而它所在的表达式被称为方法引用
1- 推导与省略:
- 如果使用
Lambda
,那么根据==可推导就是可省略==的原则,无需指定参数类型,也无需指定重载形式,他们会被自动推导 - 如果使用方法引用一样是可以根据上下文进行推导
- 方法引用是
Lambda
的孪生兄弟。可以理解为是Lambda
表达式的优化写法
- 如果使用
- 推导与省略:
使用方式:
引用类方法,即引用类的静态方法
格式: 类名::静态方法 //使用说明:Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数 例: Integer::parseInt
1
2
3
4
5引用对象的实例方法,即引用类中的成员方法(普通引用)
格式: 对象名::成员方法 //被引用方法的参数和需要的方法的参数一模一样,则这样引用 //使用说明:lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数 例: "HelloWorld"::toUpperCase
1
2
3
4
5引用类的实例方法,即引用类中的成员方法(特殊引用)
格式: 类名::方法名 //第一个参数调用"被引用方法",其他参数作为"被引用方法"的实际参数 //使用说明:Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数 例: String::substring ----------- public interface MyString { String mySubString(String s,int x,int y); } public class MyStringDemo { public static void main(String[] args) { //useMyString((String s,int x,int y) -> { // return s.substring(x,y); //}); //Lambda简化写法 useMyString((s,x,y) -> s.substring(x,y)); //引用类的实例方法 //第一个参数调用"被引用方法",其他参数作为"被引用方法"的实际参数 //相当于 s.substring(x,y); useMyString(String::substring); } private static void useMyString(MyString my) { String s = my.mySubString("HelloWorld", 2, 5); System.out.println(s); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32引用构造器,即引用构造方法
格式: 类名::new //使用说明:Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数 例: student::new
1
2
3
4
5
# 3、接口新特性
接口中的内容:
- JDK 8 以前:
- 常量:public static final
- 抽象方法:public abstract
- JDK 8 新增特性:
- 默认方法
- 静态方法
- JDK 9 新增特性:
- 私有方法
- JDK 8 以前:
默认方法:
格式: public default 返回值类型 方法名(参数列表) { }
1
2- 注意事项:
- 默认方法不是抽象方法,不强制被重写,但是可以被重写,重写的时候去掉
default
关键字 public
可以省略,default
不能省略
- 默认方法不是抽象方法,不强制被重写,但是可以被重写,重写的时候去掉
- 意义:
- 接口升级的时候,可以不强制所有的子类重写
- 可以抽取所有子类的共性内容
- 注意事项:
静态方法:
格式: public static 返回值类型 方法名(参数列表) { }
1
2- 注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public
可以省略static
不能省略
- 意义:
- 可以在不创建子类的情况下直接调用该接口中的静态方法
- 注意事项:
私有方法:
格式一:非静态私有方法 private 返回值类型 方法名(参数列表) { } 格式二:静态私有方法 private static 返回值类型 方法名(参数列表) { }
1
2
3
4
5- 注意事项:
- 默认方法可以调用静态方法和非静态方法
- 静态方法只能调用静态方法
- 意义:
- 静态私有方法可以对静态方法进行共性抽取,提高代码复用性
- 非静态私有方法可以对默认方法进行共性抽取,提高代码复用性
- 注意事项:
# 函数式接口&Stream
流
# 1、函数式接口
概述:
- 有且仅有一个抽象方法的接口即函数式接口
- 可以作为形参或返回值
检测方式
@FunctionalInterface //放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败, //此注解可以不添加,只要满足函数式接口的条件,该接口就是函数式接口,建议加上
1
2
3函数式接口-
Supplier<T>
(生产型接口)==无参有返回值==概述:
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
常用方法
T get() //按照某种实现逻辑(由Lambda表达式实现)返回一个数据
1
函数式接口-
Consumer<T>
(消费型接口)==有参无返回值==概述:
- Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
常用方法
void accept(T t) //对给定的参数进行操作 //返回一个组合的Consumer,依次执行此操作,然后执行after操作 default Consumer<T> andThen(Consumer after)
1
2
3
4
函数式接口-
Predicate<T>
==有参有返回值,返回值为boolean
类型==概述:
- Predicate接口通常用于判断参数是否满足指定的条件
常用方法:
//对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值 boolean test(T t) //返回一个逻辑的否定,对用逻辑非 default Predicate<T> negate() //返回一个组合判断,对用短路与 default Predicate<T> and(Predicate other) //返回一个组合判断,对应短路或 default Predicate<T> or(Predicate other)
1
2
3
4
5
6
7
8
9
10
11
函数式接口-
Function<T,R>
==有参有返回值==概述:
- Function接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
常用方法:
//将此函数应用于给定的参数 R apply(T t) //返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果 default <V> Function andThen(Function after)
1
2
3
4
5
# 2、Stream
流
概述:
- 专门用来快速操作集合和数组的一个工具
- 思想:生成流 --> 操作流 --> 终止流
生成
Stream
的方式//------------Collection 体系集合 List 和 Set default Stream<E> stream() //使用默认方法stream()生成流 例: list.stream() set.stream() //-----------Map体系集合 //把Map集合转成Set集合,间接生成流 例: map.keySet().stream(); map.values().stream(); map.entrySet().stream(); //-----------数组 //通过Stream接口的静态方法of(T...values)生成流 例: int[] arr = {10, 20, 30}; Stream<Integer> strArrayStream = Stream.of(arr); Stream<Integer> intStream = Stream.of(10, 20, 30);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Stream
流的中间操作概念:
- 中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作。
常见方法:
//用于对流中的数据进行过滤 Stream<T> filter(Predicate predicate) //返回此流中的元素组成的流,截取前指定参数个数的数据 Stream<T> limit(long maxSize) //跳过指定参数个数的数据,返回由该流的剩余元素组成的流 Stream<T> skip(long n) //合并a和b两个流为一个流Stream<T> distinct()返回由该流的不同元素(根据Object.equals(Object) )组成的流 static <T> Stream<T> concat(Stream a, Stream b) //返回由此流的元素组成的流,根据自然顺序排序 Stream<T> sorted() //返回由该流的元素组成的流,根据提供的Comparator进行排序 Stream<T> sorted(Comparator comparator) //返回由给定函数应用于此流的元素的结果组成的流 <R> Stream<R> map(Function mapper) //返回一个IntStream其中包含将给定函数应用于此流的元素的结果 IntStream mapToInt(ToIntFunction mapper)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Stream
流的终结操作概述:
- 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作。
常用方法:
void forEach(Consumer action) //对此流的每个元素执行操作 long count() //返回此流中的元素数
1
2
Stream
流的收集操作概述:
- 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。
常用方法:
R collect(Collector collector)把结果收集到集合中
1工具类Collectors提供了具体的收集方式
//把元素收集到List集合中 public static <T> Collector toList() //把元素收集到Set集合中 public static <T> Collector toSet() //把元素收集到Map集合中 public static Collector toMap(Function keyMapper,Function valueMapper)
1
2
3
4
5
6
7
8
# 反射&模块化&类加载器
# ==反射==
概述:
反射是指在程序运行时去获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制。
这种动态性可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期仍然可以扩展。
大白话解释:
所谓的反射,指的是程序在运行过程中,获取“类的字节码”文件,即Class对象,再从“类的字节码”文件汇总获取类的“成员变量”、“成员方法”、“构造方法”并使用,这种现象就叫做反射。
获取
Class
类对象的三种方式:- 类名
.class
属性- 说明:使用类的
class
属性来获取该类对应的Class
对象。
- 说明:使用类的
- 对象名
.getClass()
方法- 说明:调用对象的
getClass()
方法,返回该对象所属类对应的Class
对象 - 该方法是
Object
类中的方法,所有的Java
对象都可以调用该方法
- 说明:调用对象的
Class.forName
(全类名)方法- 说明:使用
Class
类中的静态方法forName(String className)
- 该方法需要传入字符串参数,该字符串参数的值得某个类的全路径,也就是完整包名的路径
- 说明:使用
- 类名
反射获取构造方法的四种方式
//返回所有公共构造方法对象的数组 //返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数,仅包含 public 修饰的构造方法 Constructor<?>[] getConstructors() //返回所有构造方法对象的数组 //返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组,包括私有(private)和默认(default)的构造方法 Constructor<?>[] getDeclaredConstructors() //返回单个公共构造方法对象 //返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数 //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 Constructor<T> getConstructor(Class<?>... parameterTypes) //返回单个构造方法对象 //返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数 //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Constructor
类用于创建对象的方法//根据指定的构造方法创建对象 //使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例 T newInstance(Object...initargs) //对于私有或者默认的构造方法,可以使用暴力反射抑制java语法检查使用 public void setAccessible(boolean flag) 值为true,取消访问检查
1
2
3
4
5
6
反射获取成员变量对象的四种方式:
//返回所有公共成员变量对象的数组 //返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段 Field[] getFields() //返回所有成员变量对象的数组 //返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段,包括私有和默认字段 Field[] getDeclaredFields() //返回单个公共成员变量对象 //返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段 Field getField(String name) //返回单个成员变量对象 //返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段 Field getDeclaredField(String name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Field
类给成员变量赋值和获取成员变量值得方法//给obj对象的成员变量赋值为value //将指定的对象参数中由此 Field对象表示的字段设置为指定的新值 void set(Object obj,Object value) //返回由该 Field表示的字段在指定对象上的值 Object get(Object obj) 返回由该 Field表示的字段在指定对象上的值 //对于私有或者默认的成员变量,可以使用暴力反射抑制java语法检查使用 public void setAccessible(boolean flag) 值为true,取消访问检查
1
2
3
4
5
6
7
8
9
反射获取成员方法的四种方式:
//返回所有公共成员方法对象的数组,包括继承的 //返回一个包含Method对象的数组,Method对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类 Method[] getMethods() //返回所有成员方法对象的数组,不包括继承的 //返回一个包含Method对象的数组,Method对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法 Method[] getDeclaredMethods() //返回单个公共成员方法对象 //返回一个Method对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法 Method getMethod(String name, Class<?>... parameterTypes) //返回单个成员方法对象 //返回一个Method对象,它反映此表示的类或接口的指定声明的方法 Class对象 Method getDeclaredMethod(String name, Class<?>... parameterTypes)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Method
类用于执行方法的方法//调用obj对象的成员方法,参数是args,返回值是Object类型 //在具有指定参数的指定对象上调用此 方法对象表示的基础方法 //Object:返回值类型 //obj:调用方法的对象 //args:方法需要的参数 Object invoke(Object obj,Object... args) //对于私有或者默认的成员变量,可以使用暴力反射抑制java语法检查使用 public void setAccessible(boolean flag) 值为true,取消访问检查
1
2
3
4
5
6
7
8
9
反射的经典用法:
- 越过泛型检查:通过反射技术向一个泛型为
Integer
的集合中添加一些字符串数据 - 运行配置文件中指定类的指定方法:通过反射获取配置文件中的类的名称和方法名称
- 反射可以提高代码的“可扩展性”,是很多框架的底层实现
- 越过泛型检查:通过反射技术向一个泛型为
# 模块化
概述:
java 9
的新特性- 为了给
Java
“瘦身”,让Java
实现轻量化,Java 9
正式的推出了模块化系统。Java
被拆分为N多个模块,并允许Java
程序可以根据需要选择加载程序必须的Java
模块,这样就可以让Java
以轻量化的方式来运行
使用:
1.在“被引入模块”和“引入模块”这两个模块中,都要创建一个“
module-info.java
”,模块化的说明文件2.在“被引入模块”中的“
module-info.java
”文件中,导出包module myOne { exports com.itheima_01; //导出“com.itheima_01”包 exports com.itheima_03; //导出“com.itheima_0”3包 }
1
2
3
43.在“引入模块”中的“
module-info.java
”文件中,导入模块
module myTwo { requires myOne; //导入myOne模块,导入后,myOne模块中的“com.itheima_01包中”和“com.itheima_03包中”的所有类,在当前模块中就可以正常使用了。 }
1
2
3模块服务的使用:
在被引入模块中指定接口的实现类
module myOne { exports 要导出的包的全路径 provides 接口名 with 实现类的名字 }
1
2
3
4在引入模块指定要使用的接口
module myTwo { requires myOne; user 接口名; }
1
2
3
4
# 类加载
- 类加载的描述
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
- 类的加载
- 就是指将
class
文件读入内存,并为之创建一个java.lang.Class
对象 - 任何类被使用时,系统都会为之建立一个
java.lang.Class
对象
- 就是指将
- 类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
- 类的初始化
- 在该阶段,主要就是对类变量进行初始化
- 类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
- 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
- 类的初始化时机
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的
java.lang.Class
对象 - 初始化某个类的子类
- 直接使用
java.exe
命令来运行某个主类
# 类加载器
作用:
- 负责将
.class
文件加载到内存中,并为之生成对应的java.lang.Class
对象。
- 负责将
JVM
的类加载机制分类- 全盘负责:就是当一个类加载器负责加载某个
Class
时,该Class所依赖的和引用的其他Class
也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 - 父类委托:就是当一个类加载器负责加载某个
Class
时,先让父类加载器试图加载该Class
,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类 - 缓存机制:保证所有加载过的
Class
都会被缓存,当程序需要使用某个Class
对象时,类加载器先从缓存区中搜索该Class
,只有当缓存区中不存在该Class
对象时,系统才会读取该类对应的二进制数据,并将其转换成Class
对象,存储到缓存区
- 全盘负责:就是当一个类加载器负责加载某个
Java中的内置类加载器分类:
Bootstrap class loader
:它是虚拟机的内置类加载器,通常表示为null
,并且没有父null
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,程序员无法获取也无法操作。换句话说,JDK中的默认有的类,都由该加载器加载。
Platform class loader
:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE
平台API
,其实现类和JDK
特定的运行时类负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中“jre\lib*.jar”或-Djava.ext.dirs指定目录下的jar包
System class loader
:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK
特定工具上的类负责加载classpath中指定的jar包及目录中class. 换句话说,就是我们自己写的类都是由该加载器加载
类加载器的继承关系:
System
的父加载器为Platform
,而Platform
的父加载器为Bootstrap
ClassLoader
中的两个方法:static ClassLoader getSystemClassLoader() //返回用于委派的系统类加载器 ClassLoader getParent() //返回父类加载器进行委派
1
2
# Optional
# Optional的创建方式:
// 创建一个Optional实例,若t为null 则会抛空指针异常
Optional.of(T t)
// 创建一个空的Optional实例
Optional.empty()
// 若T不为null,创建Optional实例,否则创建空实例
Optional.ofNullable(T t)
2
3
4
5
6
7
8
# Optional的常用方法
isPresent() : 判断是否包含值,包含返回true,否则返回false
get() : 如果Optional有值就返回,否则抛空指针异常
orElse(T t) : 如果调用对象包含值,就返回该值,否则返回参数t
orElseGet(Supplier s) : 如果对象包含值,返回该值,否则返回 s 获取的值
orElseThrow(Supply exceptionSupply) :当对象不包含值 就抛出指定的异常
map(Function f): 如果有值对其处理,返回处理后的Optional。否则返回Optional.empty()
flatMap(Function mapper) : 如果有值,对其进行处理,否则返回空Optional,flatMap与map区别是flatMap返回值必须是Optional,并且调用结束时,flatMap不会对结果进行Optional封装,需要我们手动封装
filter(Predicate predicate) : 如果有值并且满足判断条件就返回包含该值的Optional,否则返回空的Optional
ifPresent(Consumer action) : 有值就会执行方法
ifPresentOrElse(Consumer action,Runnable emptyAction) : 有值执行方法 action,否则执行emptyAction
2
3
4
5
6
7
8
9
10
# orElse()
和orElseGet()
区别
# 1. 传入参数不同
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
2
orElse()接受类型T的任何参数,而orElseGet()接受类型为Supplier的函数接口,该接口返回类型为T的对象 。
- orElse():如果有值则将其返回,否则返回指定的其它值。
- orElseGet():如果有值则将其返回,否则调用函数并将其返回调用结果。
# 2.使用时的区别
当Optional有值时:
public class OptionalTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(10, 20, 30);
int a = list.stream().reduce(Integer::sum).orElse(get("a"));
int b = list.stream().reduce(Integer::sum).orElseGet(() -> get("b"));
System.out.println("a = " + a);
System.out.println("b = " + b);
}
public static int get(String name) {
System.out.println(name + "执行了方法");
return 1;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果:
a执行了方法
a = 60
b = 60
2
3
当Optional值为空时:
public class OptionalTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList();
int a = list.stream().reduce(Integer::sum).orElse(get("a"));
int b = list.stream().reduce(Integer::sum).orElseGet(() -> get("b"));
System.out.println("a = " + a);
System.out.println("b = " + b);
}
public static int get(String name) {
System.out.println(name + "执行了方法");
return 1;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
输出结果:
a执行了方法
b执行了方法
a = 1
b = 1
2
3
4
由上可以发现,orElse()
方法在Optional
值为非空时,也会计算传入的参数,而orElseGet()
方法只有在Optional
值为空时才会执行传入的函数。
# 3. 性能上的区别
由于orElseGet()
不是每次都会调用传入的方法,所以orElseGet()
方法在性能上要优于orElse()
方法。
一般情况下,使用orElseGet()
方法更好,除非默认对象已经定义好可以直接访问。
# 断言
# 断言的概念
断言实际上是一种测试机制,它可以规定某个参数或者属性必须要满足某个条件,否则会抛出一个异常,并且程序会中止。
# 断言的一些特点
- 断言只用于开发测试阶段确定程序的内部错误
- 断言默认是禁用的,需要手动开启。禁用断言的情况下,类加载器会跳过断言代码
- 断言检测失败的时候,会抛出
AssertionError
异常,程序中止
# 断言的实例
断言是通过关键字assert
实现的,有以下两种使用形式:
assert 条件
assert 条件:表达式
这两种形式都会对条件进行检测,如果结果为false
,则抛出一个AssertionError
异常,在assert 条件:表达式
这种形式中,表达式会传入AssertError
的构造器,并将表达式转换成一个消息字符串。如果条件的检测结果为true
,则程序正常运行。