杰瑞科技汇

Java中两个类如何实现多层函数调用?

我会从最简单、最直接的方式讲起,然后逐步介绍更高级和灵活的方式,并提供完整的代码示例。

Java中两个类如何实现多层函数调用?-图1
(图片来源网络,侵删)

核心思想

在 Java 中,一个类要调用另一个类的函数,必须遵循以下步骤:

  1. 创建对象(实例化):要调用一个非静态(static)的方法,你必须先创建该类的对象(实例),对象就是这个类在内存中的一个“具体代表”。
  2. 通过对象调用方法:使用 对象名.方法名() 的语法来调用目标类中的方法。

直接调用(最常见)

这是最基础、最直接的方式,一个类创建另一个类的对象,然后调用其公共方法。

示例场景

假设我们有一个 Calculator 类,它提供一些数学计算功能,然后我们有一个 Main 类,作为程序的入口,它使用 Calculator 来进行计算。

步骤 1: 创建被调用的类 (Calculator.java)

这个类包含一些我们想要调用的方法。

Java中两个类如何实现多层函数调用?-图2
(图片来源网络,侵删)
// Calculator.java
public class Calculator {
    // 这是一个公共方法,可以被其他类访问
    public int add(int a, int b) {
        System.out.println("Calculator: 正在执行加法...");
        return a + b;
    }
    // 另一个公共方法
    public double divide(double x, double y) {
        System.out.println("Calculator: 正在执行除法...");
        if (y == 0) {
            System.out.println("错误:除数不能为零!");
            return Double.NaN; // 返回“不是一个数字”
        }
        return x / y;
    }
}

关键点:

  • public 关键字表示这个方法可以被任何其他类访问。
  • 这些方法不是 static 的,所以我们必须先创建 Calculator 的对象才能调用它们。

步骤 2: 创建调用类 (Main.java)

这个类会创建 Calculator 的对象并使用它。

// Main.java
public class Main {
    public static void main(String[] args) {
        // 1. 创建 Calculator 类的对象
        //    这就像在现实生活中拿出一个计算器
        Calculator myCalculator = new Calculator();
        // 2. 通过对象调用 Calculator 类的方法
        int sum = myCalculator.add(10, 5);
        System.out.println("Main: 计算结果是 " + sum);
        System.out.println("--------------------");
        double result = myCalculator.divide(20.0, 4.0);
        System.out.println("Main: 计算结果是 " + result);
        System.out.println("--------------------");
        myCalculator.divide(10.0, 0.0); // 测试除数为零的情况
    }
}

关键点:

  • Calculator myCalculator = new Calculator(); 这行代码做了两件事:
    • new Calculator(): 在内存中分配空间,创建一个新的 Calculator 对象。
    • Calculator myCalculator: 声明一个名为 myCalculatorCalculator 类型的变量,让它指向新创建的对象。
  • myCalculator.add(10, 5): 使用 操作符,通过 myCalculator 这个对象来访问并执行 add 方法。

运行结果

Calculator: 正在执行加法...
Main: 计算结果是 15
--------------------
Calculator: 正在执行除法...
Main: 计算结果是 5.0
--------------------
Calculator: 正在执行除法...
错误:除数不能为零!

调用静态方法(无需创建对象)

如果一个方法被声明为 static,它就属于这个类本身,而不是属于任何一个特定的对象,调用静态方法时不需要创建对象

Java中两个类如何实现多层函数调用?-图3
(图片来源网络,侵删)

修改 Calculator.java

我们添加一个静态方法 multiply

// Calculator.java
public class Calculator {
    // ... (之前的 add 和 divide 方法保持不变) ...
    // 这是一个静态方法,属于 Calculator 类本身
    public static int multiply(int a, int b) {
        System.out.println("Calculator.multiply: 正在执行乘法..."); // 静态方法不能直接访问非静态成员
        return a * b;
    }
}

Main.java 中调用静态方法

// Main.java
public class Main {
    public static void main(String[] args) {
        // 调用静态方法,直接使用类名,不需要创建对象
        int product = Calculator.multiply(6, 7);
        System.out.println("Main: 计算结果是 " + product);
        // 你也可以通过对象调用,但不推荐,这会引起混淆
        // Calculator myCalculator = new Calculator();
        // int product2 = myCalculator.multiply(6, 7);
    }
}

关键点:

  • 调用静态方法使用 类名.方法名() 的语法。
  • 静态方法通常用于工具类或与类状态无关的操作。

通过构造函数传递对象(更高级)

一个类需要长期持有另一个类的对象引用,而不是临时创建一个用完就扔,这时可以通过构造函数将对象传递进来。

示例场景

一个 Teacher 类需要知道他教的 Student 的信息。

步骤 1: 创建被依赖的类 (Student.java)

// Student.java
public class Student {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

步骤 2: 创建依赖的类 (Teacher.java)

Teacher 类在创建时就需要一个 Student 对象。

// Teacher.java
public class Teacher {
    private Student student; // Teacher 类持有一个 Student 对象的引用
    // 构造函数,接收一个 Student 对象
    public Teacher(Student student) {
        this.student = student;
        System.out.println("Teacher: 我开始教 " + student.getName() + " 了。");
    }
    // Teacher 的方法,可以调用它持有的 Student 对象的方法
    public void doSomethingWithStudent() {
        System.out.println("Teacher: " + student.getName() + " 同学,请回答问题!");
    }
}

步骤 3: 在 Main.java 中组装它们

// Main.java
public class Main {
    public static void main(String[] args) {
        // 1. 先创建被依赖的对象
        Student zhangSan = new Student("张三");
        // 2. 将这个对象作为参数,传递给 Teacher 的构造函数
        Teacher liTeacher = new Teacher(zhangSan);
        // 3. Teacher 现在可以调用它内部持有的 Student 对象的方法
        liTeacher.doSomethingWithStudent();
    }
}

运行结果:

Teacher: 我开始教 张三 了。
Teacher: 张三 同学,请回答问题!

这种方式在复杂程序中非常常见,是“依赖注入”思想的基础。


接口回调(最灵活)

这是面向接口编程的核心思想,调用方不关心具体是哪个类实现了功能,只关心它实现了某个接口,这使得代码非常灵活,易于扩展和替换。

示例场景

一个 Button(按钮)被点击后,需要执行某个操作,这个操作的具体内容是由使用者决定的。

步骤 1: 定义一个接口 (ClickListener.java)

接口定义了一个“契约”,规定了必须实现哪些方法。

// ClickListener.java
public interface ClickListener {
    // 当按钮被点击时,必须调用这个方法
    void onClick();
}

步骤 2: 创建实现接口的类 (EmailSender.javaSmsSender.java)

这两个类都实现了 ClickListener 接口,但它们的行为完全不同。

// EmailSender.java
public class EmailSender implements ClickListener {
    @Override
    public void onClick() {
        System.out.println("EmailSender: 发送一封邮件!");
    }
}
// SmsSender.java
public class SmsSender implements ClickListener {
    @Override
    public void onClick() {
        System.out.println("SmsSender: 发送一条短信!");
    }
}

步骤 3: 创建调用方 (Button.java)

Button 类持有一个 ClickListener 类型的引用,它不关心这个引用具体指向 EmailSender 还是 SmsSender

// Button.java
public class Button {
    private ClickListener listener; // 持有接口类型的引用
    // 设置点击监听器
    public void setClickListener(ClickListener listener) {
        this.listener = listener;
    }
    // 模拟按钮被点击
    public void click() {
        System.out.println("Button: 按钮被点击了!");
        if (listener != null) {
            listener.onClick(); // 调用接口的方法,具体执行哪个类的实现,取决于 set 的是谁
        }
    }
}

步骤 4: 在 Main.java 中使用

// Main.java
public class Main {
    public static void main(String[] args) {
        Button sendButton = new Button();
        // 场景一:设置邮件发送器为点击监听器
        System.out.println("--- 场景一:发送邮件 ---");
        sendButton.setClickListener(new EmailSender());
        sendButton.click();
        // 场景二:轻松切换到短信发送器
        System.out.println("\n--- 场景二:发送短信 ---");
        sendButton.setClickListener(new SmsSender());
        sendButton.click();
    }
}

运行结果:

--- 场景一:发送邮件 ---
Button: 按钮被点击了!
EmailSender: 发送一封邮件!
--- 场景二:发送短信 ---
Button: 按钮被点击了!
SmsSender: 发送一条短信!
方式 适用场景 如何调用 优点 缺点
直接调用 最常见的情况,一个类需要临时使用另一个类的功能。 ClassName obj = new ClassName();
obj.method();
简单直观。 耦合度相对较高。
静态方法调用 工具类方法、与类实例无关的操作。 ClassName.staticMethod(); 无需创建对象,节省内存。 过度使用会破坏面向对象特性,难以继承和覆盖。
构造函数传递 一个类的生命周期依赖于另一个类。 new DependentClass(new Dependency()); 关系明确,利于管理依赖。 可能导致构造函数参数过多。
接口回调 需要解耦、提高灵活性和可扩展性的场景(如事件监听、策略模式)。 button.setClickListener(new SomeListener());
button.click();
低耦合、高内聚,是优秀设计的标志。 需要额外定义接口,增加了代码的复杂性。

对于初学者来说,掌握方式一(直接调用)和方式二(静态方法调用)是最重要的,随着经验的增长,你会越来越多地使用方式三和方式四来构建更健壮、更灵活的系统。

分享:
扫描分享到社交APP
上一篇
下一篇