内部类概念
可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件。
public class OutClass {//外部类 class InnerClass{ //内部类 } }
上面的代码中,OutClass是外部类,InnerClass就是内部类。它与普通外部类最大的不同,在于其实例对象不能单独存在,必须依附于一个外部类的实例对象。
局部内部类
局部内部类是定义在外部类的局部位置,通常在方法里。它拥有外部类中所有元素的访问权限。不能添加访问修饰符,因为它就相当于是一个局部变量,但是可以使用final修饰。作用域仅仅只能在定义它的方法或代码块中。
class Outer{ //外部类 private int a = 1;//私有属性 private void fun2(){ //私有方法 System.out.println("Outer fun2()"); } public void fun1(){ System.out.println("Outer fun1()"); final class Inner{ //局部内部类 private int a = 2; public void funInner(){ System.out.println(a); // 1 外部变量名重名 就近原则 //Outer.this本质上就是外部类的对象,即哪个对象调用了fun1,Outer.this就指向哪个对象 System.out.println(Outer.this.a); // 2 fun2(); } } Inner inner = new Inner(); inner.funInner(); } }
public class InnerClass { public static void main(String[] args) { Outer outer = new Outer(); outer.fun1(); } }
如果外部类和局部内部类成员重名时,默认遵循就近原则,如果想要访问外部类成员可以使用 外部类名.this.成员名。
匿名内部类(重点重点!!! )
匿名内部类就是指没有类名的内部类,必须在创建时使用 new 语句来声明。通常情况下,如果一个方法的参数是接口类型,且该接口只需要实现一次,那么我们就可以通过匿名内部类的形式来进行定义。
基于接口的匿名内部类实例:
class Outer1 { private int a = 10; public void method() { //基于接口的匿名内部类 IA person = new IA() { @Override public void eat() { System.out.println("正在吃大餐"); } }; //查看person的运行类型 System.out.println("person的运行类型:" + person.getClass()); // Outer1$1 person.eat(); } } //接口 interface IA { void eat(); } class Person implements IA { @Override public void eat() { System.out.println("吃"); } } public class AnonInter { public static void main(String[] args) { Outer1 outer1 = new Outer1(); outer1.method(); } }
IA person = new IA() { @Override public void eat() { System.out.println("正在吃大餐"); } }; //上面这段代码其实就相当于 class Outer1$1 implements IA { @Override public void eat() { System.out.println("正在吃大餐"); } }
jdk底层在创建匿名内部类 Outer1$1 时,立即创建了 Outer1$1 实例,并且把地址返回给了person,匿名内部类只能使用一次就不能再使用了。
基于类的匿名内部类和抽象内部类实例:
class Outer2 { public void method() { //编译类型:Animal //运行类型:Outer2$1 Animal animal = new Animal("小白"){ //基于类的匿名内部类 @Override public void eat() { System.out.println("匿名内部类重写了eat()");; } }; animal.eat(); System.out.println(animal.getClass()); //animal的运行类型 Outer2$1 //编译类型:Robot //运行类型:Outer2$2 Robot robot = new Robot(){ //基于抽象类的匿名内部类 @Override void run() { System.out.println("正在奔跑"); } }; robot.run(); System.out.println(robot.getClass());// robot的运行类型 Outer2$2 } } class Animal implements IC { String name; public Animal(String name) { this.name = name; } public void eat(){ System.out.println("Animal 在吃东西"); } @Override public void run() { System.out.println("重写的run()"); } } abstract class Robot { //抽象类 abstract void run(); } interface IC { //接口 void run(); } //测试 public class InterClass { public static void main(String[] args) { Outer2 outer2 = new Outer2(); outer2.method(); } }
匿名内部类细节
匿名内部类既是一个类的定义,同时也是一个对象,从语法上看,它即有定义类的特征,也有创建对象的特征。
public class AnonClassDatil { public static void main(String[] args) { Outer3 outer3 = new Outer3(); outer3.fun(); } } class Outer3 { public void fun(){ Car car = new Car(){ // 运行类型 Outer3$1 @Override public void speed() { System.out.println("重写了speed()");; } }; car.speed(); //动态绑定 //也可以直接调用 new Car(){ @Override public void speed() { System.out.println("直接调用speed()"); } }.speed(); } } class Car { public void speed() { System.out.println("Car speed()"); } }
请看下面的代码
public class InnerExcise { public static void main(String[] args) { fun(new IQ() { //当做实参直接传递 简洁高效 @Override public void show() { System.out.println("正在学习Java内部类"); } }); } public static void fun(IQ iq) { //静态方法 形参是接口类型 iq.show(); } } interface IQ { void show(); }
练习:
有一个铃声接口Bell,里面有一个ring方法,有一个手机类 CellPhone,具有闹钟的功能alarmClock,参数是Bell类型,测试手机类的闹钟功能,通过匿名内部类作为参数,打印 “起床了要迟到了”,再传入另一个匿名内部类,打印 “已经迟到了”。
public class InnerExcise1 { public static void main(String[] args) { CallPhone callPhone = new CallPhone(); callPhone.alarmClock(new Bell() { //传递的是实现了Bell接口的匿名内部类 @Override public void ring() { System.out.println("起床了要迟到了"); } }); callPhone.alarmClock(new Bell() { @Override public void ring() { System.out.println("已经迟到了"); } }); } } interface Bell { void ring(); } class CallPhone { public void alarmClock(Bell bell) { bell.ring(); // 动态绑定 } }
成员内部类
成员内部类是定义在外部类的成员位置,并且没有static修饰,可以访问外部类的所以成员,包括私有成员。可以添加任意访问修饰符,因为它本质上就是一个成员。简而言之,成员内部类就是指没有被static修饰的内部类,也可以称为非静态内部类。
public class InnerExcise2 { public static void main(String[] args) { Outer5 outer5 = new Outer5(); outer5.fun(); } } class Outer5 { private int a = 10; private class Inner { // 可以添加任意访问修饰符 public void say() { System.out.println(a); //可以访问外部类的所有成员,包括私有成员 } } public void fun() { Inner inner = new Inner(); inner.say(); } }
public class InnerExcise2 { public static void main(String[] args) { Outer5 outer5 = new Outer5(); outer5.fun(); //外部其他类 使用成员内部类的几种方式 // 第一种 outer5.new Inner() 相当于是把new Inner()当成一个成员 Outer5.Inner outer51 = outer5.new Inner(); outer51.say(); Outer5.Inner outer52 = outer5.shawOuter5(); outer52.say(); } } class Outer5 { private int a = 10; public class Inner { // 可以添加任意访问修饰符 public void say() { System.out.println(a); //可以访问外部类的所有成员,包括私有成员 } } //第二种 定义一个方法 返回一个Inner对象 public Inner shawOuter5() { return new Inner(); } public void fun() { Inner inner = new Inner(); inner.say(); } }
如果有重名的成员,会遵守就近原则,如果要访问外部类成员,访问方法和上面的一样,外部类名.this.成员名。
静态内部类
静态内部类和成员内部类的定义类似,但要使用static修饰,所以称为静态内部类。静态内部类不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this的方式调用。但它可以访问Outer类的private静态字段和静态方法,如果我们把静态内部类移到Outer类之外,就失去了访问private的权限。
public class StaticInnerClass { public static void main(String[] args) { Outer6 outer6 = new Outer6(); outer6.fun1(); //外部其他类访问静态内部类 //静态内部类可以直接通过类名访问 Outer6.Fun fun1 = new Outer6.Fun(); fun1.show(); //通过定义一个方法返回静态内部类 Outer6.Fun fun2 = outer6.getFun(); fun2.show(); } } class Outer6 { private static int a = 99; private static String str = "java"; static class Fun { public static int a = 999; public void show() { System.out.println(str); System.out.println(a); //同名成员遵循就近原则 System.out.println(Outer6.a); //通过 外部类名.成员名访问 } } public void fun1() { Fun fun = new Fun(); fun.show(); } public Fun getFun() { return new Fun(); } }
静态内部类中只能访问静态外部类的静态属性和方法