这篇文章主要讲述Java 内部类的相关知识,主要讲解下面的知识点。

  1. 内部类的概念
  2. 内部类的特点与使用
  3. 多种形式内部类
  4. 为什么要使用内部类

内部类的概念

内部类是指在一个类的内部定义了另一个类。例如下面的代码中例子,就是一个简单的内部类。

public class A {private int a;public class B{private int b;}
}

在这个类中,我们可以看出内部类B就像A的成员一样。所以我们对内部类的修饰也可以用很多种修饰符,比如我们是不可能对一个非内部类用private修饰符和protected修饰符,但是如果这个类是内部类,我们可以对他用private和protected访问修饰符以及static和final修饰符等等,就像对一个成员添加修饰符一样。当我们对B类添加多种修饰符以后,我们就可以把B类看成隐藏在A类中,像是一种代码隐藏机制。这样的内部类有什么特点以及我们如何去使用他呢?

内部类的特点与使用

在上面的例子中,我们把B类看成是A类的一个成员,那么这个成员与普通的成员变量和成员方法有什么区别呢? 我们可以很容易的在B类访问到A类中任何成员。但是如果我们想在A类中去访问B类的成员就不这么轻松了。如果B类是一个非静态类,那我们就要在外部类中声明一个B类的引用去调用B类中的成员。如下例子所示:

public class A {private int a;public B getB(){return new B();}public void show(){System.out.println(getB().b);}protected  class B{public int b;public void print(){System.out.println(a);}}public static void main(String[] args) {new A().show();}
}

在这个例子中,我们可以看到B类中,是很随意就能访问到A类中的private成员变量a,但是A类中要访问B类中的public成员变量b也是要先定义一个B类的对象,通过这个对象去访问b。这就是内部类一个典型的特点。外部类的一切对内部类都是透明的,但是内部类确可以对外部类隐藏实现的细节。造成这样的原因是因为当某个外部类对象创建一个内部类对象的时候,此内部类的对象必定会秘密的捕获一个指向外部类的引用。这些都是编译器帮你做的。但是如果B类用static修饰也就是变成一个静态类,那么B类中也只能访问A类中的静态成员。
注意,如果上面的show()方法,如果show()是一个静态的方法,那么就不能直接new B()一个对象而是要用a的对象去创建B的对象。
那如果在B类中使用this指的是哪个对象的?按照this的定义显然指向的当前对象B,如果我们要在B类中显式调用A类的对象,我们就要用到一个特殊的语法 . this 显式调用A的对象就是A.this
那如果我们要在一个其他类中的创建B类对象呢?那我们需要用到一个新的语法.new。代码如下:

  A a = new A();A.B b = a.new B();

这样就在其他类中创建了一个B的对象。在A类中就不需要这样麻烦了,因为会默认省略一个this,所以看起来只是new B()这样简单。
当然,前面说了B类是可以用static修饰,这个时候情况就变了。首先在其他类中创建一个B的对象,我们就不能用A的对象去创建B了 因为B是一个静态类不能用对象去引用,这是我们可以用如下代码去创建B的对象

 A a = new A();A.B b = new A.B();

或者直接用A.B去调用B类中的静态方法。A.B表示访问A类中的B类
其次,如果我们在A类中写一个静态方法,那么就不需要去A.B,直接用B去调用B类中静态成员。代码如下:

public class A {public static class B{public int a = 1;public int bb;public static String print(){return "1";}}public static void main(String[] args) {B.print();}
}

注意一点,只有B类是静态类才能由静态成员。

多种形式的内部类

我们上面已经了解到了内部类的特点以及如何使用内部类,接下来我们了解一些内部类的类型,Java语言并没有规定内部类必须出现在哪里。所以内部类可以出现在方法内部,出现在作用域中,还可能还是匿名内部类.

方法内部的内部类——局部内部类

我们可以在方法内部定义一个类,这在一定时候是有必要的。这个时候类不能有修饰符。例子如下:

public class A {public C getC(){class B extends C{@Overridepublic void show() {System.out.println("我是B");}}return new B();}public static void main(String[] args) {A a = new A();a.getC().show();}
}
abstract class C{public abstract void show();
}

作用域中的内部类

这里指的是内部类出现在方法中的一个作用域内,这样说可能是难懂,比如方法中有一个if语句,内部类写在这个if语句中,这就是内部类出现在一个作用域内。虽然这不常见,但是Java语言是支持这种语法的。

public class A {public C getC(boolean flag) {if (flag) {class B extends C {@Overridepublic void show() {System.out.println("我是B");}}return new B();}return null;}public static void main(String[] args) {A a = new A();a.getC(true).show();}
}abstract class C{public abstract void show();
}

这个例子,不代表B类的生成是有条件的,相反,B类和A类在编译的时候就一起生成了。

匿名内部类

接下来,我们改写一下局部内部类的例子,让他看上去是这样的。

public class A {public C getC() {return new C() {@Overridepublic void show() {System.out.println("我的匿名内部类");}};}public static void main(String[] args) {A a = new A();a.getC().show();}
}abstract class C{public abstract void show();
}

这样看上去就简洁很多,也怪异一些。返回值的生成与表示这个返回值的类的定义结合在一起。这个类的定义还没有名字。这个语法就叫做匿名内部类。

创建一个继承自C的匿名类的对象。通过new表达式返回的引用被自动向上转型为对C的引用。

匿名内部类,主要的功能就是创建一个类的子类,然后复写父类中的方法,然后将子类的对象作为返回值返回。这个常见于接口的回调,也就是C类不在是抽象类而是一个接口。我们无法返回接口的对象,所以只能返回实现了接口的类的对象,这时用匿名内部类的形式来表示这个类的对象是再好不过了。但是这也有缺点,因为匿名内部类无法第二次使用。我们需要不止一个该类的对象的时候,就很尴尬了。我们接下来继续了解匿名内部类的特点,当了解完我们就清楚什么时候该用匿名内部类啦。
上面的代码中是通过默认的构造器来new一个C的对象,我们如果需要一个有参数的构造器来创建C对象,我们就可以直接C类中定义一个有参数的构造器,通过new C的时候直接把参数传入。

public class A {public C getC() {return new C(2) {@Overridepublic void show() {System.out.println(a);System.out.println("我的匿名内部类");}};}public static void main(String[] args) {A a = new A();a.getC().show();}
}
abstract class C{int a;public C(int a){this.a = a;}public abstract void show();
}

有的时候我们的匿名内部类所在的方法需要一个形参,而这个形参则在匿名内部类中被使用,这个时候我们需要指定这个形参为final类型。直接看这个简单的例子就可以。

public class OuterClass {  public void display(final String name,String age){  class InnerClass{  void display(){  System.out.println(name);  }  }  }
}  

这种一定要加final的原因是因为内部类和外部类完全是两个不同的类,也就是说我们在内部类改变了形参的东西,但是对于外部类而言,这个形参没有改变,因为这个形参对于内部类和外部类是两个东西。但是对于程序员而言这就是一个参数,所以为了避免这种尴尬的事情,就加上final来使形参不发生改变。还有另一种解释,就是加上final 形参,则表示给了内部类一个该形参的副本,让内部类可以对他进行任意操作。
有的时候,如果我们要给我们的匿名内部类加一个构造器来进行一些初始化的时候,就很尴尬了,因为我们并不能添加构造器,这时可以用初始化块的形式来进行一些初始化,但是要注意一点,初始化块不能是静态的,而且匿名内部类也不能用静态的成员。
所以到这里,我们也可以发现不用匿名内部类的一个理由,当我们需要一个有名字的构造器的时候,我们就不能用匿名内部类。

为什么使用内部类

如果只是需要实现一个接口或者继承一个抽象类那么直接用外部类就可以了,不需要内部类,但是如果我们需要多继承呢? 内部类最引人的地方就是

每个内部类都能独立地继承一个类或者一个接口的实现,所以无论外部类是否已经继承了类或者实现了一个接口,都不影响内部类。

所以内部类可以是多继承变得更加完整。而且使用内部类还会有更多新特性。

  1. 内部类可以有多个实例,每个实例都有自己的状态。与外部类互相独立。
  2. 在一个外部类里可以写多个内部类去实现同一个接口或者继承同一个类而有不同的实现。
  3. 在外部类中创建内部类的对象,并不依赖于外部类对象的创建。
  4. 在基于回调的事件的时候,匿名内部类是个很好的选择。

转载于:https://www.cnblogs.com/qifengshi/p/5705897.html

Java 浅析内部类相关推荐

  1. 反射-获取java私有内部类反射类型、私有字段

    获取JAVA私有内部类反射类型 方式一 Class.forName("外部类完整路径$内部私有类类名"); 方式二 通过获取对应私有内部类的字段而获取 完整的类名 Class.fo ...

  2. java使用内部类的好处及其初始化

    java使用内部类的原因 每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响 java内部类初始化 ForeCatalog foreCa ...

  3. java继承a mya new c,“内部类” 大总结(Java),内部类总结java

    "内部类" 大总结(Java),内部类总结java (本文整理自很久以前收集的资料(我只是做了排版修改),作者小明,链接地址没有找到,总之感谢,小明) 内部类的位置: 内部类可以作 ...

  4. java 函数内部类_java 内部类详解 转

    classOuter {classInner { } } (二) 内部类的访问规则 ​ A:可以直接访问外部类的成员,包括私有 ​ B:外部类要想访问内部类成员,必须创建对象 (三) 内部类的分类 ​ ...

  5. java json 内部类_使用Fastjson解析内部类的一个小问题

    使用的Fastjson的版本是1.2.7和1.2.47两个版本. 1. 问题 使用Fastjson解析包含内部类的对象时,发生异常,代码大致如下: public class HelloControll ...

  6. java的内部类和匿名类剖析

    Java 1.1通过对Java语言规范进行修改,显着简化了一些实用结构的实现.在那些修改中,最引人注目的就是内部类和匿名类.如运用得当,它们可使程序更易理解和维护.本文介绍内部类和匿名类在Java代码 ...

  7. java 内部类 加载_举例讲解Java的内部类与类的加载器

    内部类 class A { //Inner1 要在 A 初始化后 才能使用,即要被A的对象所调用 class Inner1 { int k = 0; // static int j = 0; //A加 ...

  8. Java使用内部类的意义

    Java中的内部类感觉很奇怪,一个类中还要再嵌套一个类.为什么要这样做呢? 内部类提供了一个类中实现同一接口的多次机会. 一般情况下,在一个普通类中,是没有办法将一个方法写两遍,还能表达不同意思的,而 ...

  9. java(13)内部类

    内部类就是定义在类内部的类,可以使java具备为普通类实现多重继承的能力. 非静态内部类,局部内部类,静态内部类,匿名内部类 非静态内部类: package 内部类;class Wai5 {int i ...

  10. Java基础-内部类

    文章目录 普通内部类 1.内部类的基本形式 2.实例化一个内部类对象 局部内部类 匿名内部类 静态内部类 静态内部类的使用 内部类的四种形式: 普通内部类/成员内部类 局部内部类 匿名内部类 静态内部 ...

最新文章

  1. 编程语言趋势最新报告:开发者最青睐DevOps,Kotlin增长最快
  2. python与pyqt5_【Python开发】PyQt5应用与实践
  3. 用faster-rcnn训练自己的数据集(VOC2007格式,python版)
  4. Java 折半查询,java之折半查询
  5. thread.Join(); 让主线程等待自己完成
  6. IIS日志分析方法及工具
  7. UIProgressView 圆角
  8. 【H.264/AVC视频编解码技术】序章【编码的前世今生】
  9. BAT的前端,不是技术牛就够了!还应该锻炼这些能力
  10. 字符编码(ucs2 ucs4 utf)
  11. SolidWorks2022 安装教程
  12. php源码 网页聊天_php即时在线网页聊天源码-响应式设计自适应手机端
  13. Python题目练习——天天向上的能力增长模型(进阶版)
  14. Flutter ListView子项长按浮层菜单实现
  15. 如何在知网下载PDF格式的硕博毕业论文?
  16. 【心情】2016ICPC青岛站打铁记
  17. 2020 中南大学研究生招生夏令营机试题(1252~1256)
  18. python爬取王者皮肤_Python爬取王者荣耀所有英雄以及高清大图
  19. java实现,如何在当前时间往后推三十天
  20. 工商管理专业知识与实务(初级)【2】

热门文章

  1. asp.net 安装element ui_vue结合element-ui开发项目 一学就会
  2. java 优先级文档_java 优先级队列.pdf
  3. DL_C1_week4-1(Build Deep Neural Network)
  4. 阿里云云计算 5 阿里云的基础架构
  5. 线性支持向量机与软间隔最大化
  6. 2021-10-18word2vec训练过程
  7. 用Tensorflow求逆矩阵
  8. 合作的进化 6-10
  9. KNN(k-nearest neighbor algorithm)--从原理到实现
  10. 最新xmlns:android=http://schemas.android.com/apk/res/android的理解