内部类  Inner Class

一个内部类可以定义在另一个类里,可以定义在函数里,甚至可以作为一个表达式的一部分。

Java中的内部类共分为四种

  静态内部类static inner class (also called nested class)

  成员内部类member inner class

  局部内部类local inner class

  匿名内部类anonymous inner class

 

1 成员内部类  member inner class

1.1 形式

成员内部类也是定义在另一个类中,但是定义时不用static修饰。形如:

 class Outer {
class Inner{ }
}

编译之后会产生如下2个class文件:

---->这可以将相关的类组织在一起,从而降低了命名空间的混乱。

成员内部类的修饰符:
对于普通的类,可用的修饰符有final、abstract、strictfp、public和默认的包访问。
但是成员内部类更像一个成员变量和方法。
可用的修饰符有:final、abstract、public、private、protected、strictfp和static。
一旦用static修饰内部类,它就变成静态内部类了。

1.2 创建内部类实例

成员内部类就像一个实例变量,他依赖于外部类的实例存在 --> 必须先有外部类的实例  才能创建成员内部类对象

在外部类里面创建成员内部类的实例:this.new Innerclass(); 或可以用 Inner inner = new Inner(); 方法直接创建

在外部类之外创建内部类的实例:(new Outerclass()).new Innerclass();  

或   Inner inner = new Outer().new Inner()

或   Outer outer = new Outer(); Inner inner = outer.new Inner();

案例:从外部类的非静态方法中实例化内部类对象。

 class Outer {
private int i = 10;
public void makeInner(){
Inner in = new Inner();
in.seeOuter();
}
class Inner{
public void seeOuter(){
System.out.print(i);
}
}
}

表面上,我们并没有创建外部类的对象就实例化了内部类对象,和上面的话矛盾。
事实上,如果不创建外部类对象也就不可能调用makeInner()方法,所以到头来还是要创建外部类对象的。
你可能试图把makeInner()方法修饰为静态方法,即static public void makeInner()。
这样不创建外部类就可以实例化外部类了!但是在一个静态方法里能访问非静态成员和方法吗?显然不能。

--> 必须先有外部类的实例  才能创建成员内部类对象

案例:从外部类的静态方法中实例化内部类对象

     class Outer {
private int i = 10;
class Inner{
public void seeOuter(){
System.out.print(i);
}
}
public static void main(String[] args) {
Outer out = new Outer();
Outer.Inner in = out.new Inner();
//Outer.Inner in = new Outer().new Inner();
in.seeOuter();
}
}

被注释掉的那行是它上面两行的合并形式,一条简洁的语句。
对比一下:在外部类的非静态方法中实例化内部类对象是普通的new方式:Inner in = new Inner();
在外部类的静态方法中实例化内部类对象,必须先创建外部类对象:Outer.Inner in = new Outer().new Inner();

1.3 成员内部类操作外部类

成员内部类可以访问它的外部类的所有成员变量和方法,不管是静态的还是非静态的都可以

内部类就像一个实例成员一样存在于外部类,所以内部类可以访问外部类的所有成员就想访问自己的成员一样没有限制。

内部类中的this指的是内部类的实例对象本身,如果要用外部类的实例对象就可以用类名.this的方式获得。

普通的类可以用this引用当前的对象,内部类也是如此。

但是假若内部类想引用外部类当前的对象呢?用“外部类名”.this;的形式,如下例的Outer.this。

 class Outer {
class Inner{
public void seeOuter(){
System.out.println(this);
System.out.println(Outer.this);
}
} public static void main(String[] strs){
new Outer().new Inner().seeOuter();
}
} 输出:
Outer$Inner@61de33
Outer@14318bb

1.4 内部类对象中不能有静态成员

原因很简单,内部类的实例对象是外部类实例对象的一个成员,若没有外部类对象,内部类就不会存在,何谈静态成员呢。

 class Outer {
class Inner{
static int i = 0;
public void seeOuter(){
System.out.println(this);
System.out.println(Outer.this);
}
} public static void main(String[] strs){
new Outer().new Inner().seeOuter();
}
}

我们编译这个类:

  E:\>javac Outer.java
Outer.java:3: 内部类不能有静态声明
static int i = 0;
^
1 错误

2 局部内部类local inner class

局部内部类local inner class 也可以成为方法内部类

顾名思义,就是把类放在方法内。局部内部类定义在方法中,比方法的范围还小。是内部类中最少用到的一种类型。

像局部变量一样,不能被public, protected, private和static修饰。

局部内部类在方法中定义,所以只能在方法中使用,即只能在方法当中生成局部内部类的实例并且调用其方法。

  class Outer {
public void doSomething(){
class Inner{
public void seeOuter(){
System.out.println("inner class");
}
} Inner inner = new Inner();
inner.seeOuter();
} public static void main(String ... args){
new Outer().doSomething();
}
}

输出:

inner class

局部内部类只能在声明的方法内是可见的,因此定义局部内部类之后,想用的话就要在方法内直接实例化,
记住这里顺序不能反了,一定是要先声明后使用,否则编译器会说找不到。

方法内部类的修饰符:
  与成员内部类不同,方法内部类更像一个局部变量。
  可以用于修饰方法内部类的只有final和abstract。

注意事项:

A: 方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化
B: 方法内部类对象不能使用该内部类所在方法的非final局部变量。 原因:

因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。
    但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。
    正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。下面是完整的例子:

 class Outer {
public void doSomething(){
  final int a =10;
  class Inner{
    public void seeOuter(){
      System.out.println(a);
    }
  }
  Inner in = new Inner();
  in.seeOuter();
}
  public static void main(String[] args) {
13     Outer out = new Outer();
    out.doSomething();
}
}

C:静态方法内的方法内部类。

静态方法是没有this引用的,因此在静态方法内的内部类遭受同样的待遇,即:只能访问外部类的静态成员。

3 匿名内部类Anonymous Inner Class

顾名思义,没有名字的内部类。

匿名内部类就是没有名字的局部内部类,不使用关键字class, extends, implements, 没有构造方法。

匿名内部类隐式地继承了一个父类或者实现了一个接口

匿名内部类使用得比较多,通常是作为一个方法参数。

A、继承式的匿名内部类。

 class Car {
public void drive(){
System.out.println("Driving a car!");
}
} class Test{
public static void main(String[] args) {
Car car = new Car(){
public void drive(){
System.out.println("Driving another car!");
}
};
car.drive();
}
}

结果输出了:Driving another car!

建立匿名内部类的关键点是重写父类的一个或多个方法。再强调一下,是重写父类的方法,而不是创建新的方法。
因为用父类的引用不可能调用父类本身没有的方法!创建新的方法是多余的。简言之,参考多态。

B、接口式的匿名内部类。

  interface  Vehicle {
public void drive();
} class Test{
public static void main(String[] args) {
Vehicle v = new Vehicle(){
public void drive(){
System.out.println("Driving a car!");
}
};
v.drive();
}

这种形式的代码我们一定写过,这也是内部类的存在的一个重要作用:便于编写 线程和事件驱动的代码

 public class ThreadDemo {
public static void main(String[] args) { new Thread(new Runnable(){
public void run(){
System.out.println("Hello World!");
}
}).start();
}
} E:\>javac ThreadDemo.java
E:\>java ThreadDemo
Hello World!
 public class SwingTest
{
public static void main(String[] args)
{
JFrame frame = new JFrame("JFrame");
JButton button = new JButton("JButton"); button.addActionListener(new ActionListener(){ @Override
public void actionPerformed(ActionEvent arg0){
System.out.println("Hello World"); }
}); frame.getContentPane().add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200); frame.addWindowListener(new WindowAdapter() { @Override
public void windowClosing(WindowEvent e){
System.out.println("Closing");
System.exit(0);
}
}); frame.setVisible(true);
}
}

若你了解安卓的话  会发现这样的代码编写方式有着很多的应用

好处就是简化我们的代码。

C、参数式的匿名内部类。

 class Bar{
void doStuff(Foo f){}
} inteface Foo{
void foo();
} class Test{
static void go(){
Bar b = new Bar();
b.doStuff(new Foo(){
public void foo(){
System.out.println("foofy");
}
});
}
}

4 静态内部类static inner class 

在定义成员内部类的时候,可以在其前面加上一个权限修饰符static。此时这个内部类就变为了静态内部类。

同样会被编译成一个完全独立的.class文件,名称为OuterClass$InnerClass.class的形式。

只可以访问外部类的静态成员和静态方法,包括了私有的静态成员和方法。

生成静态内部类对象的方式为:OuterClass.InnerClass inner = new OuterClass.InnerClass();

静态内部类使用代码:

 package com.learnjava.innerclass;

 class StaticInner
{
private static int a = 4; // 静态内部类
public static class Inner
{
public void test()
{
// 静态内部类可以访问外部类的静态成员
// 并且它只能访问静态的
System.out.println(a);
} }
} public class StaticInnerClassTest
{ public static void main(String[] args)
{
StaticInner.Inner inner = new StaticInner.Inner();
inner.test();
}
}

与一般内部类不同,在静态代码中不能够使用this操作,所以在静态内部类中只可以访问外部类的静态变量和静态方法。
使用静态内部类的目的和使用内部类相同。如果一个内部类不依赖于其外部类的实例变量,或与实例变量无关,则选择应用静态内部类。

可以在静态内部类的方法中,直接访问外部类的静态变量和调用静态方法。但不允许访问外部类的实例变量以及实例方法。
静态内部类的实例方法中亦只允许访问外部类的静态成员。

静态内部类不同于其他3种内部类,他有着自己特殊的特性,参看:解析静态内部类的使用目的与限制

5 小结

5.1 几种内部类的共性:

A、内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类命和$符号。
B、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。

5.2 java中为什么要引入内部类?还有匿名内部类?

1)可以是单继承的一种补充解决方案 inner classes能有效实际地允许“多重实现继承(multiple implementation)”

Java中一个类只能继承一个类 可以通过内部类达到继承多个类的效果

:每个inner class都能够各自继承某一实现类(implementation),因此,inner class不受限于outer class是否已继承自某一实现类。

2)针对具体的问题提供具体的解决方案,同时又能对外隐藏实现细节    看具体的案例

案例1

在集合中可以使用Iterator遍历 但每一种集合的数据结构不同  导致遍历的方法必然也不同
所以Java在每个具体的集合里定义了一个内部类Itr  他实现了Iterator接口   从而根据所在类的具体情况进行遍历

 public interface Iterator {//迭代器的功能
boolean hasNext();
Object next();
} public interface Iterable {//返回迭代器的能力
Iterator iterator();
} public interface Collection extends Iterable {
Iterator iterator();
} public interface List extends Collection {
Iterator iterator();
} public class ArrayList implements List {
public Iterator iterator() {
return new Itr();
} private class Itr implements Iterator {
//每种集合的具体实现采用了不同的数据结构
public boolean hasNext() {......}
public Object next(){......}
}
} Collection c = new ArrayList();
c.add("hello");
c.add("world");
c.add("java");
Iterator it = c.iterator(); //new Itr();
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
}

Itr是private的类 对外并不可见 因为我的遍历方法我自己知道就可以了 别人并不需要了解

集合类有实现了Tterable接口 通过里面的iterator方法获取该内部类实例 外部不能直接创建该内部类的实例

案例2

为某个类提供特定的数据结构 :  为了共同解决一个具体的问题 但又可以对外部保持透明

如HashMap中有Entry   ConcurrentHashMap有HashEntry 和Segment

看HashMap中的内部类:

 public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{ static final Entry<?,?>[] EMPTY_TABLE = {};
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;
// ... ...
}
}

关于HashMap 可以参考:HashMap源码解析

关于ConcurrentHashMap:ConcurrentHashMap  ConcurrentHashMap原理分析

Java Inner Class 内部类的更多相关文章

  1. [java] 深入理解内部类: inner-classes

    [java] 深入理解内部类: inner-classes // */ // ]]>   [java] 深入理解内部类: inner-classes Table of Contents 1 简介 ...

  2. Java中的内部类(成员内部类、静态内部类、局部内部类、匿名内部类)

    Java中的内部类(成员内部类.静态内部类.局部内部类.匿名内部类) 神话丿小王子的博客主页 我们先看这样一段话:人是由大脑.肢体.器官等身体结果组成.而组成我们人体的心脏它也有自己的属性和行为(血液 ...

  3. java基础之 内部类

    Java中的内部类共分为四种: 静态内部类static inner class (also called nested class) 成员内部类member inner class 局部内部类loca ...

  4. java 笔记(2) —— 内部类的作用

    一.内部类简介 个人觉得内部类没多少研究价值,GUI中的事件响应算是非常典型的应用了. Java内部类其实在J2EE编程中使用较少,不过在窗口应用编程中特别常见,主要用来事件的处理.其实,做非GUI编 ...

  5. Java基础(53):内部类(转)

    java中的内部类总结 内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类 如同一个人是由大脑.肢体.器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行 ...

  6. JAVA 第二天 内部类

    package com.company; /** * Created by Administrator on 2016/8/23. */ public class Outter {//生成的字节码文件 ...

  7. Java 中的内部类

    前言 在第一次把Java 编程思想中的内部类这一章撸完后,有点印象.大概知道了什么时内部类,局部内部类,匿名内部类,嵌套内部类.随着时间的推移,自己慢慢的就忘记了,总感觉自己思考的东西不多,于是 看了 ...

  8. Java中的 内部类(吐血总结)

    1. 内部类的作用 内部类是一个独立的实体,可以用来实现闭包:能与外部类通信:内部类与接口使得多继承更完整 2. 内部类的分类 1)普通内部类 类的实例相关,可以看成是一个实例变量.内部类的类名由 “ ...

  9. Java Nested Classes(内部类~第一篇英文技术文档翻译)

    鄙人最近尝试着翻译了自己的第一篇英文技术文档.Java Nested Classes Reference From Oracle Documentation 目录 嵌套类-Nested Classes ...

  10. java 里的内部类

    java里的内部类通常能帮我们隐藏一些具体实现,体现良好的封装效果. 内部类又分几种: 1.普通内部类 2.局部内部类 3.匿名内部类 4.静态内部类 普通内部类 先来看第一种普通的内部类,这种内部类 ...

随机推荐

  1. java程序练习

    数组求和作业 开发环境:java 工具:eclipse 两种数据类型excel和csv 在同学建议下,我选择用csv文件打开,这就引来了第一个问题,在java中如何调用csv文件.以下是我百度的结果 ...

  2. day 20 02 模块的导入

    day 20 02  模块的导入 1.模块:就是一个文件:放置一些通用的有独立功能程序或者函数.比如建立一个py文件,文件名为:demo,文件里面的内容:demo模块: print('嗨大米')def ...

  3. 一个hql语句

    在hql语句里面,in的使用方法比较特别. from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' ) in后面是一个list,我写的 ...

  4. String类的操作方法

    因String属于java核心包lang包的东西,所以不需要导包! /* * 字符串操作 * */ String name = "jck"; String name1 = &quo ...

  5. 【BZOJ2440】[中山市选2011]完全平方数

    题意描述 原题 一句话描述: 求第K个不是完全平方数的倍数的数. K≤$10^{9}$ ------------------------------------------ 题解: 首先,直接求第$k ...

  6. BZOJ4012 [HNOI2015]开店 (动态点分治)

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  7. objectARX创建 PaletteSet 停靠面板示例

    objectARX创建 PaletteSet 停靠面板示例 图文By edata ,转载注明出处 http://www.cnblogs.com/edata 部分代码参考张帆<AutoCAD Ob ...

  8. ROS初次实践(小海龟)

    启动ROS Master 启动小海龟仿真器 启动海龟控制节点(方向键控制海龟运动) rqt_graph可视化工具 /rosout节点必须存在,订阅所有节点的日志信息. 当前系统当中存在的节点. 了解当 ...

  9. VMware Workstation 14 pro License Keys

    AC5XK-0ZD4H-088HP-9NQZV-ZG2R4 CG54H-D8D0H-H8DHY-C6X7X-N2KG6 ZC3WK-AFXEK-488JP-A7MQX-XL8YF ZC5XK-A6E0 ...

  10. C语言多线程编程二

    一. 线程通信----事件: 1.一对一模式: #include <stdio.h> #include <stdlib.h> #include <Windows.h> ...