Java允许在一个类里面定义另一个类,类里面的类就是内部类。内部类看似简单,其实里面大有乾坤,下面我们就来好好聊一聊内部类。代码示例在最下面。
初识内部类
内部类作用及其一些共性和特点:
使用内部类最吸引人的原因是:每个内部类都能独立地实现某一接口,所以无论外部类是否已经实现了某个接口,对于内部类都没有影响。接口和内部类配合使用,使得多继承的解决方案变得更加完整。
① 我们都知道类一般都不声明成private和protected,但是内部类可以,所以通过内部类可以很好地隐藏我们的信息。
② 内部类它可以直接访问外部类的成员变量和方法(甚至是私有的),利用这个特性,配合接口,我们可以更好地实现多继承的效果。
③ 内部类声明成静态的,就不能随便访问外部类的成员数据了,此时内部类只能访问外部类的静态成员数据。
④ 在编译成功之后,它就与外部类是不同的类,是一个独立的类,当然他们之间还是有联系的。在编译之后内部类会被编译成独立的.class文件,前面冠以外部类的类名和$符号。
在这里我要详细解释下上面的第二点和第三点:
静态内部类虽然定义在外部类的里面, 但是它只是在形式上(写法上)和外部类有关系,其实在逻辑上和外部类并没有直接的关系,它并不依赖外部类。虽然它也能访问外部类的静态数据,这是因为在编译的时候,就已经做到数据共享了。
而一般的内部类,不仅在形式上和外部类有关系(写在外部类的里面), 在逻辑上也和外部类有联系。这种逻辑关系主要表现在:内部类对象的创建依赖于外部类对象,内部类对象持有指向外部类对象的引用。
至于为什么会持有外部类对象的引用,以后会再专门拿出篇幅进行说明,这里就先不做介绍了。
根据不同的区分方法,总的来说可以分为成员内部类(普通内部类)、静态内部类、局部内部类、匿名内部类。
一、成员内部类
概念:
成员内部类是跟外部类的成员变量和方法同级的内部类。成员内部类的修饰词跟外部类的成员变量及方法的权限修饰词的作用是一样的。
用法特征:
① 成员内部类与外部类的实例相联系,可以访问外部类的所有成员数据(包括外部类中的 private成员)。正因为成员内部类与外部类的实例联系,因此不能在其内部定义静态成员变量。
② 非静态内部类的创建需要依赖于外部类。
二、静态内部类
概念:
跟成员内部类不同的是:他是由static来修的类,叫做静态内部类。
用法特征:
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但是静态内部类却没有。没有这个引用就意味着:
① 静态内部类不能直接访问外部类的非静态成员,静态内部类可以直接访问外部类的静态成员数据。
③ 静态内部类可以直接创建实例,不需要依赖于外部类。
三、局部内部类
概念:
即在方法中定义的内部类,与局部变量类似,其范围为定义它的代码块。
用法特征:
① 局部内部类可以访问外部类的所有成员数据和方法内的数据。
② 局部内部类访问局部变量和形参时,局部变量和形参必须修饰为final。
③ 局部内部类和普通内部类是相似的,因为他们都不能定义静态成员数据(静态常量除外)。如果局部内部类在静态方法内被定义,那么这个局部内部类就只能访问方法的静态成员。
④ 不能在局部内部类中声明接口,因为接口本身就是静态的。
⑤ 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
在这里解释下上面的第二点:
局部内部类访问局部变量和形参时,局部变量和形参必须修饰为final。这是因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
在Java SE8中,不再需要这样了,只要局部变量和形参不被二次赋值即可。
四、匿名内部类
概念:
没有名字的内部类。形式参考代码示例。
语法:
new 实现接口()|父类构造器(实参列表)
{
//匿名内部类的实体部分
}
用法特征:
① 匿名内部类特别适合于只使用一次的类。
② 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
③ 匿名内部类内部不能有构造方法。
④ 匿名内部类中不能存在任何的静态成员变量和静态方法。
⑤ 匿名类和局部内部类一样,可以访问外部类的成员(必须是final或没有二次赋值的成员)。
java8以前,Java要求被局部内部类,匿名内部类访问的局部变量必须使用final修饰,java8以后,这个限制取消了!
应用场景及注意事项:
当我们只需要类的一个实例,且类在定义以后会马上被用到,代码量少,写上匿名内部类会优化程序结构,并符合以上的那些用法特征的时候,可以考虑用匿名内部类。
对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法:
① 如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数。
② 将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
③ 在这个匿名内部类中使用初始化代码块。
五、代码示例
//OuterClass类
public class OuterClass {
private static String outerStaticStr;
private int outerInt;
// 普通方法
public void outerDisplay(){
System.out.println("OuterClass outerDisplay Method");
}
// 静态方法
public static void outerStaticDisplay(){
System.out.println("OuterClass outerStaticDisplay Method");
}
/*成员内部类*/
public class InnerClass{
public String innerStr; //成员内部类不能声明静态变量
static final int innerInt = 100; // 静态常量
public void innerDisplay(){
outerStaticStr = "Tom"; // 使用外部类的成员变量
System.out.println("成员内部类 " + outerStaticStr);
outerDisplay();// 使用外部类的方法
outerStaticDisplay();
}
}
/*静态内部类*/
public static class StaticInnerClass {
private String innerStr;
public static String innerStaticStr; // 与成员内部类不同,静态内部类可以声明静态变量
public void innerDisplay() {
//静态内部类可以直接访问外部类的静态成员数据。
outerStaticStr = "Jerry";
System.out.println("静态内部类 " + outerStaticStr);
outerStaticDisplay();
}
}
/*局部内部类*/
public void outPut(String string) {
int a = 20;
outerInt = 999;
class LocalInnerClass { // 此时局部内部类与局部变量同一等级,局部不能加private等权限访问修饰词修饰。
static final int b = 20; //静态常量
public void localOutPut() {
//string = "局部内部类"; //不能再赋值
//a = 30; //不能再赋值
System.out.println(string);
System.out.println(outerInt);
}
}
// 只能在方法内实例化
LocalInnerClass localInner = new LocalInnerClass();
localInner.localOutPut();
}
/*匿名内部类*/
public void test(Person per){
System.out.println(per.getName() + "今天走了" + per.walk() + "米。");
}
public InnerClass getInnerClass(){
return new InnerClass();
}
public StaticInnerClass getStaticInnerClass(){
return new StaticInnerClass();
}
}
public class Main {
public static void main(String[] args) {
OuterClass outer = new OuterClass();
//OuterClass.InnerClass inner = outer.getInnerClass();
OuterClass.InnerClass inner = new OuterClass().new InnerClass();// 非静态内部类的创建需要依赖于外部类。
inner.innerDisplay();
OuterClass.StaticInnerClass staticInner = outer.getStaticInnerClass();
staticInner.innerDisplay();
outer.outPut("局部内部类");
/*匿名内部类*/
outer.test(new Person() {
public int walk() { // 实现抽象方法
return 500;
}
public String getName() { // 重写方法
return "小明";
}
});
/*
* test()方法接受一个Person类型的参数,同时我们知道一个抽象类是没有办法直接new的。
* 我们必须要先有实现类才能new出来它的实现类实例。
* 所以在方法中直接使用匿名内部类来创建一个Person实例。
* 由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
* */
}
}
public abstract class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int walk();
}
写完喽!ㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏㄟ(▔,▔)ㄏ
知识重在总结和梳理,只有不断地去学习并运用,才能化为自己的东西。当你能为别人讲明白的时候,说明自己已经掌握了。
欢迎转载,转载请注明出处!
如果有错误的地方,或者有您的见解,还请不啬赐教!
喜欢的话,麻烦点个赞!
本文部分参考了:http://blog.csdn.net/chenssy/article/details/13170015