什么是内部类,简单来说就是一个外部类的内部又定义了一个类,那为什么需要内部类呢?
因为内部类可以直接访问外部类中的成员,包括私有。用现实的例子描述就是一个人是由大脑、肢体、器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏,它也有自己的属性和行为(血液、跳动),心脏可以直接访问身体的血液,而不是通过医生来抽血,这时不能用属性或者方法表示一个心脏,而是用一个类
内部类可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this
外部类要访问内部类,必须建立内部类对象
内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
1.成员内部类
就是将内部类定义在外部类的成员位置,而且非私有,可以在外部其他类直接建立内部类对象。格式:
外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer().new Inner();
开头的Outer是为了标明需要生成的内部类对象在哪个外部类当中。必须先有外部类的对象才能生成内部类的对象,因为内部类的作用就是为了访问外部类中的成员变量。
//定义外部类
class Outer
{
//定义内部类
class Inner
{
void show()
{
System.out.println("内部类运行...");
}
}
}
public class InnerClassDemo {
public static void main(String[] args) {
//在外部其他类建立内部类对象
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
当内部类在成员位置上,就可以被成员修饰符所修饰。
private:将内部类在外部类中进行封装。如同是,我的心脏只能由我的身体控制,其他人无法直接访问它
static:内部类就具备static的特性。当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。
在外部其他类中,如何直接访问static内部类的非静态成员呢?
new Outer.Inner().function();
在外部其他类中,如何直接访问static内部类的静态成员呢?
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。
当外部类中的静态方法访问内部类时,内部类也必须是static的。
//定义外部类
class Outer
{
//内部类被static修饰后,只能直接访问外部类中的static成员
private static int num = 2;
static class Inner
{
static void show()
{
System.out.println("内部类运行..."+num);
}
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer.Inner.show();
}
}
2.局部内部类
内部类定义在外部类中的某个方法中,创建了这个类型的对象时,且仅使用了一次,那么可在这个方法中定义局部类。
不可以被成员修饰符修饰,如public、private、static等修饰符修饰。它的作用域被限定在了声明这个局部类的代码块中
可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。为什么呢?
因为局部内部类对象和局部变量的生命周期不同,内部类需要复制局部变量为内部类的一个成员变量,为了保证局部变量和该副本的值的一致性,所以要将修饰符设为final。
//定义外部类
class Outer
{
//局部内部类访问局部变量必须用final修饰
void show(final int num)
{
//内部类定义在外部类方法内
class Inner
{
void show()
{
System.out.println("内部类运行..."+num);
}
}
//访问语句必须在定义类局部内部类之后
new Inner().show();
}
}
public class InnerClassDemo {
public static void main(String[] args) {
Outer out = new Outer();
out.show(3);//内部类运行...3
out.show(4);//内部类运行...4
}
}
3.匿名内部类
没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式简化书写。
定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
匿名内部类的格式: new 父类或者接口(){定义子类的内容}
其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。
匿名内部类中定义的方法最好不要超过3个。
public class InnerClassDemo {
public static void main(String[] args) {
//编译通过,运行
new Object(){
void show(){
System.out.println("show run");
}
}.show();
//编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,
//就被提升为了Object类型,而编译时检查Object类中是否有show方法
Object obj = new Object(){
void show(){
System.out.println("show run");
}
};
obj.show();
}
}
从上面这个例子可以看出,匿名内部类就是参照多态的表达形式,用父类引用接收一个子类对象,编译运行参照多态的规则。
4.匿名内部类的扩展
可以通过匿名内部类的方式实现多重继承
//创建一个使用继承的类
abstract class Normal
{
abstract void methodNomal();
}
//创建一个使用匿名内部类的类
abstract class Outer
{
abstract void methodOuter();
}
//创建实现多重继承的类
class Multi extends Normal
{
//复写父类的方法
@Override
void methodNomal() {
System.out.println("normal method ...");
}
//创建返回匿名内部类对象的方法
public Outer getOuter(){
return new Outer(){
@Override
void methodOuter() {
System.out.println("Outer method...");
}
};
}
}
public class InnerClassDemo {
public static void main(String[] args) {
//实现一个类的多重继承,调用方法
Multi m = new Multi();
m.methodNomal();
m.getOuter().methodOuter();
}
}
5.继承内部类
在学习了内部类的基础知识后,产生了一个疑问,既然内部类也是一个类,那它能不能被继承呢
//定义外部类
class Outer
{
//定义内部类
class Inner
{
Inner()
{
System.out.println("内部类运行...");
}
}
}
//继承时需要指定外部类名称
class ExtendsClass extends Outer.Inner
{
//ExtendsClass();无法通过编译
//构造函数需要传递外部类对象
ExtendsClass(Outer out)
{
//调用内部类的构造函数
out.super();
}
}
public class InnerClassDemo {
public static void main(String[] args) {
//创建外部类对象,并传入继承类的构造函数,完成初始化
Outer out = new Outer();
ExtendsClass ex = new ExtendsClass(out);
}
}
上面就是继承内部类的代码,为什么要这样写?
继承内部类后,需要调用内部类的默认构造函数,但是加载内部类必须先加载外部类,所以要给继承类的构造函数传入外部类的对象,然后通过外部类.super()来调用内部类的默认构造函数完成初始化。