Day06_面向对象二:多态、final关键字、抽象类、接口

多态

  1. 前提: 存在继承/实现关系,存在父类引用子类对象,存在方法重写
  2. 多态是对象、行为的多态。属性(成员变量)不谈多态
  3. 多态是指: 父亲类型 变量名 = new 子类对象
package demo0320;

public class Father {
    String name = "父亲";
    public void run(){
        System.out.println("父亲正在奔跑");
    }
}
package demo0320;

public class SonFrist extends Father{
    String name = "儿子1";
    @Override
    public void run(){
        System.out.println("孩子1正在奔跑");
    }
}
package demo0320;

public class SonSecond extends Father{
    String name = "儿子2";
    @Override
    public void run(){
        System.out.println("孩子2正在奔跑");
    }
}
package demo0320;


public class test {
    public static void main(String[] args) {
        // 对象多态
        Father F1 = new SonFrist();
        // 行为多态
        // 编译看左边,执行看右边
        F1.run();
        System.out.println(F1.name);
        Father F2 = new SonSecond();
        F2.run();
        System.out.println(F2.name);
    }
}

多态的优势

1.可以实现解耦合,右边子类对象可以随时切换,后续可以根据业务需求随时变化

2.可以使用父类类型作为变量类型,右边可以是一切子类对象

3.多态的缺陷: 多态不能使用子类的独有功能

多态的强制类型转换

目的: 为了解决父类对象无法调用子类对象的独有功能

package demo0320;

public class Father {
    String name = "父亲";
    public void run(){
        System.out.println("父亲正在奔跑");
    }
}
package demo0320;

public class SonFrist extends Father{
    String name = "儿子1";
    @Override
    public void run(){
        System.out.println("孩子1正在奔跑");
    }

    public void Certificate(){
        System.out.println("孩子1有5张奖状");
    }
}
package demo0320;

public class SonSecond extends Father{
    String name = "儿子2";
    @Override
    public void run(){
        System.out.println("孩子2正在奔跑");
    }
    public void Certificate(){
        System.out.println("孩子2有3张奖状");
    }
}
package demo0320;


public class test {
    public static void main(String[] args) {
        // 对象多态
        Father F1 = new SonFrist();
        Father F2 = new SonSecond();
        // 此处会出现问题,虽然是new的子类对象,但是编译过程中看到是左边,也就是父类对象,由于父类中没有Certificate方法,所以会编译错误
        // F1.Certificate()
        // 可以通过强转,来保障编译器的顺利通过
        SonFrist s1 = (SonFrist) F1;
        s1.Certificate();
        // 此时不会ide不会产生红色异常,但是在运行是会报错ClassCastException
        // 原因在于,F1右边是创建的SonFrist对象,此时强转为另一个不同的子类对象,这是不被允许的
        // SonSecond s2 = (SonSecond) F1;
        SonSecond s3 = (SonSecond) F2;
        // 比较好的强转方式是在强转前,通过instanceof关键字先判断是否是期望的对象
        // 通过instanceof先判断在强转,可以有效保障强转不产生异常
        if(F1 instanceof SonSecond){
            SonSecond s4 = (SonSecond) F2;
        }
    }
}

final

  1. final关键字有最终的意思,可以修饰(类、方法、变量)
  2. 修饰类: 该类被称为最终类,特点是不能被继承了
  3. 修饰方法: 该方法称为最终方法,特点是不能被重写了
  4. 修饰变量: 该变量只能被赋值一次
package demo0320;

final class A{};
// A类被final修饰,无法在被继承
// class B extends A{}
class C{
    final public void run(){
        System.out.println("对象在奔跑");
    }
}
class D extends C{
    // run方法被final修饰,不能在被重写
//    @Override
//    public void run(){
//
//    }
}
public class test {
    // 由于final只能赋值一次,且该变量在类被创建的时候没有值的情况下,会给到一个默认值null,导致后续在想对这个变量修改变得不在可能
    // 所以对于这些再类创建时候就产生默认值的变量,如果永final修饰,必须给到想要的初始值
    public static final String name = "童话";
    public final int age = 10;
    public static void main(String[] args) {
        final int a;
        a = 10;
        // 在修改a变量的值是不可以的,赋值只能有一次
        // a = 20;
    }
    public static void buy(final double z){
        // 用final修饰的形参,是无法在函数内部修改的
        // z = 0.75;
    }
}

final修饰变量的注意事项

  1. 修饰基本类型的变量,变量存储的数据不能被改变
  2. 修饰引用类型的变量,变量存储的地址不能被改变,但是地址所指向的对象的内容是可以改变的

常量

  1. 使用static final修饰的成员变量被称为常量
  2. 作用: 通常用来记录系统的配置信息,不可被改变
  3. 优势: 代码可读性更好,可维护性也更好
  4. 程序编译后,常量会被宏替换:出现常量的地方全部都会被替换成其记住的字面量,这样可以保证使用常量和使用字面量的性能是一样的

抽象类

  1. 在java中有一个关键字abstract,用它修饰的类叫做抽象类,用它修饰的方法叫做抽象方法
  2. 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  3. 类该有的成员(成员变量、方法、构造器)抽象类也可以有
  4. 抽象类的主要特点: 抽象类是无法new对象的,仅仅作为一种特殊的父类,让子类继承并实现
  5. 一个类继承抽象类,必须重写完所有的抽象方法,否则这个类也必须被定义为抽象类
  6. abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
package demo0320;

// abstract修饰的类称为抽象类
// 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类,ublic abstract class Father 丢掉abstract会报错,方法丢掉abstract没问题
// 类该有的成员(成员变量、方法、构造器)抽象类也可以有
// 抽象类的主要特点: 抽象类是无法new对象的,仅仅作为一种特殊的父类,让子类继承并实现
// 一个类继承抽象类,必须重写完所有的抽象方法,否则这个类也必须被定义为抽象类
public abstract class Father {
    // abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
    public abstract void run();

    public String name;
    public Father(){
        System.out.println("无参数构造器");
    }

    public void eat(){
        System.out.println("普通的成员方法");
    }

}

抽象类的场景和好处

  1. 父类知道每个子类都要实现的某个方法,但是不同子类实现情况不一样,父类就定义为抽象方法,交给子类去重写实现
  2. 个人理解: 在工作中,我作为主管,我只要写好类,和这些类的方法,然后交给实习生去完成这些类的具体内容,因为是抽象类,所以实习生必须要写完我给的方法
package demo0320;
public abstract class Father {
    // abstract修饰的方法称为抽象方法,特点是有方法签名,但是不能有方法体
    public abstract void run();
    public abstract void speak();

    public String name;
    public Father(){
        System.out.println("无参数构造器");
    }

    public void eat(){
        System.out.println("普通的成员方法");
    }

}
package demo0320;

public class SonFrist extends Father{
    String name = "儿子1";
    // 必须要全部重写,少写一个都不行
    @Override
    public void run(){
        System.out.println("孩子1正在奔跑");
    }
    @Override
    public void speak(){
        System.out.println("孩子1在讲话");
    }

    public void Certificate(){
        System.out.println("孩子1有5张奖状");
    }
}
package demo0320;

public class test {
    public static void main(String[] args) {
        // 无法new出来抽象类对象
        // Father F1 = new Father();
        // 但是可以通过父类 变量 = new 子类对象来创建
        Father F1 = new SonFrist();
        F1.eat();
        F1.speak();
    }
}

接口

  1. Java提供了一个关键字interface,用这个关键字可以定义出一个特殊得结构:接口
public interface 接口名称{
	// 成员变量(常量)
	// 成员方法(抽象方法)
}
package demo0320;
// 接口有且只有两个内容,成员变量(常量),成员方法(抽象方法)
public interface A {
    // 成员变量(常量)
    // 会出现异常,因为着不是常量
    // String name;
    // java会自动帮变量添加static final修饰
    // public static final String name = "童话";
    // 所谓得常量,就是要给定初始值,且初始值不会发生改变
    String name = "童话";
    // 这样定义的方法是不被允许得,方法只能是抽象方法
//    public void test(){
//
//    }
    // 这样定义得方法是多余的,因为java也是会自动帮忙添加public abstract
//    public abstract void test();
    void test();
}

接口的实现

  1. 接口不能创建对象,接口时用来被类实现的,通过implements,实现接口的类被称为抽象类
  2. 一个类可以实现多个接口,实现类实现多个接口,必须重写完所有接口的全部抽象方法,否则实现类需要定义为抽象类
package demo0320;

public interface A {
    String name = "童话";
    void test();
}
package demo0320;

public interface B {
    void test1();
    void test2();
}
package demo0320;

public interface C {
    void test3();
}
package demo0320;

public class D implements A,B,C{
    @Override
    public void test() {

    }

    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }

    @Override
    public void test3() {

    }
}

接口的好处

1.弥补了类单继承的不足,一个类可以实现多个接口

2.让程序可以面向接口编程

3.实现类可以同时继承(extends)一个父类和继承(implements)多个接口

4.同多态一样,可以通过 接口类型 变量名 = new 实现类(),可以实现快速切换

接口中新增的三种方法

package demo0320;

public interface E {
    /*
        1.默认方法,必须使用的default修饰,默认会被public修饰
        实现方法, 必须用实现类的对象来访问
     */
    default void test1(){
        System.out.println("默认方法");
    }
    /*
        私有方法,必须使用private修饰(JDK9)
        实例方法: 只能在接口内部的默认方法或者静态方法中调用
     */
    private void test2(){
        System.out.println("私有方法");
    }
    /*
        静态方法,必须使用static修饰,默认会被public修饰
        可以通过接口.方法
     */
    static void test3(){
        System.out.println("静态方法");
    }

    default void test4(){
        test2();
    }
}
package demo0320;

public class D implements E{

}
package demo0320;

public class test {
    public static void main(String[] args) {
       E E1 = new D();
       E1.test1();
       E.test3();
       E1.test4();
    }
}

接口的多继承

1.一个接口可以同时继承多个父类接口

2.接口继承多个父类接口的时,不用重写父类方法

3.继承多个接口时,要保证这多个接口不会出现重复的方法签名,否则无法多继承

4.一个类继承了父类,又同时继承了接口,如果接口和父类中有同名的默认方法,实现类会有限用父类的

5.一个实现类实现多个接口,多个接口存在重复的同名的默认方法,可以不冲突,实现类重写该方法即可

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值