zoukankan      html  css  js  c++  java
  • Java 嵌套类基础详解

    1. 什么是嵌套类?

    在 Java 语言中允许在另外一个类中定义一个类,这样的类被称为嵌套类。包含嵌套类的类称为外部类(outer class),也可以叫做封闭类,在本文中统一叫做外部类。

    内部类的语法:

    class OuterClass {
        // code
        class NestedClass {
          // code
        }
    }
    

    嵌套类分为两类:

    • 静态嵌套类( Static nested classes ):使用static声明,一般称为嵌套类( Nested Classes )。
    • 非静态嵌套类( Non-static Nested Classes ):非static声明,一般称为内部类( Inner Classes )。
    class OuterClass {
      ...
      static class StaticNestedClass {
        ...
      }
      class InnerClass {
        ...
      }
    }
    

    嵌套类是它的外部类的成员,非静态嵌套类(内部类)可以访问外部类的其他成员,即使该成员是私有的。而静态嵌套类只能访问外部类的静态成员。

    嵌套类作为外部类的一个成员,可以被声明为:private,public,protected或者包范围(注意:外部类只能被声明为public或者包范围)。


    2. 为什么要使用嵌套类?

    使用嵌套类的主要优点有以下几个方面:

    • 嵌套类可以访问外部类的所有数据成员和方法,即使它是私有的。
    • 提高可读性和可维护性:因为如果一个类只对另外一个类可用,那么将它们放在一起,这更便于理解和维护。
    • 提高封装性:给定两个类A和B,如果需要访问A类中的私有成员,则可以将B类封装在A类中,这样不仅可以使得B类可以访问A类中的私有成员,并且可以在外部隐藏B类本身。
    • 减少代码的编写量。

    3. 嵌套类的类型

    前文已经介绍了嵌套类分为静态嵌套类和非静态嵌套类,而非静态嵌套类又称为内部类(内部类是嵌套类的子集)。

    静态嵌套类可以使用外部类的名称来访问它。例如:

    OuterClass.StaticNestedClass
    

    如果要为静态嵌套类创建对象,则使用以下语法:

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

    非静态嵌套类(内部类)又可以分为以下三种:

    • 成员内部类(Member inner class)
    • 匿名内部类(Anonymous inner class)
    • 局部内部类(Local inner class)

    Nested Classes

    类型 描述
    成员内部类 在类中和方法外部创建的类
    匿名内部类 为实现接口或扩展类而创建的类,它的名称由编译器决定
    局部内部类 在方法内部创建的类
    静态嵌套类 在类中创建的静态类
    嵌套接口 在类中或接口中创建的接口

    4. 静态嵌套类

    静态嵌套类其实就是在顶级类中封装的一个顶级类,它的行为和顶级类一样,它被封装在顶级类中其实就是为了方便打包。

    • 静态嵌套类是外部类的静态成员,它可以直接使用外部类名.静态嵌套类名访问自身。
    • 它可以访问外部类的静态成员和静态私有成员。
    • 与其他类一样,静态嵌套类不能访问非静态成员。

    静态嵌套类的语法如下:

    class OuterClass{  
        // 外部类的静态数据成员
        static int data = 30;  
        // 外部类中的静态嵌套类
        static class StaticNestedClass {  
            // 静态嵌套类中的实例方法
            void getData() {System.out.println("data is "+data);}  
        }
    
        public static void main(String args[]){  
            // 实例化静态嵌套类(创建对象)
            OuterClass.StaticNestedClass obj = new OuterClass.StaticNestedClass();  
            obj.getData();  
        }  
    }  
    

    输出结果:

    data is 30
    

    在上面的例子中,你需要先创建一个静态嵌套类的实例,因为你需要访问它的实例方法getData()。但是你并不需要实例化外部类,因为静态嵌套类相对于外部类来说它是静态的,可以直接使用外部类名.静态嵌套类名访问。

    由编译器生成的静态嵌套类:

    import java.io.PrintStream;  
        static class OuterClass$Inner  
        {  
        OuterClass$StaticNestedClass(){}  
            void getData(){  
                System.out.println((new StringBuilder()).append("data is ")
                .append(OuterClass.data).toString());  
        }    
    }  
    

    从上面的代码可以看出,静态嵌套类实际上是直接通过OuterClass.data来访问外部类的静态成员的。

    使用静态嵌套类的静态方法示例:

    如果静态嵌套类中有静态成员,则不需要实例化静态嵌套类即可直接访问。

    class OuterClass2{  
        static int data = 10;  
        static class StaticNestedClass{  
            // 静态嵌套类的静态方法
            static void getData() {System.out.println("data is "+data);}  
        }  
    
        public static void main(String args[]){  
            // 不需要创建实例可以直接访问
            OuterClass2.StaticNestedClass.getData();
        }  
    }  
    

    输出结果:

    data is 10
    

    5. 非静态嵌套类

    非静态嵌套类也就是内部类,它有以下几个特点:

    • 实例化内部类必须先实例化一个外部类。
    • 内部类实例与外部类实例相关联,所有不能在内部类中定义任何静态成员。
    • 内部类是非静态的。

    下面看详细介绍↓

    5.1 成员内部类

    在外部类中并且在外部类的方法外创建的非静态嵌套类,称为成员内部类。说白了成员内部类就是外部类的一个非静态成员而已。

    语法如下:

    class OuterClass{  
        //code  
        class MemberInnerClass{  
            //code  
        }  
    }  
    

    成员内部类的示例:

    在下面的示例中,我们在成员内部类中创建了getData()getTest()这两个实例方法,并且这两个方法正在访问外部类中的私有数据成员和成员方法。

    class OuterClass {  
        private int data = 30;  
        public void test() {System.out.println("我是外部类成员方法");}
        class MemberInnerClass {  
            // 访问外部类的私有数据成员
            void getData() {System.out.println("data is "+data);}  
            // 访问外部类的成员方法
            void getTest() {OuterClass.this.test();}
        }  
    
        public static void main(String args[]) {  
            // 首先必须实例化外部类
            OuterClass obj = new OuterClass();  
            // 接着通过外部类对象来创建内部类对象
            OuterClass.MemberInnerClass in = obj.new MemberInnerClass();  
            in.getData();
            in.getTest();  
        }  
    }  
    

    输出结果:

    data is 30
    我是外部类成员方法
    

    访问外部类的成员方法时,可以直接通过外部类名.this.外部类成员方法()来调用,或者直接使用外部类成员方法()来调用。

    成员内部类的工作方式:

    Java 编译器创建了两个.class文件,其中一个是成员内部类OuterClass$MemberInnerClass.class,成员内部类文件名格式为外部类名$成员内部类名

    如果你想创建成员内部类的实例,你必须先实例化一个外部类,接着,通过外部类的实例来创建成员内部类的实例。因此成员内部类的实例必须存在于一个外部类的实例中。另外,由于内部类的实例与外部类的实例相关联,因此它不能定义任何静态成员。

    由编译器生成的成员内部类代码:

    在下面的例子中,编译器创建了一个名为OuterClass$MemberInnerClass的内部类文件,成员内部类具有外部类的引用,这就是为什么它可以访问包括私有在内的所有外部类成员。

    import java.io.PrintStream;  
        class OuterClass$MemberInnerClass {  
            final OuterClass this$0;  
            OuterClass$MemberInnerClass() {
                super();  
                this$0 = OuterClass.this;// 引用了外部类
            }  
            void getData() {  
                System.out.println((new StringBuilder()).append("data is ")  
                .append(OuterClass.access$000(OuterClass.this)).toString());  
            }  
    }  
    

    5.2 局部内部类

    局部内部类(Local inner class)通常定义在一个块中。所以通常你会在一个方法块中找到局部内部类。

    正如局部变量那样,局部内部类的作用域受到方法的限制。它可以访问外部类的所有成员,和它所在的局部方法中所有局部变量。

    如果你想调用局部内部类中的方法,你必须先在局部方法中实例化局部内部类。

    请看下面示例:

    public class localInner {  
        private int data = 30;// 实例变量
    
        // 实例方法
        void display() {  
            // 局部内部类
            class Local{  
                void msg() {System.out.println(data);}  
            }  
            // 访问局部内部类中的方法
            Local l = new Local();  
            l.msg();  
        }  
    
        public static void main(String args[]){  
            localInner obj = new localInner();  
            obj.display();  
        }  
    }
    

    输出结果

    30
    

    由编译器生成的局部内部类代码:

    此时编译器创建了一个名为localInner$Local的类,它具有外部类的引用

    import java.io.PrintStream;  
    
    class localInner$Local {  
        final localInner this$0;  // 自动生成的局部变量
        localInner$Local() {
            super();  
            this$0 = localInner.this;  
        }  
    
        void msg()  
        {  
            System.out.println(localInner.access$000(localInner.this));  
        }  
    }  
    

    局部内部类的一些规则:

    • 无法从方法外部调用局部内部类
    • 局部内部类不能被声明为private, public, protected
    • JDK1.7之前局部内部类不能访问非final的局部变量,但是在JDK1.8及之后是可以访问非final的局部变量的。

    局部内部类和局部变量的示例:

    class localInner2 {  
    
        private int data = 30;//实例变量
    
        void display() {  
            int value = 50;//在 Jdk1.7 之前局部变量必须被声明为'final'
    
            class Local{  
                void msg() {System.out.println("value: "+value);}  
            }  
    
            Local l = new Local();  
            l.msg();  
        }  
    
        public static void main(String args[]){  
            localInner2 obj = new localInner2();  
            obj.display();  
        }  
    }  
    

    输出结果

    value: 50
    

    5.3 匿名内部类

    在 Java 中没有命名的内部类称为匿名内部类,当我们需要重写类或接口中的方法时,都会使用到它。匿名内部类在使用的时候将同时声明和实例化。

    匿名类可以通过以下两种方式进行创建:

    • 类(可能是抽象类或者其他类)
    • 接口

    使用类创建匿名内部类:

    abstract class Person {  
    	abstract void eat();  
    }  
    
    public class TestAnonymousInner {   
    	public static void main(String[] args){
    		// 使用匿名内部类实例化抽象类Person
    		Person p = new Person() {  
    			void eat() {System.out.println("nice fruits");}  
    		};
    
    		p.eat();
    	}  
    }  
    

    输出结果

    nice fruits
    

    上面代码中有以下一段:

    Person p = new Person() {  
        void eat() {System.out.println("nice fruits");}  
    };
    

    它主要有两个作用:

    • 创建一个匿名内部类,它的名称由编译器决定,并给出了Person类中eat()方法的实现。
    • 创建匿名类的对象,并使用了类型为Person的引用变量p来引用。

    由编译器生成的匿名内部类代码:

    import java.io.PrintStream;  
    static class TestAnonymousInner$1 extends Person  
    {  
       TestAnonymousInner$1(){}  
       void eat() {  
            System.out.println("nice fruits");  
        }  
    }  
    

    从以上代码,我们可以看出,编译器实际上将匿名内部类命名为TestAnonymousInner$1,并继承了抽象类Person

    使用接口创建匿名内部类:

    interface Eatable{  
        void eat();  
    }  
    
    class TestAnnonymousInner1{  
        public static void main(String args[]){  
            // 创建匿名内部类来实现接口Eatable
            Eatable e = new Eatable() {  
                public void eat(){System.out.println("nice fruits");}  
            };  
    
            e.eat();  
        }  
    }  
    

    输出结果

    nice fruits
    

    上面代码有以下一段:

    Eatable e = new Eatable() {  
        public void eat(){System.out.println("nice fruits");}  
    };  
    

    它主要有两个作用:

    • 创建一个匿名类,它的名称由编译器决定,并给出了eat()方法的实现。
    • 创建匿名类的对象,并使用了类型为Person的引用变量p来引用。

    由编译器生成的匿名内部类代码:

    import java.io.PrintStream;  
    static class TestAnonymousInner1$1 implements Eatable  
    {  
        TestAnonymousInner1$1(){}  
            void eat(){System.out.println("nice fruits");}  
    }  
    

    从以上代码我们可以看出,编译器实际上创建了一个名为TestAnonymousInner1$1的类,并使用该类实现了Eatable接口。


    6. 嵌套接口

    前文主要介绍了嵌套类相关的知识,Java 中嵌套接口与嵌套类类似,下面我们来了解一下关于嵌套接口方面的知识。

    嵌套接口(Nested Interface)指的是在一个类或接口中创建的接口。嵌套接口用于将相关的接口进行分组以便于维护。嵌套接口必须由外部接口或外部类引用,它不能直接访问。

    需要记住的关于嵌套接口的要点:

    这里给出了 Java 程序员应该记住的关于嵌套接口的要点:

    • 在接口中声明的嵌套接口,默认修饰符为public,也必须为public,但在类中声明嵌套接口则可以使用任何访问修饰符。
    • 嵌套接口都被隐世声明为静态的。

    在接口中声明嵌套接口:

    interface interface_name{  
        ...  
        interface nested_interface_name{  
        ...  
        }  
    }   
    

    在类中声明嵌套接口:

    class class_name{  
        ...  
        interface nested_interface_name{  
        ...  
        }  
    }   
    

    在接口中声明嵌套接口的示例:

    在下面这个例子中,我们将学习如何声明嵌套接口以及如何使用它。

    interface Showable{  
    	void show();  
    	// 声明嵌套接口
    	interface Message{  
    		void msg();  
    	}  
    }  
    
    class TestNestedInterface1 implements Showable.Message{  
    	// 实现嵌套接口Message中的msg()方法
    	public void msg() {System.out.println("Hello nested interface");}  
    
    	public static void main(String args[]){  
    		// 实例化TestNestedInterface1类,向上转型为Showable.Message接口类型
    		Showable.Message message = new TestNestedInterface1();  
    		message.msg();  
    	}  
    }  
    

    输出结果:

    Hello nested interface
    

    正如上面的示例中所看到的,我们不能直接访问嵌套接口Message,但是我们可以通过外部接口Showable来访问它。

    就好比房间中的衣柜,我们不能直接拿到衣柜中的衣服,我们必须先进入房间才行。

    由编译器生成的嵌套接口Message的代码:

    public static interface Showable$Message  
    {  
      public abstract void msg();  
    }  
    

    从上面代码可以看出,嵌套接口实际被声明为静态的了。

    在类中声明嵌套接口的示例:

    class A{  
    	interface Message{  
    		void msg();  
    	}  
    }  
    
    class TestNestedInterface2 implements A.Message{  
    	// 实现Message接口中的msg()方法
    	public void msg() {System.out.println("Hello nested interface");}  
    
    	public static void main(String args[]){  
    		// 实例化TestNestedInterface2类,并向上转型为A.Message类型
    		A.Message message = new TestNestedInterface2();
    		message.msg();  
    	}  
    }  
    

    输出结果:

    Hello nested interface
    

    最后思考一下,我们可以在接口中定义一个类吗?

    答案是可以的。当我们在接口中定义类时,Java 编译器会自动创建一个静态嵌套类。

    interface M{  
      class A{}  
    }  
    
  • 相关阅读:
    十进制转任意进制
    整型与字符串转换
    最长递增子序列(动态规划)
    睡眠理发师问题
    区间最值问题(RMQ)
    分解质因子
    数字统计
    After all, tomorrow is another day.
    【USB电平】电脑USB电平
    【有源滤波】滤波基础知识
  • 原文地址:https://www.cnblogs.com/nwgdk/p/8661442.html
Copyright © 2011-2022 走看看