把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(嵌套类),包含内部类的类也被称为外部类(宿主类)
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修饰符来修饰外部类的局部变量。
所谓回调,就是允许客户类通过内部类引用来调用其外部类的方法,这是一种非常灵活的功能。