外部类与内部类

众所周知,每个java文件必须有一个与这个java文件同名的外部类,这个外部类可以是public或default的。而除了这个同名外部类,还可以有与这个同名外部类平级的其他外部类,但它们必须是default的。

而内部类是指在外部类的内部再定义一个类。

内部类的分类

我们所说的内部类,官方的叫法是嵌套类(Nested Classes)。嵌套类包括静态内部类(Static Nested Classes)和内部类(Inner Classes)。而内部类分为成员内部类,局部内部类(Local Classes)和匿名内部类(Anonymous Classes)。

但按我的理解来看,应该这么画图:

而从使用方法来讲,则应该把静态内部类和成员内部类归在一起,因为这两种是可以通过outer.staticInner或者outer.memberInner这种.语法来使用类名的。与之相对,局部内部类和匿名内部类是无法通过.语法来使用类名,这是因为这二者的使用范围会被限定到某个作用域之内(即在某对{...}大括号之中),也是因为这二者不是作为外部类的成员(既不是静态成员,也不是实例成员)。

非静态内部类的对象创建时,会依赖到外部类对象(持有外部类对象引用)。但具体的说,是指在non-static context这种上下文中。更具体的说,成员内部类一定会持有外部类对象引用,局部内部类(具名或匿名)却不一定。

内部类可以访问外部类的私有方法,和私有成员。

局部内部类不管是具名还是匿名的,其类定义前不可以加上static(究其原因是局部内部类不属于外部类的成员,只有成员才有静态和非静态之分)。同样,其成员和方法也不可以加上static。

局部内部类包括匿名内部类,即局部内部类中分别两种:具名的、匿名的。匿名的就叫匿名内部类了。

具名的局部内部类的类定义不可以加上访问权限限定词。public protected private都不可以。

局部内部类

局部内部类分为两种:1.通过继承抽象类或实现接口或普通类来定义新类(将用这种举例,因为这种更具有实用价值) 2.直接定义一个新类(虽然感觉这样做没什么用)。如果是匿名内部类则只有第1种。

首先给一个interface:

public interface Destination {

String readLabel();

}

局部内部类访问外部类成员(non-static context)

当局部内部类的类定义处于一个非静态上下文时,局部内部类可以访问外部类的私有成员(静态或非静态)或私有方法(静态或非静态):(对于具名的还是匿名的都成立)

public class test3 {

private int num = 1;

private static int snum = 2;

private String returnString(){

return "outer string";

}

private static String sreturnString(){

return "static outer string";

}

public Destination destination() {//这里是非静态方法,属于非静态context

class PDestination implements Destination {

private String label;

//内部类访问外部类私有成员,这里改成snum也可以

private int i = num;

private PDestination() {

//内部类访问外部类私有方法,这里改成sreturnString也可以

label = returnString();

}

public String readLabel() { return label; }

}

return new PDestination();

}

public static void main(String[] args) {

test3 t = new test3();

Destination d = t.destination();

}

}

局部内部类访问外部类成员(static context)

public class test3 {

private int num = 1;

private static int snum = 2;

private String returnString(){

return "outer string";

}

private static String sreturnString(){

return "static outer string";

}

public static void testNamed(){//静态方法内,具名的局部内部类

class PDestination implements Destination {

private String label = "named inner";

public String readLabel() { return label; }

public void test(){

sreturnString();

//non-static method returnString() cant be refenrenced from a static context

//returnString();//无法通过编译

}

}

}

public static void testNoNamed() {//静态方法内,匿名内部类

Destination d = new Destination(){

private String label = "no named inner";

public String readLabel() { return label; }

public void test(){

sreturnString();

//non-static method returnString() cant be refenrenced from a static content

//returnString();//无法通过编译

}

};

}

public static Destination d = new Destination(){//作为静态成员,的匿名内部类

private String label = "no named inner";

public String readLabel() { return label; }

public void test(){

sreturnString();

//non-static method returnString() cant be refenrenced from a static content

//returnString();//无法通过编译

}

};

public static void main(String[] args) {

test3 t = new test3();

}

}

经过测试发现,当局部内部类(具名或匿名)的类定义属于static context时,是不可以访问外部类的非静态成员或方法。道理很简单,static context是类相关的,此时可能没有对象,当然不能访问非静态的东西,也就是说,这种情况下局部内部类对象没有持有外部类对象的引用。上面测试了三种static context的情况:

静态方法内,具名的局部内部类

静态方法内,匿名内部类

作为静态成员,的匿名内部类

外部类访问局部内部类成员

在局部内部类的作用域内(指包裹内部类的那个{...}内),内部类之外,是可以随意访问内部类对象的私有成员的。(注意,构造器可以直接访问;但方法和成员则必须通过内部类对象的.语法来访问哦;但一旦出了作用域,除了接口规定好的方法外,其他的方法和成员都无法访问,因为编译器只认为对象是一个Destination对象):(对于具名的还是匿名的都成立)

public class Parcel5 {

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);

}

public String getLabel(String s) {

class PDestination implements Destination {

private String label;

private PDestination(String whereTo) {

label = whereTo;

}

public String readLabel() { return label; }

}

//访问了内部类对象的私有成员

return new PDestination(s).label;

}

public static void main(String[] args) {

Parcel5 p = new Parcel5();

Destination d = p.destination("Tasmania");

//System.out.println(d.label); cant resolve symbol label

//new PDestination("");

System.out.println(p.getLabel("private string"));

}

}

给内部类成员赋值

当然,上面例子也可以改成,方法参数直接给内部类成员赋值:

public class Parcel5 {

public Destination destination(String s) {

class PDestination implements Destination {

//private String label = s; //定义字段时同时初始化

//也可以把下面四行注释掉,上面这行注释取消掉

private String label;

PDestination(){

label = s;

}

public String readLabel() { return label; }

}

return new PDestination();

}

public static void main(String[] args) {

Parcel5 p = new Parcel5();

Destination d = p.destination("Tasmania");

}

}

局部内部类还可以访问局部变量,在jdk1.8以前这个局部变量必须加上final,由于本人jdk是1.8,新特性支持不需要加final了(注意形参也是局部变量,所以上面的例子也不需要加final)(虽然不用加final,但编译器会自动加上只是我们看不见,以保证变量初始化以后不会变,但是通常最好加上 final 作为一种暗示)。(对于具名的还是匿名的都成立)

public class Parcel5 {

public Destination destination(String s) {

int temp = 123;//局部变量

class PDestination implements Destination {

private String label = s;

private int num = temp;//访问局部变量

public String readLabel() {

return label;

}

}

return new PDestination();

}

public static void main(String[] args) {

Parcel5 p = new Parcel5();

Destination d = p.destination("Tasmania");

System.out.println(d.readLabel());

}

}

匿名内部类

使用匿名内部类的情况只能是:创建的匿名内部类继承某个类。

继承一个外部类,或静态内部类,或成员内部类,都可以。

继承一个抽象类abstract class。

实现一个接口interface。

然后通过new表达式返回的引用被自动向上转型为对其父类的引用。既然是父类引用指向子类对象,只不过这个子类有点特殊,它没有名字。所以其父类被当做“接口”使用:

如果是普通类,那么通过覆盖非静态方法,可以实现多态。

如果是抽象类,那么实现其抽象方法。

如果是接口,那么实现其所有方法。

当获得匿名内部类对象后,只能调用其父类已经定义好的方法,所以才说其父类被当做“接口”使用。

简单示例如下:

public interface Contents {

int value();

}

public class Parcel7 {

public Contents contents() {

return new Contents() { // 调用父类的默认构造器

private int i = 11;

public int value() { return i; }

}; // 需要分号

}

public static void main(String[] args) {

Parcel7 p = new Parcel7();

Contents c = p.contents();

}

}

在上面例子中调用了父类的默认构造器,如果父类的构造器是有参数的:

public class Wrapping {

private int i;

public Wrapping(int x) { i = x; }//父类的有参构造器

public int value() { return i; }

}

public class Parcel8 {

public Wrapping wrapping(int x) {

// Base constructor call:

return new Wrapping(x) { // 直接传递这个参数给父类的有参构造器

public int value() {

return super.value() * 47;//调用父类的value方法

}

};

}

public static void main(String[] args) {

Parcel8 p = new Parcel8();

Wrapping w = p.wrapping(10);

}

}

由于匿名内部类没有名字,自然也没有构造器了。但可以通过实例初始化块来起到构造器的作用:

public class Parcel10 {

public Destination destination(String dest, float price) {

return new Destination() {

private int cost;

// Instance initialization for each object 这里就是实例初始化块

{

cost = Math.round(price);

if(cost > 100)

System.out.println("Over budget!");

}

private String label = dest;

public String readLabel() { return label; }

};

}

public static void main(String[] args) {

Parcel10 p = new Parcel10();

Destination d = p.destination("Tasmania", 101.395F);

}

}

局部内部类、匿名内部类比较

匿名内部类没有名字,也就没有构造器,只有通过实例初始化块。

相比具名的局部内部类,匿名内部类有些受限,因为只能继承或实现一个类或接口。

当我们需要一个命名的构造器或者需要重载构造器(重载构造器自然需要名字),需要使用具名的局部内部类。

当我们需要在作用域中一次定义,多次创建对象时,则需要使用具名的局部内部类。

java局部内部类_Java 局部内部类、匿名内部类详解相关推荐

  1. java jtable组件_java中jtable组件详解实例

    java中jtable组件详解实例 java 表格控件 JTable 常用操作详解 JTable 是 Swing 编程中很常用的控件,这里总结了一些常用方法以备查阅.欢迎补充,转载请注明作者与出处.一 ...

  2. java aqs原理_Java并发之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  3. java异常例子_java 异常的实例详解

    java 异常的实例详解 1.异常的定义:程序在运行时出现不正常情况. 异常的划分: Error:严重的问题,对于error一般不编写针对性的代码对其进行处理. Exception:非严重的问题,对于 ...

  4. java list用法_java list的用法详解

    java list的用法详解 java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中.以下是百分网小编搜索整理的关于java list的用法详解,需要的朋友可以参考一下!想了解更多相关 ...

  5. java 自定义正则表达式_java中正则表达式实例详解

    Java中正则表达式运用实例(参看java中正则表达式运用详解): 测试代码 package test; /** * 在String的matches()方法,split()方法中使用正则表达式. * ...

  6. java多线程代码_java多线程实例代码详解

    原文:http://blog.csdn.net/paranoidyang/article/details/70184523 作者:Paranoidyang 线程与进程的区别 (1)程序是一段静态的代码 ...

  7. java file 实例_Java File类的详解及简单实例

    Java File类的详解及简单实例 1. File():构造函数,一般是依据文件所在的指定位置来创建文件对象. CanWrite():返回文件是否可写. CanRead():返回文件是否可读. Co ...

  8. java 枚举 方法_Java枚举使用方法详解

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数 ...

  9. java 返回值_Java方法返回值详解

    Java方法返回值详解 每个方法都是为了完成某个特定的功能,例如:登录功能.求和功能等,既然是功能,那么当这个功能完成之后,大多数情况下都会有一个结果的,比如,登录成功了或者失败了(true/fals ...

  10. java红黑树_JAVA学习-红黑树详解

    1.定义 红黑树是特殊的二叉查找树,又名R-B树(RED-BLACK-TREE),由于红黑树是特殊的二叉查找树,即红黑树具有了二叉查找树的特性,而且红黑树还具有以下特性: 1.每个节点要么是黑色要么是 ...

最新文章

  1. 0基础学python-从0开始学Python,0基础小白
  2. 阿里云物模型层初始化代码实现
  3. spring异常 java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderServlet
  4. C/C++学习之路: STL
  5. cmd导入mysql文件
  6. 漫步最优化二十八——三次插值法
  7. 自定义Java定时器(基于ScheduledExecutorService)
  8. C-Free 5.0最新注册码
  9. 台达伺服ASD-B2的调试
  10. 计算机快捷键任务管理器,打开电脑任务管理器快捷键是什么
  11. proteus不能打开一直在开启界面转圈
  12. C# Activator.CreateInstance()方法使用
  13. Tangle:不同于区块链的分布式账本
  14. 百度图片爬虫【图片编码处理】
  15. c 语言 登陆窗口界面,c/c++语言实现登陆界面
  16. IP技术 -- 6 Telemetry
  17. 关于谷歌webrtc源码国内镜像的使用问题,以及Kurento媒体服务器
  18. 7000字详解数据指标体系建设实践
  19. Dapp开发实战:去中心化NFT交易平台
  20. DISTINCT和COUNT的组合使用

热门文章

  1. 最小生成树prim、
  2. 店铺如何用视觉走出差异化?
  3. 2016/1/14 java随机数生成
  4. JavaScript中的两个等号(==)和三个等号(===)
  5. pyltp实体识别_哈工大 PYLTP 安装 排坑指南
  6. sql 同一张表中两个记录不能共存_如何分析交易记录?
  7. Python嵌套定义函数增强reduce()函数功能
  8. 微课|中学生可以这样学Python(5.8.2节):使用切片修改列表
  9. 微课系列(7):Python程序中sys.argv的用法
  10. Python扩展库numpy中where()函数的三种用法