目录
1、前言
这次blog我们共同来探讨一下抽象类与接口的概念、特点以及用法。
2、抽象类
2.1 抽象类概念
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 有如下代码:
class Shape {
public void draw() {
System.out.println("画图形");
}
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("●");
}
}
class Triangle extends Shape{
@Override
public void draw() {
System.out.println("△");
}
}
class Flower extends Shape{
@Override
public void draw() {
System.out.println("❀");
}
}
- 在打印图形例子中, 我们发现,父类 Shape 中的 draw 方法好像并没有什么实际工作,主要的绘制图形都是由 Shape的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法,我们可以把它设计成一个 抽象方法(abstract method),包含抽象方法的类我们称为 抽象类(abstract class)。
2.2 抽象类语法
- 在Java中,一个类如果被abstract 修饰称为抽象类,抽象类中被abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体,这时候可以省略{}。
2.3 抽象类特性
1、抽象类不能直接实例化对象
2、抽象方法不能是 private 的
abstract class Shape {
abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private
3、抽象方法不能被final和static修饰,因为抽象方法要被子类重写,抽象方法存在的意义就是为了让子类重写他
public abstract class Shape {
abstract final void print();
abstract public static void draw();
}
// 编译报错:
// Error:(20, 25) java: 非法的修饰符组合: abstract和final
// Error:(21, 33) java: 非法的修饰符组合: abstract和static
4、
抽象类当中可以有和普通类一样的方法,成员,包括构造方法、静态代码块等。
子类可以不重写抽象类中的普通方法。有如下代码:
abstract class Shape{
//成员
public int a;
//构造方法
public Shape(){
}
//静态代码块
static{
}
//普通方法
public void test(){
}
//抽象方法
public abstract void draw();
public abstract void pint();
}
class Dog extends Shape{
@Override
public void draw() {
}
@Override
public void pint() {
}
}
public class Test {
/* Shape shape = new Shape();
Shape shape = new Dog();*/
}
5、抽象类必须被继承,并且继承后子类要重写父类中的抽象方法,否则如果不想重写抽象方法的话,子类也得是抽象类,必须要使用abstract 修饰。
abstract class Shape{
//成员
public int a;
//构造方法
public Shape(){
}
//抽象方法
public abstract void draw();
public abstract void pint();
}
//抽象类可以不重写父类的抽象方法
abstract class Dog extends Shape{
}
6、抽象类中的抽象方法有几个,子类继承抽象类的时候就要重写几个,如果没有重写完全,编译器就会报错。
7、抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类
8、 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量
2.4 抽象类的作用
- 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
- 使用抽象类相当于比普通类多了一重编译器的校验;
- 使用抽象类的场景就如上面的代码,实际工作不应该由父类完成,而应由子类完成,那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的,但是父类是抽象类就会在实例化的时候提示错误,让我们尽早发现问题。
3、接口
3.1 概念
- 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
3.2 语法
- 接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字,就定义了一个接口。
public interface 接口名称{
// 抽象方法
public abstract void method1(); // public abstract 是固定搭配,可以不写
public void method2();
abstract void method3();
void method4();
// 注意:在接口中上述写法都是抽象方法,跟推荐方式4,代码更简洁
}
注意:
1. 创建接口时, 接口的命名一般以大写字母 I 开头;
2. 接口的命名一般使用 "形容词" 词性的单词;
3. 接口中的方法和属性建议不要加任何修饰符号, 保持代码的简洁性。
3.3 接口使用
- 接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
public class 类名称 implements 接口名称{
// ...
}
注意:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
- 有如下代码,下面的讨论都是基于这个代码,在这里先展现出来
interface IShape{
int a = 10;
String name = "wujianyong";
void draw();
static void draw1(){
System.out.println("lalala");
}
default void draw2(){
System.out.println("huahuu");
}
}
class Cycle implements IShape {
@Override
public void draw() {
}
}
public class Test {
public static void main(String[] args) {
// IShape shape =new IShape();
}
}
3.4 接口特性
1、接口类型是一种引用类型,但是不能直接new接口的对象(不能直接实例化)
2、接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是public abstract,其他修饰符都会报错)
所以这些不带修饰符的方法,默认是抽象方法,不能带有具体内容,即 void test()结束即可
3、普通方法:
- 想要带有内容的方法,用default或者static修饰都可以带有内容,称为普通方法,普通方法可以在子类中可以不被重写
- 无论是default修饰还是static修饰的方法,在接口中都是public的,因为是接口,要被子类继承的。
4、接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量,不管你怎么定义成员。
所以一定要给接口中的成员变量赋初值,因为是final定义的
5、接口中不能有静态代码块和构造方法(至于什么是静态代码块请移步内部类与代码块blog中去查看);
6、重写接口中方法时,不能使用default访问权限修饰;
7、其余类和接口的关系是使用implement来关联的,关联之后,这个类中要重写我们的接口中的所有抽象类方法(没有具体实现的方法),否则会报错,
普通方法,加default修饰 或者static修饰的,是有具体方法的可以不重写。
8、重写接口中方法时,不能使用default访问权限修饰;
9、接口也可以实现向上转型
3.5 接口间的继承
1、其实就是功能的拓展 IC不仅有funC的功能还有IA,IB的里面的功能(方法)
2、再有一个类来实现接口的时候,要重写三个方法,IC和IA、IB所有的抽象方法都得重写
interface IA{
void funcA();
}
interface IB{
void funcB();
}
interface IC extends IA,IB{
void funcC();
}
3.6 实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过一个例子来展示一下多个接口:
abstract class Animal{
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
}
interface IRunning{
void run();
}
interface IFlying{
void fly();
}
interface ISwimming{
void swim();
}
interface ISum extends ISwimming,IRunning{
void sum();
}
class Dog extends Animal implements IRunning,ISwimming{
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name +" 正在吃狗粮!");
}
@Override
public void run() {
System.out.println(name + " 正在四条腿跑!");
}
@Override
public void swim() {
System.out.println(name + " 正在四条腿狗刨!");
}
}
class Bird extends Animal implements IRunning,IFlying{
public Bird(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name +" 正在吃鸟粮!");
}
@Override
public void run() {
System.out.println(name+" 正在用两条细细的腿在跳着跑!");
}
@Override
public void fly() {
System.out.println(name +" 正在用两个翅膀飞!");
}
}
public class Test {
public static void fly(IFlying iFlying){
iFlying.fly();
}
public static void run(IRunning iRunning){
iRunning.run();
}
public static void swim(ISwimming iSwimming){
iSwimming.swim();
}
public static void eat(Animal animal){
animal.eat();
}
public static void dogFunc(ISwimming iSwimming,IRunning iRunning,Animal animal){
iSwimming.swim();
iRunning.run();
animal.eat();
}
public static void main(String[] args) {
/* run(new Dog("狗子",15));
swim(new Dog("狗子",15));
eat(new Dog("gougzi",15));
*/
dogFunc(new Dog("lalla",15),new Dog("lalla",15),new Dog("lalla",15));
}
}
3.7 抽象类和接口的区别
区别:抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写);
接口中不能包含普通方法, 子类必须重写所有的抽象方法;
序号 | 区别 | 抽象类 | 接口 |
1 | 结构组成 | 普通方法+抽象方法 | 抽象方法+全局变量(static修饰) |
2 | 权限 | 各种权限都可以使用 | public权限 |
3 | 子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字来实现接口 |
4 | 关系 | 一个抽象类可以实现若干接口 | 接口可以用extends继承多个接口 |
5 | 子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
4、结语
到此,我们Java的三板斧已经全部讲解完成了,相信大家对于Java的语法有了更深一步的理解了,有什么问题可以留言呦!