Java程序设计实验4 | 面向对象(下)

*本文是博主对Java各种实验的再整理与详解,除了代码部分和解析部分,一些题目还增加了拓展部分(⭐)。拓展部分不是实验报告中原有的内容,而是博主本人自己的补充,以方便大家额外学习、参考。

目录

一、实验目的

二、实验内容

1、类的继承和方法重写

2、定义一个类,在类中声明成员变量和成员方法,尝试使用final关键词修饰类中的变量、方法及该类,测试并查看结果,必要时加以注释。

⭐下面分条演示 final 关键字的作用

3、研究生薪资管理

⭐简介Math.random()

4、创建一个抽象交通工具Vehicle类。

5、经理与员工工资,主要考察多态

⭐关于多态

6、把下面的代码补充完整,输出结果为“实现了Inner接口的匿名内部类!”,并测试输出结果。

7、设计一个类,在类中能够处理自定义异常类并测试。(选做)

三、实验总结


一、实验目的

1、理解继承的概念;

2、掌握方法的重写;

3、掌握super、final关键字的使用;

4、掌握抽象类和接口的使用;

5、掌握多态的使用;

6、掌握内部类的使用;

7、掌握异常处理方式,能够自定义异常类;

8、了解Object类。


二、实验内容

1、类的继承和方法重写

定义一个基类作为父类,再定义一个继承父类的子类,在子类中重写父类的方法,使用super关键字调用父类的方法,测试其功能。

本题是类的继承的练习。

在继承中,super关键字可以在子类中调用父类的成员变量、成员方法和构造方法。

注意:在子类构建的过程中,会调用父类的构造方法。默认情况下,子类会调用父类无参的构造方法:super()。

源代码:

// 定义父类
class ParentClass {
    void display() {
        System.out.println("This is the parent class.");
    }
}

// 定义继承自父类的子类
class ChildClass extends ParentClass {
    // 重写父类的方法
    void display() {
        System.out.println("This is the child class.");
        super.display(); // 使用super关键字调用父类的方法
    }
}

public class S4_1 {
    public static void main(String[] args) {
        ParentClass parent = new ParentClass();
        ChildClass child = new ChildClass();

        // 调用父类和子类的display方法
        parent.display(); // 调用父类的方法
        child.display();  // 调用子类的方法,同时会调用父类的方法

    }
}

列出测试数据和实验结果截图: 


2、定义一个类,在类中声明成员变量和成员方法,尝试使用final关键词修饰类中的变量、方法及该类,测试并查看结果,必要时加以注释。

在 Java 中,final关键字可以用来修饰类、变量和方法:

  1. 修饰类:当一个类被声明为final时,它表示这个类不能被继承。无法创建它的子类。

  2. 修饰变量:当一个变量被声明为final时,它表示这个变量的值只能被赋值一次,之后不可再修改。它是一个常量。

  3. 修饰方法:当一个方法被声明为final时,它表示这个方法不能被子类重写或覆盖。

源代码:

// 修饰类
final class FinalClass {
    // 修饰成员变量
    final int constantValue = 10;

    // 修饰成员方法
    final void finalMethod() {
        System.out.println("This is a final method.");
    }
}

// 尝试继承 final 类(这将导致编译错误)
// class Subclass extends FinalClass {} // 编译错误,无法继承 final 类

public class S4_2 {
    public static void main(String[] args) {
        FinalClass finalObject = new FinalClass();

        // 尝试修改 final 变量的值(这将导致编译错误)
        // finalObject.constantValue = 20; // 编译错误,无法修改 final 变量的值

        // 调用 final 方法
        finalObject.finalMethod();
    }
}

列出测试数据和实验结果截图: 

在上面示例中,FinalClass被声明为final,表示它不能被继承。同时,constantValue被声明为final,表示它是一个常量,其值不能被修改。finalMethod方法也被声明为final,表示它不能被子类重写。

如果我们尝试继承一个final类或修改一个final变量的值,就会导致编译错误。

⭐下面分条演示 final 关键字的作用

1、没有final修饰时

public class S4_2 {
    public static void main(String[] args) {
        Child child = new Child();
        child.print();
    }
}
class Father {
    String name;
    int age;
    void print(){
        System.out.println("hello world!");
    }
}

class Child extends Father{
    public Child(){
        super();
    }
    void print(){
        System.out.println("goodbye!!");
    }
}

调用了子类重写父类的方法print(),并成功输出子类的方法内容。 

2、有final修饰父类中的方法时

class Father {
    String name;
    int age;
    final void print(){
        System.out.println("hello world!");
    }
}

class Child extends Father{
    public Child(){
        super();
    }
    void print(){
        System.out.println("goodbye!!");
    }
}

程序报错,无法正常运行。因为被final修饰的方法不能被子类重写。 

3、final修饰变量

public class S4_2 {
    public static void main(String[] args) {
        final double PI = 3.14;
        PI = 666;
        System.out.println("PI is "+PI);
    }
}

PI被final修饰,此时PI为常量,必须在定义时赋值且只能赋值一次,在运行时值不能改变。 

如图,当为PI第二次赋值时,程序会报编译错误。

4、final修饰类

final class Father {
    String name;
    int age;
    void print(){
        System.out.println("hello world!");
    }
}

class Child extends Father{
    public Child(){
        super();
    }
    void print(){
        System.out.println("goodbye!!");
    }
}

由于final修饰的类不能被继承,因此在父类Father前用final修饰,继承操作会报编译错误。

3、研究生薪资管理

(注:在职研究生继承学生类,实现雇员接口)

在学校中,学生每个月需要交相应的生活费(2000元),雇员每个月有相应的工资(1000~3000随机生成),而在职研究生(on-the-job postgraduate)既是雇员又是学生,所以在职研究生既需要交学费又会有工资。下面要求编写一个程序来统计在职研究生的收入与学费,如果收入无法满足交学费,则输出“撸起袖子加油干!”信息。(思考:如果使用抽象类,是否能完成该要求?)

本题使用Java接口和继承来实现这个薪资管理系统。

首先定义一个Student类作为父类,其中包含学费信息;一个Employee接口表示雇员,以及一个OnTheJobPostgraduate类,该类继承自Student类并实现Employee接口。

OnTheJobPostgraduate类中进行随机生成工资,并通过getSalary方法获取工资。(当然也可以在控制台自己输入工资数据。)在main方法中通过创建一个OnTheJobPostgraduate对象来获取学费和工资信息。

注意:Math.random()方法用户生成随机值。

⭐简介Math.random()

Math.random() 是 Java 中 java.lang.Math 类提供的一个静态方法,用于生成一个伪随机浮点数,取值范围是0.0(包含)到1.0(不包含)之间。【左闭右开】

通常Math.random() 会与其他数学操作一起使用来生成在指定范围内的随机数,例如生成一个介于最小值和最大值之间的随机整数:

int min = 5;
int max = 15;

int randomNum = min + (int)(Math.random() * ((max - min) + 1));

推导:

random()        [0, 1)

random() * 2000        [0, 2000)

1000 + random()*2000        [1000, 3000)

即 1000 + random()*2000 能随机生成一个 [1000, 3000) 之间的随机数。注意:random函数生成的是伪随机数,但当不传参时,默认以时间戳作为随机种子。因此,当不传参时,random()生成的是真随机数;而传参时,random()生成的是以参数作为随机种子的伪随机数。

源代码:

// 学生父类
class Student {
    int tuition = 2000; // 学费

    int getTuition() {
        return tuition;
    }
}

// 雇员接口
interface Employee {
    int getSalary();
}

// 在职研究生类,继承学生类并实现雇员接口
class OnTheJobPostgraduate extends Student implements Employee {
    // 随机生成工资(1000~3000之间)
    int salary = (int) (1000 + Math.random() * 2000);

    @Override
    public int getSalary() {
        return salary;
    }
}

public class S4_3 {
    public static void main(String[] args) {
        OnTheJobPostgraduate student = new OnTheJobPostgraduate();

        int tuition = student.getTuition();
        int salary = student.getSalary();

        System.out.println("学费:" + tuition + "元");
        System.out.println("工资:" + salary + "元");

        if (salary >= tuition) {
            System.out.println("学费已支付!");
        } else {
            System.out.println("撸起袖子加油干!");
        }
    }
}

列出测试数据和实验结果截图:  

4、创建一个抽象交通工具Vehicle类。

它有 wheelNum 和 seatNum 两个成员变量以及抽象方法 display()。 类 Bus 和类 Motorcycle 继承自Vehicle类,实现打印成员变量的 display()方法。在主函数中分别生成Bus对象和Motorcycle对象,上转型为Vehicle对象调用 display()方法。 

  1. 抽象类和抽象方法:在代码中定义抽象类 Vehicle,其中可以包含一个或多个抽象方法。抽象类不能被实例化,而是用作其他类的父类,其中包含了一些共同的属性和方法,但这些方法的具体实现由其子类提供。本题中display()就是一个抽象方法,没有具体的实现。因此任何继承 Vehicle 的子类都必须提供自己的 display() 方法实现。

  2. 继承:创建BusMotorcycle两个子类继承自抽象类 Vehicle。继承允许子类继承父类的属性和方法,并且可以添加自己的属性和方法。子类可以覆盖或实现父类的抽象方法。

  3. 方法重写(override):在子类 BusMotorcycle 中,我们使用 @Override 注解来覆盖父类 Vehicle 中的抽象方法 display()。也就是说子类提供了自己的方法实现版本而覆盖了父类的方法。

  4. 向上转型:在主函数中我们创建 BusMotorcycle 对象,然后将它们向上转型为 Vehicle 对象。这是一种多态的应用,我们可以将子类的对象视为父类的对象。这样可以用一种通用的方式来处理不同类型的子类对象。

  5. 成员变量和构造函数:在子类中我们使用构造方法来初始化子类成员变量 wheelNumseatNum。为每个子类对象添加自己的属性值。

源代码:

// 抽象交通工具类
abstract class Vehicle {
    int wheelNum;
    int seatNum;

    // 抽象方法
    abstract void display();
}

// 公交车类
class Bus extends Vehicle {
    Bus(int wheelNum, int seatNum) {
        this.wheelNum = wheelNum;
        this.seatNum = seatNum;
    }

    @Override
    void display() {
        System.out.println("Bus 有 Wheels: " + wheelNum + ", Seats: " + seatNum);
    }
}

// 摩托车类
class Motorcycle extends Vehicle {
    Motorcycle(int wheelNum, int seatNum) {
        this.wheelNum = wheelNum;
        this.seatNum = seatNum;
    }

    @Override
    void display() {
        System.out.println("Motorcycle 有 Wheels: " + wheelNum + ", Seats: " + seatNum);
    }
}

public class S4_4 {
    public static void main(String[] args) {
        Vehicle vehicle1 = new Bus(6, 40); // 创建 Bus 对象并上转型为 Vehicle
        Vehicle vehicle2 = new Motorcycle(2, 2); // 创建 Motorcycle 对象并上转型为 Vehicle

        // 调用 display 方法
        vehicle1.display(); // 调用 Bus 的 display 方法
        vehicle2.display(); // 调用 Motorcycle 的 display 方法
    }
}

列出测试数据和实验结果截图:   

修改程序中传入构造函数的参数:

5、经理与员工工资,主要考察多态

某公司的人员分为员工和经理两类,但经理也属于员工中的一类,公司员工和经理都有自己的姓名,年龄,工号、工资、工龄等属性(通过属性无法区分员工和经理)和工资上涨函数。假设每次给员工涨工资一次能涨10%,经理能涨20%。要求利用多态实现给员工和经理涨工资,测试并通过。

⭐关于多态

多态是面向对象编程中的一个重要概念,它允许不同的对象以一种统一的方式进行操作。

多态性使得程序能够处理不同类的对象而无需知道对象的确切类型。它是面向对象编程的三大特性之一(其它两个是封装和继承)。

多态性的核心思想包括:

  1. 方法重写(Overriding): 在继承关系中,子类可以提供对父类的方法的不同实现。子类可以根据自己的需求来重写父类的方法。

  2. 父类引用指向子类对象: 多态性允许一个父类的引用变量来引用一个子类的对象。可以使用一个父类的引用来调用子类重写的父类的方法,实现父类引用指向子类对象的多态性。

  1. 动态绑定: 多态使得方法的具体实现在运行时而不在编译时确定。程序在运行时根据对象的实际类型来确定调用的方法。

  2. 抽象类和接口: 多态性常常与抽象类和接口一起使用。抽象类和接口定义了一组方法的规范,不提供具体实现。子类必须提供这些方法的具体实现来实现多态性。

源代码:

class Employee {
    public String name;
    public int age;
    public String employeeId;
    public double salary;
    public int yearsOfWork;

    public Employee(String name, int age, String employeeId, double salary, int yearsOfWork) {
        this.name = name;
        this.age = age;
        this.employeeId = employeeId;
        this.salary = salary;
        this.yearsOfWork = yearsOfWork;
    }

    public void displayInfo() {
        System.out.println("姓名: " + name);
        System.out.println("年龄: " + age);
        System.out.println("员工 ID: " + employeeId);
        System.out.println("薪水: " + salary);
        System.out.println("工作年份: " + yearsOfWork);
    }

    public void raiseSalary() {
        //默认增长 10%
        salary = salary * 1.10;
    }
}

class Manager extends Employee {
    public Manager(String name, int age, String employeeId, double salary, int yearsOfWork) {
        super(name, age, employeeId, salary, yearsOfWork);
    }

    @Override
    public void raiseSalary() {
        // 经理增长 20%
        salary = salary * 1.20;
    }
}

class Staff extends Employee {
    public Staff(String name, int age, String employeeId, double salary, int yearsOfWork) {
        super(name, age, employeeId, salary, yearsOfWork);
    }
}

public class S4_5 {
    public static void main(String[] args) {
        Employee manager = new Manager("张三", 35, "M123", 50000, 5);
        Employee staff = new Staff("李四", 28, "S456", 30000, 3);

        System.out.println("原始工资:");
        manager.displayInfo();
        staff.displayInfo();

        manager.raiseSalary();
        staff.raiseSalary();

        System.out.println("\n增长后工资:");
        manager.displayInfo();
        staff.displayInfo();
    }
}

列出测试数据和实验结果截图:    

6、把下面的代码补充完整,输出结果为“实现了Inner接口的匿名内部类!”,并测试输出结果。

interface Inner{

  void introduce();

}

class Outer{

       //补齐代码,完成方法主要功能

}

class InnerClassTest{

  public static void main(String[] args){

       Outer.method().introduce ();

  }

}

源代码: 

interface Inner {
    void introduce();
}

class Outer {
    public static Inner method() {
        return new Inner() {
            @Override
            public void introduce() {
                System.out.println("实现了Inner接口的匿名内部类!");
            }
        };
    }
}

public class S4_6 {
    public static void main(String[] args) {
        Outer.method().introduce();
    }
}

列出测试数据和实验结果截图:   

7、设计一个类,在类中能够处理自定义异常类并测试。(选做)

为了设计一个类来处理自定义异常类,首先需要定义一个自定义异常类,然后创建一个类,该类包含一个方法,可以抛出或捕获这个自定义异常。

首先,创建一个自定义异常类CustomException,继承自Exception类,以便可以捕获和处理这个异常:

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

接下来创建一个处理自定义异常的类ExceptionHandlingClass,并在其中定义一个方法divideNumbers,该方法接受两整数作为参数,如果第二个整数为0,则抛出自定义异常CustomException: 

class ExceptionHandlingClass {
    public void divideNumbers(int numerator, int denominator) throws CustomException {
        if (denominator == 0) {
            throw new CustomException("分母不能为 0 !");
        } else {
            int result = numerator / denominator;
            System.out.println("除法结果: " + result);
        }
    }
}

最后在主类中创建一个ExceptionHandlingClass对象,并在main方法中使用try-catch块来处理可能抛出的CustomException: 

public class S4_7 {
    public static void main(String[] args) {
        ExceptionHandlingClass exceptionHandler = new ExceptionHandlingClass();

        try {
            // 传递一个不为零的分母
//            exceptionHandler.divideNumbers(10, 2);

            // 传递零作为分母,抛出CustomException
             exceptionHandler.divideNumbers(10, 0);

        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        }
    }
}

源代码:  

class CustomException extends Exception {
    public CustomException(String message) {
        super(message);
    }
}

class ExceptionHandlingClass {
    public void divideNumbers(int numerator, int denominator) throws CustomException {
        if (denominator == 0) {
            throw new CustomException("分母不能为 0 !");
        } else {
            int result = numerator / denominator;
            System.out.println("除法结果: " + result);
        }
    }
}

public class S4_7 {
    public static void main(String[] args) {
        ExceptionHandlingClass exceptionHandler = new ExceptionHandlingClass();

        try {
            // 传递一个不为零的分母
            exceptionHandler.divideNumbers(10, 2);

            // 传递零作为分母,抛出CustomException
            // exceptionHandler.divideNumbers(10, 0);

        } catch (CustomException e) {
            System.out.println("Caught CustomException: " + e.getMessage());
        }
    }
}

 列出测试数据和实验结果截图:  

正常情况:

抛出异常情况:


实验总结

1. 实验过程中,我对Java类的继承与多态的运用更加熟练了。

2. 我对权限修饰符的了解更深了。在第五题中用protected修饰员工employee的属性,protected修饰的成员被称为保护成员,它可以被同一个包中的其他类和这个类的派生类访问,可以在该类的子类内部进行访问。

3. final关键字可以修饰变量包括成员变量和局部变量、方法和类。final就是“最终”的意思,它修饰的方法不能被子类重写;final修饰的变量会变成常量,不可对其值进行修改;final修饰类时,该类不可被继承。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值