前言
最近一段时间重读了java编程思想,把一些东西重新理解记录一遍
目的
整理知识点,方便以后回顾,这一章主要讲解了java的内部类
正文
1.内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类
内部类和组合是完全不同的概念
2.创建内部类
内部类是嵌在外部类内部的类。
如果想从外部类的非静态方法创建一个内部类对象,一定要用“. ”,Outer Class . InnerClass
比如: Parcel2.Contents c = q.contents();
3.链接到外部类
内部类可以访问外围对象的所有成员。对外围类的所有元素都有访问权。
内部类的对象只有在于外部类的对象相关联时才能被创建。当某个外围类对象创建一个内部类对象,此内部类的对象必定会秘密的捕获一个指向那个外围类对象的引用。依赖于外部对象,也就是,必须是外部类对象 . 某方法() 的方式创建
下面是"迭代器"设计模式的一个梨子
1 | public class Sequence{ |
4.使用.this和.new
内部类创建外部类对象,return OuterClass.this;
在其它地方创建内部类,要用外围类对象创建,而不是用类名。 OuterClass.InnerClass inner = outer.new Inner();
在拥有外部类对象之前是不可能创建内部类对象的,这是因为内部类对象会暗暗的连接到创建他的外部类对象上,但是如果你创建的是嵌套类(静态内部类),就不需要对外部类对象的引用了
1 | public class DotThis{ |
5.内部类与向上转型
接口的所有成员自动被设置为public
private内部类给类的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了细节。因为是private,所以除了外围类不能访问它,只能通过outer.contents() 方法return的new出来。 因为上转型为接口,所以隐藏了原本的类型。
由于不能访问任何新增加的,原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给Java编译器提供了生成更高效代码的机会。因为转型只能转型成接口类型,收窄了
1 | public interface Destination{ |
6.在方法和作用域内的内部类
可以在一个方法或者任意的作用域内定义内部类,这样做有两个理由:
1、实现了某类型的接口,于是可以创建并返回对其的引用。
2、要解决一个复杂的问题,想创建一个类来辅助解决方案,但是不希望这个类是公用的。
可以看到内部类可以在以下定义
1 | 一个定义在方法中的类 |
下面两个梨子实现了前两个
1 | //方法内部类(又称局部内部类) |
7.匿名内部类
1 | public class Parcel7 { |
由此可以创建一个继承自Contents的匿名内部类。
不能给内部类用构造器初始化,因为匿名内部类没有名字。
如果定义一个匿名内部类,它需要使用外部定义的参数,那么次参数引用需要是final的,实例初始化的效果就是构造器
1 | abstract class Base{ |
因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备,而且如果是实现接口,也只能实现要给接口
在访工厂方法
利用匿名内部类可以更加优雅的实现工厂方法
1 | interface Game{boolean move();} |
8.嵌套类
将内部类声明为static,这通常称为嵌套类。
嵌套类意味着:
1、要创建嵌套类的对象,不需要其外围类的对象。
2、不能从嵌套类的对象中访问非静态的外围对象。
普通内部类不能有static数据和static字段,也不能包含嵌套类,而嵌套类可以包含。它不需要依赖外围类引用
1 | public class Parcel11 { |
接口内部的类
接口中的任何类自动是public和static的
如果想创建某些公共代码,使得它们可以被某个接口的所有不同实现所公用,那么使用接口内部的嵌套类会显得很方便
1 | public interface ClassInInterface { |
扩展
java8接口增强
1 | a. 在接口中可以添加使用 default 关键字修饰的非抽象方法。即:默认方法(或扩展方法) |
a:在接口中可以添加使用 default 关键字修饰的非抽象方法。即:默认方法(或扩展方法):
Java 8 允许给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法(也称为默认方法或虚拟扩展方法或防护方法)。在实现该接口时,该默认扩展方法在子类上可以直接使用,它的使用方式类似于抽象类中非抽象成员方法。
Note:扩展方法不能够重写(也称复写或覆盖) Object 中的方法,却可以重载Object 中的方法。
eg:toString、equals、 hashCode 不能在接口中被覆盖,却可以被重载。
默认方法允许我们在接口里添加新的方法,而不会破坏实现这个接口的已有类的兼容性,也就是说不会强迫实现接口的类实现默认方法。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供一个默认的方法实现,所有这个接口的实现类都会通过继承得到这个方法(如果有需要也可以重写这个方法)
1 | interface Defaulable { |
b:接口里可以声明静态方法,并且可以实现
注意:Supplier 的使用,可看参考资料,在接口部分(java编程思想第四版第九章读书笔记 第十点)我们分别建一个工厂和一个产品接口类,然后实现,不同的工厂建立不同的产品,但是我们通过接口静态方法,将工厂的实现利用多态都写在一个接口内。
1 | private interface DefaulableFactory { |
a,b的调用
1 | public static void main( String[] args ) { |
9.为什么需要内部类
内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。可以认为内部类提供了某种进入其外围类的窗口。
内部类最吸引人的原因是:
1 | 内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效的实现了“多重继承”。也就是说,内部类允许继承多个非接口类型(类或抽样类)。(个人感觉一个外围类可以包含多个内部类,然后每个内部类通过多个方法分别return new baseClass1{.......}; return new baseClass2{.......}; return new baseClass3{.......}; 在调用时使用 outer.方法() 可以返回多重类型,作为函数的参数,达到“多重继承”的效果。) |
如果使用内部类,可以获得一些特别的特性:
1 | 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。 |
闭包与回调:
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。内部类是面向对象的闭包。在此作用域内,内部类有权操作所有的成员,包括private成员。
继承两个类,含有同名方法,使用内部类可以避免覆盖。当创建一个内部类,没有在外围类接口中添加东西,也没有修改外围类接口。
通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。
回调的价值在于它的灵活性——可以在运行时动态的决定要调用什么方法
1 | package com.test.demo; |
内部类与控制框架:
应用程序框架就是被设计用以解决某类特定问题的一个类或者一组类。
模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。
控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称作事件驱动系统。
“变化向量”是各种不同的Event对象所具有的不同行为,通过创建不同的Event子类来表现不同的行为。
内部类允许:
1 | 控制框架的完整实现是由单个类创建的,从而使得实现的细节被封装起来。内部类用来表示解决问题所必需的各种不同的action()。 |
10.内部类的继承
1 | package com.test.demo; |
在继承内部类时如果继续使用默认构造器会报错,而且不能只传递一个指向外部类对象的引用。此时必须在构造器内使用如下语法:
1 | 外部类对象.super(); |
这样才提供了必要的引用,才可以编译通过
内部类可以被覆盖吗》当继承某个外部类时,内部类并没有发生什么变化。内部类是完全独立的两个实体,各自在自己的命名空间内
局部内部类》局部内部类不能有访问说明符,因为它不是外部类的一部分;但是它可以访问当前代码代码块中的常量,以及此外部类的所有成员
内部类标识符》内部类也会生成一个.class文件。这些文件的命名规则是:外部类的名字+“$”+内部类的名字;如果是匿名内部类编译器也会产生一个数字作为你其标识
参考资料
https://blog.csdn.net/severusyue/article/details/49444629 severusyue
https://blog.csdn.net/sun_promise/article/details/51220518 莫若吻
https://my.oschina.net/0sbVMw/blog/535010 Solid