Java SE 8都有哪些新特性,包括哪些内容(一)?
本文内容参考自Java8标准
再次感谢尚硅谷教学资料对本文的启发!
相信所有的程序员在接触到Java8以后,都会了解到一个关键词:“Lambda”,Java8最显著的新特性就是增加了一个叫做"Lambda表达式"的新语法。
那么,要讨论它从哪里入手呢?它具体影响了Java编程的哪一块呢?
下面将通过具体的代码示例来说明。
1、没有Lambda表达式时,操作集合的代码是这样的:
⑴、最纯粹的操作集合的Java代码(此处以集合list为例)。
代码示例:
①、创建一个类Employees,再创建多个它的对象加入集合中
// 最纯粹的Java代码
//类Employees,用它的对象作为示例对象,
//创建多个对象添加进容器中
class Employees {
//String类型的类变量name
private String name;
//int类型的类变量age
private int age;
//double类型的类变量salary
private double salary;
//无参数的构造方法
public Employees() {}
//带所有类变量的构造方法
public Employees(String name, int age, double salary) {
//初始化类变量
this.name = name;
//初始化类变量
this.age = age;
//初始化类变量
this.salary = salary;
}
//get()/set()方法
public String getName() {
return name;
}
//get()/set()方法
public void setName(String name) {
this.name = name;
}
//get()/set()方法
public int getAge() {
return age;
}
//get()/set()方法
public void setAge(int age) {
this.age = age;
}
//get()/set()方法
public double getSalary() {
return salary;
}
//get()/set()方法
public void setSalary(double salary) {
this.salary = salary;
}
//toString()方法
public String toString() {
//返回所有的类变量的值。
return "Employees [name=" + name + ", age=" + age + ",
salary=" + salary + "]";
}
}
②、需求一:获取list中年龄大于35的Employees.
//目标list:
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//创建一个方法,带一个List<Employees>类型的形式参数,
//表示可以传入一个list进行处理。
//方法AgeBiggerThan35(List<Employees> list),带一个List<Employees>
//类型的形式参数list.
public List<Employees> AgeBiggerThan35(List<Employees> list) {
//创建另外的一个List<Employees> 类型的list1接收
//年龄大于35的Employees对象
List<Employees> list1 = new ArrayList<>();
//for循环list
for(int i=0;i<list.size();i++) {
//判断每个Employees对象的年龄
Employees e = list.get(i);
//if语句,如果年龄大于35,那么
//e.getAge()>35结果为true.
if(e.getAge()>35) {
//将对象e添加到list1中
list1.add(e);
}
}
//返回list1
return list1;
}
//如果你想改进上面的代码,你可以使用foreach循环:
public List<Employees> AgeBiggerThan35(List<Employees> list) {
//创建另外的一个List<Employees> 类型的list1接收
//年龄大余35的Employees对象
List<Employees> list1 = new ArrayList<>();
//foreach循环list
for(Employees e : list) {
//if语句,如果年龄大于35,那么
//e.getAge()>35结果为true.
if(e.getAge()>35) {
//将对象e添加到list1中
list1.add(e);
}
}
//返回list1
return list1;
}
③、需求二:获取list中收入大于5000的Employees.
//目标list:
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//创建一个方法,带一个List<Employees>类型的形式参数,
//表示可以传入一个list进行处理。
//方法SalaryBiggerThan5000(List<Employees> list),
//带一个List<Employees>
//类型的形式参数list.
public List<Employees> SalaryBiggerThan5000(List<Employees> list) {
//创建另外的一个List<Employees> 类型的list1接收
//收入大于5000的Employees对象
List<Employees> list1 = new ArrayList<>();
//for循环list
for(int i=0;i<list.size();i++) {
//判断每个Employees对象的收入
Employees e = list.get(i);
//if语句,如果收入大于5000,那么
//e.getSalary()>5000结果为true.
if(e.getSalary()>5000) {
//将对象e添加到list1中
list1.add(e);
}
}
//返回list1
return list1;
}
从上面两个需求的解决代码来看,你会发现有大量的代码重复,实际上,核心的代码都只有一句:
除了核心代码以外,其它代码几乎都是一致的,但是为了满足每个需求,我们在实际的编程过程中不得不每次都写一遍。
为了解决每次变换需求都需要重复写一遍主体方法,可以使用设计模式。
⑵、含设计模式的Java代码。
设计模式要解决什么问题呢?从上面的例子你会发现,需求变了,主体方法的代码几乎就要重复一遍,那么,能不能将需求放进方法的形式参数中,这样,在使用的时候通过传入不同的需求,让方法得出不同的结果?
可以!
那么,Java中哪种设计模式可以满足这种变化的需求呢?
策略模式!
实际上,策略模式还不是解决这个问题的根本,根本是什么呢?根本是:
多态!
你需要规定一个接口类型,代表需求放入方法的形式参数中,在调用方法的时候,再传入实际的对象(当然,这个对象的类是继承自这个接口的),这样,不同的实际对象就代表了不同的需求,而不用为每一个需求都重复编写一遍主体方法的代码了。
下面,我们根据这个思路来实现代码:
代码示例:
①、规定一个接口代表需求类型:MyPredicate< T >
// 先规定一个代表需求的接口类型
//代表需求的接口MyPredicate<T>,还需要使用泛型
//泛型的作用是规定它代表了哪个类的需求类型
public interface MyPredicate<T>{
//这里为什么规定方法的返回值为boolean类型
//继续往下看就知道了,因为这个方法
//需要在if语句中使用。
boolean test(T t);
}
②、主体方法的代码,这个方法最重大的改变是不再与具体的需求挂钩,只与接口方法的返回值(m.test(e))挂钩,具体是什么需求,只有在调用方法的时候才知道。
// 将需求的接口类型作为方法的形式参数
//类FirstWithoutLambda
public class FirstWithoutLambda{
//方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),带两个形式参数,一个是List<Employees>
//类型的list,一个是MyPredicate<Employees>类型的m
public List<Employees> WorkOnAll(List<Employees> list,
MyPredicate<Employees> m){
//创建另外的一个List<Employees> 类型的list1接收
//m.test(e)结果为true的Employees对象
List<Employees> list1 = new ArrayList<>();
//使用foreach循环list
for(Employees e:list) {
//判断e是否符合接口MyPredicate<T>
//规定的条件,如果符合,m.test(e)结果为true,
//如果不符合,结果为false
if(m.test(e)) {
//m.test(e)结果为true,将对象e添加到
//容器list1中.
list1.add(e);
}
}
//返回容器list1
return list1;
}
}
③、创建一个类Age implements 需求接口MyPredicate< T >代表需求:年龄大于35岁的Employees
// 年龄大于35岁
//类Age实现接口MyPredicate<Employees>,代表需求:年龄大于35岁的Employees
public class Age implements MyPredicate<Employees>{
//实现接口的方法
public boolean test(Employees e) {
//返回e.getAge()>35这个表达式的值
return e.getAge()>35;
}
}
④、测试,类FirstWithoutLambda
// 测试
//类FirstWithoutLambda
public class FirstWithoutLambda{
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用类FirstWithoutLambda的方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),传入实际的参数list和类Age的对象
//返回类型为List<Employees>
List<Employees> list11 = t.WorkOnAll(f.list, new Age());
//打印容器list11
System.out.println(list11);
}
}
结果示例:
变换需求:获取工资大于5000的Employees。创建一个类Salary implements 代表需求的接口MyPredicate< T >
// 工资大于5000
//类Salary实现接口MyPredicate<Employees>
public class Salary implements MyPredicate<Employees>{
//实现接口的方法
public boolean test(Employees e) {
//返回e.getSalary()>5000这个表达式的值
return e.getSalary()>5000;
}
}
⑤、测试,类FirstWithoutLambda
// 测试
//类FirstWithoutLambda
public class FirstWithoutLambda{
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用类FirstWithoutLambda的方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),传入实际的参数list和类Age的对象
//返回类型为List<Employees>
List<Employees> list11 = t.WorkOnAll(f.list, new Salary());
//打印容器list11
System.out.println(list11);
}
}
结果示例:
从上面的示例中你会发现,每实现一个需求,主体的方法已经是通用的了,不需要重复创建,但是一个需求仍需要一个类去实现代表需求的接口(只是将一个需求对应一个方法变成了一个需求对应一个类,仅仅提高了程序的可拓展性。并没有什么实质的改变)。还是一对一的关系,那么,有没有什么简化的方法呢?
⑥、利用匿名内部类简化上面的代码示例:获取工资大于5000的Employees
// 测试
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用类FirstWithoutLambda的方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),传入实际的参数list和匿名内部类的对象
//返回类型为List<Employees>
List<Employees> list11 = t.WorkOnAll(f.list, new
MyPredicate<Employees>(){
//实现接口的方法
public boolean test(Employees e) {
//返回e.getSalary()>5000这个表达式的值
return e.getSalary()>5000;
}
});
//打印容器list11
System.out.println(list11);
}
结果示例:
匿名内部类省略了每次换需求的时候都要使用一个类去实现需求接口的代码,但是仍然没有改变一对一的本质:
一个需求对应一个方法,
一个需求对应一个类,
一个需求对应一个内部类。
还有没有其它的简化代码的方式了呢?
有,它就是Lambda表达式了。
2、有Lambda表达式时,操作集合的代码是这样的:
为了能更加清晰地展示Lambda表达式,下面,再重头梳理一遍代码与Lambda表达式有关的代码,仔细描述一下lambda表达式的好处(你可以自行对比,重点在测试类的代码)。
①、先规定一个接口
规定一个接口代表需求类型:MyPredicate< T >
// 先规定一个代表需求的接口类型
//代表需求的接口MyPredicate<T>,还需要使用泛型
//泛型的作用是规定它代表了哪个类的需求类型
public interface MyPredicate<T>{
//这里为什么规定方法的返回值为boolean类型
//继续往下看就知道了,因为这个方法
//需要在if语句中使用。
boolean test(T t);
}
②、再规定一个方法,这个接口是这个方法的形式参数
// 将需求的接口类型作为方法的形式参数
//类FirstWithLambda
public class FirstWithLambda{
//方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),带两个形式参数,一个是List<Employees>
//类型的list,一个是MyPredicate<Employees>类型的m
public List<Employees> WorkOnAll(List<Employees> list,
MyPredicate<Employees> m){
//创建另外的一个List<Employees> 类型的list1接收
//m.test(e)结果为true的Employees对象
List<Employees> list1 = new ArrayList<>();
//使用foreach循环list
for(Employees e:list) {
//判断e是否符合接口MyPredicate<T>
//规定的条件,如果符合,m.test(e)结果为true,
//如果不符合,结果为false
if(m.test(e)) {
//m.test(e)结果为true,将对象e添加到
//容器list1中.
list1.add(e);
}
}
//返回容器list1
return list1;
}
}
③、创建一个集合list,进行测试(获取年龄大于35的Employees)
// 测试
//类FirstWithLambda
public class FirstWithLambda {
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用类FirstWithoutLambda的方法WorkOn(List<Employees> list,
//MyPredicate<Employees> m),传入实际参数list和一个lambda表达式
List<Employees> list11 = t.WorkOn(f.list, (e)->e.getAge()>35);
//打印容器list11
System.out.println(list11);
}
}
你会发现,在需要传一个接口类型的实际调用中,只传入了一个lambda表达式就解决了问题:
④、结果示例
⑤、变换需求,进行测试(获取工资大于5000的Employees)
// 测试
//类FirstWithLambda
public class FirstWithLambda {
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithLambda f = new FirstWithLambda();
//调用类TestAll的方法WorkOn(List<Employees> list,
//MyPredicate<Employees> m),传入实际参数list和一个一个lambda表达式
List<Employees> list11 = t.WorkOn(f.list, (e)->e.getSalary()>5000);
//打印容器list11
System.out.println(list11);
}
}
⑥、结果示例
Lambda表达式充分实现了:改动需求充分聚焦核心的代码。
还有没有其它的简化代码的方式了呢?
有,完全使用流(stream)操作集合
①、获取年龄大于35的Employees
// 测试
//类FirstWithLambda
public class FirstWithLambda {
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用集合list的方法stream()开启流模式
//再调用方法filter(),将lambda表达式作为实际参数传入,
//开启数据过滤模式,最后调用方法foreach()遍历集合
//System.out::println是Java8中新增的方法引用,这里是通过它将集合输出
//(方法引用后面会详细说明)
list.stream().filter((e)->e.getAge()>35).
forEach(System.out::println);
//通过流操作集合,代码非常的简便。
}
}
结果示例:
②、变换需求:获取工资大于5000的Employees
// 测试
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithoutLambda f = new FirstWithoutLambda();
//调用集合list的方法stream()开启流模式
//再调用方法filter(),将lambda表达式作为实际参数传入,
//开启数据过滤模式,最后调用方法foreach()遍历集合
//System.out::println是Java8中新增的方法引用,这里是通过它将集合输出
//(方法引用后面会详细说明)
list.stream().filter((e)->e.getSalary()>5000).
forEach(System.out::println);
//通过流操作集合,代码非常的简便。
}
}
结果示例:
整个过程梳理下来,可以发现,从最初的一个需求搭配一个主体方法,到后面的设计模式,再到匿名内部类升级设计模式,最后是Lambda表达式以及流API的使用,代码越发的简洁明白。
下一篇博文将详细描述Lambda表达式的语法,如何使用以及在什么情况下使用。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。