文章目录
- 抽象类和接口
- 抽象类
- 抽象类的概念
- 抽象类的语法
- 抽象类的特性
- 抽象类的意义
- 接口
- 接口的概念
- 接口的语法
- 接口的特性
- 接口的使用
- 实现多个接口
- 接口与多态
- 接口间的继承
- 抽象类和接口的区别
抽象类和接口
抽象类
抽象类的概念
Java使用类实例化对象来描述现实生活中的实体,不过,不是所有的类都能描述实体的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类(准确来说,我们可以将这样的类设计成抽象类)。
以画图形为例,有如下代码:
//Shape.java
public class Shape {
public void draw() {
System.out.println("画一个图形");
}
}
//Pentagram.java
public class Pentagram extends Shape {
@Override
public void draw() {
System.out.println("五角星☆");
}
}
//Rotundity.java
public class Rotundity extends Shape {
@Override
public void draw() {
System.out.println("圆形⭕");
}
}
我们在实现Shape
类的draw
方法时,由于Shape
是一个宽泛的概念,不是一个具体的图形,所以我们实际上是没办法实现的,上代码中的实现也没有实际的意义,因此,我们可以将Shape
类设计成抽象类,而对于Shape
类中的draw
方法,我们可以将它设计成抽象方法.
抽象类的语法
Java中,被 abstract
修饰的类称为抽象类,抽象类中被 abstract
修饰的方法称作抽象方法。
抽象方法不用给出具体的实现。(这是强制的,不能给出方法体)
将上面的Shape
类设计成抽象类,draw
方法设计成抽象方法:
//Shape.java
public abstract class Shape {
public abstract void draw();
}
注意: 抽象类也是类,抽象类也可以有普通的成员方法、成员变量或构造方法。
抽象类与普通类的区别是抽象类有abstract
修饰,且可以存在抽象方法(不存在也可以)。
抽象类的特性
-
抽象类不能实例化对象
public class Test { public static void main(String[] args) { Shape shape = new Shape();//实例化抽象类,报错 } }
抽象类虽然不能实例化,但是抽象类的引用可以引用其子类的对象
//Shape.java
public abstract class Shape {
public abstract void draw();
}
//Pentagram.java
public class Pentagram extends Shape {
@Override
public void draw() {
System.out.println("五角星☆");
}
}
//Rotundity.java
public class Rotundity extends Shape {
@Override
public void draw() {
System.out.println("圆形⭕");
}
}
//Test.java
public class Test {
public static void func(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
Pentagram pentagram = new Pentagram();
Rotundity rotundity = new Rotundity();
Shape[] shapes = {pentagram, rotundity};
//向上转型
for(Shape shape : shapes){
func(shape);
}
}
}
-
抽象类必须被继承,且继承抽象类的子类必须重写抽象类中的抽象方法,如果不想重写抽象方法,需要加
abstract
修饰。不过,一旦这个类被一个普通的类继承,则此普通类必须重写所有的抽象方法。public abstract class Test { public abstract void func1(); } abstract class A extends Test { public abstract void func2(); } class B extends A { @Override public void func1() { System.out.println("重写抽象类Test的抽象方法"); } @Override public void func2() { System.out.println("重写抽象类A的抽象方法"); } }
抽象类
A
继承了抽象类Test
,普通类B
继承了A
,那么普通类B
必须重写A
类和Test
类中的所有抽象方法。 -
由于抽象方法必须被重写,所以抽象方法不能被
private
、final
、static
修饰 -
抽象类中可以不包含抽象方法,但抽象方法必须在抽象类中
public class Test { public abstract void func(); }
-
抽象类中可以存在构造方法,用于子类实例化时初始化父类的成员
//Shape.java public abstract class Shape { public int a; //抽象类的构造方法 public Shape(int a) { this.a = a; } public abstract void draw(); } //Pentagram.java public class Pentagram extends Shape { public Pentagram(int a) { super(a); } @Override public void draw() { System.out.println("五角星☆"); } } //Rotundity.java public class Rotundity extends Shape { public Rotundity(int a) { super(a); } @Override public void draw() { System.out.println("圆形⭕"); } } //Test.java public class Test { public static void main(String[] args) { Shape shape = new Pentagram(20); System.out.println(shape.a); } }
打印结果是:
抽象类的意义
我们可能会有一个疑问,普通类也可以被继承,普通的成员方法也可以被重写,为什么要用抽象类和抽象方法呢?
抽象类中的抽象方法如果没有被重写,编译器会报错。 是的,使用抽象类和抽象方法的好处就是多了一层编译器的校验,能帮助我们检查抽象方法是否被重写。
抽象类不能被实例化。 很多引用场景,比如上面的画图形场景,其实际工作不应该由父类完成,而应由子类完成,此时误用父类编译器会报错。
总的来说,使用抽象类就是为了预防出错,并让我们尽早发现问题,充分利用编译器的校验,在实际开发中是非常有意义的。
接口
接口的概念
接口(英文:Interface),在JAVA编程语言中是一个抽象(引用)类型,是抽象方法的集合。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
我们很容易联想到实际生活中电脑的USB接口,它是一个标准,符合这个标准,就可以与电脑交互(可以插U盘、鼠标等)。
Java中的接口也是公共行为的规范,我们在实现时,只要符合标准,就可以通用。所以,接口可以看作是:多个类的公共规范,是一种引用类型
接口的语法
接口的定义与类的定义类似,只需要把关键字class
换成interface
接口只能被public
、abstract
修饰,abstract
默认不显性显示,如果省略public
则使用默认的访问权限
public interface 接口名 {
//...
}
- 接口的命名一般是以
I
开头 - 接口的命名一般使用形容词词性的单词,这样就能显示出接口的标准
接口的特性
-
接口是一种引用类型,但是不能直接实例化对象(接口实际上比抽象类更加抽象),不过接口可以引用实现该接口的类的对象(向上转型)
public static void main(String[] args) { ITest iTest = new ITest();//报错,不能直接实例化接口 }
interface ITest { //待实现的方法 void func(); } class A implements ITest { @Override public void func() { System.out.println("类A实现的func"); } } class NewTest { public static void main(String[] args) { //接口引用了A类对象 ITest iTest = new A(); } }
-
接口可以定义变量,且接口的成员变量都是
public
、static
、final
修饰的,且接口定义的变量必须手动初始化即使我们不写修饰符,成员变量也默认被
public
、static
、final
修饰public abstract interface ITest { //下面定义的变量都是由 public static final 修饰的(不论是否显性书写或写全) public int a = 1; static int b = 2; final int c = 3; int d = 4; public static int e = 5; static final int f = 6; public final int g = 7; public static final int h = 8; }
-
接口可以定义方法,接口的方法默认是
public
、abstract
修饰的,属于抽象方法,不需要方法体。如果想要在接口中具体实现一个方法,那么它必须是由default
或static
修饰public interface ITest { //以下写法的修饰符都是 public abstract(无论是否写或写全) void func0(); public abstract void func1(); public void func2(); abstract void func3(); //被default或static修饰的方法有具体的实现 default void func4() { System.out.println("接口中被default修饰的方法有方法体"); } static void func5() { System.out.println("接口中被static修饰的方法有方法体"); } }
-
实现接口的类,必须重写接口中的所有抽象方法,如果此类不想重写接口中的抽象方法,则它必须由
abstract
修饰,一旦此类被另一个非抽象类继承,那么这个非抽象类必须重写所有未被重写的抽象方法interface IA { //接口抽象方法 void abfunc1(); } abstract class A implements IA { public void myA() { System.out.println("A类特有的方法"); } //A类抽象方法 abstract void abfunc2(); } class B extends A { @Override public void abfunc1() { System.out.println("重写IA接口中的抽象方法"); } @Override void abfunc2() { System.out.println("重写B类中的抽象方法"); } }
-
接口中不能有静态代码块、构造代码块或构造方法
-
由于接口的成员方法默认都有
public
修饰,所以重写后的方法必须是public
修饰(重写后的方法的访问权限要大于等于父类) -
上述表述的重写严格来说不准确,不过,语法要求与重写一致(这里不必纠结,只要记住抽象方法要被重写即可)
-
接口文件的文件名必须与接口名相同,且接口文件编译后也是
.class
为后缀的字节码文件
接口的使用
接口不能直接实例化对象,接口的使用需要一个 “实现类” 来 实现 接口中的方法,要用到关键字implements
public class 类 implements 接口 {
//...
}
接口与类之间implements
是实现关系,父类和子类之间extends
是继承关系
interface ITest {
//待实现的方法
void func();
}
class A implements ITest {
@Override
public void func() {
System.out.println("类A实现的func");
}
}
继承表达的是is A
的关系,如狗是动物;接口表达的含义是具有xxx特性
,如狗会跑
实现多个接口
Java中不支持多继承,不过一个类可以实现多个接口,这个类需要实现所有接口的抽象方法:
//ISwimming.java
public interface ISwimming {
void swim();
}
//IRunning.java
public interface IRunning {
void run();
}
//Dog.java
public class Dog implements IRunning, ISwimming {
public String name;
public int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void run() {
System.out.println(this.name + "正在跑");
}
@Override
public void swim() {
System.out.println(this.name + "正在游泳");
}
}
- 一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类
一个类可以继承一个父类并同时实现多个接口:
//Animal.java
public class Animal {
public String name;
public int age;
}
//IRunning.java
public interface IRunning {
void run();
}
//ISwimming.java
public interface ISwimming {
void swim();
}
//Dog.java
public class Dog extends Animal implements IRunning, ISwimming {
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void run() {
System.out.println(this.name + "正在跑");
}
@Override
public void swim() {
System.out.println(this.name + "正在游泳");
}
}
- 继承与实现都存在时,必须先写
extends
继承,后写implements
实现 - 对于上面的代码,理解一点:只要一个类实现了如
IRunning
接口,它就可以run()
,而不必是动物。(有些废话,主要是强调接口使用)
接口与多态
Java中,接口可以实现多态,看如下代码:
//IEat.java
public interface IEat {
void eat();
}
//Animal.java
public class Animal {
public String name;
public int age;
}
//Cat.java
public class Cat extends Animal implements IEat {
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println(this.name + "正在吃猫粮...");
}
}
//Dog.java
public class Dog extends Animal implements IEat {
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println(this.name + "正在吃狗粮...");
}
}
//Test.java
public class Test {
//体现多态
public static void testEat(IEat iEat) {
iEat.eat();
}
public static void main(String[] args) {
testEat(new Dog("大哈", 2));//匿名对象
testEat(new Cat("小猫", 3));
}
}
对于上述代码,我们给同一个方法testEat
传入不同的对象Cat
和Dog
,打印结果不同:
接口间的继承
接口的继承相当于把所有的接口合并在一起。
接口间可以实现继承
//IExcellentQuality.java
public interface IExcellentQuality {
void func1();
}
//IKind.java
public interface IKind extends IExcellentQuality {
void func2();
}
上述代码逻辑上就是:善良的 是 品质优良的
-
一个接口继承了另一个接口,子接口不实现父接口的抽象方法,而当一个非抽象类实现了子接口,那么这个类要重写子接口和父接口所有的抽象方法
public class Person implements IKind { @Override public void func2() { System.out.println("kind"); } @Override public void func1() { System.out.println("excellent quality"); } }
Java中,类与类之间不可以实现多继承,但接口与接口之间可以实现多继承
比如以下例子,两栖动物既可以游泳又可以行走:
//IRunning.java
public interface IRunning {
void run();
}
//ISwimming.java
public interface ISwimming {
void swim();
}
//IAmphibious.java
public interface IAmphibious extends ISwimming, IRunning {
void func();
}
【总结】
- 一个接口继承了另一个接口,子接口不实现父接口的抽象方法,而当一个非抽象类实现了子接口,那么这个类要重写子接口和父接口所有的抽象方法
- 接口与接口之间可以实现多继承
抽象类和接口的区别
抽象类中可以包含普通的方法和字段,这些字段和方法可以被子类直接使用(普通方法不需要被重写),而接口中不能定义普通的字段和方法,子类必须重写接口中的所有抽象方法。
几点具体的区别:
-
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行(除非被
default
或static
修饰)。
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行(除非被
-
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
-
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。(从
Java8
开始接口中可以定义静态方法)
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。(从
-
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
另外,我们补充一下实现接口与继承的区别:
1.不同的关键字,即实现接口(implements),继承(extends);
2.(一个类只能继承一个父类,而一个方法可以实现多个接口) 在面向对象编程的时候只能是单继承,但是实现接口可以有多个,简单点说,就是实现接口可以有好多个,但是继承的中父类只能只有一个,因为父亲只有一个,这说明了继承在Java中具有单根性,子类只能去继承一个父类;
3.在接口中只能定义全局变量和抽象方法,而在继承中可以定义普通的属性、方法等等…
4.(接口中的抽象方法必须被重写,而继承中子类可以选择不重写父类的方法(不考虑父类是抽象类)) 当某个接口被实现的时候,在类中一定要重写接口中的抽象方法,而继承中子类可以选择是否重写父类的方法,不是强制的
以上就是对Java中抽象类和接口的所有介绍了,希望能给大家带来帮助!
接下来将会发布不少博客:Java实现简单的图书管理系统、介绍内部类、介绍String…