一.内部类的定义:
如果在一个类中在定义一个类,则将在类中在定义的那个类称为内部类。
二.内部类的分类:
内部类可以分为成员内部类、局部内部类以及匿名内部类、静态内部类。
首先来说下成员内部类;
1.成员内部类:
*成员内部类的语法:
public class OuterClass{ //外部类
private class InnerClass{//内部类
//.......
}
}
在内部类中可以随意使用外部类的成员方法以及成员变量,尽管这些类成员变量被修饰为private。下面有实例代码:
public class outclass{
private int i=0; //修饰为private的成员变量
private void goout(){ //修饰为private的成员方法
System.out.println("方法执行成功");
}
class InnerClass{ //修饰为private的成员变量和方法可以在内部类中随意调用
void test(){
goout();
i++;
}
}
}
内部类的实例一定要绑在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类上对象上。内部类初始化方法与其他类初始化方式相同,都是使用new关键字。
public class OutClassTest {
InnerClassTest in =new InnerClassTest();
public void out(){
in.intest();//在外部类方法中通过内部类对象调用内部类方法
}
class InnerClassTest{
InnerClassTest(){//内部类的构造方法
}
public void intest(){}//内部类成员方法
int i=0; //定义内部类成员变量
}
public InnerClassTest get(){//外部类方法,返回值为内部类引用
in.i=5; //外部类不可以直接访问内部类的成员变量
return new InnerClassTest();//返回内部类引用
}
public static void main(String []args){
OutClassTest out = new OutClassTest();
//内部类的实例化操作必须依靠外部类或者外部类的非静态方法中实现
System.out.println(out.in);
OutClassTest.InnerClassTest in=out.get();
System.out.println(in);
OutClassTest.InnerClassTest in1=out.new InnerClassTest();
System.out.println(in1);
}
}
注意:内部类可以访问它的外部类成员,但内部类的成员只有在内部类的范围类是可见的,不能被外部类使用。例如对内部类的成员变量i进行第二次赋值时将会出错,但是可以使用内部类对象引用调用成员变量y。如 new InnerClass().y;
*内部类向上转型为接口
如果将一个权限修饰符为private的内部类向上转型为其父类对象,或者转型为一个接口,在程序中就可以完全隐藏内部类的实现过程。可以外部提供一个接口,在接口中声明一个方法;如果在实现接口的内部类中实现接口的方法,就可以定义多个内部类以不同的方式实现接口的同一个方法。
下面有一个实例,可以同通过创建一个ClassTest测试类,并定义一个接口OutInterfaceTest,让内部类实现接口OutInterfaceTest,最后调用外部类OutClassTest1的doit()方法返回值类型为OutInterfaceTest接口;
public interface OutInterfaceTest {
public void go();
}
public class OutClassTest1 {
//定义一个外部类实现OutInterfaceTest接口
private class Innerclass1 implements OutInterfaceTest{
Innerclass1(String s){
System.out.println(s);
}
@Override
public void go() {
System.out.println("访问内部类的go()方法");
}
}
public OutInterfaceTest doit(){
return new Innerclass1("访问内部类构造方法");
}
}
public class ClassTest {
public static void main(String []args){
OutClassTest1 out =new OutClassTest1();//实例化一个OutClassTest1对象
OutInterfaceTest outinter=out.doit();//调用doit(),返回一个OutInterfaceTest接口
outinter.go(); //调用内部类的f()方法
}
}
输出结果:
由于内部类Innerclass1访问权限为private,除了外部类可以访问外,其他类都不能访问,而可以访问doit()方法,返回一个外部接口类型 ,通过上述代码,将内部类向上转型为接口,创建一个外部类对象,从而访问到私有内部类的f()方法;
注意:非内部类不能被声明为private或protected类型。
*使用this获得内部类与外部类的引用
下面有一个实例,解释了如何在外部类中定义的成员变量与内部类的成员变量相同时,可以使用this关键字进行区分。
class DemoTest {
private int i;
public class test{
private int i=12;
public void add(int i){
i++;//调用形参变量i
this.i++;//调用内部类变量i
DemoTest.this.i++;//调用外部类变量i
}
}
}
在类中如果遇到内部类与外部类同名的成员变量,可以使用this关键字进行区分。
2.局部内部类
内部类不仅可以在类中进行定义,而且还可以定义在类的局部的位置,如在类的方法或者任意的作用域中均可以定义内部类。
下面是一个实例,将内部类定义在外部类的doit()方法的内部;
public interface OutInterfaceTest1 {//定义一个接口
}
public class Demotest1 {
public OutInterfaceTest1 doit(final String s1){
//在doit方法内部定义一个内部类
class test implements OutInterfaceTest1{
test(String s2){
s2=s1;
System.out.println(s2);
}
}
return new test("doit");
}
}
从上述实例可以看到内部类被定义到了doit()方法的内部,此时的内部类test是doit()方法体的一部分,并不是Demotest1 类的一部分,所以在doit()的外部不能访问该内部类,但内部类可以访问当前代码块的常量,以及外部类所有的成员。
3.匿名内部类
在doit()方法中将return语句和内部类定义语句合并在一起,下面通过一个实例说明。
在return语句中编写返回值为一个匿名内部类。下面是一个实例;
public interface OutInterfaceTest1 {//定义一个接口
}
public class DemoTest2 {
public OutInterfaceTest1 doit(){//定义doit()方法
return new OutInterfaceTest1(){//声明匿名内部类
private int i=0;
public int getI(){
return i;
}
};
}
public static void main(String []args){
DemoTest2 demo =new DemoTest2();
System.out.println();
OutInterfaceTest1 out=demo.doit();
System.out.println(out.getClass().getName());
//System.out.println(demo.doit().getClass().getName());
}
}
上述所写的代码有点奇怪,是我以前没有接触到的,但是它确实能通过编译。首先是在doit()方法内部的一个引用,然后在retrun语句中插入一个定义内部类的代码,因为这个代码没有名称,所以就把该类称为内部类。实质上这种内部类的作用就是创建一个实现于OutInterfaceTest1接口的内部类的对象。
匿名内部类的实现语法如下:
return new A(){
//内部类代码
};
A代表类名,由于匿名内部类使用构造方法来生成OutInterface对象。在匿名内部类结束后,需要加分号标识,这个分号并不代表定义内部类结束的标识,而是代表引用表达式结束的标识。
注意:匿名内部类编译以后,或产生以“以外部类名$序号”为名称的.class文件,序号以1~n排列,分别代表1~n个匿名内部类。
例如匿名内部类的实例的输出结果就是以上述方式的结果;
输出结果:
4.静态内部类
定义:在一个内部类前添加修饰符static,这个内部类就变为静态内部类。一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。
静态内部类最大的特点是:不可以使用外部类的非静态成员,所以静态内部类在程序开发中比较少见。可以这么认为,普通内部类对象隐式的在外部保存了一个引用,指向了创建它的外部类对象。
静态内部类的两个特点:
如果创建静态内部类对象,不需要其外部类的对象。
不能从静态内部类的对象访问非静态外部类的对象;
接下来,定义一个静态内部类,下面是示例代码;
public class DemoTest3 {
int x;
static class Test{
void get(){
//System.out.println(x); //注意这条输出语句是错误的,不可以在静态内部类中调用外部类的成员变量!!!!!!!
}
}
}
注意:上述代码中get()方法中的输出外部类成员变量x的语句是错误的,因为静态内部类中是不能调用外部类的成员变量。
进行程序测试时,如果在每一个java文件都写一个主方法会显示麻烦,因此,我们可以将主方法写入静态内部类中。
下面是一个实例,在静态内部类中定义主方法;
public class DemoTest4 {
int x=50;
static class Test{
void get(){
System.out.println("get()方法执行成功");
}
public static void main(String []args){
System.out.println("静态类中的主方法");
}
}
}
代码编译完成后,会生成一个名称为“外部类名$Test”的独立类和一个StaticInnerTestClass类。
三、内部类的继承
内部类和其他类一样可以被继承,但是继承内部类比继承不同类要复杂一些,需要设置专门的语法来完成它;
下面有一个实例:
public class DemoTest extends Demo.Example{//继承内部类Example
public DemoTest(Demo d){
d.super();
}
}
class Demo{
class Example{
}
}
在某各类继承内部类的时,必须硬性给与这个类一个带参数的构造方法,并且该构造方法的参数为需要继承内部类的外部类的引用,同时在构造方法体中使用d.super()语句,这样为继承提供了必要的对象引用;
参考:java从入门到精通书籍