初步了解Java内部类

参考:
Java核心技术
Java编程思想
https://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/chenssy/p/3388487.html
局部内部类:http://baijiahao.baidu.com/s?id=1600724548575261991&wfr=spider&for=pc
外部类和内部类数据访问:https://blog.csdn.net/weixin_40707866/article/details/79652084

内部类的概念

将一个类的定义放在另一个类的定义内部,这就是内部类。虽然内部类存在于外部类定义之中,但是编译之后还是会产生属于自己的.class文件,依旧遵循着每个类都会产生一个.class文件。一般来说内部类包括四种:成员内部类、局部内部类、匿名内部类和嵌套类(也叫静态内部类)。其中局部内部类定义在方法或作用域内,静态内部类使用static修饰。

内部类与外部类之间的数据访问

内部类就相当于一个外部类的成员变量,所以可以直接访问外部类的成员,包括成员变量和成员函数。但是外部类不能直接访问内部类成员,必须通过创建内部类实例的方法进行访问。

为什么使用内部类

如果没有内部类提供的,可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。使用内部类的最大好处就是它使得Java的多重继承的解决方案更加完整,因为内部类允许继承多个非接口类型(即类和抽象类)
当多重继承应用于接口时,那么看不出使用内部类的必要性。因为使用多实现仍然可以实现功能,但是如果拥有的是抽象类或具体的类,那么就只能使用内部类实现多重继承。

package learninnerclass;

interface A{}
interface B{}

class X implements A,B{     }

class Y implements A{
	B makeB(){
		return new B(){};
	}
}

public class MultiInterfaces {
	static void takesA(A a){}
	static void takesB(B b){}
	
	public static void main(String[] args) {
		X x = new X();
		Y y = new Y();
		
		takesA(x);
		takesA(y);
		takesB(x);
		takesB(y.makeB());
	}
}

只能使用内部类实现多重继承的情况

package learninnerclass;

class D{}
abstract class E{}
class Z extends D{
	E makeE(){ return new E(){};}
}

public class MultiImplementation {
	static void takesD(D d){}
	static void takesE(E e){}

	public static void main(String[] args){
		Z z = new Z();
		takesD(z);
		takesE(z.makeE());
	}
}

.this和.new

内部类的对象有一个隐式引用(即.this),它引用了实例化该内部对象的外部类对象。通过这个引用,可以访问外部类对象的全部状态。这个引用在内部类的定义中是不可见的,是由编译器自动生成的。具体使用如下:

外部类名.this;   // 该表达式表示外部类的引用

外部类对象.new 内部类名(构造方法的参数);   // 该表达式表示创建内部类对象

外部类名.内部类名;  // 该表达表示在外部类的作用域之外,引用内部类

成员内部类

在成员内部类中要注意两点,1) 成员内部类中不能存在任何static的变量和方法,static只能用在静态常量的声明上;2)成员内部类是依附于外部类的,所以只有先创建了外部类对象才能够创建内部类对象。
在成员内部类的定义中可以对外部类所有元素进行访问,即使该元素使用private修饰。其实这就相当于将成员内部类当做外部类的一个元素(成员)了。正因为如此,所以可以使用private、包访问权限、protected、public等权限修饰符来修饰成员内部类,而不像外部类只能使用public或包访问权限来修饰。
在非静态内部类中,不能定义静态的成员变量和成员方法,包括成员内部类,局部内部类,匿名局部类,但可以在静态内部中定义静态的成员变量和方法。值得注意的是,可以在所有的内部类中定义静态常量,即static final
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类定义中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向该内部类对象的引用来访问内部类的域或者方法。
不过需要注意的是,当成员内部类拥有和外部类同名的成员变量或方法时,会发生隐藏现象。默认情况下,访问的是成员内部类的成员。如果想要访问外部类的同名成员,则需要使用下面的形式进行访问。

示例

一般示例

package com.thinkinginjava.chapter10;

public class Parcel3 { // 外部类

	class Contents{  // 成员内部类
		private int i = 11;
		public int value() { return i;  }	
	}

	class Destination{// 成员内部类
		private String label;
		Destination(String whereTo) {
			label = whereTo;
		}
		String readLabel() { return label;}
	}
	
	public static void main(String[] args) {
		Parcel3 p = new Parcel3();  // 生成外部类对象
		// 必须通过外部类对象才能创建内部类对象
		Parcel3.Contents c = p.new Contents();  // 生成Contents成员内部类对象
		System.out.println(c.value());
		Parcel3.Destination d = p.new Destination("Suzhou");// 生成Destination成员内部类对象
		System.out.println(d.readLabel());
	}
}

访问同名属性

class MemberInnerClass{
	private String str = "外部类同名属性";// 与内部类数据有重名
	
	class Contents{  // 成员内部类
		private String str = "内部类同名属性";
		public String value() { return str;  }// 只会访问到内部类的同名属性
		// 要在内部类中访问外部类同名属性,需要借助外部类对象
		public String FatherValue() { return new MemberInnerClass().str;  }
	}
	
	public void useInnerClass(){
		System.out.println("在外部类中,str = "+str); //访问同名变量(只会访问到外部类的)
		// 要想在外部类中访问内部类同名属性需要借助内部类对象
		Contents c = new Contents();
		System.out.println("Contents中的域 :"+c.str);
		System.out.println("Contents中的方法 value: "+ c.value());
	}
}

public class MemberInnerClassTest {
	
	public static void main(String[] args) {
		MemberInnerClass mic = new MemberInnerClass();
		MemberInnerClass.Contents c = mic.new Contents();
		
		System.out.println("通过内部类的方法访问内部类的属性 str:"+c.value());
		System.out.println("通过内部类的方法访问外部类的属性 str:"+c.FatherValue());
		System.out.println("*****************");
		mic.useInnerClass();
	}
}

匿名内部类

匿名内部类的特点:1)没有名字的局部内部类;2)匿名内部类是一种没有构造器的类;3)匿名内部类没有访问修饰符和static修饰符;4)匿名内部类其实是隐式地继承某一个父类或者实现某一个接口。这句话的意思就是如果声明匿名内部类,前提是这个父类或者接口必须先存在。如下代码所示,Contents是一个早已存在的接口,如果Contents接口不存在则无法创建匿名内部类。换句话说,匿名内部类是实现一个接口或者继承一个父类并重写的简洁方式。

示例

package learninnerclass;
// 已存在的一个接口
interface Contents{
	int value();
}
public class AnonymousInnerClassTest {
	
	public Contents contents() {
		return new Contents() {   // 匿名内部类
			private int i = 11;
			public int value() { return i;}
		};
	}
	
	public static void main(String[] args) {
		AnonymousInnerClassTest p = new AnonymousInnerClassTest();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

其实上面的代码,如果不使用匿名内部类,则等价于下面的写法。

public class Parcel7b {
	// 继承接口并实现
	class MyContents implements Contents{
		private int i =11;
		public int value() { return i;   }
	}
	
	public Contents contents() {   return new MyContents();   }
	
	public static void main() {
		Parcel7b p = new Parcel7b();
		Contents c = p.contents();
		System.out.println(c.value());
	}
}

局部内部类

局部内部类定义在方法或作用域(代码块)内,不能使用任何访问修饰符。局部内部类的优势在于,除包含该类的方法,其他代码均不知道局部内部类的存在。同其他内部类比较,局部内部类不仅可以访问包含它们的外围类,而且还可以访问局部变量。
1) 如果局部内部类定义在静态方法中,它可以访问外部类中所有静态成员,包含私有。
2) 如果局部内部类定义在实例方法中,它可以访问外部类中所有的成员,包含私有。

除此之外,局部内部类还可以有构造方法。如果局部内部类要访问局部变量,那么局部变量必须声明为final类型。在实践中,局部内部类是所有内部类中最少使用的一种形式。

示例

package learninnerclass;

interface Counter{
	int next();
}

public class LocalInnerClass {
	
	private int count = 10;
	private static double salary = 13.14;
	
	public int getNum(){
		return count;
	}
	public static double getSalary(){
		return salary;
	}
	// 1.在实例方法中的局部内部类
	Counter getCounter(final String name)
	{
		class LocalCounter implements Counter
		{   //局部内部类中可以有构造方法
			public LocalCounter(){
				System.out.println("局部内部类的构造器...");
			}
			@Override
			public int next() {
				System.out.println("name = "+name);
				// 1.1 实例方法中的局部内部类能访问外部类的实例变量和实例方法
				System.out.println("count = "+count);
				System.out.println("通过方法获取count = "+getNum());
				// 1.2 实例方法中的局部内部类能访问外部类的静态变量和静态方法
				System.out.println("salary = "+salary);
				System.out.println("通过方法获取salary = "+getSalary());
				return count++;
			}
		}
		return new LocalCounter();
	}
	// 2.在静态方法中的局部内部类
	static Counter getCounter2(final String name)
	{
		class LocalCounter implements Counter
		{   //局部内部类中可以有构造方法
			public LocalCounter(){
				System.out.println("局部内部类的构造器...");
			}
			@Override
			public int next() {
				System.out.println("name = "+name);
				// 2.1 静态方法中的局部内部类不能访问外部类的实例变量和实例方法
//				System.out.println("count = "+count);
//				System.out.println("通过方法获取count = "+getNum());
				// 2.2 静态方法中的局部内部类只能访问外部类的静态变量和静态方法
				System.out.println("salary = "+salary);
				System.out.println("通过方法获取salary = "+getSalary());
				return 0;
			}
		}
		return new LocalCounter();
	}
	
	public static void main(String[] args) 
	{
		LocalInnerClass lic = new LocalInnerClass();
		Counter c1 = lic.getCounter("实例方法中的局部内部类");
		c1.next();
		
		Counter c2 = lic.getCounter2("静态方法中的局部内部类");
		c2.next();
	}
}

静态内部类(嵌套类)

如果不需要内部类对象与其对应的外部类对象之间存在联系,那么就可以将内部类声明为static的,以便消除产生的引用。换句话说,当内部类是静态内部类的时候,它并不会隐式地保存this引用(该引用指向创建它的外部类对象,普通内部类访问外部类对象的数据也是通过该引用才能够实现。),因此静态内部类对象和外部类对象之间也就不存在联系了。
如果创建了一个静态内部类,那就意味着:
1) 要创建静态内部类的对象,并不需要其外部类的对象;
2) 在静态内部类中不能访问外部类的非静态(static)的成员变量和成员方法,只能访问静态的成员变量和静态成员方法。

示例

package learninnerclass;

public class StaticInnerClassTest02 {
	
	private int num =10;
	private static double salary=100.4;
	
	public int getNum(){
		return num;
	}
	public static double getSalary(){
		return salary;
	}
	
	public static class Inner{
		// 1.访问外围类的成员变量
		// 1.1 在静态内部类中不能访问外部类的非静态属性
//		public void info1(){// 这段代码是错误的
//			System.out.println("num : "+num);
//		}
		// 1.2访问外围类的静态成员变量
		public void info2(){// 可以访问外部类的静态成员变量
			System.out.println("salary : "+salary);
		}
		// 2.访问外围类的成员方法
		// 2.1 在静态内部类中不能访问外部类的非静态方法
//		public void info3(){// 这段代码是错误的
//			System.out.println("num :"+ getNum());
//		}
		// 2.2 在静态内部类中可以访问外部类的静态方法
		public void info4(){// 可以访问外部类的静态成员方法
			System.out.println("salary : "+getSalary());
		}
		
	}
	
	public static void main(String[] args) {
		// 创建静态内部类对象(创建静态内部类对象的时候并没有通过外部类对象,即:外部类对象.new 内部类名() )
		StaticInnerClassTest02.Inner in = new Inner();
		in.info2();
		in.info4();
	}
}

另外,声明在接口中的内部类也会自动成为static和public。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章