内部类的简单概述
我们作为刚入门的新手,定义一个类的主要方式是在一个文件中定义一个类。但是一个文件中也可以定义多个类,且其中任何一个类都不在其他类的内部,这种情况不在这节的讨论范围内。这节的讨论范围主要是在一个类的内部再定义一个类,最外层的类称为外部类,里面的则称为内部类。内部类又可以按照定义的形式不同分为 成员内部类、局部内部类、匿名内部类。下面做逐一说明。
一、定义类的一般做法
我们以往定义一个类,都只是在一个文件中定义一个类就已经足够满足使用需求了。如下:
public class Demo {
private String name;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void doTest() {
System.out.println("最常用的定义方式!");
}
}
二、定义内部类
有的时候我们选择在一个类的内部再定义其他类,这就是内部类,外面的是外部类。下面对内部类中成员内部类、局部内部类、匿名内部类进行说明。
1、成员内部类
这种定义方式,可以在内部类中直接存取所在外部类的成员变量,也可以随意调用外部类中定义的成员方法,尽管这些成员变量或方法被修饰为private的。如下:
package com.ycz.test.innerClass;
/*
* 成员内部类
*/
public class OuterClass {//定义外部类
innerClass in = new innerClass();//在外部类中实例化内部类
//定义一个外部类的方法
public void ouf() {
//调用内部类里面的方法,通过内部类对象
in.inf();
}
/*
* 定义内部类
*/
class innerClass {
innerClass(){//无参构造方法
}
//内部类中定义一个方法
public void inf() {
System.out.println("调用内部类里的方法!");
}
//内部类里面定义成员变量
int y = 0;
}
//外部类里定义一个方法
public innerClass doit() {
//在外部类中不能直接访问内部类的成员变量,要通过内部类的对象来访问
in.y = 24;//给内部类的成员变量重新赋值
return new innerClass();
}
//主方法中测试
public static void main(String args[]) {
//实例化外部类对象
OuterClass out = new OuterClass();
out.ouf();
//实例化一个内部类对象,通过外部类中定义的doit方法来获取
//注意内部类的定义,要通过外部类
OuterClass.innerClass in = out.doit();
//内部类的实例化,通过外部类的对象来实现
OuterClass.innerClass in2 = out.new innerClass();
//可以一句代码实现,但是必须先实例化外部类对象,再通过外部类对象实例化内部类对象
OuterClass.innerClass in3 = new OuterClass().new innerClass();
}
}
可以发现,内部类对象的创建必须要依赖于外部类的对象,也就是说如果想要实例化一个内部类的对象,那么必须先创建一个外部类对象,然后基于该外部类对象创建内部类对象。因为内部类和外部类是存在依赖关系的,内部类依赖于外部类而存在。
代码:
package com.ycz.test.innerClass.demo1;
/*
* 定义一个接口
*/
public interface OuterInterface {
public void f();//定义一个抽象方法
}
package com.ycz.test.innerClass.demo1;
public class OuterClass {
/*
* 定义一个内部类,实现接口
*/
private class InnerClass implements OuterInterface {
// 内部类的构造方法
InnerClass(String s) {
System.out.println(s);
}
// 实现抽象方法
@Override
public void f() {
System.out.println("内部类中的f()方法!");
}
}
// 外部类中定义一个方法,返回类型为接口
public OuterInterface doit() {
return new InnerClass("访问内部类的构造方法!");
}
}
测试类:
package com.ycz.test.innerClass.demo1;
/*
* 测试内部类向上转型为接口
*/
public class Test1 {
public static void main(String[] args) {
OuterClass out = new OuterClass();//实例化外部类对象
OuterInterface outInterface = out.doit();//调用外部类的方法,返回一个接口
outInterface.f();
}
}
控制台输出结果:
通过代码可以看出,这个将private修饰的内部类向上转型为一个接口,在程序中可以完全隐藏内部类的实现细节。在外部提供一个接口,接口中定义一个方法,而内部类实现该接口,实现了该接口中定义的抽象方法,如果某个类继承了这个外部类,那么由于权限的限制,该子类不能向下转型为内部类,也就无法访问内部类中定义的f()方法,但是却可以访问接口中的f()方法,而接口又被内部类实现了,这种方式仅为子类留下一个接口和外部类,而隐藏了实现细节,这是内部类的最基本的用途。
如果在外部类中定义的成员变量和内部类中的成员变量重名时,这时可以使用this关键字来区分。如下:
package com.ycz.test.innerClass.demo2;
/*
* 测试用this关键字指向内部类对象和外部类对象
*/
public class OuterClass {
private int x = 21;//外部类的成员变量
//定义一个成员内部类
private class Inner{
private int x = 25;//内部类的成员变量
//内部类的成员方法
public void doit(int x) {
x = 18;
System.out.println("方法的局部变量x的值:" + x);
System.out.println("内部类的成员变量x的值:" + this.x);//this是指向内部类对象
System.out.println("外部类的成员变量x的值:" + OuterClass.this.x);//OuterClass.this是指向外部类对象
}
}
public static void main(String []args) {
new OuterClass().new Inner().doit(3);
}
}
控制台输出结果:
可以看到,在内部类中,如果内部类成员变量和外部类成员变量重名时,使用this.成员变量名指向内部类成员变量,使用外部类名.this.成员变量名指向外部类成员变量。如果不重名,可以不使用this来指向,直接写变量名。需要注意的是,如果是在外部类中想访问内部类成员变量和方法,必须要通过内部类对象来访问。
2、局部内部类
局部内部类的一般做法是将内部类定义在方法中的。如下:
package com.ycz.test.innerClass.demo3;
/*
* 定义一个接口
*/
public interface OuterInterface {
}
package com.ycz.test.innerClass.demo3;
/*
* 局部内部类
*/
public class OuterClass {
/*
* 定义一个方法,该方法返回一个接口 方法的参数类型是String,而且是final修饰的,方法的局部变量相当于常量,不可更改
*/
public OuterInterface doit(final String x) {
// 在方法内定义一个内部类,即为局部内部类
// 且该内部类是实现接口的
// 这个内部类的生命周期是和方法同步的
class InnerClass implements OuterInterface {
// 内部类的构造方法
InnerClass(String s) {
System.out.println(x);
System.out.println(s);
}
}
return new InnerClass("访问内部类的构造方法!");
}
}
测试代码:
package com.ycz.test.innerClass.demo3;
public class Test {
public static void main(String[] args) {
new OuterClass().doit("lalala");
}
}
控制台输出结果:
注意,将内部类定义在doit()方法内部,内部类是方法的一部分,所以在doit()方法之外是不能访问这个内部类的,但是这个内部类可以访问当前代码块的常量及外部类的所有成员。以上代码中,doit()方法的参数是final修饰的,如果要在方法体中使用局部变量,那么局部变量需要加final修饰,也就是说在方法中的内部类只能访问final修饰的局部变量,其实加了final之后是相当于一个常量的,值是不能被更改的。
3、匿名内部类
顾名思义,匿名内部类的明显特点就是匿名,因为没有名称,所以在别的地方也就无法使用这个类了,匿名内部类只能使用一次,通常可以使用匿名内部类的方式来避免类爆炸现象。
package com.ycz.test.innerClass.demo4;
public interface People {
public void run();
}
package com.ycz.test.innerClass.demo4;
/*
* 测试匿名内部类
*/
public class DemoClass {
public static void main(String[] args) {
// 这个匿名类是实现了接口的
People p = new People() {
@Override
public void run() {
System.out.println("穿了特步健步如飞!");
}
};
p.run();
}
}
上面的代码中,定义了一个接口,普通的做法是定义一个类实现接口,这里其实也是继承了接口,只不过是匿名内部类实现了该接口,重写了接口中的抽象方法。
上面介绍的其实都是非静态内部类,实际上还有静态内部类,只要在内部类的前面加static修饰符就行了,静态内部类中可以声明static静态成员,而且静态内部类中是不能访问外部类的非静态成员的,只能访问静态成员。静态内部类有几个特点:
♦ 创建静态内部类的对象,不需要通过外部类对象。
♦ 不能从静态内部类的对象中访问非静态外部类对象。
♦ 不能在静态内部类中访问外部类的非静态成员。
package com.ycz.test.innerClass;
/*
* 测试静态内部类
*/
public class StaticInnerClass {//外部类
int x = 21;//非静态成员
//定义静态内部类
static class Inner{
void doit() {
//会报错,因为外部类的x成员是非静态的,在静态内部类中无法访问
//System.out.println("外部类的成员变量x的值:" + x);
}
}
}
静态内部类在开发中是非常少见的,作为学习我们还是应该有所了解。关于内部类的使用就简单介绍这几种,内部类还可以被继承,但是继承内部类比较复杂,而却要设置专门的语法来完成,这里就不做介绍了。内部类是Java中类的比较高级的用法,我们初学者往往会一头雾水,不知所云,不过只要多看代码,多加练习,相信每一个人都能熟练的掌握!