内部类  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. Hibernate实体类注解的问题

    刚刚和八千哥弄一个问题,这个很诡异的问题,困扰了我这么长时间.哎,说来惭愧. 用三大框架写毕设,结果今天获取前台数的时候,发现传值有个传不到. 我一开始用的是名为cows的数据,后来换了个数据库,加了 ...

  2. [转载]灵动思绪EF(Entity FrameWork)

    很久之前就想写这篇文章了,但是由于种种原因,没有将自己学习的EF知识整理成一片文章.今天我就用CodeFirst和ModelFirst两种方式的简单案例将自己学习的EF知识做个总结. 在讲解EF之前, ...

  3. 10-12Linux流编程的一些知识点

    第五章  Linux 的流编程 Linux流操作基础      流和文件的关系:流相当于一个缓冲区,可以将文件描述符和流关联,获得相应的缓冲区,以此来提高系统对磁盘的存取速度.     流的结构和操作 ...

  4. 记一次Angular2环境搭建及My First Angular App小demo呈现

    参考连接?不如说是照搬链接.AngularJs官网地址快速起步地址. 对于一个一直只是用jq,偶尔学习点Knockout js,了解一点mvvm结构的前端来说,学习Angular2还是有点困难的.好了 ...

  5. C# 中多态和重载的区别

    一.多态性意味着有多重形式. 在面向对象编程范式中,多态性往往表现为"一个接口,多个功能". using System; using System.Collections.Gene ...

  6. django drf CreateModelMixin和Serializer.validate_columun

    view demo class ValidateCodeSet(mixins.CreateModelMixin, viewsets.GenericViewSet): serializer_class ...

  7. Let it crash philosophy part II

    Designing fault tolerant systems is extremely difficult.  You can try to anticipate and reason about ...

  8. dokcer 的export 、improt和save 、load

    export .improt 是对容器操作也就是类似于虚拟机的快照 save .load 是针对于镜像操作的..

  9. 获取BinaryReader中读取的文件名

    BinaryReader br; br = null; br = new BinaryReader(new FileStream("E:demo.txt", FileMode.Op ...

  10. 工作中常用Linux命令

    建立软链接  ln -s      例:ln -s b a 解释:把文件夹a和文件夹b关联起来,访问文件夹a,实际访问的是问价夹b 删除软连接  rm -rf a  直接删掉a文件夹跟a和b的软连接. ...