面向对象-多态&抽象类&接口

一.多态

1.认识多态

多态是在继承/实现情况下的一种现象,表现为对象多态和行为多态

  • 多态的前题
  1. 有继承/实现关系
  2. 有方法的重写
  3. 存在父类引用指向子类对象
  • 多态的注意事项:多态是对象,行为的多态;变量则不涉及多态
  • 代码执行:编译看左,运行看右
    /**
     * 父类
     */
    public class People {
    
        //省略其他公共的属性和方法
    
        //公共的方法跑步
        public void run() {
            System.out.println("正常跑步");
        }
    }
    
    
    //学生子类,继承people
    public class Student extends People{
        @Override
        public void run() {
            System.out.println("学生跑步,蹦蹦跳跳");
        }
        //独有的方法
        public void study () {
            System.out.println("努力学习");
        }
    }
    
    //讲师子类,继承people
    public class Teacher extends People {
    
        @Override
        public void run() {
            System.out.println("讲师跑步,颤颤巍巍");
        }
    }
    
    
    //测试
    public class Demo {
        public static void main(String[] args) {
            //之前的写法
            //Student student = new Student();
            //使用多态的形式创建Student对象
            People p1 = new Student();
            p1.run();
            System.out.println(p1.toString());
    
            //使用多态的形式创建Teacher对象
            People p2 = new Teacher();
            p2.run();
        }
    }
    2.多态的好处
  • 在多态形势下,等号左右两边松耦合,更便于修改和维护
  • 在多态下,定义方法时,可以使用父类类型作为形参,那么该方法可以接收该父类下所有子类的对象
public class Demo {
    public static void main(String[] args) {
        //1. 在多态形式下,等号左右两边松耦合,更便于修改和维护
        Person person = new Teacher();
        person.run();
        person.run();
        person.run();
        person.run();

        //2. 在多态下, 定义方法时, 可以使用父类类型作为形参, 那么该方法可以接收该父类下所有子类的对象
        Person person1 = new Teacher();
        Person person2 = new Student();
        login(person1);
        login(person2);
    }

    /**
     * 登录方法,
     *     验证用户名字和密码(省略)
     *     跳转不同的主页
     */
    public static void login(Person person) {
        //验证用户名字和密码(省略)
        //跳转不同的主页
        person.jump();
    }

}



class Person{
    public void run(){}

    public void jump() {
        System.out.println("---登录---");
    }
}



class Student extends Person{
    @Override
    public void run() {
        System.out.println("学生跑的快");
    }

    @Override
    public void jump() {
        System.out.println("学生登录成功-好好学习");
    }
}



class Teacher extends Person{
    @Override
    public void run() {
        System.out.println("老师跑的慢");
    }

    @Override
    public void jump() {
        System.out.println("老师登录成功,努力备课");
    }
}
3.多态的弊端

不能直接使用子类特有的功能(解决方案:强制类型转换)

  • 多态中的转型

子-->父(小到大 自动转换):也称为向上转型,父类引用指向子类对象 Person p = new Student();

父-->子(大到小 强制转换):也称为向下转型,父类引用转为子类对象 Student s = (Student)p;

  • 强转风险

强转是存在风险的,如果转为父类引用记录的真实子类对象,那么不会报错(否则会报ClassCastException)

如果想规避这个风险,可以在强转前,使用instanceof关键字,判断变量对应的类型

public class Demo {
//    public static void main(String[] args) {
//        //1、多态创建学生
//        Person p1 = new Student();
//        //2、调用方法
//        p1.run();
//        //3、调用学生对象中独有的功能
//        //将 p1 转化成 子类 (父类转化子类:强转)
//        Student stu =  (Student) p1;
//        stu.study();
//    }

    public static void main(String[] args) {
        //1、多态创建学生
        Person p1 = new Teacher();
        System.out.println(p1);
        //2、调用方法
        p1.run();
        //3、调用学生对象中独有的功能
        //4、判断真实类型和强转的类型是否一致    变量 instanceof  类型
        if (p1 instanceof Student) {
            Student stu = (Student) p1; //  ClassCastException:真实类型与强转类型不一致
            stu.study();
        } else {
            System.out.println("哈哈,你小子被我识破了");
        }
    }
}

//父类
class Person{
    public void run(){}
}

//子类
class Student extends Person {
    @Override
    public void run() {
        System.out.println("学生跑的快");
    }
//独有的方法
    public void study(){
        System.out.println("学生在学习~~~~");
    }
}

class Teacher extends Person {
    @Override
    public void run() {
        System.out.println("老师跑的慢");
    }

    //独有的方法
    public void teach(){
        System.out.println("老师在上课~~~~");
    }
}

二.final

 final关键字是最终的意思,可以修饰(类,方法,变量)

  1. 修饰类:该类被称为最终类,类不能再被继承
  2. 修饰方法:该方法被称为最终方法,方法不能再被重写
  3. 修饰变量:该变量只能被赋值一次,赋值完毕之后不能再被修改 

成员变量:声明时赋完值,或者在构造方法结束之前完成赋值

局部变量:变量只能被赋值一次

  • final修饰变量的注意事项

基本类型变量:变量记录的数据不能再被改变

引用数据变量:变量记录的地址不能再被改变,但是地址对应的堆内存中内容可以改变

public class Demo {
    public static void main(String[] args) {
    }

}

class Father {
    public void run() {
        System.out.println("爸爸在跑");
    }
}

class Son extends Father {
    String name = "123";
    String school;

    @Override
    public void run() {
        final int age = 10;
        final int [] array = {1,2,3};
        array[2] = 5;

        int [] arr2 = {2,3,4};
        //array = arr2;

        System.out.println(age + "岁的儿子在跑");
    }
}

 三.抽象类

1.认识抽象类

在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类,成员方法

abstract修饰类,这个类就是抽象类

abstract修饰方法,这个方法就是抽象方法,抽象方法没有方法题体

  • 抽象类的特点
  1. 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  2. 类该有的成员(成员变量,方法,构造器)抽象类都可以有
  3. 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
  4. 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类
    public class Demo {
        public static void main(String[] args) {
            Person p1 = new Student();
            p1.run();
        }
    }
    
    //需求1: 将Person类声明为抽象类
    abstract class Person {
        //成员变量
        private String name;
    
        //成员方法
        public void eat(){
            System.out.println("吃饭");
        }
    
        //需求2: 将run方法修改为抽象方法
        public abstract void run();
    }
    
    class Student extends Person{
    
        @Override
        public void run() {
            System.out.println("学生跑");
        }
    }
    
    class Teacher extends Person{
    
        @Override
        public void run() {
            System.out.println("老师跑");
        }
    }
    2.抽象类的使用场景
  • 将所有子类中重复的代码,抽取到抽象的父类中,提高了代码的复用性(先编写子类,在编写抽象类)
  • 我们不知道系统未来具体的业务时,可以先定义抽象类,将来让子类去继承实现,提高了代码的扩展性(先编抽象类,在编写子类)
/*
需求
    某宠物游戏,需要管理猫、狗的数据。
        猫的数据有:名字;行为是:喵喵喵的叫~
        狗的数据有:名字;行为是:汪汪汪的叫~
*/
public class Demo {
    public static void main(String[] args) {
        //创建猫的对象
        Animal an = new Cat();
        an.setName("加菲猫");
        an.cry();
    }
}

//动物的父类,公共的属性和方法
abstract class Animal {

    private String name;

    public abstract void cry();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//定义猫的java类
class Cat extends Animal {
    @Override
    public void cry() {
        System.out.println(super.getName() + "喵喵喵的叫~");
    }
}
//定义枸的java类
class Dog extends Animal {
    @Override
    public void cry() {
        System.out.println(super.getName() + "汪汪汪的叫~");
    }
3.模板方法设计模式

一个功能的完成需要经过一系列步骤,这些步骤是固定的,但是中间某些步骤具体行为是待定的,在不同场景下行为不同

  • 使用思路
  1. 定义一个抽象类,提供模板方法
  2. 模板方法中,需要让子类自己实现的地方,定义为抽象方法
  3. 子类只需要继承该抽象类,重写抽象方法即可完成
  • 建议使用final关键字修饰模板方法,模板方法是给对象直接使用的,不能被子类重写,一旦子类重写了模板方法,模板方法就失效了

/**
 * 抽象类模版
 */
public abstract class Person {
    /**
     * 1、定义模版(必须具有执行的步骤和顺序)
     */
    public final void work() {
        System.out.println("1、吃饭");
        //具体的工作
        doWork();
        System.out.println("3、睡觉");
    }

    /**
     * 2、定义具体的业务功能的抽象方法
     */
    public abstract void doWork();
}


/**
 * 子类
 */
public class Teacher extends Person{



    @Override
    public void doWork() {
        System.out.println("讲课");
    }
}


public class Student extends Person{
    @Override
    public void doWork() {
        System.out.println("学习");
    }
}


public class Demo {
    public static void main(String[] args) {
        Person p1 = new Teacher();
        p1.work();
    }
}

四.接口

1.接口概述

Java提供了一个关键字interface,用这个关键字可以定义一个特殊的结构:接口

  • 定义格式

public interface 接口名{

     成员变量(接口中的成员变量都是常量,默认是被public static final修饰的)

     成员方法(接口中的成员方法都是抽象方法,默认是被public abstract修饰的)

     注意:接口中不能有构造方法和代码块

}

  • 注意事项
  1. 接口不能直接创建对象
  2. 接口是用来被类实现(implements)的,实现接口的类称为实现类
  3. 一个类可以实现多个接口,实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。修饰符class 实现implements 接口1,接口2,接口3,......{}
/**
 * 定义接口
 */
public interface UserInterface {
    //定义成员变量, public static final
    String name = "zhangsan";

    //定义成员方法, public abstract
    void save();  //抽象方法,需要被实现
}


public interface HeadInterface {

    void eat();
}

/**
 * 1、定义一个实现类,实现UserInterface接口
 * 2、实现接口中的抽象方法
 */
public class UserInterfaceImpl implements UserInterface {

    @Override
    public void save() {
        System.out.println("保存");
    }
}

public class Demo {
    public static void main(String[] args) {
        // 面相接口编程,创建对象,调用方法
        // 接口 变量 = new 实现类()   //是多态的第二种情况
        UserInterface ui = new UserInterfaceImpl();
        ui.save();
    }
}
2.接口的好处

让程序可以面向接口编程,这样就可以灵活方便的切换各种业务实现(解耦合)

/**
 * 接口的作用:解耦(松散耦合)
 * 1、接口是用来定义规范
 * 2、定义好,登录或者注册等功能的方法名,参数,返回值类型
 */
public interface UserService {

    void login();

    void register();
}

//这是一个用户操作类,可以完成用户的注册、登录功能
public class UserServiceImpl implements UserService {

    // 注册
    public void register() {
        //省略手机号码登验证的步骤
        System.out.println("我是通过手机号码注册的");
    }

    // 登录
    public void login() {
        //省略发短信和验证短信验证码的步骤
        System.out.println("通过手机验证码登录");
    }

}


//这是一个用户操作类,可以完成用户的注册、登录功能
public class UserServiceImpl2  implements UserService {

    @Override
    public void login() {
        System.out.println("通过账号/密码登录");
    }

    @Override
    public void register() {
        System.out.println("我是通过账号/密码注册的");
    }
}

public class UserServiceImpl3 implements  UserService{
    @Override
    public void login() {
        System.out.println("微信登录");
    }

    @Override
    public void register() {
        System.out.println("微信注册");
    }
}


//使用这个类  模拟一个调用这角度的类
public class Demo {
    public static void main(String[] args) {
        //调用别人写好的代码完成登录和注册
        //UserServiceImpl usi = new UserServiceImpl();
        //usi.register();
        //usi.login();

//        UserServiceImpl2 usi1 = new UserServiceImpl2();
//        usi1.zhuce();
//        usi1.denglu();

        //面相接口调用
        UserService us = new UserServiceImpl3();
        us.register();
        us.login();
    }
}



3.interface 新特性
/*
JDK8开始,接口中新增的三种方法
    1、默认方法(jdk8开始支持):对接口中的方法提供默认实现
        使用default修饰,有方法体,可以但是不强制要求实现类重写, 只能通过实现类的对象调用
    2、静态方法(jdk8开始支持):方便调用
         使用static修饰,有方法体,只能通过接口名调用
    3、私有方法(jdk9开始支持):提高代码复用性
        使用private修饰,服务于接口内部,用于抽取相同的功能代码
*/
public class Demo {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.eat();
        a.defaultPrint();

        Animal b = new Cat();
        b.eat();
        b.defaultPrint();

        Animal.print();
    }
}

interface Animal {
    void eat();
    //默认方法:有方法体,有默认的功能实现。实现类可以选择实现此方法也可以不实现
    default void defaultPrint() {
        System.out.println("defaultPrint");
        privtePrint();
    }

    //静态方法
    static void print() {
        System.out.println("动物不知道在干嘛");
    }
    //私有方法
    private void privtePrint() {
        System.out.println("privtePrint");
    }
}

class Dog implements Animal{

    public void defaultPrint() {
        System.out.println("dog实现了defaultPrint方法");
    }

    public void eat(){
        System.out.println("动物苏醒了");
        System.out.println("动物开始用膳");
    }
}

class Cat implements Animal{
    public void eat(){
        System.out.println("动物苏醒了");
        System.out.println("动物开始用膳");
    }
}

4.interface注意事项
/*
类和接口的关系总结
    1、类和类: 继承(extends)关系,对于类只支持单继承,不支持多继承,但是可以多层继承
    2、接口和接口: 继承(extends)关系,对于接口,支持多继承
    3、类和接口:实现(implements)关系,支持多实现,一个类同时实现多个接口


接口使用注感事项
    1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
    2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
    3、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
    4、一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的
*/
public class Demo {
    public static void main(String[] args) {

    }
}

interface A {
    void print();
    default void defaultPrint() {
        System.out.println("defaultPrint");
    }
}

interface B {
    void print();
    default void defaultPrint() {
        System.out.println("defaultPrint");
    }
}

// 1、一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承。
//interface C extends A ,B {
//
//}

//2、一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
//class D implements A,B {
//    @Override
//    public void print() {
//        System.out.println("xxxx");
//    }
//}


// 3、一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
//class E implements A,B {
//    @Override
//    public void print() {
//
//    }
//
//    @Override
//    public void defaultPrint() {
//        A.super.defaultPrint();
//    }
//}

class F {
    public void show() {
        System.out.println("展示");
    }
}

interface G {
    void show();
}

class H extends F implements G {

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值