Java 内部类浅析

目前已转至个人博客,本文地址:Lam's Blog - Knowledge as Action

一、引言

        由于目前是Android开发的实习生,在开发过程中发现越来越多自己知道但是真正去使用的时候却用不上的情况,比如内部类的使用上,所以在经过网上查找资料学习以及自己总结之后,特此发表一篇博文与大家分享,如有不当之处,万望指出。

二、内部类

内部类是指在一个外部类的内部再定义一个类,是一个编译时的概念,一旦编译成功,内部类与其外部类就会成为完全不同的两类,只是内部类的前面会冠以外部类的类名和$符号,如一个outer类内部定义了一个inner的内部类,那么编译完成后会生成outer.class和outer$inner.class两个类,因此内部类的成员变量与方法可以与外部类相同。
从内部类的定义上来看,其实内部类严重破坏了良好的代码结构,那么为什么还要使用内部类呢?因为内部类可以随意使用外部类的成员变量(包括私有的)而不用生成外部类对象,这也是内部类的唯一优点。

三、内部类的分类及示例

        内部类分为成员内部类、局部内部类、静态内部类和匿名内部类。下面针对各个内部类进行介绍:

    (1)成员内部类

 

        作为外部类的一个成员存在,与外部类的属性、方法并列。成员内部类中可以访问外部类的所有成员,当外部类的变量与内部类的变量名重复时,使用外部类名.this.变量名可以对外部类的变量进行调用,如果没有重复时可直接使用变量名进行调用。在普通外部类方法中访问静态内部类可new出成员内部类对象进而直接调用其中的方法,而在静态外部类方法中需要先new出外部类对象,再调用外部类对象名.new 内部类的方式来获得成员内部类对象。

        成员内部类中不能定义静态变量,因为成员内部类需要先创建了外部类,才能创建它自己的。

        成员内部类可以使用外部类的私有成员和属性,同时其在外部类中定义了不可访问的属性,这样就在外部类中实现了比外部类的private还要小的访问权限。

示例代码如下:

/**
 * 
 * @ClassName: MemberInnerClass
 * @Description: 成员内部类
 * @author ADAM
 * @date 2014年7月25日 上午9:20:28
 *
 */
public class MemberInnerClass {
	private int i = 0;

	public class Inner {// 成员内部类可加权限修饰符
		int i = 3;

		public void print() {
			System.out.println(i);// 输出为内部类的i,值为3
			System.out.println(this.i);// 输出为内部类的i,值为3
			System.out.println(MemberInnerClass.this.i);// 输出外部类的i,值为0
		}
	}

	public void print() {
		(new Inner()).print();// 外部类普通方法调用成员内部类直接new出内部类进行调用
	}

	public static void main(String[] args) {
		((new MemberInnerClass()).new Inner()).print();// 外部类静态方法中调用成员内部类首先new出外部类然后new出内部类进行调用
	}
}

    (2)局部内部类

        即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。局部内部类中不可定义静态变量,可以访问外部类(即方法)中的变量,但是变量必须是final。局部内部类与外部类的命名重名时的调用方法与成员内部类相同,访问局部内部类必须得先有外部类对象。

        在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要生成外部类对象,对象再去调用方法,在方法中才能调用其局部内部内。通过内部类与接口达到了一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。

示例代码如下:

/**
 * 
 * @ClassName: LocalInnerClass 
 * @Description: 局部内部类
 * @author ADAM
 * @date 2014年7月25日 上午10:36:26 
 *
 */
public class LocalInnerClass {
	private int j = 3;
	private int i = 2;

	public void test() {
		MemberInnerClass memberInnerClass = new MemberInnerClass();
// 若要访问其他类中的成员内部类只能当该成员内部类的修饰符为public才可
// 否则编译错误,即成员内部类可以再类内部实现比private还要小的权限
		memberInnerClass.new Inner();
		final int i = 0;
		class Inner {// 此处不得加上private等修饰符,否则编译错误
			int i = 1;

			public void print() {
				System.out.println(j);饰符,否则编译错误
				System.out.println(i);// 此时输出局部内部类的i,外部方法中的变量只能通过传参的形式进行调用
				System.out.println(LocalInnerClass.this.i);// 此时输出的是外部类中的i
			}
		}
		Inner inner = new Inner();// 局部内部类只能在方法内实例化并进行调用
		inner.print();
	}

	public static void main(String[] args) {
		(new LocalInnerClass()).test();
	}
}

         局部内部类不仅可以定义在方法中还可以定义在作用域内。以下提供两个Thinking in Java的例子

定义在方法内:

public class Parcel4 { 
    public Destination destination(String s) { 
        class PDestination implements Destination { 
            private String label; 
 
            private PDestination(String whereTo) { 
                label = whereTo; 
            } 
 
            public String readLabel() { 
                return label; 
            } 
        } 
        return new PDestination(s); 
    } 
 
    public static void main(String[] args) { 
        Parcel4 p = new Parcel4(); 
        Destination d = p.destination("Tasmania"); 
    } 
}  

定义在作用域里:

public class Parcel5 { 
    private void internalTracking(boolean b) { 
        if (b) { 
            class TrackingSlip { 
                private String id; 
                TrackingSlip(String s) { 
                    id = s; 
                } 
                String getSlip() { 
                    return id; 
                } 
            } 
            TrackingSlip ts = new TrackingSlip("slip"); 
            String s = ts.getSlip(); 
        } 
    } 
 
    public void track() { 
        internalTracking(true); 
    } 
 
    public static void main(String[] args) { 
        Parcel5 p = new Parcel5(); 
        p.track(); 
    } 
} 

         局部内部类也像别的类一样进行编译,只是作用域有所不同,只在该方法或者条件的作用域内才能使用,出了这些作用域则无法被使用。

    (3)静态内部类

         静态内部类也称为嵌套内部类,定义在类中,任何方法外,用static定义,静态内部类中可以定义静态或非静态的成员,其可用public、private等修饰符修饰,一般使用public修饰,方便调用,其只能访问外部类中的静态成员。外部类访问静态内部类中的静态成员时直接使用内部类.静态成员,访问静态内部类的非静态成员时先实例化静态内部类再通过对象访问。

         生成一个静态内部类不需要外部类对象成员,这是与成员内部类的区别,同时普通内部类不能有static方法和属性,也不能包含嵌套类,静态内部类的对象可以直接生成:外部类.内部类 对象名= new 外部类.内部类()即可

         注意:当类与接口(或者接口与接口)发生方法命名冲突时,必须使用内部类来实现。用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。

示例代码如下:

/**
 * 
 * @ClassName: StaticInnerClass 
 * @Description: 静态内部类
 * @author ADAM
 * @date 2014年7月25日 上午10:37:04 
 *
 */
public class StaticInnerClass {
	private int i = 1;
	public static int j = 1;
	public int k = 0;

	public static class Inner {
		private int i = 0;
		private static int j = 0;// 可定义静态与非静态成员

		public static void print() {
//			System.out.println(k);// 编译错误,不得调用外部类中的非静态成员
			System.out.println(new StaticInnerClass().i);//访问外部类中的非静态成员变量需要先实例化外部类
			System.out.println(j);
			System.out.println(new StaticInnerClass().j);// 静态内部类使用外部类中的静态成员
		}
	}

	public static void main(String[] args) {
		Inner.print();// 外部类静态方法或者非静态方法调用静态内部类中的静态成员时直接内部类名.成员即可
		StaticInnerClass.Inner inner = new StaticInnerClass.Inner();// 静态内部类的实例化不需要先实例化外部类
		System.out.println(inner.i);// 访问静态内部类中的非静态成员时需要先实例化静态内部类
	}
}

通过内部类实现多继承示例代码如下:

/**
 * 
 * @ClassName: Pencil 
 * @Description: 笔类
 * @author ADAM
 * @date 2014年7月25日 上午10:42:59 
 *
 */
public abstract class Pencil {
	public abstract void write();
}
/**
 * 
 * @ClassName: Eraser 
 * @Description: 橡皮擦类
 * @author ADAM
 * @date 2014年7月25日 上午10:43:28 
 *
 */
public abstract class Eraser {
	public abstract void erase();
}
/**
 * 
 * @ClassName: PencilWithEraser 
 * @Description: 多继承类
 * @author ADAM
 * @date 2014年7月25日 上午10:43:53 
 *
 */
public class PencilWithEraser {
	private MyPencil pencil = new MyPencil();
	private MyEraser eraser = new MyEraser();

	private class MyPencil extends Pencil {//继承笔类

		@Override
		public void write() {
			System.out.println("To Write");
		}

	}

	private class MyEraser extends Eraser {//继承橡皮擦类
		@Override
		public void erase() {
			System.out.println("To Erase");
		}

	}

	// 本类自己的方法,调用继承类中的方法
	public void write() {
		pencil.write();
	}

	public void erase() {
		eraser.erase();
	}

	public static void main(String[] args) {
		PencilWithEraser pencilWithEraser = new PencilWithEraser();
		pencilWithEraser.write();
		pencilWithEraser.erase();
	}
}

    (4)匿名内部类

          用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的重写或实现,也使用于只是为了得到一个对象实例,不需要知道其实际类型,此时类名是没有意义的,也不需要被使用到。
示例代码如下:

/**
 * 
 * @ClassName: AnonymousInnerClass 
 * @Description: 匿名内部类
 * @author ADAM
 * @date 2014年7月25日 上午10:38:25 
 *
 */
public class AnonymousInnerClass {
	public void click(OnClickListener onClickListener) {
		System.out.println("click,click");
	}

	public static void main(String[] args) {
		AnonymousInnerClass anonymousInnerClass = new AnonymousInnerClass();
		anonymousInnerClass.click(new OnClickListener() {// 直接new出匿名内部类,不需要保存对象,一般用于继承其他类或是实现接口
					@Override
					public void onClick() {
						System.out.println("Inner Click");
					}
				});
	}
}

四、总结

          由于目前还是处于学习阶段,对于内部类的使用最多的也是成员内部类和匿名内部类,所以对于诸如非常细致地去区分各个内部类的使用场景以及涉及到的更小的访问权限的作用等方面体会并不是很深刻,之后有更进一步的学习会补上,也希望看到这篇博文的朋友如果有一些其他的见解或者补充或者是推荐学习的资料也可以提出来,这篇博文也是借鉴网上各位大神以及百科的相关资料,再自己手动编码验证总结出来的,感恩各位大神,谢谢。



你可能感兴趣的:(学习收获)