java基础教程5:内部类和内部接口

内部类,顾名思义,是指在类的内部在定义一个类。内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两个类,类名为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<K,V> {
	...
	interface Entry<K,V> {
		....
	}
	...
}

对该接口的实现类如下

public abstract class AbstractMap<K,V> implements Map<K,V> {
	...
	public static class SimpleEntry<K,V>
        implements Entry<K,V>, java.io.Serializable{
		...
	}
	...
}

很显然,在典型应用中,内部接口用于提示实现类要设置内部类实现内部接口。设计思想是一种类似命名空间的区分。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值