把一个类放到另一个类的内部定义,这个定义在其他类内部的类就被称为内部类。
内部类的作用:
(1).内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
(2).内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节。
(3).匿名内部类适用于创建那些仅需要一次使用的类。
(4).内部类比外部类可以多使用三个修饰符:private、protected、static——外部类不可以使用这三个修饰符。
(5).非静态内部类不能拥有静态成员。
1.非静态内部类
定义内部类非常简单,只要把一个类放在另一个类内部定义即可。此处的“类内部”包括类中的任何位置,甚至在方法中也可以定义内部类(方法里定义的内部类被称为局部内部类)。
成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类是静态内部类,没有使用static修饰的成员内部类是非静态内部类。
因为内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰。
外部类的上一级程序单元是包,所以他只有两个作用域:同一包内和任何位置。而内部类的上一级程序单元是外部类,它就具有4个作用域:同一个类、同一个包、父子类和任何位置,因此可以使用四中访问控制权限。
public class Cow { private double weight; //外部类的两个重载的构造器 public Cow(){} public Cow(double weight){ this.weight=weight; } //定义一个非静态内部类 @SuppressWarnings("unused") 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("当前牛腿颜色是:"+color+",高:"+length); //访问外部类的private修饰的成员变量 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(); /* * 输出结果: * 当前牛腿颜色是:黑白相间,高:1.12 * 本牛腿所在奶牛重:378.9 */ } }
上面程序在编译后生成两个class文件,一个是Cow.class,另一个是Cow$CowLeg.class,前者是外部类Cow的class文件,后者是内部类CowLeg的class文件,即成员内部类(包括静态内部类、非静态内部类)的class文件总是这种形式:OuterClass$InnerClass.class。
在非静态内部类对象里,保存了一个它所寄生的外部类对象的引用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例中)。
当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是够存在该名字的局部变量,如果存在就是用该变量;如果不存在,则到该方法所在的内部类中查找是否存在该名字的成员变量,如果存在就是用该成员变量;如果不存在,则到该内部类所在的外部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量,如果已然不存在,系统将出现编译错误:提示找不到该变量。
如果外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可通过使用this、外部类类名.this作为限定来区分。
public class DiscernVariable { private String prop="外部类的实例变量"; private class InClass{ private String prop="内部类的实例变量"; public void info(){ String prop="局部变量"; //通过外部类类名.this.varname访问外部类实例变量 System.out.println("外部类的实例变量值:"+DiscernVariable.this.prop); //通过this.varname访问内部类实例的变量 System.out.println("内部类的实例变量值:"+this.prop); //直接访问局部变量 System.out.println("局部变量的值:"+prop); } } public void test(){ InClass in=new InClass(); in.info(); } public static void main(String[] args) { new DiscernVariable().test(); /* * 输出结果: * 外部类的实例变量值:外部类的实例变量 内部类的实例变量值:内部类的实例变量 局部变量的值:局部变量 */ } }
通过OuterClass.this.propName的形式访问外部类的实例变量,通过this.propName的形式访问非静态内部类的实例变量。
非静态内部类的成员可以访问外部类的private成员,但反过来不成立。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显式创建非静态内部类对象来调用访问其实例成员。
public class Outer { private int outProp=9; class Inner{ private int inProp=5; public void acessOuterProp(){ //非静态内部类可以直接访问外部类的private成员变量 System.out.println("外部类的outProp值:"+outProp); } } public void accessInnerProp(){ //外部类不能直接访问非静态内部类的实例变量 //下面代码编译错误 //System.out.println("内部类的inProp值:"+inProp); //如需访问内部类的实例变量,必须显式创建内部类对象 System.out.println("内部类的inProp值:"+new Inner().inProp); } public static void main(String[] args) { Outer out=new Outer(); out.accessInnerProp(); /* * 输出结果: * 内部类的inProp值:5 */ } }
非静态内部类对象和外部类对象的关系:非静态内部类对象必须寄生在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄生其中。简单地说,如果存在一个非静态内部类对象,则一定存在一个被它寄生的外部类对象那个。但外部类对象存在时,外部类对象里不一定寄生非静态内部类对象。因此外部类对象访问非静态内部类成员时,可能非静态普通内部类对象根本不存在。而非静态内部类对象访问外部类成员时,外部类对象一定存在。
根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。总之,不允许在外部类的静态成员中直接使用非静态内部类。
Java不允许在非静态内部类中定义静态变量。
非静态内部类中不能有静态方法、静态成员变量、静态初始化块。
非静态内部类中不可以有静态初始化块,但可以包含普通初始化块。非静态被不累普通初始化块的作用于外部类的初始化块作用完全相同。
2.静态内部类
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,有的地方也成为静态内部类。
static关键字的作用是把类的成员变成类相关,即static修饰的成员属于整个类,而不属于单个对象。外部类的上一级程序单元是包,所以不可使用static修饰;而内部类的上一级程序单元是外部类,使用static修饰可以将内部类变成外部类相关,而不是外部类实例相关,因此static关键字不可修饰外部类,但可修饰内部类。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,非静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的就静态成员。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。
静态内部类是外部类的一个静态成员,因此外部类的所有方法、所有初始化块中可以使用静态内部类来定义变量、创建对象等。
外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名来作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。
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); } }
Java还允许在接口里定义内部类,接口里定义的内部类默认使用public static修饰——接口内部类只能是静态内部类。
如果为接口内部类指定访问控制符,则只能指定public访问控制符;如果定义接口内部类暂时省略访问控制符,则该内部类默认是public访问控制权限。