一 内部类的基本概念
1 定义
把一个类放在另一类的内部定义,这个定义在其它类内部的类就被称为内部类,也叫嵌套类,包含内部类的类也被称为外部类,也叫宿主类。
2 作用
- 内部类提供更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类成员可以直接访问外部类的私有数据,因为内部类是外部类的成员,同一个类的成员之间可以互相访问,但外部类不能访问内部类的实现细节,例如内部类的成员变量。
- 匿名内部类适用于创建那些仅需一次使用的类。
3 内部类和外部类的区别
- 内部类比外部类可以多使用三个修饰符:private、protected、static,外部类不能使用这三个修饰符。
- 非静态内部类不能有静态成员。
二 非静态内部类基本概念
内部类在外部类中的位置,这个位置可以是任意位置,甚至在方法中也可以定义内部类(在方法里定义的内部类称为局部内部类)。内部类定义语法格式如下:
public class outerclass
{
//此处定义内部类
}
大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类。成员内部类是一种与成员变量、成员方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分静态内部类和非静态内部类,使用static修饰的成员内部类是静态内部类,没有使用static修饰的成员内部类是非静态内部类。
三 非静态内部类应用
1 代码
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;
}
// 下面省略length、color的setter和getter方法
public void setLength(double length)
{
this.length = length;
}
public double getLength()
{
return this.length;
}
public void setColor(String color)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
// 非静态内部类的实例方法
public void info()
{
System.out.println("当前牛腿颜色是:"
+ color + ", 高:" + length);
// 直接访问外部类的private修饰的成员变量
System.out.println("本牛腿所在奶牛重:" + weight); //①
}
}
public void test()
{
CowLeg cl = new CowLeg(1.2 , "黑白相间");
cl.info();
}
public static void main(String[] args)
{
Cow cow = new Cow(400);
cow.test();
}
}
2 运行结果
当前牛腿颜色是:黑白相间, 高:1.2
本牛腿所在奶牛重:400.0
3 结果分析
Cowleg类定义放在另一类的内部,所以它就成了一个内部类。
外部类里包含了一个test方法,该方法里创建了一个Cowleg对象,并调用该对象的info方法,不难发现,在外部类使用非静态内部类时,与普通类并没太大区别。
四 同名变量内部类访问应用
1 点睛
当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,如果存在该名字的局部变量,就使用该变量;如果不存在,则到内部类中查找是否存在该名字变量,如果存在则使用该变量;如果不存在,则到内部类所在的外部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果依然不存在,则报错。
总之,第一步,先找局部变量,第二步,找内部类变量,第三步,找外部类变量。
2 代码
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();
}
}
3 运行结果
外部类的实例变量值:我是外部类的实例变量
内部类的实例变量值:我是内部类的实例变量
局部变量的值:我是局部变量
4 结果分析
程序中通过OutterClass.this.propName的形式访问外部类的实例变量,通过this.propName的形式访问非静态内部类的实例变量。
五 外部类不能直接访问内部类变量举例
1 代码
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();
}
}
2 运行结果
内部类的inProp值:5
3 结果分析
在外部类里访问非静态内部类的实例变量,会引起编译错误。
外部内不允许访问非静态内部内的实例成员还有一个原因,上面程序main方法创建了一个外部类对象,此时非静态内部类对象根本不存在,如果允许accessInnerProp方法访问非静态内部类对象,将引起错误。
六 非静态内部类错误用法
1 点睛
根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。
2 代码
public class StaticTest
{
// 定义一个非静态的内部类,是一个空类
private class In{}
// 外部类的静态方法
public static void main(String[] args)
{
// 下面代码引发编译异常,因为静态成员(main()方法)
// 无法访问非静态成员(In类)
new In();
}
}
3 结果分析
不允许外部静态成员直接使用非静态内部类。
七 非静态内部类错误用法
1 点睛
不允许在非静态内部类里定义静态成员。
2 代码
public class InnerNoStatic
{
private class InnerClass
{
/*
下面三个静态声明都将引发如下编译错误:
非静态内部类不能有静态声明
*/
static
{
System.out.println("==========");
}
private static int inProp;
private static void test(){}
}
}
3 说明
非静态内部类里不能有静态方法、静态成员变量、静态初始化块。
在Java中不允许在非静态内部类里包含静态成员,否则会引发编译错误。
非静态内部类可以包含普通初始化块。非静态内部类普通初始化块的作用和外部类初始化块的作用完全相同。