Should I instantiate instance variables on declaration or in the constructor?

前端 未结 15 1861

Is there any advantage for either approach?

Example 1:

class A {
    B b = new B();
}

Example 2:

class A {
    B b;         


        
相关标签:
15条回答
  • 2020-11-22 06:52

    my personal "rule" (hardly ever broken) is to:

    • declare all variables at the start of a block
    • make all variables final unless they cannot be
    • declare one variable per line
    • never initialize a variable where declared
    • only initialize something in a constructor when it needs data from the constructor to do the initialization

    So I would have code like:

    public class X
    {
        public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
        private static final int A;
        private final int b;
        private int c;
    
        static 
        { 
            A = 42; 
        }
    
        {
            b = 7;
        }
    
        public X(final int val)
        {
            c = val;
        }
    
        public void foo(final boolean f)
        {
            final int d;
            final int e;
    
            d = 7;
    
            // I will eat my own eyes before using ?: - personal taste.
            if(f)
            {
                e = 1;
            }
            else
            {
                e = 2;
            }
        }
    }
    

    This way I am always 100% certain where to look for variables declarations (at the start of a block), and their assignments (as soon as it makes sense after the declaration). This winds up potentially being more efficient as well since you never initialize a variable with a value that is not used (for example declare and init vars and then throw an exception before half of those vars needed to have a value). You also do not wind up doing pointless initialization (like int i = 0; and then later on, before "i" is used, do i = 5;.

    I value consistency very much, so following this "rule" is something I do all the time, and it makes it much easier to work with the code since you don't have to hunt around to find things.

    Your mileage may vary.

    0 讨论(0)
  • 2020-11-22 06:54

    I got burned in an interesting way today:

    class MyClass extends FooClass {
        String a = null;
    
        public MyClass() {
            super();     // Superclass calls init();
        }
    
        @Override
        protected void init() {
            super.init();
            if (something)
                a = getStringYadaYada();
        }
    }
    

    See the mistake? It turns out that the a = null initializer gets called after the superclass constructor is called. Since the superclass constructor calls init(), the initialization of a is followed by the a = null initialization.

    0 讨论(0)
  • 2020-11-22 06:56
    • There is no difference - the instance variable initialization is actually put in the constructor(s) by the compiler.
    • The first variant is more readable.
    • You can't have exception handling with the first variant.
    • There is additionally the initialization block, which is as well put in the constructor(s) by the compiler:

      {
          a = new A();
      }
      

    Check Sun's explanation and advice

    From this tutorial:

    Field declarations, however, are not part of any method, so they cannot be executed as statements are. Instead, the Java compiler generates instance-field initialization code automatically and puts it in the constructor or constructors for the class. The initialization code is inserted into a constructor in the order it appears in the source code, which means that a field initializer can use the initial values of fields declared before it.

    Additionally, you might want to lazily initialize your field. In cases when initializing a field is an expensive operation, you may initialize it as soon as it is needed:

    ExpensiveObject o;
    
    public ExpensiveObject getExpensiveObject() {
        if (o == null) {
            o = new ExpensiveObject();
        }
        return o;
    }
    

    And ultimately (as pointed out by Bill), for the sake of dependency management, it is better to avoid using the new operator anywhere within your class. Instead, using Dependency Injection is preferable - i.e. letting someone else (another class/framework) instantiate and inject the dependencies in your class.

    0 讨论(0)
  • 2020-11-22 06:56

    Example 2 is less flexible. If you add another constructor, you need to remember to instantiate the field in that constructor as well. Just instantiate the field directly, or introduce lazy loading somewhere in a getter.

    If instantiation requires more than just a simple new, use an initializer block. This will be run regardless of the constructor used. E.g.

    public class A {
        private Properties properties;
    
        {
            try {
                properties = new Properties();
                properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
            } catch (IOException e) {
                throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
            }
        }
    
        // ...
    
    }
    
    0 讨论(0)
  • 2020-11-22 07:00

    Using either dependency injection or lazy initialization is always preferable, as already explained thoroughly in other answers.

    When you don't want or can't use those patterns, and for primitive data types, there are three compelling reasons that I can think of why it's preferable to initialize the class attributes outside the constructor:

    1. avoided repetition = if you have more than one constructor, or when you will need to add more, you won't have to repeat the initialization over and over in all the constructors bodies;
    2. improved readability = you can easily tell with a glance which variables will have to be initialized from outside the class;
    3. reduced lines of code = for every initialization done at the declaration there will be a line less in the constructor.
    0 讨论(0)
  • 2020-11-22 07:01

    Both of the methods are acceptable. Note that in the latter case b=new B() may not get initialized if there is another constructor present. Think of initializer code outside constructor as a common constructor and the code is executed.

    0 讨论(0)
提交回复
热议问题