JavaSE笔记_day08_抽象、final关键字、多态

434 阅读9分钟

一.抽象

  • 如果父类当中的方法不确定如何进行方法体实现,那么这就应该是一个抽象方法。
  • 如:动物吃东西  猫吃鱼  狗吃骨头 
  • 将eat定义为抽象方法 
      抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。 

      抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract即可 

如何使用抽象类和抽象方法, 

      1.不能直接创建new抽象类对象 

      2.必须用一个子类来继承抽象父类 

      3.子类必须重写抽象父类当中所有的抽象方法 

               重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。

      4.创建子类对象进行使用

public class DemoMain {
    public static void main(String[] args) {
        // Abimal animal = new Animal();//错误写法,不能直接创建抽象类对象
        Cat cat = new Cat();
        cat.eat();
    }
}
public abstract class Animal {//抽象类
    public abstract void eat();//这是一个抽象方法,代表吃东西,但是具体吃什么不确定
    public void normalMethod(){//这是普通的成员方法
    }
}
public class Cat extends Animal {
    @Override
    public void eat(){
        System.out.println("猫吃鱼!");
    }
}

一个抽象类不一定含有抽象方法 

只要保证抽象方法所在的类是抽象类,即可。

1.抽象类不能创建对象,如果创建,编译无法通过而报错,只能创建其非抽象子类的对象
        理解:加入创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
        理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
        理解:未包含抽象方法的类,目的是不想让调用者创建该类对象,通常用于特殊类结构设计。
4.抽象类的子类,必须重写抽象父类中所有的抽象方法,否则编译无法通过而报错,除非该子类也是抽象类。全部重写之后,此时这个子类就是一个普通的类。
        理解:假设不重写所有的抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
5.抽象类中可以使用静态方法、变量
        理解:静态属于类,类名.调用。与有无对象无关

public class DemoMain {
    public static void main(String[] args) {
        Zi zi = new Zi();
	zi.eat();
    }
}
public abstract class Fu {//抽象类
    public Fu(){
        System.out.println("抽象父类构造方法执行");
    }
    public abstract void eat();//抽象方法
}
public class Zi extends Fu {
    public Zi (){
        //super();无参构造Fu要想运行,只能通过Zi中赠送的的super调用,直接new是不允许的
        System.out.println("子类构造方法执行!");
    }
    @Override
    public void eat() {//抽象方法的实现
        System.out.println("吃饭饭!");
    }
}

二.final关键字

  • 对于类、方法来说,abstract关键字和final关键字不能同时使用,矛盾。

1.final修饰类

格式:
    public final class 类名称{
        //...
    }
含义:
    当前这个类不能有任何的子类(因为不能被继承,即不能作为父类)

2.final修饰成员变量

  • 只能手动赋值一次 或 只能在构造方法中赋值一次 
  • 必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值 
  • 例如:π = 3.1415926... 说明:final修饰的变量,称为常量

3.final修饰方法

  • (子父类的重写关系当中)不能被重写

public /*final*/ class Final{//1.final修饰的类不能被继承
    final int j ;//2.final修饰的成员变量,只能手动赋值一次
    public Final(){//3.或 final修饰的成员变量没有手动赋值,只能构造方法中赋值一次
        j = 999;
    }
    public final void method(){
        System.out.println("我是一个父类中final修饰的方法");
    }
    public static void main(String[] args) {
        final int  a = 10;
        /*a = 20;4.final修饰的变量不能被修改,报错*/
        Final f = new Final();
        /*f.j = 20;final修饰的成员变量不能被修改,报错*/
    }
}	

public class FianalSub extends Final{//从父类Final中继承到了,i,j,a,method()
    /*@Override  5.报错,父类中final修饰的方法,不能被重写
    public final void method(){
    }*/
    public static void main(String[] args) {
        Final f = new Final();
        f.method();//继承使用父类中的method方法
    }
}

三.多态

  • extends继承 或 implements实现,是多态性的前提。

1.概念

  • 表示事物的多种形态 
  •  举例:Person类,人类的表现方式,工程师,医生,学生... 
                  Animal类,cat  dog  snake 
  • 多态的定义:
    1.具有子父类的继承关系   2.需要子类重写父类方法   3.父类的引用指向子类对象

格式:
    父类名称 对象名 = new 子类名称();
    接口名称 对象名 = new 实现类名称();
public class Demo01Multi {
    public static void main(String[] args) {
        //使用多态写法
        Fu obj = new Zi();//父类的引用 指向 子类的对象
        obj.method();//method是子类父类都有的成员方法,规则:new的是谁就是谁,没有则向上找
        obj.methodFu();//methodFu是父类特有的成员方法,new的是Zi,子类没有,向上找,父类中有
    }
}
public class Fu {
    public void method(){
        System.out.println("父类方法!!");
    }
    public void methodFu(){
        System.out.println("父类特有方法!");
    }
}
public class Zi extends Fu{
    @Override
    public void method(){//覆盖重写
        System.out.println("子类方法!!");
    }
}

2.多态中成员变量的使用特点

  • 1.直接通过对象名称访问成员变量:编译看左,运行看左,没有则向上找
  • 2.间接通过成员方法访问成员变量:编译看左,运行看右,没有则向上找

public class Demo01MultiField {
    public static void main(String[] args) {
        //使用多态的写法,父类引用指向子类对象
        Fu obj = new Zi();
        System.out.println(obj.num);//10
        //System.out.println(obj.age);//错误写法,左边Fu中没有
        System.out.println("==========================");
        obj.showNUm();//10
        obj.showNUm();//20
    }
}
public class Fu {
    int num = 10;
    public void showNUm(){
        System.out.println(num);
    }
}
public class Zi extends Fu{
    int num = 20;
    int age = 16;
    @Override
    public void showNUm(){
        System.out.println(num);
    }
}

3.多态中成员方法使用特点

  • 编译看左,运行看右,没有则向上找
public class Demo02MultiMethod {
    public static void main(String[] args) {
        Fu obj = new Zi();//多态 父类引用指向子类对象
        obj.method();//编译看左 父类,父类中有method编译通过,运行看右 子类,子类中有method运行
        obj.methodFu();//编译看左 父类,父类中特有methodFu,编译通过,运行看右 子类,子类中没有向上找运行
    //  obj.methodZi();//错误写法,编译看左,左边是Fu,Fu中没有methodZi方法,所以编译报错
    }
}
public class Zi extends Fu{
    int num = 20;
    int age = 16;
    public void method (){
        System.out.println("子类方法!");
    }
    public void methodZi(){
        System.out.println("子类特有方法!");
    }
}
public class Fu {
    int num = 10;
    public void method (){
        System.out.println("父类方法!");
    }
    public void methodFu(){
        System.out.println("父类特有方法!");
    }
}

4.多态中静态的使用特点

  • 编译看左,运行看左

public class Test(){
    public static void main(String[] args){
        Fu f = new Zi();
        f.speak();//父类中的静态speak!
    }
}
public class Fu(){
    public static void speak(){
        sout("父类中的静态speak!");
    }
}
public class Zi extends Fu(){
    public static void speak(){//静态不可以继承,静态中的同名静态方法
        sout("子类中的静态speak!");
    }
}

5.使用多态的好处

  1. 提高了代码的维护性(继承保证)
  2. 提高了代码的扩展性(由多态保证)


榨汁机案例:
要求:定义一个榨汁机,类中有一个榨汁方法,榨所有水果汁
public class Test {
    public static void main(String[] args) {
        Juicer j = new Juicer();//榨汁机对象
        //水果类型多态
        Fruit f1 = new Apple();
        j.juicing(f1);//榨苹果汁儿!
        Fruit f2 = new Orange();
        j.juicing(f2);//榨橙汁儿!
    }
}
public class Juicer {//定义一个榨汁机类
    public void juicing(Fruit fruit){//榨汁机入口,不同的水果,出不同的果汁
        fruit.flow();
    }
}
public class Fruit  {
    public void flow(){//抽象类型,水果变果汁
    }
}
public class Orange extends Fruit{
    @Override
    public void flow() {
        System.out.println("榨橙汁儿!");
    }
}
public class Apple extends Fruit{
    @Override
    public void flow() {
        System.out.println("榨苹果汁儿");
    }
}

6.对象的上下转型

  • 对象的向上转型,其实就是多态写法:

格式:
    父类名称 对象名 = new 子类名称();				
    Animal  animal  = new Cat();
含义: 
    右侧创建一个子类对象,把它当做父类来看待使用	
    创建了一只猫,当做动物看待,没问题。
注意事项:
    向上转型一定是安全的,从小范围转向了大范围。从小范围的猫,向上转换成为更大范围的动物。
类似于:
    double num = 100;//正确,int -> double 自动类型转换。
缺点:
    对象一旦向上转型为父类,那么就无法调用子类原本特有的内容
解决方案:
    用对象的向下转型[还原]

  • 对象的向下转型,其实就是一个[还原]的动作

格式:
    子类名称 对象名 = (子类名称)父类对象; 
含义:
    将父类对象,[还原]为本来的子类对象			
    Animal animal = new Cat();//本来是猫,向上转型成动物。
    Cat c = (Cat)animal;//本来是猫,已经当做成动物了,还原为本来的猫
注意事项:
    a.必须保证对象创建的时候,就是猫,才能向下转型称为猫。
    b.如果对象创建的时候本来是猫,现在非要向下转型成为狗,报错。
类似于:
    int num = (int)10.0;//可以		int num = (int)10.5;//不可以,精度损失

public class Demo01Main {
    public static void main(String[] args) {
        Animal animal = new Cat();//左父,右子。本来创建的时候是一只猫,向上转型成动物
        animal.eat();//猫吃鱼  //编译看左,动物类确实有,运行看右,右边覆盖重写了  
    //  animal.catchMouse();错误写法,编译看左,父类无此方法
    //向下转型进行还原动作===============================
        Cat c = (Cat)animal;//本来就是一只猫,转为动物了,还原为本来的猫
        c.catchMouse();
    //  Dog d = (Dog)animal;//本来new的时候是一只猫,现在向下转型成为一只狗,编译不报错,运行出现异常java.lang.ClassCastException 类转换异常
    }
}
public abstract class Animal {//抽象类
    public abstract void eat();//抽象方法
}
public class Cat extends Animal {
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//子类特有方法
        System.out.println("猫抓老鼠");
    }
}
public class Dog extends Animal {
    @Override
    public void eat (){
        System.out.println("狗吃骨头");
    }
    public void watchHouse(){
        System.out.println("狗看家");
    }
}

7.如何知道一个父类引用的对象,本类是什么子类?

  • instanceof 关键字  - 返回值类型,Boolean
  • 格式: 
             对象名 instanceof 类型

public class Demo02Instanceof {
    public static void main(String[] args) {
        Animal animal = new Cat();//本来是一只猫,向上转型为动物了,
        animal.eat();//猫吃鱼
        //希望调用子类特有方法,向下转型。
        if (animal instanceof Dog) {//判断一下父类引用本来是不是Dog
            Dog d = (Dog) animal;
            d.watchHouse();
        }
        if (animal instanceof Cat) {//判断一下父类引用本来是不是Cat
            Cat c = (Cat) animal;
            c.catchMouse();
        }
        giveMeAPet(new Dog());
    }
    public static void giveMeAPet(Animal animal) {
        if (animal instanceof Dog) {//判断一下父类引用本来是不是Dog
            Dog d = (Dog) animal;
            d.watchHouse();
        }
        if (animal instanceof Cat) {//判断一下父类引用本来是不是Cat
            Cat c = (Cat) animal;
            c.catchMouse();
        }
    }
}