Java中的内部类

在Java中类可以定义在另一个类的内部或者某一个方法中,这样的类称之为内部类。而包含了内部类的类我们称为外部内。根据定义的位置和定义方式的不同,内部类可以归纳为四类:成员内部类、静态内部类、局部内部类以及匿名内部类。

成员内部类:
定义在类内部,并且非static修饰。
成员内部类定义的位置与一般成员变量一致,相当于是一个特殊的成员变量。成员内部类有如下特点:
1、成员内部类可以访问外部类的成员变量或者成员方法(不管是否是私有的或者是静态的成员变量或者方法)。
2、成员内部类如果是用private修饰的,那么只能在外部类内部使用。
3、成员内部类依赖于外部类存在,当我们想创建一个成员内部类对象时必先有一个外部内对象,通过这个外部内对象创建内部类对象。
4、外部内如果想访问内部类成员变量或者方法时,需要先创建一个内部类对象,通过这个对象来方法内部类内部的成员变量和方法。
5、当成员内部类拥有和外部内一样名称的成员变量或者方法时通过:外部内.this.成员变量/外部内.this.成员方法 的方式来区分外部类的成员变量或者方法,否则会根据就近原则使用内部类的成员变量或者方法。

public class Out {
    private double mDouble = 0.01;
    public static final String STR = "STR";


    public void getInner(){
        Inner inner = new Inner();
        Log.i("TAG","fromInner:"+inner.doubleInInner);//外部类访问内部类成员变量
        inner.innerFn();//外部类访问内部类方法
    }

    class Inner {
        private double doubleInInner = 0.02;
        private void innerFn() {
            Log.i("TAG", "double = " + mDouble);//访问外部内私有成员变量
            Log.i("TAG", "String = " + STR);//访问外部内静态成员变量
            outFn();//访问外部内方法
        }
    }

    private void outFn() {
        Log.i("TAG", "fn in Out class");
    }

}

public class DemoActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        Out out = new Out();
        Out.Inner inner = out.new Inner();//通过外部类对象创建内部类对象
    }
}

内部类只是编译时的概念,Java虚拟机中并没有内部类的说法,包含有内部类的类(这里主要指成员内部类)在Java虚拟机中认为是两个类,会生成两个class文件:外部类.class 和 外部类 $ 内部类.class (匿名内部类生成的class名称一般是:外部类 $ 1.class)。如上面的代码会生成Out.class 和 Out$Inner .class。内部类之所以可以调用外部内的成员变量和方法是因为:A、编译器在编译阶段自动给内部类添加了一个外部类类型的成员变量 B、编译器在内部类的构造函数中默认添加了一个外部类类型的参数 并且把这个参数赋值给A中添加的成员变量 C、在创建内部类对象时 会默认把外部类对象引用传入内部类的构造函数中。 通过以上编译器默认给我们增加的这几个步骤,在内部类中默认就持有外部类的对象引用,所以可以方便的调用外部类的成员变量和方法。

静态内部类:
静态内部类定义位置与成员内部类一致,只是静态内部类需要用static修饰。
静态内部类有如下特点:
1、只能访问外部类中的静态成员变量和静态方法。
2、静态内部类不依赖外部类

public class Out {
    private String mStr = "abc";
    private static String STR = "ABC";

    static class Inner {
        private void innerFn() {
//            Log.i("TAG","mStr = " + mStr);//不能访问非静态成员变量
            Log.i("TAG", "STR = " + STR);
            outFn();
        }
    }

    private static void outFn() {
        Log.i("TAG", "fn in Out class");
    }
}

public class DemoActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);

        Out.Inner inner = new Out.Inner();//不依赖于外部类
    }
}

局部内部类
局部内部类是定义在方法中的类,又叫做方法内部类。
局部内部类有如下特点:
1、不能有public或者private等修饰符(与方法中的属性不能有修饰符一样)
2、如果访问方法中的局部变量,那么局部变量必须用final修饰
3、如果定义局部内部类的方法是非static的,那边内部类可以访问外部类的成员变量或者方法(不管成员变量或者方法是否是静态的),如果定义局部内部类的方法是static的,那么只能访问外部类中静态的成员变量和静态方法。

public class Out {
    private String mStr = "abc";
    private static String STR = "ABC";

    public void fn() {
        final String abc = "fn";
        class innerClass {
            private void innerFn() {
                Log.i("TAG", "STR=" + STR);//访问外部类static变量
                Log.i("TAG", "mStr=" + mStr);//访问外部类成员变量
                Log.i("TAG", "abc=" + abc);//访问局部变量 局部变量需要final修饰
                outFn();//访问成员方法
            }
        }
    }

    private void outFn() {
        Log.i("TAG", "fn in out class");
    }
}

匿名内部类
匿名内部类是开发中使用最多的,匿名内部类主要是用于监听回调,很多时候都是为了重写接口方法。
匿名内部类主要有以下几点特点:
1、匿名内部类可以无条件访问外部类成员变量和方法
2、匿名内部类如果访问局部变量,那么局部变量必须用final修饰

public class DemoActivity extends AppCompatActivity {
    private String str = "abc";
    private static final double PI = 3.14;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        final int count = 1;

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("TAG", "str=" + str);//访问外部类成员变量
                Log.i("TAG", "PI=" + PI);//访问外部内成员变量(静态成员变量)
                Log.i("TAG", "count=" + count);//访问局部变量 final修饰
                fn();//访问成员方法
            }
        });

    }

    private void fn() {
        Log.i("TAG", "fn in out class");
    }
}

局部内部类和匿名内部类访问局部变量为什么需要加final呢?

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
        final int count = 1;

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {  
                Log.i("TAG", "count=" + count);
            }
        });
    }

如上代码:在onCreate方法执行完成后 局部变量count 的生命周期也结束了,但是这个时候OnClickListener的生命周期完全可能还没结束,而这时我们又会在OnClickListener的onClick方法中用到变量count 。为了解决这个问题,Java编译器使用了赋值的方式。Java编译器默认会在匿名内部类或者局部内部类中添加一个变量,并且这个变量的值和局部变量的值相等,这时局部变量和匿名内部类或者局部内部类中添加的变量就是两个不同的变量,仅仅是值相同,生命周期也就没关联。但是这时如果局部变量的值改变,那么匿名内部类或者局部内部类中是不知道,所以为了解决这种局部变量可以改变带来的数据不一致,导致达不到原来的意图或者要求的问题,编译器强制要求局部变量是final,这样局部变量不能改变 则解决了数据不一致问题。

所以局部内部类和匿名内部类访问局部变量之所以要加final是:
1、解决生命周期问题:赋值一个变量
2、解决数据不一致问题:final修饰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值