封装、继承(super、方法的重写)、多态、抽象类、接口

封装

this关键字

  • 在构造方法中出现this关键字,则代表当前正在构造的对象(经常用)
  • 若在成员方法中出现this,则代表当前正在调用的对象
  • this关键字可看作当前类类型的引用变量
  1. 当局部变量名和成员变量名相同时,方法体中会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量前加上this.前缀,明确要求改变量是成员变量(非常重要)
  2. this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值
    在这里插入图片描述
  3. 在构造方法的第一行可使用this()的方式调用本类中的其他构造方法(了解)
    在这里插入图片描述
    在这里插入图片描述
    形参就是局部变量,this区分的是成员变量
    在这里插入图片描述

封装的概念

在测试类中可以给变量赋值一些合法但不合理的数值,这些数值不会报错。为了避免这样的错误,需要堆成员变量进行密封包装,隐藏成员变量的细节以及保证成员变量数值的合理性,这个机制就是封装。

属性私有,get/set

关键词:private
使成员变量只能在当前类的内部使用
并提供共有的get和set方法,在方法体中进行合理值得判断
把get和set方法设为public即可
在这里插入图片描述

idea中可用Alt+insert自动生成get和set

封装可避免有害数据破坏系统
set的用法

public void setAge(int age) {
    
    
        if(age<0||age>100){
    
    
            this.age = 0;
        }else{
    
    
        this.age = age;
        }
    }

get的用法

public void getAge() {
    
    
       return age;
    }

可以避免将岁数过大的数据输入到系统中。

最终用法

在共有的构造方法中调用set方法进行合理值的判断
在这里插入图片描述

封装的好处:

  1. 提高程序安全性,保护数据
  2. 隐藏代码实现细节
  3. 统一接口
  4. 系统可维护性增加了

编程实现学生信息的录入和打印

  1. 根据用户输入的学生人数用变量来记录
  2. 根据学生人数准备一维数组
  3. 根据用户输入的每个学生信息(学号,姓名)记录到一维数组中
  4. 打印学生信息
    主程序代码
import java.util.Scanner;
public class StudentTest2 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("请输入学生人数:");
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();

        //数组中每个元素都可以看作Student类型的变量
        //Student[0] = new Student();
        Student[] arr = new Student[num];

        for (int i = 0; i < num; i++) {
    
    
            System.out.println("请输入第"+(i+1)+"个学生的信息(学号 姓名)");
            arr[i] = new Student(sc.nextInt(),sc.next());
        }
        System.out.println("-------------------------------");
        for (int i = 0; i < num; i++) {
    
    
            //System.out.println(arr[i]);会打印地址信息
            arr[i].show();
        }
    }
}

Student方法中代码

public class Student {
    
    
    private int id;
    private String name;
    public Student(){
    
    }
    public Student(int id,String name){
    
    
        setName(name);
        setId(id);
    }
    public int getId() {
    
    
        return id;
    }
    public void setId(int id) {
    
    
        if(id>0) {
    
    
            this.id = id;
        }else{
    
    
            System.out.println("输入了不合理的学号值");
        }
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
    public void show(){
    
    
        System.out.println("我的学号是:"+getId()+"\t我的姓名是:"+getName());
    }
}

结果是:
在这里插入图片描述

static关键字

加了static关键字后成员变量可被所有对象共享,隶属于类层级
static也可修饰成员方法,而在静态方法中没有this关键字,静态方法可直接用类名调用
因为this代表的是调用这个函数的对象的引用,而静态方法是属于类的,不属于对象,静态方法成功加载后,对象还不一定存在
在这里插入图片描述

封装的案例(重中之重)

  • 编程实现Singleton类的封装
  • 编程实现SingletonTest对Singleton类进行测试,要求能得到且只能得到该类的一个对象
    核心思想是让构造方法不能容易的被调用
Singleton s1 = new Singleton();
Singleton s2 = new Singleton();

这就是对构造方法没有限制,可创建多个对象
正确方法如下:

在这里插入图片描述
在这里插入图片描述

单例模式

在这里插入图片描述
在这里插入图片描述

继承

extends扩展,子类可以继承父类的所有方法

有父类的全部方法,前提是public

所有类都直接或间接继承object类
在这里插入图片描述

super的运用

super();

表示调用父类的构造方法,只能写在首行
括号内加参数就代表调用了有参构造

在这里插入图片描述

的输出是:

Xiaoming

Xiaohong

Apple

但私有的东西private不能被继承。

而且在创建子类的对象时,会默认先调用父类的无参构造

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法的重写在这里插入图片描述

重写都是方法的重写,和属性无关

只有非静态的方法才有重写(无static),且关键词只能是public

在这里插入图片描述
在这里插入图片描述

重写需要有继承关系,子类重写父类的方法,方法体不同(Alt+insert+override)

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符范围可以扩大不能缩小:public>Protected>Default>private
  4. 抛出的异常:范围可以被缩小不能扩大

为什么需要重写?

父类的功能子类不一定需要或不一定满足
重写的重要意义在于如果重写了就调用子类的,没有重写就调用父类的

继承的案例

在这里插入图片描述

public class Animal {
    
    
    private String name;
    private String color;

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

    public String getName() {
    
    
        return name;
    }

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

    public String getColor() {
    
    
        return color;
    }

    public void setColor(String color) {
    
    
        this.color = color;
    }
    public void show(){
    
    
        System.out.println("名字是:"+getName()+"\t毛色是:"+getColor());
    }
}
public class Dog extends Animal{
    
    
    private int tooth;

    public Dog() {
    
    
    }

    public Dog(String name, String color, int tooth) {
    
    
        super(name, color);//表示调用父类的构造方法
        this.tooth = tooth;
    }

    public int getTooth() {
    
    
        return tooth;
    }

    public void setTooth(int tooth) {
    
    
        if(tooth>0&&tooth<100){
    
    
        this.tooth = tooth;}
        else{
    
    
            System.out.println("输入的牙齿数量不合理");
        }
    }

    @Override
    public void show() {
    
    
        super.show();
        System.out.println("牙齿数量是:"+getTooth());
    }
}
public class DogTest {
    
    
    public static void main(String[] args) {
    
    
        Dog d1 = new Dog();
        d1.show();
        Dog d2 = new Dog("旺财","红色",30);
        d2.show();
    }
}

结果
在这里插入图片描述

构造块和静态代码块的考点

构造块即类中用大括号括起来的代码块
执行之前会先加载方法区中的静态代码块
而执行构造方法前会先执行构造代码块

public class SuperTest {
    
    
    {
    
    
        System.out.println("SuperTest中的构造块!");
    }

    static{
    
    
        System.out.println("SuperTest中的静态代码块");
    }
    public SuperTest(){
    
    
        System.out.println("SuperTest中的构造方法体");
    }

    public static void main(String[] args) {
    
    
        SuperTest st = new SuperTest();

    }
}

结果是
在这里插入图片描述
这时有SuperTest的子类SubSuperTest

public class SubSuperTest extends SuperTest{
    
    
    {
    
    
        System.out.println("==========SuperTest中的构造块!");
    }

    static{
    
    
        System.out.println("==========SuperTest中的静态代码块");
    }
    public SubSuperTest(){
    
    
        System.out.println("===========SuperTest中的构造方法体");
    }

    public static void main(String[] args) {
    
    
        SubSuperTest st = new SubSuperTest();

    }
}

问执行SubSuperTest时候的加载次序

  1. 首先肯定会先加载父类,则父类中的静态代码块会先执行
  2. 然后就会加载子类中的静态代码块
  3. 父类的构造方法会先于子类的构造方法,所以第三步加载父类的构造快
  4. 然后加载父类的构造方法,才算父类对象创建完成
  5. 执行子类构造快
  6. 执行子类构造方法
    结果如下:
    在这里插入图片描述

常用的控制访问符

在这里插入图片描述
在这里插入图片描述

final

final可修饰类、成员方法和成员变量
final关键字修饰的类不能被继承
final关键字修饰的方法不能被重写但可以被继承
在这里插入图片描述
在这里插入图片描述

多态

在这里插入图片描述

一个对象的实际类型是确定的,可以指向的引用类型不确定

在这里插入图片描述

例子

在这里插入图片描述

public class Shape {
    
    
    private int x;
    private int y;
    public Shape(){
    
    }
    public Shape(int x,int y){
    
    
        setX(x);
        setY(y);
    }
    public int getX() {
    
    
        return x;
    }

    public void setX(int x) {
    
    
        this.x = x;
    }

    public int getY() {
    
    
        return y;
    }

    public void setY(int y) {
    
    
        this.y = y;
    }
    void showX(){
    
    
        System.out.println("横坐标是:"+getX()+"\t纵坐标是:"+getY());
    }
}
public class Rect extends Shape{
    
    
    private int length;
    private int width;
    Rect(){
    
    }
    public Rect(int x,int y,int length, int width) {
    
    
        super(x,y);
        this.length = length;
        this.width = width;
    }

    public int getLength() {
    
    
        return length;
    }

    public void setLength(int length) {
    
    
        this.length = length;
    }

    public int getWidth() {
    
    
        return width;
    }

    public void setWidth(int width) {
    
    
        this.width = width;
    }

}
public class ShapeRectTest {
    
    
    public static void main(String[] args) {
    
    
        //1.声音Shape类型引用指向Shape类型对象
        Shape s1 = new Shape(1,2);
        s1.showX();
        System.out.println("====================");
        //2.声明Rect类型引用指向Rect类型对象
        Rect s2 = new Rect(3,4,5,6);
        s2.showX();
        System.out.println("====================");
        //3.声明Shape类型引用指向Rect类型对象
        Shape s3 = new Rect(7,8,9,10);
        s3.showX();
    }
}

结果是
在这里插入图片描述
说明了当Rect没有重写Shape中的show()时,就只会调用Shape中的show()
当Rect中重写了show方法后
在这里插入图片描述
说明当父类引用指向子类时,调用的是子类重写后的方法
但是在编译阶段调用的是父类中的show方法,运行阶段调用子类的show方法
且Shape类的引用也可以调用父类中独有的方法
在这里插入图片描述
在这里插入图片描述
自定义成员方法实现将参数指定矩形对象特征打印出来,也就是绘制图像的行为

public class ShapeTest {
    
    
    //自定义成员方法实现将参数指定矩形对象特征打印出来,也就是绘制图像的行为
    public static void draw(Rect r){
    
    
        r.showX();
    }
    public static void main(String[] args) {
    
    
        //Rect r = new Rect(1,2,3,4)
        ShapeTest.draw(new Rect(1,2,3,4));
    }
}

在这里插入图片描述
在这里插入图片描述

一个类的实际对象是确定的,都是new Student(),但是指向的引用类型不一样。

在这里插入图片描述
在这里插入图片描述

用Student重写了Person的run方法,这里面:

  • Student能调用自己的和父类的方法
  • Person只能调用自己的方法,只有指向子类,但不能调用子类独有的方法

static、final、private不能被重写

instanceof和类型转换

instanceof关键字可用来测试两个类之间是否存在父子关系。

在这里插入图片描述

由注释中关系理解结果

在这里插入图片描述

强制转换:父类Person ——>子类Student

Person obj = new Student();
((Student)obj).go();

这样就把Person类obj转换为Student类,可使用Student类中的go方法

而子类转换为父类可能会丢失自己本来的一些方法

回顾一下:

多态就是父类引用指向子类对象

Person obj = new Student();

抽象类

在这里插入图片描述

抽象方法是只有方法的名字,没有方法的实现,如:

public abstract void doSomething();

在这里插入图片描述

抽象类的所有方法,继承了它的子类,都必须实现它的方法

抽象类不能new出来,只能靠子类去实现它。相当于约束

在这里插入图片描述

抽象类中也可以写普通方法

在这里插入图片描述
在这里插入图片描述

接口可以实现多继承!

接口

在这里插入图片描述

接口只有规范,自己无法写方法。约束和实现分离。接口的本质是契约

接口中所有定义都是抽象的public abstract

所有接口都要有实现类 implement关键字

利用接口实现多继承如下

在这里插入图片描述

接口中没有构造方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Maybe_do_it/article/details/114684505