在任何函数之外为成员变量赋值被认为是一种良好的实践吗?

3

例如:

public class App {

   private Car car = new Car();

   public static void main(String[] args) {
       // TO DO
   } 
}

如果不好,有什么解决办法?你会如何重写这段代码?
9个回答

1

我一直被教导,声明应该放在前面,初始化应该放在内部。在构造函数内部初始化更加高效,因为如果你需要在构造时使用传递的参数来改变它,你可以直接初始化而不是初始化和赋值。

例如:

public class TestClass{
   //Declared but uninitialized
   Object obj;

   //Makes no difference but easier to read
   public TestClass(){
      this.obj = new Object();
   }

   //In this constructor however the object being passed in is what is initializing obj
   //-so if you were to initialize it above and then change it down here you are writing 
   //-to the mem twice and it is less efficient.  
   public TestClass(Object arg){
      this.obj = (Object)arg;
   }
}

这种做法的一个缺点是,如今的内存非常便宜。除了不想显得像个新手之外,唯一的真正目的就是让其他人能够轻松管理。

我认为你应该使用“可重用的”而不是“高效”的说法。 - Sednus

1

如果程序将“始终”需要对象实例并且创建实例的成本不太高(时间、资源),则可以在声明中初始化对象。然后,是的,可能需要这种类型的“急切初始化”。

然而,这种设计违反了OO设计,使类保持松散耦合,并使单元测试更加困难。

例如:

public class App {
  private Car car = new Car();
}

您在说:
  1. 应用程序将“始终”需要一个Car对象。
  2. 在App被实例化之后,Car将始终被实例化。(如果Car的实例化很耗费资源,例如它还有几个对象在实例化时创建,并且从某种类型的远程调用中加载数据,这可能会有问题)
理想情况下,您只希望在实际需要时才创建对象。或者在构造函数(默认或重载)中提供一些灵活性。
public class App {
    private Car car;

    App() {
    }        

    // overloaded constructor
    App(Car car) {
        this.car = car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public Car getCar() {
        return car;
    }

    public static void main(String[] args) { 
        // default constructor, lightweight, no car initialization happening;
        App ap1 = new App();

       // Ok, now I want a car, and it should be red.
        Car redCar = new Car("red");
        ap1.setCar(redCar);


        // Using overloaded constructor, now I can control aspects of "car"
        Car blueCar = new Car("blue");
        App ap2 = new App(blueCar);
    }

}

1
在我看来,一切都取决于您正在开发的应用程序的设计。对于所提供的示例,我认为它是可以接受的。但对于其他更明确的数据类型,我更喜欢使用构造函数初始化,主要是因为构造函数重载是可能的。

0

如果你想让你的代码更容易测试,这是一个不好的做法。原因是创建 App 也会创建一个 Car,无论你是否需要它。现在,如果 Car 有连接到数据库的代码,那么当你测试 App 时,你需要有一个可用的数据库可以连接,否则你的测试将失败。

解决方案是依赖注入,也称为控制反转。你可以这样写:

public class App {

   private Car car;

   public App(Car car) {
       this.car = car;
   }

   public static void main(String[] args) {
       // TO DO
   } 
}

现在创建 App 不一定会创建一个 Car,它们耦合程度较低。

现在,我在这里非常严谨。我可能一直在我的代码中使用您的示例。我只是指出了一个缺点。这并不总是坏的,也不总是好的。


0

请注意,“meaning”和“意义”之间有区别:

public class App {

   private Car car = new Car();

   public static void main(String[] args) {
       // TO DO
   } 
}

并且

public class App {

   private Car car;
   public App(){
     car = new Car();
   } 
}

如果new Car()在第一次失败,那么你肯定会在调试时度过愉快的时光。如果需要,第二个选项更易读和可调试。如果您将字段视为类蓝图的组成部分,则在声明中初始化它们就没有多大意义。 由于您在此处拥有main,因此这可能是您的入口点,但对于其他类,如果将它们视为对象的蓝图,则构造函数的概念就有很多意义:
public class App{
  private Car car;
  public Car getCar(){
    return car;
  }
  public void setCar(Car car){
    this.car = car;
  }
  public App(Car car){
    this.car = car;
  }
}

这个,我想,是面向对象类最常见的结构。


1
嗯,我认为你的例子还需要一些改进;你试图在静态上下文中访问实例方法。 - G. Bach
哦,太尴尬了。已经修复 ;) - Jainathan Leung

0
执行此代码的完美方式是在主方法中创建一个类型为App的对象,该对象将调用Car类的构造函数。因此,代码应该像这样编写。
公共类应用程序 {
    private Car car;

    public static void main(String[] args)
    {
      App app=new App(); //
      app.car.  //Followed by the method or the member variable that you would like to
                //access

    } 

}


我从未见过 main 函数中的代码看起来像那样。我特别指的是直接设置自己的字段。我不建议这种做法。这会让某人创建一个 App 并在以后出现 NPE。如果你将它传递到构造函数中,你可以更快地失败。 - Daniel Kaplan

0
使用一个Init()方法进行所有初始化。
公共类App {
private Car car;

public App() {
    this.car = null;
}

public void Init() {
    this.car = new Car();
}

public void Shutdown() {
    this.car = null;
}

public static void main(String[] args) {
    App app = new App();
    app.Init();
    app.Shutdown();
    app = null;
} 

}


延迟初始化通常更昂贵。适用于昂贵构造函数的初始化(建立数据库连接),但我不建议将其作为标准。 - Sednus

0

private Car car = new Car();

我认为这是完全可以的。不进行该操作的几个原因如下:

  1. Car.<init>需要参数,这些参数仅在App.init中可用。
  2. App有许多字段,其他字段需要在App.<init>中初始化,为了保持一致性,您希望将它们全部放在一起。

无论如何,请不要执行以下操作:

private Car car = null;

因为每个Java开发人员都知道实例字段被初始化为null


0
除了tieTYT写的内容外,值得考虑的是,如果你在构造函数中实例化所有成员,则会使代码更易读。通过阅读构造函数,您可以了解有关该类型新对象的所有必要信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接