多态基础知识 抽象类 接口
1.概念 前提 优缺点
一:含义:多种形态,同一个对象在不同时刻表现出来的不同形态
二:实现前提:
1. 有继承/实现关系
2. 有方法重写
3. 有父类引用指向子类对象
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
//有继承关系
public class Cat extends Animal {
//有重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}public class AnimalDemo {
public static void main(String[] args) {
//有父类的引用指向子类对象
Animal a = new Cat();
}
}
三:优缺点
好处:提高程序的扩展性:具体体现就是我们在定义方法的时候使用父类作为我们的方法的参数 将来在使用的时候将子类类型放入进行操作
弊端:多态的形式 它是不能访问具体的子类所特有的功能
public class Animal {
public int age = 40;
public void eat(){
System.out.println("动物吃东西");
}
}
public class Cat extends Animal {
public int age = 20;
public int weight = 10;
//有重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void plagGame(){
System.out.println("猫捉老鼠");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class AnimalControl {
public void control(Animal animal){
animal.eat();
}
}
public class AnimalDemo {
public static void main(String[] args) {
AnimalControl ac = new AnimalControl();
Cat cat = new Cat();
ac.control(cat); //猫吃鱼
Dog dog = new Dog();
ac.control(dog);//狗吃骨头
}
}
2.多态中成员访问特点
2.1多态中成员变量访问特点
**成员变量:编译看左边,执行看左边。**虽然我们最终在内存的对象是子类cat,通过多态的形式来访问成员变量的时候 它的编译和运行都要看左边animal
public class Animal {
public int age = 40;
public void eat(){
System.out.println("动物吃东西");
}
}
//有继承关系
public class Cat extends Animal {
public int age = 20;
public int weight = 10;
//有重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void plagGame(){
System.out.println("猫捉老鼠");
}
}
public class AnimalDemo {
public static void main(String[] args) {
//有父类的引用指向子类对象
Animal a = new Cat();
//下面来演示多态中成员的访问特点
System.out.println(a.age); //输出40 这个40是animal中的age的值 而不是cat中age的值 通过多态的形式来访问成员变量的时候 它的运行要看左边animal
// System.out.println(a.weight); 这里会报错 为什么会报错? 这里是因为虽然我们最终在内存的对象是cat,
//但是在外界看到的是animal 动物这个引用 所以我们通过多态的形式来访问成员变量的时候 它的编译要看左边animal它有没有
//因为animal它没有weight 所以它报错了
}
}
2.2多态中成员方法访问特点
**成员方法:编译看左边,执行看右边。**通过多态的形式来访问成员方法的时候 它的编译也是要看左边父类的有没有这个方法 要是父类没有这个方法 它会报错 但运行的时候看的是子类右边的cat的
public class Animal {
public int age = 40;
public void eat(){
System.out.println("动物吃东西");
}
}
//有继承关系
public class Cat extends Animal {
public int age = 20;
public int weight = 10;
//有重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void plagGame(){
System.out.println("猫捉老鼠");
}
}
public class AnimalDemo {
public static void main(String[] args) {
//有父类的引用指向子类对象
Animal a = new Cat();
a.eat(); //输出子类方法的内容 运行的时候它要看右边子类的内容 输出 猫吃鱼
//a.playGame(); 这行报错 编译时也是要看左边父类有没有这个方法
}
}
那为什么多态中的成员变量和成员方法的访问是不一样的?
因为成员方法有重写,而成员变量没有
3.多态中的转型
前面我们说过了 多态的弊端是不能使用子类的特有的功能 而多态中的转型它就能帮助我们实现使用子类中的特有的功能
3.1向上转型
向上转型:从子到父 父类引用指向子类对象
public class Animal {
public void eat(){
System.out.println("动物吃东西");
}
}
//有继承关系
public class Cat extends Animal {
//有重写
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void plagGame(){
System.out.println("猫捉迷藏");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
/*
* 向上转型:从子到父 父类引用指向子类对象*/
public class AnimalDemo {
public static void main(String[] args) {
//多态
Animal a = new Cat();//向上转型,父类引用指向子类对象
a.eat(); //输出猫吃鱼
//a.playGame() 这里是会报错的 因为父类没这个方法
//但是现在我就是想使用这个cat特有的playGame() 怎么办?
//创建cat类对象 就可以了
Cat c = new Cat();
c.eat();//输出猫吃鱼
c.plagGame(); //输出猫捉迷藏 除了这种方法 还有其他方法吗?--向下转型
}
}
3.2 向下转型
向下转型:从父到子 父类引用转为子类对象
/*
* 向下转型:从父到子 父类引用转为子类对象*/
public class AnimalDemo {
public static void main(String[] args) {
//多态
Animal a = new Cat();//向上转型,父类引用指向子类对象
a.eat(); //输出猫吃鱼
//向下转型
Cat c=(Cat)a;
c.eat(); //输出猫吃鱼
c.plagGame(); //输出猫捉迷藏 通过向下转型解决了多态的弊端:就是不能访问子类的特有的功能
}
}
3.3多态转型内存图解
黑马程序员全套Java教程
/*
* 向下转型:从父到子 父类引用转为子类对象*/
public class AnimalDemo {
public static void main(String[] args) {
//多态
Animal a = new Cat();//向上转型,父类引用指向子类对象
a.eat(); //输出猫吃鱼
//向下转型
Cat c=(Cat)a;
c.eat(); //输出猫吃鱼
c.plagGame(); //输出猫捉迷藏 通过向下转型解决了多态的弊端:就是不能访问子类的特有的功能
a = new Dog();
a.eat();//狗吃骨头
// Cat cc=(Cat)a; 报 ClassCastException 类型转化异常 这里的a是一个dog的对象 你把它转为cat 是不行的
// c.plagGame();
}
}
4.抽象类
4.1概念 特点
上面 Aniaml a=new Animal 其实这样是不对的 这样是不知道是哪一个动物的 动物本身并不是一个具体的事物 而是一个抽象的事物,只有 cat dog 是真正的事物
在上面说 父类中的eat() 要是子类都没有对这个方法进行重写 那他只能调用父类的eat() 但我们都知道 每一个具体的动物eat()表现都应该是不一样的
4.1在java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
public abstract class Animal {//抽象类
public abstract void eat(); //抽象方法
}
4.2.特点:
1.抽象类和抽象方法必须使用abstract关键字修饰
2.抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
3.抽象类不能实例化—如何实例化呢?参照多态的方式 通过子类对象实例化 这叫抽象多态
4.抽象类的子类
(1)要么重写抽象类所有的抽象方法
(2)要么是抽象类
public abstract class Animal {
public abstract void eat();
//1.抽象类里可以有非抽象方法 也可以没有抽象方法(不建议,没意义) 要是有抽象方法 它一定是一个抽象类
public void sleep(){
System.out.println("睡觉");
}
}
//有继承关系
public class Cat extends Animal {
@Override//要实现父类的抽象方法
public void eat() {
System.out.println("猫吃鱼");
}
}
public abstract class Dog extends Animal {
//要是你就是不想实现抽象类中的抽象方法 那你也可以把这个类也改为抽象类 就不会报错
}
public class AnimalDemo {
public static void main(String[] args) {
// 2. Animal a = new Animal(); 这会报错 抽象类是不能实例化的
//3.那抽象类如何创建对象呢?--它是会参照多态的形式来创建对象
Animal a=new Cat();
a.eat(); //猫吃鱼 4.猫类继承于动物类(抽象类) 猫类就要实现它的所有抽象方法 否则会报错 --也可把该类改为抽象类 --这样不报错
//要是你就是不想实现抽象类中的抽象方法 那你也可以把这个类也改为抽象类 就不会报错
a.sleep();//睡觉
}
}
4.2抽象类的成员特点
就是要研究抽象类里面可以有哪些成员?
- 成员变量:可以使变量 也可以是常量
- 构造方法 :有构造方法,但是不能直接实例化,那么,构造方法的作用是什么呢?用于子类实例化时访问父类数据的初始化
- 成员方法:可以有抽象方法:限定子类必须完成某些动作,也可以没有静态方法(不建议,没意义) ;也可以有非抽象方法:提高代码的复用性
public abstract class Animal {
private int age = 20; //1.抽象类可以包含成员变量的 它可以是变量 也可以是常量
private final String city = "北京";
//2.抽象类中是包含成员方法 这个成员方法是包含抽象方法和非抽象方法
//抽象类是可以有非抽象方法的
public void show() {
age = 40;
System.out.println(age);
//city="上海" //常量不能再次赋值
System.out.println(city);
}
public abstract void eat();
//3.抽象类可以有构造方法的
public Animal() {
}//那我们不是说抽象类不能实例化吗?为什么会有构造方法?
/*这里强调一下 我们说抽象类不能实例化 是指抽象类不能直接实例化
但是它是可以通过多态的方式去实例化 子类在实始化的过程中也是要访问父类的构造方法去完成父类的实例化*/
public Animal(int age) {
this.age = age;
}
}
//有继承关系
public class Cat extends Animal {
@Override//要实现父类的抽象方法 否则会报错
public void eat() {
System.out.println("猫吃鱼");
}
}
public class AnimalDemo {
public static void main(String[] args) {
Animal a=new Cat();
a.eat(); //猫吃鱼
a.show(); //40
//北京
}
}
5.接口
5.1概念
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用
java中的接口更多的体现在对行为的抽象
5.2接口的特点
- 接口用关键字interface修饰
public interface 接口名{} - 类实现接口用implements表示
public class 类名 implements 接口名{} - 接口不能实例化(直接)
接口如何实例化 参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态(很少用),抽象类多态,接口多态
多态的前提:有继承或者实现关系;有方法的重写;有父(类/接口)引用指向(子/实现)类对象 - 接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类 但它的子类继承时也得实现接口中的抽象方法
/*
* 定义了一个接口
* */
public interface Jumpping {
public abstract void jump();
}
//有继承关系
public class Cat implements Jumpping {
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
public class JumppingDemo {
public static void main(String[] args) {
// Jumpping j=new Jumpping(); 它会报 接口是抽象的 不能直接被实例化
// 接口也是一个抽象的内容 它主要是对行为进行抽象
Jumpping j=new Cat();//3.接口的实例化也是采用多态的形式
j.jump(); //输出猫可以跳高了
}
}
public abstract class Dog implements Jumpping {
//抽象类在实现接口的时候 是可以不实现接口中的方法 但是当时它的子类要继承它的时候 还得重写接口中的抽象方法
}
5.3接口的成员特点
- 接口中的成员变量
只能是常量
默认修饰符:public static final
public interface Inter {
public int num = 10;//变量
public final int num2 = 20;//常量
}
public class InterImpl implements Inter{
}
public class InterfaceDemo {
public static void main(String[] args) {
Inter i = new InterImpl();
// i.num = 20; 这里会报错,说num是被final修饰的 但是我没有用final修饰num
//尤其可见 接口中的这个变量默认是final修饰的 所以接口中是没有成员变量的 我们是把它看做是常量
System.out.println(i.num);//10
System.out.println(i.num2);//20
System.out.println(Inter.num);//可以直接通过Inter.属性名访问 说明它默认是被static修饰的
//接口中的成员变量它只能是常量 并且它还被static修饰的
//它的默认修饰符是 public static final int num3= 30 它与直接写int num3=30是一样的
}
}
- 接口中的构造方法
接口是没有构造方法的 以为接口主要是对行为进行抽象,是没有具体的存在 ,一个类如果没有父类, 默认就是继承Object类
public interface Inter {
//public Inter(){} 它会报错 所以接口是没有构造方法的,因为接口主要是对行为进行抽象,
// 所以它是没有具体的存在的
}
public class InterImpl implements Inter{
public InterImpl(){
super(); //那这有疑惑了 这里是调用了哪里的呢?不是说接口是没有构造方法的吗?
//这是因为在java中 所有的类都是直接或者间接地继承object类 object类只有一个无参构造方法
//所以我们默认都是使用无参构造 因为祖宗只有无参构造 所以这里用的是object中的构造方法
/*
public class InterImpl implements Inter
等价于 public class InterImpl extends Object Implements inter
* */
}
}
3.接口中的成员方法
只能是抽象方法 默认修饰符:public abstract
public interface Inter {
//public void show(){} 它是会报错的 --接口里是不能有非抽象方法的
public abstract void method();
void show();//默认是抽象的
}
public class InterImpl implements Inter{
@Override
public void method() {
System.out.println("method");
}
@Override
public void show() {
System.out.println("show");
}
}
6.类和接口的关系,抽象类与接口的区别
6.1类和接口的关系
- 类和类的关系:继承关系,只能单继承,但是可以多层继承
- 类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系:继承关系,可以单继承,也可以多继承
6.2抽象类与接口的区别
6.2.1语法层面的区别:
(1)成员区别
抽象类:变量,常量,有构造方法,有抽象方法 也有非抽象方法
接口 : 常量,抽象方法
(2)关系区别
类与类:继承 单继承
类与接口:实现 可以单实现 也可以多实现
接口和接口 : 继承,单继承,多继承
6.2.2设计层面的区别:
抽象类:对类,对事物的抽象,包括属性,行为
接口:对行为抽象 , 主要是行为