目录
在学习Java中的内部类之前,需要先搞清楚:外部其它类,外部类,内部类之间的关 系:
内部类就是定义在类的内部的类;外部类指包含有内部类的类;外部其它类是针对包含InnerClass类的OuterClass之外的类。
内部类可以分为两大类,四小类:
- 定义在外部类的局部位置上的:局部内部类、匿名内部类
- 定义在外部类的成员位置上的:成员内部类和静态内部类
局部内部类
局部内部类是指定义在外部类的局部位置(通常是方法,还包括代码块)内的有名称的类。
局部内部类的使用需要注意:
- 局部内部类可以直接访问外部类的所有属性和方法,包括私有的。局部内部类本质上一个类
- 局部内部类不能添加访问修饰符,因为它的地位就是一个局部变量,但是可以使用final修饰符
- 局部内部类的作用域只在方法内或者代码块内
- 外部类在包含该内部类的方法中,可以创建局部内部类对象,然后调用该局部内部类的方法
- 外部类和局部内部类重名时,访问遵循就近原则。如果在局部内部类中访问外部类中的变量,可以使用"外部类名.this.属性"(外部类名.this指的是外部类对象)
局部内部类的实例:
//外部类 class Outer { private int money = 14000; private void m1() { System.out.println("Outer m1()"); } public void m2() { //局部内部类 final class Inner { private int money = 12000; public void m3() { System.out.println("Inner m3()"); System.out.println("Outer" + Outer.this.money + " Inner" + money); m1(); } } Inner in = new Inner();//在方法中创建了一个Inner对象 in.m3(); } } //外部其它类 public class InnerClassOfPart { public static void main(String[] args) { /* 创建一个Outer对象,访问m2方法,在m2方法的颞部创建了一个局部 内部类,调用该局部内类的m3()方法,该局部内部类的m3()方法中又调用了外部类中的私有成员变量 money和成员方法m1 */ Outer out = new Outer(); out.m2(); } }
匿名内部类(重点!!!!!)
匿名内部类和局部内部类类似,使用匿名内部类需要注意:
- 不能添加访问修饰限定符,因为他是一个局部变量
- 匿名内部类的作用域为定义它的方法内部或者代码块内部
- 外部其它类不能访问匿名内部类,因为匿名内部类的地位是一个局部变量
- 在匿名内部类中可以直接访问外部类的所有成员,访问方法使用"外部类名.this.成员"
- 匿名内部类的使用应该注意语法,匿名内部类既是一个类的定义,同时本身也是一个对对象的引用;因此既有定义类的特征,也有创建对象的特征
假设现在有一个接口IBell,在接口的内部有一个ring()方法,在开发的过程中需要用这个接口中的ring方法在屏幕中输出"该上课了",那么传统的方法是再定义一个类来实现这个接口,然后覆写该抽象方法。这种做法当然可行,但是如果每次输出的内容都不同,就需要每次都使用不同的类来实现接口,覆写方法吗?匿名内部类的出现就解决了这个问题。
基于接口的匿名内部类定义和使用:
interface IBell { void ring();//默认为public abstract void ring() } class Cellphone { public void alarmclock(IBell bell) { bell.ring(); } } public class InnerClassExercise02 { public static void main(String[] args) { Cellphone cellphone = new Cellphone(); //使用基于接口的匿名内部类 cellphone.alarmclock(new IBell() { @Override public void ring() { System.out.println("懒猪起床了"); } }); cellphone.alarmclock(new IBell() { @Override public void ring() { System.out.println("小伙伴上课了"); } }); } }
在上述基于接口的匿名对象类的使用过程中,调用Cellphone类中的alarmclock方法传入的实参即为匿名内部类,对于该种使用方法,在底层其实创建了一个名为InnerClassExercise02$1的实体类,并且在该类实现了接口,覆写了接口中的ring()方法。
class InnerClassExercise02$1 implements IRing { @Override public void ring() { System.out.println("上课了!"); } }
在这个过程中发生的调用关系为:
基于类的匿名内部类定义和使用:
假如说现在有一个Stu类和一个Certificate类,Stu类的内部含有一个属性male默认为male,有一个f2方法。Certificate类里的含有一个输出证书奖项名称的print方法。但是这时候学生的群体中有一个学生获得了省级一等奖,另一个学生获得了省级二等奖,而其他同学获得的都是省级三等奖。这个时候需要输出获得省级一等奖、二等奖的这两个学生的奖项,按照传统的方法,就是在分别定义两个类继承Cretificate并且覆写print()方法,这样会显得很繁琐。使用基于类的匿名内部类可以解决这个问题,如下:
class Stu { private String male = "male"; public void f1() { System.out.println("我是f1"); } public void f2() { //anonymous类的使用 new Certificate() { @Override public void print() { System.out.println("XXX省级二等奖"); // System.out.println(Stu.this.male);匿名内部类中可以直接访问外部类中的所有成员 } }.print(); //匿名类的使用 Certificate certificate = new Certificate() { @Override public void print() { System.out.println("XXX省级一等奖"); } }; certificate.print();//调用的是匿名类中覆写过的方法 } } class Certificate { public void print() { System.out.println("XXX省级三等奖"); } } public class AnonymousInnerClass { public static void main(String[] args) { Stu stu = new Stu(); stu.f2(); } } //输出: //XXX省级二等奖 //XXX省级一等奖
在上述基于类的匿名对象类的使用过程中,实际上在底层创建了一个名为Stu$2的继承自Certificate的类并且覆写了父类中的print方法:
class Stu$2 extends Certificate { @Override public void print() { System.out.println("XXX省级X等奖"); } }
在这个过程中发生的调用关系为:
成员内部类
成员内部类是指定义在外部类的成员位置山的类。本质上是一个成员,具有成员的属性,因此使用成员内部类需要注意:
- 成员内部类可以直接访问内部类的所有成员
- 成员内部类的作用域为外部类内
- 成员内部类可以添加任意一种访问修饰限定符,因为它本质上是作为一个成员存在
- 外部类实例化成员内部类有两种方法:
①先实例化外部类成员变量,再实例化成员内部类
②在外部类中提供getInstance返回成员内部类的实例class Dog { private int age = 6; private String name = "huahua"; //定义成员内部类 public class DogOfJack { int gg; public void print() { System.out.println("Jack's Dog " + Dog.this.name +" 的内部类"); } } public void f1() { new DogOfJack().print(); } public DogOfJack getInstance() { return new DogOfJack(); } } public class MemberInnerClass { public static void main(String[] args) { Dog dog = new Dog(); Dog.DogOfJack dogOfJack1 = new Dog().new DogOfJack(); dogOfJack1.print(); Dog.DogOfJack dogOfJack2 = dog.getInstance(); dogOfJack2.print(); } }
静态内部类
静态内部类与成员内部类相似,不过被static关键字修饰。但是其本质还是作为一个成员存在,使用静态内部类的注意事项:
- 在静态内部类中只能访问外部类中的静态变量,因为其本身被static关键字修饰
- 作为成员,静态内部类可以添加任意的访问修饰限定符
- 外部其它类实例化静态内部类:使用类名.属性实例化静态内部类
class Dog { private static int age = 6; private String name = "huahua"; //定义成员内部类 static public class DogOfJack { int gg; public void print() { System.out.println("Jack's Dog " + Dog.age +" 的内部类"); } } public void f1() { new DogOfJack().print(); } public DogOfJack getInstance() { return new DogOfJack(); } } public class MemberInnerClass { public static void main(String[] args) { Dog dog = new Dog(); Dog.DogOfJack dogOfJack = new Dog.DogOfJack(); dogOfJack.print(); } } //输出:Jack's Dog 6 的内部类
总结
Java中的内部类有四种,可以根据其分别定义在外部类的局部位置上、成员位置上将内部类分为局部内部内、匿名内部类和成员内部类、静态内部类,其中使用最多的为匿名内部类。
- 局部内部类:定义在外部类的方法或者代码块中,作为类存在。作用域为方法或者代码块内部。局部内部类可以直接访问外部类的所有成员,通过外部类名.this.属性的方法访问外部类中的属性。
- 匿名内部类:定义在外部类的方法或者代码块中。匿名内部类分为基于接口的匿名内部类和基于对象的匿名内部类,在使用匿名内部类时要注意继承、多态和动态绑定。匿名内部类的作用域和局部内部类相同,注意匿名内部类具有类的特征也具有对象的特征,外部类无法访问。匿名内部类可以直接访问外部类的所有属性,通过外部类名.this.属性访问。
- 成员内部类:定义在外部类的成员位置上,作为一个成员存在,作用域为外部类内。可以访问外部类中的所有属性,通过外部类名.this.属性访问。
- 静态内部类:与成员内部类类似,位于外部类的成员位置上,在类内不能使用this关键字,同时在类的内部只能通过类名.属性访问外部类中的静态变量