一、接口是什么
在Java中,接口(Interface)是一种引用数据类型,类似于类,然而它只能包含常量、方法签名和嵌套类型。接口不能包含实例变量或方法的实现(在Java 8及之后的版本,可以在接口中定义默认方法和静态方法)。接口主要用于定义类的外部行为,并允许类实现这些行为。
二、接口详细介绍
1、接口定义语法
接口的定义使用 interface 关键字,以下是一个简单的接口示例:
public interface Animal {
// 常量
int LEGS = 4; // 默认有修饰符 public static final
// 抽象方法
void makeSound();// 默认有修饰符 public abstract
void eat();// 默认有修饰符 public abstract
}
2、接口的实现
一个类通过 implements 关键字来实现接口,并提供接口中所有抽象方法的具体实现。例如:
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
@Override
public void eat() {
System.out.println("Dog is eating.");
}
}
3、接口的使用
通过接口引用实现类对象,可以实现多态性(类似于父类引用指向子类对象):
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.makeSound(); // 输出: Woof!
myDog.eat(); // 输出: Dog is eating.
}
}
三、接口的特点
1、方法公有性和抽象性
接口中的方法都默认是 public 的,而且必须是 public 的,因为 private 和 protected 修饰符都不能在接口中使用,而且如果不写访问修饰符的话,就是默认访问限制,但在接口中默认访问限制就是 public 的。所以接口中的所有方法都是 public 的,以便能够被实现类访问。
除了默认方法和静态方法之外,接口中的其他方法默认是抽象的,即没有方法体。实现接口的类必须提供这些抽象方法的实现。
interface Animal {
void cry();// 默认有修饰符 public abstract
public abstract void eat();// 这里的 public abstract 是没有必要的
}
可以看到图中的 public abstract 颜色是灰色,这说明在这里写 public abstract 是没有必要的。
2、变量默认为公有静态常量
接口中定义的变量默认是 public static final 修饰的,即静态常量,所有实现该接口的类都可以访问这些常量。因为接口中不能使用 private 和 protected 修饰符,所以接口中的属性也都是公有的,而且必定是公有静态常量。
public class Example {
public static void main(String[] args) {
System.out.println(Animal.a);// 直接使用接口名访问
//Animal.a++;// 会报错
}
}
interface Animal {
int a = 0;// 默认有修饰符 public static final
}
可以看到图中的 public static final 都是灰色的,说明这里写 public static final 是没有必要的。
所以说对于接口中的成员属性能直接使用接口名访问:
InterfaceName.variable
3、不能被实例化
接口不能被实例化,所以不支持构造函数。尝试在接口中写构造函数会出现报错:
4、默认方法和静态方法
从Java 8开始,接口可以包含默认方法和静态方法:
默认方法(default):默认方法可以有方法体。默认方法不需要实现接口的类去实现,但是它们仍然是 public 的。使用 default 关键词修饰的是默认方法。
静态方法(static):静态方法也有方法体,不需要实现接口的类实现,这些方法也是 public 的,并且可以在接口本身上调用。使用 static 修饰的为静态方法。
interface Animal {
// 默认方法
default void eat() {
System.out.println("...");
}
// 静态方法
static void foo() {
System.out.println("~");
}
}
默认方法和静态方法都是默认且一定是 public 的,所以在它们前面显式地添加 public 修饰符是没有必要的。
对于默认方法,实现这个接口的类可以重写它:
interface Animal {
// 默认方法
default void eat() {
System.out.println("...");
}
// 静态方法
static void foo() {
System.out.println("~");
}
}
class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃鱼...");
}
}
5、接口的抽象方法的实现
一个普通类实现接口,则需要将接口中的所有抽象方法实现。如果是一个抽象类实现接口,则是否实现抽象方法都行。
因为抽象类中是允许有抽象方法的,但是普通类中不允许有抽象方法。
interface Interface {
void foo();
}
abstract class AbstractClass implements Interface{
// 抽象类实现接口,可以不实现接口的抽象方法
}
6、接口之间的继承
接口不能继承某个类,但是接口可以继承接口。
interface InterfaceA {
void fun1();
}
interface InterfaceB extends InterfaceA {
void fun2();
}
Java接口支持多重继承,一个接口可以继承多个其他接口。这与类的继承不同,类在Java中只能单继承(即一个类只能继承一个父类)。
interface InterfaceA {
void fun1();
}
interface InterfaceB {
void fun2();
}
interface InterfaceC extends InterfaceA, InterfaceB{
}
示例:
// 定义基本接口
interface Animal {
void eat();
}
// 定义一个继承自 Animal 的接口
interface Mammal extends Animal {
void giveBirth();
}
// 定义一个继承自 Mammal 的接口
interface Dog extends Mammal {
void bark();
}
// 实现 Dog 接口的类
class Bulldog implements Dog {
@Override
public void eat() {
System.out.println("Bulldog is eating.");
}
@Override
public void giveBirth() {
System.out.println("Bulldog gives birth to puppies.");
}
@Override
public void bark() {
System.out.println("Bulldog barks.");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
Bulldog myDog = new Bulldog();
myDog.eat(); // 输出: Bulldog is eating.
myDog.giveBirth(); // 输出: Bulldog gives birth to puppies.
myDog.bark(); // 输出: Bulldog barks.
}
}
四、多重接口实现
Java中的类不能多重继承(一个子类继承多个基类),但一个类可以实现多个接口,这样就实现了多重继承的效果。这种特性称为多重接口实现。
1、语法与要求
一个类实现多个接口时,接口名之间用逗号分隔。例如:
public class MyClass implements InterfaceA, InterfaceB {
// 实现接口中的所有方法
}
实现多个接口时,类必须实现所有接口中定义的抽象方法。如果某个接口有默认方法,则可以选择重写它或直接使用。
2、示例
// 定义接口A
interface InterfaceA {
void methodA();
}
// 定义接口B
interface InterfaceB {
void methodB();
}
// 定义一个类实现这两个接口
public class MyClass implements InterfaceA, InterfaceB {
@Override
public void methodA() {
System.out.println("Method A implemented.");
}
@Override
public void methodB() {
System.out.println("Method B implemented.");
}
// 其他类的方法
public void additionalMethod() {
System.out.println("Additional method.");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
MyClass myObject = new MyClass();
myObject.methodA(); // 输出: Method A implemented.
myObject.methodB(); // 输出: Method B implemented.
myObject.additionalMethod(); // 输出: Additional method.
}
}
3、方法冲突
如果多个接口中定义了同名的方法,且没有默认实现,类必须提供具体实现。例如:
interface InterfaceA {
void method();
}
interface InterfaceB {
void method();
}
public class MyClass implements InterfaceA, InterfaceB {
@Override
public void method() {
System.out.println("Implemented method from both interfaces.");
}
}
4、默认方法冲突
如果多个接口中有相同方法标签的默认方法,实现类必须重写该方法以消除歧义。
interface InterfaceA {
default void foo() {
System.out.println("A");
}
}
interface InterfaceB {
default void foo() {
System.out.println("A");
}
}
class C implements InterfaceA, InterfaceB {
@Override
public void foo() { // 重写方法以消除歧义。必须重写这个方法,不然会报错
System.out.println("C");
}
}
如果方法标签不同,则可形成重载,则不需要实现类重写来消除歧义。
interface InterfaceA {
default void foo() {
System.out.println("A");
}
}
interface InterfaceB {
default void foo(int a) {
System.out.println("A");
}
}
class C implements InterfaceA, InterfaceB {
// 这里即使不重写,也不会报错,因为上面的两个方法不存在歧义
}
五、实现接口和继承类
1、继承单一性 vs 接口多实现:
继承类:Java中一个类只能继承一个父类(单继承)。这是因为Java不支持多重继承,以避免菱形继承问题。
实现接口:一个类可以实现多个接口。这允许Java类通过实现多个接口来模拟多重继承。
2、使用场景:
继承类:用于表示一种“is-a”关系,表示子类是一种特殊的父类类型。
实现接口:用于表示一种“can-do”关系,表示类能够执行接口定义的行为。
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public abstract void cry();
}
interface ShakeHands {
void shakeHands();
}
interface SpinningAround {
void spinningAround();
}
class Dog extends Animal implements ShakeHands, SpinningAround {
public Dog(String name) {
super(name);
}
@Override
public void cry() {
System.out.println("汪汪~");
}
@Override
public void shakeHands() {
System.out.println("握手~");
}
@Override
public void spinningAround() {
System.out.println("转圈圈~");
}
}
Dog 类继承自抽象类 Animal 表示狗是一种动物,Dog 实现了 ShakeHands 和 SpinningAround 两个接口,说明狗通过练习可以握手和转圈圈。
3、价值
继承类:解决代码的复用性和可维护性。
实现接口:接口设计好各种规范,实现类来实现这些规范的方法,可以更加灵活。
4、接口对于代码的解耦
接口在Java中用于定义一组方法的规范,允许不同的类实现这些方法,从而实现代码的解耦。
代码解耦意味着不同的模块或类之间的依赖关系减少,使得修改一个部分的代码时,不会影响到其他部分,从而提高了系统的灵活性和可维护性。
// 定义支付接口
interface Payment {
void pay(double amount);
}
// 实现信用卡支付
class CreditCardPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Credit Card.");
}
}
// 实现支付宝支付
class AlipayPayment implements Payment {
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Alipay.");
}
}
// 使用支付接口的用户类
class ShoppingCart {
private Payment payment;
public ShoppingCart(Payment payment) {
this.payment = payment;
}
public void checkout(double amount) {
payment.pay(amount);
}
}
// 测试代码
public class Main {
public static void main(String[] args) {
Payment creditCardPayment = new CreditCardPayment();
ShoppingCart cart1 = new ShoppingCart(creditCardPayment);
cart1.checkout(100.0); // 使用信用卡支付
Payment alipayPayment = new AlipayPayment();
ShoppingCart cart2 = new ShoppingCart(alipayPayment);
cart2.checkout(150.0); // 使用支付宝支付
}
}
ShoppingCart 类与具体的支付方式(如CreditCardPayment或AlipayPayment)没有直接的依赖关系。你可以轻松地在不修改 ShoppingCart 类的情况下,添加新的支付方式,只需实现 Payment 接口即可。
六、接口的多态
接口多态意味着可以使用接口类型的引用来指向任何实现该接口的类对象。这样可以在程序中灵活切换不同实现,达到多态的效果。
1、多态数组
可以将接口类型引用数组的元素指向不同的实现类的对象。
package com.study.episode10.Interface.Example3;
public class Example {
public static void main(String[] args) {
InterfaceCry[] ic = new InterfaceCry[2];
ic[0] = new Dog();
ic[1] = new Cat();
for(InterfaceCry i : ic) {
i.cry();
}
}
}
interface InterfaceCry {
void cry();
}
class Dog implements InterfaceCry {
@Override
public void cry() {
System.out.println("Woof~");
}
}
class Cat implements InterfaceCry {
@Override
public void cry() {
System.out.println("Meow~");
}
}
运行结果:
2、多态参数
利用接口类型作为方法参数,可以将任何实现了该接口的类的对象作为参数传入。
package com.study.episode10.Interface.Example3;
public class Example {
public static void animalCry(InterfaceCry ic) {
ic.cry();
}
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
animalCry(dog);
animalCry(cat);
}
}
interface InterfaceCry {
void cry();
}
class Dog implements InterfaceCry {
@Override
public void cry() {
System.out.println("Woof~");
}
}
class Cat implements InterfaceCry {
@Override
public void cry() {
System.out.println("Meow~");
}
}
运行结果:
3、接口的多态传递
public class Example {
public static void main(String[] args) {
Interface1 i1 = new A();
}
}
interface Interface1 {}
interface Interface2 extends Interface1 {}
class A implements Interface2 {}
这个示例展示了Java中接口的继承和多态性。具体来说,Interface2
继承自 Interface1
,而类 A
实现了 Interface2
。这使得 A
也间接实现了 Interface1
。这种结构提供了接口的传递性,即可以使用Interface1
类型的引用来指向实现了Interface2
的类的对象。
4、接口相关的向下转型和向上转型
在进行过向上转型后,也就是接口类型的引用指向实现类的对象,这时,只能通过引用变量访问 接口中有的成员,不能访问实现类的特有成员,如果想要访问实现类的特有成员,则需要进行向下转型:
public class Example {
public static void main(String[] args) {
Interface i = new ImplementingClass();
System.out.println(i.specificField);// 接口特有字段,接口中有的成员,所以能访问
i.commonMethod();// 共有成员,接口中也有,所以能调用
// i.specificMethod; // 这里就会报错,因为接口中没有这个成员,
// 这是实现类的特有成员,如果要访问,则需要向下转型
((ImplementingClass)i).specificMethod();// 向下转型后访问实现类的特有成员
}
}
interface Interface {
String specificField = "Interface specific field~";
void commonMethod();
}
class ImplementingClass implements Interface {
@Override
public void commonMethod() {
System.out.println("common method~");
}
public void specificMethod() {
System.out.println("implementing class specific method~");
}
}
七、补充
1、名称冲突
interface I {
int x = 0;// public static final int x = 0;
}
class A {
int x = 1;
}
class B extends A implements I {
public void prtX() {
System.out.println(x);// 这里会出现歧义,会报错,说x是模棱两可的,没有指明x
// 因为B类从A类中继承了一个x,从I接口中实现时得到了一个静态常量x
// 这里直接写一个x就相当于this.x,这样是不清晰的,因为B类中有了两个x
// 需要指明是哪个x
}
}
这是因为接口 I
和类 A 中都定义了一个同名的字段 x
,这会导致类 B
中引用 x
时产生歧义。Java中解决这种字段冲突的办法是明确指定你想要访问的 x
的来源。
访问接口中的 x
如果想要在 prtX()
方法中访问接口 I
中定义的 x
,可以这样修改:
public void prtX() {
System.out.println(I.x);
}
因为接口中的字段都是静态的,可以直接使用接口名加字段名访问。
访问父类中的 x
如果你想要在 prtX()
方法中访问类 A
中定义的 x
,可以这样修改:
public void prtX() {
System.out.println(super.x);
}
标签:Java,void,System,接口,println,public,out
From: https://blog.csdn.net/stewie6/article/details/143482361