Java允许在一个类中定义另外一个类,这样的类被称为嵌套类,就像下面这样:

class OuterClass {
...
class NestedClass {
...
}
}

  嵌套类分为两种:静态的和非静态的。声明为static的嵌套类被称为静态嵌套类,非静态嵌套类则被称为内部类:

class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}

  嵌套类是其所在的外部类的成员。内部类可以访问外部类中的其他成员,即使这个成员被private修饰。静态嵌套类则没有访问外部类中其他成员的权限。作为外部类的一个成员,嵌套类可以被声明为private、public、protected或者包私有的。

  下面是几个为什么要使用嵌套的类原因:

  • 能够将仅在一个地方使用的类合理地组合。如果一个类可能只对于另外一个类有用,此时将前者组合到后者,可以使得程序包更加简洁。
  • 增强封装性。假如有两个类A和B,B类需要使用A类中的成员,而恰好该成员又是仅类内部可见(private)的,如果将B定义为A的嵌套类,则B可以使用A的任何成员,而且B也可以声明为外部不可见(private),将B隐藏起来。
  • 能够使代码可读性和维护性更强。嵌套的类代码相较于顶级类,更靠近它被使用的地方,方便查看。

一.静态嵌套类

  就像静态方法和静态变量一样,静态嵌套类是和外部类相关联的。和静态方法一样,静态嵌套类不能直接引用实例变量和实力方法,只能通过一个对象引用。实际上,可以将静态嵌套类看作是一个顶级类,只不过将其嵌套在其他类中方便打包。

  静态嵌套类可以用过外部类的名字去访问:

OuterClass.StaticNestedClass

  可以使用下面的语法为静态嵌套类创建对象:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

二.内部类

  就像实例方法和实例变量一样,内部类与外部类的实例相关联并且可以直接访问外部类的方法和成员。并且,因为内部类与外部类的实例相关联,因此它内部不能定义静态成员。

  要实例化内部类,必须创建外部类的对象,然后使用这个对象去创建内部类的对象:

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

  实际上,还有两种特殊的内部类————局部类和匿名类。有关这两种类的内容将会在下文中介绍。强烈建议不要对内部类(包括局部类和匿名类)进行序列化(把对象转换为字节序列的过程称为对象的序列化,有关序列化的内容会在以后的文章中进行介绍)。

三.屏蔽现象

  如果一个类型(例如成员变量或参数)与外部作用域中的类型同名,那么内部作用域中的声明将会屏蔽外部作用域中的声明,这样就不能直接通过名称去访问外部作用域中同名的类型。如下例所示:

public class ShadowTest {
public int x = 0; class FirstLevel {
public int x = 1; void methodInFirstLevel(int x) {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
}
} public static void main(String... args) {
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}

  这个例子的输出如下:

x = 23
this.x = 1
ShadowTest.this.x = 0

  这个例子中定义了三个名为x的变量,分别是ShadowTest类的成员变量,内部类FirstLevel的成员变量,以及方法methodInFirstLevel的参数。方法methodInFirstLevel的参数x屏蔽了内部类FirstLevel的成员变量x和ShadowTest类的成员变量x。因此,在表示内部类FirstLevel的成员变量x时,要像下面这样:

System.out.println("this.x = " + this.x);

  在表示ShadowTest类的成员变量x时,要像下面这样:

System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);

四.局部类

  局部类是在块(由大括号包围的零条或多条语句)中定义的类。经常会在方法体中见到局部类。下面是一个定义在方法中的局部类:

public class OuterClass {
public void method() {
...
class LocalClass {
...
}
}
}

  局部类可以访问外部类的成员。此外,局部类还可以访问局部变量。然而,局部类只能访问由final修饰的局部变量。当一个局部类访问一个块中的局部变量或参数时,它就捕获了这个变量或者参数。从Java8开始,局部类不但可以访问由final修饰的局部变量和参数,还可以访实际上是final的局部变量和参数。实际上是final的意思是说这个变量或参数的值自从初始化之后就没有修改过。此外,局部类中的变量也会屏蔽定义在外部的同名变量或参数。

  局部类中基本不能定义静态成员。不过也有例外,可以在局部类中定义静态常变量(常变量是指类型为基本数据类型或者String,被声明为final,并且使用编译时常量表达式进行初始化的变量。编译时常量表达式通常是可以在编译时计算的字符串或算术表达式)。

  定义在静态方法中的局部类,只能引用外部类的静态成员。不能在块中定义接口,因为接口是天生静态的。也不能在局部类中定义静态初始化器或者接口。

五.匿名类

  匿名类让代码看上去更加简洁,它能让你同时声明和实例化一个类。它们类似于局部类,只不过匿名类没有名称。如果某个局部类只使用一次,可以将它定义为匿名类。

  局部类是类的声明,而匿名类则是表达式,这意味着匿名类是在一个表达式中定义的。匿名类表达式的语法就像是调用构造器的语法,只不过构造器后面跟的是类的定义。下面定义了一个Object类的子类,它重写了Object类的toString方法。由于这个类没有名字,因此只能在声明它的地方使用一次。虽然这个例子没什么意义,不过可以从中看到匿名类的语法,就像下面这样:

Object obj = new Object() {
@Override
public String toString() {
Sysyem.out.println("I'm a method from anonymous class.");
}
};

  匿名类可以继承自某个类,也可以实现某个接口。匿名类必须实现父类型中的抽象方法,至于父类型中的其他方法则视需求选择重写或不重写。如果匿名类中新增的方法不是来自父类,那么这个方法将无法访问。这是由于匿名类没有属于自己的类型,所以引用它的变量一定是父类或接口,而通过父类或接口类型的变量无法访问子类型中的方法。当然,可以定义一些内部调用的方法供其他继承自父类的方法调用。

  下面是匿名类的语法:

new 父类/接口(参数列表) {类定义};

  匿名类的语法包含以下几部分:

  • new操作符;
  • 匿名类实现的接口或者继承的类;
  • 包含了构造器参数的小括号。注意,当匿名类实现了某个接口时,由于接口没有构造器,因此使用一个空的小括号来表示;
  • 匿名类的定义体。

  就像局部类一样,匿名类也可以捕获变量。下面是几条规则:

  • 匿名类可以访问它的外部类的成员;
  • 匿名类不能访问外部范围中没有使用final修饰或不是近似final的局部变量。
  • 匿名类中会屏蔽外部范围中同名的类型。

  匿名类在成员的定义上和局部类有相同的规则:

  • 不能在匿名类中声明静态初始化器或成员接口;
  • 匿名类中可以有静态成员,前提是这个静态成员必须是常变量。

  可以在匿名类中声明以下元素:

  • 域;
  • 额外的方法(接口中没有定义的方法);
  • 非静态初始化块;
  • 局部类。

  不能在匿名类中定义构造方法。

Java基础教程(14)--嵌套类的更多相关文章

  1. Java基础教程(19)--Object类

      Object类位于类结构树的最顶端,所有的类都是它的直接或间接子类,因此所有的类都继承了Object类的方法,我们可以在需要的时候覆盖这些方法.下面是一些将会在本文中讨论的Object类的方法: ...

  2. java基础之 内部类 & 嵌套类

    参考文档: 内部类的应用场景 http://blog.csdn.net/hivon/article/details/606312 http://wwty.iteye.com/blog/338628 定 ...

  3. Java基础教程——Random随机数类

    Random类 java.util.Random类用于产生随机数.需要导入包: import java.util.Random; 方法 解释 Random() 创建一个Random类对象 Random ...

  4. Java基础教程(12)--深入理解类

    一.方法的返回值   当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行.当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行: 执行完方法中所有的语句: 遇到return语句: ...

  5. Java基础教程(18)--继承

    一.继承的概念   继承是面向对象中一个非常重要的概念,使用继承可以从逻辑和层次上更好地组织代码,大大提高代码的复用性.在Java中,继承可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法. ...

  6. Java基础-集合的嵌套

    Java基础-集合的嵌套 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.静态导入 静态导入是在JDK1.5后的新特性,可以减少开发的代码量,但是实际用处是很一般,静态导入的标准 ...

  7. Java基础教程:内部类

    Java基础教程:内部类 内部类 内部类,是指在一个类的内部定义的类.就像下面这样: public class EnclosingClass {   . . .   public class Nest ...

  8. Java基础教程:注解

    Java基础教程:注解 本篇文章参考的相关资料链接: 维基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3 注解基础与高级应用:http: ...

  9. Java基础教程:网络编程

    Java基础教程:网络编程 基础 Socket与ServerSocket Socket又称"套接字",网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个s ...

随机推荐

  1. 《JavaScript》高级程序设计第7章 函数表达式

    7.2 闭包 定义: 闭包是指有权访问另一个函数作用域中的变量的函数. 理解闭包: 作用域链: 当某个函数被调用时,会创建一个执行环境以及相应的作用域链. 作用域链中,外部函数的活动对象始终处于第二位 ...

  2. TCP BBR - 一键开启脚本

    这是秋水逸冰提供的TCP BBR一键开启脚本,脚本详细说明地址: https://teddysun.com/489.html 按照说明操作就可以了,注意问题:这个脚本获取有的时候可能有问题,如果提示脚 ...

  3. Kafka运行环境优化实践

    Kafka高性能的特点及条件 Kafka是一个高吞吐量分布式消息中间件,并且提供了消息的持久化功能.其高可行有两个重要的特点: 利用了磁盘连续读写性能显著高于随机读写性能的特点 并发,将一个topic ...

  4. spring json的支持

    在spring中可以通过配置来实现对json的支持: 以下连接是看到的一篇对这方面内容讲解比较好的文章 http://www.cnblogs.com/fangjian0423/p/springMVC- ...

  5. 初次学习Vue,输出Hello Vue!

    Vue.js作为目前比较流行的js框架,而我却迟迟没有接触,深感不安! 使用vue之前先要下载vue.js文件,然后在html里面导入vue.js文件,下面试着输出"Hello Vue!&q ...

  6. Grid++Report——打印功能

    一.安装下载 http://www.rubylong.cn/Download.htm 二.添加引用 三.添加类 四.制作打印模板 1.新增报表节 新增明细网格 新增列→设置为自由格→调整大小 报表→设 ...

  7. 【数组】Product of Array Except Self

    题目: iven an array of n integers where n > 1, nums, return an array output such that output[i] is ...

  8. Postman—测试脚本

    前言 对于Postman中的每个请求,我们都可以使用JavaScript语言来开发测试脚本.这也就好比单元测试.我们先看看Postman的相关界面: 编写测试脚本 Postman测试脚本本质上是在发送 ...

  9. ES6-Object

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. n-grama

    一.N-Gram的原理(这个词出现在句子中出现的概率) N-Gram是基于一个假设:第n个词出现与前n-1个词相关,而与其他任何词不相关.(这也是隐马尔可夫当中的假设.)整个句子出现的概率就等于各个词 ...