Java基础进阶_day03_(抽象类,接口,多态)
1. 抽象类
1.1 抽象方法
抽象方法:没有方法体(就是没有那对大括号)的方法称为抽象方法.public voidshow();
1.2 抽象类理解
抽象类:含有抽象方法的类必须定义为抽象类;抽象就是没有具体的事物,只有类的描述信息,只有出现具体的子类后这些描述信息才有意义.
1.3 抽象类格式
权限修饰符 abstract class 类名{
定义类的成员;
}
子类通过继承抽象类后,实现父类中的抽象方法.
1.4 抽象类特点
# 抽象类必须使用abstract关键字修饰;
# 含有抽象方法的类一定是抽象类,抽象类不一定有抽象方法;
# 抽象类有成员变量(常量,变量)和成员方法(抽象,非抽象);
# 抽象类有构造方法,但是抽象类不能被直接实例化(可以通过多态的方式实例化),抽象类的构造方法的作用是被子类调用进行父类成员初始化以便子类使用父类的数据;
# 抽象类的子类(继承):
* 当子类为抽象类时,子类不必重写父类的抽象方法;
* 当子类为普通类时,子类必须重写父类的所有的抽象方法;
1.5 抽象类注意事项
# abstract修饰符与其他修饰符关系:
* private:冲突(抽象方法必须被子类重写,而private修饰的方法子类不能重写);
* final:冲突(抽象方法必须被子类重写,而final修饰的方法子类不能重写);
* static:没有意义(抽象方法是没有方法体,使用static修饰后可以类名.方法名调用,该方法无意义);
# 当抽象类没有抽象方法时意义:
* 限制该类不能被创建对象(可以作为工具类).
1.6 案例代码
/*
* 抽象类案例
*/
public class AbstractDemo {
public static void main(String[] args) {
// 抽象类不能被创建对象
//MyAbstractDemo mad = new MyAbstractDemo();
}
}
// 定义抽象类
abstract class MyAbstractDemo {
// 抽象类有成员常量,常量需要定义时或构造方法中进行初始化
private final String name;
// 抽象类有成员变量
private int age;
// 抽象类有构造方法
public MyAbstractDemo() {
name = "张三";
}
public MyAbstractDemo(String name) {
this.name = name;
}
// 抽象类有非抽象方法
public void show() {
}
// 抽象类可以有抽象方法
public abstract void show2();
}
// 抽象类的子类可以为抽象类
abstract class SonAbstract extends MyAbstract {
// 子类不必重写父类的抽象方法;
}
// 抽象类的子类可以为具体类
class SonAbstract2 extends MyAbstract {
// 必须实现父类中的所有抽象方法
public void show2(){}
}
2. 接口
接口是功能的集合,是类的功能扩展;接口不是一个类,是一种较为特殊的”类”.
2.1 接口格式
# 使用interface关键字修饰,且没有class关键字
interface 接口名{
定义静态常量及抽象方法;
}
# 类实现接口:
public class 类名 implements 接口名{
实现接口中的抽象方法;
}
2.2 接口特点
# 接口使用interface关键字修饰;
# 接口可以有成员变量:成员变量只能是静态常量,默认都有public static final关键字修饰(定义时加上);
# 接口有成员方法:成员方法只能是抽象方法,方法默认都有public abstract关键字修饰(定义时加上);
# 接口没有构造方法,不能被创建对象,也不需要进行初始化(接口的所有成员变量均是静态的,不需要进行初始化), 接口可以使用多态的方式进行实例化(创建对象);
# 接口子类:
* 当子类为抽象类时,不需要重写接口中的抽象方法;
* 当子类为具体类时,需要实现接口的所有抽象方法.
2.3 接口注意事项
# 当类C继承A类同时实现接口B,如果类A中和接口B中有相同的方法且A类中方法为非抽象方法:
C类可以不用重写接口中方法;
C类重写了类A中的方法,也同时将接口B中方法抽象进行了重写(子类继承父类后,获取了父类的成员方法,相当于重写了接口中抽象方法);
class A {
public void method() {
}
}
interface B {
public abstract void method();
}
class C extends A implements B {
//如果不重写method()方法,使用的是A中的method()方法
//如果重写method()方法将同时重写A和接口B中的method()方法
}
# 接口不是类,没有父类;
# 类实现多个接口时只能有一个implements关键字,接口名间使用逗号隔开.
2.4 类与类,类与接口,接口和接口的关系
# 类与类:继承关系,只能单继承,不能多继承,可以多层继承,不能循环继承;
# 类与接口:实现关系,类能实现一个或多个接口,并在继承一个类的同时实现多个接口;
# 接口与接口:继承关系,可以单继承,可以多继承.
案例代码
/*
* 类,接口间的关系
*/
public class ClassAndInterface {
}
/*
* 类与类的关系:继承关系
*/
// 定义类A
class A {}
// 定义类B
class B extends A{
// 1.类支持单继承
}
// 定义类C
//class C extends A,B{} 2.类不支持多继承
class C extends B{
// 3.类支持多层继承
}
// 4.类不能循环继承
//class D extends F{}
//class F extends D{}
/*
* 类与接口的关系:实现关系
*/
interface InterA {}
interface InterB {}
// 1.类实现接口,可以单实现
class ClassA implements InterA {}
// 2.类实现接口,可以多实现
class ClassB implements InterA,InterB {}
// 3.类继承的同时单/多实现接口
class ClassC extends ClassA implements InterA,InterB {}
/*
* 接口与接口的关系:继承关系
*/
// 1.可以单继承
interface InterC extends InterA {}
// 2.可以多继承
interface InterD extends InterA, InterB{}
2.5 抽象类与接口的区别
# 成员变量的区别:
* 抽象类的成员变量:可以为常量,可以为变量
* 接口的成员变量:只能为静态的常量
# 构造方法区别:
* 抽象类的构造方法:有构造方法,不能被实例化(子类访问父类成员时需先将父类成员进行初始化,通过调用父类的构造方法进行父类成员的初始化)
* 接口的构造方法:没有构造方法,不能被实例化(接口的成员变量均为常量,不需要进行初始化)
# 成员方法的区别:
* 抽象类的成员方法:可以是非抽象,也可以是抽象
* 接口的成员方法:只能是抽象方法
# 关系区别:
* 抽象类是类,遵循类的特点(类与接口遵循类与接口的关系)
# 设计理念区别:
* 抽象类-->被继承体现的是:"is a"的关系。抽象类中定义的是该继承体系的共性功能,抽象类就是当作普通类使用,只不过是继承时强制子类实现抽象方法并且不能创建实例对象.
* 接口-->被实现体现的是:"like a"的关系。接口中定义的是该继承体系的扩展功能。
接口是类的扩展功能定义的集合,类需要扩展额外的功能时,就实现具有该功能的接口.
3. 多态
多态: 对象在不同的时刻表现出不同的状态.
3.1 多态前提
# 继承关系或实现接口的关系存在;
# 方法的重写(可以没有方法重写,当没有方法重写时,没有意义);
# 父类(接口)引用指向子类对象.
案例代码
/*
* 多态现象
*/
public class Demo01 {
public static void main(String[] args) {
// 父类引用指向子类对象
Father f = new Son();
// 成员方法编译看左边,运行看右边(子类的方法)
f.show(); // Son show
f.show2(); // Son show2
// 子类没有重写父类方法,调用父类本身的方法
f.show3(); // Father show3
}
}
abstract class Father {
public void show() {
System.out.println("Father show");
}
public abstract void show2();
public void show3() {
System.out.println("Father show3");
}
}
class Son extends Father {
public void show() {
System.out.println("Son show");
}
// 重写父类的方法
@Override
public void show2() {
System.out.println("Son show2");
}
}
3.2 多态的好处
# 提高了程序的维护性(继承体现);
# 提高了程序的扩展性(多态体现,父类作为方法的形式参数,子类对象作为方法调用时的实际参数).
案例代码
/*
* 多态提高代码的扩展性
*/
public class Demo02 {
public static void main(String[] args) {
// 测试Animal所有子类的sleep()方法
testSleep(new Dog()); // 狗在睡觉
testSleep(new Cat()); // 猫在睡觉
// 当Animal再有一个子类要测试sleep()方法时
// 可以直接调用testSleep()方法测试新的子类的slee()方法,这就是代码的扩展性
// testSleep(new Pig());
}
// 定义调用Animal子类的sleep()的方法
public static void testSleep(Animal animal) {
animal.sleep();
}
}
// 动物类,所有动物的父类
class Animal {
public void sleep() {
System.out.println("动物睡觉");
}
}
// 狗类
class Dog extends Animal {
// 重写父类的方法
@Override
public void sleep() {
System.out.println("狗在睡觉");
}
}
// 猫类
class Cat extends Animal {
// 重写父类的方法
@Override
public void sleep() {
System.out.println("猫在睡觉");
}
}
3.3 多态的弊端
# 父类引用指向子类对象,父类引用不能调用子类特有的方法.
* 解决方法:多态的向下转型:
父类 fu = new 子类();
子类 zi = (子类)fu;
3.4 多态中的成员访问特点
# 多态中成员访问特点: 父类 fu = new 子类();
* 成员变量:编译看左边,执行看左边(父类的引用,调用的是父类的成员变量);
* 构造方法:子类创建对象时将父类的成员进行初始化;
* 成员方法:编译看左边,执行看右边(方法被子类重写,所以执行看右边);
* 静态方法:编译左边,执行看左边(静态方法和类直接相关,子类中的方法不是重写父类的静态方法).
3.5 多态其他注意事项
多态分类
# 具体类多态:父类是具体的类;
# 抽象类多态:父类为抽象类;
# 接口多态:子类实现的是接口(应用较多).
多态转型
# 向上转型:多态本身就是向上转型(子类转换为父类对象);
# 向下转型:要注意一定能够转换为相应的子类对象,否则会报错(ClassCastException类型转换异常).
父类 fu = new 子类();
子类 zi = (子类)fu;
instanceof关键字
# 用于判断某个对象是不是某一个数据类型,如果是则返回true,否则返回false, 一般用于判断多态中向下转型.