java 内部类 学习笔记

把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(嵌套类),包含内部类的类也被称为外部类(宿主类)

  

package com.hb;

public class Cow {
	
	private double weight;
	
	public Cow(){}
	
	public Cow(double weight){
		this.weight = weight;
	}
	
	//定义一个非静态内部类
	private class CowLeg{
		
		private double length;
		private String color;
		public CowLeg(){}
		public CowLeg(double length,String color){
			this.length = length;
			this.color = color;
		}
		public double getLength() {
			return length;
		}
		public void setLength(double length) {
			this.length = length;
		}
		public String getColor() {
			return color;
		}
		public void setColor(String color) {
			this.color = color;
		}
		//非静态内部类的实例方法
		public void info(){
			System.out.println("当前牛腿的颜色是:" + this.color + ",高 :" + length);
			//直接访问外部类的private修饰的Field
			System.out.println("本牛腿所在奶牛重:" + weight);
		}
	}
	
	public void test(){
		CowLeg c1 = new CowLeg(1.12,"黑白相间");
		c1.info();
	}
	
	public static void main(String[]args){
		Cow cow = new Cow(378.9);
		cow.test();
	}
	
} 

成员内部类(静态内部类、非静态内部类)的class文件总是这样的形式:OuterClass$InnerClass.class;例如:Cow$CowLeg.class 

 

备注: 在非静态内部类的方法访问某个变量时,系统有限在该方法内查找是否存在该名字的局部变量,如果存在就是用该变量;如果不存在,则到该方法所在的内部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果依然不存在,则到该内部类所在的外部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果依然不存在,系统将出现变异错误:提示找不到该变量

 

package com.hb;

public class DiscernVariable {
	
	private String prop = "外部类的实例变量";
	private class InClass{
		private String prop = "内部类的实例变量";
		public void info(){
			String prop = "局部变量";
			//通过"外部类类名,this.varName"访问外部类实例Field
			System.out.println("外部类的Field值:"+DiscernVariable.this.prop);
			//通过this.varName访问内部类实例的Field
			System.out.println("内部类的Field值:"+this.prop);
			//直接访问局部变量
			System.out.println("局部变量的值:"+prop);
		}
	}
	
	public void test(){
		InClass in = new InClass();
		in.info();
	}
	
	public static void main(String[]args){
		new DiscernVariable().test();
	}
}

 

 

package com.hb;

public class Outer {
	private int outProp = 9;
	
	class Inner{
		private int inProp=5;
		public void acessOuterProp(){
			//非静态内部类可以直接访问外部类的成员	
			System.out.println("外部类的outProp值:"+outProp);
		}
	}
	
	public void acessInnerProp(){
		//外部类不能直接访问非静态内部类的实例Field
		//下面代码出现编译错误
		//System.out.println("内部类的inProp值:"+inProp);
		//如果需要访问内部类的实例Field,则必须显示创建内部类对象
		System.out.println("内部类的inProp值:"+new Inner().inProp);
		//调用内部类的方法
		new Inner().acessOuterProp();
	}
	
	public static void main(String[]args){
		//执行下面代码,只创建了外部类对象,还未创建内部类对象
		Outer out = new Outer();
		out.acessInnerProp();
	}
}
 非静态内部类的构造器必须使用外部类对象来调用;例如:

Out.In in = new Out().new In("...");

 

package com.hb;

public class Out {

	//定义一个内部类,不使用访问控制符
	//即只有同一个包中的其他类可访问该内部类
	class In{
		public In(String msg){
			System.out.println(msg);
		}
	}
	
	public static void main(String[]args){
		Out.In in = new Out().new In("测试信息");
		/**
		 * 上面代码可改为如下三行代码:
		 * 使用OutterClass.InnerClass的形式定义内部类变量 Out.In in;
		 * 创建外部类实例,非静态内部类实例蒋寄存在该实例中
		 * Out out = new Out();
		 * 通过外部类实例和new来调用内部类的构造方法创建非静态内部类实例
		 * in = out.new In("测试信息")
		 */
	}
}

 

 

1、内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类

2、内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间是可以相互访问

3、匿名内部类适用于创建那些仅需要一次使用的类

 

在方法中也可定义内部类(方法里定义的内部类被称为局部内部类)。大部分内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类是一种与Field、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。

 

内部类:静态内部类和非静态内部类

 

非静态内部类的成员可以访问外部类的private成员,但是反过来就不成立了。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显示创建非静态内部类对象来调用访问其实力成员。

 

package com.hb;

public class StaticTest {

	//定义一个非静态内部类,是一个空类
	private class In{}
	//外部类的静态方法
	public static void main(String[] args) {
		//下面代码引发编译异常,因为静态成员(main方法)
		//无法访问非静态成员(In类)
		//new In();
	}

}
 

 

非静态内部类对象必须寄存在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄存其中。因此外部类对象访问非静态内部类成员时,可能非静态普通内部类对象根本不存在,而非静态内部类对象访问外部类成员时,外部类对象是一定存在的

 

    根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。总之,不允许在外部类的静态成员中直接使用非静态内部类。

 

非静态内部类里不能有静态方法、静态Field、静态初始化块

 

package com.hb;

public class InnerNoStatic {

	private class InnerClass{
		/**
		 * 下面三个静态声明都将引发编译错误:
		 * 非静态内部类不能有静态声明
		 */
//		static{
//			System.out.println("============");
//		}
//		private static int inProp;
//		private static void test(){}
	}
}
 

 

静态内部类:

如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不是属于外部类的某个对象,因此使用static修饰的内部类被称为类内部类(静态内部类)

 

外部类的上一级程序单元是包,所以不可使用static修饰,而内部类的上衣程序单元是外部类,使用static修饰可以将内部类变成外部类相关,而不是外部类实例相关。

 

静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部的实例成员,只能访问外部类的类成员。即使静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。

 

当静态内部类对象存在时,并不存在一个被他寄存的外部类对象,静态内部类对象里只有外部类的类引用,没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄存的外部类对象,这将引起错误。

 

静态内部类是外部类的一个静态成员,因此外部类的静态方法、静态初始化块中可以使用静态内部类来定义变量、创建对象等。

 

package com.hb;

public class StaticInnerClassTest {
	
	private int prop1 = 5;
	private static int prop2 = 9;
	
	static class StaticInnerClass{
		//静态内部类里可以包含静态成员
		private static int age;
		public void accessOuterProp(){
			//下面代码出现错误
			//静态内部类无法访问外部类的实例成员
//			System.out.println(prop1);
			//下面代码正常
			System.out.println(prop2);
		}
	}
	
}

  

java还允许在接口里定义内部类,接口里定义的内部类默认使用public static修饰,也就是说,接口内部类只能是静态内部类。

 

非静态内部类的子类不一定是内部类,它可以是一个外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在的外部类对象。

 

使用静态类比使用非静态内部类要简单很多,只要把外部类当成静态内部类的包空间即可,因此当程序需要使用内部类时,应该优先考虑使用静态内部类。

 

package com.hb;

public class AccessStaticInnerClass {
	
	static class StaticInnerClass{
		private static int prop1 = 5;
		private int prop2 = 9;
	}
	
	public void accessInnerProp(){
//		System.out.println(prop1);
		//上面代码出现错误,应该改为如下形式
		//通过类名访问静态内部类的类成员
		System.out.println(StaticInnerClass.prop1);
		
//		System.out.println(prop2);
		//上面代码出现错误,应该改为如下形式
		//通过实例访问静态内部类的实例成员
		System.out.println(new StaticInnerClass().prop2);
	}
	
}

 

问:既然内部类是外部类的成员,那么是否可以为外部类定义子类,在子类中再定义一个内部类来重写其父类中的内部类

不可以!内部类的类名不再是简单地由内部类的类名组成,它实际上还把外部类的类名作为一个命名空间,作为内部类类名的限制。因此子类中的内部类和父类中的内部类不可能完全同名,即使二者所包含的内部类类名相同,但因为他们所处的外部类空间不同,所以它们不可能完全同名,也就不可能重写。

 

对于局部内部类成员而言,不管是局部变量还是局部内部类,他们的上一级程序单元是方法,而不是类,使用static修饰他们是没有任何意义的,因此局部成员都不可能使用static修饰,不仅如此,因为局部成员的作用域是所在方法,其他程序单元永远一不可能访问一个方法中的局部成员,所以所有的局部成员都不能使用访问控制符修饰

 

package com.hb;

public class LocalInnerClass {
	public static void main(String[] args){
		//定义局部内部类
		class InnerBase{
			int a;
		}
		
		//定义局部内部类的子类
		class InnerSub extends InnerBase{
			int b;
		}
		
		//创建局部内部类的对象
		InnerSub is = new InnerSub();
		is.a = 5;
		is.b = 8;
		System.out.println("InnerSub对象的a和b Field事:" + is.a + "," + is.b);
	}
}

 

局部内部类的class文件总是遵循如下命名格式:OuterClass$NInnerClass.class;局部内部类的class文件的文件名比成员内部类的class文件的文件名多了一个数字,因为同一个类里不可能有两个同名的成员内部类,而同一个类里则可能有两个以上同名的局部内部类(处于不同的方法中)

 

定义匿名内部类的格式如下:

new 父类构造器(实参列表)|实现接口(){

//匿名内部类的类体部分

}

 

匿名内部类有如下两条规则:

1、匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象

2、匿名内部类不能定义构造器,因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化,通过实例初始化块来完成构造器需要完成的事情。

 

如果匿名内部类需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量。

 

所谓回调,就是允许客户类通过内部类引用来调用其外部类的方法,这是一种非常灵活的功能。

 

你可能感兴趣的:(java 内部类)