抽象类
在java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,这个类就应该定义为抽象类。
-
抽象类和抽象方法必须用abstract关键字来修饰。
抽象类: abstract class 类名 { } 抽象方法: public abstract void 方法名 ( );
-
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类。
-
抽象类中可以有构造方法,但是抽象类不能实例化。
那么构造方法有什么作用呢?
用于子类访问父类数据时的初始化。 -
抽象类不能实例化,所以它要按照多态的方式,通过具体的子类进行实例化。
-
抽象类的子类,要么是抽象类,要么重写抽象类中的所有抽象方法。
-
成员变量:可以是变量,也可以是常量
-
构造方法:用于子类方法访问父类数据时的初始化
-
成员方法:既可以是抽象的,也可以是非抽象的
抽象方法:强制要求子类(重写)做的事情
非抽象方法: 子类继承的事情,提高代码的复用性
eg: 抽象类及其继承使用
//抽象类:
public abstract class Animal {
String name;
int age;
//抽象类的构造方法:仅用于子类访问父类数据时的初始化,并不代表Animal可以实例化
public Animal() {
System.out.println("抽象类的构造方法执行了");
}
//抽象方法_eat()
public abstract void eat();
//成员方法_sleep()
public void sleep() {
System.out.println("所有的动物都要睡觉");
}
}
//子类:
class Dog extends Animal {
String name = "小狗";
int age = 2;
//子类对于父类中的抽象方法必须进行重写
@Override
public void eat() {
System.out.println("狗狗爱吃骨头");
}
//子类对于父类中的非抽象方法可以不用重写
}
//测试类:
public class MyAbstract {
public static void main(String[] args) {
Animal dog = new Dog(); //多态形式
dog.eat();
dog.sleep();
}
}
-
Q1: 一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
-
可以 。这么做是为了不在其它类中实例化该类,而是通过该类的子类去实例化。
Q2: abstract不能和哪些关键字共存?
-
static:
static修饰后可以直接类名调用方法,但是抽象方法没有实现体,调用无意义; -
final:
final修饰的类不能被继承,修饰的方法不能被重写,修饰的成员变量只能为常量;但是继承的子类要重写这个抽象方法,所以会出现矛盾; -
private:
抽象方法是为了让子类重写并给定实现体,但是private修饰的方法只能本类访问,不能被继承,所以矛盾。
接口
为了体现事物功能的扩展性,Java中提供了接口来定义这些额外功能,并不给出具体实现。
- 接口用关键字interface表示:interface 接口名 { }
- 类实现接口用implements表示 :class 类名 implements 接口名 { }
- 接口不能实例化,只能按照多态的方式实例化。
- 接口的子类可以是抽象类,也可以是具体类。必须要重写接口中的所有抽象方法。
- 成员变量:只能是常量,并且是静态的。
默认修饰符:public static final (建议自己给出修饰符。) - 构造方法:接口中没有构造方法。
- 成员方法:只能是抽象方法。
默认修饰符:public abstract (建议自己给出修饰符。)
eg:
//接口
interface myInterface {
//成员变量:只能是常量,并且是静态的。默认修饰符:public static final
int age = 100;
public static final String name = "大黄";
//成员方法:只能是抽象方法。默认修饰符:public abstract
//JDK1.8之后,可以定义静态方法和default修饰的方法,可以给出方法的具体实现,可以弥补单继承所带来的不足。
void dogMath();
public abstract void dogWork();
}
abstract class myAnimal {
String variety = "拉布拉多";
//定义一个成员方法
public void sleep() {
System.out.println("动物都要睡觉");
}
//定义一个抽象方法
public abstract void eat();
}
//myDog类继承了myAnimal类的成员变量和方法,并且实现了接口myInterface,功能更加强大
class myDog extends myAnimal implements myInterface {
//接口中的抽象方法必须全部实现(重写)
@Override
public void dogMath() {
System.out.println("狗狗可以算加减");
}
@Override
public void dogWork() {
System.out.println("狗狗可以逗人玩");
}
//抽象类中的抽象方法必须全部重写
@Override
public void eat() {
System.out.println("狗狗喜欢吃骨头");
}
//抽象类中非抽象方法可以不重写
}
//测试类:
public class Test {
public static void main(String[] args) {
/** 现在我们创建一只新狗狗,就可以有三种方式:
myDog myDog = new myDog();
myInerface myDog = new myDog();
myAinmal myDog = new myDog();**/
//1、先来看通过接口名引用指向子类对象
myInterface myDog1 = new myDog();
//我们可以通过对象名访问到接口中的变量(实际上这个变量是个静态变量public static final)
int age1 = myDog1.age;
//通过接口名myInterface来创建的子类对象,如果我们要访问父类myAnimal中的variety变量,就要向下转型
String variety1 = ((myDog) myDog1).variety;
//我们来依次实现所有的方法
myDog1.dogMath();
myDog1.dogWork();
//同样的,如果我们按照这种方法创建对象,要访问父类中的方法,仍然要先向下转型
((myDog) myDog1).eat();
((myDog) myDog1).sleep();
//2、我们再来通过父类名来创建对象
myAnimal myDog2 = new myDog();
//尝试访问接口中的变量,仍然需要向下转型
int age2 = ((myDog) myDog2).age;
//尝试访问接口中的方法,仍然需要向下转型
((myDog) myDog2).dogMath();
/*因为myDog类中继承了myAnimal类,实现了myInterface接口,所以它可以访问到父类和接口中的
成员变量和方法,如果我们通过多态去创建对象,要去访问接口或父类中的方法,就要先向下转型 */
//3、按照普通的方式来创建对象
myDog myDog3 = new myDog();
//这样创建的对象我们不需要转型就可以访问接口和父类中的成员变量和方法
int age3 = myDog3.age;
String name3 = myDog3.name;
myDog3.dogMath();
myDog3.dogWork();
String variety3 = myDog3.variety;
myDog3.eat();
myDog3.sleep();
}
}
- 类与类:继承关系,只能单继承,多层继承
- 类与接口:实现关系,可以单实现,也可以多实现,可以在继承一个类的情况下实现多个接口
- 接口与接口:继承关系,可以单继承,也可以多继承
interface A {
}
interface B extends A {
}
class C {
}
class D extends C {
}
//接口与接口之间支持多继承,也可以多层继承
interface E extends A, B {
}
//类与类之间只能单继承,但是可以实现多个接口
class F extends C implements A, B {
}
-
成员区别:
抽象类:有构造方法,成员变量可以是变量也可以是常量,成员方法可以抽象也可以非抽象。
接口:没有构造方法,成员变量只能是常量,成员方法只能是抽象的(JDK1.8之后,可以定义静态方法和default修饰的具体方法) -
关系区别:
类与类——继承,单继承
类与接口——实现,单实现,多实现
接口与接口——继承,单继承,多继承 -
设计理念区别
抽象类被继承体现的是:“is a”的关系,抽象类中定义的是该继承体系的共性功能。
接口被实现体现的是:“like a”的关系,接口中定义的是该继承体系的扩展功能。