文章目录
一、抽象类
1.何为抽象类
在面向对象的思想中,所有的对象都是通过类来描述的;但是,并不是所有的类都是用来描述对象的,当一个类中没有包含足够的信息来描述一个具体的对象时,我们就将这样的类称为抽象类。
我们之前经常使用到的Animal类就可以被设计为一个抽象类。
2.抽象类语法
在Java中,被abstract修饰的类称为抽象类;被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现(我们以Animal类为例):
java
//抽象类
abstract class Animal {
public String name;
public int age;
//抽象方法
abstract public void eat();
}
注意:
1.抽象类也是类,在抽象类中可以包含普通成员方法和普通成员变量以及构造方法。
2.抽象类的构造方法依靠子类的调用,帮助抽象类初始化成员。
3.当一个类中有抽象方法时,那么该类一定为抽象类。
4.抽象类中不一定有抽象方法。
3.抽象类特性
当我们定义完一个抽象类之后,我们想去使用,此时我们尝试使用抽象类实例化一个对象:
java
public static void main(String[] args) {
Animal animal = new Animal();
}
在编写代码时程序会报错,编译器会提示我们抽象类不能被实例化。
一个类不能被实例化,那么这个类是干什么的呢?抽象类就是为了被继承的:
java
class Dog extends Animal {
}
当我们定义一个狗类继承动物类时,此时代码会报错,这又是为什么呢?
当一个普通的类继承抽象类之后,在普通类中一定要重写抽象类中所有的抽象方法:
java
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
此时,代码就可以正常运行。
对于抽象类我们还需要知道:抽象方法不能被final、static和private修饰,因为抽象方法在子类中一定要被重写。
当一个普通类A继承了抽象类B之后,不想重写B中的抽象方法,可以将普通类A也设计为抽象类;但是当另一个普通类C继承抽象类A之后,需要重写A和B中所有的抽象方法。
使用抽象类时,依旧可以发生向上转型和动态绑定:
java
abstract class Animal {
public String name;
public int age;
abstract public void eat();
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃猫粮");
}
}
public class Test {
public static void test(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
//向上转型
Animal animal = new Dog();
Animal animal1 = new Cat();
test(animal);
test(animal1);
test(new Dog());
}
}
在传递参数的时候,我们也可以使用匿名对象,如"new Dog()";但是,匿名对象也有缺点:每次使用都需要重新实例化。
4.抽象类作用
抽象类并不能被实例化,主要就是用来被继承的,那抽象类的存在有什么意义呢?
当我们使用抽象类时,就相当于增加了一重编译器的校验,通过编译器提示可能出现的问题,帮助我们尽早发现。
抽象类存在的意义就是为了让编译器进行更好的校验,如:像Animal这样的类我们并不会直接使用,而是使用其子类,但万一我们创建了一个Animal的实例,编译器就会及时提醒我们。
二、接口
1.何为接口
接口在我们的生活中随处可见,笔记本电脑上的USB接口,墙上的电源接口等等。在Java中,也存在接口的概念:在Java中,接口可以理解为多个类的公共规范,是一种引用数据类型。
2.接口语法及特性
接口的语法定义格式与类的语法定义格式基本相同,只需将class换为interface关键字即可。
java
interface IAnimal {
}
此时,我们就定义了一个接口。
我们想在接口中添加一些变量和方法:
java
interface IAnimal {
int a = 10;
void test();
public static final int b = 20;
public abstract void test1();
}
以上这两种写法都是可以的:
1.接口中的成员变量默认为"public static final"修饰的,可省略不写。
2.接口中的成员方法默认为"public abstract"修饰的,可省略不写。
原则上来说,在接口中不可以存在普通的成员方法:
java
interface IAnimal {
//error
public void test2() {
}
}
但是,从Java8开始,允许在接口中定义一个default方法,此方法可以有具体的实现:
java
interface IAnimal {
default public void test2() {
System.out.println("嘻嘻");
}
}
在接口中,被static修饰的方法,也可以有具体的实现:
java
interface IAnimal {
public static void test3() {
System.out.println("嘿嘿");
}
}
接口不能通过new来实例化对象:
java
public static void main(String[] args) {
//error
IAnimal iAnimal = new IAnimal();
}
类和接口之间,可以通过implements关键字来实现接口:
java
class Dog implements IAnimal {
}
但此时代码会报错,这是因为接口中存在抽象方法,我们需要对抽象方法进行重写:
java
class Dog implements IAnimal {
@Override
public void test() {
}
@Override
public void test1() {
}
}
此时,代码就可以正常运行。
在使用接口时,我们还需注意:
1.接口也可以发生向上转型和动态绑定。
2.重写接口中的抽象方法时,必须使用public修饰,因为重写的 方法的 访问权限 不能比 被重写的 方法的 访问权限低。
3.接口中不能存在静态代码块和构造方法。
4.一个接口也会产生独立的字节码文件(×××.class)。
5.当一个类没实现接口中所有的抽象方法时,该类一定为抽象类。
6.父类和子类之间是继承关系(extends),类和接口之间是实现关系(implements)。
三、接口使用
接口不能直接使用,需要有一个类来实现接口,并重写接口中所有的抽象方法。
我们举个例子来熟悉一下接口的使用:
定义一个接口:USB,接口中包含打开设备功能,关闭设备功能。
定义一个鼠标类:实现USB接口,并具备点击功能。
定义一个键盘类:实现USB接口,并具备输入功能。
定义一个笔记本类:包含开机,关机及使用USB设备功能。
根据题意,我们先定义一个接口:
java
public interface IUSB {
void openDevice(); //打开设备
void closeDevice(); //关闭设备
}
定义一个鼠标类实现接口(注意:重写抽象方法):
java
public class Mouse implements IUSB {
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
public void click() {
System.out.println("点击功能");
}
}
定义一个键盘类实现接口(注意:重写抽象方法):
java
public class Keyboard implements IUSB {
@Override
public void openDevice() {
System.out.println("打开键盘");
}
@Override
public void closeDevice() {
System.out.println("关闭键盘");
}
public void inPut() {
System.out.println("输入功能");
}
}
定义一个笔记本类,包含开机关机功能:
java
public class Computer {
public void open() {
System.out.println("打开笔记本");
}
public void close() {
System.out.println("关闭笔记本");
}
public void use(IUSB iusb) {
iusb.openDevice();
//向下转型
if (iusb instanceof Mouse) {
Mouse mouse = (Mouse) iusb;
mouse.click();
} else if (iusb instanceof Keyboard) {
Keyboard keyboard = (Keyboard) iusb;
keyboard.inPut();
}
iusb.closeDevice();
}
//测试
public static void main(String[] args) {
Computer computer = new Computer();
computer.open();
computer.use(new Mouse());
computer.use(new Keyboard());
computer.close();
}
}
运行结果为:
四、实现多个接口
在Java中,类和类之间是不支持多继承的,但是一个类可以实现多个接口。
我们举个例子来说明一下:
定义三个接口,分别表示三种功能:飞、跑、游:
java
public interface IFlying {
void fly();
}
java
public interface IRunning {
void run();
}
java
public interface ISwimming {
void swim();
}
再定义一个动物类,定义为抽象类,因为类中需要有被重写的eat抽象方法:
java
abstract public class Animal {
public String name;
public int age;
public abstract void eat();
public Animal(String name) {
this.name = name;
}
}
再定义一个狗类,继承动物类,并实现跑和游的接口:
java
public class Dog extends Animal implements IRunning,ISwimming {
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name + "吃狗粮");
}
@Override
public void run() {
System.out.println(name + "小狗跑");
}
@Override
public void swim() {
System.out.println(name + "小狗游");
}
}
在狗类中,我们要重写继承和实现的所有抽象方法。
再定义一个鸟类,继承动物类,并实现跑和飞的接口:
java
public class Bird extends Animal implements IFlying,IRunning {
public Bird(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name + "吃鸟粮");
}
@Override
public void fly() {
System.out.println(name + "小鸟飞");
}
@Override
public void run() {
System.out.println(name + "小鸟跑");
}
}
同样,在鸟类中,我们要重写继承和实现的所有抽象方法。
我们再给一个测试类,用来测试写过的代码:
java
public class Test {
public static void test1(Animal animal) {
animal.eat();
}
public static void test2(IRunning iRunning) {
iRunning.run();
}
public static void test3(IFlying iFlying) {
iFlying.fly();
}
public static void test4(ISwimming iSwimming) {
iSwimming.swim();
}
public static void main(String[] args) {
}
}
此时,代码的使用就会非常灵活:
java
public static void main(String[] args) {
Dog dog = new Dog("小狗");
Bird bird = new Bird("小鸟");
test1(dog);
test1(bird);
System.out.println("===========");
test2(dog);
test2(bird);
System.out.println("===========");
//test3(dog); error:没实现此接口
test3(bird);
System.out.println("===========");
test4(dog);
//test4(bird); error:没实现此接口
}
运行结果为:
对于test2,test3,test4方法,方法的参数为接口类型,当调用方法时,只要传递的参数实现了这个接口,就可以使用。因此,我们还可以这样使用接口:
我们再定义一个车类,实现跑的接口:
java
public class Car implements IRunning {
@Override
public void run() {
System.out.println("小车跑");
}
}
此时,在测试类中调用test2方法,并将实例化的小车作为参数进行传递,代码可以正常运行:
java
public static void main(String[] args) {
test2(new Car()); //小车跑
}
当使用了接口时,就无需关注类的类型,只需考虑类是否实现了接口,具不具备接口中的功能即可,大大提高了代码的灵活性。
五、接口间的继承
在Java中,类和类之间不能是多继承关系的,但是接口和接口之间可以实现多继承,即可以使用接口达到多继承的目的。
接口使用 extends关键字 拓展(继承)一个接口,从而达到代码复用的效果:
java
interface A {
void testA();
}
interface B {
void testB();
}
interface C extends A,B {
void testC();
}
public class Test implements C {
@Override
public void testA() {
}
@Override
public void testB() {
}
@Override
public void testC() {
}
}
在上面这个例子中,接口C继承了接口A和接口B,表示C这个接口具备了B接口和A接口的功能 ,当一个普通类实现C接口时,需要重写A、B、C中所有的抽象方法。
六、抽象类和接口的区别
二者的核心区别在于:抽象类中可以包含普通方法和普通变量,而接口中不能包含普通方法。

Ending。