一、前言                              

对于内部类平时编码时使用的场景不多,比较常用的地方应该就是绑定事件处理程序的时候了(从C#、JS转向Java阵营的孩子总不不习惯用匿名内部类来做事件订阅:()。本文将结合Bytecode对四种内部类作介绍,当作一次梳理以便日后查阅。

首先要明确的是内部类是编译器提供的特性,编译器会将含内部类的java文件编译成外部类和内部类的N个文件(N>=2) ,然后JVM就按普通类的方式运行。就如下面的源码会被编译为Outer.class和和Outer$Inner.class文件。

class Outer{
class Inner{}
}

三、成员内部类                        

定义在一个类的内部。相对外部类仅有默认和public两种访问修饰符而言,成员内部类可有默认、private、proteced和public四种访问修饰符,效果与成员字段和方法的一样。

示例:

import java.io.*;
// Main.java文件
class Main{
public static void main(String[] args) throws IOException{
MemberCls outer = new MemberCls();
Inner inner1 = outer.new Inner();
Inner inner2 = outer.getInner(); System.out.println(inner1.getVal());
System.out.println(inner2.getVal());
inner1.setVal();
System.out.println(inner1.getVal());
System.out.println(inner2.getVal());
inner2.setVal();
System.out.println(inner1.getVal());
System.out.println(inner2.getVal()); System.in.read();
}
}
// MemberCls.java文件
class MemberCls{
private int val = ; class Inner{
void setVal(int val){
MemberCls.this.val = val;
}
int getVal(){
return val;
}
} Inner getInner(){
return new Inner();
}
// 运行结果
// 1
// 1
// 2
// 2
// 3
//

并生成MemberCls.class和MemberCls$Inner.class两个类文件。

Classfile /F:/skyDrive/repos/self/jottings/java/sample//MemberCls.class
Last modified --; size bytes
MD5 checksum aea71084f78ab319a339717e4d0e1e79
Compiled from "MemberCls.java"
class MemberCls
SourceFile: "MemberCls.java"
InnerClasses:
#= # of #; //Inner=class MemberCls$Inner of class MemberCls
minor version:
major version:
flags: ACC_SUPER Constant pool:
# = Fieldref #.# // MemberCls.val:I
# = Methodref #.# // java/lang/Object."<init>":()V
# = Class # // MemberCls$Inner
# = Methodref #.# // MemberCls$Inner."<init>":(LMemberCls;)V
# = Class # // MemberCls
# = Methodref #.# // MemberCls."<init>":()V
# = Methodref #.# // java/lang/Object.getClass:()Ljava/lang/Class;
# = Methodref #.# // MemberCls.getInner:()LMemberCls$Inner;
# = Fieldref #.# // java/lang/System.out:Ljava/io/PrintStream;
# = Methodref #.# // MemberCls$Inner.getVal:()I
# = Methodref #.# // java/io/PrintStream.println:(I)V
# = Methodref #.# // MemberCls$Inner.setVal:(I)V
# = Fieldref #.# // java/lang/System.in:Ljava/io/InputStream;
# = Methodref #.# // java/io/InputStream.read:()I
# = Class # // java/lang/Object
# = Utf8 Inner
# = Utf8 InnerClasses
# = Utf8 val
# = Utf8 I
# = Utf8 <init>
# = Utf8 ()V
# = Utf8 Code
# = Utf8 LineNumberTable
# = Utf8 getInner
# = Utf8 ()LMemberCls$Inner;
# = Utf8 main
# = Utf8 ([Ljava/lang/String;)V
# = Utf8 Exceptions
# = Class # // java/io/IOException
# = Utf8 access$
# = Utf8 (LMemberCls;I)I
# = Utf8 access$
# = Utf8 (LMemberCls;)I
# = Utf8 SourceFile
# = Utf8 MemberCls.java
# = NameAndType #:# // val:I
# = NameAndType #:# // "<init>":()V
# = Utf8 MemberCls$Inner
# = NameAndType #:# // "<init>":(LMemberCls;)V
# = Utf8 MemberCls
# = NameAndType #:# // getClass:()Ljava/lang/Class;
# = NameAndType #:# // getInner:()LMemberCls$Inner;
# = Class # // java/lang/System
# = NameAndType #:# // out:Ljava/io/PrintStream;
# = NameAndType #:# // getVal:()I
# = Class # // java/io/PrintStream
# = NameAndType #:# // println:(I)V
# = NameAndType #:# // setVal:(I)V
# = NameAndType #:# // in:Ljava/io/InputStream;
# = Class # // java/io/InputStream
# = NameAndType #:# // read:()I
# = Utf8 java/lang/Object
# = Utf8 java/io/IOException
# = Utf8 (LMemberCls;)V
# = Utf8 getClass
# = Utf8 ()Ljava/lang/Class;
# = Utf8 java/lang/System
# = Utf8 out
# = Utf8 Ljava/io/PrintStream;
# = Utf8 getVal
# = Utf8 ()I
# = Utf8 java/io/PrintStream
# = Utf8 println
# = Utf8 (I)V
# = Utf8 setVal
# = Utf8 in
# = Utf8 Ljava/io/InputStream;
# = Utf8 java/io/InputStream
# = Utf8 read
{
MemberCls();
flags: Code:
stack=, locals=, args_size=
: aload_0
: invokespecial # // Method java/lang/Object."<init>":()V
: aload_0
: iconst_1
: putfield # // Field val:I
: return
LineNumberTable:
line :
line :
line : MemberCls$Inner getInner();
flags: Code:
stack=, locals=, args_size=
: new # // class MemberCls$Inner
: dup
: aload_0
: invokespecial # // Method MemberCls$Inner."<init>":(LMemberCls;)V
: areturn
LineNumberTable:
line : public static void main(java.lang.String[]) throws java.io.IOException;
flags: ACC_PUBLIC, ACC_STATIC Code:
stack=, locals=, args_size=
: new # // class MemberCls
: dup
: invokespecial # // Method "<init>":()V
: astore_1
: new # // class MemberCls$Inner
: dup
: aload_1
: dup
: invokevirtual # // Method java/lang/Object.getClass:()Ljava/lang/Class;
: pop
: invokespecial # // Method MemberCls$Inner."<init>":(LMemberCls;)V
: astore_2
: aload_1
: invokevirtual # // Method getInner:()LMemberCls$Inner;
: astore_3
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_3
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: aload_2
: iconst_2
: invokevirtual # // Method MemberCls$Inner.setVal:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_3
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: aload_3
: iconst_3
: invokevirtual # // Method MemberCls$Inner.setVal:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_3
: invokevirtual # // Method MemberCls$Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: getstatic # // Field java/lang/System.in:Ljava/io/InputStream;
: invokevirtual # // Method java/io/InputStream.read:()I
: pop
: return
LineNumberTable:
line :
line :
line :
line :
line :
line :
line :
line :
line :
line :
line :
line :
line :
Exceptions:
throws java.io.IOException static int access$(MemberCls, int);
flags: ACC_STATIC, ACC_SYNTHETIC Code:
stack=, locals=, args_size=
: aload_0
: iload_1
: dup_x1
: putfield # // Field val:I
: ireturn
LineNumberTable:
line : static int access$(MemberCls);
flags: ACC_STATIC, ACC_SYNTHETIC Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field val:I
: ireturn
LineNumberTable:
line :
}

MemberCls.class

Classfile /F:/skyDrive/repos/self/jottings/java/sample//MemberCls$Inner.class
Last modified --; size bytes
MD5 checksum b092ffe3c5b358c786d99d98c104dc40
Compiled from "MemberCls.java"
class MemberCls$Inner
SourceFile: "MemberCls.java"
InnerClasses:
#= # of #; //Inner=class MemberCls$Inner of class MemberCls
minor version:
major version:
flags: ACC_SUPER Constant pool:
# = Fieldref #.# // MemberCls$Inner.this$0:LMemberCls;
# = Methodref #.# // java/lang/Object."<init>":()V
# = Methodref #.# // MemberCls.access$002:(LMemberCls;I)I
# = Methodref #.# // MemberCls.access$000:(LMemberCls;)I
# = Class # // MemberCls$Inner
# = Class # // java/lang/Object
# = Utf8 this$
# = Utf8 LMemberCls;
# = Utf8 <init>
# = Utf8 (LMemberCls;)V
# = Utf8 Code
# = Utf8 LineNumberTable
# = Utf8 setVal
# = Utf8 (I)V
# = Utf8 getVal
# = Utf8 ()I
# = Utf8 SourceFile
# = Utf8 MemberCls.java
# = NameAndType #:# // this$0:LMemberCls;
# = NameAndType #:# // "<init>":()V
# = Class # // MemberCls
# = NameAndType #:# // access$002:(LMemberCls;I)I
# = NameAndType #:# // access$000:(LMemberCls;)I
# = Utf8 MemberCls$Inner
# = Utf8 Inner
# = Utf8 InnerClasses
# = Utf8 java/lang/Object
# = Utf8 ()V
# = Utf8 MemberCls
# = Utf8 access$
# = Utf8 (LMemberCls;I)I
# = Utf8 access$
# = Utf8 (LMemberCls;)I
{
final MemberCls this$;
flags: ACC_FINAL, ACC_SYNTHETIC MemberCls$Inner(MemberCls);
flags: Code:
stack=, locals=, args_size=
: aload_0
: aload_1
: putfield # // Field this$0:LMemberCls;
: aload_0
: invokespecial # // Method java/lang/Object."<init>":()V
: return
LineNumberTable:
line : void setVal(int);
flags: Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LMemberCls;
: iload_1
: invokestatic # // Method MemberCls.access$002:(LMemberCls;I)I
: pop
: return
LineNumberTable:
line :
line : int getVal();
flags: Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LMemberCls;
: invokestatic # // Method MemberCls.access$000:(LMemberCls;)I
: ireturn
LineNumberTable:
line :
}

MemberCls$Inner.class

由于成员内部类依赖于外部类实例,因此创建内部类实例时要先创建外部类实例,然后通过下列两种形式来创建内部类实例:

// 方式一
内部类 内部类实例 = 外部类实例.new 内部类(); // 方式二
外部类{
内部类{}
内部类 get内部类(){
return new 内部类();
}
}
内部类 内部类实例 = 外部类实例.get内部类();

注意:
    1. 当成员内部类拥有与外部类同名的成员变量或方法时,默认是使用成员内部类的成员。若要访问外部类的同名成员,则需要进行如下操作:

外部类.this.成员变量;
外部类.this.成员方法;

2. 对于同一个外部类实例创建的内部类实例,这些内部类实例均操作同一个外部实例。像上述例子那样,均操作同一个val字段。
        看Bytecodes可知,编译器自动为MemberCls创建创建两个静态方法access$002和access$000,而MemberCls$Inner实例则通过这两个静态方法访问私有私有字段val的。

// MemberCls.class文件

/** 等价于
* static int setVal(MemberCls outer, int val){
* outer.val = val;
* return val;
* }
*/
static int access$(MemberCls, int);
flags: ACC_STATIC, ACC_SYNTHETIC
Code:
stack=, locals=, args_size=
: aload_0
: iload_1
: dup_x1
: putfield # // Field val:I
: ireturn
LineNumberTable:
line : /** 等价于
* static int getVal(MemberCls outer){
* return outer.val;
* }
*/
static int access$(MemberCls);
flags: ACC_STATIC, ACC_SYNTHETIC
Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field val:I
: ireturn
LineNumberTable:
line :
// MemberCls$Inner.class文件

/** 等价于
* void setVal(int val){
* MemberCls实例.setVal(val);
* }
*/
void setVal(int);
flags:
Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LMemberCls;
: iload_1
: invokestatic # // Method MemberCls.access$002:(LMemberCls;I)I
: pop
: return
LineNumberTable:
line :
line : /** 等价于
* int getVal(int val){
* MemberCls实例.getVal();
* }
*/
int getVal();
flags:
Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LMemberCls;
: invokestatic # // Method MemberCls.access$000:(LMemberCls;)I
: ireturn
LineNumberTable:
line :

因此内部类可以访问外部类的所有类型的字段和方法(包括private)。

四、局部内部类                           

局部内部类定义在方法或某个作用域里面,并且仅限于方法和该作用域内访问。

示例:

import java.io.*;

class Main{
public static void main(String[] args) throws IOException{
LocalCls outer = new LocalCls();
outer.print(); System.in.read();
}
} class LocalCls{
private int val = ; void print(){
final String name = "fsjohnhuang";
class Inner{
int getVal(){
return val;
}
void setVal(int val){
LocalCls.this.val = val;
}
String getName(){
return name;
}
} Inner inner = new Inner();
System.out.println(inner.getVal());
inner.setVal();
System.out.println(inner.getVal());
System.out.println(inner.getName());
}
}
// 结果:
// 1
// 2
// fsjohnhuang

生成LocalCls.class和LocalCls$1Inner.class两个类文件。

Classfile /F:/skyDrive/repos/self/jottings/java/sample//LocalCls.class
Last modified --; size bytes
MD5 checksum a636f470da37d8c1cb9370dde083d6d8
Compiled from "LocalCls.java"
class LocalCls
SourceFile: "LocalCls.java"
InnerClasses:
#= #; //Inner=class LocalCls$1Inner
minor version:
major version:
flags: ACC_SUPER Constant pool:
# = Fieldref #.# // LocalCls.val:I
# = Methodref #.# // java/lang/Object."<init>":()V
# = Class # // LocalCls
# = Methodref #.# // LocalCls."<init>":()V
# = Methodref #.# // LocalCls.print:()V
# = Fieldref #.# // java/lang/System.in:Ljava/io/InputStream;
# = Methodref #.# // java/io/InputStream.read:()I
# = Class # // LocalCls$1Inner
# = Methodref #.# // LocalCls$1Inner."<init>":(LLocalCls;)V
# = Fieldref #.# // java/lang/System.out:Ljava/io/PrintStream;
# = Methodref #.# // LocalCls$1Inner.getVal:()I
# = Methodref #.# // java/io/PrintStream.println:(I)V
# = Methodref #.# // LocalCls$1Inner.setVal:(I)V
# = Methodref #.# // LocalCls$1Inner.getName:()Ljava/lang/String;
# = Methodref #.# // java/io/PrintStream.println:(Ljava/lang/String;)V
# = Class # // java/lang/Object
# = Utf8 Inner
# = Utf8 InnerClasses
# = Utf8 val
# = Utf8 I
# = Utf8 <init>
# = Utf8 ()V
# = Utf8 Code
# = Utf8 LineNumberTable
# = Utf8 main
# = Utf8 ([Ljava/lang/String;)V
# = Utf8 Exceptions
# = Class # // java/io/IOException
# = Utf8 print
# = Utf8 access$
# = Utf8 (LLocalCls;)I
# = Utf8 access$
# = Utf8 (LLocalCls;I)I
# = Utf8 SourceFile
# = Utf8 LocalCls.java
# = NameAndType #:# // val:I
# = NameAndType #:# // "<init>":()V
# = Utf8 LocalCls
# = NameAndType #:# // print:()V
# = Class # // java/lang/System
# = NameAndType #:# // in:Ljava/io/InputStream;
# = Class # // java/io/InputStream
# = NameAndType #:# // read:()I
# = Utf8 LocalCls$1Inner
# = NameAndType #:# // "<init>":(LLocalCls;)V
# = NameAndType #:# // out:Ljava/io/PrintStream;
# = NameAndType #:# // getVal:()I
# = Class # // java/io/PrintStream
# = NameAndType #:# // println:(I)V
# = NameAndType #:# // setVal:(I)V
# = NameAndType #:# // getName:()Ljava/lang/String;
# = NameAndType #:# // println:(Ljava/lang/String;)V
# = Utf8 java/lang/Object
# = Utf8 java/io/IOException
# = Utf8 java/lang/System
# = Utf8 in
# = Utf8 Ljava/io/InputStream;
# = Utf8 java/io/InputStream
# = Utf8 read
# = Utf8 ()I
# = Utf8 (LLocalCls;)V
# = Utf8 out
# = Utf8 Ljava/io/PrintStream;
# = Utf8 getVal
# = Utf8 java/io/PrintStream
# = Utf8 println
# = Utf8 (I)V
# = Utf8 setVal
# = Utf8 getName
# = Utf8 ()Ljava/lang/String;
# = Utf8 (Ljava/lang/String;)V
{
LocalCls();
flags: Code:
stack=, locals=, args_size=
: aload_0
: invokespecial # // Method java/lang/Object."<init>":()V
: aload_0
: iconst_1
: putfield # // Field val:I
: return
LineNumberTable:
line :
line : public static void main(java.lang.String[]) throws java.io.IOException;
flags: ACC_PUBLIC, ACC_STATIC Code:
stack=, locals=, args_size=
: new # // class LocalCls
: dup
: invokespecial # // Method "<init>":()V
: astore_1
: aload_1
: invokevirtual # // Method print:()V
: getstatic # // Field java/lang/System.in:Ljava/io/InputStream;
: invokevirtual # // Method java/io/InputStream.read:()I
: pop
: return
LineNumberTable:
line :
line :
line :
line :
Exceptions:
throws java.io.IOException void print();
flags: Code:
stack=, locals=, args_size=
: new # // class LocalCls$1Inner
: dup
: aload_0
: invokespecial # // Method LocalCls$1Inner."<init>":(LLocalCls;)V
: astore_2
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method LocalCls$1Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: aload_2
: iconst_2
: invokevirtual # // Method LocalCls$1Inner.setVal:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method LocalCls$1Inner.getVal:()I
: invokevirtual # // Method java/io/PrintStream.println:(I)V
: getstatic # // Field java/lang/System.out:Ljava/io/PrintStream;
: aload_2
: invokevirtual # // Method LocalCls$1Inner.getName:()Ljava/lang/String;
: invokevirtual # // Method java/io/PrintStream.println:(Ljava/lang/String;)V
: return
LineNumberTable:
line :
line :
line :
line :
line :
line : static int access$(LocalCls);
flags: ACC_STATIC, ACC_SYNTHETIC Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field val:I
: ireturn
LineNumberTable:
line : static int access$(LocalCls, int);
flags: ACC_STATIC, ACC_SYNTHETIC Code:
stack=, locals=, args_size=
: aload_0
: iload_1
: dup_x1
: putfield # // Field val:I
: ireturn
LineNumberTable:
line :
}

LocalCls.class

Classfile /F:/skyDrive/repos/self/jottings/java/sample//LocalCls$1Inner.class
Last modified --; size bytes
MD5 checksum a4bf7c12f15f22b2ebb3f79438a555ab
Compiled from "LocalCls.java"
class LocalCls$1Inner
SourceFile: "LocalCls.java"
EnclosingMethod: #.# // LocalCls.print
InnerClasses:
#= #; //Inner=class LocalCls$1Inner
minor version:
major version:
flags: ACC_SUPER Constant pool:
# = Fieldref #.# // LocalCls$1Inner.this$0:LLocalCls;
# = Methodref #.# // java/lang/Object."<init>":()V
# = Methodref #.# // LocalCls.access$000:(LLocalCls;)I
# = Methodref #.# // LocalCls.access$002:(LLocalCls;I)I
# = String # // fsjohnhuang
# = Class # // LocalCls$1Inner
# = Class # // java/lang/Object
# = Utf8 this$
# = Utf8 LLocalCls;
# = Utf8 <init>
# = Utf8 (LLocalCls;)V
# = Utf8 Code
# = Utf8 LineNumberTable
# = Utf8 getVal
# = Utf8 ()I
# = Utf8 setVal
# = Utf8 (I)V
# = Utf8 getName
# = Utf8 ()Ljava/lang/String;
# = Utf8 SourceFile
# = Utf8 LocalCls.java
# = Utf8 EnclosingMethod
# = Class # // LocalCls
# = NameAndType #:# // print:()V
# = NameAndType #:# // this$0:LLocalCls;
# = NameAndType #:# // "<init>":()V
# = NameAndType #:# // access$000:(LLocalCls;)I
# = NameAndType #:# // access$002:(LLocalCls;I)I
# = Utf8 fsjohnhuang
# = Utf8 LocalCls$1Inner
# = Utf8 Inner
# = Utf8 InnerClasses
# = Utf8 java/lang/Object
# = Utf8 LocalCls
# = Utf8 print
# = Utf8 ()V
# = Utf8 access$
# = Utf8 (LLocalCls;)I
# = Utf8 access$
# = Utf8 (LLocalCls;I)I
{
final LocalCls this$;
flags: ACC_FINAL, ACC_SYNTHETIC LocalCls$1Inner(LocalCls);
flags: Code:
stack=, locals=, args_size=
: aload_0
: aload_1
: putfield # // Field this$0:LLocalCls;
: aload_0
: invokespecial # // Method java/lang/Object."<init>":()V
: return
LineNumberTable:
line : int getVal();
flags: Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LLocalCls;
: invokestatic # // Method LocalCls.access$000:(LLocalCls;)I
: ireturn
LineNumberTable:
line : void setVal(int);
flags: Code:
stack=, locals=, args_size=
: aload_0
: getfield # // Field this$0:LLocalCls;
: iload_1
: invokestatic # // Method LocalCls.access$002:(LLocalCls;I)I
: pop
: return
LineNumberTable:
line :
line : java.lang.String getName();
flags: Code:
stack=, locals=, args_size=
: ldc # // String fsjohnhuang
: areturn
LineNumberTable:
line :
}

LocalCls$1Inner.class

上述两个类文件与成员内部类的几乎一模一样,那么就是说内部类作用范围的限制其实是编译器的限制,而不是JVM的限制了。

注意:

1. 不能有public、protected、private和static作修饰;

2. 局部内部类中仅能访问方法或作用域内的常量,若访问的是变量则编译时会出错。

Q:为什么不能访问局部变量呢?

A:假设可以访问局部变量,那么要考虑的是如何引用到局部变量。

首先局部变量是存放在JVM栈帧中的局部变量表中,并且当方法执行完栈帧也随之弹出,也就是说局部变量所占的内存空间是短暂的(不稳定)。

假如局部变量A是基本类型的话,那么数据直接就存放在局部变量表中相应的Slots中,方法执行完就没了。那局部内部类中所访问的局部变量A到底是什么就无从得知了!             假如局部变量A是String类型或其他类类型,那么局部内部类中访问的局部变量A时就有两种方式了,第一种是访问String常量池中该字符串的地址,第二种是指向局部变量A的地址,然后通过变量A去访问String常量池中该字符串。

但上述这些均是在运行时才能决定,而编译时是无法正确地被描述出来。并且由于内部类将被编译成独立的类文件,访问其他类方法的局部变量的操作无法在类文件中描述。而常量则可以在内部类文件的常量池部分中被正确地描述,而JVM中处理时也十分简单高效。类文件的常量池条目将合并到运行时常量池中,因此外部和内部量访问的是同一个常量。

下面的Bytecodes表示内部类中直接将常量池中的常量压栈后作为返回值返回。

java.lang.String getName();
flags:
Code:
stack=, locals=, args_size=
: ldc # // String fsjohnhuang
: areturn
LineNumberTable:
line :

五、匿名内部类                          

匿名内部类其实是局部内部类的特殊形式。一般用来绑定事件监听处理程序上。Android示例:

class Outer{
public void subs(){
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub }
});
}
}

上述代码生成了一个继承OnClickListener类的匿名内部类,然后实例化匿名类的一个实例,然后以该实例作为参数调用setOnClickListener方法。
  并生成一个Outer.class和Outer$1.class类文件。

  注意事项与局部内部一样。

六、静态内部类                          

静态内部类定义在类下,只不过多了个关键字static。静态内部类只能访问外部类的静态字段和静态方法。
  而实例化静态内部类时只需 new 外部类.静态内部类() 。

七、总结                            

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4270044.html  ^_^肥仔John

八、参考                            

http://www.cnblogs.com/dolphin0520/p/3811445.html

Java魔法堂:内部类详解的更多相关文章

  1. java四种内部类详解

    一般来说,有4中内部类:常规内部类.静态内部类.局部内部类.匿名内部类. 一.常规内部类:常规内部类没有用static修饰且定义在在外部类类体中.   1.常规内部类中的方法可以直接使用外部类的实例变 ...

  2. Java基础(54):java四种内部类详解(转)

    一般来说,有4中内部类:常规内部类.静态内部类.局部内部类.匿名内部类.  一.常规内部类:常规内部类没有用static修饰且定义在在外部类类体中. 1.常规内部类中的方法可以直接使用外部类的实例变量 ...

  3. java中的内部类详解

    https://www.cnblogs.com/dolphin0520/p/3811445.html https://www.cnblogs.com/chenssy/p/3388487.html

  4. 【转】Java魔法堂:String.format详解

    Java魔法堂:String.format详解     目录     一.前言    二.重载方法     三.占位符     四.对字符.字符串进行格式化     五.对整数进行格式化     六. ...

  5. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  6. [转] Java内部类详解

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  7. JAVA基础——内部类详解

    JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...

  8. 【转】Java内部类详解

    一.内部类基础 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.广泛意义上的内部类一般来说包括这四种:成员内部类.局部内部类.匿名内部类和静态内部类.下面就先来了解一 ...

  9. Java内部类详解(一)

    (转自:http://blog.csdn.net/wangpeng047/article/details/12344593) 很多人对于Java内部类(Inner Class)都十分陌生,甚至听都没听 ...

  10. “全栈2019”Java第九十六章:抽象局部内部类详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

随机推荐

  1. openSUSE 11 上的配置可以Xmanager远程桌面

    openSUSE 11 上的配置(适用于默认图形环境为KDE的Linux): 1.配置KDM. openSUSE 11的默认图形环境为KDE,虽然可以同时安装GDM和KDM,但默认只启动了KDM.所以 ...

  2. 使用 Productivity Power Tools 高级扩展 来帮助你提高 VS2012 的工作效率

    Productivity Power Tools 高级工具是帮助开发者提高工作效率的, 用于 Visual Studio 2012 专业版(及以上) 的一组免费扩展. 本文大多数内容译自MSDN:ht ...

  3. 移动App开发需要更多的PaaS平台而不是IaaS

    时代的变迁,创业的大潮,越来越多的人关注了有点开发,越来越多的人了解了互联网服务术语:PaaS.IaaS.SaaS.BaaS等.今天大家在开发App的时候这么多复杂的云服务如何来选择呢? IaaS服务 ...

  4. 团队项目——站立会议DAY12

    第十二次站立会议记录: 参会人员:张靖颜,钟灵毓秀,何玥,赵莹,王梓萱 项目进展: 1.张靖颜:已经将部分代码完成,对一些模块化的功能进行扩展,对已具备的功能进行完善. 2.钟灵毓秀:对代码进行了修改 ...

  5. js跨浏览器的事件处理函数

    /* 跨浏览器的事件处理函数 */ var EventUtil = { addHandler : function(element,type,handler){ if(element.addEvent ...

  6. 使用NodeList

    理解NodeList.NamedNodeMap和HTMLCollection是整体透彻理解DOM的关键. 这三个集合都是“动态”的,也就是说:每当文档结构发生变化时,他们都会得到更新,他们始终保存的都 ...

  7. Linux创建用户(转)

    这篇文章总结的太好了,让我没动力新写一篇了,啊哈哈哈哈. 源自:http://www.cnblogs.com/ylan2009/articles/2321177.html Linux 系统是一个多用户 ...

  8. linnux 3

    kill [信号代码] 进程ID 以优雅的方式结束进程# kill -l PID-l选项告诉kill命令用好像启动进程的用户已注销的方式结束进程.当使用该选项时,kill命令也试图杀死所留下的子进程. ...

  9. 知方可补不足~利用LogParser将IIS日志插入到数据库

    回到目录 LogParser是微软开发的一个日志分析工具,它是命令行格式的,我们通过这个工具,可以对日志文件进行操作,对于一个几百兆的log文件,使用记事本打开是件很残酷的事,所以,很多情况下,我们都 ...

  10. EF架构~CodeFirst自关联表的插入

    回到目录 这个文章对之前EF的一个补充,对于一些自关联表的添加,如果你建立了表约束确实有这种问题,一般主键为整形自增,父ID为可空,这时,在添加时如果不为ID赋值,结果就会出错. 错误: 无法确定依赖 ...