java内部类的作用分析

提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested
Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看,就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。下面从以下几个方面来介绍:

第一次见面

[java] view plaincopy

  1. public interface Contents {

  2. int value();

  3. }

  4. public interface Destination {

  5. String readLabel();

  6. }

  7. public class Goods {

  8. private class Content implements Contents {

  9. private int i = 11;

  10. public int value() {

  11. return i;

  12. }

  13. }

  14. protected class GDestination implements Destination {

  15. private String label;

  16. private GDestination(String whereTo) {

  17. label = whereTo;

  18. }

  19. public String readLabel() {

  20. return label;

  21. }

  22. }

  23. public Destination dest(String s) {

  24. return new GDestination(s);

  25. }

  26. public Contents cont() {

  27. return new Content();

  28. }

  29. }

  30. class TestGoods {

  31. public static void main(String[] args) {

  32. Goods p = new Goods();

  33. Contents c = p.cont();

  34. Destination d = p.dest("Beijing");

  35. }

  36. }

在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和Destination。在后面的main方法里,直接用
Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了
隐藏你不想让别人知道的操作,也即封装性。

同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:

outerObject=new outerClass(Constructor Parameters);

outerClass.innerClass innerObject=outerObject.new
InnerClass(Constructor Parameters);

注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题
非静态内部类对象有着指向其外部类对象的引用,对刚才的例子稍作修改:

[java] view plaincopy

  1. public class Goods {

  2. private int valueRate = 2;

  3. private class Content implements Contents {

  4. private int i = 11 * valueRate;

  5. public int value() {

  6. return i;

  7. }

  8. }

  9. protected class GDestination implements Destination {

  10. private String label;

  11. private GDestination(String whereTo) {

  12. label = whereTo;

  13. }

  14. public String readLabel() {

  15. return label;

  16. }

  17. }

  18. public Destination dest(String s) {

  19. return new GDestination(s);

  20. }

  21. public Contents cont() {

  22. return new Content();

  23. }

  24. }

在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处
一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象就必须有指向外部类对象的引用。Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。

有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么办?没事,Java里用如下格式表达外部类的引用:

outerClass.this

有了它,我们就不怕这种屏蔽的情况了。

静态内部类

和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来讲还有区别。

除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一层)。不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。

局部内部类

是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。

[java] view plaincopy

  1. public class Goods1 {

  2. public Destination dest(String s) {

  3. class GDestination implements Destination {

  4. private String label;

  5. private GDestination(String whereTo) {

  6. label = whereTo;

  7. }

  8. public String readLabel() {

  9. return label;

  10. }

  11. }

  12. return new GDestination(s);

  13. }

  14. public static void main(String[] args) {

  15. Goods1 g = new Goods1();

  16. Destination d = g.dest("Beijing");

  17. }

  18. }

上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。

下面有一个更怪的例子:

[java] view plaincopy

  1. public class Goods2 {

  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("slip");

  14. String s = ts.getSlip();

  15. }

  16. }

  17. public void track() {

  18. internalTracking(true);

  19. }

  20. public static void main(String[] args) {

  21. Goods2 g = new Goods2();

  22. g.track();

  23. }

  24. }

你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。

匿名内部类

java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的:

new interfacename(){......}; 或 new
superclassname(){......};

下面接着前面继续举例子:

[java] view plaincopy

  1. public class Goods3 {

  2. public Contents cont() {

  3. return new Contents() {

  4. private int i = 11;

  5. public int value() {

  6. return i;

  7. }

  8. };

  9. }

  10. }

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。

在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:

[java] view plaincopy

  1. frame.addWindowListener(new WindowAdapter(){

  2. public void windowClosing(WindowEvent e){

  3. System.exit(0);

  4. }

  5. });

有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法:

如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。

将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。

在这个匿名内部类中使用初始化代码块。

为什么需要内部类?

java内部类有什么好处?为什么需要内部类?

首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。

不过你可能要质疑,更改一下方法的不就行了吗?

的确,以此作为设计内部类的理由,实在没有说服力。

真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题
没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

java内部类的作用分析

时间: 2024-11-06 11:59:09

java内部类的作用分析的相关文章

【转】java内部类的作用

http://andy136566.iteye.com/blog/1061951/ 推荐一. 定义 放在一个类的内部的类我们就叫内部类. 二. 作用 1.内部类可以很好的实现隐藏 一般的非内部类,是不允许有 private 与protected权限的,但内部类可以 2.内部类拥有外围类的所有元素的访问权限 3.可是实现多重继承 4.可以避免修改接口而实现同一个类中两种同名方法的调用. 三.   例子 1.实现隐藏 平时我们对类的访问权限,都是通过类前面的访问修饰符来限制的,一般的非内部类,是不允

Java 内部类综述

摘要: 多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承.但有时候,我们确实是需要实现多重继承,而且现实生活中也真正地存在这样的情况,比如遗传:我们即继承了父亲的行为和特征也继承了母亲的行为和特征.可幸的是,Java 提供了两种方式让我们曲折地来实现多重继承:接口和内部类.事实上,实现多重继承是内部类的一个极其重要的应用.除此之外,内部类还可以很好的实现隐藏(例如,私有成员内部类).内部类共有四种类型,即成员内部类.静态内部类.

Java内部类

本文是<Java核心技术 卷1>中第六章接口与内部类中关于内部类的阅读总结. Java中的内部类(inner class)是定义在另一个类内部的类.那么内部类有什么用呢?这里主要由三个内部类存在的原因: 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据.即,如果类A中定义了类B,那么类B可以访问类A中的数据,甚至是私有数据,但类A不能访问类B中的私有数据: 内部类可以对同一个包中的其他类隐藏起来.在一个包中,定义一个类时,即使不加上访问权限关键词,这个类也是包内其他类可访问的,不

JAVA 从GC日志分析堆内存 第七节

JAVA 从GC日志分析堆内存 第七节 在上一章中,我们只设置了整个堆的内存大小.但是我们知道,堆又分为了新生代,年老代.他们之间的内存怎么分配呢?新生代又分为Eden和Survivor,他们的比例大小能改变吗?其实这些都是可控的,以前没有讲到是因为就算讲了也只是讲讲而已,看不到实质性的东西.因此这章我们通过分析GC日志来一步步讲解如何细化设置堆内存. 首先我们来了解几个相关的参数: -XX:+PrintGCDetails:用于告诉虚拟机回收垃圾的时候顺便打印日志. -Xloggc:路径 :将打

Java内部类详解(一)(转自:http://blog.csdn.net/wangpeng047/article/details/12344593)

很多人对于Java内部类(Inner Class)都十分陌生,甚至听都没听过也没有使用过,内部类在Java中其实是比较重要的一块内容,掌握好这门知识对于编程来说,犹如插上一对翅膀. 一.概念 内部类是指在一个外部类的内部再定义一个类,类名不需要和文件名相同. 对于一个名为outer的外部类和其内部定义的名为inner的内部类.编译完成后会生成outer.class和outer$inner.class两个类.所以内部类的成员变量.方法名可以和外部类的相同. 内部类可以是静态static和非静态的,

Java 内部类浅析

一.引言 由于目前是Android开发的实习生,在开发过程中发现越来越多自己知道但是真正去使用的时候却用不上的情况,比如内部类的使用上,所以在经过网上查找资料学习以及自己总结之后,特此发表一篇博文与大家分享,如有不当之处,万望指出. 二.内部类 内部类是指在一个外部类的内部再定义一个类,是一个编译时的概念,一旦编译成功,内部类与其外部类就会成为完全不同的两类,只是内部类的前面会冠以外部类的类名和$符号,如一个outer类内部定义了一个inner的内部类,那么编译完成后会生成outer.class

Java之OutOfMemoryError简单分析

Java之OutOfMemoryError简单分析 最近编码遇到了Java内存溢出的问题,所以就想顺便总结一下几种导致Java内存溢出的栗子,以及碰到Java内存溢出要如何去解决. Java堆溢出 Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达的路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆容量限制之后就会产生内存溢出的异常. 下面通过一段代码进行堆内存溢出异常的测试. 1 public class HeapOOM{ 2 static cl

浅析总结 Java 内部类的一些使用与梳理

======================================================== 作者:qiujuer 博客:blog.csdn.net/qiujuer 网站:www.qiujuer.net 开源库:Genius-Android 转载请注明出处:http://blog.csdn.net/qiujuer/article/details/43282699 --学之开源,用于开源:初学者的心态,与君共勉! ================================

Java内部类之间的闭包和回调详解

前言 闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域.通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用城内,内部类有权操作所有的成员,包括private成员. Java最引人争议的问题之一就是,人们认为Java应该包含某种类似指针的机制,以允许回调(callback).通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象.如果回调是通