• 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()。

    源代码:

    1. // 定义父类
    2. class ParentClass {
    3. void display() {
    4. System.out.println("This is the parent class.");
    5. }
    6. }
    7. // 定义继承自父类的子类
    8. class ChildClass extends ParentClass {
    9. // 重写父类的方法
    10. void display() {
    11. System.out.println("This is the child class.");
    12. super.display(); // 使用super关键字调用父类的方法
    13. }
    14. }
    15. public class S4_1 {
    16. public static void main(String[] args) {
    17. ParentClass parent = new ParentClass();
    18. ChildClass child = new ChildClass();
    19. // 调用父类和子类的display方法
    20. parent.display(); // 调用父类的方法
    21. child.display(); // 调用子类的方法,同时会调用父类的方法
    22. }
    23. }

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


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

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

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

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

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

    源代码:

    1. // 修饰类
    2. final class FinalClass {
    3. // 修饰成员变量
    4. final int constantValue = 10;
    5. // 修饰成员方法
    6. final void finalMethod() {
    7. System.out.println("This is a final method.");
    8. }
    9. }
    10. // 尝试继承 final 类(这将导致编译错误)
    11. // class Subclass extends FinalClass {} // 编译错误,无法继承 final 类
    12. public class S4_2 {
    13. public static void main(String[] args) {
    14. FinalClass finalObject = new FinalClass();
    15. // 尝试修改 final 变量的值(这将导致编译错误)
    16. // finalObject.constantValue = 20; // 编译错误,无法修改 final 变量的值
    17. // 调用 final 方法
    18. finalObject.finalMethod();
    19. }
    20. }

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

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

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

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

    1、没有final修饰时

    1. public class S4_2 {
    2. public static void main(String[] args) {
    3. Child child = new Child();
    4. child.print();
    5. }
    6. }
    7. class Father {
    8. String name;
    9. int age;
    10. void print(){
    11. System.out.println("hello world!");
    12. }
    13. }
    14. class Child extends Father{
    15. public Child(){
    16. super();
    17. }
    18. void print(){
    19. System.out.println("goodbye!!");
    20. }
    21. }

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

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

    1. class Father {
    2. String name;
    3. int age;
    4. final void print(){
    5. System.out.println("hello world!");
    6. }
    7. }
    8. class Child extends Father{
    9. public Child(){
    10. super();
    11. }
    12. void print(){
    13. System.out.println("goodbye!!");
    14. }
    15. }

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

    3、final修饰变量

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

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

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

    4、final修饰类

    1. final class Father {
    2. String name;
    3. int age;
    4. void print(){
    5. System.out.println("hello world!");
    6. }
    7. }
    8. class Child extends Father{
    9. public Child(){
    10. super();
    11. }
    12. void print(){
    13. System.out.println("goodbye!!");
    14. }
    15. }

    由于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() 会与其他数学操作一起使用来生成在指定范围内的随机数,例如生成一个介于最小值和最大值之间的随机整数:

    1. int min = 5;
    2. int max = 15;
    3. 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()生成的是以参数作为随机种子的伪随机数。

    源代码:

    1. // 学生父类
    2. class Student {
    3. int tuition = 2000; // 学费
    4. int getTuition() {
    5. return tuition;
    6. }
    7. }
    8. // 雇员接口
    9. interface Employee {
    10. int getSalary();
    11. }
    12. // 在职研究生类,继承学生类并实现雇员接口
    13. class OnTheJobPostgraduate extends Student implements Employee {
    14. // 随机生成工资(1000~3000之间)
    15. int salary = (int) (1000 + Math.random() * 2000);
    16. @Override
    17. public int getSalary() {
    18. return salary;
    19. }
    20. }
    21. public class S4_3 {
    22. public static void main(String[] args) {
    23. OnTheJobPostgraduate student = new OnTheJobPostgraduate();
    24. int tuition = student.getTuition();
    25. int salary = student.getSalary();
    26. System.out.println("学费:" + tuition + "元");
    27. System.out.println("工资:" + salary + "元");
    28. if (salary >= tuition) {
    29. System.out.println("学费已支付!");
    30. } else {
    31. System.out.println("撸起袖子加油干!");
    32. }
    33. }
    34. }

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

    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。为每个子类对象添加自己的属性值。

    源代码:

    1. // 抽象交通工具类
    2. abstract class Vehicle {
    3. int wheelNum;
    4. int seatNum;
    5. // 抽象方法
    6. abstract void display();
    7. }
    8. // 公交车类
    9. class Bus extends Vehicle {
    10. Bus(int wheelNum, int seatNum) {
    11. this.wheelNum = wheelNum;
    12. this.seatNum = seatNum;
    13. }
    14. @Override
    15. void display() {
    16. System.out.println("Bus 有 Wheels: " + wheelNum + ", Seats: " + seatNum);
    17. }
    18. }
    19. // 摩托车类
    20. class Motorcycle extends Vehicle {
    21. Motorcycle(int wheelNum, int seatNum) {
    22. this.wheelNum = wheelNum;
    23. this.seatNum = seatNum;
    24. }
    25. @Override
    26. void display() {
    27. System.out.println("Motorcycle 有 Wheels: " + wheelNum + ", Seats: " + seatNum);
    28. }
    29. }
    30. public class S4_4 {
    31. public static void main(String[] args) {
    32. Vehicle vehicle1 = new Bus(6, 40); // 创建 Bus 对象并上转型为 Vehicle
    33. Vehicle vehicle2 = new Motorcycle(2, 2); // 创建 Motorcycle 对象并上转型为 Vehicle
    34. // 调用 display 方法
    35. vehicle1.display(); // 调用 Bus 的 display 方法
    36. vehicle2.display(); // 调用 Motorcycle 的 display 方法
    37. }
    38. }

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

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

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

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

    ⭐关于多态

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

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

    多态性的核心思想包括:

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

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

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

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

    源代码:

    1. class Employee {
    2. public String name;
    3. public int age;
    4. public String employeeId;
    5. public double salary;
    6. public int yearsOfWork;
    7. public Employee(String name, int age, String employeeId, double salary, int yearsOfWork) {
    8. this.name = name;
    9. this.age = age;
    10. this.employeeId = employeeId;
    11. this.salary = salary;
    12. this.yearsOfWork = yearsOfWork;
    13. }
    14. public void displayInfo() {
    15. System.out.println("姓名: " + name);
    16. System.out.println("年龄: " + age);
    17. System.out.println("员工 ID: " + employeeId);
    18. System.out.println("薪水: " + salary);
    19. System.out.println("工作年份: " + yearsOfWork);
    20. }
    21. public void raiseSalary() {
    22. //默认增长 10%
    23. salary = salary * 1.10;
    24. }
    25. }
    26. class Manager extends Employee {
    27. public Manager(String name, int age, String employeeId, double salary, int yearsOfWork) {
    28. super(name, age, employeeId, salary, yearsOfWork);
    29. }
    30. @Override
    31. public void raiseSalary() {
    32. // 经理增长 20%
    33. salary = salary * 1.20;
    34. }
    35. }
    36. class Staff extends Employee {
    37. public Staff(String name, int age, String employeeId, double salary, int yearsOfWork) {
    38. super(name, age, employeeId, salary, yearsOfWork);
    39. }
    40. }
    41. public class S4_5 {
    42. public static void main(String[] args) {
    43. Employee manager = new Manager("张三", 35, "M123", 50000, 5);
    44. Employee staff = new Staff("李四", 28, "S456", 30000, 3);
    45. System.out.println("原始工资:");
    46. manager.displayInfo();
    47. staff.displayInfo();
    48. manager.raiseSalary();
    49. staff.raiseSalary();
    50. System.out.println("\n增长后工资:");
    51. manager.displayInfo();
    52. staff.displayInfo();
    53. }
    54. }

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

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

    interface Inner{

      void introduce();

    }

    class Outer{

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

    }

    class InnerClassTest{

      public static void main(String[] args){

           Outer.method().introduce ();

      }

    }

    源代码: 

    1. interface Inner {
    2. void introduce();
    3. }
    4. class Outer {
    5. public static Inner method() {
    6. return new Inner() {
    7. @Override
    8. public void introduce() {
    9. System.out.println("实现了Inner接口的匿名内部类!");
    10. }
    11. };
    12. }
    13. }
    14. public class S4_6 {
    15. public static void main(String[] args) {
    16. Outer.method().introduce();
    17. }
    18. }

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

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

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

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

    1. class CustomException extends Exception {
    2. public CustomException(String message) {
    3. super(message);
    4. }
    5. }

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

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

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

    1. public class S4_7 {
    2. public static void main(String[] args) {
    3. ExceptionHandlingClass exceptionHandler = new ExceptionHandlingClass();
    4. try {
    5. // 传递一个不为零的分母
    6. // exceptionHandler.divideNumbers(10, 2);
    7. // 传递零作为分母,抛出CustomException
    8. exceptionHandler.divideNumbers(10, 0);
    9. } catch (CustomException e) {
    10. System.out.println("Caught CustomException: " + e.getMessage());
    11. }
    12. }
    13. }

    源代码:  

    1. class CustomException extends Exception {
    2. public CustomException(String message) {
    3. super(message);
    4. }
    5. }
    6. class ExceptionHandlingClass {
    7. public void divideNumbers(int numerator, int denominator) throws CustomException {
    8. if (denominator == 0) {
    9. throw new CustomException("分母不能为 0 !");
    10. } else {
    11. int result = numerator / denominator;
    12. System.out.println("除法结果: " + result);
    13. }
    14. }
    15. }
    16. public class S4_7 {
    17. public static void main(String[] args) {
    18. ExceptionHandlingClass exceptionHandler = new ExceptionHandlingClass();
    19. try {
    20. // 传递一个不为零的分母
    21. exceptionHandler.divideNumbers(10, 2);
    22. // 传递零作为分母,抛出CustomException
    23. // exceptionHandler.divideNumbers(10, 0);
    24. } catch (CustomException e) {
    25. System.out.println("Caught CustomException: " + e.getMessage());
    26. }
    27. }
    28. }

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

    正常情况:

    抛出异常情况:


    实验总结

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

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

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

  • 相关阅读:
    安卓面经_安卓基础面全解析<4/30>之内容提供者全解析
    python09 字符串切片,字符串反转
    Linux【安全 02】OpenSSH漏洞修复(离线升级最新版本流程)网盘分享3个安装包+26个离线依赖
    Spring Security—OAuth 2.0 资源服务器的多租户
    SpringBoot 中使用 RabbitTemplate
    【哈士奇赠书活动 - 44期】- 〖从零基础到精通Flutter开发〗
    多线程与高并发编程二
    Termius 8.4.0(多协议远程管理软件)
    #第九章 子查询课后习题
    MySQL的卸载
  • 原文地址:https://blog.csdn.net/wyd_333/article/details/129033981