Java中的嵌套类详解

嵌套类的定义

  • 重点是嵌套,一个类定义在别的类内部
//文件名为 B.java
public class B {
    //A类在B类中定义,嵌套
    class A {
        
    }
}
//文件名为D.java
//类C与D是并列的,无从属关系
class C {
    
}

public class D {
}
  • 嵌套类:Nested classes

----静态嵌套类:Static nested classes,即 类前面有static 修饰符;
----非静态嵌套类:Non-static nested classes,又名 内部类,Inner classes:

  • 普通内部类(成员内部类);
  • 局部内部类;
  • 匿名内部类;

嵌套类官方文档点击这里

静态嵌套类

public class Outer1 {
    String name;

    //静态嵌套类
    static class Inner1 {

    }
}

成员内部类

public class Outer2 {
    String name;

    //普通内部类/成员内部类
    public class Inner2 {

    }
}

局部内部类

public class Outer3 {
    String name;
    
    //局部内部类
    public void f1() {
        class Inner3 {
            String name;
        }
    }
}

匿名内部类

public class Outer4 {
    String name;

    //匿名内部类
    public void f1() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello!");
            }
        }).start();
    }
}

这个匿名内部类没有名字,它实现了Runnable接口,只调用一次;

为什么需要嵌套类

  • 不同的访问权限要求,更细粒度的访问控制;
    在这里插入图片描述
  • 简洁,避免过多的类定义;
  • 缺点:设计过于复杂,较难学习和使用;

静态嵌套类详解

  • 层级和包围类的成员变量/方法一样;
public class Outer1 {
    String name;

    //静态嵌套类
    static class Inner1{
        String name;
    }
}
---测试类
public class MyTest1 {
    public static void main(String[] args) {
        Outer1.Inner1 obj = new Outer1.Inner1();
        //第三方需要通过外部包围类才可以访问到静态嵌套类
    }
}
  • 静态内部类分析:
----测试类
public class Outer2Test {
    public static void main(String[] args) {
        //第三方类访问静态嵌套类
        Outer2.Inner1 obj1 = new Outer2.Inner1();
        obj1.getInnFlied1();

        Outer2.Inner1 obj2 = new Outer2.Inner1();
        System.out.println(obj1 == obj2);

        System.out.println("===================");
        Outer2 obj3 = new Outer2();
        obj3.outFun2();
        /*false
        ===================
        false
        inner 333
        inner hello
        inner static 444*/
    }
}
----静态内部类
public class Outer2 {
    private static String outStaticFiled2 = "outer static 222";
    private String outFiled = "outer 111";

    public void outFun2() {
        Inner1 obj1 = new Inner1();
        Inner1 obj2 = new Inner1();
        System.out.println(obj1 == obj2);//false
        System.out.println(obj1.getInnFlied1());//inner 333
        System.out.println(Inner1.getInnerStaticFiled2());//inner static 444
    }

    public String getOutFiled() {
        return this.outFiled;
    }

    //静态嵌套类
//    private static class Inner1
//    static class Inner1
//    protected static class Inner1
    public static class Inner1 {
        static String innerStaticFiled2 = "inner static 444";
        //静态嵌套类可以定义静态的、非静态的成员
        private String innFlied1 = "inner 333";

        public static String getInnerStaticFiled2() {
            hello();
            //Outer2.hello();
            return innerStaticFiled2;
        }

        private static void hello() {
            System.out.println("inner hello");
        }

        public String getInnFlied1() {
            return innFlied1;
        }

        public void innFun1() {
            //静态嵌套类可以直接访问包围类的静态成员
            System.out.println(outStaticFiled2);
            //System.out.println(outFiled);--error

            //静态嵌套类可以通过对象访问包围类的非静态成员
            Outer2 obj = new Outer2();
            System.out.println(obj.getOutFiled());
        }
    }
}
  • 需要加修饰符static;
  • 可以定义静态成员和非静态成员;
  • 不能直接访问包围类的非静态成员,可直接访问包围类的静态成员;
  • 可以通过包围类的对象进行访问非静态成员;
  • 外界可以通过静态嵌套类名访问其静态成员,通过对象访问其非静态成员;
  • 外界需要通过包围类才可以访问到静态嵌套类,并创建其对象,不需要外部包围类的实例;
  • 和正常的类没有什么区别,纯粹只是为了打包的便利性存在;

成员内部类详解

  • 非static的类,定义在某个类的成员变量的位置;
  • 定义后,在类里面均可以使用;
public class Outer3 {
    public Bird obj = new Bird();
    String name = "aaaaaa";

    public Bird getBird(){
        return this.obj;
    }

    public void f1() {
        obj.fly();
        System.out.println(obj.getClass().getName());
        this.name = "bbbbbbb";
        obj.fly();
    }

    public class Bird extends Animal implements Flyable {
        //常量OK
        public static final int a = 3;

        public void eat() {
            System.out.println("I can eat.");
        }

        public void fly() {
            System.out.println("I can fly." + name);
        }
    }
}
----测试类:
public class Outer3Test {
    public static void main(String[] args) {
        Outer3 foo1 = new Outer3();
        foo1.f1();

        Outer3.Bird foo2 = foo1.new Bird();
        foo2.fly();
        System.out.println(foo2 == foo1.getBird());
        /*I can fly.aaaaaa
        org.westos.demo1.Outer3$Bird
        I can fly.bbbbbbb
        I can fly.bbbbbbb
        false*/
    }
}

在这里插入图片描述

如果在测试类里面直接写上Outer3.Bird foo3 = new Outer3.Bird();会报错,不允许没有关联的单独的普通内部类对象,也就是说,你必须通过外围类的实例才能new出普通内部类的对象;

  • 编译后名称:外部类名+$+内部类名;
  • 可以继承其他类,也可以实现其它接口;
  • 可以用private/package private(不写)/protected/public 控制外界访问;
  • 非静态的类,不能包含静态变量/方法,除了常量;
  • 和外部包围类的实例相关,一个普通内部类的实例肯定是在一个外部包围类的实例中,且可以访问外部包围类的所有成员;
  • 在第三方类中,需要首先创建外部包围类的实例,才能创建普通内部类的实例,不允许单独的普通内部类对象存在!

局部内部类详解

  • 定义在代码块中的非静态的类,如方法、for循环、if语句等;
  • 定义后,即可创建对象使用;
  • 只能存活在这个代码块中,代码快结束,外界无法使用该类;
public class MyTest1 {
    String name = "abc";

    //局部内部类
    public static void f2() {
        final String name = "def";
        class Inner2 {
            public void f2() {
                //只能访问常量
                System.out.println(name);
                //System.out.println(MyTest1.this.name);
                //静态方法只能访问静态变量
            }
        }
        Inner2 obj1 = new Inner2();
        obj1.f2();
        System.out.println(obj1.getClass().getName());
    }

    //局部内部类
    public void f1() {
        String name = "def";

        class Inner2 {
            final static int a = 1;
            String name = "ghi";

            //static int b=2;
            //不允许定义静态变量
            public void f2() {
                System.out.println(name);
                System.out.println(MyTest1.this.name);
            }

            //不允许定义静态方法
//            public static void f3(){
//
//            }
        }
        Inner2 obj1 = new Inner2();
        obj1.f2();
        System.out.println(obj1.getClass().getName());
    }
}

在这里插入图片描述

  • 编译后名称:外部类名+$+序号+内部类名;
  • 可以继承其他类或者实现其它接口;
  • 非静态的类,不能包含静态变量(成员+方法),除了常量;
  • 可以访问外部包围类的成员;
  • 如果定义在静态方法中,只能访问包围类的静态成员;
  • 局部内部类不能是一个接口,即接口不能定义在代码块中;
  • 可以重用;

匿名内部类详解

  • 没有类名的内部类,必须继承一个父类/实现一个父接口;
  • 在实例化以后,迅速转型为父类/父接口;
  • 这种类型的对象,只能new一个对象,之后以对象名操作;
  • 可以在普通语句和成员变量赋值时使用内部类;
public class MyTest {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello!");
            }
        };
        
        new Thread(r).start();
    }
}
public class Outer1 {
    private String name = "abc";

    //public static void f1()
    public void f1() {
        String name = "def";
        Runnable r = new Runnable() {
            //匿名内部类不能定义静态变量,除非是常量
            public final static int a = 5;
            //public static int b = 3;
            String name = "ghi";

            @Override
            public void run() {
                System.out.println("hello" + name);
                //拼接外部方法的临时变量
            }
            //静态方法不能在匿名内部类里面定义
            /*public static void f2(){

            }*/
        };
        new Thread(r).start();
        System.out.println(r.getClass().getName());

        Runnable r2 = new Runnable() {
            @Override
            public void run() {
            	//访问到最外层的变量
                System.out.println("hello" + Outer1.this.name);
            }
        };
        new Thread(r2).start();
        System.out.println(r2.getClass().getName());
    }
}

两个匿名内部类不是同一个类,编译结果为:

在这里插入图片描述

  • 可以继承、改写、补充父类/父接口的方法;
  • 内部不可以新定义静态成员(变量+方法),常量除外;
final static int a = 5;
  • 没有正式类名的内部类:
编译器产生内部名字:类名+$+数字编号
  • 可以访问外部包围类的成员变量和方法(包括private);
  • 如果定义在静态方法中,也只能访问外部包围类的静态成员;
  • 没有类名,外部包围类和其他类也无法访问到匿名内部类;
  • 匿名内部类很简洁;

嵌套类的对比

  • Oracle官方文档这样描述对比:

匿名内部类:

  • 应用它,如果需要定义额外的变量和方法;
    局部内部类:
  • 在一个方法内,需要创建一个新的类型,并重复使用;
    普通内部类:
  • he局部内部类相似,在一个类中定义,可重复使用,可以访问外部类的成员,但不需要访问外部类方法的形参和内部变量;
    静态内部类:
  • 在一个类中定义,可重复使用,并需要访问外部类的静态成员;

在这里插入图片描述
在这里插入图片描述

  • 外部访问和修饰符的关系:
  • 普通内部类和静态嵌套类可以被外部访问;
  • 外部访问普通内部类和静态嵌套类,和普通类之间访问规则一样;

变量遮蔽

嵌套类变量和外部包围类的变量重名:

  • 就近优先原则;
  • 优先级高的变量会遮蔽优先级低的变量;
  • 外部包围类.this.变量名,可以访问到外部包围类的成员变量;
  • 静态嵌套类不能访问非静态变量;
  • Java 7及以前,匿名内部类和局部内部类只能访问外部包围类的final成员变量;
  • Java 8之后,匿名内部类可以访问外部包围类的final成员变量和事实意义上的final变量(一个变量定值后,再也没有改过值);
public class shadowTest {
    private int x = 0;

    public static void main(String[] args) {
        shadowTest st = new shadowTest();
        FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(20);
        /*x=20
        this.x=1
        ShadowText.this.x=0*/
    }

    class FirstLevel {
        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x=" + x);
            System.out.println("this.x=" + this.x);
            System.out.println("ShadowText.this.x=" + shadowTest.this.x);
        }
    }
}
public class shadowTest2 {
    private int x = 0;

    public void f1() {
        int x = 20;
        //局部内部类无法访问!
        class FirstLevel {
            public int x = 1;

            void methodInFirstLevel(int x) {
                System.out.println("x=" + x);
                System.out.println("this.x=" + this.x);
                System.out.println("ShadowTest2.this.x=" + shadowTest2.this.x);
            }
        }

        FirstLevel obj = new FirstLevel();
        obj.methodInFirstLevel(10);
    }
}
public class shadowTest3 {
    private int x = 0;

    public void f1() {
        int x = 20;
        //可以访问
        class FirstLevel {
            public int x = 20;

            void methodInFirstLevel() {
                System.out.println("x=" + x);
                System.out.println("ShadowTest3.this.x=" + shadowTest3.this.x);
            }
        }

        FirstLevel obj = new FirstLevel();
        obj.methodInFirstLevel();//x=20
    }

    public static void main(String[] args) {
        shadowTest3 st = new shadowTest3();
        st.f1();
        /*x=20
        ShadowTest3.this.x=0*/
    }
}

嵌套类的应用

  • 匿名内部类:

无需类名,用过即焚,使用广泛,且方法只有一个,代码短;
Android中常用的匿名内部类:
在这里插入图片描述

  • 局部内部类:

定义在方法体内,只能在当前方法内使用,代码短,使用较少;
介于匿名内部类和普通内部类之间;
继承某一个类或接口,重新定义方法,并当做返回值在外部使用;

在这里插入图片描述

  • 普通内部类

广泛使用在具有母子结构的类,内部类对象和外围类保持联系;
如Map和Map.Entry,ZipFile和ZipFile.ZipEntryIterator等;

在这里插入图片描述

  • 静态嵌套类

—和普通类一致,只是"碰巧"声明在一个外围类的内部;
—和外围类没有太多的联系,可以脱离外围类对象存在,也可以访问外围类静态成员;
—如果不需要访问外围类的非静态成员,尽量将普通内部类变更为静态嵌套类;

  • 节省了内部类和外围类的联系开销,使得外围类更容易被垃圾回收器回收;

在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值