内部类基本概念

  • 可将一个类的定义置于另一个类定义的内部
  • 内部类允许将逻辑相关的类组织在一起,并控制位于内部的类的可见性
  • 甚至可将内部类定义于一个方法或者任意作用域内!
  • 当然,内部类 ≠ 组合
  • 内部类拥有其外围类 所有元素的 访问权
  • 更有甚,嵌套多层的内部类能透明地访问所有它所嵌入的外围类的所有成员

一个典型的例子:利用 Java内部类 实现的 迭代器模式

// 接口
-------------------------------------------------------------
public interface Selector {
boolean end();
Object current();
void next();
}
// 外部类(集合类) + 内部类(迭代器类)
-------------------------------------------------------------
public class Sequence { // 外部类(代表一个集合类) private Object[] items;
private int next = 0; public Sequence( int size ) {
items = new Object[size];
} public void add( Object x ) {
if( next < items.length )
items[next++] = x;
} // 迭代器类:实现了 Selector接口的 内部类
private class SequenceSelector implements Selector {
private int i = 0;
public boolean end() { return i == items.length; }
public Object current() { return items[i]; }
public void next() {
if( i<items.length )
++i;
}
} public Selector selector() { // 该函数也表明了:内部类也可以向上转型,这样在外部就隐藏了实现细节!
return new SequenceSelector();
} public static void main( String[] args ) {
Sequence sequence = new Sequence(10);
for( int i=0; i<10; ++i ) { // 装填元素
sequence.add( Integer.toString(i) );
}
Selector selector = sequence.selector(); // 获取iterator!
while( !selector.end() ) {
print( selector.current() + " " );
selector.next();
}
}
}
// 输出
-------------------------------------------------------------
0 1 2 3 4 5 6 7 8 9

.this 与 .new 的使用场景

.this用于在内部类中生成对其外部类对象的引用之时,举例:

public class DotThis {

  void f() { print("DotThis.f()"); }

  public class Inner { // 内部类
public DotThis outer() { // 返回外部类对象的引用
return DotThis.this; // 若直接返回this,那指的便是内部类自身
}
} public Inner inner() { return new Inner(); } public static void main( String[] args ) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f(); // 输出 DotThis.f()
}
}

.new用于直接创建内部类的对象之时,距离:

public class DotNew {
public class Inner { } // 空内部类
public static void main( String[] args ) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner(); //注意此处必须使用外部类的对象,而不能直接 DotNew.Inner dni = new DotNew.Inner()
}
}

嵌套类(static类型的内部类)

嵌套类是无需依赖其外部类的对象的。非static内部类通过一个特殊的this链接到其外围类的对象,而static类型的内部类无此this引用。

接口与内部类有着很有趣的关系: 放到接口中的任何类自动都是public且static,即接口中的任何类都是嵌套类,我们甚至可以在接口的内部类中去实现其外围接口,举例:

public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface { // 类Test默认static,所以是嵌套类
public void howdy() {
print("Howdy!");
}
public static void main( String[] args ) {
new Test().howdy();
}
}
}

在 方法 和 作用域 内的内部类

可以称这类为 局部内部类!

方法中定义的内部类只能在方法内被使用,方法之外不可访问,举例:

public class Parcel {  // parcel是“包裹”之意

  public Destination destination( String s ) {

    class PDestination implements Destination { // 方法中定义的内部类
private String label;
private PDestination( String whereTo ) { label = whereTo; }
public String readLabel() { return label; }
} return new PDestination( s ); // 只有在方法中才能访问内部类PDestination
} public static void main( String[] args ) {
Parcel p = new Parcel();
Destination d = p.destination( "Hello" );
...
}
}

更进一步,可在任意作用域内定义内部类,举例:

public class Parcel {

  private void internalTracking( boolean b ) {

    if( b ) { // 局部作用域中定义了内部类,作用域之外不可访问!
class TrackingSlip {
private String id;
TrackingSlip( String s ) { id = s; }
String getSlip() { return id; }
}
} } public void track() { interTracking( true ); } public static void main( String[] args ) {
Parcel p = new Parcel();
p.track();
}
}

匿名内部类

直观上看,这种内部类没有“名字”,举例:

public class Parcel {

  public Contents contents() {
return new Contents() { // 此即匿名内部类!!!
private int i = 11;
public int value() { return i; }
}; // !!!注意这里必须要加分号!!!
} public static void main( String[] args ) {
Parcel p = new Parcel();
Contents c = p.contents();
}
}

若想将外部的参数传到匿名内部类中(典型的如将外部参数用于对匿名内部类中的定义字段进行初始化时)使用的话,该参数必须final,举例:

public class Parcel {

  public Destination destination( final String s ) { // final必须!
return new Destination() {
private String label = s;
public String readLabel() { return label; }
}; // 分号必须!
} public static void mian( String[] args ) {
Parcel p = new Parcel();
Destination d = p.destination("Hello");
}
}

匿名内部类中不可能有命名的显式构造器,此时只能使用实例初始化的方式来模仿,举例(当然下面这个例子还反映了匿名内部类如何参与继承):

// 基类
---------------------------------------------
abstact class Base() {
public Base( int i ) {
print( "Base ctor, i = " + i );
}
public abstract void f();
} //主类(其中包含了继承上面Base的派生匿名内部类!)
----------------------------------------------
public class AnonymousConstructor { public static Base getBase( int i ) { // 该处参数无需final,因为并未在下面的内部类中直接使用!
return new Base(i){ // 匿名内部类
{ // 实例初始化语法!!!
print("Inside instance initializer");
}
public void f() {
print( "In anonymous f()" );
}
}; // 分号必须!
} public static void main( String[] args ) {
Base base = getBase(47);
base.f();
}
} // 输出
------------------------------------------
Base ctor, i = 47 // 先基类
Inside instance initializer // 再打印派生类
In anonymous f()

匿名内部类 + 工厂模式 = 更加简洁易懂:

// Service接口
---------------------------------------------------
interface Service {
void method1();
void method2();
}
// ServiceFactory接口
---------------------------------------------------
interface ServiceFactory {
Service getService();
}
// Service接口的实现
---------------------------------------------------
class Implementation1 implements Service {
private Implementation1() {} // 构造函数私有
public void method1() { print("Implementation1 method1"); }
public void method2() { print("Implementation1 method2"); }
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
}; // 分号必须!!!
} class Implementation2 implements Service {
private Implementation2() {}
public void method1() { print("Implementation2 method1"); }
public void method2() { print("Implementation2 method2"); }
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation2();
}
}; // 分号必须!!!
}
// 客户端代码
----------------------------------------------------
public class Factories {
public static void serviceConsumer( ServiceFactory fact ) {
Service s = fact.getService();
s.method1();
s.method2();
} public static void main( String[] args ) {
serviceComsumer( Implementation1.factory );
serviceComsumer( Implementation2.factory );
}
}

总结:为什么需要内部类

内部类可以独立地继承自一个接口或者类而无需关注其外围类的实现,这使得扩展类或者接口更加灵活,控制的粒度也可以更细!

注意Java中还有一个细节:虽然Java中一个接口可以继承多个接口,但是一个类是不能继承多个类的!要想完成该特性,此时除了使用内部类来“扩充多重继承机制”,你可能别无选择,举例:

class D { }               // 普通类
abstract class E { } // 抽象类 class Z extend D { // 外围类显式地完成一部分继承
E makeE() {
return new E() { }; // 内部类隐式地完成一部分继承
}
} public class MultiImplementation {
static void takesD( D d ) { }
static void takesE( E e ) { }
public static void main( String[] args ) {
Z z = new Z();
takesD( z );
takesE( z.makeE() );
}
}

Java编程思想学习录(连载之:内部类)的更多相关文章

  1. Java编程思想学习笔记_3(继承,内部类)

    一.继承与清理 如果某个类需要去清理自身的资源,那么必须用心为其创建回收垃圾的方法,而如果此类有导出的子类,那么必须在导出类中覆盖回收的方法,当覆盖被继承类的回收垃圾的方法的时候,需要注意销毁的顺序应 ...

  2. Java编程思想学习(八) 内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿 ...

  3. [Java编程思想-学习笔记]第3章 操作符

    3.1  更简单的打印语句 学习编程语言的通许遇到的第一个程序无非打印"Hello, world"了,然而在Java中要写成 System.out.println("He ...

  4. java编程思想--学习心得

    学习Java编程思想,需要了解语言特性,对于各种名词,能够借助项目代码,解释其含义,不借助搜索工具,明白其在什么样场景下使用,会带来什么样的问题,能否避免这类问题. 学习的过程,与软件开发相同,一样是 ...

  5. Java编程思想学习(一)----对象导论中多态的理解

    1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...

  6. Java编程思想 学习笔记1

    一.对象导论 1.抽象过程 Alan Kay曾经总结了第一个成功的面向对象语言.同时也是Java所基于的语言之一的Smalltalk的五个基本特性,这些特性表现了纯粹的面向对象程序设计方式 1)万物皆 ...

  7. Java编程思想学习(五)----第5章:初始化与清理

    随着计算机革命的发展,“不安全”的编程方式已逐渐成为编程代价高昂的主因之一. C++引入了构造嚣(constructor)的概念,这是一个在创建对象时被自动调用的特殊方法.Java中也采用了构造器,并 ...

  8. [Java编程思想-学习笔记]第1章 对象导论

    1.1  抽象过程 Java是一门面向对象的语言,它的一个优点在于只针对待解问题抽象,而不用为具体的计算机结构而烦心,这使得Java有完美的移植性,也即Java的口号"Write Once, ...

  9. Java编程思想学习(十) 正则表达式

    正则表达式是一种强大的文本处理工具,使用正则表达式我们可以以编程的方法,构造复杂的文本模式,并且对输入的字符串进行搜索.在我看来,所谓正则表达式就是我们自己定义一些规则,然后就可以验证输入的字符串是不 ...

随机推荐

  1. Spring IOC(一)

    最近复习,准备整理下复习笔记 Spring IOC 部分: 控制反转(Inversion of Control) IOC是什么 简言之 IOC完成的事情原先由程序员主动通过new实例化对象事情,转交给 ...

  2. 解决 Failed to start LSB: Bring up/down networking 问题

    我用的是虚拟机,一换网络环境虚拟机的ip就没有了,重启网上就报Failed to start LSB: Bring up/down networking错误.网上查了一圈说什么HWADDR有问题,改了 ...

  3. 对elementui整体设计分析-------引用

    1.需求分析 丰富的 feature:丰富的组件,自定义主题,国际化. 文档 & demo:提供友好的文档和 demo,维护成本小,支持多语言. 安装 & 引入:支持 npm 方式和 ...

  4. GO语言学习笔记6-Sort的使用

    GoLang标准库的sort包提供了排序切片和用户自定义数据集以及相关功能的函数. Sort操作的对象通常是一个slice,需要满足三个基本的接口,并且能够使用整数来索引. 1.sort实现原理 So ...

  5. Lambda表达式匿名类实现接口方法

    Lamb表达式匿名类实现接口方法 import java.util.ArrayList; public class HandlerDemo{ public static void main(Strin ...

  6. k8s实战--redis主从--guestbook

    快速入门 实验:通过服务自动发现的redis主从 难点: 1,服务的自动发现,即如何确定coreDNS 已生效 2,redis的主从验证 遇到的问题: 1,Can't handle RDB forma ...

  7. 随堂小测APP使用体验

    随堂小测APP使用体验 先要去注册账号需要填写用户名.密码.手机号.学号/教师号.学校.专业.即可注册,注册成功后,即可登录APP进,登陆进去以后.会有两个界面,课堂和我的,注册.登录简单,通俗易懂, ...

  8. Jmeter性能测试一

    用jmeter进行压力测试,在网上看到一个简单的例子.按步骤做,在jmeter中执行时,结果中error一直为100%.通过在代码中加入打印语句,才找出代码中的一处错误.下面po上的代码中已将错误修改 ...

  9. [SPOJ20174]DIVCNT3 - Counting Divisors (cube):Min_25筛

    分析 首先,STO ywy OTZ,ywy TQL%%%! 说一下这道题用min_25筛怎么做. 容易发现,对于所有质数\(p\),都满足\(f(p)=4\),于是我们就可以直接通过\([1,x]\) ...

  10. ElasticSearch的介绍

    1.ELK 1.1 集中式日志系统 日志,对于任何系统来说都是及其重要的组成部分.在计算机系统里面,更是如此.但是由于现在的计算机系统大多比较复杂,很多系统都不是在一个地方,甚至都是跨国界的:即使是在 ...