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

时间:2021-11-26 22:39:56

编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致;非public得,无强制要求)。如果想把多个java类放在一个java文件中,则只能有一个public类。如下面的两个类放在同一个文件中就会报错,无法编译通过。

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

可以看出,因为TestOne.java文件中已经有一个public类TestOne,这时再加了一个public类TestTwo就报错了。如果将类TestTwo前面的public修饰符去掉就没有问题了。

我们下面介绍内部类的概念和使用,所谓内部类,简单的说,就是一个类定义在另一个类的内部。与上面的两个类在同一个文件中不同(TestOne和TestTwo虽然在一个文件中,但相互没有嵌套,是并列的)。

采用内部类,有时会对代码的可读性带来一点问题,但很多场景下还是很有必要的。尤其在使用一些框架(如java的swing,集合框架中的排序操作)等,使用内部类(尤其是匿名内部类)会带来很多便利;再比如我们在开发Android app时,就会大量的使用内部类,如果不了解内部类的含义和使用规则,几乎无法顺利的进行Android app的开发。

内部类有其特别的地方,其中核心之一是,内部类实例可以访问包含它的外部类的所有成员,包括private成员。另外非常关键的一点是,内部类的使用必须与一个外部类的实例绑定,注意是实例,后面的例子中会说明这点。

内部类也分好几种情况,下面一一来解释。

一、一般内部类

例1:TestTwo就是一个内部类,它可以直接访问外部类中的私有成员a。

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
new TestOne().call();
} public void call(){
TestTwo two = new TestTwo();
two.show();
} class TestTwo {
public void show(){
System.out.println(a);
}
}
}

 例2:如果需要在外部类的静态方法中使用内部类,必须创建一个外部类的实例,通过该实例来new内部类。不能直接new,否则会报编译错误。

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
TestOne testOne = new TestOne();
TestTwo testTwo = testOne.new TestTwo();
testTwo.show();
} public void call(){
TestTwo two = new TestTwo();
two.show();
} class TestTwo {
public void show(){
System.out.println(a);
}
}
}

 例3:如何在内部类中引用外部类的实例。有两种方法,一是普通的方法,在new内部类时,将外部类的this作为内部类的构造函数参数传给内部类;还有一种更简单的方法,直接在内部类中通过 "外部类类名.this" 就可以使用外部类的实例。如下面代码:

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
new TestOne().call();
} public void call(){
new TestTwo(this).show();
} class TestTwo {
private TestOne one; public TestTwo(TestOne one){
this.one = one;
}
public void show(){
System.out.println(a);
System.out.println(one.a);
System.out.println(TestOne.this.a);
}
}
}

上述情况下使用内部类,通常该内部类只是被该外部类使用,这样可以对内部类起到隐藏作用,并且跟外部类绑定的更紧密。

二、方法内的内部类

就是内部类定义在一个方法的内部。这种情况下,只有在该方法内才能使用该内部类;内部类也可以访问外部类的成员;但注意不能访问包含它的方法的局部变量(包括参数),除非方法内的变量(或参数)是final的。 举例:

public class TestOne {
private int a = 2; public static void main(String[] args) {
new TestOne().call();
} public void call(){
final int b=3;
class TestTwo {
private TestOne one; public TestTwo(TestOne one){
this.one = one;
}
public void show(){
System.out.println(a);
System.out.println(one.a);
System.out.println(TestOne.this.a);
System.out.println(b);
}
}
new TestTwo(this).show();
}
}

  需要注意的是,如果是在静态方法中定义内部类,则该内部类只能使用外部类的静态成员。

三、匿名内部类

匿名内部类,其实也分很多情况。最常见的情况是,直接通过父类(比如抽象类)创建子类的实例,或直接通过接口创建对象,该对象可传递作为方法的参数,或者赋值给某个变量。实际上在内部类的使用中,匿名类是用途最广泛的。

因为我们都知道,在java中,一般情况下,不像c/c++一样,我们可以把一个方法作为一个变量进行传递或引用。

我们只能传递和引用java对象。比如我们定义了一个方法,有个参数是接口类型或抽象类,这样不使用匿名内部类,我们每次都需要先定义一个类实现该接口或抽象类,再创建该类的实例传递给该方法。当接口或抽象类中需要实现的方法数量比较少时(比如只有1个),这个先创建类,再使用就显的比较啰嗦了。而且这种场景下,往往创建的类就这一个地方使用。而通过匿名内部类,就可以让编写的代码简洁很多。 实际上,java本身提供的一些api(如多线程、io、集合框架等)就大量的使用了这个特性。 我们来看几个例子:

例1:通过接口创建对象

public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.fun1();
testThread.fun2();
}
public void fun1(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("fun1");
}
};
new Thread(runnable).start();
} public void fun2(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("fun2");
}
}).start();
}
}

可以看出,方法fun1中直接通过Runnable接口创建了对象,供Thread类使用。fun2更简化了下,直接连变量名也省掉了,对象的创建直接在传入参数时创建。

例2:直接创建父类的子类对象

public class TestChild {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
System.out.println("thread is run");
}
}.start(); }
}

  可以看出,Thread类有一个run方法,但这个run方法是空的,我们如果要用继承Thread类来实现多线程,必须在子类中重载run方法。上面的代码我们实际上没有显示的定义一个Thread的子类,而是直接通过上述形式创建了一个线程实例,并启动它。

四、静态嵌套类(内部类)

也就是在一个类中定义一个静态类(注意不能在方法中定义)。 与前面介绍的内部类不同。这个嵌套类只是命名空间限制在外部类中。实质上它不需要与外部类的实例绑定,也无法访问外部类的实例成员,只能访问外部类的静态成员。代码如:

public class TestOne {
private int a = 2;
static private int b = 2; public static void main(String[] args) {
TestTwo testTwo = new TestTwo();
} public void call(){
TestTwo testTwo = new TestTwo();
} static class TestTwo {
public void show(){
System.out.println(b);
}
}
} class TestThree{
public static void main(String[] args) {
TestOne.TestTwo testTwo = new TestOne.TestTwo();
} public void call(){
TestOne.TestTwo testTwo = new TestOne.TestTwo();
}
}

可以看出,TestTwo的使用不需要依赖外部类TestOne的实例。

上面介绍了内部类的几种情况,使用的时候稍加注意即可。  

Java学习笔记:内部类/匿名内部类的全面介绍的更多相关文章

  1. Java学习笔记-内部类

    内部类在Android中有着大量的运用 内部类 内部类提供了更好的封装:内部类可以直接访问外部类的私有数据:匿名内部类适合那些只需要使用一次的类.非静态内部类不能拥有静态成员.内部类比外部类可以多使用 ...

  2. 2)Java学习笔记:匿名内部类

    为什么要使用匿名内部类 ①如果以前的类有一些缺陷,只是想在某一个模块进行修复,可以在引用该类的地方使用匿名内部类,在overRide方法进行修复. ②如果一个类,需要派生出很多类,而且这些类大多只是在 ...

  3. Java学习笔记——内部类及其调用方法

    一.static内部类的static方法 public class Test0719_Inner_Test { public static void main(String[] args) { //s ...

  4. 【Java学习笔记之二十六】深入理解Java匿名内部类

    在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意 ...

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

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

  6. java学习笔记(1)java的基础介绍 、JDK下载、配置环境变量、运行java程序

    java工程师是开发软件的 什么是软件呢? 计算机包括两部分: 硬件: 鼠标.键盘.显示器.主机箱内部的cpu.内存条.硬盘等 软件: 软件包括:系统软件和应用软件 系统软件:直接和硬件交互的软件:w ...

  7. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  8. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  9. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  10. java学习笔记9--内部类总结

    java学习笔记系列: java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对 ...

随机推荐

  1. js闭包使用

    闭包就是在一个函数内定义一个内部函数 并返回内部函数 function f1(){ var a=1; add=function(){a=a+1;} function f1Sub(){ console. ...

  2. 洛谷⑨月月赛Round2 P3392涂国旗&lbrack;DP&rsqb;

    题目描述 某国法律规定,只要一个由N*M个小方块组成的旗帜符合如下规则,就是合法的国旗.(毛熊:阿嚏——) 从最上*干行(>=1)的格子全部是白色的. 接下来若干行(>=1)的格子全部是 ...

  3. CLR内存管理

    CLR管理内存的区域,主要有三块,分别为: 1.线程的堆栈:(在程序应该编译过程为值类型实例分配好内存) 用于分配值类型实例.堆栈主要由操作系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时 ...

  4. robotframework笔记11

    测试用例的语法 基本语法 测试用例构造测试用例表中可用 关键词. 关键字可以进口 测试库 或 资源 文件 或创建的 关键字表 的测试用例文件 本身. 测试用例表中第一列包含测试用例的名称. 一个 测试 ...

  5. ASP&period;NET MVC 学习第一天

    今天开始第一天学习asp.net mvc,写的不是很好,高手不要喷,希望大家能一起进步学习. 好了,开始学习 新建项目,选择mvc 4应用程序 接下来选择基本,视图引擎当然要选择Razor,如果在选择 ...

  6. 让你系统认识flume及安装和使用flume1&period;5传输数据到hadoop2&period;2

    本文链接: http://www.aboutyun.com/thread-7949-1-1.html 问题导读:1.什么是flume?2.如何安装flume?3.flume的配置文件与其它软件有什么不 ...

  7. 【C&plus;&plus;基金会 04】vector详细解释

    根据写博客开始总有一些事情的习惯,加鸡汤文,今天请原谅我记得. ============================================= 今天要写的内容是顺序型容器.首先,标准库定义 ...

  8. Django中模板使用

    第一步:配置 1.在工程中创建模板目录templates. 2.在settings.py配置文件中修改TEMPLATES配置项的DIRS值:TEMPLATES = [ { 'BACKEND': 'dj ...

  9. Problem F&colon; 平面上的点——Point类 &lpar;VI&rpar;

    Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定.现在我们封装一个“Point类”来实现平面上的点的操作. 根据“append.cc”,完成Point类的构造方 ...

  10. Linux - 服务基础

    /etc/init.d/sendmail start # 启动服务 /etc/init.d/sendmail stop # 关闭服务 /etc/init.d/sendmail status # 查看服 ...