【问题标题】:What is the use of non-static block in java? [duplicate]java中的非静态块有什么用? [复制]
【发布时间】:2012-08-08 17:29:22
【问题描述】:

可能重复:
How is an instance initializer different from a constructor?

当所有需要的工作都可以在构造函数中完成时,为什么我们还需要 Java 中的非静态块呢?

编辑:非静态块每次在构造函数之前运行的普通类怎么样?

【问题讨论】:

    标签: java


    【解决方案1】:

    除了@Bohemian 的回答。

    如果您有多个构造函数,则初始化程序块避免重复。

    public class A {
         final Map<String, String> map = new HashMap<String, String>(); {
            // put things in map.
         }
         final int number; {
            int number;
            try {
                number = throwsAnException();
            } catch (Exception e) {
                number = 5;
            }
            this.number = number;
         }
    
         public A() { /* constructor 1 */ }
         public A(int i) { /* constructor 2 */ }
    }
    

    非静态块每次在构造函数之前运行的普通类怎么样?

    从技术上讲,顺序是

    • 总是首先调用超级构造函数
    • 所有初始化程序块按出现顺序排列。
    • 构造函数代码

    实际上,所有这些代码都在每个构造函数的字节码中,因此在运行时构造函数之前或之后没有任何内容。


    在初始化顺序出现更多混乱之前

    public class Main extends SuperClass {
        {
            System.out.println("Initialiser block before constructor");
        }
    
        Main() {
            System.out.println("Main constructor");
        }
    
        {
            System.out.println("Initialiser block after constructor");
    
        }
    
        public static void main(String... args) {
            new Main() {{
                System.out.println("Anonymous initalizer block");
            }};
        }
    }
    
    class SuperClass {
        SuperClass() {
            System.out.println("SuperClass constructor");
        }
    }
    

    打印

    SuperClass constructor
    Initialiser block before constructor
    Initialiser block after constructor
    Main constructor
    Anonymous initalizer block
    

    【讨论】:

    • 将前括号与number的声明放在同一行看起来有点混乱。
    • @veer 这是一个样式问题。它表明该块与该字段特别相关。
    • 它可以通过重复每个构造函数中的所有代码或找到一种使用方法this() 问题是如果添加构造函数,请记住在之前和之后添加位。不需要这样做要简单得多。
    • @Ashwyn,好点子。,正在修复。
    • @PeterLawrey 匿名类的实例块在构造函数之后按出现顺序执行。也许将您的答案与类中实例块的执行顺序区分开来。 (ps 恭喜获得 100K,并感谢您的 java 连接器字符回答制作时事通讯!)。
    【解决方案2】:

    您可以将它与匿名类一起使用:

    new MyClass() {
        {
             // do something extra on construction (after the constructor executes)
        }
    }
    

    我发现这对于初始化“查找”地图(即固定内容)特别有用:

    Map<String, String> map = new HashMap<String, String>() {
        {
            put("foo", "bar");
            put("one", "two");
            // etc
        }
    };
    

    仅供参考,这有时(不太好)称为“双括号初始化”,而实际上它只是使用initializer block

    虽然这样一个匿名类在技术上是一个子类,但在将使用这种技术与创建unmodifiable 映射的更传统技术进行比较时,它的美妙之处就显现出来了:

    比较这个简单的单行,它将数据和赋值放在一起:

    private static final Map<String, String> map = Collections.unmodifiableMap(
        new HashMap<String, String>() {{
            put("foo", "bar");
            put("one", "two");
            // etc
        }});
    

    由于final 只允许一个分配,因此必须创建一个单独的对象:

    private static final Map<String, String> map;
    
    static {
        Map<String, String> tempMap = new HashMap<String, String>();
        tempMap.put("foo", "bar");
        tempMap.put("one", "two");
        // etc
        map = Collections.unmodifiableMap(tempMap);
    }
    

    还要注意,在混乱版本中,两个语句不必相邻,因此不可修改映射的内容可能变得不那么明显。

    【讨论】:

    • 请注意,像这样初始化容器(称为"double brace initialization")会不恰当地创建一个子类——类似于当您只想实现Runnable 时认为子类化Thread 是不恰当的。滥用继承权时要小心;提到的一个例子是它如何破坏equals 的常见实现。您可能应该考虑使用collection factory method
    • @veer 不错的链接/阅读。我认为最好在 equals 中检查类(和/或任何支持的超类)的 CTT,不过..
    • @veer 我绝对不认为这是“滥用继承”;它只是在线填充一个对象。我经常在我的代码中使用它,没有任何问题。请参阅编辑后的答案。
    • @Bohemian 您可以使用工厂方法进一步简化代码,例如Collections.unmodifiableMap(newHashMap&lt;String, String&gt;("foo", "bar", "one", "two", ...))。除此之外,我认为您应该查看Liskov Substitution Principle
    • @Bohemian 我犯了一个错误,Maps.&lt;String, String&gt;newHashMap*。但无论如何,它可以返回一个Map&lt;String, String&gt;(尽管考虑到方法合同,这将毫无意义)。我并不是想成为一个书呆子,但在以这种非常规的方式使用继承时应该小心。
    猜你喜欢
    • 1970-01-01
    • 2013-07-29
    • 2014-02-19
    • 2016-10-21
    • 1970-01-01
    • 2013-09-18
    • 2012-05-26
    • 2015-07-29
    • 2013-02-21
    相关资源
    最近更新 更多