Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我应该在声明中实例化实例变量还是在构造函数中实例化实例变量?_Java_Constructor_Instance Variables - Fatal编程技术网

Java 我应该在声明中实例化实例变量还是在构造函数中实例化实例变量?

Java 我应该在声明中实例化实例变量还是在构造函数中实例化实例变量?,java,constructor,instance-variables,Java,Constructor,Instance Variables,这两种方法都有好处吗 例1: class A { B b = new B(); } 例2: class A { B b; A() { b = new B(); } } 这两种方法都是可以接受的。请注意,在后一种情况下,如果存在另一个构造函数,则可能无法初始化b=new b()。将构造函数外的初始值设定项代码视为公共构造函数,然后执行代码 我认为示例2更可取。我认为最好的做法是在构造函数外部声明并在构造函数中初始化。第二个例子是延迟初始化。第

这两种方法都有好处吗

例1:

class A {
    B b = new B();
}
例2:

class A {
    B b;

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

这两种方法都是可以接受的。请注意,在后一种情况下,如果存在另一个构造函数,则可能无法初始化
b=new b()。将构造函数外的初始值设定项代码视为公共构造函数,然后执行代码

我认为示例2更可取。我认为最好的做法是在构造函数外部声明并在构造函数中初始化。

第二个例子是延迟初始化。第一个是更简单的初始化,它们本质上是相同的。

我认为这几乎只是一个品味问题,只要初始化简单,不需要任何逻辑

如果不使用初始值设定项块,则构造函数方法更为脆弱,因为如果稍后添加第二个构造函数并忘记在那里初始化b,则只有在使用最后一个构造函数时,才会得到null b

有关Java初始化的更多详细信息(以及有关初始化程序块和其他不知名的初始化功能的说明),请参阅。

  • 没有区别-实例变量初始化实际上是由编译器放入构造函数中的
  • 第一种变体更具可读性
  • 不能对第一个变量进行异常处理
  • 另外还有初始化块,编译器也会将其放入构造函数中:

    {
        a = new A();
    }
    
检查

发件人:

然而,字段声明不是任何方法的一部分,因此它们不能像语句那样执行。相反,Java编译器会自动生成实例字段初始化代码,并将其放入类的构造函数中。初始化代码按照它在源代码中出现的顺序插入到构造函数中,这意味着字段初始值设定项可以使用在它之前声明的字段的初始值

此外,您可能希望延迟初始化字段。如果初始化字段是一项昂贵的操作,您可以在需要时立即对其进行初始化:

ExpensiveObject o;

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

最后(正如比尔所指出的),为了进行依赖关系管理,最好避免在类中的任何地方使用
new
操作符。相反,使用更可取-即让其他人(另一个类/框架)实例化并将依赖项注入到您的类中。

另一个选项是使用

这将从
A
的构造函数中删除创建
B
对象的责任。从长远来看,这将使您的代码更易于测试和维护。其思想是减少两个类
A
B
之间的耦合。这给您带来的一个好处是,您现在可以将任何扩展
B
(或实现
B
,如果它是一个接口)的对象传递给
A
的构造函数,并且它将工作。一个缺点是您放弃了
B
对象的封装,因此它向
A
构造函数的调用方公开。你必须考虑利益是否值得权衡,但在很多情况下是这样的。

< P>我个人的“规则”(几乎从未被打破)是:

  • 在开始时声明所有变量 街区
  • 将所有变量设为最终变量,除非 不可能
  • 每行声明一个变量
  • 不要在以下情况下初始化变量: 宣布
  • 只初始化一个文件中的某个内容 构造函数,当它需要来自 执行以下操作的构造函数: 初始化
所以我会有这样的代码:

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;
        }
    }
}
这样,我总是100%确定在哪里查找变量声明(在块的开头)和它们的赋值(只要在声明之后有意义)。这可能会更有效,因为您从不使用未使用的值初始化变量(例如,declare和init vars,然后在这些变量中有一半需要有值之前抛出异常)。您也不会最终执行无意义的初始化(比如int i=0;然后,在使用“i”之前,do i=5;)

我非常重视一致性,所以遵循这个“规则”是我一直在做的事情,它使使用代码变得更加容易,因为您不必四处寻找来找到东西


您的里程数可能会有所不同。

示例2灵活性较差。如果您添加了另一个构造函数,则需要记住在该构造函数中实例化字段。只需直接实例化字段,或在getter中的某个位置引入延迟加载

如果实例化需要的不仅仅是一个简单的
new
,请使用初始值设定项块。无论使用哪种构造函数,都将运行该初始化项。例如

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.
        }
    }

    // ...

}
使用依赖注入或延迟初始化总是可取的,正如在其他答案中已经详细解释的那样

当您不想要或不能使用这些模式时,对于原始数据类型,我可以考虑以下三个令人信服的原因,即为什么最好在构造函数外部初始化类属性:

  • 避免重复=如果您有多个构造函数,或者当您需要添加更多构造函数时,您不必在所有构造函数体中重复初始化
  • 改进的可读性=您可以很容易地从类外判断哪些变量必须初始化
  • 减少的代码行数=对于在声明中完成的每个初始化,构造函数中将少一行

  • 今天我被一种有趣的方式烧伤了:

    class MyClass extends FooClass {
        String a = null;
    
        public MyClass() {
            super();     // Superclass calls init();
        }
    
        @Override
        protected void init() {
            super.init();
            if (something)
                a = getStringYadaYada();
        }
    }
    
    看到错误了吗?原来是在调用超类构造函数之后调用了
    a=null
    初始值设定项。由于超类构造函数调用init(),因此
    a的初始化
    
    class MyClass extends FooClass {
        String a = null;
    
        public MyClass() {
            super();     // Superclass calls init();
        }
    
        @Override
        protected void init() {
            super.init();
            if (something)
                a = getStringYadaYada();
        }
    }
    
    class A {
        int b;
    
        // secondary ctor
        A(String b) {
             this(Integer.valueOf(b));
        }
    
        // primary ctor
        A(int b) {
             this.b = b;
        }
    }
    
        class MyClass extends FooClass {
        String a = null;
    
        public MyClass() {
            super();     // Superclass calls init();
        }
    
        @Override
        protected void init() {
            super.init();
            if (something)
                a = getStringYadaYada();
        }
    }
    
    String a = null;
    
    class MyClass extends FooClass 
    {
        String a;
        {
            if( a==null ) a="my custom default value";
        }
        ...