JAVA8新特性总结
高山仰止,景行行止;虽不以至,心向往之。
1. 接口默认方法
Talk is cheep, show me your code
在java7以前,接口中不能有存在方法体的方法,举个错误例子:
public interface MyJava7Interface {
//以下代码为错误代码,java7中,接口方法不允许有方法体。
String getMyJavaVersion(){
System.out.println("JAVA7");
};
}
在JAVA8中,加入了一个新性,接口可以有已经实现了的方法(称之为默认方法),但必须由static
或者 default
修饰,以下说明两种修饰,以及接口默认方法的特性。
1.1 static方法
- static修改的方法必须有方法主体。
- static修饰的接口方法只能通过本接口名称.方法名调用。
interface MyJava8Interface {
//以下方法是java8中是允许的,与类里静态方法的调用方式一样,通过接口名.方法名方法调用。
static void doSomething() {
System.out.println("Now, I am in java version 8.");
}
}
public class MyInterfaceTest {
public static void main(String[] args) {
MyJava8Interface.doSomething();
}
}
- static修饰的方法不能被实现类及子接口继承。
需要注意的是,接口静态方法不能被继承.
举个例子:
public class MyInterfaceTest {
public static void main(String[] args) {
//以下代码正确
MyJava8Interface.doSomething();
//以下代码错误,MySubInterface找不到doSomething方法。
MySubInterface.doSomething();
//以下代码错误,MySubClass找不到doSomething方法。
MySubClass.doSomething();
}
}
interface MyJava8Interface {
static void doSomething() {
System.out.println("Now, I am in java version 8.");
}
}
interface MySubInterface extends MyJava8Interface {
}
class MySubClass implements MyJava8Interface {
}
- static修改的方法不能被实现类直接调用或覆写。
这句话什么意思呢,简单理解就是不能加@Override
,开动脑筋,静态方法肯定不能被重写。
example:
public class MyInterfaceTest {
public static void main(String[] args) {
MyJava8Interface.doSomething();
MySubInterface.doSomething();
MySubClass.doSomething();
}
}
interface MyJava8Interface {
static void doSomething() {
System.out.println("Now, I am in java version 8.");
}
}
interface MySubInterface extends MyJava8Interface {
//错误示范,子接口中的同名static方法:
@Override
static void doSomething() {
System.out.println("Now, I am in java version 8, and I am am sub interface.");
}
}
class MySubClass implements MyJava8Interface {
//此处的static方法与父接口中的static方法虽同名,但是是两个完全不同的方法,请考虑static方法的调用方式
static void doSomething() {
System.out.println("Now, I am in java version 8, and I am am implement.");
}
}
1.2 default方法
- default修饰的接口方法必须有方法体。
- 接口default方法可以不被子类重写,当然,也可以被重写。
- default方法只能被实例调用(
接口名称.super.method
这种方式也属于实例调用。)。
接口中的default方法提供了这个方法的默认实现(从这个角度理解,更像一个抽象类)。需要注意的是default只能修饰接口中的方法。
public class MyInterfaceTest {
public static void main(String[] args) {
//接口default方法不能直接像static方法一样调用(这不是废话吗。)
//MyInterface.doSomething();
MySubClass mySubClass = new MySubClass();
mySubClass.doSomething();
}
}
interface MyInterface {
default void doSomething() {
System.out.println("I am default method.");
}
}
class MySubClass implements MyInterface {
//可以不重写接口中的default方法。
}
需要注意的是,如果,子接口中重写了父接口中的default
方法,同时又想在重写的方法中调用父接口的default
方法,需要以接口名称.super.method
的方式调用。
public class MyInterfaceDefaultTest {
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.doSomething();
}
}
interface MySuperInterface {
default void doSomething() {
System.out.println("I am default method.");
}
}
interface SubInterface extends MySuperInterface {
default void doSomething() {
//只能通过接口名.super.方法名调用父接口的default方法。
MySuperInterface.super.doSomething();
System.out.println("I am a sub interface.");
}
}
class SubClass implements SubInterface {
public void sayHello() {
SubInterface.super.doSomething();
System.out.println("I am a sub class and doing something...");
//以下方法是错误的,不能越级调用父接口的父接口的default方法。
MySuperInterface.super.doSomething();
}
}
2. lambda表达式
2.1 函数式接口
所谓函数式接口,即当且公当只拥有一个抽象方法(可以有多个接口默认方法)的接口。
例如:
public interface FunctionInterface {
static int doSomething() {
return 1;
}
default void doSomeThing() {
System.out.println("default do some thing.");
}
int sum(int a, int b);
}
以上接口拥有一个静态默认方法,一个默认方法,一个抽象方法sum,它是一个函数式接口,更常见的函数式接口一般为没有接口默认方法的函数式接口,具体根据业务需要。
不包含接口默认方法的函数式接口,最典型的就是java.lang包下的Runnable接口了:
package java.lang;
@FunctionalInterface
public interface Runnable {
void run();
}
2.2 @FunctionalInterface注解
在一个接口上在添加@FunctionalInterface
注解,如果这个接口添加了不只一个抽象方法,那么就会编译失败,报此接口并非函数式接口的错误,利用这个注解,可以保证我们的编写的函数式接口的正确性,此注解的使用参见上节Runnable
接口的代码。
2.3 lambda函数
在讲lambda函数之前,需要先回顾一下匿名内部类的概念:
匿名内部类体现了面向接口编程,以及里氏代换原则,匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。
new AbstractClass(参数列表) | Interface {
@Override
public type doSomeThing(Object...args) {
///do something...
}
}
在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。
回顾一下新建一个线程的几种方式……是不是能够想起来其中一种方式跟Runnable接口有关?
public class TestLambda {
public static void main(String[] args) {
String[] a = {"aaa", "bbb", "ccc"};
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (String anA : a) {
System.out.println(anA);
}
}
});
thread.start();
System.out.println("test thread.");
}
}
多线程编程时新建一个线程的方法为往在Thread
构造器中传递一个Runnable
匿名内部类。
2.3.1 lambda函数写法
lambda函数标准写法如下:
FunctionalInterface obj = (型参列表) -> {
//方法体
}
同时呢,根据函数式接口的抽象方法的不同,又可以进一步简化lambda函数的写法。
- 无参数,无返回值的写法
通过观察以下Runnable匿名内部类的新建,了解lambda函数写法的代码结构:
//非lambda写法,需要使用new关键字创建一个匿名内部类,并实现接口内的方法。
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("I am run method in anonymous inner class");
}
};
// lambda写法,之所以要把方法体写为两行,是因为要体现lambda函数的写法。
Runnable runnable1 = () -> {
String str = "I am run method in anonymous inner class";
System.out.println(str);
};
2.3.2 lambda表达式
如果抽象的方法的实现只有一句代码,可以简写为lambda表达式——即可以省略方法体的大括号:
Runnable runnable1 = () -> System.out.println("I am run method");
- 有参数无返回值的写法
@FunctionalInterface
interface MyFunctionInterface {
void sayHiAndName(String name, String hi);
}
...
MyFunctionInterface mf = (String name, String hi) -> {
System.out.println(hi + ":" + name);
}
//参数类型自动推断,lambda函数中的形参列表中,数据类型可以不写,lambda函数会自动推断参数类型,
//1. 由于以上代码方法休只有一行代码,写为lambda表达式
//2. 再省略型参数据类型,可简写为:
MyFunctionInterface mf = (name, hi) -> System.out.println(hi + ":" + name);
- 有参数有返回值的写法
有参数有返回值的情况与上面两种情况在同小异。
@FunctionalInterface
interface MyFunInterface {
String sayHiAndName(String name, String hi);
}
...
MyFunInterface myFun = (name, hi) -> {
System.out.println(hi + ":" + name);
return hi + ":" + name;
};
同样的,如果方法体只有一行代码,可以简写为lambda表达式:
@FunctionalInterface
interface MyFuncInterface {
String wo(String name);
}
...
MyFunInterface myFun = (name, hi) -> hi + ":" + name;
另外,如果形参列表有且只有一个参数时,那么,可以不加括号:
MyFuncInterface myFunc = name -> "hi" + name;
//为加深理解,将上面lambda表达式改写为lambda函数写法;
MyFuncInterface myFunc = (String name) -> {
return "hi" + name;
};