阅读 339

《Java编程思想》笔记07------内部类

一、创建内部类

/**
 * 内部类的基本使用
 * @author LiangYu
 * 2018-01-09
 */
public class InnerClassTest {
	@Test
	public void test() throws Exception {
		ShowClass show1 = new ShowClass();
		show1.doSomething();
		ShowClass show2 = new ShowClass();
		//如果是在静态方法中,则需要强制写成:外部类.内部类
		ShowClass.InnerClass1 innerClass1 = show2.getInnerClass1();
		ShowClass.InnerClass2 innerClass2 = show2.getInnerClass2();
	}
}

class ShowClass{
	class InnerClass1{
		public InnerClass1() {
			System.out.println("InnerClass1");
		}
	}
	
	class InnerClass2{
		public InnerClass2(){
			System.out.println("InnerClass2");
		}
	}
	
	public InnerClass1 getInnerClass1(){
		return new InnerClass1();
	}
	
	public InnerClass2 getInnerClass2(){
		return new InnerClass2();
	}
	
	public void doSomething(){
		InnerClass1 innerClass1 = getInnerClass1();
		InnerClass2 innerClass2 = getInnerClass2();
		System.out.println("doSomething");
	}
}
复制代码

所谓的内部类其实就是一个普通类嵌套在内部。

二、链接到外部类

/**
 * 内部类与外部类的链接(以及适配器模式的使用)
 * @author LiangYu
 * 2018-01-09
 */
public class InnerClassTest2 {
	@Test
	public void test() throws Exception {
		TestList testList = new TestList(5);
		for(int i = 0 ; i < 5 ; i++){
			testList.add(Integer.toString(i));
		}
		Selector selector = testList.selector();
		while(!selector.end()){
			System.out.println(selector.current());
			selector.next();
		}
	}
}

interface Selector{
	boolean end();
	void next();
	String current();
}

class TestList{
	private String[] items;
	private int next = 0;
	public TestList(int size){
		items = new String[size];
	}
	public void add(String text){
		if(next < items.length){
			items[next] = text;
			next++;
		}
	}
	
	private class ListSelector implements Selector{
		private int i = 0;
		@Override
		public boolean end() {
			if(items.length == i){
				return true;
			}
			return false;
		}

		@Override
		public void next() {
			if(i < items.length){
				i++;
			}
		}

		@Override
		public String current() {
			return items[i];
		}
		
	}
	
	public Selector selector(){
		return new ListSelector();
	}
}

复制代码

内部类ListSelector使用了外部类中的私有变量items,由此可以看出,外部类对象拥有创建它外部类对象的所有成员的访问权。

原理:外部类创建内部类对象时,内部类对象会捕获一个指向外部类对象的引用,通过这个引用可以访问外部类的所有成员。

二、使用.this和.new


/**
 * .this和.new的使用
 * @author LiangYu
 * 2018-01-09
 */
public class InnerClassTest3 {
	@Test
	public void test() throws Exception {
		OuterClass outerClass = new OuterClass();
		InnerClass innerClass = outerClass.getInnerClass();
		OuterClass outerClass2 = innerClass.getOuterClass();
	}
}

class OuterClass{
	public OuterClass() {
		System.out.println("Constructor OuterClass");
	}
	
	class InnerClass{
		public InnerClass() {
			System.out.println("Constructor InnerClass");
		}
		
		public OuterClass getOuterClass(){
			return OuterClass.this; //通过.this的方式,获取外部类对象
		}
	}
	
	public InnerClass getInnerClass(){
		return this.new InnerClass();  //通过.new的方式,获取内部类的对象
	}
}
复制代码

三、内部类与向上转型

/**
 * 内部类与向上转型
 * @author LiangYu
 * 2018-01-10
 */
public class InnerUpCastTest {
	
	@Test
	public void test() throws Exception {
		InnerExample1 example = new InnerExample1();
		ShowNumber showNumber = example.showNumberImpl();
		ShowText showText = example.showTextImpl("hello");
		InnerExample1.ShowNumberImpl showNumber2 = example.new ShowNumberImpl();
//		InnerExample1.ShowText showText2 = example.new ShowTextImpl();   编译失败,因为内部类是私有的
	}
}

interface ShowText{
	String getText();
}

interface ShowNumber{
	int value();
}

class InnerExample1{
	private class ShowTextImpl implements ShowText{
		private String text;
		private ShowTextImpl(String text) {
			this.text = text;
		}
		@Override
		public String getText() {
			return text;
		}
	}
	
	protected class ShowNumberImpl implements ShowNumber{
		private int i = 11;
		
		@Override
		public int value(){
			return i;
		}
	}
	
	public ShowTextImpl showTextImpl(String s){
		return new ShowTextImpl(s);
	}
	
	public ShowNumberImpl showNumberImpl(){
		return new ShowNumberImpl();
	}
}
复制代码

private内部类可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现细节。只需要通过showTextImpl()、showNumberImpl()来获取对象。

四、在方法和作用域内的内部类


/**
 * 通过局部内部类的方法,对之前的代码进行修改
 * @author LiangYu
 * 2018-01-10
 */
public class InnerUpCastText2 {
	@Test
	public void test() throws Exception {
		InnerExample2 example2 = new InnerExample2();
		ShowNumber showNumber = example2.showNumber();
		ShowText showText = example2.showText("haha");
		System.out.println(showNumber.value());
		System.out.println(showText.getText());
	}
}

class InnerExample2{
	public ShowText showText(String s){
		class ShowTextImpl implements ShowText{
			private String text;
			public ShowTextImpl(String text) {
				this.text = text;
			}
			@Override
			public String getText() {
				return text;
			}
		}
		return new ShowTextImpl(s);
	}
	public ShowNumber showNumber(){
		class ShowNumberImpl implements ShowNumber{
			private int number = 11;
			@Override
			public int value() {
				return number;
			}
		}
		return new ShowNumberImpl();
	}
}
复制代码

局部内部类与其他类共同编译,但是只在作用域内可用,在其他作用域中使用相同的类名不会有命名冲突。

五、匿名内部类

/**
 * 通过匿名内部类的方式修改之前的代码
 * @author LiangYu
 * 2018-01-10
 */
public class InnerUpCastTest3 {
	@Test
	public void test() throws Exception {
		InnerExample3 example3 = new InnerExample3();
		ShowNumber showNumber = example3.showNumber();
		ShowText showText = example3.showText("hehe");
		System.out.println(showNumber.value());
		System.out.println(showText.getText());
	}
}


class InnerExample3{
	public ShowText showText(String text){
		return new ShowText() {
			@Override
			public String getText() {
//				text = "hehehe";  Local variable text defined in an enclosing scope must be final or effectively final

				return text;
			}
		};
	}
	
	public ShowNumber showNumber(){
		return new ShowNumber() {
			private int num = 11;
			@Override
			public int value() {
				return num;
			}
		};
	}
}
复制代码

在showNumber()方法的内部,在返回了一个ShowNumber引用的时候,插入了一个类的定义。这里实际的情况是,创建了一个继承自ShowNumber接口的匿名类的对象,通过new表达式返回的时候,实际上已经向上转型为对ShowNumber的引用了。

初始化的过程:在实例初始化的内部,实现了匿名类的初始化。

在代码中,text变量无法被修改,提示错误信息:“Local variable text defined in an enclosing scope must be final or effectively final”

即:内部类使用外部类的对象被看作是final的。即:初始化值之后不能被修改。

对于final类型来说: 编译器编译后,final类型是常量,被存储到了常量池中,在匿名内部类中使用该变量的地方都被替换成了具体的常量值,关于外部类的变量的信息,内部类是不知道的。

对于非final类型来说:传入内部类的仅仅只是传值操作,所以在匿名内部类中改变这个值是无效的。如果在外部类中修改这个值,那么匿名内部类得到的参数值可能已经不是期望中的那个值。所以,在内部类使用外部类的变量时,不允许做任何修改才会避免所以问题。

JAVA8版本对于非final类型会进行检查,要求不允许修改。

六、嵌套类

如果不想使内部类对象与外部类对象相互联系,那么可以将内部类声明为static,这就是嵌套类。

非静态内部类必须通过外部类对象创建并且获得一个该外部类对象的引用。

对于静态内部类:

1.创建静态内部类的对象并不需要外部类的对象。

2.静态内部类中不能使用外部类的非静态成员(因为没有外部类的引用)

3.静态内部类中可以创建静态类中的数据和字段(普通内部类不可以,因为它通过外部类对象创建,不属于类成员)

/**
 * 嵌套类(静态内部类的使用)
 * @author LiangYu
 * 2018-01-10
 */
public class StaticInnerClassTest {
	@Test
	public void test() throws Exception {
		//可以直接使用
		InnerStaticClass innerStaticClass = new InnerStaticClass();
		System.out.println(innerStaticClass.getNum());
	}
}

class InnerClassExample4{
	 static class InnerStaticClass{
		static int num = 11; //可以创建静态变量
		int getNum(){
			return num;
		}
	 }
}
复制代码

七、接口内部的类

/**
 * 接口中的内部类
 * @author LiangYu
 * 2018-01-10
 */
public class InterfaceInnerClassTest {
	@Test
	public void test() throws Exception {
		InnerClassInterface.ShowText showText = new InnerClassInterface.ShowText();
		showText.sayHello();
	}
}

interface InnerClassInterface{
	void sayHello();
	static class ShowText implements InnerClassInterface{
		@Override
		public void sayHello() {
			System.out.println("Hello World");
		}
		
	}
}
复制代码

嵌套类可以实现它命名空间下的接口。接口中的内部类即使是静态的也不能够直接使用,需要“接口.类名”的形式

八、为什么需要内部类

内部类可以有效的实现“多重继承”

/**
 * 多继承
 * @author LiangYu
 * 2018-01-10
 */
public class MultiinheritanceTest {
	@Test
	public void test() throws Exception {
		NultiinheritanceExample example = new NultiinheritanceExample();
		String msg = "QwErTyUiOpAsDfGhJkL";
		example.showMessage(msg);
		B upperB = example.getUpper(msg);
		B lowerB = example.getLower(msg);
		upperB.changeText();
		lowerB.changeText();
	}
}

class A{
	void showMessage(String msg){
		System.out.println("A "+msg);
	}
}

abstract class B{
	abstract void changeText();
}

//实现了多重继承
class NultiinheritanceExample extends A{
	B getUpper(String text){
		return new B() {
			@Override
			void changeText() {
				System.out.println(text.toUpperCase());
			}
		};
	}
	
	B getLower(String text){
		return new B() {
			@Override
			void changeText() {
				System.out.println(text.toLowerCase());
			}
		};
	}
}
复制代码

8-1 闭包与回调

闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。

内部类是面向对象的闭包,它不仅包含了外部类对象的信息,还自动拥有一个指向该外部类对象的引用,在此作用域下,内部类有权操作所有的成员,包括private成员。

Java可以通过内部类提供的闭包功能,实现回调

/**
 * 回调功能:模拟Android系统按钮点击的回调机制
 * @author LiangYu
 * 2018-01-10
 */
public class CallbackTest {
	@Test
	public void test() throws Exception {
		Activity activity = new Activity();
		activity.click();
	}
}

interface OnClickListener{
	void onClick();
}

class Button{
	private OnClickListener onClickListener;
	
	void setOnClickListener(OnClickListener onClickListener){
		this.onClickListener = onClickListener;
		
	}
	
	//执行Click
	void performClick(){
		if(onClickListener != null){
			onClickListener.onClick();
		}
	}
}

class Activity{
	Button button1 = new Button();
	Button button2 = new Button();
	void click(){
		button1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick() {
				System.out.println("点击1号按钮");
			}
		});
		button2.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick() {
				System.out.println("点击2号按钮");
			}
		});
		button1.performClick();
		button2.performClick();
	}
}
复制代码

所谓的回调,其实就是先实现了抽象方法,但是不执行,直到某事件被触发后才会执行。

九、内部类的继承

/**
 * 内部类被继承
 * @author LiangYu
 * 2018-01-10
 */
public class InnerClassExtendsTest {
	@Test
	public void test() throws Exception {
		SonInner sonInner = new SonInner(new ExampleClass5(666));
	}
}

class ExampleClass5{
	public ExampleClass5(int i){
		System.out.println(i);
	}
	class InnerClass{
	}
}

class SonInner extends InnerClass{
	public SonInner(ExampleClass5 exampleClass5) {
		exampleClass5.super();
	}
}
复制代码

在继承内部类的类中,必须获取一个创建内部类的外部类对象的引用,通过这个引用调用内部类父类进行初始化操作。

十、内部类被覆盖

/**
 * 内部类被覆盖
 * @author LiangYu
 * 2018-01-11
 */
public class CoverInnerTest {
	@Test
	public void test() throws Exception {
		Parent parent = new Child();
		parent.parentMethod();
	}
}

class Parent{ 
	private InnerClass innerParent = new InnerClass();  //先于构造器之前,初始化变量
	class InnerClass{
		public InnerClass() {
			System.out.println("Parent Inner Constructor");
		}
		public void method(){
			System.out.println("Parent Inner Method");
		}
	}
	public Parent() {
		System.out.println("Parent Constructor");
	}
	
	public void changeInner(InnerClass innerParent){
		innerParent = this.innerParent;
	}
	
	public void parentMethod(){
		innerParent.method();
	}
}

class Child extends Parent{
	class InnerClass extends Parent.InnerClass{
		public InnerClass(){
			System.out.println("Child Inner Constructor");
		}
		public void method(){
			System.out.println("Child Inner Method");
		}
	}
	public Child() {
		changeInner(new InnerClass());
	}
}
复制代码

输出结果:

Parent Inner Constructor

Parent Constructor

Parent Inner Constructor

Child Inner Constructor

Parent Inner Method

过程分析:

1.在test()方法中,首先new Child(),调用了Child构造器,然而,Child继承自Parent,因此,首先初始化Parent。

2.在初始化Parent类的时候,首先对字段innerParent初始化。所以,通过new InnerClass()调用了父类内部类的构造方法,因此,输出Parent Inner Constructor

3.执行父类构造方法,输出Parent Constructor

4.返回到子类中,执行子类构造方法:changeInner(new InnerClass()),首先执行方法中的new InnerClass()

5.调用子类的内部类构造函数,由于子类内部类继承自父类内部类,因此,首先执行父类内部类的构造方法

6.执行父类内部类的构造方法,输出Parent Inner Constructor

7.返回到子类的内部类,输出Child Inner Constructor

8.此时,Child类已经构造完毕,由test()方法中,向上转型为Parent类。

9.执行parent.parentMethod(),调用innerParent.method(),输出Parent Inner Method

关注下面的标签,发现更多相似文章
评论