内部类详解(Oracle官方)

时间:2019-07-08 12:17:53   收藏:0   阅读:101

嵌套类(Nested Classes)

Java允许将一个类定义在另一个类内部。这样的类称为嵌套类,我们统称内部类,Oracle官方给出了不同的名字,如下

1 class OuterClass {
2     ...
3     class NestedClass {
4         ...
5     }
6 }

术语:嵌套类分为两类:静态和非静态。

静 态:静态嵌套类

非静态:内部类

1 class OuterClass {
2     ...
3     static class StaticNestedClass {
4         ...
5     }
6     class InnerClass {
7         ...
8     }
9 }

嵌套类作为外部类的一个成员存在。非静态嵌套类(内部类)拥有外部类的成员的权限,即使他们被定义为私有。相反,静态嵌套类没有外部封闭类其他成员的权限。作为外部类的一个成员,嵌套类可以被private, public, protected, or package private修饰。外部类只可以被 public or package private修饰。

为什么使用嵌套类(Why Use Nested Classes?)

  如果一个类只对另外一个类有用,逻辑上他被嵌入这个类。内嵌“帮助类”可以使包更合理化。

  考虑两个顶级类,A和B,B需要访问A的可能声明为私有的成员。通过把类B隐藏在类A中,B可以访问A的私有成员。此外B也可以从外界隐藏。

  嵌套小类在顶级类中,可以使代码更接近使用的地方。

静态嵌套类(Static Nested Classes)

就像类方法和变量一样,静态嵌套类和外部类关联一起。像静态方法一样,静态内部类无法直接访问定义在外部类中实例变量和实例方法,只能通过对象引用。

静态嵌套类通过如下方式使用

OuterClass.StaticNestedClass

创建内部嵌套类的实例:

1 OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

内部类(Inner Classes)

 和实例方法、实例变量一样,一个内部类和一个外部类实例想关联,可以直接访问外部类对象所有方法和变量。因为内部类和一个实例相关联,所以它不能定义任何静态成员。内部类的实例对象,存在于外部类实例之中。

 内部定义静态变量会报错:The field y cannot be declared static in a non-static inner type, unless initialized with a constant expression。只能定义静态常量

技术分享图片

 

1 class OuterClass {
2     ...
3     class InnerClass {
4         ...
5     }
6 }

实例化内部类的格式

1 OuterClass outerObject = new OutterClass();
2 OuterClass.InnerClass innerObject = outerObject.new InnerClass();

内部类分为两种:局部内部类匿名内部类

遮蔽(Shadowing)

在特定范围内(例如一个内部类或者一个方法定义),类型的声明(例如成员变量或者参数名称)和外部类拥有同样的名字,内部类会遮盖外部类。不能直接通过名字引用遮盖类型。

 1 public class ShadowTest {
 2 
 3     public int x = 0;
 4 
 5     class FirstLevel {
 6 
 7         public int x = 1;
 8 
 9         void methodInFirstLevel(int x) {
10             System.out.println("x = " + x);
11             System.out.println("this.x = " + this.x);
12             System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
13         }
14     }
15 
16     public static void main(String... args) {
17         ShadowTest st = new ShadowTest();
18         ShadowTest.FirstLevel fl = st.new FirstLevel();
19         fl.methodInFirstLevel(23);
20     }
21 }

输出如下:

x = 23
this.x = 1
ShadowTest.this.x = 0

在内部类中访问外部类的成员变量,需要通过以下方式(OuterClass.this.variable)

1 ShadowTest.this.x

序列化(Serialization)

强烈不建议序列化内部类,包括局部内部类和匿名内部类。

 

1. 内部类的例子(Inner Class Example)

◆ 局部类和匿名类(Local and Anonymous Classes)

局部类声明在方法体中,匿名类是声明在方法体中的非命名类。

◆ 修饰符(Modifiers)

privatepublicprotected

2. 局部内部类(Local Classes)

局部内部类定义在由一组0或多个声明的块中,作用范围也是块。典型的如,定义在方法中的内部类。

块(Blocks)

 1 class BlockDemo {
 2      public static void main(String[] args) {
 3           boolean condition = true;
 4           if (condition) { // begin block 1
 5                System.out.println("Condition is true.");
 6           } // end block one
 7           else { // begin block 2
 8                System.out.println("Condition is false.");
 9           } // end block 2
10      }
11 }

局部类只能被abstract和final修饰

技术分享图片

◆ 声明局部内部类

 1 public class LocalClassExample {
 2     static String regularExpression = "[^0-9]";
 3 
 4     public static void validatePhoneNumber(String phoneNumber1, String phoneNumber2) {
 5 
 6         final int numberLength = 10;
 7 
 8         // Valid in JDK 8 and later:
 9         // int numberLength = 10;
10 
11         class PhoneNumber {
12 
13             String formattedPhoneNumber = null;
14 
15             PhoneNumber(String phoneNumber) {
16                 // numberLength = 7;
17                 String currentNumber = phoneNumber.replaceAll(regularExpression, "");
18                 if (currentNumber.length() == numberLength)
19                     formattedPhoneNumber = currentNumber;
20                 else
21                     formattedPhoneNumber = null;
22             }
23 
24             public String getNumber() {
25                 return formattedPhoneNumber;
26             }
27 
28             // Valid in JDK 8 and later:
29             public void printOriginalNumbers() {
30                 System.out.println("Original numbers are " + phoneNumber1 + " and " + phoneNumber2);
31             }
32         }
33 
34         PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
35         PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
36 
37         // Valid in JDK 8 and later:
38         myNumber1.printOriginalNumbers();
39 
40         if (myNumber1.getNumber() == null)
41             System.out.println("First number is invalid");
42         else
43             System.out.println("First number is " + myNumber1.getNumber());
44         if (myNumber2.getNumber() == null)
45             System.out.println("Second number is invalid");
46         else
47             System.out.println("Second number is " + myNumber2.getNumber());
48 
49     }
50 
51     public static void main(String... args) {
52         validatePhoneNumber("123-456-7890", "456-7890");
53     }
54 }

打印如下

First number is 1234567890
Second number is invalid

◆ 访问外部类的成员

局部类可以访问外部类的变量,也可以访问方法的局部变量,且只能访问声明为final的局部变量。实际上局部类访问的是变量副本。

Java8之后,声明在方法中的局部类,可访问方法的参数。

局部类中变量对外部相同名字的变量产生遮盖作用。

局部类和内部类相同,都不能定义任何静态成员(变量和方法)。静态方法中的局部类只能访问外部类的静态成员。静态资源都是属于类的,静态方法可直接调用,所以局部类只能方位静态成员。

3. 匿名内部类(Anonymous Classes)

匿名内部类可以使代码更简洁,允许同时声明和实例化一个类。除了没有名字之外,其他都和局部类一样。如果你只使用一次局部类,请用匿名内部类。

◆ 声明匿名内部类(Declaring Anonymous Classes)

 1 public class HelloWorldAnonymousClasses {
 2   
 3     interface HelloWorld {
 4         public void greet();
 5         public void greetSomeone(String someone);
 6     }
 7   
 8     public void sayHello() {
 9         
10         class EnglishGreeting implements HelloWorld {
11             String name = "world";
12             public void greet() {
13                 greetSomeone("world");
14             }
15             public void greetSomeone(String someone) {
16                 name = someone;
17                 System.out.println("Hello " + name);
18             }
19         }
20       
21         HelloWorld englishGreeting = new EnglishGreeting();
22         
23         HelloWorld frenchGreeting = new HelloWorld() {
24             String name = "tout le monde";
25             public void greet() {
26                 greetSomeone("tout le monde");
27             }
28             public void greetSomeone(String someone) {
29                 name = someone;
30                 System.out.println("Salut " + name);
31             }
32         };
33         
34         HelloWorld spanishGreeting = new HelloWorld() {
35             String name = "mundo";
36             public void greet() {
37                 greetSomeone("mundo");
38             }
39             public void greetSomeone(String someone) {
40                 name = someone;
41                 System.out.println("Hola, " + name);
42             }
43         };
44         englishGreeting.greet();
45         frenchGreeting.greetSomeone("Fred");
46         spanishGreeting.greet();
47     }
48 
49     public static void main(String... args) {
50         HelloWorldAnonymousClasses myApp =
51             new HelloWorldAnonymousClasses();
52         myApp.sayHello();
53     }            
54 }

◆ 匿名内部类语法(Syntax of Anonymous Classes)

 1 HelloWorld frenchGreeting = new HelloWorld() {
 2     String name = "tout le monde";
 3     public void greet() {
 4         greetSomeone("tout le monde");
 5     }
 6     public void greetSomeone(String someone) {
 7         name = someone;
 8         System.out.println("Salut " + name);
 9     }
10 };

因为匿名类就是一个表达式,它必须是语句的一部分

◆ 访问局部变量,声明和访问匿名类的成员

和局部类一样,匿名类可以捕获变量;他们拥有相同的访问封闭范围局部变量的权限:

匿名类和局部类对各自的成员拥有相同的限制:

以下是可以在匿名类中声明的:

但是,您不能在匿名类中声明构造函数。

◆ 匿名内部类的例子

 1 import javafx.event.ActionEvent;
 2 import javafx.event.EventHandler;
 3 import javafx.scene.Scene;
 4 import javafx.scene.control.Button;
 5 import javafx.scene.layout.StackPane;
 6 import javafx.stage.Stage;
 7  
 8 public class HelloWorld extends Application {
 9     public static void main(String[] args) {
10         launch(args);
11     }
12     
13     @Override
14     public void start(Stage primaryStage) {
15         primaryStage.setTitle("Hello World!");
16         Button btn = new Button();
17         btn.setText("Say ‘Hello World‘");
18         btn.setOnAction(new EventHandler<ActionEvent>() {
19  
20             @Override
21             public void handle(ActionEvent event) {
22                 System.out.println("Hello World!");
23             }
24         });
25         
26         StackPane root = new StackPane();
27         root.getChildren().add(btn);
28         primaryStage.setScene(new Scene(root, 300, 250));
29         primaryStage.show();
30     }
31 }

匿名类是实现包含两个或多个方法的接口的理想方式。

 1 mport javafx.application.Application;
 2 import javafx.event.ActionEvent;
 3 import javafx.event.EventHandler;
 4 import javafx.geometry.Insets;
 5 import javafx.scene.Group;
 6 import javafx.scene.Scene;
 7 import javafx.scene.control.*;
 8 import javafx.scene.layout.GridPane;
 9 import javafx.scene.layout.HBox;
10 import javafx.stage.Stage;
11 
12 public class CustomTextFieldSample extends Application {
13     
14     final static Label label = new Label();
15  
16     @Override
17     public void start(Stage stage) {
18         Group root = new Group();
19         Scene scene = new Scene(root, 300, 150);
20         stage.setScene(scene);
21         stage.setTitle("Text Field Sample");
22  
23         GridPane grid = new GridPane();
24         grid.setPadding(new Insets(10, 10, 10, 10));
25         grid.setVgap(5);
26         grid.setHgap(5);
27  
28         scene.setRoot(grid);
29         final Label dollar = new Label("$");
30         GridPane.setConstraints(dollar, 0, 0);
31         grid.getChildren().add(dollar);
32         
33         final TextField sum = new TextField() {
34             @Override
35             public void replaceText(int start, int end, String text) {
36                 if (!text.matches("[a-z, A-Z]")) {
37                     super.replaceText(start, end, text);                     
38                 }
39                 label.setText("Enter a numeric value");
40             }
41  
42             @Override
43             public void replaceSelection(String text) {
44                 if (!text.matches("[a-z, A-Z]")) {
45                     super.replaceSelection(text);
46                 }
47             }
48         };
49  
50         sum.setPromptText("Enter the total");
51         sum.setPrefColumnCount(10);
52         GridPane.setConstraints(sum, 1, 0);
53         grid.getChildren().add(sum);
54         
55         Button submit = new Button("Submit");
56         GridPane.setConstraints(submit, 2, 0);
57         grid.getChildren().add(submit);
58         
59         submit.setOnAction(new EventHandler<ActionEvent>() {
60             @Override
61             public void handle(ActionEvent e) {
62                 label.setText(null);
63             }
64         });
65         
66         GridPane.setConstraints(label, 0, 1);
67         GridPane.setColumnSpan(label, 3);
68         grid.getChildren().add(label);
69         
70         scene.setRoot(grid);
71         stage.show();
72     }
73  
74     public static void main(String[] args) {
75         launch(args);
76     }
77 }

 

4. 拉姆达表达式(Lambda Expressions)

方法引用(Method Referens)

5. 如何选择嵌套类、局部类、匿名内部类、Lambda表达式

原文:https://www.cnblogs.com/blouson/p/NestedClasses.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!