我应该使用静态方法还是实例方法?
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 变量?早些时候我将所有参数变量设置为静态,因为我知道静态方法只能访问静态变量。直到现在我才知道参数可以是实例变量。
如有任何指导,我们将不胜感激。
我使用静态变量的经验法则有点像这样:
- 不使用
static
- 需要使用时
static
参考(1)
在大多数情况下,上述经验法则都有效。但是,我们拥有由 JDK、apache-commons 库推广的标准习语。查看它们,您会发现:
- 拥有包含所有静态方法的实用程序 classes 是可以的。
- 可以使用 classes 中的
static
字段创建常量。不过,为此使用 Enum
可能是更好的选择。
- 可以将在同一个 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 搜索,这将有助于消除静态关键字的迷惑:
每当我的 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,具有不可变的类型)。
我真的很困惑静态方法和实例方法的概念。我创建了一个 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 变量?早些时候我将所有参数变量设置为静态,因为我知道静态方法只能访问静态变量。直到现在我才知道参数可以是实例变量。
如有任何指导,我们将不胜感激。
我使用静态变量的经验法则有点像这样:
- 不使用
static
- 需要使用时
static
参考(1)
在大多数情况下,上述经验法则都有效。但是,我们拥有由 JDK、apache-commons 库推广的标准习语。查看它们,您会发现:
- 拥有包含所有静态方法的实用程序 classes 是可以的。
- 可以使用 classes 中的
static
字段创建常量。不过,为此使用Enum
可能是更好的选择。 - 可以将在同一个 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 搜索,这将有助于消除静态关键字的迷惑:
每当我的 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,具有不可变的类型)。