(1) 在类中定义一个类(私有内部类,静态内部类)
(2) 在方法中定义一个类(局部内部类,匿名内部类)
1、私有内部类 —— 在方法之间定义的内部类,非静态
- //代码1:内部类对外部类可见
- class
Outer{ -
//创建私有内部类对象 -
public Inner in=new Inner(); -
//私有内部类 -
private class Inner{ -
... -
} - }
- //代码2:外部类对内部类可见
- class
Outer{ -
//外部类私有数据域 -
private int data=0; -
//内部类 -
class Inner{ -
void print(){ -
//内部类访问外部私有数据域 -
System.out.println(data); -
} -
} - }
- //反编译1
- class
Outer$Inner - {
-
Outer$Inner(Outer,Outer$Inner); //包可见构造器 -
private Outer$Inner(Outer); //私有构造器将设置this$0域 -
final Outer this$0; //外部类实例域this$0 - }
总结一下编译器对类中内部类做的手脚吧:
(1)
(2)
这样,类中定义的内部类无论私有,公有,静态都可以被包围它的外部类所访问。
2、静态内部类
- package
hr.test; - //代码3:静态内部类对外部变量的引用
- public
class Outer{ -
private static int i=0; -
//创建静态内部类对象 -
public Inner in=new Inner(); -
//静态 -
private static class Inner{ -
public void print(){ -
System.out.println(i); //如果i不是静态变量,这里将无法通过编译。 -
} -
} -
- }
3、局部内部类 —— 在方法中定义的内部类
- class
Outter{ -
public void outMethod(){ -
final int beep=0; -
class Inner{ -
//使用beep -
} -
Inner in=new Inner(); -
} - }
内部类的特点总结
(1)
(2) 在方法间定义的静态内部类:
(3) 在方法中定义的局部内部类:
(4) 在方法中定义的匿名内部类:
-------------------------------------------------------------------------------------
方法内部类是JAVA里边非常尴尬的角色,基本上属于食之无味,弃之不惜。但它的使用陷阱非常多。最大的陷阱就是局部方法内部类不能访问方法内的局部变量。人们都会觉得非常之不解,我到自家粮库里拿点粮食,还不行了?!大家都属于一个组织,同在一个方法下,何必逼人太甚。可是就是不行。因为方法内部类对象,那也是堆对象,它生存能力就是比较你局部变量(栈上分配)强,当方法调用结束时,它产生的这个对象还存在着。可能有人觉得非常不服气,方法调用都结束了,你这个类对象还赖着,有什么意思?可是方法是可以返回对象引用的,这个局部方法内部类,可能是实现或者继承了某个类,返回给父类引用,那是完全合情合理的。如果真要使用局部变量,那么在变量前加final,有了final这道护身符,就是跳上枝头当凤凰了,可以被编制入常量区,而不用在局部方法经常混的栈区,那么这个局部变量的生存能力,就可以和对象一样强了。另外,局部方法内部类可以使用final,和 abstract 的修饰符。其它修饰符都不行!最后,局部方法内部类,可以访问外部类的任何数据成员,包括private成员。下边这个例子,是关于局部方法内部类与内部类,如何匹配。可以尝试解一下,答案见最后。
class A {
}
public class TestInners {
}
----------------------------------------------------------------------------------------
在执行go方法是,它是先实例化TestInners里面的A类(midlle类)因为他会屏蔽掉外部outer的A类,不实例化inner类是因为inner类处在其后,我在最后的时候加了一句new A().m();他会实例化最近的A类,(屏蔽掉了middle的A类),所以在实例化过程中当出现同名的时候,一般都是实例化最近的类。new A时,查找最近的类A,TestInnner类中的A类,屏蔽了外部的A类,而go函数中定义的A类,在new A()后面,似乎也不能提前使用。
输出:
middle
--------------------------------------------------------------------------------------
在搞Spring时,有很多方法都用到了回调函数,因而涉及到了更多的内部类,在使用内部类时,发现从内部类中访问局部变量需要被声明为最终类型(final),有些迷惑,找了一些分析文章。如下:
这是从编译器的角度去分析的:
class A
{
public void shout(final
{
class B
{
public void shout1()
{
System.out.println(iargs);
}
}
B b=new B();
b.shout1();
}
}
class C
{
public static void main(String [] args)
{
A a=new A();
a.shout(5);
}
}这段代码...为什么要加final我觉得这个外部类的方法直接把参数传进去...执行方法...产生一个B对象调用这个shout1();就可以了.为什么非要加final?我知道一个方法中局部变量使用完之后就被释放掉了.而final定义的就超过了这个外部方法中的生命周期...但是就是搞不懂啊..能自己讲讲吗?
------------------解释如下------------------
局部内部类直接访问在其外部定义的对象(包括普通变量),编译器要求参数引用必须是final的。
其中:
1. 必须是局部内部类,显然包括匿名内部类;
2. 内部类访问外部类的对象必须是直接访问。
看下面的代码,注意a并不需要是final的:
类A1:
class A1{
}
类A:
class A{
public static void main(String [] args) {
}
}
再看这个代码:
class A1{
}
类A:
class A{
}
类C:
class C{
}
语句 c.shoutc(a.shout(5)); 在 a.shout(5) 得到返回值后,a 的 shout()方法栈被清空了!
即iargs不存在了,而c.shoutc()却又调用了 b.shout1(); 语句去执行 System.out.println(iargs); 你不觉得很诡异吗?呵呵。
我们再来看java虚拟机是怎么实现这个诡异的访问的:有人认为这种访问时之所以能完成是因为iargs是final的,由于变量的生命周期,事实是这样的吗?方法栈都不存在了,变量即使存在,怎么可能能访问到?试想下:一个方法能访问另一个方法的定义的final局部变量吗(不通过返回值)?
研究一下这个诡异的访问执行的原理,用反射探测一下局部内部类
编译器会探测局部内部类中是否有直接使用外部定义变量的情况:如果有访问就会定义一个同类型的字段然后在构造方法中用外部变量给自己定义的字段赋值而后局部内部类所使用的变量都是自己定义的字段!当然就可以访问!见下:
class A1$1$B
{
A1$1$B(A1, int);
private final int var$iargs;
private final A1 this$0;
}
A1$1$B 类型的对象会使用var$iars变量,而不是shout()方法中的final int iargs变量,当然就可以访问了!
OK,终于到正题了,为什么是final,即使外部变量不是final,编译器也可以如此处理:自己定义一个同类型的变量,然后在构造方法中赋值就行了。
原因就是为了让我们能够挺合逻辑的直接使用外部变量,而且看起来是在始终使用 iargs 变量(而不是赋值以后的自己的字段)!
考虑出现这种情况:局部内部类使用的是外部的变量iargs,而且又对这个变量作了变值操作,如:iargs++,根据前面的分析,如果编译器允许iargs不是final的,那么,改变的是 var$iargs,而iargs并没有变!仍然是5(var$iargs才是6)。为了避免这样如此不合逻辑的事情发生:你用了外部变量,又改变了变量的值,但那个变量死活没有变化!自然的iargs被强行规定必须是final 所修饰的(让两个值永远一样,或所指向的对象永远一样,后者可能更重要)!