写在前面
本文主要介绍了多态,抽象类,接口以及它们之间的简单联系,每个知识点都有案例。欢迎有疑问的小伙伴留言~~~~~~~~~~
多态
简单来说,就是
某个事物在不同时刻表现出的不同状态。
来看一个小的案例:
public class Demo {
public static void main(String[] args) {
Cat cat = new Cat();
//Animal cat = new Cat();
}
}
class Animal {
}
class Cat extends Animal {
}
在这里,cat可以是Cat类型,也可以是Animal类型,这就是多态。
多态的前提
1、要有继承关系。
2、要有方法重写
。其实没有也是可以的,但是没有这个就没有意义。
3、要有父类引用指向子类对象
。父 f = new 子();
public class Demo1 {
public static void main(String[] args) {
//多态的前提:继承,方法重写,父类引用指向子类对象
Animal cat1 = new Cat();
Animal dog1 = new Dog();
//多态:父类引用指向子类对象
//我们通过多态的方式,去访问成员变量,访问到的是父类的成员变量
int catAge = cat1.age;
int dogAge = dog1.age;
System.out.println(catAge); //10
System.out.println(dogAge); //10
//但是方法以重写的为准
cat1.Eat(); //猫爱吃鱼干
dog1.Eat(); //狗爱吃骨头
}
}
class Animal {
int age = 10;
public void Eat() {
System.out.println("动物要吃饭");
}
}
class Cat extends Animal {
int age = 2;
@Override
public void Eat() {
System.out.println("猫爱吃鱼干");
}
}
class Dog extends Animal {
int age = 5;
@Override
public void Eat() {
System.out.println("狗爱吃骨头");
}
}
多态中的成员变量访问特点
1、成员变量——编译看左边,运行看左边
2、构造方法——创建子类对象时,会访问父类的构造方法,对父类的数据进行初始化
3、成员方法——编译看左边,运行看右边
4、静态方法——编译看左边,运行看左边(静态是和类相关的,算不上重写)
案例演示
多态中的成员访问特点
public class Demo2 {
public static void main(String[] args) {
//多态的前提:继承,方法重写,父类引用指向子类对象
//成员变量——编译看左边,运行看左边
//创建子类对象时,会访问父类的构造方法,对父类数据进行初始化
Animal cat = new Cat();
int catAge = cat.age;
System.out.println("成员变量-----------" + catAge);
/*run:
*父类的构造方法执行了
*子类的构造方法执行了
*成员变量----------- 10
*/
//成员方法——编译看左边,运行看右边
cat.Eat();
/*run:
*子类Eat方法——猫爱吃鱼干
*/
//静态方法——编译看左边,运行看左边
cat.Job();
/*run:
*父类静态方法——动物都有工作
*/
}
}
class Animal {
int age = 10;
public Animal() {
System.out.println("父类的构造方法执行了");
}
public void Eat() {
System.out.println("父类Eat成员方法——动物要吃饭");
}
public static void Job() {
System.out.println("父类静态方法——动物都有工作");
}
}
class Cat extends Animal {
int age = 2;
public Cat() {
System.out.println("子类的构造方法执行了");
}
@Override
public void Eat() {
System.out.println("子类Eat成员方法——猫爱吃鱼干");
}
public static void Job() {
System.out.println("子类静态方法——猫的工作是抓老鼠");
}
}
多态的好处
1、提高了代码的
维护性
——继承保证
2、提高了代码的
扩展性
——多态保证
案例演示
多态的好处
public class Demo3 {
public static void main(String[] args) {
//我现在想知道老虎喜欢吃什么,那我就先创建一个Tiger对象,然后调用Eat方法
Animal tiger = new Tiger();
tiger.Eat(); //老虎喜欢吃肉
//我现在又想知道熊猫喜欢吃什么,那我就先创建一个panda对象,然后调用Eat方法
Animal panda = new Panda();
panda.Eat(); //熊猫喜欢吃竹子
//随着需求的变化,这样会很麻烦
//解决方法:我们可以创建一个类Zoo来专门构造对象,并调用相应的Eat方法
Zoo.eatMethod(new Sheep()); //绵羊喜欢吃草
}
}
class Zoo {
//构造私有化
private Zoo() {
}
//提供一个静态方法供外界使用
public static void eatMethod(Animal animal) {
animal.Eat();
}
}
class Animal {
public void Eat() {
System.out.println("不知道是什么动物,不知道喜欢吃什么");
}
}
class Panda extends Animal {
@Override
public void Eat() {
System.out.println("熊猫喜欢吃竹子");
}
}
class Tiger extends Animal {
@Override
public void Eat() {
System.out.println("老虎喜欢吃肉");
}
}
class Sheep extends Animal {
@Override
public void Eat() {
System.out.println("绵羊喜欢吃草");
}
}
多态的弊端
不能访问子类特有的方法
解决方法:把父类的引用强制
向上转型
或者
向下转型
案例演示
什么是向上转型和向下转型?
class Person {
public void Job() {
System.out.println("每个人都要工作");
}
}
class Teacher extends Person {
@Override
public void Job() {
System.out.println("老师的工作是教书育人");
}
public void Eat() {
System.out.println("老师喜欢吃火锅");
}
}
public class Demo4 {
public static void main(String[] args) {
//创建一个teacher对象,向上转型
Person teacher = new Teacher();
//我们可以调用被重写的方法Job(),但是无法调用子类中特有的方法Eat()
teacher.Job();
//向下转型,完成对Eat()方法的调用
Teacher teacher1 = (Teacher) teacher;
teacher1.Eat();
//简单的写法是这样的:
((Teacher) teacher).Eat();
}
}
看下面程序是否有问题,如果没有,说出结果
class Fu {
public void show() {
System.out.println("fu show");
}
}
class Zi extends Fu {
public void show() {
System.out.println("zi show");
}
public void method() {
System.out.println("zi method");
}
}
class DuoTaiTest3 {
public static void main(String[] args){
Fu f = new Zi();
f.method();
f.show();
}
}
这个程序是会报错的:
Error:(27, 10) java: 找不到符号
符号: 方法 method()
位置: 类型为xxxxxx的变量 f
解释:main方法里的对象f是通过多态来实现的,而method方法是Zi类中特有的方法,
此时的f的类型是Fu类,在没有完成向下转型转成Zi类的情况下是无法调用method方法的。
抽象类
在java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,这个类就应该定义为抽象类。
抽象类的特点
1、抽象类和抽象方法必须用
abstract
关键字来修饰。
抽象类: abstract class 类名 {}
抽象方法: public abstract void 方法名 ();
2、抽象类不一定有抽象方法,有抽象方法的类一定是抽象类。
3、——抽象类中可以有构造方法,但是
抽象类不能实例化
,那么构造方法有什么作用呢?
——用于子类访问父类数据时的初始化。
4、抽象类不能实例化,它是按照多态的方式,通过具体的子类进行实例化。
5、抽象类的子类,要么是抽象类,要么重写抽象类中的所有抽象方法。
抽象类的成员特点
1、成员变量:可以是变量,也可以是常量
2、构造方法:用于子类方法访问父类数据时的初始化
3、成员方法:既可以是抽象的,也可以是非抽象的
抽象方法:强制要求子类做的事情
非抽象方法: 子类继承的事情,提高代码的复用性
案例演示
抽象类
//抽象类——Animal
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();
}
}
案例演示
假如我们在开发一个系统时需要对员工(Employee)类进行设计,员工包含3个属性:姓名、工号以及工资(salary)。
经理(Manager)也是员工,除了含有员工的属性外,另为还有一个奖金(bonus)属性。
然后定义工作的方法.
请使用继承的思想设计出员工类和经理类
public class Test {
public static void main(String[] args) {
Employee manager = new Manager();
manager.name = "玉玉";
manager.num = "001";
manager.salary = 20000;
((Manager) manager).bonus = 12*1000;
}
}
abstract class Employee {
String name;
String num;
double salary;
public abstract void work();
}
class Manager extends Employee {
double bonus;
@Override
public void work() {
System.out.println("经理的工作是写需求文档");
}
}
class Coder extends Employee {
@Override
public void work() {
System.out.println("程序员的工作是编写程序");
}
}
问题1
:一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
答案: 可以 。这么做是为了不在其它类中实例化该类,而是通过该类的子类去实例化。
问题2
:abstract不能和哪些关键字共存?
答案:
static:
static修饰后可以直接类名调用方法,但是抽象方法没有实现体,调用无意义;
final:
final修饰的类不能被继承,修饰的方法不能被重写,修饰的成员变量只能为常量;但是继承的子类要重写这个抽象方法,所以会出现矛盾;
private:
抽象方法是为了让子类重写并给定实现体,但是private修饰的方法只能本类访问,不能被继承,所以矛盾。
接口
为了体现事物功能的扩展性,Java中提供了接口来定义这些额外功能,并不给出具体实现。
接口的特点
1、接口用关键字
interface
表示:
interface 接口名 {}
2、类实现接口用
implements
表示 :
class 类名 implements 接口名 {}
3、
接口不能实例化,只能按照多态的方式实例化。
4、接口的子类可以是抽象类,也可以是具体类。
必须要重写接口中的所有抽象方法。
接口成员特点
1、
成员变量:只能是常量,并且是静态的。
默认修饰符:public static final
建议自己给出修饰符。
2、
构造方法:接口中没有构造方法。
3、
成员方法:只能是抽象方法。
默认修饰符:public abstract
建议自己给出修饰符。
案例演示
多态和接口
//接口
interface myInterface {
//成员变量:只能是常量,并且是静态的。默认修饰符:public static final
int age = 100;
public static final String name = "大黄";
//成员方法:只能是抽象方法。默认修饰符:public abstract
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();
/*现在我们知道了,因为mDog类中继承了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();
}
}
类与类,类与接口,接口与接口的关系
1、类与类:继承关系,
只能单继承
,多层继承
2、类与接口:实现关系,可以单实现,也
可以多实现
,可以在继承一个类的情况下
实现多个接口
3、接口与接口:继承关系,可以单继承,也
可以多继承
interface A {
}
interface B extends A {
}
class C {
}
class D extends C {
}
//接口与接口之间支持多继承,也可以多层继承
interface E extends A, B {
}
//类与类之间只能单继承,但是可以实现多个接口
class F extends C implements A, B {
}
抽象类和接口的区别?
1、成员区别:
抽象类:成员变量可以是变量也可以是常量,有构造方法,成员方法可以抽象也可以非抽象。
接口:成员变量只能是常量,成员方法只能是抽象的
2、关系区别:
类与类——继承,单继承
类与接口——实现,单实现,多实现
接口与接口——继承,单继承,多继承
3、设计理念区别
抽象类被继承体现的是:“is a”的关系,抽象类中定义的是该继承体系的共性功能。
接口被实习体现的是:“like a”的关系,接口中定义的是该继承体系的扩展功能。