Java中将定义在另一个类或方法里面的类称为内部类。
一、内部类基础
内部类分为成员内部类、局部内部类、匿名内部类和静态内部类
2.成员内部类
类在另一个类的里面作为成员
public class OuterClass01 {
private String outPrivateAttr = "outPrivateAttr";
private void outPrivateM() {
System.out.println("outer private method");
}
//内部类
class InnerClass {
public void innerClassVisitOuterClass(String[] args) {
System.out.println(outPrivateAttr);
outPrivateM();
}
}
}
普通的类只能用public和default权限修饰符修饰,而内部类则没有这方面的限制,private和protected也可以。
1)成员内部类可以访问访问外部类的成员变量和成员方法,不受内部类权限修饰符限制。(见上一个代码示例,访问外部类private修饰的方法和变量不受限制)
2)当成员内部类和外部类拥有同名的成员变量和成员方法时,会发生隐藏现象(默认访问成员内部类的成员),如果要访问外部类的同名成员,用如下格式进行访问:外部类.this.成员变量
外部类.this.成员方法
package com.chensan;
public class OuterClass01 {
public String attr1 = "outer attr";
public void method1() {
System.out.println("outer method1");
}
//内部类
class InnerClass {
public String attr1 = "inner attr";
public void method1() {
System.out.println("inner method");
}
public void innerClassVisitOuterClass(String[] args) {
//与外部类同名的方法和变量(默认访问内部类自己的)
System.out.println(attr1);//com.chensan.OuterClass01.InnerClass.attr1
method1();//com.chensan.OuterClass01.InnerClass.method1()
//与外部类同名的方法和变量(访问外部类的)
System.out.println(OuterClass01.this.attr1);//com.chensan.OuterClass01.attr1
method1();//com.chensan.OuterClass01.method1()
}
}
}
3)外部类访问内部类的成员,必须先创建一个内部类的对象,然后通过指向这个对象的引用来访问内部类。
package com.chensan;
public class OuterClass01 {
private InnerClass inner = null;
//内部类
class InnerClass {
public String attr1 = "inner attr";
public void method1() {
System.out.println("inner method");
}
}
public InnerClass getInnerInstance() {
if(inner == null) {
inner = new InnerClass();
}
return inner;
}
}
package com.chensan;
public class TestInnerClass01 {
public static void main(String[] args) {
OuterClass01 outer = new OuterClass01();
//第一种方式
OuterClass01.InnerClass inner = outer.new InnerClass();
//第二种方式
OuterClass01.InnerClass inner2 = outer.getInnerInstance();
System.out.println(inner.attr1);
inner2.method1();
}
}
内部类可用private、default、default、protected和public权限修饰符,权限修饰符的范围同样适用于内部类,如private修饰的内部类只能在外部类的内部访问。
2.局部内部类
局部内部类是定义在一个方法或作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
局部内部类就像方法里的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
3.匿名内部类
匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段Android事件监听代码:
scan_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
history_bt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:
new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。
private void setListener() {
scan_bt.setOnClickListener(new Listener1());
history_bt.setOnClickListener(new Listener2());
}
class Listener1 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
class Listener2 implements View.OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
4.静态内部类
静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
public class OuterClass01 {
int a = 1;
static int b = 2;
static class Inner01 {
private void method2() {
//System.out.println(a);//Cannot make a static reference to the non-static field a
System.out.println(b);
}
}
public static void main(String[] args) {
OuterClass01.Inner01 inner = new OuterClass01.Inner01();
inner.method2();
}
}
二、深入理解java内部类
1.为什么成员内部类可以无条件访问外部类的成员?
在此之前,我们已经讨论过了成员内部类可以无条件访问外部类的成员,那具体究竟是如何实现的呢?下面通过反编译字节码文件看看究竟。事实上,编译器在进行编译的时候,会将成员内部类单独编译成一个字节码文件,下面是OuterClass01.java
package com.chensan;
public class OuterClass01 {
private InnerClass inner = null;
class InnerClass {
public String attr1 = "inner attr";
public void method1() {
System.out.println("inner method");
}
}
public InnerClass getInnerInstance() {
if(inner == null) {
inner = new InnerClass();
}
return inner;
}
}
OuterClass01.java在D:\workspace-eclipse\javaSE\src\com\chensan下,cmd下编译:
D:\workspace-eclipse\javaSE\src\com\chensan>javac OuterClass01.java
在相同目录下生成:OuterClass01$InnerClass.class和OuterClass01.class两个编译文件。
反编译OuterClass01$InnerClass.class
D:\workspace-eclipse\javaSE\src\com\chensan>javap -v OuterClass01$InnerClass
懒得写了
参考:海子博客园 Java内部类详解