内部类

内部类:将一个类的定义放在另一个类的定义内部。内部类机制可以把逻辑相关的类组织在一起,并控制位于内部的类的可视性。

内部类与组合是完全不同的概念。

内部类不仅是一种代码隐藏机制(将类置于其他类的内部),还能与外围类通信。

类似于外围类的成员的内部类。

2-1 链接到外部类

内部类可以访问其外围类的 所有字段和方法(所有成员)。创建一个普通内部类对象需要先创建其外围类的对象,然后使用该外围类对象取创建内部类对象。当某个外围类创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后就可使用这个引用来访问外围类对象的成员。

运行结果为:

2-2 使用 外围类名.this

如果需要在内部类中获取外围类对象的引用,可以使用 外围类名.this 来获取

运行结果为:

2-3 使用 外围类对象.new

要直接创建普通内部类对象,必须使用外围类的对象来创建该内部类对象。这也解决了内部类名字作用域的问题,即使用外围类对象只能创建定义在外围类中的内部类的对象。

因此,就不必声明(也不能声明)外围类的类名,如inner = dot.new DotNew.Inner();

在拥有外围类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外围类对象上。但是,如果你创建的是嵌套类(静态内部类),那就不需要先创建外围类对象。

2-4 外围类访问内部类

先看一下内部类访问外围类成员的方式:如果想要访问外围类的某个成员,直接使用外围类这个成员的名字就可以。

但是,如果外围类要访问内部类的成员的话就不能使用这种方式(即直接使用内部类成员的名字):

正确的方式应该先创建内部类的对象再通过此对象去访问:

内部类可以直接通过外围类的成员名去访问外围类成员,而外围类需要通过内部类的对象去访问内部类成员。造成这种差别的原因可能是:

在使用内部类对象时,可保证其外围类对象一定被创建了,且内部类对象一定有一个指向其外围类对象的引用,所以内部类可以通过成员名直接访问(可能编译器会在成员名前自动拼接 外围类对象引用.);而在使用外部类对象时,并不能保证内部类对象一定被创建了,所以需要先创建内部类对象,然后通过这个内部类对象去访问内部类成员。

可以在一个方法里面或在任意作用域(方法的作用域:if(isOk){作用域}、类的作用域)中定义内部类。这么做有两个理由:

3-1定义在方法中的类

在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类,这被称作局部内部类。

运行结果为:

注意:

1.PDestination类是destination方法的一部分,而不是Parcel5的一部分。所以,在destination()之外不能访问PDestination;

2.在destination()方法中定义了内部类PDestination,并不意味着一旦destination()方法执行完毕,PDestination就不可用了。

3-2定义在方法内作用域中的类

TrackingSlip类被嵌入在if语句的作用域内,这并不是说该类本身的创建是有条件的,它其实会与其他类一起被编译。然而,在定义TrackingSlip的作用域之外,它是不可用的;除此之外,它与普通类一样。

3-3实现了接口的匿名内部类

return语句的含义为:创建一个实现(继承)了Contents接口(类)的匿名类对象。

3-4扩展了有非默认构造器的类的匿名内部类

有非默认构造器的类Wrapping

匿名类Parcel8扩展Wrapping类

运行结果为:

目前来看,定义匿名内部类的方式为:

1.当是 new 接口名()时,创建的就是一个实现类对象;

2.当是 new 基类名(基类构造器的参数)时,创建的是一个子类对象,但在创建子类对象中包含的基类对象时调用的就是new关键字后 指定的基类构造器

3-5执行字段初始化的匿名内部类

在匿名类中定义字段时,还能够对其执行初始化操作。

运行结果为:

如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象(可以是方法传入的参数,也可以是外围类的成员),那么编译器会要求 这个对象是final的。

注:匿名内部类(子类)本身使用外部对象才要求是final的,但如果只是基类使用而匿名内部类自身不使用,则不需要是final的。

上面的例子中的匿名内部类使用的在其外部定义的对象是通过方法的参数传入的,也可以直接使用外围类的成员(private成员默认是final的)。

运行结果为:

3-6通过实例化实现构造的匿名内部类

匿名类因为没有名字,所以不可能有构造器。

通过实例初始化(本质就是通过 普通代码块),就能达到为匿名内部类创建一个构造器的效果。

运行结果为:

匿名内部类与正规的继承相比有些受限,因为匿名内部类 既可以扩展类,也可以实现接口,但是不能两者兼备。而且如果是实现接口,也只能实现一个接口。

如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static。这通常称之为嵌套类或者静态内部类。

普通的内部类对象隐式地保存了一个引用,指向它的外围类对象。而嵌套类对象就没有这个引用,这意味着:

普通的内部类中不能有static数据和static字段,也不能包含嵌套类(也是static的),但是嵌套类可以包含这些静态的东西。其原因可认为是:

普通内部类可看作是外围类的非静态成员,要求它的初始化必须在其外围类对象创建之后进行。在创建内部类对象的时候,会先加载外围类,再加载内部类。类加载完成后就会初始化类的静态成员,如果内部类有静态成员,那么就会在类加载后初始化内部类的静态成员,但这个时候其外围类对象还未创建,这与要求冲突。所以不允许普通内部类包含静态成员。

这个例子中还有一个需要注意的地方:外围类能直接访问静态内部类的静态私有成员(构造器)。所以总结一下外围内访问内部类的规律:

4-1接口中的类

接口中的变量默认是public static final的;

接口中的方法默认是public的;

接口中的类默认是public static的,也就是说,接口中的类默认是静态内部类。

运行结果为:

Original: https://www.cnblogs.com/certainTao/p/14844775.html
Author: certainTao
Title: 内部类

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585275/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球