接口的由来
兔子不会游泳,游泳这个方法放在动物类中,显然是不合适的。而猫和狗都有游泳这个方法,那么如果猫和狗各自写自己的游泳方法,则可能导致这个方法不统一。
于是就可以定义一个接口,在其中规定了游泳这个方法的书写规则,让猫和狗各自去实现。
而看家这个方法又是狗这个类所特有的,就不要放进接口中。
所以接口放的就是部分类公共的方法,给这些方法规定一个统一的规则。
接口定义的不是继承的规则,只是一个功能,或者说是一种规则,是对行为的抽象。
接口的定义和形式
接口用关键字 interface 来定义:public interface 接口名 {}
接口不能实例化。
接口和类之间是实现关系,通过 implements 关键字来表示:public class 类名 implements 接口名 {}
接口的子类也叫实现类。实现类要么重写接口中的所有抽象方法,要么实现类本身也是一个抽象类。一般都是重写接口中的所有抽象方法。
接口和类之间的实现关系,可以是单实现,也可以是多实现。
多实现:public class 类名 implements 接口名 1, 接口名 2 {}
实现类可以在继承一个类的同时实现多个接口:public class 类名 extends 父类 implements 接口名 1, 接口名 2 {}
新建接口文件:
如果新建接口文件时,忘了选择 interface 选项,可以在建立完文件后将 class 改为 interface,文件图标也会跟着改过来。
练习:
编写带有接口和抽象类的标准 Javabean 类
青蛙 属性:名字,年龄 行为:吃虫子,蛙泳
狗 属性:名字,年龄 行为:吃骨头,狗刨
兔子 属性:名字,年龄 行为:吃胡萝卜
Javabean 类:
package demo;
public abstract class Animal {
String name;
int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
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 abstract void eat();
}
接口:
package demo;
public interface Swim {
public abstract void swim();
}
Javabean 类:
package demo;
public class Dog extends Animal implements Swim {
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头。");
}
@Override
public void swim() {
System.out.println("狗在狗刨。");
}
}
Javabean 类:
package demo;
public class Frog extends Animal implements Swim {
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void swim() {
System.out.println("青蛙在蛙泳。");
}
@Override
public void eat() {
System.out.println("青蛙在吃虫子。");
}
}
Javabean 类:
package demo;
public class Rabbit extends Animal {
public Rabbit() {
}
public Rabbit(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("兔子在吃胡萝卜。");
}
}
测试类:
package demo;
public class Test {
public static void main(String[] args) {
Frog f = new Frog("小青", 23);
System.out.println(f.getName() + ", " + f.getAge());
f.eat();
f.swim();
Rabbit r = new Rabbit("小白", 21);
System.out.println(r.getName() + ", " + r.getAge());
r.eat();
// r.swim();
}
}
执行结果:
小青, 23
青蛙在吃虫子。
青蛙在蛙泳。
小白, 21
兔子在吃胡萝卜。
接口中成员的特点
成员变量
只能是常量。
默认修饰符:public static final。默认指的是就算不写,也默认是存在的。接口是一种规则,规则是不能被改变的,所以接口里的成员变量都是常量,所以用 final 修饰。用 static 来修饰是为了方便调用,用 接口名.常量名
就可以调用。public 表示公共的,即所有的地方都可以使用接口里的常量。
构造方法
没有构造方法。因为接口不能创建对象,并且接口也不需要给子类的成员变量赋值。
成员方法
在 JDK 7 之前,只能写抽象方法,默认修饰符为 public abstract,即哪怕不写虚拟机也会自动帮你加上。
JDK 8:接口中可以定义有方法体的方法。
JDK 9:接口中可以定义私有方法。
程序示例:
接口:
package demo3;
public interface Inter {
int a = 10;
}
测试类:
package demo3;
public class Test {
public static void main(String[] args) {
System.out.println(Inter.a); // 打印 10,此处是用 接口名.变量名 的形式来调用的,由此可以说明是用 static 来修饰的。
// Inter.a = 20; // 报错:cannot assign a value to final variable 'a' 由此说明是用 final 修饰的。
}
}
接口和类之间的关系:
类和类的关系:继承关系,只能单继承,不能多继承,但是可以多层继承
类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口. 如果一个类实现了多个接口, 那么就要重写全部的抽象方法.
接口和接口的关系:继承关系,可以单继承,也可以多继承。如果实现类实现了最下面的子接口的话,那么就需要重写所有的抽象方法。
如果实现多个接口时,多个接口有相同的方法,那么在子类中只需要重写一次重复的方法。
接口 1:
public interface Inter1 {
public abstract void method1();
public abstract void method_abc();
}
接口 2:
public interface Inter2 {
public abstract void method2();
public abstract void method_abc();
}
接口 3:
public interface Inter3 extends Inter2, Inter1 {
public abstract void method3();
}
实现类:
public class InterImpl implements Inter3{ // 该类实现了最下面的子接口,需要重写所有的方法
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method_abc() { // 重复的方法只需要实现一遍
}
@Override
public void method3() {
}
}
接口 3 继承自接口 1 和接口 2,实现类实现了接口 3,则实现类中要重写三个接口的全部方法。
练习:
编写带有接口和抽象类的标准 Javabean 类
我们现在有乒乓球运动员和篮球运动员,乒乓球教练和篮球教练。
为了出国交流,跟乒乓球相关的人员都需要学习英语。
请用所有知识分析,在这个案例中,哪些是具体类,哪些是抽象类,哪些是接口?
乒乓球运动员:姓名,年龄,学打乒乓球,说英语
篮球运动员:姓名,年龄,学打篮球
乒乓球教练:姓名,年龄,教打乒乓球,说英语
篮球教练:姓名,年龄,教打篮球
Javabean 类:
package demo5;
// 因为现在我不想让外界去直接创建人的对象
// 因为直接创建顶层父类人的对象此时是没有意义的
// 所以我就把他写为抽象的。
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
package demo5;
public abstract class Sporter extends Person {
public Sporter() {
}
public Sporter(String name, int age) {
super(name, age);
}
public abstract void study();
}
package demo5;
public abstract class Coach extends Person {
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
package demo5;
public class PingPangSporter extends Sporter implements English {
public PingPangSporter() {
}
public PingPangSporter(String name, int age) {
super(name, age);
}
@Override
public void speakEnglish() {
System.out.println("乒乓球运动员说英语。");
}
@Override
public void study() {
System.out.println("乒乓球运动员学习怎么打乒乓球。");
}
}
package demo5;
public class BasketballSporter extends Sporter {
public BasketballSporter() {
}
public BasketballSporter(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员在学打篮球。");
}
}
package demo5;
public class PingPangCoach extends Coach implements English {
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练在教别人打乒乓球。");
}
@Override
public void speakEnglish() {
System.out.println("乒乓球教练在学习英语。");
}
}
package demo5;
public class BasketballCoach extends Coach {
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练在教别人打篮球。");
}
}
接口类:
package demo5;
public interface English {
public abstract void speakEnglish();
}
测试类:
package demo5;
public class Test {
public static void main(String[] args) {
PingPangSporter pps = new PingPangSporter("福原爱", 23);
System.out.println(pps.getName() + ", " + pps.getAge());
pps.study();
pps.speakEnglish();
}
}
执行结果:
福原爱, 23
乒乓球运动员学习怎么打乒乓球。
乒乓球运动员说英语。
JDK 8 开始接口中新增的方法
JDK 7 之前,接口中只能定义抽象方法。
JDK 8 的新特性:接口中可以定义有方法体的方法,分两种,一种是默认的方法,一种是静态的方法。
JDK 9 的新特性:接口中可以定义私有方法,即用 private 修饰。
Java 为什么这么设计?
按照 JDK 7 的规定,如果接口变了,那么所有的实现类都需要进行修改。
接口中有方法体的方法,是在接口升级时,为了兼容性而使用的。接口添加了有方法体的方法后,实现类不需要立马重写,不重写也不会报错,当这个实现类要用到这个方法的时候再去重写即可。
JDK 8 开始,允许在接口中定义默认方法,用关键字 default 修饰。作用就是解决接口升级带来的兼容性问题。
接口中默认方法的定义格式:
public default 返回值类型 方法名 (参数列表) {}
例如:
public default void show () {}
默认方法不是抽象方法,不会被强制重写,但是如果被重写,重写的时候要去掉 default 关键字。
public 是默认修饰符,可以省略,但是 default 不能被省略。
如果一个实现类实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对这个方法进行重写。
程序示例:
接口:
package demo6;
public interface Inter {
public abstract void method();
public default void show() {
System.out.println("接口的默认方法 ---- show");
}
}
实现:
package demo6;
public class ImplInter implements Inter{
@Override
public void method() {
System.out.println("实现类的 method 方法。");
}
}
测试类:
package demo6;
public class Test {
public static void main(String[] args) {
ImplInter ii = new ImplInter();
ii.method();
ii.show();
}
}
执行结果:
实现类的 method 方法。
接口的默认方法 ---- show
程序示例:
接口:
package demo6;
public interface Inter {
public abstract void method();
public default void show() {
System.out.println("接口的默认方法 ---- show");
}
}
实现类:
package demo6;
public class ImplInter implements Inter {
@Override
public void method() {
System.out.println("实现类的 method 方法。");
}
@Override
public void show() {
System.out.println("重写接口的默认方法。");
}
}
测试类:
package demo6;
public class Test {
public static void main(String[] args) {
ImplInter ii = new ImplInter();
ii.method();
ii.show();
}
}
执行结果:
实现类的 method 方法。
重写接口的默认方法。
如果两个接口中有同名的默认方法, 则实现了这两个接口的类必须重写这个方法, 否则类的对象在调用这个默认方法时, 不知道是调用哪个接口中的默认方法. 程序示例:
第一个接口:
public interface InterA {
public abstract void methodA();
public default void show() {
System.out.println("InterA 里面的 show()");
}
}
第二个接口:
public interface InterB {
public abstract void methodB();
public default void show() {
System.out.println("InterB 里面的 show()");
}
}
实现类:
public class InterImpl implements InterA,InterB{
@Override
public void methodA() {
}
@Override
public void show() { // 这个 default 方法被强制重写,因为两个接口都有这个方法,
System.out.println("实现类中的重写的默认方法");
}
@Override
public void methodB() {
}
}
测试类:
public class Test {
public static void main(String[] args) {
InterImpl inter = new InterImpl();
inter.show(); // 实现类中的重写的默认方法
}
}
JDK 8 开始允许在接口中定义静态方法,用 static 修饰。
接口中的静态方法的定义格式:
public static 返回值类型 方法名 (参数列表) {}
例如:
public static void show () {}
接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或对象名来调用。
public 可以省略,但是 static 不能被省略。如果 static 省略不写, 则会被当为抽象方法, 因为 abstract 是默认的, 不写就默认是 abstract.
静态方法不需要重写.
程序示例:
接口代码:
public interface Inter {
// 接口中的抽象方法
public abstract void method();
// 接口中的静态方法
public static void show() {
System.out.println("接口中的静态方法");
}
}
实现类的代码:
public class InterImpl implements Inter{
// 重写接口的抽象方法
@Override
public void method() {
System.out.println("InterImpl 重写的抽象方法.");
}
// 接口的静态方法不需要重写
// 这个不是重写, 只是实现类里面有一个和接口里面同名的静态方法
// 如果调用时, 用 InterImpl.show(), 则调用的是子类里面的 show() 方法,
// 用 Inter.show(), 则调用的是接口里面的 show() 方法
public static void show(){
System.out.println("InterImpl 的 show() 方法.");
}
}
测试类:
public class Test {
public static void main(String[] args) {
// 调用接口中的show()方法
Inter.show(); // 接口中的静态方法
// 调用实现类中的静态方法
InterImpl.show(); // InterImpl 的 show() 方法.
}
}
私有方法分两种:普通的和静态的。
接口中私有方法的定义格式:
普通私有方法,是给默认方法服务的:
格式:private 返回值类型 方法名(参数列表){}
范例:private void show(){}
加了 static 的私有方法,即静态的私有方法,是给静态方法服务的:
格式:private static 返回值类型 方法名(参数列表){}
范例:private static void method(){}
程序示例:
接口:
package demo6;
public interface Inter {
public default void show1() {
System.out.println("show1方法开始执行。");
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
public default void show2() {
System.out.println("show2方法开始执行。");
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
}
显然两个方法中有重复代码,可以进行抽取。
package demo6;
public interface Inter {
public default void show1() {
System.out.println("show1方法开始执行。");
show3();
}
public default void show2() {
System.out.println("show2方法开始执行。");
show3();
}
public default void show3() {
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
}
抽取出来之后在 show1() 和 show2() 中进行调用这个抽取出来的 show3() 即可。
但是这个方法不希望被外面的其他类调用,因为毫无意义。因此规定其为 private 并删掉 default。
package demo6;
public interface Inter {
public default void show1() {
System.out.println("show1方法开始执行。");
show3();
}
public default void show2() {
System.out.println("show2方法开始执行。");
show3();
}
private void show3() { // 这就是普通的私有方法, 即没有用 static 修饰的私有方法
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
}
针对静态方法则需要静态私有方法为其服务:
package demo6;
public interface Inter {
public static void show1() {
System.out.println("show1方法开始执行。");
show4();
}
public static void show2() {
System.out.println("show2方法开始执行。");
show4();
}
private void show3() {
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
private static void show4() { // 加 static 修饰的静态私有方法
System.out.println("记录程序在开始执行之后的各种细节,这里有100行代码。");
}
}
当一个方法的形参是接口时, 可以传递接口所有实现类的对象, 这种方式称为接口多态.
标签:void,System,接口,println,public,out From: https://www.cnblogs.com/Chengkai730/p/18409074