thinkinginjava学习笔记09_内部类
定义与创建
将一个类定义放在另一个类、方法、作用域、匿名类等地方,就是内部类;内部类只能由外部类对象创建(通过外部方法或者.new方法),内部类对象创建时必须已经有一个外部类对象,并且与之连接(在内部类中会创建一个指向其外部类对象的引用),内部类可以访问到外部类对象的所有成员(包括private);
如:示例代码中,在Parcel类的内部定义了内部类:Contents和Destination,在外部类方法ship中,可以直接创建内部类对象;但是在类外部,只能想创建外部类对象q,然后再通过该对象的contents()和to()方法来创建内部类对象;或者可以通过q2.new来调用内部类的构造方法;注意内部类的引用类型格式是OutClass.InnerClass;
在内部类中,可以使用OutClass.this来调用外部类对象(必然存在该对象,且和内部类对象连接);
当内部类指定是private时,只能在类的内部调用该内部类,外部类对象无法访问并且创建该内部类对象;protected内部类则只有外部类的子类可以访问;通过创建private内部类可以通过类来完成一些工作并且隐藏所有类定义的细节;
在作用域中使用类时,超过作用域该类就无法被访问;如示例代码展示了几种内部类的定义方式;
注意在Parcel7、Parcel8中使用了匿名类,通过基类和接口创建匿名类对象的同时创建类的定义,匿名类的意义为:创建继承自一个基类或者接口的匿名类对象,默认添加继承关系;
使用匿名类时,不要忘记类定义后的分号;
如果如Parcel9中匿名内部类使用外部类中定义的对象,那么编译器会要求其参数引用是final的(如外部类方法destination中),否则将会出错;
如AnonymousConstructor中,可以通过对实例的初始化实现一个匿名构造器功能;如果匿名类中没有直接使用i,则外部方法的参数不需要指定是final的;又如Parcel10中,由于if语句并不能在初始化时被执行,所有匿名类中对实例化的初始化实际效果就相当于是构造器;
匿名内部类只能实现接口和扩展类的其中一个;
使用内部类改进工厂方法
在上一节最后使用到了工厂方法,将多个子类的构造器使用通用接口包装,然后对该接口进行调用即可实现多种子类的构造活动;而由于子类的构造器是不可见的,并且没有必要创建作为工厂的具名类,而只需要一个单一的工厂对象即可,使用匿名内部类改进后的代码为:示例代码;改进后的代码更加具有实际意义(消除了每个子类单独的工厂类);
嵌套类与单元测试
当不需要内部类和外部类的对象之间有联系时,可以将内部类声明为static,这种方式称为嵌套类;
嵌套类由于申明了static,并不像内部类一样,具有一个外部类的对象引用;一旦创建了嵌套类,就说明了嵌套类中的对象并不需要外部类对象的引用,并且不能从嵌套类的对象中访问非静态的外部类对象,没有new方法和this;
正常情况下,接口中是不能放置任何代码的,但是嵌套类可以作为接口的一部分;由于接口中所有的成员都将变为static和public,类也一样,一旦放入接口中,该类就是一个嵌套类;如:示例代码;
通过在每个类中添加main()方法可以完成单元测试的工作,但是这样做会将带上已经编译后的额外代码,通过将main方法放置到嵌套类中,可以优化单元测试,如: 示例代码;在编译之后,每个嵌套类(以及内部类)都会产生一个OutClass$InnerClass.class类似的文件,单元测试的类也一样,只要运行java TestBed\$Tester即可进行测试工作;只要在发布时,简单的将该文件删除就可以删除单元测试的内容;

内部类应用
1. 实现多重继承
通过使用内部类独立继承自一个接口的实现完成多重继承的工作;同时由于外部类不能同时继承自两个基类,只能通过内部类完成多重继承;
如:示例代码中,A、B接口可以通过X、Y两种方式实现多重继承;C、D抽象类则只能通过Z来实现多重继承;
2. 在同一个外围类中,可以使用多个内部类以不同的方式实现同一个接口,这样可以在同一个类中完成一个接口的多种实现而不用另外创建一个新类,通过这种方式可以形成更加清晰的代码结构;如Sequence中实现ForwardSelector和ReverseSelector两种Selector接口实现:
interface Selector{
char getCharAt(int i);
}
class Sequence {
private String s;
Sequence(String s){
this.s = s;
}
private Selector fs(){
return new Selector(){
public char getCharAt(int i){
return s.charAt(i);
}
};
}
private Selector rs(){
return new Selector(){
public char getCharAt(int i){
int index = s.length() - i;
return s.charAt(index);
}
};
}
public char forwardSelector(int i){return fs().getCharAt(i);}
public char reverseSelector(int i){return rs().getCharAt(i);}
}
3. 实现回调
当一个外部类继承自一个接口的实现时,可以通过内部类完成接口的另一个实现;并在适当的适合进行选择;如:示例代码 中,Callee2继承自MyIncrement,该类是接口Incrementable的一个实现,Callee2在某些时候需要使用继承来的方法,但是同时如果又有对该接口的另外实现,则可以通过新建内部类:Closure来完成,在Closure中返回一个Callee2的钩子,并且只能调用increment方法;并且通过设置内部类为private以及getCallbackReference()方法来隐藏实现过程;
4. 控制框架的编写;
控制框架往往由单个类来创建,用内部类来表示解决问题所必须的各种不同的action(),从而使得实现的细节被封装起来;
注意要点
1. 内部类的继承
由于内部类必须要和一个外部类的对象连接才可以被实例化,所以在继承一个内部类时,在构造方法中应该引用一个其外部类的对象,并且必须使用:
outclassReference.super()
的方式进行引用;如:
class WithInner{
class Inner{}
}
public class InheritInner extends WithInner.Inner {
InheritInner(WithInner wi){
wi.super();
}
}
2. 内部类的覆盖
内部类和外部类是完全独立的个体,各自在自己的命名空间内,如:示例代码中,在new BigEgg()时,先调用基类构造方法,打印New Egg()以及调用基类的内部类Yolk构造方法;此时,BigEgg()中的Yolk类没有起到作用,但是,如果添加BigEgg的构造方法,并且在其中创建Yolk内部类,则是可以完成覆盖的(其实是生成了一个同名方法);真正完成内部类的覆盖,则需要使内部类也继承基类中的内部类,并完成改写操作,如下面的示例;
在示例代码中还可以看到内部类的初始化过程:
基类的内部类 -> 基类 -> 基类内部类 -> 子类内部类 -> 子类;
3. 局部内部类
当一个内部类在作用域内创建时,则会局部内部类,局部内部类不能访问说明符,不是外部类的一部分,而是作用域的一部分,所以可以访问作用域内的常量以及外部类的成员;
局部作用域常在方法体内定义,并且被用来重载构造器,或者生成多个内部类对象等;
thinkinginjava学习笔记09_内部类的更多相关文章
- Java学习笔记之---内部类
Java学习笔记之---内部类 (一)成员内部类 内部类在外部使用时,无法直接实例化,需要借助外部类信息才能实例化 内部类的访问修饰符可以任意,但是访问范围会受到影响 内部类可以直接访问外部类的成员, ...
- Java学习笔记:内部类/匿名内部类的全面介绍
编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致:非public得,无强制要求).如果想把多个java类放在一 ...
- 3)Java学习笔记:内部类
什么是内部类 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和 ...
- thinkinginjava学习笔记07_多态
在上一节的学习中,强调继承一般在需要向上转型时才有必要上场,否则都应该谨慎使用: 向上转型和绑定 向上转型是指子类向基类转型,由于子类拥有基类中的所有接口,所以向上转型的过程是安全无损的,所有对基类进 ...
- thinkinginjava学习笔记06_复用类
MarsEdit粘代码好麻烦,所有代码交给github:https://github.com/lozybean/MyJavaLearning 复用一个类常用的两种方式:组合.继承: 组合 将对象引用置 ...
- thinkinginjava学习笔记05_访问权限
Java中访问权限等级从大到小依次为:public.protected.包访问权限(没有关键词).private: 以包访问权限为界限,public.protected分别可以被任意对象和继承的对象访 ...
- thinkinginjava学习笔记04_初始化与清理
java沿用了c++的构造器,使用一个和类名完全一样的方法作为类的构造器,可以有多个构造器来通过不同的参数进行构造,称为重载:不仅是构造器可以重载,其他方法也一样通过不同的形参以及不同的返回值来实现重 ...
- thinkinginjava学习笔记01_导论
初学java,希望旅途愉快 :) 类型决定对象的接口,(有人认为类是类型的特定实现),接口确定对象所能发出的请求(消息),满足请求的代码和隐藏的数据一起构成实现: 对象设计时,应该很好地完成一项任务 ...
- 【原】Java学习笔记025 - 内部类
package cn.temptation; public class Sample01 { public static void main(String[] args) { // 内部类(嵌套类): ...
随机推荐
- 关于PHP 采集类
伟大的筒子们,我们需要经常采集. 不知道大家每次采集的时候会不会烦躁,不用八爪鱼,不用PYTHON 是不是感到手无力,看到正则匹配每次匹配不对,一换采集内容就是头疼,重新拼写正则? 不要说是高手 ,就 ...
- 高质量PHP代码的50个实用技巧必备(上)
1.不要使用相对路径 常常会看到: ? 1 require_once('../../lib/some_class.php'); 该方法有很多缺点: 它首先查找指定的php包含路径, 然后查找当前目录. ...
- 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略
1. 前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...
- C#中如何使用断点操作调试程序
Visual Studio调试器调试 当代码不能正常运行时,可以通过调试定位错误.常用的程序调试操作包括设置断点.开始.中断和停止程序的执行.单步执行程序以及使程序运行到指定的位置.下面将对这几种常用 ...
- archsummit2017见闻和思考
前几天参加了archsummit的北京站.2天的日程安排的十分紧凑,大多数时间同时有多场专题分享,选择想要听的专题就成了首要的事情,按照感兴趣的,可能用到的,启发思考的原则选择了几场适合自己的专题,这 ...
- 轻谈BFC
BFC 定义 CSS2.1的定义 Block formatting contexts 9.4.1 Block formatting contexts Floats, absolutely positi ...
- Java基础—标识符及命名规范
什么是标识符符? 凡是可以由自己命名的地方都称为修饰符. 例: 项目名 ,包名 ,类名 .方法名 2. 命名规范. ① 不可使用java关键字和保留字,但是可以包含关键字和保留字. ② ...
- ZED-Board从入门到精通系列(八)——Vivado HLS实现FIR滤波器
http://www.tuicool.com/articles/eQ7nEn 最终到了HLS部分.HLS是High Level Synthesis的缩写,是一种能够将高级程序设计语言C,C++.Sys ...
- BZOJ 1211 HNOI2004 树的计数 Prufer序列
题目大意:给定一棵树中全部点的度数,求有多少种可能的树 Prufer序列.详细參考[HNOI2008]明明的烦恼 直接乘会爆long long,所以先把每一个数分解质因数.把质因数的次数相加相减.然后 ...
- 浏览器正确理解和使用GBK及UTF-8(UTF-8 + BOM)网页编码
网页编码英文译为web page encoding.是在网页中指定其特定的字符编码格式的库. GBK是国家标准GB2312基础上扩容后兼容GB2312的标准. GBK的文字编码是用双字节来表示的.即不 ...