定义与创建

将一个类定义放在另一个类、方法、作用域、匿名类等地方,就是内部类;内部类只能由外部类对象创建(通过外部方法或者.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_内部类的更多相关文章

  1. Java学习笔记之---内部类

    Java学习笔记之---内部类 (一)成员内部类 内部类在外部使用时,无法直接实例化,需要借助外部类信息才能实例化 内部类的访问修饰符可以任意,但是访问范围会受到影响 内部类可以直接访问外部类的成员, ...

  2. Java学习笔记:内部类/匿名内部类的全面介绍

    编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致:非public得,无强制要求).如果想把多个java类放在一 ...

  3. 3)Java学习笔记:内部类

    什么是内部类 内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和 ...

  4. thinkinginjava学习笔记07_多态

    在上一节的学习中,强调继承一般在需要向上转型时才有必要上场,否则都应该谨慎使用: 向上转型和绑定 向上转型是指子类向基类转型,由于子类拥有基类中的所有接口,所以向上转型的过程是安全无损的,所有对基类进 ...

  5. thinkinginjava学习笔记06_复用类

    MarsEdit粘代码好麻烦,所有代码交给github:https://github.com/lozybean/MyJavaLearning 复用一个类常用的两种方式:组合.继承: 组合 将对象引用置 ...

  6. thinkinginjava学习笔记05_访问权限

    Java中访问权限等级从大到小依次为:public.protected.包访问权限(没有关键词).private: 以包访问权限为界限,public.protected分别可以被任意对象和继承的对象访 ...

  7. thinkinginjava学习笔记04_初始化与清理

    java沿用了c++的构造器,使用一个和类名完全一样的方法作为类的构造器,可以有多个构造器来通过不同的参数进行构造,称为重载:不仅是构造器可以重载,其他方法也一样通过不同的形参以及不同的返回值来实现重 ...

  8. thinkinginjava学习笔记01_导论

    初学java,希望旅途愉快  :) 类型决定对象的接口,(有人认为类是类型的特定实现),接口确定对象所能发出的请求(消息),满足请求的代码和隐藏的数据一起构成实现: 对象设计时,应该很好地完成一项任务 ...

  9. 【原】Java学习笔记025 - 内部类

    package cn.temptation; public class Sample01 { public static void main(String[] args) { // 内部类(嵌套类): ...

随机推荐

  1. 预防onion比特币勒索病毒,如何快速关闭135,137,138,139,445端口

    预防onion比特币勒索病毒,如何快速关闭135,137,138,139,445等端口   如果这种网络端口关闭方法行不通,可以尝试一种新的关闭网络端口方法(比较繁琐)见106楼,补丁安装教程见126 ...

  2. Retrofit网络请求库应用02——json解析

    PS:上一篇写了Retrofit网络请求库的简单使用,仅仅是获取百度的源码,来证明连接成功,这篇讲解如何解析JSON数据,该框架不再是我们之前自己写的那样用JsonArray等来解析,这些东西,我们都 ...

  3. Vux配置指南

    流程 Vux是Vue.js的一个ui库,官网在这里,官方文档的配置指南侧重于技术的罗列,我这里简化一下Vux的配置流程. 1. 安装vux npm install vux --save 2. 安装le ...

  4. SpringCloud Feign使用详解

    添加依赖: <dependency> <groupId>org.springframework.cloud</groupId> <artifactId> ...

  5. Opticks依赖库的下载和编译

    最近下载Opticks的代码编译,用其自带的retrieve-dependencies.bat下载依赖库,总是提示缺少模块. 分析了一下错误原因,根据命令窗的提示,手动下载如下配置文件: https: ...

  6. Python_02笔记

    数据类型 引子 什么是数据?x=10, 10 是我们要存储的数据 为啥数据要分不同的类型数据是用来表示状态的,不同的状态就应该用不同的类型的数据去表示 数据类型数字(整形,长整型,浮点型,复数)字符串 ...

  7. 【CODEFORCES】 A. Dreamoon and Sums

    A. Dreamoon and Sums time limit per test 1.5 seconds memory limit per test 256 megabytes input stand ...

  8. Eclipse的Servers视图中无法加入Tomcat6/Tomcat7

    引言: 在基于Eclipse的开发过程中,出现了无法在Eclipse中加入Tomcat的问题,经过从网上搜索之后.找到了答案. 问题的提出: 无法从下面方式,加入Tomcatserver.  当中Se ...

  9. One-Based Arithmetic

    One-Based Arithmetic time limit per test 0.5 seconds memory limit per test 256 megabytes input stand ...

  10. Spring定时任务有时候会莫名奇妙的终止?

    最近在是使用Spring配置定时定时任务(基于xml配置使用spring自带的定时任务),一开始使用没什么问题当使用久了就会出现有些定时任务自动停止了.(关于如何使用以及如何它的原理是啥,这里不进行阐 ...