在类内部可定义成员变量和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)。
* 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的`.class`文件,前面冠以外部类的类名和`$`符号。
* 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
* 内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。
## 1、成员内部类
成员内部类(实例内部类)是指没有用 static 修饰的内部类,有的地方也称为非静态内部类。示例如下:
```
public class Out {
class Inner {}
}
```
* 在外部类的静态方法和外部类以外的其他类中,必须通过外部类的实例创建内部类的实例。
```
public class Outer {
class Inner {}
Inner inner = new Inner(); // 不需要创建外部类实例
public void method1() {
Inner i = new Inner(); // 不需要创建外部类实例
}
public static void method2() {
Inner i = new Outer().new Inner(); // 需要创建外部类实例
}
}
```
* 在实例内部类中,可以访问外部类的所有成员。
```
public class Outer {
public int a = 100;
static int b = 100;
final int c = 100;
private int d = 100;
public String method3() {
return "实例方法";
}
public static String method4() {
return "静态方法";
}
class Inner {
int a2 = a + 1; // 访问 public 的 a
int b2 = b + 1; // 访问 static 的 b
int c2 = c + 1; // 访问 final 的 c
int d2 = d + 1; // 访问 private 的 d
String str1 = method1(); // 访问实例方法method1
String str2 = method2(); // 访问静态方法method2
}
public static void main(String[] args) {
Inner in = new Outer().new Inner();
System.out.println(in.a2); // 输出 101
System.out.println(in.b2); // 输出 101
System.out.println(in.c2); // 输出 101
System.out.println(in.d2); // 输出 101
System.out.println(in.str1); // 输出实例方法
System.out.println(in.str2); // 输出静态方法
}
}
```
* 在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问。如果类 A 包含内部类 B,类 B 中包含内部类 C,则在类 A 中不能直接访问类 C,而应该通过类 B 的实例去访问类 C。
* 外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,而一个外部类实例则可以对应多个内部类实例。
```
public class Outer {
int a = 10;
class Inner {
int a = 20;
int a1 = this.a;
int b3 = Outer.this.a;
}
}
```
* 在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 修饰。
【选择】已知外部类 Out 中含有成员内部类 Inner,在主方法中怎么获取内部类 Inner 的实例化对象 inner()(选择两项)
```
A Out o = new Out(); Out.Inner inner = o.new Out();
B Out o = new Out(); Out.Inner inner = o.new Inner();
C Out.Inner inner = new Inner();
D Out.Inner inner = new Out().new Inner();
```
## 2、静态内部类
静态内部类是指使用 static 修饰的内部类。示例代码如下:
```
public class Outer {
static class Inner {} // 静态内部类
}
```
* 在创建静态内部类的实例时,不需要创建外部类的实例。
```
class OtherClass {
Outer.Inner oi = new Outer.Inner();
}
```
* 静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。
```
public class Outer {
static class Inner {
int a = 0;
static int b = 0;
}
}
class OtherClass {
Outer.Inner oi = new Outer.Inner();
int a2 = oi.a; // 访问实例成员
int b2 = Outer.Inner.b; // 访问静态成员
}
```
* 静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。
```
public class Outer {
int a = 0;
static int b = 0;
static class Inner {
Outer o = new Outer();
int a2 = o.b; // 访问实例变量
int b2 = b; // 访问静态变量
}
}
```
## 3、方法内部类
局部内部类(方法内部类)是指在一个方法中定义的内部类。示例代码如下:
```
public class Test {
public void method() {
class Inner {} // 局部内部类
}
}
```
* 局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
* 局部内部类只在当前方法中有效。
```
public class Test {
Inner i = new Inner(); // 编译出错
Test.Inner ti = new Test.Inner(); // 编译出错
Test.Inner ti2 = new Test().new Inner(); // 编译出错
public void method() {
class Inner {}
Inner i = new Inner();
}
}
```
* 局部内部类中不能定义 static 成员。
* 局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
* 在局部内部类中可以访问外部类的所有成员。
* 在局部内部类中可以直接访问当前方法中的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用`外部类.this.变量`的形式访问外部类中的成员。
```
public class Test {
int a = 0;
int d = 0;
public void method() {
int b = 0;
final int c = 0;
final int d = 10;
class Inner {
int a2 = a; // 访问外部类中的成员
int b2 = b; // 访问外部类中的成员
int c2 = c; // 访问方法中的成员
int d2 = d; // 访问方法中的成员
int d3 = Test.this.d; //访问外部类中的成员
}
Inner i = new Inner();
System.out.println(i.d2); // 输出 10
System.out.println(i.d3); // 输出 0
}
}
```
【选择】关于下列代码,说法正确的是()(选择一项)
```
public class OutClass {
public Object info() { // (1)
public static class FunInner { // (2)
public String print() {
return "方法内部类";
}
}
return new FunInner().print(); // (3)
}
}
```
```
A 代码编译成功
B 代码编译错误,错误发生在位置一
C 代码编译错误,错误发生在位置二
D 代码编译错误,错误发生在位置三
```
## 4、匿名内部类
匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。其语法形式如下:
```
new <类或接口>() {
// ....
}
```
这种形式的 new 语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。使用匿名类可使代码更加简洁、紧凑,模块化程度更高。
匿名类有两种实现方式:
* 继承一个类,重写其方法。
* 实现一个接口(可以是多个),实现其方法
```
public class Out {
void show() {
System.out.println("调用 Out 类的 show() 方法");
}
}
public class Test {
public static void main(String[] args) {
Out o = new Out() {
void show() {
System.out.println("调用匿名类中的 show() 方法");
}
}
o.show(); // 调用匿名类中的 show() 方法
}
}
```
* 匿名类和局部内部类一样,可以访问外部类的所有成员。
```
public static void main(String[] args) {
int a = 10;
final b = 10;
Out o = new Out() {
void show() {
System.out.println(a); // 编译通过
System.out.println(b); // 编译通过
}
}
o.show();
}
```
* 匿名类中允许使用非静态代码块进行成员初始化操作。
```
Out o = new Out() {
int i;
{
i = 10;
}
public void show() {
System.out.println("i");
}
}
```
* 匿名类的非静态代码块会在父类的构造方法之后被执行。
【选择】下面截取了一段匿名内部类的代码,关于这段代码,下面说法正确的是()(选择两项)
```
test.show(new Example() { // (1)
public static int x; // (2)
public abstract void show(); // (3)
public void info() { // (4)
System.out.println("匿名内部类");
}
});
```
```
A 位置一错误 B 位置二错误 C 位置三错误 D 位置四错误
```
【编程】分别通过成员内部类、方法内部类、匿名内部类完成接口 Ball,在测试类 BallTest 的调用。程序参考运行效果图如下:
```
成员内部类:
打篮球
*************
方法内部类:
打乒乓球
*************
匿名内部类:
打排球
```
```
public class Test {
public static void main(String[] args) {
// 1. 完成成员内部类内部测试
// 2. 完成方法内部类测试
// 3. 完成匿名内部类测试
}
}
public interface Ball {
// 抽象方法 play()
}
public class BallTest {
// 创建成员内部类 Inner_m
// 创建方法内部类
// 定义一个方法 void playBall(Ball ball),调用了 play() 方法
}
```
- 阶段一 Java 零基础入门
- 步骤1:基础语法
- 第01课 初识
- 第02课 常量与变量
- 第03课 运算符
- 第04课 选择结构
- 第05课 循环结构
- 第06课 一维数组
- 第08课 方法
- 第09课 数组移位与统计
- 第10课 基础语法测试
- 第09课 基础语法测试(含答案)
- 步骤2:面向对象
- 第01课 类和对象
- 第02课 封装
- 第03课 学生信息管理
- 第04课 继承
- 第05课 单例模式
- 第06课 多态
- 第07课 抽象类
- 第08课 接口
- 第09课 内部类
- 第10课 面向对象测试
- 第10课 面向对象测试(含答案)
- 步骤3:常用工具类
- 第01课 异常
- 第02课 包装类
- 第03课 字符串
- 第04课 集合
- 第05课 集合排序
- 第06课 泛型
- 第07课 多线程
- 第08课 输入输出流
- 第09课 案例:播放器
- 第10课 常用工具测试(一)
- 第10课 常用工具测试(一)(答案)
- 第10课 常用工具测试(二)
- 第10课 常用工具测试(二)(答案)
- 阶段二 从网页搭建入门 JavaWeb
- 步骤1:HTML 与 CSS
- 第01课 HTML 入门
- 第01课 HTML 入门(作业)
- 第02课 CSS 入门
- 第02课 CSS 入门(作业)
- 第03课 CSS 布局
- 第03课 CSS 布局(作业)
- 步骤2:JavaScript 与前端案例
- 第01课 JavaScript 入门
- 第01课 JavaScript 入门(作业)
- 第02课 仿计算器
- 第03课 前端油画商城案例
- 第04课 轮播图
- 第05课 网页搭建测试
- 第05课 网页搭建测试(含答案)
- 步骤3:JavaScript 教程
- 入门
- 概述
- 基本语法
- 数据类型
- 概述
- 数值
- 字符串
- undefined, null 和布尔值
- 对象
- 函数
- 数组
- 运算符
- 算术运算符
- 比较运算符
- 布尔运算符
- 位运算符
- 运算顺序
- 语法专题
- 数据类型的转换
- 错误处理机制
- 标准库
- String
- Date
- Math
- DOM
- 概述
- Document 节点
- 事件
- EventTarget 接口
- 事件模型
- 常见事件
- 阶段三 数据库开发与实战
- 步骤1:初始数据库操作
- 第01课 数据类型
- 第02课 表的管理
- 第03课 数据管理
- 第04课 常用函数
- 第05课 JDBC 入门
- 第06课 Java 反射
- 第07课 油画商城
- 第08课 数据库基础测试
- 步骤2:MyBatis 从入门到进阶
- 第01课 IntelliJ IDEA 开发工具入门
- 第02课 Maven 入门
- 第03课 工厂模式
- 第04课 MyBatis 入门
- 第05课 MyBatis 进阶
- 第06课 商品信息管理
- 第07课 MyBatis 基础测试
- 步骤3:Redis 数据库与 Linux 下项目部署
- 第01课 Linux 基础
- 第02课 Linux 下 JDK 环境搭建及项目部署
- 第03课 Redis 入门
- 阶段四 SSM 到 Spring Boot 入门与综合实战
- 步骤1:Spring 从入门到进阶
- 第01课 Spring 入门
- 第02课 Spring Bean 管理
- 第03课 Spring AOP
- 第04课 基于 AspectJ 的 AOP 开发
- 第05课 JDBC Template
- 第06课 Spring 事务管理
- 第07课 人员管理系统开发
- 第08课 Spring 从入门到进阶测试
- 步骤2:Spring MVC 入门与 SSM 整合开发
- 第01课 Spring MVC 入门与数据绑定
- 第02课 Restful 风格的应用
- 第03课 SpringMVC 拦截器
- 第04课 办公系统核心模块
- 步骤3:Spring Boot 实战
- 第01课 Spring Boot 入门
- 第02课 校园商铺项目准备
- 第03课 校园商铺店铺管理
- 第04课 校园商铺商品管理及前台展示
- 第05课 校园商铺框架大换血
- 步骤4:Java 面试
- 第01课 面试准备
- 第02课 基础面试技巧
- 第03课 Web基础与数据处理
- 第04课 主流框架