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

内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人对内部类也只是略知一二)。

第一次见面

内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类。

 1 public class OuterClass {
 2     private String name ;
 3     private int age;
 4
 5     public String getName() {
 6         return name;
 7     }
 8
 9     public void setName(String name) {
10         this.name = name;
11     }
12
13     public int getAge() {
14         return age;
15     }
16
17     public void setAge(int age) {
18         this.age = age;
19     }
20
21     class InnerClass{
22         public InnerClass(){
23             name = "chenssy";
24             age = 23;
25         }
26     }
27 }

在这里InnerClass就是内部类,对于初学者来说内部类实在是使用的不多,鄙人菜鸟一个同样没有怎么使用过,但是随着编程能力的提高,我们会领悟到它的魅力所在,它可以使用能够更加优雅的设计我们的程序结构。在使用内部类之间我们需要明白为什么要使用内部类,内部类能够为我们带来什么样的好处。

一、为什么要使用内部类

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

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

 1 public interface Father {
 2
 3 }
 4
 5 public interface Mother {
 6
 7 }
 8
 9 public class Son implements Father, Mother {
10
11 }
12
13 public class Daughter implements Father{
14
15     class Mother_ implements Mother{
16
17     }
18 }

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

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

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

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

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

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

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

二、内部类基础

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

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

 1 public class OuterClass {
 2     private String name ;
 3     private int age;
 4
 5     /**省略getter和setter方法**/
 6
 7     public class InnerClass{
 8         public InnerClass(){
 9             name = "chenssy";
10             age = 23;
11         }
12
13         public void display(){
14             System.out.println("name:" + getName() +"   ;age:" + getAge());
15         }
16     }
17
18     public static void main(String[] args) {
19         OuterClass outerClass = new OuterClass();
20         OuterClass.InnerClass innerClass = outerClass.new InnerClass();
21         innerClass.display();
22     }
23 }
24 --------------
25 Output:
26 name:chenssy   ;age:23

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

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

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

 1 public class OuterClass {
 2     public void display(){
 3         System.out.println("OuterClass...");
 4     }
 5
 6     public class InnerClass{
 7         public OuterClass getOuterClass(){
 8             return OuterClass.this;
 9         }
10     }
11
12     public static void main(String[] args) {
13         OuterClass outerClass = new OuterClass();
14         OuterClass.InnerClass innerClass = outerClass.new InnerClass();
15         innerClass.getOuterClass().display();
16     }
17 }
18 -------------
19 Output:
20 OuterClass...

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

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

三、成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

 1 public class OuterClass {
 2     private String str;
 3
 4     public void outerDisplay(){
 5         System.out.println("outerClass...");
 6     }
 7
 8     public class InnerClass{
 9         public void innerDisplay(){
10             //使用外围内的属性
11             str = "chenssy...";
12             System.out.println(str);
13             //使用外围内的方法
14             outerDisplay();
15         }
16     }
17
18     /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
19     public InnerClass getInnerClass(){
20         return new InnerClass();
21     }
22
23     public static void main(String[] args) {
24         OuterClass outer = new OuterClass();
25         OuterClass.InnerClass inner = outer.getInnerClass();
26         inner.innerDisplay();
27     }
28 }
29 --------------------
30 chenssy...
31 outerClass...

推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。

四、局部内部类

有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

对于局部内部类实在是想不出什么好例子,所以就引用《Think in java》中的经典例子了。

定义在方法里:

 1 public class Parcel5 {
 2     public Destionation destionation(String str){
 3         class PDestionation implements Destionation{
 4             private String label;
 5             private PDestionation(String whereTo){
 6                 label = whereTo;
 7             }
 8             public String readLabel(){
 9                 return label;
10             }
11         }
12         return new PDestionation(str);
13     }
14
15     public static void main(String[] args) {
16         Parcel5 parcel5 = new Parcel5();
17         Destionation d = parcel5.destionation("chenssy");
18     }
19 }

定义在作用域内:

 1 public class Parcel6 {
 2     private void internalTracking(boolean b){
 3         if(b){
 4             class TrackingSlip{
 5                 private String id;
 6                 TrackingSlip(String s) {
 7                     id = s;
 8                 }
 9                 String getSlip(){
10                     return id;
11                 }
12             }
13             TrackingSlip ts = new TrackingSlip("chenssy");
14             String string = ts.getSlip();
15         }
16     }
17
18     public void track(){
19         internalTracking(true);
20     }
21
22     public static void main(String[] args) {
23         Parcel6 parcel6 = new Parcel6();
24         parcel6.track();
25     }
26 }

五、匿名内部类

在做Swing编程中,我们经常使用这种方式来绑定事件

1 button2.addActionListener(
2                 new ActionListener(){
3                     public void actionPerformed(ActionEvent e) {
4                         System.out.println("你按了按钮二");
5                     }
6                 });

我们咋一看可能觉得非常奇怪,因为这个内部类是没有名字的,在看如下这个例子:

 1 public class OuterClass {
 2     public InnerClass getInnerClass(final int num,String str2){
 3         return new InnerClass(){
 4             int number = num + 3;
 5             public int getNumber(){
 6                 return number;
 7             }
 8         };        /* 注意:分号不能省 */
 9     }
10
11     public static void main(String[] args) {
12         OuterClass out = new OuterClass();
13         InnerClass inner = out.getInnerClass(2, "chenssy");
14         System.out.println(inner.getNumber());
15     }
16 }
17
18 interface InnerClass {
19     int getNumber();
20 }
21
22 ----------------
23 Output:

这里我们就需要看清几个地方

1、 匿名内部类是没有访问修饰符的。

  2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

PS:由于篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客做详细的介绍,包括为何形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……

六、静态内部类

在java提高篇-----关键字static中提到Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

1、 它的创建是不需要依赖于外围类的。

2、 它不能使用任何外围类的非static成员变量和方法。

 1 public class OuterClass {
 2     private String sex;
 3     public static String name = "chenssy";
 4
 5     /**
 6      *静态内部类
 7      */
 8     static class InnerClass1{
 9         /* 在静态内部类中可以存在静态成员 */
10         public static String _name1 = "chenssy_static";
11
12         public void display(){
13             /*
14              * 静态内部类只能访问外围类的静态成员变量和方法
15              * 不能访问外围类的非静态成员变量和方法
16              */
17             System.out.println("OutClass name :" + name);
18         }
19     }
20
21     /**
22      * 非静态内部类
23      */
24     class InnerClass2{
25         /* 非静态内部类中不能存在静态成员 */
26         public String _name2 = "chenssy_inner";
27         /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
28         public void display(){
29             System.out.println("OuterClass name:" + name);
30         }
31     }
32
33     /**
34      * @desc 外围类方法
35      * @author chenssy
36      * @data 2013-10-25
37      * @return void
38      */
39     public void display(){
40         /* 外围类访问静态内部类:内部类. */
41         System.out.println(InnerClass1._name1);
42         /* 静态内部类 可以直接创建实例不需要依赖于外围类 */
43         new InnerClass1().display();
44
45         /* 非静态内部的创建需要依赖于外围类 */
46         OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
47         /* 方位非静态内部类的成员需要使用非静态内部类的实例 */
48         System.out.println(inner2._name2);
49         inner2.display();
50     }
51
52     public static void main(String[] args) {
53         OuterClass outer = new OuterClass();
54         outer.display();
55     }
56 }
57 ----------------
58 Output:
59 chenssy_static
60 OutClass name :chenssy
61 chenssy_inner
62 OuterClass name:chenssy

上面这个例子充分展现了静态内部类和非静态内部类的区别。

到这里内部类的介绍就基本结束了!对于内部类其实本人认识也只是皮毛,认知有限!我会利用这几天时间好好研究内部类!

【Java学习笔记之二十五】初步认知Java内部类相关推荐

  1. B站韩顺平java学习笔记(二十五)-- 正则表达式章节

    目录 一  正则表达式入门 1  极速体验正则表达式威力 二  为什么要学正则表达式 三  正则表达式基本介绍 1  介绍 2  正则表达式底层实现

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

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

  3. java学习笔记(二十八)——开发一个小项目(VMeeting3.0)

    上篇文章按照较规范的产品需求文档梳理了项目的逻辑,感觉开发起来明晰了很多:挂上一篇文章java学习笔记(二十七)--开发一个小项目(VMeeting2.0)_Biangbangbing的博客-CSDN ...

  4. 【OS学习笔记】二十五 保护模式七:任务和特权级保护对应的汇编源代码

    本汇编代码是以下两篇文章讲解的内容的内核代码; [OS学习笔记]二十三 保护模式七:保护模式下任务的隔离与任务的特权级概念 [OS学习笔记]二十四 保护模式七:调用门与依从的代码段----特权级保护 ...

  5. Java学习笔记(三十五)

    在完成对C语言的学习后,我最近开始了对C++和Java的学习,目前跟着视频学习了一些语法,也跟着敲了一些代码,有了一定的掌握程度.现在将跟着视频做的笔记进行整理.本篇博客是整理Java知识点的第三十五 ...

  6. ViSP学习笔记(二十五):基于图像的视觉伺服

    开发环境:Ubuntu 18.04 LTS + ROS Melodic + ViSP 3.3.1 文章内容主要参考ViSP官方教学文档:https://visp-doc.inria.fr/doxyge ...

  7. 【Java学习笔记之二十八】深入了解Java8新特性

    前言: Java 8 已经发布很久了,很多报道表明java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java ...

  8. 【Java学习笔记之二十九】Java中的equals和==的用法及区别

    Java中的"equals"和"=="的用法及区别 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String(&quo ...

  9. lableimg闪退_CV学习笔记(二十五):数据集标注与制作

    最近在做一些数据标注的工作,虽然标注数据比较枯燥,但这也是每个做算法的工程师升级打怪的必由之路.使用一些合适的工具往往可以事半功倍,效率UP. 一:数据标注流程 二:数据处理的一些小代码 1:重命名 ...

最新文章

  1. 第二阶段冲刺——第三天
  2. 一款遥控器拆解之后可利用的元器件
  3. python读取caffemodel文件
  4. Intel汇编语言程序设计学习-第二章 IA-32处理器体系结构-下
  5. java classifier_Java ZeroR.buildClassifier方法代码示例
  6. Qt之QObjectCleanupHandler使用介绍
  7. 数组按逆向求最大差值的算法
  8. c++中的set容器和multiset容器
  9. 2017总结、2018展望
  10. Android File数据存储
  11. python数组对应元素相乘_python – 将一个数组的每一行与numpy中另一个数组的每个元素相乘...
  12. 达内JAVA云笔记_达内云笔记项目完整代码+注释
  13. 电脑罗盘时钟html怎么设置成桌面,抖音最火的罗盘时钟桌面壁纸使用教程
  14. Xilinx Vitis学习-ug1393
  15. Linux驱动实践:你知道【字符设备驱动程序】的两种写法吗?
  16. Mac压缩文件怎么加密?BetterZip加密Word压缩文件教程
  17. 【Scratch-文字朗读模块】Scratch-文字朗读模块分析——人工智能的基础
  18. DateFormat 线程安全
  19. 目标检测yolo系列
  20. ibd文件结构组成(带图详解)

热门文章

  1. 定位域中长期不活动计算机
  2. 元胞自动机:更接近人类思考的智能模型
  3. iOS指示器之UIProgressView和UIPageControl
  4. 微软宣布.NET Native预览版
  5. Windows7操作系统自定义运行命令(简单方法之二)
  6. 2011年的32有用的jQuery插件
  7. Windows Mobile 7 梦幻之旅系列1之- What’s New?
  8. Mars 算法实践——人脸识别
  9. 跟我学Spring Cloud(Finchley版)-08-Ribbon深入
  10. 元素均匀排列自动换行二维数组前端遍历