我应该使用静态方法还是实例方法?

Should I be using static or instance methods?

我真的很困惑静态方法和实例方法的概念。我创建了一个 BMR 计算器。我使用不同的 类.

将 GUI 从计算中分离出来
public class Calculations {

/**
 * If user input is correct, this method will calculate the BMR value of the user given their input and measurement choices.
 * 
 * @param userAge, userHeight, userWeight
 * @return BMR Value as a string
 */
public static int calcBMR(int age, String gender, double height, double weight) {

    // This is the body of the calculations - different formulas used depending on gender. Conversions to kg and cm done earlier so no conversions needed here.
    if (gender.equals("M")) { // Uses male bmr formula if genderMale radiobutton is selected
        return (int) (Math.round((10 * weight) + (6.25 * height) - (5 * age) + 5)); // This is the Miffin St-Jeor formula, calculations done in cm/kg
    } else { // else gender.equals("F") - there are only 2 options for gender, M or F.
        return (int) (Math.round((10 * weight) + (6.25 * height) - (5 * age) - 161));
    }
}


/**
 * If the user selects the TDEE option, this method will be executed after the calcBMR() method. 
 * A value from the calcBMR() method will be passed down to this method, and is multiplied
 * by the activity level parameter passed into this method.
 * 
 * @param selectedActivityLevel
 * @return TDEE Value (as a string)
 */
public static int calcTDEE(double activityMultiplier, int bmr) {
    System.out.println(activityMultiplier);
    return (int) Math.round(bmr * activityMultiplier);
}

}

如您所见,方法是静态的,但是传递给(两个方法)的变量是实例变量。

我只是通过以下几行调用这些方法:

            bmrValue = Calculations.calcBMR(userAge, userGender, userHeight, userWeight);
            bmrLabel.setText("<html><br /><font size=4>You have a <i><font color=#ce0000>BMR</font></i> of: " + "<font color=#59AF0E>" +  bmrValue + "</font></html>");
            if (tdeeYes.isSelected()) {
                userActivityLevel = activityMap.get(activityLevelBox.getSelectedItem());
                // Looks up selected item from combo box, which is the KEY. Then looks up the value to this key from the map - this value is the TDEE multiplier.
                tdeeLabel.setText("<html><br /><font size=4>You have a <i><font color=#ce0000>TDEE</font></i> of: " + "<font color=#59AF0E>" + Calculations.calcTDEE(userActivityLevel, bmrValue) + "</font></html>");
                }

变量定义为:

HashMap<String, Double> activityMap;
String[] activityLevels = {"Sedentary", "Lightly Active", "Moderately Active", "Very Active", "Extra Active"};


int userAge;
String userGender;
double userHeight;
double userWeight;
double userActivityLevel;

int bmrValue;

我是否正确使用了 static/instance 变量?早些时候我将所有参数变量设置为静态,因为我知道静态方法只能访问静态变量。直到现在我才知道参数可以是实例变量。

如有任何指导,我们将不胜感激。

我使用静态变量的经验法则有点像这样:

  1. 不使用static
  2. 需要使用时static参考(1)

在大多数情况下,上述经验法则都有效。但是,我们拥有由 JDK、apache-commons 库推广的标准习语。查看它们,您会发现:

  1. 拥有包含所有静态方法的实用程序 classes 是可以的。
  2. 可以使用 classes 中的 static 字段创建常量。不过,为此使用 Enum 可能是更好的选择。
  3. 可以将在同一个 class 上工作的一次性实用函数放入 class。

至于重组代码,您应该看看谁拥有数据。在这种情况下,Calculations class 中的两种方法看起来都在使用另一个 class 中的数据。将方法移动到另一个 class.

Calculations class 存在的唯一原因应该是,如果您正在使用多个其他 class 不相关的方法。如果它们是相关的,您尝试对它们的关系建模并查看这些方法的去向。

对于初学者来说,静态变量和实例变量之间的区别在于,class 的所有实例只存在一个静态变量,而 class 的每个实例都存在一个实例变量.

现在,当您谈论方法时,在大多数情况下,当您试图从另一个静态方法(例如 main)调用它时,您需要将它设为静态。

总的来说,这种做法对于 OOP 是错误的,您可能应该重新考虑程序的结构方式。

如果您可以提供有关用于调用这些方法的代码的更多详细信息,我将能够帮助您提供有关如何解决此问题的更多详细信息。

编辑: 根据您提供的新信息:

1) 我相信 bmrMain、BMRMain 和 Calculations 都可以合并为一个 class。新 class 的构造函数应调整为读取输入(以及每个变量的 getter 和 setter 以增加灵活性 - 可选)

2) 关于bmr部分的计算,有很多方法可以解决这个问题,所以我会继续提出我认为最好的方法。 添加一个带有文本 "Click to calculate" 或类似内容的按钮,并实现一个 ActionListener。动作监听器将依次调用(每当单击按钮时)计算所有内容的方法,最后它会更改 JLabel 上的文本。

动作侦听器的示例代码:

JButton button = new JButton("Text Button");
button.addActionListener(new ActionListener()
{
  public void actionPerformed(ActionEvent e)
  {
    //Call the methods for the calculation
    // bmr and tdee are temporary variables to store the results from the methods
    //handle them properly, or remove them if not needed (I just wrote a sample)
    int bmr = calcBMR(...); //pass the correct variables as arguments here
    int tdee;
    if(userHasPickedTDEE) {  tdee = calcTDEE(...); }  //and here
    label.setText(....);

  }
}); 

这 2 个步骤将解决您的 "static" 问题。

我建议您对 event-driven programming

做一些研究

这里有一些关于 Action Listeners

的额外和更深入的阅读

如果您需要进一步的帮助或说明,请告诉我:)

编辑 2:

一般来说,是的,分开 class 是一个很好的做法,以保持代码的可管理性。但在这种情况下,我认为只为 2 个方法创建一个新的 class 有点多余。 不过,如果您想保留当前结构,修复 "static" 的方法是: 1)从2种计算方法中去除静态。 2) 第 332 行应该是

Calculations c = new Calculations();
bmrValue = c.calcBMR(userAge, userGender, userHeight, userWeight);

FINAL_EDIT:

好吧,这些问题可以很容易地转移到他们自己的线程中。但这里有一个有用的 link 来自我刚刚进行的快速 google 搜索,这将有助于消除静态关键字的迷惑:

Static keyword in Java

每当我的 class 仅用于调用这些方法时,我都使用静态方法(即我的主要方法调用静态方法以在主要 class 等中设置变量)。我相信这就是 KDM 提到的 "utility" class。我使用静态或实用程序时的一个小例子 classes:

class Utility {
  public static int add(int a, int b) {
    // You would, of course, put something besides simple addition here
    return a + b;
  }
}

class Main {
  public static void main(String[] args) {
    int a = 2, b = 3;
    int sum = Utility.add(a, b);
    System.out.println(sum);
  }
}

另一方面,如果您的 class 更接近实际对象,具有自己的属性,请远离静态。如果您使用静态,那么您希望成为独立对象的每个 class 实例最终将具有相同的值。

根据您的 class 的名称和功能,您似乎有一个实用程序 class。 static应该可以,但未必有必要。但是,假设地,如果您想将此 class 与多个 "people" class 实例一起使用以计算 BMR(每个实例都是唯一的),那么我会把 calcBMR() 在一个人 class 中并使其成为非静态的,以便每个人都有自己的 calcBMR().

编辑:也许这也会有帮助:

实例 -> 实例化:new Calculations().instanceMethod();

静态 -> Class 本身,class 的 "state":Calculations.staticMethod();

如果您考虑我需要实例方法还是静态方法,您将会迷失方向。 Java 不是面向方法的,而是面向对象的。因此,对于您的功能,决定您是想要一个对象还是一个纯函数(纯函数意味着没有依赖关系也没有副作用,它严格来说是一个给定这个returns那个的函数)。

最好将其建模为对象,因为您拥有描述用户某些方面的信息,将它们放在一起可能有意义:

public class User {
    private int age;
    private String gender; // todo: use an Enum
    private double height;
    private double weight;
    private String activityLevel; // todo: use an Enum

    public User(int age, String gender, double height, double weight,
        String activityLevel) {
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.weight = weight;
        this.activityLevel = activityLevel;
    }

    public double calculateBmr() {
        int offset = gender.equals("M") ? 5 : -161;
        return (int) (Math.round((10 * weight) 
            + (6.25 * height) - (5 * age) + offset));
    }
}

等这样我们就不必在变量前加上"user",它们都放在一个对象中。对象是收集相关信息的组织方案,实例方法是使用对象信息的函数。

另一种方法是创建实用程序 class:

public final class UserCalcUtil {
    private UserCalcUtil() {} // no point instantiating this

    public static int calculateBmr(String gender, double height, 
    double weight, int age) {
        int offset = gender.equals("M") ? 5 : -161;
        return (int) (Math.round((10 * weight) 
            + (6.25 * height) - (5 * age) + offset));            
    }
}

在这里,您单独跟踪用户数据,并在您想要计算某些内容时将其传递给静态方法。实用程序 class 是单独静态方法(您可以将其视为函数)的垃圾场。

您是要使用对象还是实用程序 classes 取决于您希望如何组织数据并对其进行计算。如果您总是对单个对象进行计算,则将用户的数据与作用于该数据的方法打包在一起可能会更方便,或者如果有多个不相关的对象,则将计算移动到实用程序 class 中可能更有意义class您要计算的内容。由于 Java 对面向对象的构造有更多支持,因此采用对象方法通常很有意义。

并且您可以使用静态方法,但除了常量之外,避免使用静态变量(public static final,具有不可变的类型)。