内部类的定义
将一个类定义在另一个类的类里面或者方法里面称为内部类。内部类分为四种:成员内部类、局部内部类、静态内部类、匿名内部类。下面我们将对这四种内部类一一展开讲解。
成员内部类
局部内部类是最普通的内部类,如下Inner类就是Outer类的内部类:
public class Outer {
private Integer pri = 2;
static Integer sta = 3;
public void add(Integer a,Integer b){
System.out.println(a + "+" + b + "=" + (a+b));
}
class Inner{ // 成员内部类
public void InnerMethod(){
private Integer innerNum = 55;
private Integer pri = 4;
System.out.println("我是成员内部类");
//直接访问外部类的成员方法,私有属性和静态属性
add(pri,sta);
}
}
}
成员内部类可以无条件的访问外部类的所有属性包括是私有(private)属性和静态(static)属性。虽然内部类可以无条件访问外部类的属性,但是外部类想要访问内部类的数据必须得先new出内部类(你的是我的,我的还是我的)
public void userInner(){
//外部类使用内部类必须先new
System.out.println("我是内部类的属性:" + new Inner().innerNum);
}
不过注意当内部类和外部类有相同的属性或方法时优先使用内部类的属性和方法,想用使用外部类的属性或方法需要用一下方法
//当成员内部类和外部类有相同的属性或方法时,默认使用内部类的属性方法
public void userSame(){
System.out.println("我是内部类的pri=" + pri);
System.out.println("我是外部类的pri=" + Outer.this.pri);
}
因为内部类是依附外部类而存在的,所以内部类需要使用外部类才能创建出来,创建内部类的方法如下:
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.new Inner();
}
局部内部类
局部内部类是定义在方法或作用域里的类,它相对于成员内部类的区别就是它的访问权限仅限于该方法内或者该作用域内。
public Outer getPart(){
class Part extends Outer{ //局部内部类
int partNum = 12;
public void print(){
System.out.println("我是局部内部类 partNum = " + partNum);
}
}
return new Part();
}
静态内部类
静态内部类也是定义在另一个类里的类,不过它是用static修饰的,所以它的创建不依赖外部类,它只能访问外部类的static属性。
static class StaicInnerClass{ //静态内部类
public void staticMethod(){
System.out.println("我是静态内部类,可以访问外部内的static属性 sta = " + sta);
}
}
public static void main(String[] args) {
// 可以直接用new来创建我,不需要依赖外部内
StaicInnerClass staicInnerClass = new StaicInnerClass();
}
匿名内部类
匿名内部类是比较难以理解的一个内部类,顾名思义,它是没有名字的,创建方法如下:
new 类名/接口/抽象类(){
}
举个例子:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
int i = 3;
new Runnable() {
@Override
public void run() {
System.out.println("我是匿名内部类");
System.out.println("访问外部内的final属性 i = " + i);
}
};
它是new一个Runnable接口,并实现这个接口里面的run方法,整个这一段代码就是一个匿名内部类,它和我们平常见到的类的书写方式不一样,它没有名字,是段代码块。注意:匿名内部类只能访问final修饰的属性,但是看上面的代码i没有被final修饰也能被匿名内部类使用,不要着急,这个问题我们下面详细来解释。
通过class文件来详细讲解内部类
我们先新建一个内部类测试一下
public class OuterClassTest {
private Integer pri = 2;
Integer sta = 3;
class Inner{ // 成员内部类
private Integer innerPri = pri;
private Integer innerSta = sta;
}
}
然后用javac编译这个类,我们会发现生成了俩个class文件,说明在底层这里是生成了俩个类去处理的。
那我们的内部类是怎么访问到外部类的属性的呢,我们是用javap反编译OuterClassTest$Inner.class文件看一下
看第四行,这里传入了一个外部类的指针。
那这个引用又是如何赋初始值的呢:
example.innerclass.OuterClassTest$Inner(example.innerclass.OuterClassTest)
从这里我们知道虽然我们给内部类是个无参的构造器,但是jvm在编译的时候会给我们的内部类的构造器添加一个外部类的引用,所以我们内部类可以直接访问到外部内的所有的成员。这里也说明了内部类的创建依赖于外部类,因为如果没有外部类,内部类构造的时候就获取不到外部类的引用,也就创建不了了。
接下来我们来看一下为什么匿名内部类只能访问局部final变量,那为什么上面的匿名内部类的代码访问了一个非final变量。我们直接编译一上面的例子,然后直接用idea打开:
我们会发现,编译器直接自动给我们的变量加上了final属性。原来这里是java8之后的一个语法糖,就是你可以不写,之后编译器会自动给你加上,java8之前的版本不写final就会编译报错。
好啦,以上就是小编今天整理的一些关于java内部类的一些知识,有帮助的同学请帮忙走波关注,本公众号会不定期推动一些java干货。