Java8部分新特性
一,接口的新特性
- 增加default关键字和static关键字修饰接口中的方法。
- default方法需要实例引用,static方法只能接口来引用。
- 接口里的静态方法不会被继承,静态变量会被继承。
- 如果一个类实现了多个接口,并且这些接口相互之间没有继承关系,同时存在存在相同的默认方法。若果多个接口有继承关系,那么,多个接口会被子接口覆盖。
- 多个继承中,相同的默认方法,可以在实现类中,重写接口的方法,在重写的方法中使用super关键字调用父接口的方法。
- 如果一个接口只有一个抽象方法(包括继承的),那么,该接口默认是一个函数式接口。
- 如果接口里面使用FunctionalInterface注解,那么,限定接口里面必须有且只能有一个抽象方法。
demo
定义两个含有default方法和static接口,一个子,一个父
Interface1
// 接口1
public interface Interface1 extends Interface2 {
// default关键字方法
default void default1() {
System.out.println("Interface1的default1方法");
}
default void default3() {
System.out.println("Interface1的default3方法");
}
// static关键字方法
static void static1() {
System.out.println("Interface1的static1方法");
}
}
Interface2
// 接口2
public interface Interface2 {
// default关键字方法
default void default2() {
System.out.println("Interface2的default1方法");
}
default void default3() {
System.out.println("Interface2的default3方法");
}
// static关键字方法
static void static2() {
System.out.println("Interface2的static1方法");
}
}
定义一个类实现这两个接口
InterfaceImpl
public class InterfaceImpl implements Interface1,Interface2 {
// 重写父接口的默认方法
public void defaul3(){
Interface1.super.default3();
}
}
测试
public class InterfaceFeatures {
// 测试方法
public static void main(String[] args) {
// 调用接口的静态方法
Interface1.static1();
Interface2.static2();
// 实例一个对象
InterfaceImpl ii1 = new InterfaceImpl();
ii1.default1();
//ii1.static1();
ii1.default3();
}
}
使用注解声明的函数式接口
// 函数式接口
@FunctionalInterface
public interface FunctionalInterface1 {
void test1();
//void test2();//有且只能有一个抽象方法
default void d1(){
}
static void s1(){
}
}
二, Lambda表达式
Java是面向对象的语言,不能象函数式语言那样嵌套定义方法。Java的匿名内部类只能存在于创建于它的线程中,不能运行在多线程中,无法充分利用多核的硬件优势。
匿名内部类的缺点:语法相对复杂;在调用内部类的上下文中,指引和this的指代容易混淆;类加载和实例创建语法不可避免;不能引用外部的非final对象;不能抽象化控制流程。
对于上述的问题,开发了Java中使用Lambda表达式。
Lambda表达式只能应用于函数式接口。Lambda表达式可以认为是一种特殊的匿名内部类。
1,Lambda语法
([形参列表,不带数据类型]) –> {
// 执行语句;
[return …;]
}
2,形参列表的注意事项
如果形参列表是空的,只需要保留()即可;
如果没有返回值,只需要在{}写执行语句即可;
如果接口的抽象方法只有一个形参,()可以省略,只需要参数的名称即可;
如果执行语句只有一行,可以省略{},但是如果有返回值的时候,有点特殊;
形参列表的数据类型自动推断,只要参数名称;
如果函数式接口的方法有返回值,必须要给定返回值,如果执行语句只有一行代码,可以省略大括号,但必须同时省略return;
demo
public class LambdaTest1 {
public void testLambda(UserTest4 ut1, int a) {
int b = ut1.test(a);
System.out.println("Lambda表达式的返回值是:" + b);
}
public static void main(String[] args) {
// 使用匿名内部类的方式
UserTest1 ut1 =
new UserTest1() {
@Override
public void test() {
System.out.println("使用匿名内部类的方式实现接口");
}
};
ut1.test();
// 使用lambda表达式的方式
UserTest1 ut2 =
() -> {
System.out.println("使用lambda表达式的方式实现接口");
};
ut2.test();
// 无参时,如果执行语句只有一行,可以省略{}和一个;
// 如果有返回值,可以省略return
UserTest1 ut3 = () -> System.out.println("简化版,使用lambda表达式的方式实现接口");
ut3.test();
// 一个参数,无返回值
// 数据类型自动根据接口方法中的参数类型定义推断
UserTest2 ut4 =
(x) -> {
System.out.println("一行代码,一个参数,参数值为:" + x);
};
ut4.test(5);
// 简化版
UserTest2 ut5 = x -> System.out.println("简化版,一行代码,一个参数,参数值为:" + x);
ut5.test(15);
// 两个参数,无返回值
UserTest3 ut6 =
(x, y) -> {
System.out.println("两个参数,无返回值。x:" + x + ";y:" + y);
};
ut6.test(1, 3);
// 一个参数,有返回值
UserTest4 ut7 =
x -> {
x += 10;
return x;
};
System.out.println("一个参数,有返回值,值为:" + ut7.test(10));
// 简化版,一个参数,有返回值,省略{},需要同时省略掉return关键字
UserTest4 ut8 = x -> x + 10;
System.out.println("简化版,一个参数,有返回值,值为:" + ut8.test(20));
// 测试方法中以函数式接口为参数的Lambda表达式语法
LambdaTest1 lt1 = new LambdaTest1();
int b = 10;
lt1.testLambda((a)-> {return a + b;}, 10);
}
}
// 接口方法无参,无返回值
@FunctionalInterface
interface UserTest1 {
void test();
}
// 接口方法一个参数,无返回值
@FunctionalInterface
interface UserTest2 {
void test(int a);
}
// 接口方法两个参数,无返回值
@FunctionalInterface
interface UserTest3 {
void test(int a, int b);
}
// 接口方法一个参数,一个返回值
@FunctionalInterface
interface UserTest4 {
int test(int a);
}
三,方法的应用
引用实例方法,自动把调用方法的时候的参数,全部传给引用的方法。
<函数式接口><变量名> = <实例>::<实例方法名>;
//自动把[实参]全部传递引用的实例方法。
<变量名>.<接口方法名>([实参])
引用类方法
全部参数传给引用的方法
<函数式接口><变量名>=<类>::<类方法名>;
//自动把[实参]全部传递给引用的类方法。
<变量名>.<接口方法名>([实参]);
引用类的实例方法
定义、调用接口方法的时候,需要多一个参数,并且参数的类型和引用实例方法的类型必须一致。
把第一个参数作为引用的实例,后面的每个参数全部传递给引用的方法。
interface <函数式接口>
{
<返回值><方法名>(<类名><名称>[,其他参数…]);
}
<函数式接口><变量名>=<类名>::<实例方法名>;
<变量名>.<方法名>(<类名的实例>[,其他参数]);
构造器引用
把方法的所有参数全部传递给引用的构造器,根据参数的类型推断调用的构造器。
<类名>::new
demo
public class LambdaTest2 {
public static void main(String[] args) {
// 实现了方法,然后引用
MethodRef1 mr1 =
(s) -> {
System.out.println(s);
};
mr1.test("打印字符串");
// 实例的实例方法的引用
MethodRef1 mr2 = System.out::println;
mr2.test("使用方法的引用");
// 类的方法的引用
MethodRef2 mr3 = Arrays::sort;
int[] array = {1, 55, 23, 2, 5, 16};
mr3.test(array);
System.out.println(Arrays.toString(array));
// 类的实例方法的引用
MethodRef3 mr4 = PrintStream::println;
// 第二个参数作为引用的方法的参数
mr4.test(System.out, "第二个参数");
// 构造器的引用,根据函数式接口的方法名来推断引用哪个构造器
MethodRef4 mr5 = String::new;
String str = mr5.test(new char[] {'h', 'e', 'l', 'l', 'o'});
System.out.println(str);
// 上述构造器引用,等价于Lambda表达式的写法为
MethodRef4 mr6 = (c) -> {
String s = new String(c);
return s;
};
String str2 = mr6.test(new char[]{'1','b','c'});
System.out.println(str2);
}
}
interface MethodRef1 {
void test(String s);
}
interface MethodRef2 {
void test(int[] array);
}
interface MethodRef3 {
void test(PrintStream printStream, String str);
}
interface MethodRef4 {
String test(char[] chars);
}
四,新的数据流接口
1,stream接口
Stream是Java8中被定义为泛型接口;代表数据流;不是一个数据结构 ,不直接存储数据;通过管道操作数据。
管道代表一个操作序列。包含有,数据集,可能是集合、数组等;0个或多个中间业务,如过滤器;一个终端操作,如forEach。过滤器是,用给定的条件在源数据基础上过滤出新的数据,并 返回一个Stream对象,过滤器包含匹配的谓词。
demo
编写一个Person类
public class Person {
public static enum Sex{
MALE,FEMALE;
}
private String name;
private int age;
private Sex gender;
private double height;
public Person(String name, int age, Sex gender, double height) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sex getGender() {
return gender;
}
public void setGender(Sex gender) {
this.gender = gender;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender=" + gender +
", height=" + height +
'}';
}
}
编写一个测试类,测试Stream
public class StreamTest1 {
public static void main(String[] args) {
List<Person> personList = createPerson();
// 获取Stream流
Stream<Person> personStream = personList.stream();
personStream.forEach(
person -> {
System.out.println(person);
});
System.out.println("===========================================");
// 测试过滤器
Stream<Person> personStream1 = personList.stream().filter(person -> {
// 返回性别为女的对象
return person.getGender() == Person.Sex.FEMALE;
});
personStream1.forEach(
person -> {
System.out.println(person);
});
}
public static List<Person> createPerson() {
List<Person> personList = new ArrayList<>();
Person p1 = new Person("张三", 24, Person.Sex.MALE, 170);
Person p2 = new Person("李四", 25, Person.Sex.FEMALE, 169);
Person p3 = new Person("张子怡", 35, Person.Sex.FEMALE, 168);
Person p4 = new Person("李冰冰", 32, Person.Sex.FEMALE, 171);
personList.add(p1);
personList.add(p2);
personList.add(p3);
personList.add(p4);
return personList;
}
}
2,DoubleStream接口
表示元素类型是double的数据源。
常用方法
max().getAsDouble(),获取数据流中数据集的最大值。
stream.min().getAsDouble(),获取流中数据集的最小值。
stream.average(),获取流中数据集的平均值。
demo
public class StreamTest2 {
public static void main(String[] args) {
// 使用DoubleStream获取名字中包含张的平均身高
List<Person> personList = createPerson();
double avgHeight = personList
.stream()
.filter(
person ->
{return person.getName().indexOf("张") >= 0;})
.mapToDouble(person -> {return person.getHeight();})
.average()
.getAsDouble();
System.out.println("姓名中包含张字的人的平均身高为:" + avgHeight);
}
public static List<Person> createPerson() {
List<Person> personList = new ArrayList<>();
Person p1 = new Person("张三", 24, Person.Sex.MALE, 170);
Person p2 = new Person("李四", 25, Person.Sex.FEMALE, 169);
Person p3 = new Person("张子怡", 35, Person.Sex.FEMALE, 168);
Person p4 = new Person("李冰冰", 32, Person.Sex.FEMALE, 171);
personList.add(p1);
personList.add(p2);
personList.add(p3);
personList.add(p4);
return personList;
}
}
五,日期相关的新的类
1,LocalDate
使用ISO日历表示年月日。
LocalDaet.now(),获取系统当前日期。
LocalDate.of(int year,int month,int dayOfMonth),按指定日期创建LocalDate对象。
getYear(),返回日期中的年份。
getMonth(),返回日期中的月份。
getDayOfMonth(),返回月份中的日。
demo
public class LocalDateTest {
public static void main(String[] args) {
// 获取当前日期
LocalDate date = LocalDate.now();
// 打印当前日期
System.out.println(date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth() + "\t");
System.out.println(date.toString());
}
}
2,LocalTime
用于表示一天中的时间。
LocalTime.now(),获取系统当前时间。
LocalTime.of(int hour, int minute, int seond),按指定时间创建LocalTime对象。
getHour(),返回小时。
getMinute(),返回分钟。
getSecond(),返回秒。
demo
public class LocalTimeTest {
public static void main(String[] args) {
// 获取当前时间
LocalTime time = LocalTime.now();
// 打印当前时间
System.out.println(time.getHour() + ":" + time.getMinute() + ":" + time.getSecond());
System.out.println(time.toString());
}
}
3,LocalDateTime
用于表示日期和时间。
demo
public class LocalDateTimeTest {
public static void main(String[] args) {
// 获取当前日期时间
LocalDateTime dateTime = LocalDateTime.now();
// 打印当前日期时间
System.out.println(
dateTime.getYear()
+ "年"
+ dateTime.getMonthValue()
+ "月"
+ dateTime.getDayOfMonth()
+ "日\t"
+ dateTime.getHour()
+ "时"
+ dateTime.getMinute()
+ "分"
+ dateTime.getSecond()
+ "秒");
System.out.println(dateTime.toString());
}
}
4,DateTimeFormatter
用于将字符串解析为日期。
demo
public class DateTimeFormatterTest {
public static void main(String[] args) {
// 按照一定的时间日期格式获取一个格式化对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
// 需要格式化的字符串
String dateTimeStr = "2018年12月23日 17:15:12";
// 格式化字符串
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, dateTimeFormatter);
System.out.println(dateTime);
}
}
5,ZonedDateTime
处理日期和时间与相应的时区。
demo
public class ZonedDateTimeTest {
public static void main(String[] args) {
// 获取当前时区时间日期
ZonedDateTime zonedDateTime = ZonedDateTime.now();
// 获取一个指定时间日期格式的格式化对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
// 格式化时间日期为字符串
String dateTimeStr = zonedDateTime.format(dateTimeFormatter);
System.out.println(dateTimeStr);
}
}