序言:我们还延续介绍是抽象类的时候所举的例子,我们知道继承抽象类的所有子类,需要将抽象类中的所有抽象方法进行重写,这样在多态的机制中,就可以将父类修改为抽象类,将draw()方法设置为抽象方法,然后每个子类都重写这个方法来处理。但这又会使代码变的冗余,同样这样的父类局限性很大,也许某个不需要draw()方法的子类也不得不重写draw()方法。如果将draw()方法放置在另一个类中,让那些需要draw()方法的类继承该类,不需要draw()方法的类继承图形类,又会产生新的问题:所有的子类都需要继承图形类,因为这些类是从图形类中导出的,同时某些类还需要draw()方法,而Java中规定类不能同时继承多个父类。为了应对这一个概念,接口的概念便出现了。
总纲: 我们可以把接口当成一种特殊的抽象类。
1.1 接口的组成接口用关键字interface修饰
public interface 接口名 { }
类实现接口用implements表示
public class 类名 implements 接口名 { }
(1)注意事项:
★接口不能实例化,但我们可以创建接口的实现类对象使用。
★接口的子类:要么重写接口中的所有抽象方法,要么子类也是抽象类。
(2)接口的成员组成:- 成员变量:
只能是常量
默认修饰符:public static final
- 构造方法:
没有,因为接口主要是扩展功能的,而没有具体存在的
- 成员方法:
只能是抽象方法
默认修饰符:public abstract
(3)代码演示:接口
public interface Inter {
public static final int NUM = 10;
public abstract void show();
}
实现类
class InterImpl implements Inter{
public void method(){
// NUM = 20;
System.out.println(NUM);
}
public void show(){
}
}
测试类
public class TestInterface {
public static void main(String[] args) {
System.out.println(Inter.NUM);
}
}
1.2类和接口的关系
- 类与类的关系
继承关系,只能单继承,但是可以多层继承
- 类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口与接口的关系
继承关系,可以单继承,也可以多继承
1.3 接口中默认方法- 格式
public default 返回值类型 方法名(参数列表) { }
- 作用
解决接口升级的问题
范例
public default void show3() {
}
注意事项
-
默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
-
public可以省略,default不能省略
-
如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
- 格式
public static 返回值类型 方法名(参数列表) { }
- 范例
public static void show() {
}
注意事项
-
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
-
public可以省略,static不能省略
- 私有方法产生原因
Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
- 定义格式
格式1: private 返回值类型 方法名(参数列表) {
}
案例: private void show() {
}
格式2: private static 返回值类型 方法名(参数列表) {
}
案例: private static void method() {
}
- 注意事项
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
3.多态 3.1 多态的相关用法 (1)多态的好处与弊端好处:提高程序的拓展性,定义方法的时候,使用父类型作为参数,在使用的时候,使用具体的子类型参数操作。
弊端:不能使用子类的特有成员。
(2)多态中的转型:向上转型:当有子类对象赋值给父类应用时,便是向上转型。多态本身就是向上转型的过程。
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。(注意前提是有向上转型的子类对象,如果是直接创建父类对象是无法向下转型的)。
为什么要有向下转型:
这是由于多态的弊端,通过向上转型不能使用子类的特有功能。
如果就想使用子类的特有功能,怎么做?
A:最原始的创建对象调用方法呗。(但是很多时候不合理。而且建了太多对象会占内存)
B: 把父类的引用强制转换为子类的引用。(向下转型)
(3)我们最好理解下这里的方法重写,以及继承的意义就会很好理解public class fu {
int age =42;
public void teach(){
System.out.println("我是一名教Java的老师");
}
}
public class zi extends fu {
int age =19;
public void teach(){
System.out.println("我也会教Java,我从我爸那学的");
}
public void student(){
System.out.println("我还是一名学生");
}
}
public class Test01 {
public static void main(String[] args) {
//向上转型 (只能调出子类的重写方法)
fu f= new zi();
f.teach(); //我也会教Java,我从我爸那学的
System.out.println(f.age); /
swimming.swim();
}
}
5 Lambda表达式
★函数式接口:仅包含一个抽象方法的接口。
★lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象。
★lambda表达式的理解:
5.1 lambda表达式调用无参抽象方法( ) -> {代码块}
这个方法 按照 这样的代码来执行
无参抽象方法在lambda表达式中使用“()”表示
//通过多态的形式
public interface Eatable {
void eat();
}
public class EatableDemo {
public static void main(String[] args) {
useEatable(() -> {
System.out.println("一天一苹果,医生远离我");
});
}
//lambda表达式不能独立执行,因此必须实现函数式接口,并且会返回一个函数式接口的对象(子类)。
private static void useEatable(Eatable e) {
e.eat();
}
}
//***************************************************************
//通过创建对象的形式
public interface Eatable {
void eat();
}
public class EatableDemo {
public static void main(String[] args) {
Eatable e = () -> {
System.out.println("一天一苹果,医生远离我");
};
}
System.out.println(e.eat());
}
5.2 lambda表达式实现有参抽象方法
public interface Addable {
int add(int x,int y);
}
public class AddableDemo {
public static void main(String[] args) {
useAddable((int x,int y) -> {
return x + y;
});
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
5.3 lambda表达式调用外部变量
(1)lambda表达式无法更改局部变量(注意局部变量的定义)
interface defin{
void method();
}
public class s2{
public static void main(String[] args) {
int value =100; //创建局部变量
defin v=()->{
int num =value -90;
value =12; //更改局部变量,报错
};
}
}
(2)lambda 表达式可以更改类成员变量
interface face2{
void method();
}
public class Server {
int value =100;
public void action(){
face2 v =()->{
value =-12;
};
System.out.println("运行前"+value); //100
v.method();
System.out.println("运行后"+value); //-12
}
public static void main(String[] args) {
Server d =new Server();
d.action();
}
}
总结:
lambda表达式只是描述了函数式接口的抽象方法是如何实现的,在抽象方法没有调用前,lambda表达式中的代码并没有被执行
5.4 lambda表达式和匿名内部类的区别- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
- 使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
- 实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成



