内部类

Posted velscode

tags:

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

非静态内部类

定义内部类非常简单,只要把一个类放在另一个类内部定义即可。此处“类内部”包括类中的任何位置,甚至方法中也可以方法里定义的内部类杯称为局部内部类。内部类定义语法如下

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

大部分时候,内部类都被作为成员内部类定义,而不是局部内部类。成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员。局部内部类和匿名内部类则不是类成员。

成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类是静态内部类,没有使用static修饰的成员内部类是非静态内部类。

因为内部类作为其外部类的成员,所以可以使用任意访问控制符。


下面程序在Cow类里定义了一个CowLeg非静态内部类,并在CowLeg类的实例方法中之间访问Cow的private访问权限的实例变量

public class Cow {
    private double weight;

    public Cow(double weight) {
        this.weight = weight;
    }
    
    //定义一个非静态内部类
    private class CowLeg {
        private String color;

        public CowLeg(String color) {
            this.color = color;
        }
        
        //非静态内部类的实例方法
        public void info() {
            System.out.println("牛腿颜色:"+color);
            //直接访问外部类private修饰的成员变量
            System.out.println("本牛腿所在牛重:"+weight);
        }
    }
    
    public void test() {
        CowLeg cl = new CowLeg("黑色");
        cl.info();
    }
    
    public static void main(String[] args) {
        Cow cow = new Cow(50);
        cow.test();
    }
}

上例子中,Cow是外部类,CowLeg是内部类。

编译后,目录中出现了Cow.classCow$CowLeg.class两个class文件。

非静态内部类里可以直接访问外部累的private成员,这是因为在非静态内部类对象里,保存了一个它所寄生的外部类的对象的引用(当调用非静态内部类时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)

技术图片

静态内部类

如果用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象,称为静态内部类。

匿名内部类

匿名内部类适合创建只需要使用一次的类。创建匿名内部类会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。

定义匿名内部类的格式如下:

new 实现接口() |  父类构造器(实参列表)
{
    //匿名内部类的类体部分
}

从上面的定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类或实现一个接口。

还有如下两条规则

  • 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许定义为抽象类。
  • 匿名内部类不能定义构造器。因为没有类名,所以没有构造器,但是可以定义初始化块。
interface Product {
    public String getName();
}

public class Test {
    public void test(Product p) {
        System.out.println("购买了一个:"+p.getName());
    }
        
    public static void main(String[] args) {
        Test t = new Test();
        
        t.test(new Product() {
            public String getName() {
                return "显卡";
            }
        });
    }
}

上面的程序中Test类定义了一个test方法,该方法需要一个Product对象作为参数,但Product只是一个接口,无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入。

正如上面程序中看到的,定义匿名内部类无须class关键字,而是在定义匿名内部类时直接生成该匿名内部类的对象。

由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。

上面代码等价于

public static void main(String[] args) {
    Test t = new Test();
    
    class product implements Product {
        public String getName() {
            return "显卡";
        }
    }

    t.test(new product());
}

当通过实现接口来创建匿名内部类时,匿名内部类也不能显示创建构造器,故new接口名后的括号里不能传入参数值。

但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参列表。

//定义一个抽象类设备
abstract class Device {
    private String name;
    
    public abstract double getPrice();
    
    public Device(){}
    public Device(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
public class Test {
    public void test(Device d) {
        System.out.println(d.getName() + "价格是" + d.getPrice());
    }
    public static void main(String[] args) {
        Test t = new Test();
        //调用有参数的构造器
        t.test( new Device("示波器") {
            public double getPrice() {
                return 10000;
            }
        });
        
        //调用无参数的构造器
        Device d = new Device() {
            {
                System.out.println("匿名内部类的初始化块");
            }
            //实现抽象方法
            public double getPrice() {
                return 50;
            }
            //重写父类的实例方法
            public String getName(){
                return "键盘";
            }
        };
        
        t.test(d);
    }
}

输出结果

示波器价格是10000.0
匿名内部类的初始化块
键盘价格是50.0

当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法。

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

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

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

ForegroundService没有从片段开始?

在内部片段类中使用ListView

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

底部导航 如何从片段内部更改片段