【Day02-多态、抽象类、接口】

 1.多态        

        1.1 么是多态?

                多态是在继承或者实现情况下的一种现象,表现为:对象多态、行为多态。

        1.2 多态的具体代码体现

         1.3 多态的前提

  1.                 有继承/实现关系
  2.                 有方法的重写
  3.                 存在父类引用指向子类对象

         1.4 多态的注意事项 

                多态是对象、行为的多态,Java中的属性(成员变量)不谈多态。

/*
多态
    是在继承/实现情况下的一种现象, 表现为对象多态和行为多态
    对象多态:子类对象可以多态创建一个Person类,它的对象可以是他子类中的任意一个Student(),Teacher()
    ps:父类 对象名 = new 对象类;new什么对象名就是什么
    行为多态:指的是子类重写了父类的方法,执行类方法时,执行的时重写的类方法
    ps:父类中没有定义的方法,在子类存在,但是多态不能使用子类的自有的方法
多态的前提
    1. 有继承/实现关系
    2. 有方法的重写
    3. 存在父类引用指向子类对象

多态的注意事项
    多态是对象、行为的多态; 变量则不涉及多态
	
代码执行:
    编译看左,运行看右
*/
public class Demo {
    public static void main(String[] args) {
        //使用多态的形式创建Student对象
        People p1 = new Student();
        p1.run();//后空翻跑步
//        p1.study();虽然子类定义了study方法,但是在多态形式下,不能调用子类独功能;
        p1.drink();//喝口水:只是继承来的方法,行为多态存在父类引用指向子类对象
        //使用多态的形式创建Teacher对象
        People p2 = new Teacher();
        p2.run();//颤颤巍巍跑步
    }
}

//定义父类People, 内含跑步run方法
class People {
    public void run() {
        System.out.println("正常人跑步");
    }
    public void drink() {
        System.out.println("喝口水");
    }
}
//定义子类Student, 内含跑步run方法
class Student extends People {
    public void study() {
        System.out.println("我正在学习");
    }
    @Override
    public void run() {
        System.out.println("后空翻跑步");;
    }
}
//定义父类Teacher, 内含跑步run方法
class Teacher extends People {
    @Override
    public void run() {
        System.out.println("颤颤巍巍跑步");
    }
}

         1.5 多态的好处与弊端

  • 在多态形式下,等号左右两边松耦合,更便于修改和维护
  • 定义方法时,使用父类类型的形参,可以接收一切子类对象扩展性更强、更便利

/*
多态的好处
    1. 在多态形式下,等号左右两边松耦合,更便于修改和维护
    2. 在多态下, 定义方法时, 可以使用父类类型作为形参, 那么该方法可以接收该父类下所有子类的对象
*/
public class Demo {
    public static void main(String[] args) {
        //好处1: 在多态形式下,等号左右两边松耦合,更便于修改和维护
        //创建一个老师对象,调用5次跑步的方法=======有一天需求改了========>创建一个学生对象,调用5次跑步的方法
        Teacher teacher = new Teacher();
        teacher.run();
        teacher.run();
        teacher.run();
        teacher.run();
        teacher.run();
        //又得改成Student()
        Student student = new Student();
        student.run();
        student.run();
        student.run();
        student.run();
        student.run();
        //用多态的话就避免了这种情况
        Person person = new Student();//需要什么对象改成什么对象
        person.run();
        person.run();
        person.run();
        person.run();
        person.run();
        //好处2: 在多态下, 定义方法时, 可以使用父类类型作为形参, 那么该方法可以接收该父类下所有子类的对象
        Person p1 = new Student();
        Person p2 = new Teacher();
        login(p1);//登陆成功,请开始学习
        login(p2);//登陆成功,请开始备课
    }

    //好处2: 在多态下, 定义方法时, 可以使用父类类型作为形参, 那么该方法可以接收该父类下所有子类的对象
    //创建一个run方法,接收一个老师对象,然后调用对象的run方法=======有一天需求改了========>接收一个学生对象,然后调用对象的run方法
    public static void run(Teacher teacher){
        teacher.run();
    }
    public static void run(Student student) { student.run(); }//这样写也很麻烦,需要new一个新对象,然后再用对象调用

    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("学生跑的快");
    }

    public void jump() {
        System.out.println("登陆成功,请开始学习");
    }
}

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

    public void jump() {
        System.out.println("登陆成功,请开始备课");
    }
}
/*
多态的弊端
    不能直接使用子类特有的功能
    解决方案: 强制类型转换

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

强转风险
    强转是存在风险的, 如果转为父类引用记录的真实子类对象,那么不会报错(否则会报ClassCastException)
    如果想规避这个风险,可以在强转前,使用instanceof关键字, 判断变量对应的类型
*/
public class Demo {
    public static void main(String[] args) {
        //传入一个老师对象
        run(new Teacher());
    }

    public static void run(Person person){
        //调用老师的run()方法
        person.run();
        //为了防止坏B使坏,我们可以先判断一下传来的类是不是Teacher
        if (person instanceof Teacher){
            //需求: 想再调用一下老师的teach()方法
            Teacher teacher = (Teacher) person;
            teacher.teach();
        } 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("老师在上课~~~~");
    }
}

         1.6 总结

        1.7 final 关键字

        1、final修饰的类、方法、变量各有什么特点?

                修饰类:该类被称为最终类, 类不能再被继承

                修饰方法:该方法被称为最终方法, 方法不能被重写

                修饰变量:该变量只能被赋值一次, 赋值完毕之后不能再修改

        2、final修饰基本类型和引用类型的变量有啥区别

                final修饰基本类型的变量,变量存储的数据不能被改变

                final修饰引用类型的变量,变量存储的地址不能被改变

                但地址所指向对象中的内容是可以被改变的。

/*
final关键字是最终的意思,可以修饰(类、方法、变量)
    修饰类:该类被称为最终类, 类不能再被继承
    修饰方法:该方法被称为最终方法, 方法不能被重写
    修饰变量:该变量只能被赋值一次, 赋值完毕之后不能再修改
        成员变量: 声明时赋完值,或者在构造方法结束之前完成赋值
        局部变量: 变量只能被赋值一次

final修饰变量的注意事项
    基本类型变量: 变量记录的数据不能再被改变
    引用类型变量: 变量记录的地址不能再被改变, 但是地址对应的堆内存中的内容可以改变
*/
public class Demo {
    public static void main(String[] args) {
        final int age;
        age = 20;//局部变量: 变量只能被赋值一次
//        age = 18;//Variable 'age' might already have been assigned to
    }
}

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

final class Son extends Father {
    String name;
    final String school = "12";
//    school = "12";//Variable 'school' might not have been initialized 声明时赋完值,或者在构造方法结束之前完成赋值
    @Override
    public void run() {//修饰方法,就不能被重写
        int age = 10;
        System.out.println(age + "岁的儿子在跑");
    }
}
//class uncle extends Son{//Cannot inherit from final 'd_final.Son'修饰类就不能被继承
//
//}

2.抽象类

        2.1 什么是抽象类

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

        2.2 抽象类的特点

  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
  • 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
  • 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
  • 个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
/*
抽象类和抽象方法
    在Java中有一个关键字叫:abstract,它就是抽象的意思,可以用它修饰类、成员方法。
        abstract修饰类,这个类就是抽象类
        abstract修饰方法,这个方法就是抽象方法,抽象方法没有方法体

抽象类的特点
    1. 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
    2. 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
    3. 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
    4. 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
*/
public class Demo {
    public static void main(String[] args) {
        Person person = new Student();
        person.run();
//        new Person();//'Person' is abstract; cannot be instantiated 抽象类是无法创建对象的
    }
}

//需求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.3 使用抽象类的场景和好处

多个类中只要有重复代码( 包括相同的方法签名 ),我们都应该抽取到父类中去,此时,父类中就有可能存在只有方法签名的方法,这时,父类必定是一个抽象类了, 我们抽出这样的抽象类,就是为了更好的支持多态。 (可以减少编写重复代码的繁琐)

/*
抽象类的应用场景和好处
    1、将所有子类中重复的代码,抽取到抽象的父类中,提高了代码的复用性(先编写子类,再编写抽象类)
    2、我们不知道系统未来具体的业务时,可以先定义抽象类,将来让子类去继承实现,提高了代码的扩展性 (先编抽象类,再编写子类)

需求
    某宠物游戏,需要管理猫、狗的数据。
        猫的数据有:名字;行为是:喵喵喵的叫~
        狗的数据有:名字;行为是:汪汪汪的叫~
*/
public class Demo {
    public static void main(String[] args) {
        Animal an1 = new Cat();
        Animal an2 = new Dog("雪豹");
        an1.setName("小馋猫");
        an1.cry();
        an2.cry();
    }
}

//动物
abstract class Animal {
    private String name;

    public Animal() {
    }

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void cry();
}

//猫
class Cat extends Animal {
    public Cat() {
        super();
    }

    public Cat(String name) {
        super(name);
    }

    @Override
    public void cry() {
        System.out.println(super.getName() + ",喵喵叫");
    }
}

//狗
class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }

    @Override
    public void cry() {
        System.out.println(super.getName() + ",你不要给我汪汪叫");
    }
}

        2.4 模板方法设计模式

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

        值得注意的是:如果使用模板方法之后,建议用 final 关键字进行修饰,这样可以有效避免子类重学模板方法,致使模板方法失效。 

/*
设计模式
    对于某类问题,前人总结出类的解决问题的套路

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

使用思路
    1、定义一个抽象类(Person作为父类),提供模板方法
    2、模板方法中,需要让子类自己实现的地方,定义为抽象方法
    3、子类(Teacher Student)只需要继承该抽象类,重写抽象方法即可完成些完成的功能

多学一招:
    建议使用final关健字修饰模板方法
        模板方法是给对象直接使用的,不能被子类重写
        一旦子类重写了模板方法,模板方法就失效了
*/
public class Demo {
    public static void main(String[] args) {
        Person person = new Teacher();
        person.work();
    }
}
abstract class Person {
    public final void work() {//为了避免子类重写方法,影响代码执行,如果不加会执行Student类中的重写方法work
        System.out.println("吃饭");
        doWork();
        System.out.println("睡觉");
    }
    public abstract void doWork();
}
class Student extends Person {
//    @Override
//    public void work() {
//        System.out.println("捣蛋鬼别捣蛋");
//    }

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

class Teacher extends Person {

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

 3.接口

       3.1 认识接口

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

定义格式
    public interface 接口名 {
        成员变量(接口中的成员变量都是常量, 默认是被public static final修饰的)
        成员方法(接口中的成员方法都是抽象方法, 默认是被public abstract修饰的)
        注意: 接口中不能有构造方法和代码块
    }

注意事项
    1. 接口不能直接创建对象
    2. 接口是用来被类实现(implements)的,实现接口的类称为实现类。
    3. 一个类可以实现多个接口,实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。
        修饰符 class 实现类 implements 接口1, 接口2, 接口3 , ... {}
*/
public class Demo {
    public static void main(String[] args) {
        //面向接口编程,创建对象,调用方法  接口 变量 = new 实现类()
        UserInterface ui = new UserInterfaceImpl();
        ui.save();
    }
}

//接口
public interface UserInterface {
    //定义成员变量,默认会有 public static final
    String name = "顶真";

    //定义成员方法,默认会有 public abstract
    void save();//保存用户顶真
}

//实现类
public class UserInterfaceImpl implements UserInterface{
    @Override
    public void save() {
        System.out.println("保存用户" + UserInterface.name );
    }
}

        接口其实和多态差不多,只是多态是类的多态,接口则是创建一个接口,在JDK 8 之前接口中只能定义一些成员变量和成员方法(抽象的,需要被重写),但在JDK 8 之后接口新增了三个特性,使得接口内可以定义其他内容。          

        3.2 接口的好处

                接口的好处:一句话带过就是解耦合(让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现)。可以看看代码,帮助与理解什么是解耦合。

/*
接口特点、好处
   让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。(解耦合)
*/

//使用这个类  模拟一个调用这角度的类
public class Demo {
    public static void main(String[] args) {
        UserService ui = new UserServiceImpl1();
        //这种还不是最大程度的解耦合只是帮助理解,可以根据登录的方式不同直接改上边使用的实现类
        ui.register();
        ui.login();
    }
}

//接口
public interface UserService {
    void register();

    void login();
    
}

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

    // 注册
    @Override
    public void register() {
        System.out.println("手机号/验证码登录");
    }
    // 登录
    public void login() {
        System.out.println("登陆成功");
    }
}

public class UserSeviceImpl2 implements UserService{
    @Override
    public void register() {
        System.out.println("账号/密码登陆");
    }

    @Override
    public void login() {
        System.out.println("登录成功");
    }
}


         3.3 接口的其他细节

                方法签名冲突是指:在一个类/接口继承的两个类/接口中,两个类/接口有相同的方法(方法名、形参、返回值都一样),这样会导致方法冲突。

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值