内部类,顾名思义,是指在类的内部在定义一个类。内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两个类,类名为outer$inner.class。内部类的成员变量和方法可以和外部类相同。注意,在同一个.java文件中,互不包含的两个类不是内部类。
成员内部类不得含有static类型的变量和方法。
外部类使用内部类的成员和方法,只能通过内部类的对象
public class Sandbox1
{
public class Inner{
public void innerMethed(){
System.out.println("Inner Class 1");
OuterMethed();
}
public void OuterMethed(){
System.out.println("NOT Outer Class");
}
}
public static void main(String args[])
{
Sandbox1 outer = new Sandbox1();
Inner inner =outer.new Inner();
inner.innerMethed();
}
public void OuterMethed(){
System.out.println("Outer Class");
}
}
//运行结果
//Inner Class 1
//NOT Outer Class
//Outer Class
上面这段程序有三个注意的地方:
+ 内部类的定义方式;
+ 内部类可以直接引用外部类的方法和变量,包括private,如果函数名相同,优先使用内部类自己的同名函数;
+ 可以用outer.this来表示内部对象
应用案例
public class LinkedList<T> {
class Node {
T data;
Node prev;
Node next;
}
Node fst;
Node lst;
}
这里当然可以把Node单独拆出去,不过,这就增加了一个高耦合的类。毫无必要。遵循高内聚的原则进行如此的设计。
因为成员内部类不含有静态成员,所以只能以对象形式被使用。必须先实例化外部类的对象,才能实例化内部类的对象。
在函数或者其他形式的作用域中使用
局部内部类在class前面不作任何修饰,只有效域自己所处的作用域内
其他和成员局部类一致
public class Sandbox1
{
public static void main(String args[])
{
class Inner{
public void innerMethed(){
System.out.println("Inner Class 1");
OuterMethed();
}
}
Sandbox1 outer = new Sandbox1();
Inner inner =new Inner();
inner.innerMethed();
}
public static void OuterMethed(){
System.out.println("Outer Class");
}
}
指声明为static的内部类。
普通内部类中不能含有嵌套内部类。嵌套内部类可以包含static的成员
嵌套内部类不能声明为private。
嵌套类只能使用外部类的static的成员和方法
public class SandBoxMain
{
static {
System.out.println("Outer Class INIT");
}
public static void main(String args[])
{
System.out.println("Main Func Here");
System.out.println(SandBoxMain.Inner.i.toString());
SandBoxMain outer = new SandBoxMain();
Inner inner =new SandBoxMain.Inner();
inner.innerMethed();
}
public static class Inner{
static {
System.out.println("Inner Class INIT");
}
static Integer i = 10;
public void innerMethed(){
System.out.println("Inner Class 1");
OuterMethed();
}
}
public static void OuterMethed(){
System.out.println("Outer Class");
}
}
上述代码的输出为
Outer Class INIT
Main Func Here
Inner Class INIT
10
Inner Class 1
Outer Class
我们可以发现,静态内部类虽然是静态的,但是却不和外部类一起初始化。只有在使用到静态内部类时,才会将其初始化。是不是有点按需加载的意思?所以,静态内部类一种应用就是用于实现多线程并发下的单例模式
public class Singleton {
private Singleton(){
}
private static class SingletonHolder{
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
实际应用
//JDK Integer类
public final class Integer extends Number implements Comparable<Integer> {
...
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
...
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
在上述应用中,Integer类利用IntegerCache内部类为-128~127的integer类对象创建了缓存。之所以这么采取静态内部类,个人推测就是要使用按需加载的特性。因为Interger类同样也是处理int的工具类。如果代码里只使用int,就没必要维护Integer元素缓存了。
因为没有名字,所以一定只能实现一次。而且,匿名内部类必须继承一个父类或者一个接口。这点看程序可以看出来(作为唯一使用一次时的介质)。
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
匿名内部类的设计思想是如果一个类只使用一次,单独写一个内部类是在是过于奢侈。
在GUI编程时我们经常可以看到使用匿名内部类的回调,事实上,JAVA就是使用内部类实现C++函数指针的回调的。
闭包(closure),无论时中英文都不好理解。闭包是一个可调用的对象。 设想一下,我们设计一个可传入可回调对象
class callBack implements candosomething{
dosomething(){
}
}
class needCallBack(){
candosomething a;
set(candosomething b){
a = b;
}
//在执行needCallBack的do方法是会回调callBack类的方法dosomething方法。
do(){
...
a.dosomething();
...
}
}
上述设计的最大问题是,我们将callBack对象的引用存储在了needCallBack对象中。这毫无必要!b对象只需要能找到a对象暴露的回调方法就可以了!所以,我们的needCallBack需要存储一种对象,这个对象只能访问a对象提供的回调方法即可。所以,完全可以采取内部类实现。传给needCallBack对象一个callBack对象内部类的对象,通过这个对象,needCallBack可以完成回调。
既然类可以包含内部类。那么接口也可以包含内部接口吧。
在java设计中,最明显的例子就是MAP接口中的Entry接口。
public interface Map {
...
interface Entry {
....
}
...
}
对该接口的实现类如下
public abstract class AbstractMap<K,V> implements Map<K,V> {
...
public static class SimpleEntry<K,V>
implements Entry<K,V>, java.io.Serializable{
...
}
...
}
很显然,在典型应用中,内部接口用于提示实现类要设置内部类实现内部接口。设计思想是一种类似命名空间的区分。