内部类分类
内部类分为四种:
1:实例内部类:定义在类的内部的一个类
2:静态内部类:定义在类的内部的一个类,但是由static来修饰的
3:匿名内部类
4:本地内部类:方法里面定义的类,没有意义,可以不看
实例内部类
如何实例化内部类对象
方式:
OuterClass.InnerClass innerClass2 = out.new InnerClass();
外部类名.内部类名 变量 = 外部类对象的引用.new 内部类();
代码示例
//实例内部类 class OuterClass { public int data1 = 3; public static final int data4 = 4; //InnerClass就是我们的实例内部类 class InnerClass { public int data3; //实例内部类可以书写构造方法 public InnerClass() { } /*注意实例内部类内部不能定义静态的成员变量 例如public static int data = 9;这种就不能定义 */ //如果非要定义静态的成员变量,要加上final public static final int data5 = 9; public void test() { System.out.println("InnerClass::test()"); System.out.println(data3); System.out.println(this.data3); System.out.println(OuterClass.this.data1); } } } public class TestMain { public static void main(String[] args) { //实例内部类实例化对象的方式 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); } }
注意事项:
1、只要是内部类,那么生成的字节码文件格式:外部类$内部类.class
2、在实例内部类当中,不能定义静态的成员变量!!
如果非要定义,那么一定要是在编译时期确定的值,即常量
其定义方式为public static final
3、先来看一段代码:思考我们的data1最后的输出结果是多少?
class OuterClass { public int data1 = 3; class InnerClass { public int data1 = 10; public void test() { //最后输出的data1的值为10 System.out.println(data1); } } } public class TestMain { public static void main(String[] args) { //实例内部类实例化对象的方式 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); //结果为10 innerClass.test(); } }
可以看到最后的输出结果为10,所以说当一个类和其内部类声明了相同的一个成员变量的时候,最后输出的成员变量的值一定是这个内部类的这个成员变量的值
但是注意了:此时我们就想用外部类的data1的值,不想用内部类的该怎么办呢?,来看代码:
class OuterClass { public int data1 = 3; public static final int data2 = 4; class InnerClass { public int data1 = 10; public static final int data4 = 9; public InnerClass() { } public void test() { //最后输出的data1的值为3,注意书写方式 System.out.println(OuterClass.this.data1); } } } public class TestMain { public static void main(String[] args) { //实例内部类实例化对象的方式 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); //结果为3 innerClass.test(); } }
总结
格式:System.out.println(OuterClass.this.data1);
可以看到使用外部类名.this.变量名就可以获取到对应的值了
这里就说明一个问题:this实际上也是一个静态的成员
同时也说明实例内部类拥有两个this,一个是实例内部类自己的,一个是外部类的:来看代码:
class OuterClass { public int data1 = 3; class InnerClass { public int data1 = 10; public InnerClass() { } public void test() { //此处输出的data1的值为10 System.out.print(this.data1); //最后输出的data1的值为3 System.out.println(OuterClass.this.data1); } } } public class TestMain { public static void main(String[] args) { //实例内部类实例化对象的方式 OuterClass outer = new OuterClass(); OuterClass.InnerClass innerClass = outer.new InnerClass(); //结果为10,3 innerClass.test(); } }
可以看到我们可以使用this获取到内部类的data1,可以使用外部类名.this获取到外部类中的data1.
所以这里有个面试题:同学,实例内部类,是否有额外的内存开销??
答:当然有,实例内部类拥有两个this,一个是实例内部类自己的,一个是外部类的
静态内部类
概念
定义在类的内部的一个类。但是由static来修饰的.
如何实例化对象
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
= new 外部类.静态内部类();
下面来看代码:
class OuterClass { public int data1 = 3; static class InnerClass { public InnerClass() { } public void test() { /*注意此时不能访问外部类中的非静态数据成员 System.out.print(data1); */ } } } public class TestMain { public static void main(String[] args) { //静态内部类实例化对象的方式 OuterClass.InnerClass innerClass = new OuterClass.InnerClass(); //结果为10,3 innerClass.test(); } }
总结:
1:静态内部类的实例化方式我已经写在了代码中
2:只要是内部类,此处尽管是静态内部类,那么生成的字节码文件格式仍为:外部类$内部类.class
3:静态内部类当中,是不可以访问外部类的非静态数据成员的!!!!
因为,外部类的非静态数据成员 是依赖于外部类对象的
那么假如面试官非要我们访问的话,该怎么办呢?
思路:给个外部类对象的引用就好了:来看代码:
class OuterClass { public int data1 = 3; static class InnerClass { public OuterClass out; //构建一个有参的构造函数 public InnerClass(OuterClass out) { this.out = out; } public void test() { System.out.println(out.data1); } } } public class TestMain { public static void main(String[] args) { OuterClass out = new OuterClass(); //传入一个out变量 OuterClass.InnerClass innerClass = new OuterClass.InnerClass(out); //结果为3 innerClass.test(); } }
匿名内部类
代码示例
/* 匿名内部类 */ class OuterClass { public void test() { System.out.println("匿名内部类"); } } public class TestMain { public static void main(String[] args) { new OuterClass(){ }.test(); } }
可以看到直接在主方法main的内部直接new一个类名,这样的写法就叫做匿名内部类,然后如果想要调用OuterClass内的方法,直接在后面.方法名即可.
此时我们也可以重写OuterClass类中的test方法
/* 匿名内部类 */ class OuterClass { public void test() { System.out.println("匿名内部类"); } } public class TestMain { public static void main(String[] args) { new OuterClass(){ @Override public void test() { System.out.println("重写我们的匿名内部类"); } }.test(); //重写后的输出结果为:重写我们的匿名内部类 } }
注意此处我们在匿名内部类重写了外部类中的test方法,最终的输出结果也一定是重写后的test方法当中的输出结果
匿名内部类的变量捕获
先来看一段代码:
class Test { public void func() { System.out.println("func()"); } } public class TestMain { public static void main(String[] args) { int a = 100; new Test() { @Override public void func() { System.out.println("我是内部类,且重写了func这个方法!"); System.out.println("我是捕获到变量 a == " + a + " 我是一个常量,或者是一个没有改变过值的变量!"); } }; } }
可以看到我们在匿名内部类的外部有一个变量a,其值为100,这个变量是可以在匿名内部类中是可以被捕获到并且进行输出的,最终输出的值仍为100
假设此时我们想要对这个a重新在内部类中进行赋值输出的话,最后能够成功输出吗?来看代码:
/* 匿名内部类 */ class Test { public void func() { System.out.println("func()"); } } public class TestMain { public static void main(String[] args) { int a = 100; new Test() { @Override public void func() { //重新赋值 a = 50; System.out.println("我是内部类,且重写了func这个方法!"); System.out.println("我是捕获到变量 a == " + a + " 我是一个常量,或者是一个没有改变过值的变量!"); } }; } }
此时编译就直接报错啦
总结:
1:在上述代码当中的变量a就是捕获的变量。这个变量要么是被final修饰,是一个常量,不能被修改,要么就不是被final修饰的
2:如果不是被final修饰的你要保证在使用之前,没有修改。即如果上面的代码我要是在匿名内部类外面已经定义了一个变量a的话,在匿名内部类中是不能对这个变量a进行修改的.否则会编译出错.