Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/314.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/python-2.7/5.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 - Fatal编程技术网

Java中的匿名代码块

Java中的匿名代码块,java,Java,在Java中匿名代码块有什么实际用途吗 public static void main(String[] args) { // in { // out } } 请注意,这与命名块无关,即 name: { if ( /* something */ ) break name; } 它们限制了变量的范围 public void foo() { { int i = 10; } System

在Java中匿名代码块有什么实际用途吗

public static void main(String[] args) {
    // in
    {
        // out
    }
}
请注意,这与命名块无关,即

name: { 
     if ( /* something */ ) 
         break name;
}

它们限制了变量的范围

public void foo()
{
    {
        int i = 10;
    }
    System.out.println(i); // Won't compile.
}

然而,在实践中,如果您发现自己正在使用这样一个代码块,那么这可能是您想要将该代码块重构为一个方法的标志。

您可以将其用作匿名内部类的构造函数

像这样:

这样可以初始化对象,因为空闲块是在对象构造期间执行的

它不仅限于匿名内部类,也适用于常规类

public class SomeClass {
    public List data;{
        data = new ArrayList();
        data.add(1);
        data.add(1);
        data.add(1);
    }
}

匿名块可用于限制变量的范围,也可用于

比较

Set<String> validCodes = new HashSet<String>();
validCodes.add("XZ13s");
validCodes.add("AB21/X");
validCodes.add("YYLEX");
validCodes.add("AR2D");
Set validCodes=new HashSet();
有效代码。添加(“XZ13s”);
有效代码添加(“AB21/X”);
有效代码。添加(“YYLEX”);
有效代码。添加(“AR2D”);

Set validCodes=new HashSet(){{
添加(“XZ13s”);
添加(“AB21/X”);
添加(“YYLEX”);
添加(“AR5E”);
}};

我认为你和/或其他答案混淆了两种不同的句法结构;即实例初始值设定项和块。(顺便说一下,“命名块”实际上是一个带标签的语句,其中语句恰好是一个块。)

实例初始值设定项用于类成员的语法级别;e、 g

public class Test {
    final int foo;

    {
         // Some complicated initialization sequence; e.g.
         int tmp;
         if (...) {
             ...
             tmp = ...
         } else {
             ...
             tmp = ...
         }
         foo = tmp;
    }
}
根据@dfa的示例,初始化器构造最常用于匿名类。另一个用例是对“final”属性进行复杂的初始化;e、 g.参见上面的示例。(但是,使用常规构造函数更常见。上面的模式更常用于静态初始值设定项。)

另一个构造是普通块,出现在代码块中,例如method;e、 g

public void test() {
    int i = 1;
    {
       int j = 2;
       ...
    }
    {
       int j = 3;
       ...
    }
}
块最常用作控制语句的一部分,用于对语句序列进行分组。但是当您在上面使用它们时,它们(只是)允许您限制声明的可见性;e、 g.
j

这通常表明您需要重构代码,但这并不总是明确的。例如,您有时会在用Java编写的解释器中看到这种情况。开关臂中的语句可以分解为单独的方法,但这可能会对解释器的“内部循环”造成显著的性能影响;e、 g

    switch (op) {
    case OP1: {
             int tmp = ...;
             // do something
             break;
         }
    case OP2: {
             int tmp = ...;
             // do something else
             break;
         }
    ...
    };

@David Seiler的答案是正确的,但我认为代码块非常有用,应该经常使用,不一定表示需要将其分解为方法。我发现它们对于构建Swing组件树特别有用,例如:

JPanel mainPanel = new JPanel(new BorderLayout());
{
    JLabel centerLabel = new JLabel();
    centerLabel.setText("Hello World");
    mainPanel.add(centerLabel, BorderLayout.CENTER);
}
{
    JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0,0));
    {
        JLabel label1 = new JLabel();
        label1.setText("Hello");
        southPanel.add(label1);
    }
    {
        JLabel label2 = new JLabel();
        label2.setText("World");
        southPanel.add(label2);
    }
    mainPanel.add(southPanel, BorderLayout.SOUTH);
}
代码块不仅尽可能严格地限制了变量的范围(这总是好的,尤其是在处理可变状态和非最终变量时),而且它们还以XML/HTML的方式演示了组件层次结构,使代码更易于阅读、编写和维护

我将每个组件实例化分解成一个方法的问题是

  • 即使是私有实例方法,该方法也只会被使用一次,并且会向更广泛的受众公开
  • 更难阅读,想象一个更复杂的组件树,你必须深入寻找你感兴趣的代码,然后松散的视觉上下文
  • 在这个Swing示例中,我发现,当复杂性确实超出了可管理性时,它表明是时候将树的一个分支分解成一个新类,而不是一堆小方法了。

    通常最好这样做。匿名代码块可以帮助实现这一点

    我发现这对于
    switch
    语句特别有用。考虑下面的示例,没有匿名代码块:

    public String manipulate(Mode mode) {
        switch(mode) {
        case FOO: 
            String result = foo();
            tweak(result);
            return result;
        case BAR: 
            String result = bar();  // Compiler error
            twiddle(result);
            return result;
        case BAZ: 
            String rsult = bar();   // Whoops, typo!
            twang(result);  // No compiler error
            return result;
        }
    }
    
    public String manipulate(Mode mode) {
        switch(mode) {
            case FOO: {
                String result = foo();
                tweak(result);
                return result;
            }
            case BAR: {
                String result = bar();  // No compiler error
                twiddle(result);
                return result;
            }
            case BAZ: {
                String rsult = bar();   // Whoops, typo!
                twang(result);  // Compiler error
                return result;
            }
        }
    }
    
    和匿名代码块:

    public String manipulate(Mode mode) {
        switch(mode) {
        case FOO: 
            String result = foo();
            tweak(result);
            return result;
        case BAR: 
            String result = bar();  // Compiler error
            twiddle(result);
            return result;
        case BAZ: 
            String rsult = bar();   // Whoops, typo!
            twang(result);  // No compiler error
            return result;
        }
    }
    
    public String manipulate(Mode mode) {
        switch(mode) {
            case FOO: {
                String result = foo();
                tweak(result);
                return result;
            }
            case BAR: {
                String result = bar();  // No compiler error
                twiddle(result);
                return result;
            }
            case BAZ: {
                String rsult = bar();   // Whoops, typo!
                twang(result);  // Compiler error
                return result;
            }
        }
    }
    
    我认为第二版更干净,更容易阅读。而且,它将开关中声明的变量的范围缩小到了声明它们的情况,根据我的经验,99%的时间都是您想要的


    但是请注意,它不会改变案例失败的行为-您仍然需要记住包含
    中断
    返回
    ,以防止它

    您可以使用块从父范围初始化最终变量。这是一种很好的方法,可以限制一些仅用于初始化单个变量的变量的范围

    public void test(final int x) {
        final ClassA a;
        final ClassB b;
        {
            final ClassC parmC = getC(x);
            a = parmC.getA();
            b = parmC.getB();
        }
        //... a and b are initialized
    }
    

    一般来说,最好将块移动到方法中,但是当需要返回多个变量并且您不想创建包装类时,这种语法对于一次性情况很好。

    实例初始值设定项块:

    class Test {
        // this line of code is executed whenever a new instance of Test is created
        { System.out.println("Instance created!"); }
    
        public static void main() {
            new Test(); // prints "Instance created!"
            new Test(); // prints "Instance created!"
        }
    }
    
    class Test {
        public static void main() {
            { int i = 20; }
            System.out.println(i); // error
        }
    }
    
    匿名初始值设定项块:

    class Test {
    
        class Main {
            public void method() {
                System.out.println("Test method");
            }
        }
    
        public static void main(String[] args) {
            new Test().new Main() {
                {
                    method(); // prints "Test method"
                }
            };
    
            {
                //=========================================================================
                // which means you can even create a List using double brace
                List<String> list = new ArrayList<>() {
                    {
                        add("el1");
                        add("el2");
                    }
                };
                System.out.println(list); // prints [el1, el2]
            }
    
            {
                //==========================================================================
                // you can even create your own methods for your anonymous class and use them
                List<String> list = new ArrayList<String>() {
                    private void myCustomMethod(String s1, String s2) {
                        add(s1);
                        add(s2);
                    }
    
                    {
                        myCustomMethod("el3", "el4");
                    }
                };
    
                System.out.println(list); // prints [el3, el4]
            }
        }
    }
    

    使用注释或根据代码结构和所选标识符描述任务,然后使用代码块在语言本身不强制的情况下创建层次关系。例如:

    public void sendAdminMessage(String msg) throws IOException {
        MessageService service; {
            String senderKey = properties.get("admin-message-server");
            service = MessageService.of(senderKey);
            if (!ms.available()) {
              throw new MessageServiceException("Not available: " + senderKey);
            }
        }
    
        /* workaround for issue 1298: Stop sending passwords. */ {
            final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
            Matcher m = p.matcher(msg);
            if (m.matches()) msg = m.group(1) + m.group(2);
        }
        ...
    }
    
    上面只是一些示例代码来解释这个概念。第一个块通过其前面的内容“记录”:该块用于初始化
    服务
    变量。第二个块由注释记录。在这两种情况下,块都为注释/变量声明提供了“作用域”:它们解释了特定流程的结束位置。这是一种更常见的样式的替代:

    public void sendAdminMessage(String msg) throws IOException {
        // START: initialize service
        String senderKey = properties.get("admin-message-server");
        MessageService service = MessageService.of(senderKey);
        if (!ms.available()) {
          throw new MessageServiceException("Not available: " + senderKey);
        }
        // END: initialize service
    
        // START: workaround for issue 1298: Stop sending passwords.
        final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
        Matcher m = p.matcher(msg);
        if (m.matches()) msg = m.group(1) + m.group(2);
        // END: workaround for issue 1298: Stop sending passwords.
    
        ...
    }
    
    尽管这些块更好:它们允许您使用编辑器工具更有效地导航(“转到块的末尾”),它们限定块内使用的局部变量的范围,以便它们无法逃逸,最重要的是,它们与包含的概念保持一致:作为java程序员,您已经很熟悉,使用包含的概念:对于块、if块、方法块:它们都是代码流中层次结构的表达式。出于文件原因而非技术原因对代码的遏制仍然是遏制。为什么要使用不同的机制?一致性是有用的。减少精神负担