一、基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class)嵌套其他类的类称为外部类(outer class)。它是我们类的第五大成员,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
二、内部类的分类
2.1 定义在外部类局部位置上(比如方法内):
①局部内部类(有类名)
②匿名内部类(没有类名,重点)
2.2 定义在外部类的成员位置上:
①成员内部类(没用static修饰)
②静态内部类(使用static修饰)
三、内部类的使用细节
3.1 局部内部类
1.局部内部类是定义在外部类的局部位置,比如方法中或者代码块中,并且有类名。
class OuterClass {
private int n1 = 10;
public void m1() {
class InnerClass {
//内部类
}
}
}
2.可以直接访问外部类的所有成员,包含外部类私有的成员
class OuterClass {
private int n1 = 10;
public void m1() {
class InnerClass {
//内部类
public void f1(){
System.out.println("n1="+n1);
}
}
}
}
3.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final 修饰(这样就不可被继承),因为局部变量也可以使用final
class OuterClass {
private int n1 = 10;
public void m1() {
class InnerClass {
//内部类
public void f1(){
System.out.println("n1="+n1);
}
}
class InnerClass1 extends InnerClass{
//继承上面的内部类
}
}
}
4.作用域:仅仅在定义它的方法或代码块中
5.局部内部类可以访问外部类的成员
6.外部类访问局部内部类的成员,访问方式: 创建对象,再访问(注意:必须在作用域[所定义内部类的方法中]内)
7.如果外部类和局部内部类的成员重名,那么调用遵循就近原则(即先找局部内部类的成员),如果想访问外部类的成员变量,则需使用
外部类名.this.成员
//很好理解,我们可以解读为,外部类的对象的成员
3.2 匿名内部类
匿名内部类是定义在外部类的局部位置,比如方法中或者代码块中,并且没有类名;
匿名内部类的使用是为了简化开发
①基于接口的匿名内部类
假如:我们有一个需求,“使用IA接口,并创建对象使用其方法”
按照传统方法:我们需要①编写实现类实现接口的方法②new实现类的实例
public class AnonymousInnerClass {
public static void main(String[] args) {
Animal animal = new Animal();
animal.eat();
}
}
interface IA {
void eat();
}
class Animal implements IA {
@Override
public void eat() {
System.out.println("动物吃饭");
}
}
现在,有了匿名内部类,我们就可以这样实现:
public class AnonymousInnerClass {
public static void main(String[] args) {
IA animal = new IA() {
@Override
public void eat() {
System.out.println("动物吃饭");
}
};
animal.eat();
}
}
interface IA {
void eat();
}
说是匿名,其实是有名字的,在底层,系统给它起了名字,底层实现类似(大概模拟)这样:
class XXXX implements IA { @Override public void eat() { System.out.println("动物吃饭"); } }
类名是由系统起的,我们可以通过.getClass( )方法获取其运行时类型,编译类型当然是IA这不用说。
注意:匿名内部类只能使用一次,也就是只能new一个实例对象,这不代表其引用就能用一次。匿名内部类需要把接口的抽象方法都实现。
②基于普通类的匿名内部类
public class AnonymousInnerClass {
public static void main(String[] args) {
Animal animal = new Animal("老虎") {
@Override
void eat() {
System.out.println(this.name+"要吃肉");
}
};
System.out.println(animal.getClass());
animal.eat();
}
}
class Animal {
String name;
void eat() {
System.out.println(this.name + "要吃饭");
}
public Animal(String name) {
this.name = name;
}
}
底层实现(大概模拟)
class XXXX extends Animal{
@Override void eat() { System.out.println(this.name+"要吃肉"); } }
我们可以通过.getClass( )方法获取其运行时类型
③基于抽象类的匿名内部类
public class AnonymousInnerClass {
public static void main(String[] args) {
Animal animal = new Animal("老虎") {
@Override
void eat() {
System.out.println(this.name + "吃肉");
}
};
System.out.println(animal.getClass());
animal.eat();
}
}
abstract class Animal {
String name;
abstract void eat();
void shout() {
System.out.println("动物大叫");
}
public Animal(String name) {
this.name = name;
}
}
必须把抽象类中的抽象方法都实现。
这里补充说明一下,父类的私有属性可以被子类继承,但是子类无法使用;父类的构造方法,子类无法继承,但是子类创建对象会先调用父类的构造方法(默认是无参构造方法),以上案例匿名内部类应该是调用了父类的有参构造方法。
3.3 成员内部类
成员内部类定义在外部类的成员位置,并且没有static修饰。
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位是一个成员。
1.成员内部类可访问外部类所有成员,包括私有的
使用内部类方法一
public class ChengYuan {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.shiYong();
}
}
class OuterClass {
int n = 10;
public void eat() {
System.out.println("吃顿饭");
}
class innerClass {
public void f1() {
System.out.println(n);
eat();
}
}
public void shiYong() {
innerClass innerClass = new innerClass();
innerClass.f1();
}
}
使用内部类方法二
OuterClass.innerClass innerClass = outerClass.new innerClass();
innerClass.f1();
使用内部类方法三
外部类写方法,返回内部类对象
public InnerClass shiYong1(){
return new InnerClass();
}
3.4 静态内部类
静态内部类是定义在外部类的成员位置,并且有static修饰
可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
作用域:同其他的成员,为整个类体
静态内部类可通过类名直接访问
public class ChengYuan {
public static void main(String[] args) {
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.f1();
}
}
class OuterClass {
static int n = 10;
static class InnerClass {
public void f1() {
System.out.println(n);
}
}
}
如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。