从零开始学Java-面向对象进阶(二)

162 阅读11分钟

在前篇文章中,我们学习了面向对象的static关键字和继承等,下面我们接着来学习第二部分吧!

目录

  • 多态
  • final关键字
  • 权限修饰符
  • 代码块
  • 抽象类

多态

什么是多态?多态有什么作用呢?

我们来回想一下Java的三大特征是什么呢?是不是封装、继承、多态呀!前面我们学习了封装和继承,你还记得嘛,接下来我们就来学习最后一个多态吧!

我们先来回顾一下吧:

  • 封装:对象代表什么,就得封装对应的数据,并提供数据对应的行为。

image.png 那现在就有个问题了,如果我的JavaBean类越来越多之后,重复的内容就越来越多了,这个时候怎么办呢?是不是就要用到继承呀,将相同的属性抽取到父类当中,子类就不用重复写代码了。提高了复用性。

image.png

那么继承是多态的前提条件,没有继承就没有多态。

以前创建学生对象的方式:

image.png

有了多态以后,我们还可以把学生对象赋值给他的父类类型,这个时候学生对象就有了两种形态:

image.png

那下面我们就来了解一下多态吧:

  • 什么是多态

    • 同类型的对象,表现出的不同形态
  • 多态的表现形式

    • 父类类型 对象名 = 子类对象
  • 多态的前提

    • 有继承关系
    • 有父类引用指向子类对象
    • 有方法重写
  • 多态调用成员的特点

    • 变量调用:编译看左边,运行也看左边。
    • 方法调用:编译看左边,运行看右边
    • 成员变量:在子类的对象中,会把父类的成员变量也继承下来。
    • 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
  • 变量调用:

    • 编译看左边:Java编译代码的时候,会看左边的父类中有没有这个变量,如果有就编译成功,如果没有就编译失败!
    • 运行看左边:Java运行代码的时候,实际获取的就是左边父类中成员变量的值。
  • 方法调用:

    • 编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个变量,如果有就编译成功,如果没有就编译失败!
    • 运行看右边:Java运行代码的时候,实际上运行的是子类中的方法。

下面我们来实操一下吧:

创建父类属性:姓名和年龄

private String name;
private int age;

public void show(){
    System.out.println(name + "," + age);
}

创建学生子类:

// 继承父类的属性
public class Student extends Person{
    // 改写方法
    @Override
    public void show(){
        System.out.println("学生的信息为:" + getName() + "," + getAge());
    }
}

创建老师子类:

public class Teachar extends Person{
    @Override
    public void show(){
        System.out.println("老师的信息为:" + getName() + "," + getAge());
    }
}

创建管理员子类:

public class Admin extends Person{
    @Override
    public void show(){
        System.out.println("管理员的信息为:" + getName() + "," + getAge());
    }
}

定义一个方法接收三

// 这个方法既能接收老师,又能接收学生,还能接收管理员
// 只能把参数写成这三个类型的父类
public static void register(Person p){
    p.show();
}

创建三个对象,并调用register方法:

Student stu = new Student();
stu.setName("张三");
stu.setAge(18);

Teachar t = new Teachar();
t.setName("李四");
t.setAge(25);

Admin admin = new Admin();
admin.setName("管理员");
admin.setAge(30);

调用register方法并传递对象:

register(stu);
register(t);
register(admin);

我们来运行看一下吧:

image.png

是不是能根据你传递的对象不同选择不同的方法呀,这就是多态的最基本的应用。

下面我们来说说多态的弊端吧:

多态的优势和弊端

  • 多态的好处
    • 使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利
    • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
  • 多态的弊端
    • 父类不能调用子类的特有功能(会报错),解决方案:把类型变回子类类型就可以了。
  • 引用数据类型转换,有几种方式
    • 自动类型转换、强制类型转换
  • 强制类型转换能解决什么问题
    • 可以转换成真正的子类类型,从而调用子类独有功能。
    • 转换类型与真实对象类型不一致会报错
    • 转换的时候用instanceof关键字进行判断

下面我们来了解一下,报错的原因。

当调用方法的时候,编译看左边,运行看右边,那么在编译的时候会先检查左边父类中有没有这个类,如果没有直接报错。

那怎么解决呢?其实很简单。

变回子类类型就可以了,转换的时候不能瞎转,如果转成其他类的类型就会报错。这个时候我们可以利用一个instanceof关键字来给他判断啦,我们看下面这段代码:

if(a instanceof Dog d){ // 判断a是不是Dog类型,如果是就强转为Dog类型,转换之后变量名为d,如果不是就不转,结果直接false
   d.方法名();
}else if(a instanceof Cat c){  // 判断a是不是Cat类型,如果是就强转为Cat类型,转换之后变量名为c如果不是就不转,结果直接false
   c.方法名();
}else{
   sout("没有这个类型,无法转换");
}

  • 什么是包

    • 包就是文件夹,用来管理各种不同功能的Java类,方便后期维护代码。
  • 包名的规则

    • 公司域名反写+包的作用,需要全部英文小写,见名知意
  • 使用其他类的规则

    • 使用其他类时,需要使用全类名。

    例:

      impoer com.itheima.domain.Student
      com.itheima.domain:包
      Student:类名
      
    
  • 导包的情况

    • 使用同一个包中的类时,不需要导包。
    • 使用Java.lang包中的类时,不需要导包。
    • 其他情况下都需要导包。
    • 如果同时使用两个包中的同名类,需要用全类名。

final关键字

  • 什么是final?final翻译过来是不是最终的意思呀!

image.png

那final能修饰什么呢?其实呀能修饰方法、类、变量。下面我们就展开来看看吧:

  • final修饰方法:表明该方法是最终方法,不能被重写

    下面我们来看一下:

    image.png

    我们来用final修饰一下父方法看一下:

    image.png

    是不是直接就报错了呀,那就表示被final修饰的方法就不能被子类重写,表示是最终的方法。通常在当前这个方法是一种规则,不想被改变就可以使用final修饰方法。

  • final修饰类:表明该类是最终类,不能被继承

    下面我们来看一下:

    image.png

    是不是被修饰的类也不能继承呀,表明是最终的类。

  • final修饰变量:这个时候就得叫常量了,只能被赋值一次

    下面是重点了,如果我的变量的值不想被改变,这个时候就可以使用final修饰变量。

    image.png

    下面我们加final修饰符看一下:

    image.png

    是不是就直接报错了呀,被修饰后就不叫变量了,就叫常量了,常量只可以被赋值一次。下面我们就来展开了解一下常量吧:

  • 常量

实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。

  • 常量的命名规则:

    • 单个单词:
      • 全部大写。
    • 多个单词:
      • 全部大写,单词之间用下划线隔开。
  • 细节:

    • final修饰的变量是基本类型,那么变量存储的数据值不能发生改变。
    • final修饰的变量是引用类型,那么变量存储的地址值不能发生改变,对象内部的可以改变。

好啦,包就讲到这里啦,下面我们来看一下权限修饰符!

权限修饰符

  • 权限修饰符
    • 权限修饰符:是用来控制一个成员能够被访问的范围的。
    • 可以修饰成员变量,方法,构造方法,内部类。
  • 权限修饰符的分类
    • 有四种作用访问由小到大:(private < 空着不写 < protected < public)

我们来看一下他们的作用范围吧:

image.png

  • 权限修饰符的使用规则
    • 实际开发中,一般只用最小的:private和最大的:public
    • 成员变量私有
    • 方法公开

代码块

什么是代码块呢?加了单独的大括号,这个就叫做代码块。

image.png

根据出现的位置不同,可以分成三种代码块!

  • 局部代码块

    局部代码块我们需要分为两部分:局部代码块

    局部指的是在方法里面。局部代码块指的是写在方法里面的代码块。

    例: image.png

  • 构造代码块

    我们来看一下:

    image.png

    此时是不是就有重复的代码出现了呀?那怎么办呢?这个时候就可以把重复的代码抽取到一个构造方法去。

    image.png

    下面我们来说一下构造方法的注意点:

    1.写在成员位置的代码块

    2.作用:可以把多个构造方法中重复的代码抽取出来。

    3.执行时间:我们在创建本类对象的时候会先执行构造代码块在执行构造方法

  • 静态代码块

    • 格式:static{};
    • 特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
    • 使用场景:在类加载的时候,做一些数据初始化的时候使用

    我们来看一下创建静态代码块吧:

    image.png

  • 下面我们来总结一下吧:

  • 局部代码块的作用:

    • 提前结束变量的生命周期(已淘汰)
  • 构造代码块的作用:

    • 抽取构造方法中重复的代码(不够灵活)
  • 静态代码块的作用:

    • 数据初始化(重点)

抽象类

  • 抽象关键字:abstract

我们来看这张图片: image.png

这个父类中是不是有个work工作属性子类的student和子类的teacher的工作方式都不一样呀,那怎么办呢?之前是不是学习了在子类方法上重写呀,那是可以的,但是有个弊端,那我如果不重写呢,怎么办?那我们就可以使用抽象类治治他们了。

如果不确认方法体,那可以使用abstract去修饰,被abstract修饰的方法子类必须重写,否则子类的代码就会直接报错。这个方法就叫做抽象方法。而抽象方法所在的类就叫做抽象类。

image.png

下面我们来了解一下抽象类和抽象方法的二个需要掌握的知识点吧:

1.抽象类的定义格式

  • 抽象类:

    • 如果一个类中存在抽象方法,那么该类就必须声明为抽象类
  • 抽象类的定义格式:

    • public abstract class 类名{};
    • public abstract class Person{};
  • 抽象类的注意格式:

    • 抽象类不能实例化。(创建对象)

    • 抽象类中不一定有抽象方法有抽象方法的类一定是抽象类

    • 可以有构造方法(当创建了子类对象时,给属性进行赋值的)

    • 抽象类的子类

      • 要么重写抽象类中的所有抽象方法
      • 要么是抽象类

2.抽象方法的定义格式

  • 抽象方法:

    • 共性的行为(方法)抽取到父类之后,由于每一个子类的内容是不一样的,所以在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。
  • 抽象方法的定义格式:

    • public abstract 返回值类型 方法名(参数列表); // 不写方法体,直接分号结束
    • public abstract void word();

3.抽象类和抽象方法的意义:

  • public abstract void eat();
  • 强制子类必须按照这种格式进行重写方法。

总结

  • 抽象类的作用是什么:
    • 抽取共性时,无法确定方法体,就把方法定义为抽象的。强制让子类按照某种格式重写。抽象方法所在的类必须是抽象类
  • 抽象类和抽象方法的格式:
    • public abstract 返回值类型 方法名(参数列表);
    • public abstract class 类名{};
  • 继承抽象类有哪些注意:
    • 要么重写抽象类中所有的抽象方法
    • 要么是抽象类

好啦,这篇文章就学到这里,我们下篇继续学习接口、内部类。有什么不懂的可以在评论区评论一起探讨哟,我们下期不见不散!!!

==最后非常感谢您的阅读,也希望能得到您的反馈  ==