文章目录
内部类
如果定义类在局部位置(方法中/代码块中): (1) 局部内部类 (2) 匿名内部类
定义在成员位置:(1)成员内部类 (2)静态内部类
1 基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
类的5大成员:属性、方法、构造器、代码块、内部类。
内部类的最大特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
基本语法:
class Outer {//外部类
class Inner {//内部类
}
}
class Other {//外部其他类
}
2 内部类的分类
-
定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名, 非常重要)
-
定义在外部类的成员位置上:
- 成员内部类(没用 static 修饰)
- 静态内部类(使用 static 修饰)
2.1 局部内部类
局部内部类是定义在外部类的局部位置,比如方法中/代码块中,并且有类名。
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的,但是可以使用 final 修饰,因为局部变量也可以使用 final
- 作用域:仅仅在定义它的方法或代码块中。
- 局部内部类 访问 外部类的成员 [访问方式:直接访问即可]
- 外部类 访问 局部内部类的成员 [访问方式:创建对象,再通过对象访问(必须在作用域内)]
- 外部其它类,不能访问局部内部类,因为局部内部类的地位是一个局部变量。
- 如果外部类和局部内部类的成员重名是,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
总结:
- 局部内部类定义在方法中/代码块中
- 作用域在方法体或者代码块中
- 本质上仍然是一个类。
public class Outer {//外部类
private int n1 = 100;
private void m2() {
System.out.println("我是Outer的私有方法m2()");
}
public void m1() {
class Inner {//局部内部类
private n1 = 80;
public void f1() {
System.out.println(n1);//访问内部类的n1
System.out.println(Outer.this.n1);//访问外部类的n1
m2();//内部类没有此方法,自己调用
}
}
Inner inner = new Inner();
inner.f1();//创建对象,访问内部类成员
}
}
2.2 匿名内部类(重要!!!)
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
匿名内部类的基本语法:
new 类或接口(参数列表) {
类体
};
2.2.1 基于接口的匿名内部类
案例演示:
public class AnonymousTest {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}
interface IA {
void cry();
}
class Outer {
private int n1 = 10;
public void method() {
IA tiger = new IA() {//该匿名类只使用一次,后面不再使用
@Override
public void cry() {
System.out.println("匿名类实现了接口的cry()方法");
}
};
// tiger 的编译类型 ?IA
// tiger 的运行类型 ? 就是匿名内部类 Outer$1
/*
底层会自动分配给匿名类类名 Outer$1,
如有多个则按顺序接下去,如 Outer$2,Outer$3,...
class Outer$1 implements IA {
@Override
public void cry() {
System.out.println("匿名类实现了接口的cry()方法");
}
}
jdk 底层在创建匿名内部类 Outer$1,
立即马上就创建了 Outer$1实例,并且把地址返回给 tiger
*/
System.out.println("tiger的运行类型:" + tiger.getClass());
tiger.cry();
}
}
运行结果为:
2.2.2 基于类的匿名内部类
案例演示:
public class AnonymousTest {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}
class Father {
private String name;
public Father(String name) {
this.name = name;
}
public void test() {
System.out.println("我是Father类的test()方法");
}
}
class Outer {
private int n1 = 10;
public void method() {
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("匿名内部类重写了test()方法");
}
};
//1. father 编译类型 Father
//2. father 运行类型 Outer$1
//3. 底层会创建匿名内部类
/*
class Outer$1 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了 test 方法");
}
*/
System.out.println("father的运行类型:" + father.getClass());
father.test();
}
}
运行结果为:
2.2.3 基于抽象类的匿名内部类
案例演示:
public class AnonymousTest {
public static void main(String[] args) {
Outer outer = new Outer();
outer.method();
}
}
abstract class Animal {
public void eat() {
System.out.println("我是Animal类的eat()方法");
}
}
class Outer {
private int n1 = 10;
public void method() {
Animal animal = new Animal() {
@Override
public void eat() {
System.out.println("匿名内部类重写了eat()方法");
}
};
animal.eat();
}
}
自己思考,输出内容是什么呢?
2.2.4 匿名内部类使用细节
- 匿名内部类的语法比较奇特,需要注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上来看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法。
class Outer {
private int n1 = 10;
public void method() {
Animal animal = new Animal() {
@Override
public void eat() {
System.out.println("匿名内部类重写了eat()方法");
}
}.eat();//这样调用,eat()方法,体现了对象的特征
}
}
- 可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为它的地位是一个局部变量。
- 作用域:仅仅在定义它的方法或代码块中。
- 匿名内部类 访问 外部类成员(直接访问)。
- 外部其他类 不能访问 匿名内部类,因为匿名内部类地位是一个局部变量。
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问。
2.2.5 匿名内部类的使用实践
当做实参直接传递:
public class InnerExercise {
public static void main(String[] args) {
f1(new IL() {
@Override
public void show() {
System.out.println("我是匿名内部类的show()方法");
}
});
}
public static void f1(IL il) {
il.show();
}
}
interface IL {
void show();
}
2.3 成员内部类
成员内部类是定义在外部类的成员位置,并且没有 static 修饰。
- 可以直接访问外部类的所有成员,包含私有的。
- 可以添加任意访问修饰符(public , protected , 默认, private), 因为它的地位就是一个成员。
- 作用域:和外部类的其他成员一样,为整个类体。
- 成员内部类 访问 外部类成员(直接访问)。
- 外部类 访问 成员内部类(创建对象再访问)。
- 外部其他类 访问 成员内部类。看下面示例:
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
//第一种方式
//outer.new Inner();
//相当于把 new Inner()当做是 outer 成员
Outer.Inner inner1 = outer.new Inner();
inner1.say();
//第二种方式,在外部类中,编写一个方法,可以返回 Inner对象
Outer.Inner inner2 = outer.getInner();
inner2.say();
}
}
class Outer {
public class Inner {
public void say() {
System.out.println("我是内部成员类的say()方法");
}
}
public Inner getInner() {//返回内部成员类对象
return new Inner();
}
}
2.4 静态内部类
静态内部类是定义在外部类的成员位置,并且有 static 修饰。
- 可以直接访问外部类的 所有静态成员 ,包含私有的,但不能直接访问 非静态成员 。
- 可以添加任意访问修饰符(public , protected, 默认, private),因为它的地位就是一个成员。
- 作用域:同其他成员,为整个类体。
- 静态内部类 访问 外部类 (静态属性或静态方法),直接访问。
- 外部类 访问 静态内部类(创建对象,再访问)。
- 外部其他类 访问 静态内部类。看下面示例:
- 如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。
public class Test {
public static void main(String[] args) {
//第一种方式
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer.StaticInner stInner1 = new Outer.StaticInner();
stInner1.say();
//第二种方式
//编写一个方法,可以返回静态内部类的对象实例
Outer outer = new Outer();
Outer.StaticInner stInner2 = outer.getstInner();
stInner2.say();
}
}
class Outer {
public static class StaticInner {
public void say() {
System.out.println("我是静态内部类的say()方法");
}
}
public StaticInner getstInner() {
return new StaticInner();
}
}