多态性:
定义:某一个事物,在不同环境下表现出来的不同状态。
多态的三种形式:普通类的多态、抽象类的多态、接口的多态。 ------------------------------ 类是根源的抽象,接口是功能的扩展。
使用前提:1. 有继承关系
2. 方法的重写
3. 父类的引用指向子类的对象(向上转型)
优点:提高了程序的维护性(由继承保证)
提高了程序的扩展性(由多态保证)
缺点:不能访问子类特有功能 ----------------------------------------------- 只能通过向下转型的方式访问子类特有功能
两种转型方式:
向上转型: Person p = new Teacher( ); ----------------------------- Person为父类,Teacher为子类
向下转型:Teacher t = (Teacher) p; ------------------------------------ 强制类型转换(父类引用转为子类对象)
调用关系:(发生向上转型,即父类引用指向子类对象的时候)
调用同名的成员变量-----父类的
调用同名的静态方法----父类的
调用同名的成员方法----子类的
调用同名的静态变量----父类的
------------------------------------------------ 不能调用子类独有的方法和变量
public class PolyDemo01 {
public static void main(String[] args) {
Animal a = new Cat(); //向上转型
System.out.println(a.num); //10
System.out.println(a.age); //20
//System.out.println(a.name);父类引用不能调用子类中独有的属性
a.run(); //动物在奔跑
a.sleep(); //动物在睡觉
a.eat(); //猫在吃饭
//a.catchMouse();父类引用不能调用子类中独有的方法
Cat c = (Cat)a; //向下转型(强制类型转换)
System.out.println(c.name);
System.out.println(c.num);
System.out.println(c.age);
c.eat();
c.sleep();
c.run(); //子类中没有,就调用父类中的run方法
c.catchMouse();
}
}
//父类
class Animal{
int num = 10;
static int age = 20;
public static void sleep() { //父类中的静态方法,与子类同名
System.out.println("动物在睡觉");
}
public void eat() { //父类中的成员方法,与子类同名
System.out.println("动物在吃饭");
}
public void run() { //父类中独有的成员方法
System.out.println("动物在奔跑");
}
}
//子类
class Cat extends Animal{
int num = 80;
static int age = 90;
String name = "小花"; //子类中的独有成员属性
public static void sleep() {
System.out.println("猫在睡觉");
}
public void eat() {
System.out.println("猫在吃饭");
}
public void catchMouse() { //子类中独有的成员方法
System.out.println("猫在抓老鼠");
}
}
注意:你会发现父类中与子类同名的成员方法体都没有执行,那么父类中是不是就可以不写方法体,子类只需要重写方法名就可以了.......接下来就引入了抽象类的概念,即将 父类 定义为 抽象类,将父类中的 成员方法 定义为 抽象方法。===============
abstract关键字:
抽象类:使用abstract关键字修饰的类。
抽象类中可以没有抽象方法,有抽象方法的类一定是抽象类。
抽象类本身不能创建对象,必须使用子类向上转型的方式创建,即依赖子类而存在。
抽象类不能使用final修饰,因为final修饰的类不能继承。
抽象方法:使用abstract修饰的方法。
abstract:在修饰方法的时候,不能与大括号同时存在。
abstract不能修饰静态方法,因为抽象方法需要被子类重写,而静态方法没有重写的性质。
注意事项:抽象类的子类必须实现/重写抽象类中的所有抽象方法(重写之后有方法体),否则自己就是一个抽象类。
抽象类中虽然不能创建对象,但是必须有构造方法(完成子类数据的初始化,即将父类当中的属性继承过来)
public class PolyDemo03 {
public static void main(String[] args) {
shape s1 = new Rect(4,2);
shape s2 = new Circle(4); //向上转型,父类引用指向子类对象
s1.area();
s1.per();
areaAndPer(s1); //相当于:s1.area();s1.per();
areaAndPer(s2); //多个对象重复调用时可以节省代码空间
}
//定义为static是为了可以直接在main方法中使用,多态性增加了共性代码的可扩展性。
public static void areaAndPer(shape s) { //发生了向上转型:shape s = new Rect(4,2)
s.area();
s.per();
}
}
//抽象类
abstract class shape{
abstract public void per();
abstract public void area();
}
//抽象类的子类
class Rect extends shape{
double lon;
double wid;
public Rect() {}
public Rect(double lon,double wid) {
this.lon = lon;
this.wid = wid;
}
@Override
public void per() {
System.out.println((lon+wid)*2);
}
@Override
public void area() {
System.out.println(lon*wid);
}
}
class Circle extends shape{
public static final double PI = 3.14;
private double r;
public void setR(double r) {
this.r = r;
}
public Circle(double r) {
this.r = r;
};
@Override
public void per() {
System.out.println(2*PI*r);
}
@Override
public void area() {
System.out.println(PI*r*r);
}
}
上面代码中:将对象作为参数传给方法的形式也可以解决数组不能存储不同类型数据的问题。原理是:通过向上转型的方式,将父类引用指向不同的子类对象,之后在Main类中写一个形参为父类引用的方法,通过将不同的子类对象传进去,实现将不同类型的数据存入一个数组中的目的。
接口:
接口:使用interface声明,与类平级,也是引用数据类型,在开发中用的是最多的 ------ 类是根源的抽象,接口是功能的扩展。
* 1.接口中只能定义常量(public static final),不能定义变量,默认使用public static final
* 2.接口中的方法都是抽象的方法(JDK8之前),public abstract可以省略。
* 3.接口不能用来创建对象(对象属于具体的事物),必须使用子类向上转型的方式实现。
* 接口的子类:实现接口的类
* class 子类名 implements 接口1,接口2...{} ------------ 接口支持多实现
* 注意事项:接口的子类要么实现接口中所有的抽象方法,要么自己是一个抽象类。(与抽象类的要求一致)
* 4.一个类可以实现多个接口,并且可以在继承类的同时实现多个接口。
* 5.接口中没有构造方法
* 6.jdk8以后可以在接口中添加已经实现(有方法体)的方法,必须用static/default(默认方法,不可省略)
* 7.类与类之间的关系:继承;类和接口之间的关系:(多)实现;接口与接口之间的关系:继承(多继承)
引无数英雄竞折腰
花落有时终须有,五花落尽更无花
==================
江山如此多娇
==================
一曲肝肠断,天涯何处觅知音
百战金沙,经年不归
少儿无心,老妪亦无意
风戏柳枝絮无语
===========================================================================
public class InterfaceDemo05 {
public static void main(String[] args) {
A3 a = new D3();
a.func1();
a.func2();
System.out.println("==================");
B3 b = new D3();
b.func3();
System.out.println("==================");
C3 c = new E3();
c.func1();
c.func2();
c.func3();
c.func4();
}
}
interface A3{
final int A = 10;
void func1();
void func2();
}
interface B3{
void func3();
}
interface C3 extends A3,B3{ //继承不用重写,会自动继承父类接口的方法及A3中的常量值
void func4();
}
class D3 implements A3,B3{ //A3,B3为父类,D3为子类
@Override
public void func3() {
System.out.println("江山如此多娇");
}
@Override
public void func1() {
System.out.println("引无数英雄竞折腰");
}
@Override
public void func2() {
System.out.println("花落有时终须有,五花落尽更无花");
}
}
class E3 implements C3{ //C3为父类,E3为子类
@Override
public void func1() {
System.out.println("一曲肝肠断,天涯何处觅知音");
}
@Override
public void func2() {
System.out.println("百战金沙,经年不归");
}
@Override
public void func3() {
System.out.println("少儿无心,老妪亦无意");
}
@Override
public void func4() {
System.out.println("风戏柳枝絮无语");
}
}
抽象类与接口的区别:--------------------------------------------------接口是对动作的抽象,而抽象类是对根源的抽象
设计理念的区别:抽象类 ----------- 共性功能
接口 -------------- 扩展功能
关系区别:一个类只能继承一个抽象类
一个类可以实现多个接口
成员区别:抽象类---------------------- 变量+常量+抽象方法+非抽象方法
接口 ------------------------ 常量+抽象方法+非抽象方法(JDK1.8之后)
注:1.普通类实现接口时需要重写抽象方法,而抽象类则不需要
2.接口本质上就是一种抽象类,接口之间继承是不需要重写方法的
3.子类继承普通类是不需要重写方法的,但是继承抽象父类时需要重写所有的抽象方法。
抽象类和接口的共同点大致有以下两个方面:
(1)接口的本质是特殊的抽象类,接口和抽象类都只能用public修饰权限。
(2)抽象类和接口都无法实例化。
抽象类和接口的区别大致有以下五个方面:
(1)抽象类中可以包含任意访问权限、任意类型的变量或常量;接口中只能包含公有的静态的常量。
(2)抽象类中可以包含任意访问权限的非抽象方法或抽象方法;接口中只能包含公有的抽象方法。
(3)抽象类中可以定义构造方法,但接口中禁止定义构造方法。
(4)抽象类支持单一继承,接口支持多重继承。
(5)抽象类是对类的抽象,继承了该类的对象同属于某一个较大的、共同的类别;接口是一种行为的规范,已实现该接口的类所创建的对象都具有某种共同的能力。