*本文是博主对Java各种实验的再整理与详解,除了代码部分和解析部分,一些题目还增加了拓展部分(⭐)。拓展部分不是实验报告中原有的内容,而是博主本人自己的补充,以方便大家额外学习、参考。
目录
2、定义一个类,在类中声明成员变量和成员方法,尝试使用final关键词修饰类中的变量、方法及该类,测试并查看结果,必要时加以注释。
6、把下面的代码补充完整,输出结果为“实现了Inner接口的匿名内部类!”,并测试输出结果。
1、理解继承的概念;
2、掌握方法的重写;
3、掌握super、final关键字的使用;
4、掌握抽象类和接口的使用;
5、掌握多态的使用;
6、掌握内部类的使用;
7、掌握异常处理方式,能够自定义异常类;
8、了解Object类。
定义一个基类作为父类,再定义一个继承父类的子类,在子类中重写父类的方法,使用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(); // 调用子类的方法,同时会调用父类的方法
-
- }
- }
列出测试数据和实验结果截图:
在 Java 中,final关键字可以用来修饰类、变量和方法:
修饰类:当一个类被声明为final
时,它表示这个类不能被继承。无法创建它的子类。
修饰变量:当一个变量被声明为final
时,它表示这个变量的值只能被赋值一次,之后不可再修改。它是一个常量。
修饰方法:当一个方法被声明为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
变量的值,就会导致编译错误。
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修饰,继承操作会报编译错误。
(注:在职研究生继承学生类,实现雇员接口)
在学校中,学生每个月需要交相应的生活费(2000元),雇员每个月有相应的工资(1000~3000随机生成),而在职研究生(on-the-job postgraduate)既是雇员又是学生,所以在职研究生既需要交学费又会有工资。下面要求编写一个程序来统计在职研究生的收入与学费,如果收入无法满足交学费,则输出“撸起袖子加油干!”信息。(思考:如果使用抽象类,是否能完成该要求?)
本题使用Java接口和继承来实现这个薪资管理系统。
首先定义一个Student
类作为父类,其中包含学费信息;一个Employee
接口表示雇员,以及一个OnTheJobPostgraduate
类,该类继承自Student
类并实现Employee
接口。
在OnTheJobPostgraduate
类中进行随机生成工资,并通过getSalary
方法获取工资。(当然也可以在控制台自己输入工资数据。)在main
方法中通过创建一个OnTheJobPostgraduate
对象来获取学费和工资信息。
注意: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("撸起袖子加油干!");
- }
- }
- }
列出测试数据和实验结果截图:
它有 wheelNum 和 seatNum 两个成员变量以及抽象方法 display()。 类 Bus 和类 Motorcycle 继承自Vehicle类,实现打印成员变量的 display()方法。在主函数中分别生成Bus对象和Motorcycle对象,上转型为Vehicle对象调用 display()方法。
抽象类和抽象方法:在代码中定义抽象类 Vehicle
,其中可以包含一个或多个抽象方法。抽象类不能被实例化,而是用作其他类的父类,其中包含了一些共同的属性和方法,但这些方法的具体实现由其子类提供。本题中display()
就是一个抽象方法,没有具体的实现。因此任何继承 Vehicle
的子类都必须提供自己的 display()
方法实现。
继承:创建Bus
和Motorcycle
两个子类继承自抽象类 Vehicle
。继承允许子类继承父类的属性和方法,并且可以添加自己的属性和方法。子类可以覆盖或实现父类的抽象方法。
方法重写(override):在子类 Bus
和 Motorcycle
中,我们使用 @Override
注解来覆盖父类 Vehicle
中的抽象方法 display()
。也就是说子类提供了自己的方法实现版本而覆盖了父类的方法。
向上转型:在主函数中我们创建 Bus
和 Motorcycle
对象,然后将它们向上转型为 Vehicle
对象。这是一种多态的应用,我们可以将子类的对象视为父类的对象。这样可以用一种通用的方式来处理不同类型的子类对象。
成员变量和构造函数:在子类中我们使用构造方法来初始化子类成员变量 wheelNum
和 seatNum
。为每个子类对象添加自己的属性值。
源代码:
- // 抽象交通工具类
- 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 方法
- }
- }
列出测试数据和实验结果截图:
修改程序中传入构造函数的参数:
某公司的人员分为员工和经理两类,但经理也属于员工中的一类,公司员工和经理都有自己的姓名,年龄,工号、工资、工龄等属性(通过属性无法区分员工和经理)和工资上涨函数。假设每次给员工涨工资一次能涨10%,经理能涨20%。要求利用多态实现给员工和经理涨工资,测试并通过。
多态是面向对象编程中的一个重要概念,它允许不同的对象以一种统一的方式进行操作。
多态性使得程序能够处理不同类的对象而无需知道对象的确切类型。它是面向对象编程的三大特性之一(其它两个是封装和继承)。
多态性的核心思想包括:
方法重写(Overriding): 在继承关系中,子类可以提供对父类的方法的不同实现。子类可以根据自己的需求来重写父类的方法。
父类引用指向子类对象: 多态性允许一个父类的引用变量来引用一个子类的对象。可以使用一个父类的引用来调用子类重写的父类的方法,实现父类引用指向子类对象的多态性。
动态绑定: 多态使得方法的具体实现在运行时而不在编译时确定。程序在运行时根据对象的实际类型来确定调用的方法。
抽象类和接口: 多态性常常与抽象类和接口一起使用。抽象类和接口定义了一组方法的规范,不提供具体实现。子类必须提供这些方法的具体实现来实现多态性。
源代码:
- 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();
- }
- }
列出测试数据和实验结果截图:
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();
- }
- }
列出测试数据和实验结果截图:
为了设计一个类来处理自定义异常类,首先需要定义一个自定义异常类,然后创建一个类,该类包含一个方法,可以抛出或捕获这个自定义异常。
首先,创建一个自定义异常类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修饰类时,该类不可被继承。