内部类顾名思义,就是在类中定义的一个类。内部类可以分为成员内部类、局部内部类以及匿名类,不过在介绍之前,我们先来说一下内部类到底有啥用。
《Think in java》中提到:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类而言都没有什么影响。
传统意义上的继承,对于具体的类而言只能实现单继承,要想多继承,则只能继承多接口,但是对于接口的实现,一个类只能实现一次接口,如果想要对接口进行不同方式的实现,那么就可以利用到内部类。多个内部类继承同一接口,从而可以达到在一个类中做出多个不同的响应事件。同时,多个内部类可以继承多个不同的实体类,从而进一步完善了多重继承的解决方案。
成员内部类
作为成员内部类,有以下几个特性要说明一下:
- 内部类可以随意使用外部类的成员方法及成员变量。
- 外部类不能直接修改内部类的成员方法或者成员变量,内部类的方法和属性不能被外部类使用,只有在内部类的范围之内是可知的。
- 内部类实例一定要绑在外部类实例上,内部类的对象实例化必须在外部类或者外部类的非静态方法中实现。
package com.java11.dyp;
public class Demo {
int i=1;
inClass inc=new inClass();
public void outf(){
System.out.println("这是外部类方法outf()");
inc.inf();
}
class inClass{
int y=0;
inClass(){
System.out.println("这是内部类构造函数的第"+i+"次调用");
i++;
}
public void inf(){
System.out.println("修改外部类的属性值i="+i);
}
}
public inClass doit(){
inc.y=4; //修改内部类的属性
return new inClass();
}
public static void main(String[] args) {
Demo demo=new Demo();
Demo.inClass in2=demo.doit(); //使用外部类的非静态方法实现实例化
Demo.inClass in3=demo.new inClass(); //使用外部类对象调用构造函数
}
}
下面举一个例子来说明内部不累向上转型为接口,实现同一个接口中接口方法的不同实现方式。
package com.java11.dyp;
interface OutInterface{
public void f();
}
class OuterClass{
private class InterClass1 implements OutInterface{
public InterClass1(){
System.out.println("InterClass1进行了实例化");
}
public void f(){
System.out.println("这是InterClass1调用的f()方法");
}
}
private class InterClass2 implements OutInterface{
public void f(){
System.out.println("这是InterClass2调用的f()方法");
}
public InterClass2(){
System.out.println("InterClass2进行了实例化");
}
}
public OutInterface doit1(){
return new InterClass1();
}
public OutInterface doit2(){
return new InterClass2();
}
}
public class Demo {
public static void main(String[] args) {
OuterClass out=new OuterClass();
OutInterface oif=out.doit1();
oif.f(); //实现interClass1中的f()
oif=out.doit2();
oif.f(); //实现interClass2中的f()
}
}
这里对上例的子类解释一下:该例中子类被定义为private权限,那么就只能由外部类进行访问,其他类不能对子类进行操作。但是由于外部类中提供了doit()方法,能够返回一个接口的引用,如 代码中定义了oif来接收这个返回值,oif能够使用向上转型的特点来调用子类中对接口重写的f()方法,这样不仅很好的隐藏了子类的具体内容和f()方法的细节,同时又能够满足对接口方法的使用,这也是内部类的用途之一。
当内部类和外部类的属性重名时,this关键词在内部类中的使用
public class Demo {
private int x=2;
private class inner{
private int x=9;
public void doit(int x){
System.out.println("参数x="+(++x));
System.out.println("子类属性x="+(++this.x));
System.out.println("外部类属性x="+(++Demo.this.x));
}
}
public static void main(String[] args) {
Demo demo = new Demo();
inner in=demo.new inner();
in.doit(4)
}
}
参数x=5
子类属性x=10
外部类属性x=3
在上述代码中,子类的this可以通过 this.xxx 来使用,区别于形参和外部类,外部类的属性则可以通过 外部类名.this.XXX 的方式来调用。
局部类
interface OutInterface2{
}
public class Demo {
public OutInterface2 doit(final String x){
class InnerClass2 implements OutInterface2{
public InnerClass2(String s){
s=x;
System.out.println(s);
}
}
return new InnerClass2("doit");
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.doit("Hello World!");
}
}
作为局部内部类时,有几点需要注意:
1、内部类是定义该类的方法的一部分,并非外部类的一部分,所以在doit()方法的外部不能访问该内部类。
2、内部类可以访问当前代码块的常量以及此外部类的所有成员。
匿名类
先看下例:
interface OutInterface2{
public int getValue();
}
public class Demo {
public OutInterface2 doit(){
return new OutInterface2(){
private int i=0;
public int getValue(){
return i;
}
};
}
public static void main(String[] args) {
Demo demo = new Demo();
OutInterface2 out=demo.doit();
System.out.println("i="+out.getValue());
}
}
结果输出:
i=0
可能初看会感觉莫名其妙,但是确实是被Java编译器认可的。在doit()方法内部首先返回一个OutInterface2的引用,然后在return语句中插入一个定义内部类的代码,由于该类没有名称,所以将该类称为匿名内部类。这种类的作用就是创建一个实现于OutInterface2接口的匿名类的对象。
这里需要说一下,内部类定义结束后,需要加分号标识,这个分号并不是代表内部类结束的标志,而是代表创建OutInterface2引用表达式的标识。
最后在说一些内部类的其他知识点:
静态内部类
内部类前面加static就变成了静态内部类。它有以下特点:
- 静态内部类中可以声明static成员,但是在非静态内部类中不可以申明静态成员。
- 静态内部类不能够使用外部类的非静态成员,所以静态内部类在程序开发中比较少见。
- 如果创建静态内部类对象,不需要其外部类的对象。
下面举个代码示例:
public class StaticInnerClass{
int x=100;
static class Inner{
void doitInner(){
//System.out.println("外部类"+x); //调用外部类的成员变量x
}
}
}
在上述代码中,println是无法实现的,因为不能访问外部类的非静态成员。
内部类的继承
内部类的继承相对与一般类的继承会比较复杂,要设置专门的语法,这里也只做格式介绍:
public class OutputInnerClass extends ClassA.ClassB{
public OutputInnerClass(Class a){
a.super();
}
}
class ClassA{
class ClassB{
}
}
某个类继承内部类时,必须硬性的给予这个类一个含参的构造方法,并且该构造方法的参数需为继承内部类的外部类的引用,同时在构造方法中使用a.super()语句,这样才为继承提供了必要的对象引用。