Java面向对象(进阶)-- 四种权限测试与方法的重写(override_overwrite)

一、四种权限修饰

前面说了四种权限修饰,其中protected(受保护的)没有细说,因为它权限调用的过程中涉及到它不同包子类的问题。现在说过了继承性,就可以说一说它的使用了。

下面这个表格要会,不会的去看封装性那一篇的博客:
image.png

注意:

  • 实际开发中,各权限修饰的使用频率是怎样的? publicprivate是使用频率最高的!
  • 若不设置,其实也是一种权限–缺省。
  • 属性方面,通常是private,基本是通过方法(get/set)来调用属性。
  • 方法方面,通常是public

有一类属性会声明为public,就是常量,后边讲final关键字的时候再说。

二、测试四种权限修饰

(1)准备

在yuyi08包下创建两个package:test1和test2,测试Java中提供的4种权限修饰。

image.png

(2)同一个类

test1包下创建Order类。

image.png

【Order.java】

package yuyi08.test1;

/**
 * ClassName: Order
 * Package: yuyi08.test1
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/1 0001 15:21
 */
public class Order {    //类的权限是public,可以跨包访问
    //声明四种不同属性的权限和方法
    //属性
    private int orderPrivate;
    int orderDefault;
    protected int orderProtected;
    public int orderPublic;

    //方法
    private void methodPrivate(){

    }

    void methodDefault(){

    }

    protected void methodProtected(){

    }

    public void methodPublic(){

    }

    //类内部都可以访问
    public void show(){
        orderPrivate=1;
        orderDefault=2;
        orderProtected=3;
        orderPublic=4;

        methodPrivate();
        methodDefault();
        methodProtected();
        methodPublic();

    }
}

(3)同一个包

image.png

test1包下再建立OrderTest类(与Order类同包)。

【OrderTest.java】

package yuyi08.test1;

/**
 * ClassName: OrderTest
 * Package: yuyi08.test1
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/1 0001 15:37
 */
public class OrderTest {
    public void method1(){
        //在OrderTest类里面想要调用Order类里面的属性和方法,没有继承性关系,想要使用只能通过对象
        Order order=new Order();

        //通过对象来调用Order类中声明的属性、方法

        //不能调用OrderTest类中的私有属性、方法
        order.orderPublic=1;
        order.orderProtected=2;
        order.orderProtected=3;
        //order.orderPrivate=4;    //受封装性的影响,不能够调用

        order.methodPublic();
        order.methodProtected();
        order.methodProtected();
        //order.methodPrivate();   //受封装性的影响,不能够调用

    }
}

正常调用的时候,私有属性也不会显示,如下:

image.png

出了类本身,在同一个包下,私有的属性、方法是不能互相调用的。

(4)同一个包子类

比如在test1包下造一个Order子类,本来private(私有)的属性出了类就用不了,别提子类了;default在同一个包下都能看到(不论是不是子类);public更不用说了,项目内都能看见。

所以这里就不用测试了。

(5)不同包子类

test2包下创建SubOrder类,继承于Order类:

image.png

由于在不同的包下,所以在SubOrder类中使用别的包下的Order类时需要调包。

image.png

Order类本身也有权限,一种是public,一种是默认的。

若此时将Order类的public去掉,就会出问题。(跨包的话,默认权限的Order类就看不见了)

如下:

image.png

在另一个包(test2)下创建当前类(Order)的子类(SubOrder),当前类(Order)的权限修饰只能是public。

若当前类的权限修饰是默认(就是不写),当前类的public属性和方法都不能被调用。

【SubOrder.java】

package yuyi08.test2;

import yuyi08.test1.Order;

/**
 * ClassName: SubOrder
 * Package: yuyi08.test2
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/1 0001 15:57
 */
public class SubOrder extends Order {
    //子类中调用父类的属性和方法时不用创建对象
    public void method(){

        orderProtected=1;   //不同包下的子类受保护的属性可以使用
        orderPublic=2;
        //orderPrivate=3;  //私有属性不能使用
        //orderDefault=4; //出了包,默认权限不能用了

        methodProtected();
        methodPublic();
        //methodPrivate();  //权限问题
        //methodDefault();  //权限问题
    }
}

(6)跨包不是子类

在test2包下创建OrderTest2类。

image.png

此类与Order类没有什么关系,所以想要测试属性和方法得造对象,同时也需要导包,如下:

image.png

可以发现,此时只能调用公共的属性、方法了:

image.png

【OrderTest2.java】

package yuyi08.test2;

import yuyi08.test1.Order;

/**
 * ClassName: OrderTest2
 * Package: yuyi08.test2
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/1 0001 16:13
 */
public class OrderTest2 {
    public void method(){
        //此类与Order类没有什么关系,所以想要测试属性和方法得造对象
        Order order=new Order();
        order.orderPublic=1;
        order.methodPublic();

        //不能访问
        /*order.orderPrivate=2;
        order.orderDefault=3;
        order.orderProtected=4;
        
        order.methodPrivate();
        order.methodDefault();
        order.methodProtected();*/

    }
}

(7)总结

权限修饰符:public,protected,缺省,private

修饰符本类本包其他包子类其他包非子类
private×××
缺省√(本包子类非子类都可见)××
protected√(本包子类非子类都可见)√(其他包仅限于子类中可见)×
public

外部类:public和缺省

成员变量、成员方法等:public,protected,缺省,private

1、外部类要跨包使用必须是public,否则仅限于本包使用

(1)外部类的权限修饰符如果缺省,本包使用没问题

在这里插入图片描述

(2)外部类的权限修饰符如果缺省,跨包使用有问题

在这里插入图片描述

2、成员的权限修饰符问题

(1)本包下使用:成员的权限修饰符可以是public、protected、缺省

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)跨包下使用:要求严格

在这里插入图片描述

(3)跨包使用时,如果类的权限修饰符缺省,成员权限修饰符>类的权限修饰符也没有意义

image.png

三、方法的重写(overwrite / override)

之前说过重载,不要混淆了,博客链接:https://blog.csdn.net/m0_55746113/article/details/133964522?spm=1001.2014.3001.5502

(1)引入

父类的所有方法子类都会继承,但是当某个方法被继承到子类之后,子类觉得父类原来的实现不适合于自己当前的类,该怎么办呢?

子类可以对从父类中继承来的方法进行改造,我们称为方法的重写 (override、overwrite)。也称为方法的重置、覆盖。

在程序执行时,子类的方法将覆盖父类的方法


①问:为什么需要方法重写

子类在继承父类以后,就获取了父类中声明的所有的方法。但是,父类中的方法可能不太适用于子类,换句话说,子类需要对父类中继承过来的方法进行覆盖、覆写的操作

②问:何为方法的重写

子类对父类继承过来的方法进行的覆盖、覆写的操作,就称为方法的重写。

(2)举例

举例1

比如新的手机增加来电显示头像的功能,代码如下:

package com.atguigu.inherited.method;

public class Phone {
    public void sendMessage(){
        System.out.println("发短信");
    }
    public void call(){
        System.out.println("打电话");
    }
    public void showNum(){
        System.out.println("来电显示号码");
    }
}

package com.atguigu.inherited.method;

//SmartPhone:智能手机
public class SmartPhone extends Phone{
    //重写父类的来电显示功能的方法
	@Override
    public void showNum(){
        //来电显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");
    }
    //重写父类的通话功能的方法
    @Override
    public void call() {
        System.out.println("语音通话 或 视频通话");
    }
}
package com.atguigu.inherited.method;

public class TestOverride {
    public static void main(String[] args) {
        // 创建子类对象
        SmartPhone sp = new SmartPhone();

        // 调用父类继承而来的方法
        sp.call();

        // 调用子类重写的方法
        sp.showNum();
    }
}

@Override使用说明:

写在方法上面,用来检测是不是满足重写方法的要求。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法。

举例2

【银行账户】

class Account{//账户
    double balance;//余额

    //取钱
    public void withdraw(double amt){
        //判断balance余额是否够amt取钱的额度
    }
}

class CheckAccount extends Account{ //信用卡
    double protectedBy;//透支额度


    public void withdraw(double amt){
        //判断balance余额是否够amt取钱的额度
        //如果不够,还可以考虑从protectedBy额度里取
    }
}

class AccountTest{
    public static void main(String[] args){
        CheckAccount acct = new CheckAccount();
        acct.withdraw(); //执行的是子类重写父类的方法
    }
}

举例3

【Person.java】

package yuyi09;

/**
 * ClassName: Person
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/10/29 0029 16:39
 */
public class Person {
    //属性
    String name;
    private int age;

    //方法
    public void eat(){
        System.out.println("人吃饭");
    }

    public void sleep(){
        System.out.println("人睡觉");
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

【Student.java】

package yuyi09;

/**
 * ClassName: Student
 * Package: yuyi04
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/10/29 0029 16:40
 */
public class Student extends Person {
    //属性
    //String name;
    //int age;

    String school;

    //方法
    /*public void eat(){
        System.out.println("人吃饭");
    }

    public void sleep(){
        System.out.println("人睡觉");
    }*/

    public void study(){
        System.out.println("学生学习");
    }

    public void show1(){
        //System.out.println("age: "+age);

        //无法直接访问私有属性age,可以通过getAge方法获取
        System.out.println("age: "+ getAge());
    }
}

测试一下:

【OverrideTest.java】

package yuyi09;

/**
 * ClassName: OverrideTest
 * Package: yuyi09
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/1 0001 19:09
 */
public class OverrideTest {
    public static void main(String[] args) {
        //这里不涉及继承性了,直接造子类对象
        Student s1=new Student();

        //eat()方法与sleep()方法在Student类中没有声明过,这里调用就是调用父类中的方法
        s1.eat();
        s1.sleep();
    }
}

`

eat()方法与sleep()方法在Student类中没有声明过,这里调用就是调用父类中的方法,执行过后就是父类中的的输出值,如下:

image.png


现在这两个方法不适合子类Student了,需要做重写

最基本的方法是,将eat()方法从父类Person中直接赋值过来:

image.png

可以在Student类中改方法体:(声明没有动)

public void eat(){
    System.out.println("学生应该多吃有营养的食物");
}

再次回到测试类OverrideTest,打印输出:

image.png

可以发现,eat()方法输出的是Student类重写父类的输出值;sleep()方法没有重写还是输出父类中的方法。

(3)方法重写应遵循的规则

【复习】方法声明的格式:

权限修饰符 返回值类型 方法名(形参列表) [throws 异常类型] {
	//方法体 
}

【具体规则】

父类被重写的方法与子类重写的方法的方法名和形参列表必须相同。

  • 父类中的某个方法可能有重载,子类要重写该方法,必须知道覆盖的是哪一个方法,需要通过名字和形参列表来判定覆盖的是哪一个方法。

子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。

  • 子类不能重写父类中声明为private权限修饰的方法。
  • 若在子类中赋值了父类中的方法,不算是重写它的方法,而是重新定义了一个方法。

关于返回值类型

  • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void。
  • 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须与被重写的方法的返回值类型相同。

image.png

  • 父类被重写的方法的返回值类型是引用数据类型(比如类),则子类重写的方法的返回值类型可以与被重写的方法的返回值类型相同 或 是被重写的方法的返回值类型的子类。
    • 子类返回值与父类一致(可以)

image.png

  • 子类重写的返回值为父类的子类(可以)

image.png

Student类是Person类的子类,左边的Person类要返回Person类型的值,右边的Student类返回的是Student类型的值,而Student类是Person类的子类,所以Student类相当于也是返回Person类型的值。(先这样理解,后边讲完多态之后就会更透彻)

  • 子类重写的返回值为父类的父类(不可以)

image.png

方法体:没有要求。

但是子类重写的方法的方法体必然与父类被重写的方法的不同。

重写的目的就是要改方法体,子类重写父类的方法方法体肯定不一样,要不然没有意义。

⑤ (超纲)子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类

若不遵循规则,不能认为叫重写,说白了就是覆盖不了了。

在重写的时候,一般将父类的方法完全的声明全部粘贴到子类,在子类中专门写方法体即可。

若是连父类的方法也懒得赋值,可以直接在子类中敲方法名,比如sleep(),如下:

image.png

此时会弹出提示,直接点击弹出来的这个方法然后回车即可,就会自动获取这个方法:

@Override	//这个是注解,表示对父类中的某个方法做重写了
public void sleep() {
    super.sleep();	//super下一篇再说
}

🗃️补充:

@Override //这个是注解,表示对父类中的某个方法做重写了

它写没写跟是不是重写没有关系,删了这个也叫重写。

加上它的意义是做个校验

比如,现在想重写sleep()方法,但是写错了,写成了sleap()

这时候就会报错(此时你想重写,但是父类中没有这个方法,这里它相当于告诉你这个方法不是重写啦,相当于做个提示),如下:

image.png

(4)面试题

【面试题】区分方法的重载(overload)与重写(override / overwrite)

答:重载:“两同一不同”

重写:继承以后,子类覆盖父类中同名同参数的方法

image.png


【类比】相同类型的面试题

下面各组的区别(目前看看就好,这里只是列举相似的面试题)

throws / throw
final(关键字) / finally (关键字)/ finalize(方法名)
Collection / Collections
String / StringBuffer / StringBuilder
ArrayList / LinkedList
HashMap / LinkedHashMap / Hashtable
...

sleep() / wait()
== / equals()
同步 / 异步

四、方法的重写–练习题

继承:https://blog.csdn.net/m0_55746113/article/details/134135984

(1)练习1

🌋题目描述

修改继承内容的练习1中定义的类Kids,在Kids中重新定义employeed()方法,

覆盖父类ManKind中定义的employeed()方法,输出"Kids should study and no job."

【Mankind.java】

package yuyi10;

/**
 * ClassName: Mankind
 * Package: yuyi05
 * Description:
 * (1)定义一个ManKind类,包括
 *    成员变量int sex和int salary;
 * - 方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
 *
 * - 方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
 * @Author 雨翼轻尘
 * @Create 2023/10/30 0030 10:32
 */
public class Mankind {
    //属性
    private int sex;
    private int salary;

    //方法

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public void manOrWoman(){
        if(sex==1){
            System.out.println("man");
        } else if (sex==0) {
            System.out.println("woman");
        }
    }

    public void employeed(){
        if(salary==0){
            System.out.println("no job");
        } else {
            System.out.println("job");
        }
    }

    //构造器

    public Mankind() {

    }

    public Mankind(int sex, int salary) {
        this.sex = sex;
        this.salary = salary;
    }
}

【Kids.java】

package yuyi10;

/**
 * ClassName: Kids
 * Package: yuyi05
 * Description:
 *   (2)定义类Kids继承ManKind,并包括
 * - 成员变量int yearsOld;
 * - 方法printAge()打印yearsOld的值。
 * @Author 雨翼轻尘
 * @Create 2023/10/30 0030 10:56
 */
public class Kids extends Mankind { //父类中声明的属性和方法都被继承到子类了,构造器就不提了。后边提super关键字的时候会提到,在子类当中调用父类中的构造器
    private int yearOld;

    public int getYearOld() {
        return yearOld;
    }

    public void setYearOld(int yearOld) {
        this.yearOld = yearOld;
    }

    public void printAge(){
        System.out.println("I am "+yearOld+" years old");
    }

    //构造器
    public Kids(){

    }

    public Kids(int yearOld){
        this.yearOld=yearOld;
    }

    //把父类中的属性也做一个赋值,包括自己的属性
    public Kids(int sex, int salary,int yearOld){
        this.yearOld=yearOld;
        //sex、salary两个 属性是父类继承过来的,怎么给他们赋值?
        setSex(sex);
        setSalary(salary);
    }
}

【KidsTest.java】

package yuyi10;

/**
 * ClassName: KidsTest
 * Package: yuyi05
 * Description:
 *(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问其父类的成员变量及方法。
 * @Author 雨翼轻尘
 * @Create 2023/10/30 0030 10:58
 */
public class KidsTest {
    public static void main(String[] args) {
        Kids someKid=new Kids();
        someKid.setSex(1);
        someKid.setSalary(100);
        someKid.setYearOld(12);

        //Kids类自己声明的方法
        someKid.printAge();

        //来自于父类中声明的方法
        someKid.manOrWoman();
        someKid.employeed();

    }
}

运行结果:

image.png

🤺代码

【 Kids.java】

package yuyi10;

public class Kids extends Mankind { //父类中声明的属性和方法都被继承到子类了,构造器就不提了。后边提super关键字的时候会提到,在子类当中调用父类中的构造器
   //...
    @Override
    public void employeed() {
        System.out.println("Kids should study and no job.");
    }
}

👻运行结果

image.png

(2)练习2

🌋题目描述

修改继承内容的练习中定义的类Cylinder,在Cylinder中重写父类方法findArea(),用于计算圆柱的表面积

【Circle.java】

package yuyi11;

/**
 * ClassName: Circle
 * Package: yuyi06
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/10/31 0031 10:07
 */
public class Circle {
    //属性
    private double radius;  //半径
    //方法
    public void setRadius(double radius){
        this.radius=radius;
    }
    public double getRadius(){
        return radius;
    }
    //求圆的面积
    public double findArea(){
        return Math.PI*radius*radius;
    }

    //构造器
    public Circle(){
        radius=1;
    }
}

【Cylinder.java】

package yuyi11;

/**
 * ClassName: Cylinder
 * Package: yuyi06
 * Description:
 *  圆柱类
 * @Author 雨翼轻尘
 * @Create 2023/10/31 0031 10:19
 */
public class Cylinder extends Circle {
    //属性
    private double length;  //高

    //方法
    public void setLength(double length){
        this.length=length;
    }
    public double getLength(){
        return length;
    }
    //求圆柱的体积
    public double findVolume(){
        //return Math.PI*getRadius()*getRadius()*getLength();
        return findArea()*getLength();
    }


    //构造器
    public Cylinder(){
        length=1;
    }
}

【CylinderTest.java】

package yuyi11;

/**
 * ClassName: CylinderTest
 * Package: yuyi06
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/10/31 0031 10:29
 */
public class CylinderTest {
    public static void main(String[] args) {
        Cylinder cy=new Cylinder();
        cy.setRadius(2.3);
        cy.setLength(1.4);
        System.out.println("圆柱的体积为: "+cy.findVolume());
        System.out.println("圆柱的底面积为: "+cy.findArea());
    }
}

运行结果:

image.png

🤺代码

【Cylinder.java】

package yuyi11;

public class Cylinder extends Circle {
    //属性
    private double length;  //高

    //...

    //求圆柱的体积
    public double findVolume(){
        return Math.PI*getRadius()*getRadius()*getLength();
        //return findArea()*getLength();	//错误
    }
    
    @Override
    public double findArea() {
        return Math.PI*getRadius()*getRadius()*2+
        2*Math.PI*getRadius()*getLength();
    }
}

👻运行结果

image.png

⚡注意

子类继承父类之后,若子类重写了父类中的方法,在子类中再次调用此方法是重写的方法,而不是父类中的方法。

若还想在子类中继续使用父类中的那个方法,下一节讲Super再说,不加就是重写的方法,加上了就是父类中的方法。

image.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨翼轻尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值