java基础(十三)-----详解内部类——Java高级开发必须懂的

目录

  • 为什么要使用内部类
  • 内部类基础
  • 静态内部类
  • 成员内部类
    • 成员内部类的对象创建
    • 继承成员内部类
  • 局部内部类
  • 推荐博客
  • 匿名内部类

正文

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

回到顶部

为什么要使用内部类

  为什么要使用内部类?在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

public interface Father {}public interface Mother {}public class Son implements Father, Mother {}public class Daughter implements Father{class Mother_ implements Mother{}
}

其实对于这个实例我们确实是看不出来使用内部类存在何种优点,但是如果Father、Mother不是接口,而是抽象类或者具体类呢?这个时候我们就只能使用内部类才能实现多重继承了。

其实使用内部类最大的优点就在于它能够非常好的解决多重继承的问题,但是如果我们不需要解决多重继承问题,那么我们自然可以使用其他的编码方式,但是使用内部类还能够为我们带来如下特性(摘自《Think in java》):

1、内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。

2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

3、创建内部类对象的时刻并不依赖于外围类对象的创建。

4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

回到顶部

内部类基础

  在这个部分主要介绍内部类如何使用外部类的属性和方法,以及使用.this与.new。

当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。

public class OuterClass {private String name ;private int age;/**省略getter和setter方法**/public class InnerClass{public InnerClass(){name = "chenhao";age = 23;}public void display(){System.out.println("name:" + getName() +"   ;age:" + getAge());}}public static void main(String[] args) {OuterClass outerClass = new OuterClass();OuterClass.InnerClass innerClass = outerClass.new InnerClass();innerClass.display();}
}
--------------
Output:
name:chenhao   ;age:23

在这个应用程序中,我们可以看到内部了InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管它是private修饰的。这是因为当我们在创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,就会用这个引用来选择外围类的成员。

其实在这个应用程序中我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类:OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。当然这点实在编译期就知晓了,没有任何运行时的成本。

public class OuterClass {public void display(){System.out.println("OuterClass...");}public class InnerClass{public OuterClass getOuterClass(){return OuterClass.this;}}public static void main(String[] args) {OuterClass outerClass = new OuterClass();OuterClass.InnerClass innerClass = outerClass.new InnerClass();innerClass.getOuterClass().display();}
}
-------------
Output:
OuterClass...

到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。

在Java中内部类主要分为静态内部类、成员内部类、局部内部类、匿名内部类。

回到顶部

静态内部类

静态内部类: 一般也称”静态嵌套类“,在类中用static声明的内部类。

因为是static,所以不依赖于外围类对象实例而独立存在,静态内部类的可以访问外围类中的所有静态成员,包括private的静态成员,但是它不能使用任何外围类的非static成员变量和方法。

同时静态内部类可以说是所有内部类中独立性最高的内部类,其创建对象、继承(实现接口)、扩展子类等使用方式与外围类并没有多大的区别。

public class OuterClass {//外围类public int aa; //实例成员private static float f = 1.5f;//private的静态成员static void println() {System.out.println("这是静态方法");}protected static class StaticInnerClass{//protected的静态内部类float a;public StaticInnerClass() {a = f;// 外围类的private静态变量println();//外围类的静态方法
        }}
}
class OtherClass{public static void main(String[] args) {//创建静态内部类的对象OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass(); }
}

回到顶部

成员内部类

成员内部类: 定义在类的内部,而且与成员方法、成员变量同级,即也是外围类的成员之一,因此 成员内部类 与 外围类 是紧密关联的。

注意:
这种紧密关联指的是,成员内部类的对象的创建必须依赖于外围类的对象(即没有外围类对象,就不可能创建成员内部类)。因此,成员内部类有以下3个特点:

  • 成员内部类可以访问外围类的所有成员,包括私有成员;

  • 成员内部类是不可以声明静态成员(包括静态变量、静态方法、静态成员类、嵌套接口),但有个例外---可以声明 static final的变量, 这是因为编译器对final类型的特殊处理,是直接将值写入字节码;

  • 成员内部类对象都隐式地保存了一个引用,指向创建它的外部类对象;或者说,成员内部类的入口是由外围类的对象保持着(静态内部类的入口,则直接由外围类保持着)

成员内部类中的 this,new关键字:

  • 获取外部类对象:OuterClass.this

  • 明确指定使用外部类的成员(当内部类与外部类的名字冲突时):
    OuterClass.this.成员名

  • 创建内部类对象的new:外围类对象.new

//先创建外围类对象
OuterClass outer=new OuterClass();
//创建成员内部类对象
OuterClass.InnerClass inner=outer.new InnerClass();

成员内部类的对象创建

  我们知道,成员内部类就像外围类的实例成员一样,一定要存在对象才能访问,即成员内部类必须绑定一个外围类的对象。上面已经介绍了成员内部类的创建格式了,我们直接看一个例子

public class OuterClass {//外围类public int aa; //实例成员private static float f = 1.5f;//private的静态成员public void initInnerClass() {System.out.println("内部类的初始化方法");}public void createInnerClass() {////外围类的成员方法中创建成员内部类对象InnerClass innerClass = new InnerClass();}class InnerClass{//成员内部类private double aa; //与围类的变量aa的名字重复public InnerClass(){this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
            initInnerClass();}}
}
//其他类
class OtherClass{public static void main(String[] args) {//其他类中创建成员内部类OuterClass oc = new OuterClass();//外部类对象//创建内部类对象OuterClass.InnerClass innerClass = oc.new InnerClass();}
}

补充几点:

  • 成员内部类可以继续包含成员内部类,而且不管一个内部类被嵌套了多少层,它都能透明地访问它的所有外部类所有成员;

  • 成员内部可以继续嵌套多层的成员内部类,但无法嵌套静态内部类;静态内部类则都可以继续嵌套这两种内部类。

class InnerClass{//成员内部类private double aa; //与围类的变量aa的名字重复public InnerClass(){this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
        initInnerClass();}public  class InnerInnerCalss2{//成员内部类中的成员内部类protected double aa = OuterClass.this.aa;//最外层的外围类的成员变量}//InnerInnerCalss2
}//InnerClass

继承成员内部类

在内部类的访问权限允许的情况下,成员内部类也是可以被继承的。由于成员内部类的对象依赖于外围类的对象,或者说,成员内部类的构造器入口由外围类的对象把持着。因此,继承了成员内部类的子类必须要与一个外围类对象关联起来。同时,子类的构造器是必须要调用父类的构造器方法,所以也只能通过父类的外围类对象来调用父类构造器。

下面的例子也是基于上面的例子的,只贴出多出的部分代码。

class ChildClass extends OuterClass.InnerClass{//成员内部类的子类的构造器的格式public ChildClass(OuterClass outerClass) {outerClass.super();//通过外围类的对象调用父类的构造方法
    }
}

回到顶部

局部内部类

局部内部类: 就是在方法、构造器、初始化块中声明的类,在结构上类似于一个局部变量。因此局部内部类是不能使用访问修饰符。

局部内部类的两个访问限制:

  • 对于局部变量,局部内部类只能访问final的局部变量。不过,后期JDK(忘了是JDK几了)局部变量可不用final修饰,也可以被局部内部类访问,但你必须时刻记住此局部变量已经是final了,不能再改变。

  • 对于类的全局成员,局部内部类定义在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员。

public class OuterClass {private int a = 21;static {//静态域中的局部内部类class LocalClass1{//  int z = a; //错误,在静态的作用域中无法访问对象成员
       }}{//实例初始化块中的局部内部类class localClass2{          }}public OuterClass(){int x = 2;final int y = 3;// x = 3;//若放开此行注释,编译无法通过,因为局部变量x已经是final类型//构造器中的局部内部类class localClass3{int z = y; //可以访问final的局部变量int b = a;//可以访问类的所有成员//访问没有用final修饰的局部变量int c = x;}}public void createRunnable() {final int x = 4;//方法中的局部内部类class LocalClass4 implements Runnable {//
            @Overridepublic void run() {System.out.println("局部final变量:"+x);System.out.println("对象成员变量:"+a);}}}
}

回到顶部

推荐博客

  程序员写代码之外,如何再赚一份工资?

回到顶部

匿名内部类

匿名内部类: 与局部内部类很相似,只不过匿名内部类是一个没有给定名字的内部类,在创建这个匿名内部类后,便会立即用来创建并返回此内部类的一个对象引用。

作用:匿名内部类用于隐式继承某个类(重写里面的方法或实现抽象方法)或者实现某个接口。

匿名内部类的访问限制: 与局部内部类一样,请参考局部内部类;

匿名内部类的优缺点:
优点: 编码方便快捷;
缺点:

  • 只能继承一个类或实现一个接口,不能再继承其他类或其他接口。

  • 只能用于创建一次对象实例;

下面的例子是我们创建线程时经常用到的匿名内部类的方式来快速地创建一个对象的例子:

class MyOuterClass {private int x = 5;void createThread() {final int a = 10;int b = 189;// 匿名内部类继承Thread类,并重写Run方法Thread thread = new Thread("thread-1") {int c = x;  //访问成员变量int d = a;  //final的局部变量int e = b; //访问没有用final修饰的局部变量
            @Overridepublic void run() {System.out.println("这是线程thread-1");}};// 匿名内部类实现Runnable接口Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("线程运行中");}};}
}
原文地址https://www.cnblogs.com/java-chen-hao/p/10412677.html

java基础(十三)-----详解内部类——Java高级开发必须懂的相关推荐

  1. Java基础——匿名内部类详解及举例

    Java基础--匿名内部类详解及举例 说明:定义在外部类的局部位置,比如方法中,并且没有类名 匿名内部类的使用 匿名内部类的基本语法 new 类或接口(参数列表){类体 }; 用途:有些时候为了简化开 ...

  2. Java类加载机制详解【java面试题】

    Java类加载机制详解[java面试题] (1)问题分析: Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数 ...

  3. Java基础:详解static关键字与类加载顺序

    1. 前言 前文中说到了static关键字,在Java中这是一个很重要的关键字,它有很多的用法,并且在某些特定的情况下使用可以优化程序的性能.本文学习static关键字的应用场景.在这之前了解变量的类 ...

  4. JAVA基础——异常详解

    阅读目录 一.异常简介 二.try-catch-finally语句 三.throw和throws关键字 四.java中的异常链 五.结束语 JAVA异常与异常处理详解 回到顶部 一.异常简介 什么是异 ...

  5. java图形界面详解_JAVA 图形界面开发基础详解

    与C的win32一样,JAVA也有自己的图形界面开发,将在此篇博客中对基础部分进行讲解. 1.Java提供的图形界面类有哪些? Java提供了两套图形界面 (1)AWT组建(基础) AWT组件是jdk ...

  6. java 矩阵求逆_详解用java描述矩阵求逆的算法

    今天很开心把困扰几天的问题解决了,在学习线性代数这门课程的时候.想通过程序实现里面的计算方法,比如矩阵求逆,用java代码该如何描述呢? 首先,咱们先用我们所交流语言描述一下算法思路: 1.求出一个矩 ...

  7. Java基础知识详解

    一.基础 1.Java都有哪些数据类型?基本数据类型有哪些?分别占多少字节?多少位?引用数据类型又有哪些? Java的数据类型:基本数据类型和引用数据类型 基本数据类型:byte(1),short(2 ...

  8. Java基础--awt详解以及简单应用

    GUI 图形用户界面 CLI 命令行用户接口 Java为GUI提供的对象存在java.Awt和Javax.Swing两个包中. Java当中如何完成图形化界面的制作呢? AWT:abstract Wi ...

  9. Java基础——IO详解

    文章目录 前言 一.IO概述 1.IO体系图 2.IO概念及基本操作 二.IO--字节流 InputStream(字节输入流) 1.FileInputStream 2.BuffedInputStrea ...

最新文章

  1. 正向最大匹配 和逆向最大匹配对比比较
  2. 女生学编程有哪些好处呢?
  3. 【总结整理】关于IE6的兼容性
  4. Vue — 第二天(v-model和过滤器)
  5. JUC 中断线程的3种方式
  6. Matplotlib 命令总结
  7. python遍历目录树 除文件_python遍历目录树删除指定后缀的文件
  8. spring 监听器
  9. es 多索引联合查询_HBase二级索引设计思想
  10. sql插入后返回id
  11. 权威服务器和域名注册,域名服务器简介
  12. 使用javaw在windows上部署jar包在后台运行,并关闭
  13. 怎样在mac系统里将文件拷贝到移动硬盘教程
  14. 在fpga中用Cordic算法来产生正弦函数
  15. 邮箱发信数量被限制了,有什么方法可以解决么?
  16. 磁盘阵列 and RAID技术简介
  17. BZOJ1721 Ski Lift 缆车支柱
  18. mysql .zip 包简易安装
  19. Mysql Data types in summary.
  20. 【泛函分析】Riemann-Stieltjes 积分

热门文章

  1. 用“逐步排除”的方法定位Java服务线上“系统性”故障
  2. ImageNet图像数据集介绍
  3. 一维码Codabar简介及其解码实现(zxing-cpp)
  4. Eclipse中通过Android模拟器调用OpenGL ES2.0函数操作步骤
  5. 3650服务器性能,全新联想System x3650 M4服务器性能出色
  6. js中every用法_every方法
  7. 计算机专业期末考试是编程序,计算机专业技能期末考试题
  8. matlab一元线性回归分析_建模开讲:一元线性回归分析及SPSS软件实现
  9. 网络新手ip隐藏器_动态IP代理的用途
  10. python如何调用文件_如何调用另一个python文件中的代码