5.7 内部类

Posted weststar

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5.7 内部类相关的知识,希望对你有一定的参考价值。

一、内部类的定义

在某些情况下,我们将一个类放在另一个类的内部定义,这个定义在其他类内部的类就叫做内部类(嵌套类),包含内部类的外部类也被称为外部类(宿主类)。
内部类的主要作用:
1、内部类提供更好的封装,可以把内部类隐藏在外部类之中,不允许同一个包中的其他类访问该类。
2、内部类成员可以访问外部类的私有数据,因为内部类被当成外部类的成员,同一个类中的成员之间可以相互访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
3、匿名内部类适合用于创建那些只需要使用一次的类
内部类组要注意的两点:
1、内部类的修饰符比外部类多三个:protected public static.
2、非静态内部类不能用于静态成员变量。

二、非静态内部类

2.1 非静态内部类的定义

内部类一定放在一个类的类体部分(花括号内)定义,内部类的定义语法:

public class OuterClass
{
    //此处可以定义内部类
}

大部分的时候,内部类作为成员内部类被定义,而不是局部内部类。成员内部类是一种与成员变量、方法、构造器、初始化块相似的类成员,所以可以使用任意访问控制符;局部内部类和匿名内部类不是类成员。
注意:外部类的上一次程序单元是包,所以他只有两个作用域:同一个包和任何位置。对应两种控制符不写、public
内部类上一级程序单元式外部类,因此具有四个作用域:同一个类、同一个包中、父子类和任何位置。因此可以有四种访问权限。
下面在Cow类里定义一个CowLeg非静态内部类,并在CoeLeg类的实例中直接访问Cow类的private访问权限的实例变量。

public class Cow
{
    private double weight;
    //外部类的两个构造器
    public Cow(){}
    public Cow(double weight)
    {
        this.weight=weight;
    }

    //定义一个非静态内部类
    public class CowLeg
    {
        //非静态内部类的两个实例变量
        private double length;
        private String color;
        public CowLeg(){}
        public CowLeg(double length,String color)
        {
            this.length=length;
            this.color=color;
        }

        //非静态内部类的实例方法
        **public void info()
        {
            System.out.println("当前牛腿颜色是:"+color+",长:"+length);
            //直接访问外部类的private修饰的成员变量
            System.out.println("本腿所在奶牛重:"+weight);//①
        }**
    }
    public void test()
    {
        var c1=new CowLeg(1.2,"黑白相间");
        c1.info();
    }
    public static void main(String[] args)
    {
        var cow=new Cow(378.9);
        cow.test();
    }
}
---------- 运行Java捕获输出窗 ----------
当前牛腿颜色是:黑白相间,长:1.2
本腿所在奶牛重:378.9

输出完成 (耗时 0 秒) - 正常终止

编译上面的程序,将看到在文件所在路径生成两个class文件,一个是Cow.class,另一个是 Cow$CowLeg.class,前者是一个外部类,后者是内部类CowLeg的class文件,即成员变量(包括静态内部类和非静态内部类)的class文件总是以这样的形式:OutClass$InnerClass.class

上面①号粗体部分就是在内部类CowLeg中访问外部类private实例成员。因为这是在非静态内部类中,保存了它所寄生的外部类的应用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例中)。下图显示上面程序在运行时在内存中的示意图:
技术图片

2.2 非静态内部类的成员访问外部类的实例成员

当非静态内部成员的方法访问某个变量时,系统查找顺序:方法体存在的局部变量————>该变量所在方法体的内部内中查找————>外部类。若都不存在则出现编译错误:提示找不到该变量。如果外部类、内部类、局部成员变量名相同,则可以通过this,外部类名.this作为区分。如下程序所示:

public class DiscernVariable
{
    private String prop="外部类实例成员";
    private class InClass
    {
        private String prop="内部类实例成员变量";
        public void info()
        {
            var prop="方法体内局部变量";
            System.out.println("外部类实例成员变量值:"+DiscernVariable.this.prop);
            System.out.println("内部类类实例成员变量值:"+this.prop);
            System.out.println("局部变量值:"+prop);
        }
    }
    public void test()
    {
        var in=new InClass();
        in.info();
    }
    public void test1()
    {
        System.out.println(prop);
    }
    public static void main(String[] args)
    {
        DiscernVariable d=new DiscernVariable();
        d.test();
        d.test1();
    }
}
---------- 运行Java捕获输出窗 ----------
外部类实例成员变量值:外部类实例成员
内部类类实例成员变量值:内部类实例成员变量
局部变量值:方法体内局部变量
外部类实例成员
输出完成 (耗时 0 秒) - 正常终止

2.3 外部类不能直接访问内部类实例成员

如果需要在外部类访问内部类实例成员,则必须显示创建非静态内部类对象来调用访问其实例成员。

class Outer 
{
    private int outProp=9;
    class Inner
    {
        private int inProp=5;
        public void accessOuterProp()
        {
            //非静态内部类直接访问外部类的private实例成员
            System.out.println("外部类的outProp值:"+outProp);
        }
    }
    public void accessInnerProp()
    {
        //外部类不能直接访问非静态内部类实例成员变量
        //System.out.println("内部类的inProp值:"+inProp);
        //Outer.java:16: 错误: 找不到符号
        //外部类访问内部类的实例成员变量必须显示创建内部类对象
        System.out.println("内部类的inProp值:"+new Inner().inProp);
    }
    public static void main(String[] args)
    {
        var out=new Outer();
        out.accessInnerProp();
    }
}
---------- 运行Java捕获输出窗 ----------
内部类的inProp值:5

输出完成 (耗时 0 秒) - 正常终止

2.4不允许在外部静态成员中访问内部类非静态成员

静态成员不能访问非静态成员,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。

class StaticTest 
{
    //定义一个非静态内部类:是一个空类
    private class In
    {
    }
    //外部静态方法
    public static void main(String[] args)
    {
        new In();
    }
}
StaticTest.java:10: 错误: 无法从静态上下文中引用非静态 变量 this

2.5 Java不允许在非静态内部类定义非静态成员

class  InnerNoStatic
{
    private class InnerClass
    {
        static int num=5;
    }
}
---------- 编译Java ----------
InnerNoStatic.java:5: 错误: 内部类InnerNoStatic.InnerClass中的静态声明非法
        static int num=5;
                   ^
  修饰符 'static' 仅允许在常量变量声明中使用
1 个错误

输出完成 (耗时 1 秒) - 正常终止

非静态内部类不能有静态成员,包括静态方法、静态初始化块、静态成员变量。

三、静态内部类

如果使用static修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类称为类内部类,有的地方也称为静态内部类。

3.1 静态内部类不能访问外部类实例成员

注:静态内部类可以包含静态成员变量,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。

public class StaticInnerClassTest  
{
    //外部类的实例成员
    private int prop1=5;
    //外部类的类成员变量
    private static int prop2=9;

    //定义静态内部类
    static class StaticInnerClass
    {
        //静态内部类可以包含静态成员和非静态成员
        private static int age;
        private String name;
        public void accessOuterProp()
        {
            //静态内部类无法访问外部类的实例变量
            //System.out.println(prop1);//错误: 无法从静态上下文中引用非静态 变量 prop1
            System.out.println(prop2);
        }
    }
}

静态内部类是外部类相关的,而不是外部类对象相关的。也就是说,静态内部类对象不是寄生在外部类的实例中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的引用,没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到外部类对象的引用,这将引起编译错误。

3.2 外部类访问静态内部类成员

静态内部类是外部类的一个静态成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象。
外部类不能直接访问静态内部类成员,但可以使用静态内部类的类名作为调用者来访问静态内部类成员,也可以使用静态内部类的对象作为调用者来访问静态内部类的实例成员。

public class AccessStaticInnerClass 
{
    //定义静态内部类
    static class StaticInnerClass
    {
        private static String prop1="静态内部类类成员";
        private String prop2="静态内部类实例成员";
    }
    public void accessInnerProp()
    {
        //通过类名访问静态成员
        //System.out.println(prop1);//错误: 找不到符号
        System.out.println(StaticInnerClass.prop1);
        //通过对象来访问外部类的实例成员
        System.out.println(new StaticInnerClass().prop2);
    }
    public static void main(String[] args)
    {
        var a=new AccessStaticInnerClass();
        a.accessInnerProp();
    }
}
---------- 运行Java捕获输出窗 ----------
静态内部类类成员
静态内部类实例成员

输出完成 (耗时 0 秒) - 正常终止

3.3 接口中内部类

Java允许在接口中定义内部类,接口中定义的内部类默认使用public static修饰符,也就是说接口内部类只能是静态内部类。

非静态内部类和静态内部类小结:

非静态内部类
技术图片
静态内部类:
技术图片

以上是关于5.7 内部类的主要内容,如果未能解决你的问题,请参考以下文章

错误:这个片段内部类应该是静态的 [ValidFragment]

片段 - 全局视图变量与本地和内部类侦听器和内存泄漏

为啥片段类应该是公开的?

ForegroundService没有从片段开始?

在内部片段类中使用ListView

自定义无内存泄漏的Handler内部类