内部类
类的五大成员
- 属性
- 方法
- 构造器
- 代码块
- 内部类
基本介绍:
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。
基本语法
class Outer{ //外部类
class Inner{ //内部类
}
}
class Other{ //外部其他类
}
内部类的分类
- 定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点)
- 定义在外部类的成员位置上:
- 成员内部类(没用static修饰)
- 静态内部类(使用static修饰)
局部内部类的使用
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码中。
- 局部内部类访问外部类的成员【访问方式:直接访问】
- 外部类访问局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
- 外部其他类不能访问局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
代码实现
public class LocalInnerClass {
public static void main(String[] args) {
Outer01 outer01 = new Outer01();
outer01.m1();
}
}
class Outer01{ //外部类
private int n1 = 100; //私有成员变量
private void m2(){ //私有方法
System.out.println("m2方法被调用。");
}
public void m1(){ //外部类的方法
final class Inner01{ //局部内部类
private int n1 = 800; //局部内部类与外部类成员变量重名
public void f1(){ //局部内部类可以直接访问外部类的所有成员,包含私有的
System.out.println("局部内部类的n1="+n1+" 外部类的n1="+Outer01.this.n1); //遵循 //就近原则,Outer01.this相当于Outer01实例化对象
m2();
}
}
Inner01 i1 = new Inner01();
i1.f1();
}
}
匿名内部类的使用
- 本质是类
- 内部类
- 该类没有名字
- 同时还是一个对象
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名(它的类名是系统内部自动生成的,用户是 看不到的)。
-
基于接口实现匿名内部类:
public class AnonymousInnerClass { public static void main(String[] args) { Outer02 outer02 = new Outer02();//创建外部类对象 outer02.method(); } } /** * 基于接口实现匿名内部类 * Outer02为外部类 */ class Outer02{ private int n1 = 10; public void method(){ /** * 1.如果想实现Animal接口,常规方法是定义一个类实现Animal接口,并创建对象 * 但如果定义的类只使用一次,后面不再使用,就会造成浪费 * 2.匿名内部类可以省去定义一个新的类,定义如下所示: * tiger的编译类型:Animal,就是接口类型 * tiger的运行类型:就是匿名内部类 Outer02$1 * 底层会分配一个类名为Outer02$1的类实现接口 * 3.jdk底层在创建匿名内部类时,立即创建了匿名内部类实例,并且把地址传给了tiger */ /* class Outer02$1 implements Animal{ @Override public void cry(){ System.out.println("老虎叫唤..."); } } */ Animal tiger = new Animal() { @Override public void cry() { System.out.println("老虎叫唤..."); } }; tiger.cry(); System.out.println("tiger对象的运行类型:"+tiger.getClass()); } } interface Animal{ void cry(); }
2.基于类实现匿名内部类:
public class AnnoymousInnerClass02 { public static void main(String[] args) { Outer03 outer03 = new Outer03(); outer03.method(); } } class Outer03{ private int n1 = 10; public void method(){ /** * P1的编译类型:Person * p1的运行类型:匿名内部类 Outer03$1 * 传递的参数“Jack”传给了Person的构造器 * 如果基于抽象类的匿名内部类,匿名内部类要实现抽象类的所有抽象方法 */ /* 底层会创建匿名内部类 class Outer03$1 extends Person{ @Override public void test(){ System.out.println("重写了Person类的test方法..."); } } */ Person p1 = new Person("jack"){ public void test(){ System.out.println("重写了Person类的test方法..."); } }; p1.test(); System.out.println("p1的运行类型:"+p1.getClass()); } } class Person{ public Person(String name){ System.out.println("调用了Person类的构造器..."); } public void test(){ } }
匿名内部类的使用细节
-
匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。
Person p1 = new Person("jack"){ public void test(){ System.out.println("重写了Person类的test方法..."); } }; new Person("rose"){ public void test(){ System.out.println("重写了Person类的test方法..."); } }.test();
-
可以直接访问外部类的所有成员,包含私有的。
-
不能添加访问修饰符,因为它的地位就是一个局部变量。
-
作用域:仅仅在定义它的方法或代码块中
-
外部其他类不能访问匿名内部类(因为匿名内部类地位是一个局部变量)
-
如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
匿名内部类的案例
1.有一个铃声接口Bell,里面有个ring方法。
2.有一个手机类Cellphone,具有闹钟功能alarmclock,参数是Bell类型
3.测试手机类的脑涨功能,通过匿名内部类(对象)作为参考,打印:懒猪起床了
4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
说明:运用了(1)继承(2)多态(3)动态绑定(4)内部类
代码实现:
public class InnerClassExercise02 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
//匿名内部类作为参数传入alarmclock(Bell bell)方法中
cellphone.alarmclock(new Bell() { //定义匿名内部类
@Override
public void ring() {
System.out.println("懒猪起床了...");//重写ring()方法
}
});
cellphone.alarmclock(new Bell() { //定义匿名内部类
@Override
public void ring() {
System.out.println("小伙伴上课了...");//重写ring()方法
}
});
}
}
interface Bell{
void ring();
}
class Cellphone{
public void alarmclock(Bell bell){ //传入一个匿名内部类对象
bell.ring();
}
}
成员内部类的使用
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域和外部类的其他成员一样,为整个类体
- 成员内部类访问外部类成员【访问方式:直接访问】
- 外部类访问成员内部类【访问方式:创建对象,再访问】
- 外部其他类访问成员内部类有两种方法
- 如果外部类和内部类的成员重名时(如下代码所示)
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.n1();
//外部其他类调用成员内部类
//1.第一种方法:
Outer04.Inner04 i5 = outer04.new Inner04();
i5.m1();
//2.第二种方法:在外部类定义一个方法返回成员内部类对象
Outer04.Inner04 i6 = outer04.getInstanceInner04();
i6.m1();
}
}
class Outer04{
private int n1 = 10;
public String name = "jack";
class Inner04{
private int n1 = 99;
public void m1(){
System.out.println("调用m1方法...");
System.out.println("外部类属性 name = "+name);
//外部类与成员内部类成员名字冲突时:
System.out.println("外部类 n1 = "+Outer04.this.n1);
System.out.println("成员内部类 n1 = "+n1);
}
}
public void n1(){
Inner04 i4 = new Inner04();//创建成员内部类对象
i4.m1();
}
public Inner04 getInstanceInner04(){
return new Inner04();
}
}
静态内部类的使用
说明:静态内部类是定义在外部类的成员位置,并且有static修饰
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
- 作用域:同其他的成员,为整个类体
- 静态内部类访问外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
- 外部类访问静态内部类【访问方式:创建对象,再访问】
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时候,默认遵循就近原则。如果想访问外部类成员,则可以使用(外部类名.成员)
代码实现:
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.n1();
//外部其他类调用静态内部类
//1.第一种方法:
Outer05.Inner05 i5 = new Outer05.Inner05();
//2.第二种方法:在外部类定义一个方法返回静态内部类
Outer05.Inner05 i6 = Outer05.getInner05();
}
}
class Outer05{
private static int n1 = 10;
public static String name = "jack";
public static class Inner05{
private static int n1 = 99;
public void m1() {
System.out.println("调用m1()方法...");
//外部类和静态内部类成员重名
System.out.println("外部类 n1 = "+Outer05.n1);
System.out.println("静态内部类 n1 = "+n1);
}
}
public void n1(){
Inner05 inner05 = new Inner05();
inner05.m1();
}
public static Inner05 getInner05(){
return new Inner05();
}
}
综合案例
public class test {
public static void main(String[] args) {
//并不是10,因为成员变量的初始值不会改变,并且也不是静态成员变量
Outer06 outer06 = new Outer06();
Outer06.Inner06 i6 = outer06.new Inner06();
System.out.println(i6.n);//5
}
}
class Outer06{
public Outer06(){
Inner06 inner06 = new Inner06();
inner06.n = 10;
Inner06 i2 = new Inner06();
System.out.println(i2.n);//5
}
class Inner06{
public int n = 5;
}
}