jdk8&9新特性
jdk8&9新特性
接口的新特性
概述
jdk8之前接口是规则的集合体,方法只有抽象方法。
jdk8版本开始不光光有抽象方法同时增加了实体方法。增加内容
jdk8: 抽象方法 默认方法 静态方法 jdk9: 私有方法
默认方法
概述
被关键字 default 修饰的方法就是默认方法,是在jdk8版本才出现的方法,独属于接口所有。
语法格式:
修饰符 default 返回值类型 方法名 (参数列表){方法体} public default void show(){ SOUT("哈哈"); }
使用规则:
1、直接被实现类对象调用使用 2、可以被实现类重写后调用使用 3、实现类的其他方法中想要调用接口的默认方法: 直接使用接口的实现类对象调用使用【前提:实现类没有重写接口的默认方法】 可以直接调用【调用的肯定是默认方法原有的方法体】 调用格式: 接口名.super.默认方法名(实参) 前提:方法所在的类必须是接口的实现类
影响:
1、使接口拥有了具体的方法从而越来越接近抽象类 2、因为接口是实现的关系,一个类可以实现多个接口,比抽象类更加容易使用,降低了耦合性加强了扩展 性,未来有取代抽象类的趋势。
代码示例:
接口: package com.hxzy.demo; public interface InterfaceA { void show();// 抽象方法 // 默认方法 public default void work(){ System.out.println("我是新来的默认方法 请多多关照,有事您说话"); } } 实现类: package com.hxzy.demo; public class ClassA implements InterfaceA { @Override public void show() { System.out.println("实现类给接口擦的屁股 "); } @Override public void work() { // 保留接口中的原有功能 调用接口中的work方法 InterfaceA.super.work(); System.out.println("看着新来的默认方法有点不爽,给你增加点伙计"); } // 实现类独有的方法 中调用接口中的默认方法 public void run() { // 调用接口中的work方法 InterfaceA.super.work(); // 执行的永远都是 默认方法的方法体 // 使用接口对象 调用默认方法 new ClassA().work();// 没有重写 :执行的就是 默认方法的方法体 重写:执行重写后的方法体 } } 测试类: package com.hxzy.demo; public class TestClassA { public static void main(String[] args) { // 想要使用接口中的方法 --- 获取接口的实现类对象 ClassA classA = new ClassA(); // 实现类对象调用接口中有的方法 classA.show();// 调用抽象方法 执行结果是实现类中重写后的方法体的结果 // 直接调用接口中的方法 classA.work();// 结果是接口中默认方法的方法体的结果 // 直接调用发现接口的默认方法太low了 我要扩展功能【重写方法】 // 调用run方法 classA.run(); } }
静态方法
概述:
静态方法独属于接口本身,实现类对象没有访问的权利,只能接口名调用方法
使用:
1、直接使用接口名调用 2、不同的接口中可以定义相同方法声明的静态方法,互不影响
结论
1、接口中的静态方法只能接口名调用,实现类对象没有权利使用 2、静态方法提供给接口的默认方法使用
代码示例:
接口A: package com.hxzy.demo; public interface InterfaceA { void show();// 抽象方法 // 默认方法 public default void work(){ System.out.println("我是新来的默认方法 请多多关照,有事您说话"); } // 静态方法 public static void get(){ System.out.println("我是接口A中的静态方法get"); } } 实现类: package com.hxzy.demo; public class ClassA implements InterfaceA { @Override public void show() { System.out.println("实现类给接口擦的屁股 "); } @Override public void work() { // 保留接口中的原有功能 调用接口中的work方法 System.out.println("看着新来的默认方法有点不爽,给你增加点伙计"); } // 实现类独有的方法 中调用接口中的默认方法 public void run() { // 调用接口中的work方法 InterfaceA.super.work(); // 执行的永远都是 默认方法的方法体 // 使用接口对象 调用默认方法 new ClassA().work();// 没有重写 :执行的就是 默认方法的方法体 重写:执行重写后的方法体 } // 实现类重写不了接口中的静态方法 } 测试类: package com.hxzy.demo; public class TestClassA { public static void main(String[] args) { // 想要使用接口中的方法 --- 获取接口的实现类对象 ClassA classA = new ClassA(); // 实现类对象调用接口中有的方法 classA.show();// 调用抽象方法 执行结果是实现类中重写后的方法体的结果 // 直接调用接口中的方法 classA.work();// 结果是接口中默认方法的方法体的结果 // 直接调用发现接口的默认方法太low了 我要扩展功能【重写方法】 // 调用run方法 classA.run(); // 实现类对象 调用不了接口的静态方法 // classA.get(); InterfaceA.get(); InterfaceB.get(); } }
私有方法
概述
是jdk9版本增加的一个实体方法,主要是用来进一步封装代码,提升相关代码安全性的手段。私有化之后方法不能被实现类直接调用使用或重写修改,只能提供给接口的静态方法和默认方法使用。
使用
普通私有方法:只能提供给默认方法调用使用 静态私有方法:默认方法和静态方法都可以调用
代码示例
接口: package com.hxzy.demo; public interface InterfaceA { void show();// 抽象方法 // 默认方法 public default void work(){ System.out.println("我是新来的默认方法 请多多关照,有事您说话"); get(); // 默认方法调用所有的私有方法 start(); end(); } // 静态方法 public static void get(){ System.out.println("我是接口A中的静态方法get"); // 静态方法只能静态的私有方法 end(); } // 普通的私有方法 private void start(){ System.out.println("接口的普通私有方法"); } // 静态的私有方法 private static void end(){ System.out.println("接口的静态私有方法"); } // private default void end(){ // System.out.println("接口的静态私有方法"); // } } 实现类: package com.hxzy.demo; public class ClassA implements InterfaceA { @Override public void show() { System.out.println("实现类给接口擦的屁股 "); } // 实现类中不可以重写接口私有方法 } 测试类: package com.hxzy.demo; public class TestClassA { public static void main(String[] args) { // 想要使用接口中的方法 --- 获取接口的实现类对象 ClassA classA = new ClassA(); // 实现类对象不能直接调用私有方法 可以借助默认方法调用 // classA.start(); // classA.end(); } }
Lambda表达式
概述:
Lambda表达式是java对数学函数表达式的一种体现形式,本质是一个值,在java中主要是体现在对特殊的匿名内部类对象的一种表示,代表匿名内部类的对象。 也可以理解为Lambda表达式是匿名内部类格式的一种简化,但是本质不一样。 都是匿名内部类对象 但是一个是进行单独编译,创建的对象 Lambdavia表达式不会单独编译 本质是值 值表示 了匿名内部类的对象
前提:【记住】
1、有且只有一个抽象方法的接口 2、必须要有上下文推断
格式:【记住】
标准格式:【三个一:一个小括号 一个箭头 一个花括号】 (参数列表)->{执行代码段} 格式解释: (参数列表):是匿名内部类中重写的抽象方法的方法参数列表 【就是接口抽象方法的参数列表】 ->:原封不动 他是lambda表达式的标志 {执行代码段}:lambda表达式对接口抽象方法的重写
结论:
lambda表示式就是得到接口实现类对象的另一种方式【1、定义实现类创建对象 2、 匿名内部类 3、lambda表达式】
代码示例:
package com.hxzy.demo; public class LambdaDemo { public static void main(String[] args) { // 直接使用Runnable的实现类对象 // 传统的方式【匿名内部类】 Runnable r = new Runnable() { @Override public void run() { System.out.println("这是匿名内部类重写抽象方法的方法体"); } }; new Runnable() { @Override public void run() { System.out.println("这是匿名内部类重写抽象方法的方法体"); } }; // 上面的匿名内部类符合lambda表示的使用前提 之一 Runnable r1 = ()->{ System.out.println("这是匿名内部类重写抽象方法的方法体");}; // 不能单独存在这是一个值 // ()->{ System.out.println("这是匿名内部类重写抽象方法的方法体");}; // 使用:匿名内部类课直接调用方法 lambda表达式也可以直接调用方法 效果上:都是接口的实现类对象 // 本质: 匿名内部类就是一个对象 lambda本质是一个值 java中这个值就是 对象 // 结论:lambda表示式就是得到接口实现类对象的另一种方式【1、定义实现类创建对象 2、 匿名内部类 3、lambda表达式】 r.run(); r1.run();
// 开辟新线程做事情
new Thread(()->{
// 线程要干的事
System.out.println("run方法要干的事");
}).start();new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run方法要干的事");
}
}).start();
}}
Lambda表达式的省略原则
概述:
在符合一定条件下Lambda表达式的格式可以进行简化的
具体原则:
1、小括号参数的参数类型可以直接省略 2、小括号的参数有且只有一个的时候,小括号可以省略 3、花括号中有且只有一个执行语句的时候,可以同时省略花括号 分号 和 return关键字 【要省略三者同时省略,否则一个都不要省略】 4、小括号和花括号的省略原则互不干扰,各省略各的
代码示例
package com.hxzy.demo; public class TestInterfaceC { public static void main(String[] args) { // 使用Lambda表达式获取接口InterfaceC的实现类对象 // 标准格式 InterfaceC c = (int num)->{ return num *2; }; // 简化 :小括号符合省略原则 InterfaceC c1 = num ->{ // System.out.println(num); return num *2; }; // 简化 :花括号符合省略原则 [要么省略全部省略 要么一个都不要省略] // InterfaceC c2 = num -> System.out.println(num); InterfaceC c2 = num -> num *2; } }
方法引用
概述:
函数式接口:如果一个接口中只有一个抽象方法,它就叫函数式接口 方法引用是对函数式接口的实现类对象获取的另一种方式,他一般是lambda表达式的栾生兄弟 都是用来体现特殊接口的实现类读写的新方式。 方法引用对有且只有一个抽象方法接口中的重写方法的方法逻辑在其他类的方法中已经实现过的接口可以使用方法引用?
特殊条件:
Lambda表达式对接口抽象方法重写的方法体,在其他类中有具体的方法已经书写实现过的情况就可以使用方法引用表示接口的实现类对象 使用范围比lambda更小了。 lambda表达式使用所有的有且只有一个抽象方法的接口 方法引用只能使用于有且只有一个抽象方法的接口的部分接口
使用前提:
1、有且只有一个抽象方法的接口 2、必须有上下文推断
方法引用的格式:
静态方法引用: 类名 ::静态方法名 普通方法引用: 对象名 ::普通方法名 构造方法的引用: 类名 ::new
代码示例:
e接口 package com.hxzy.demo; public interface InterfaceE { void work(String str,int num); } F接口 package com.hxzy.demo; public interface InterfaceF { Demo get(int num); } Demo类 package com.hxzy.demo; public class Demo { int name ; public void work(String str){ // 方法的逻辑和方法的声明和接口中抽象方法重写是相同的 System.out.println(str); } public static void show(String str ,int num){ // 方法的逻辑和方法的声明和接口中抽象方法重写是相同的 String s = str + num ; System.out.println(s); } public Demo(int name) { this.name = name; } public Demo() { } } 静态方法引用 package com.hxzy.demo; public class TestInterfaceE { public static void main(String[] args) { // 想要获取E接口的实现类对象 // 匿名内部类 InterfaceE e1 = new InterfaceE() { @Override public void work(String str, int num) { System.out.println(str + num); } }; // lambda表达式方式 InterfaceE e2 = (String str, int num)-> {System.out.println(str + num);}; // 方法引用【静态方法的引用】 InterfaceE e3 = Demo :: show; e1.work("123",456); e2.work("123",456); e3.work("123",456); } } 构造方法引用 package com.hxzy.demo; public class TestInterfaceF { public static void main(String[] args) { // 获取F接口的实现类对象 调用get方法得到Demo对象 // 匿名内部类 InterfaceF f1 = new InterfaceF() { @Override public Demo get(int num) { return new Demo(); } }; Demo demo = f1.get(200); System.out.println(demo.name);// 0 // Lambda 表达式 InterfaceF f2 = (num) ->{ return new Demo(num);// 调用Demo类的空参构造 }; Demo demo2 = f2.get(300); System.out.println(demo2.name);// 0 // 方法引用 [构造的引用有局限性 ] InterfaceF f3 = Demo :: new;// 引用所有的构造 具体执行按照上下文推断 Demo demo3 = f3.get(500); System.out.println(demo3.name);// 0 } }
函数式接口
概述:
有且只有一个抽象方法的接口就是函数式接口 比如:jdk中熟悉的函数式接口 :Runnable
注解:
@FunctionalInterface 作用:定义函数接口的时候需要在接口上面增加这个注解,方便编译的时候判断该接口是不是函数式接口
jdk内置常用函数式接口
1、Consumer:消费型接口 有一个可以消费任意数据类型的数据的方法 2、Supplier:供给型接口 有一个可以提供任意数据类型数据的方法 3、Function:函数型接口 有一个可以把任意数据乐行转换为其他任意数据类型的方法 4、Predicate:断言型接口 有一个可以对任意数据进行判断的方法 指定定义了功能,没有定义具体的实现原则,可以使用这样的接口对象定义对应的具体规则,对象采用Lambda表达式来体现,规则在对象中 对象又是Lambda表达式,表达式又是一个值 就可以传递这个值,相当于实现把规则进行传递了
Consumer
<T>
【消费性接口】
功能
accept(T t):消费使用掉任意的一个数据
作用:
当某个函数可以接收一个数据,并且处理这个数据,处理完成之后,不需要返回任何数据,这个函数需要当做数据来进行传递,就使用消费型接口
代码示例:
package com.hxzy.demo; import java.util.function.Consumer; public class ConsumerDemo { public static void main(String[] args) { // 创建一个Consumer的对象 ---- 定义好一个如何消费的原则 Consumer c = (money)->{ System.out.println("哥哥使用你的"+ money + "带你领略男人的伟大"); }; Consumer c1 = (money)->{ System.out.println("哥哥使用你的"+ money + "买了一套房,给红颜知己跟你没啥事"); }; // 调用userMoney方法帮忙花钱 useMoney(100000000,c1); } // 功能 :有一个钱 不知道怎么花 需要找一个会花钱的主 带他化 public static void useMoney(int money,Consumer<Integer> c){ System.out.println("我现在有"+ money+ "钱,不知道怎么浪,我是一个好孩子先找找一个可以替我花的"); c.accept(money); } }
Supplier【供给型接口】
功能
T get():提供返回任意类型数据的规则
作用
当某个函数可以直接一个数据,这个函数需要当做获取数据的结果数据来进行传递,就使用供给型接口
代码示例
package com.hxzy.demo; import java.util.function.Supplier; public class SupplierDemo { public static void main(String[] args) { // 写一个可以获取Demo对象的规则 ---- Supplier的对象 Supplier<Demo> s = Demo::new; Demo demo = getDemo(s); System.out.println(demo.name);// 0 Supplier<Demo> s1 = ()->{ return new Demo(258);}; Demo demo1 = getDemo(s1); System.out.println(demo1.name);// 258 } // 有一个方法可以获取Demo对象 但是自己又不知道怎么获取,找Supplier接口 帮忙返回一个Demo的对象 public static Demo getDemo(Supplier<Demo> supplier){ Demo demo = supplier.get(); return demo; } }
函数接口
Function<T,R>【函数型接口】
功能
R apply(T t):抽象方法提供把一个数据类型转换为另一个数据类型规则
代码示例:
package com.hxzy.demo; import java.util.function.Function; public class FunctionDemo { public static void main(String[] args) { // 直接使用Function接口 Function<String, char[]> f1 = new Function<String, char[]>() { @Override public char[] apply(String s) { // 转化过程【转换原则】 char[] chars = s.toCharArray(); return chars; } }; char[] chars = f1.apply("abcd"); for (char c : chars) { System.out.println(c); } // Lambda 获取对象 Function<String,String> f2 = (t)->{ String str = t.substring(2); return str;}; String s = f2.apply("abcdefght"); System.out.println(s);// cdefght Function<char[],String> f3 = (arr) -> { return new String(arr); }; } }
Predicate <T>
【断言型接口】
功能
boolean test(T t):抽象方法 判断对给定的数据是否符合某个条件 Predicate and (Predicate p): 把两个断言型接口的判断规则判断的结果进行逻辑且【与】的处理 Predicate or (Predicate p): 把两个断言型接口的判断规则判断的结果进行逻辑或的处理 Predicate negate(): 取判断结果的反向结果
代码示例:
package com.hxzy.demo; import java.util.function.Predicate; public class PredicateDemo { public static void main(String[] args) { // 想要判断一个字符串是不是以str开头 Predicate<String> p1 = new Predicate<String>() { @Override public boolean test(String s) { // 定义判断的原则【想要怎么判断:判断一个字符串是不是以str开头】 boolean b = s.startsWith(s); return b; } }; // 判断一个字符串的长度是不是大于10 Predicate<String> p2 = (str)->{return str.length() > 10;}; boolean b = p1.test("strjklfjalskjg"); boolean b1 = p2.test("strjk"); System.out.println(b);// true System.out.println(b1);// false boolean b2 = p1.and(p2).test("strjk"); boolean b3 = p1.or(p2).test("strjk"); boolean b4 = p2.negate().test("strjk"); System.out.println(b | b1);// true System.out.println(b3);// true // System.out.println(b & b1);// false // System.out.println(b2);// false System.out.println(!b1);// true System.out.println(b4);// true } }