Java内部类
- 内部类的基本介绍
- 局部内部类
- 匿名内部类
- 成员内部类
- 静态内部类
内部类基本介绍
-
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类又称为内部类,嵌套其他类的类称为外部类。是类的五大成员之一(属性、方法、构造器、代码块、内部类)。
-
内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系
-
基本语法
class Outer{//外部类 class Inner{//内部类 } } class Other{//外部其他类 }
-
内部类的分类
- 定义在外部类的局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点!)
- 定义在外部类的成员位置上:
- 成员内部类(没用static修饰)
- 静态内部类(使用static修饰)
- 定义在外部类的局部位置上(比如方法内):
局部内部类
说明:局部内部类是定义在外部类的局部位置上的,通常在方法中,并且有类名。
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final。(使用了final说明该内部类不能被继承!)
-
作用域:仅仅在定义它的方法或代码块中。
-
局部内部类访问外部类的成员(访问方式:直接访问)
-
外部类访问局部内部类的成员(访问方式:创建对象,再访问[注意,必须在作用域内!])
代码演示
package com.innerclass; public class LocalInnerClass { public static void main(String[] args) { Outer01 outer01 = new Outer01(); outer01.m2(); } } class Outer01{ private int n = 100; private void m1(){ System.out.println("m1"); } public void m2(){ //定义为final就不能被继承 //作用域:定义它的方法或代码块中 final class Inner01{//局部内部类 public void f1(){ //访问外部类的成员属性n与m1 System.out.println("n = "+ n); m1(); } } //外部类调用必须在内部类的作用域内! Inner01 inner01 = new Inner01(); inner01.f1(); } }
-
外部其他类不能访问局部内部类!因为局部内部类是一个局部变量
-
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想要访问外部类的成员,则可以使用(外部类目.this.成员)去访问。
System.out.println("OuterClass的n2 = "+ OuterClass.this.n2);
OuterClass.this本质就是外部类的对象,即哪个对象调用了内部类作用域的方法,OuterClass.this就是哪个对象
匿名内部类
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名(系统的底层有给他分配名字)。
-
匿名内部类的基本语法
new 类或接口(参数列表){ //类体 };
-
匿名内部类的本质(代码演示)
有一个需求,想要使用AnonInterface接口,并创建对象。
传统的方式是写一个类,实现该接口,并创建其对象。
AnonmousInterface接口
interface AnonInterface{ public void cry(); }
Tiger类
class Tiger implements AnonInterface{ public void cry(){ @Override System.out.println("Titge 在叫"); } }
然后在main函数创建其对象即可
package com.innerclass; public class AnonymousInnerClass { public static void main(String[] args) { AnonInterface tiger = new Tiger(); tiger.cry(); } }
如果我们现在的需求改为Tiger类只使用一次,后面不再使用时。我们可以使用匿名内部类来简化开发。
AnonInterface接口
interface AnonInterface{ public void cry(); }
外部类Outer02
class Outer02{ private int n = 10; public void method(){ //使用匿名内部类 AnonInterface tiger = new AnonInterface(){ @Override public void cry() { System.out.println("tiger 在叫"); } }; tiger.cry(); } }
main
package com.innerclass; public class AnonymousInnerClass { public static void main(String[] args) { Outer02 outer02 = new Outer02(); outer02.method(); } }
上述代码与传统方式均实现了需求,而且上面的代码,Tiger类只使用了一次。这就是匿名内部类的奇妙之处!
思考:上述外部类Outer02中,Tiger的编译类型及运行类型分别是什么?
-
Tiger的编译类型为接口AnonInterface
-
Tiger的运行类型为匿名内部类 Outer02$1
其实在使用匿名内部类时,底层是给该内部类分配了名字的
Class Outer02$1 implements AnonInterface{ @Override pubilc void cry(){ System.out.println("Tiger 在叫"); } }
我们通过getclass()方法可以验证上述代码。
System.out.println(tiger.getClass());
而且,在外部类Outer02中,AnonInterface tiger = new AnonInterface()这条语句的执行,jdk底层会在创建匿名内部类Outer02$1时,立刻创建Outer02$1实例,并且把地址返回给tiger
需要注意的是:匿名内部类Outer02$1是只使用一次,就不能再使用。而对象Tiger能重复使用!
基于类构建的匿名内部类也与上述大体一致。
-
-
匿名内部类的细节
-
匿名内部类的语法比较奇特,需要注意的是,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。因此可以直接调用匿名内部类的方法。
代码演示:
创建Person类
class Person{ public void hi(){ System.out.println("hi"); } public void sayHi(String name){ System.out.println("Hi "+name); } }
创建外部类Outer03
class Outer03{ private int i = 10; public void f1(){ Person person = new Person(){ @Override public void hi() { System.out.println("匿名内部类重写了Hi方法"); } }; //动态绑定,person的运行类型是Outer03$1 person.hi(); //因为匿名内部类返回的是一个对象(new),所以可以直接调用方法 //在底层 class 匿名内部类 extends Person new Person(){ @Override public void sayHi(String name) { super.sayHi(name); } }.sayHi("jack"); } }
创建AnonymousInnerClassDetail
package com.innerclass; public class AnonymousInnerClassDetail { public static void main(String[] args) { Outer03 outer03 = new Outer03(); outer03.f1(); } }
输出结果:
-
外部其他类不能访问匿名内部类
-
-
匿名内部类的实践
匿名内部类可以当做实参直接传递,简洁高效,避免硬编码
创建接口InterfaceExercise
interface InterfaceExercise{ public void show(); }
创建InnerClassExercise01
package com.innerclass; public class InnerClassExercise01 { public static void main(String[] args) { //匿名内部类做形参,简洁高效。 f1(new InterfaceExercise(){ @Override public void show() { System.out.println("这是一幅名画..."); } }); } //静态方法,参数是接口类型 public static void f1(InterfaceExercise iE){ iE.show(); } }
如果使用实现类实现该接口,那修改一个实现类的对象时会影响到其他的实现类对象。而使用匿名内部类,可以避免这个问题,并且这个类只使用一次即可。
成员内部类
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。
-
可以直接访问外部类的所有成员,包含私有的
-
可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员。
代码演示:
创建外部类Outer05
class Outer05{ private int i = 10; //定义在成员位置上,可以添加修饰符! public class Inner04{ public void say(){ //可以访问外部类的私有属性 System.out.println("成员内部类 i = "+i); } } //因为作用域是方法或代码块内,所以需要写一个方法来使用内部类 public void t1(){ Inner04 inner04 = new Inner04(); inner04.say(); } }
创建测试类MemberInnerClass
package com.innerclass; public class MemberInnerClass { public static void main(String[] args) { Outer05 outer05 = new Outer05(); outer05.t1(); } }
运行结果:
-
作用域与外部类的其他成员一样,为整个类体。比如上述案例,在外部类的成员方法中创建成员内部类对象,再调用方法。
-
外部其他类可以访问成员内部类
外部其他类使用成员内部类的两种方式
第一种方式
//第一种方式 //outer05.new Inner04();相当于把 new Inner04()当做是outer05的成员 Outer05.Inner04 inner04 = outer05.new Inner04();
第二种方式
//在外部类中编写一个方法,可以返回一个Inner04的值 Outer05.Inner04 getInner04 = outer05.getInner04();
外部类Outer05中
//该方法返回一个成员内部类 Inner04 getInner04(){ return new Inner04(); }
静态内部类
说明:静态内部类定义在外部类的成员位置,并且有static修饰
-
可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
-
可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员。
-
作用域:同其他成员,为整个类体
代码演示
创建Outer06类
class Outer06{ private int n1 = 10; private static int n2 = 100; static class Inner06{ void say(){ System.out.println("hi n = " + n2); } } void t1(){ Inner06 inner06 = new Inner06(); inner06.say(); } }
创建测试类StaticInnerClass
package com.innerclass; public class StaticInnerClass { public static void main(String[] args) { Outer06 outer06 = new Outer06(); outer06.t1(); } }
运行结果:
-
静态内部类要访问外部类的话,可以访问其全部静态成员。
-
外部类访问静态内部类,需要先创建对象,再访问。
-
外部其他类使用静态内部类的方式有两种,与成员内部类的一样。