一、内部类

1、引入

类的成员包括:

(1)属性:成员变量;

(2)方法:成员方法;

(3)构造器;

(4)代码块;

(5)内部类;

其中1、2是代表这类事物的特征;

其中3、4是初始化类和对象用的;

其中5是协助完成2的功能的实现,表现;

2、内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。

Inner class的名字不能与包含它的外部类类名相同;

3、分类

根据内部类的所在的位置不同:

(1)成员内部类:(static成员内部类和非static成员内部类)外部类中方法外;

(2)局部内部类:匿名内部类,方法体内(也可以在代码块内)

4、

二、成员内部类

1、概述及分类

成员内部类:定义在 类中方法外 的类。

(1)有 static 修饰的:静态成员内部类,简称 静态内部类;

(2)没有 static 修饰的:非静态成员内部类,简称 成员内部类;

思考:什么情况下会用到成员内部类(方法外声明的?)

当描述一个事物时,发现它的内部还有一个完整的结构需要用一个类来描述;

并且发现这内部的结构,如果独立存在是没有意义的,必须在这个外部类中才有意义。而且这个内部结构只为这个外部类服务。

例如:Body身体,发现它内部还有完整的结果,如:心脏,发现心脏单独创建对象没有意义,只有在 Body 对象中才有意义,而且只为 Body 对象服务。

2、成员内部类的特点

(1)成员内部类作为类的成员的角色:

① 和外部类不同,Inner class 还可以声明为 private或 protected;

② 可以调用外部类的结构;

③ Inner class 可以声明为 static的,但此时就不能再使用外层类的非static 的成员变量;

(2)成员内部类作为类的角色:

① 成员内部类,可以出现原本类中能够定义的所有的成员:

属性:可以有静态属性和非静态属性;

方法:可以有静态方法和非静态方法,如果静态内部类是抽象类,还可定义抽象方法;

构造方法:有参方法、无参方法;

代码块:可以有静态代码块和非静态代码块;

内部类:允许,但很少再写内部类(臃肿)

② 可以声明为 abstract 类,因此可以被其他的内部类继承;

③ 可以声明为 final 类;

④ 编译以后生成 OuterClass$InnerClass.class 字节码文件(也适用于局部内部类)

(3)注意

① 非 static 的成员内部类中的成员不能声明为 static 的,只有在外部类或 static 的成员内部类才可声明 static 成员;

② 外部类访问成员内部类的成员,需要“内部类.成员” 或 “内部类对象.成员” 的方式;

③ 成员内部类可以直接使用外部类的所有成员,包括私有的数据;

④ 当想要在外部类的静态成员部分使用内部类时, 可以考虑内部类声明为静态的;

2、静态成员内部类(静态内部类)

(1)语法格式

【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{

【其他修饰符】 static class 内部类 【 extends 父类】 【implements 父接口们】{

}

}

注意:

① 只有成员内部类才能用 static 修饰,其他的外部类,局部内部类等够不可以用 static 修饰。

② 外部类,内部类的父类,父接口都没有关系,各是各自的。

(2)特点

① 静态内部类中不能使用外部类的非静态的成员;

② 在外部类中,使用静态内部类,和使用其他的类一样的原则;

如果使用静态内部类的静态成员,直接“静态内部类名.”

如果使用静态内部类的非静态成员,直接“静态内部类对象名.”

③ 在外部类的外面,使用静态内部类:

如果使用静态内部类的静态成员,直接“类名”;

使用 外部类名.静态内部类名.静态方法;

使用 import 包.外部类名.静态内部类名; 然后在代码中使用“静态内部类名”

④ 静态内部类不会随着外部类的初始化一起初始化,而是要在使用到这个静态内部类才会初始化;

(3)小节

① 同级的来说静态的不能直接使用非静态的;

② 访问一个类的静态成员,用 “类名.” 即可;访问一个类的非静态成员,用“对象名.”即可;

③ 当使用到这个类时,这个类才会进行初始化;

(4)Demo:

1 packagecom.java.test;2 //import com.java.test.Outer.Inner;
3
4 public classTestStaticInner {5 public static voidmain(String[] args) {6 //访问内部类的静态方法
7 //Inner.test();//上面有导包语句,import 包.外部类名.静态内部类名;
8
9 Outer.Inner.test();//外部类名.静态内部类名.静态方法(不使用导包语句)
10
11 //访问内部类的非静态方法
12 //Inner in = new Inner();//上面有导包语句,import 包.外部类名.静态内部类名;
13 Outer.Inner in = newOuter.Inner();14in.method();15
16 Outer out = new Outer(); //内部类不会初始化
17 out.outMethod(); //该方法中用到内部类,内部类会进行初始化
18}19}20 classOuter{21 private int i = 1;22 private static int j = 2;23
24 static{25 System.out.println("外部类的静态代码块");26}27
28 static classInner{29 static{30 System.out.println("静态内部类的代码块");31}32
33 public voidmethod(){34 System.out.println("静态内部类的非静态方法");35 //System.out.println(i);//错误,不能访问非静态的
36System.out.println(j);37}38
39 public static voidtest(){40 System.out.println("静态内部类的静态方法");41}42}43 //外部类访问内部类
44 public voidoutMethod(){45 Inner in = newInner();46 in.method();//非静态方法,用对象名.访问
47
48 Inner.test();//静态方法,用类名.访问
49}50 }

(5)

3、成员内部类(非静态内部类)

(1)语法格式

【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{

【其他修饰符】 class 内部类 【 extends 父类】 【implements 父接口们】{

}

}

(2)特点

① 在非静态内部类中,不能出现任何和 static 有关的声明;

② 在非静态内部类中,可以随意访问外部类的所有的成员,包括静态的和非静态的;

③ 在外部类的静态成员中,不能使用非静态的成员内部类;

④ 在外部类的外面使用

第一步:先创建外部类的对象

第二步:要么通过外部类的对象,去创建内部类的对象

Outer out = new Outer();
Outer.Inner in = out.new Inner();

要么通过外部类的对象,去获取内部类的对象

Outer out = new Outer();
Outer.Inner in = out.getInner();

(3)Demo

1 public classTestNonStaticInner {2 public static voidmain(String[] args) {3 //Outer.Inner in = new Outer.Inner();//错误的
4
5 //在这里使用Inner,因为此时的Inner是Outer的非静态成员,所以需要用到Outer的对象
6 Outer out = newOuter();7 //Outer.Inner in = out.new Inner();
8 Outer.Inner in =out.getInner();9in.method();10}11}12 classOuter{13 private int i = 1;14 private static int j = 2;15
16 classInner{17 public voidmethod(){18 System.out.println("非静态内部类的非静态方法");19System.out.println(i);20System.out.println(j);21}22}23
24 public static voidoutTest(){25 //Inner in = new Inner();//静态的方法不能访问非静态的成员
26}27 public voidoutMethod(){28 Inner in = newInner();29in.method();30}31
32 publicInner getInner(){33 return newInner();34}35 }

(4)练习案例:创建一个外部类,里面有一个抽象内部类,并有一个抽象方法,要求写一个类去继承该内部类;

代码:

1 public classTest {2 public static voidmain(String[] args) {3 MySub my = new MySub(newOuter());4my.test();5}6}7 classOuter{8 abstract classInner{9 public abstract voidtest();10}11}12 class MySub extendsOuter.Inner{13MySub(Outer out){14 out.super();//需要外部类的对象,才能调用非静态内部类的构造器
15}16
17@Override18 public voidtest() {19 System.out.println("hello");20}21 }

(5)小节

① 使用非静态内部类名时,可以使用:import 包.外部类名.内部类名; 或 外部类名.内部类名;

② 要调用非静态内部类的构造器,需要用到外部类的对象;

③ 因为子类的构造器的首行一定要调用父类的构造器,默认调用父类的无参构造(需要借助外部类对象);

④ 继承抽象内部类,要重写抽象类的抽象方法;

注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。如:Person$Heart.class

三、局部内部类

1、概述

局部内部类:定义在类中方法中(方法内、代码块内、构造器内)的类,就是局部内部类。

即只有当前所属的方法才能使用它,出了这个方法外面就不能使用了。

2、分类

(1)有名字的局部内部类:简称局部内部类;

(2)没名字的局部内部类:简称 匿名内部类。

3、语法格式

局部内部类定义格式:

【修饰符】 class 外部类名称 {

【修饰符】 返回值类型 外部类方法名称(参数列表) {

【修饰符】 class 有名字的局部内部类名称 {

// ...

}

}

}

说明:如果子类调用的是父类的无参构造,那么()中实参列表不用写;

如果子类调用的是父类的有参构造,那么()中传入实参列表;

定义一个类的时候,权限修饰符规则:

(1)外部类:public / default

(2)成员内部类:public / protected / default / private

(3)局部内部类:不加任何修饰符

4、特点

(1)局部内部类的修饰符,只能有 abstract 或 final;

(2)局部内部类有使用的作用域;

(3)如果局部内部类在静态方法中,不能使用外部类的非静态成员;

(4)在局部内部类中,可以使用当前局部内部类所在方法的局部变量,但是要求,这个局部变量必须是 final的常量。

5、扩展

在Java8时,如果某个局部变量被局部内部类使用了,会自动添加final变为常量,一旦变为常量,它的值就不能修改了。

为什么它要这么要求?给局部变量加 final呢?

避免局部内部类对象被返回到外部类的外面使用时,访问不到这个局部变量,所以要把这个局部变量变为final的常量。(常量在方法区中)

原因:

① new出来的对象在堆内存当中。

② 局部变量是跟着方法走的,在栈内存当中。

③ 方法运行结束之后,立刻出栈,局部变量就会立刻消失。

④ 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

Demo:

1 public classTestLocalInner {2 public static voidmain(String[] args) {3 Outer out = newOuter();4 Father in = out.test();//在外部类的外面虽然不能使用局部内部类,但是可以得到它的对象
5System.out.println(in.getClass());6 in.method();//在这里仍然可以访问到这个a,那么这个a就不能存在栈中,得挪到方法区,变为常量
7}8}9 abstract classFather{10 public abstract voidmethod();11}12 classOuter{13 private int i = 1;//成员变量,实例变量,非静态成员变量
14 private static int j = 2;//成员变量,类变量,静态变量
15
16 publicFather test(){17 //Inner in = new Inner(); 现在还没有声明该类,不能使用
18
19 final int a = 10;//局部变量==>局部的常量
20
21 //局部内部类
22 class Inner extendsFather{23 public voidmethod(){24System.out.println(i);25System.out.println(j);26System.out.println(a);27}28}29
30 Inner in = newInner();31in.method();32
33 returnin;34}35
36 public voidmethod(){37 //Inner in = new Inner(); 超过了作用域
38}39
40 public static voidfun(){41 //局部内部类
42 classInner{43 public voidmethod(){44 //System.out.println(i);//是因为fun方法是静态的
45System.out.println(j);46}47}48}49 }

6、

四、匿名内部类【重要】

1、概述

匿名内部类:是内部类的简化写法。它的本质是一个 带具体实现的  父类或者父接口的 匿名的 子类对象。

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在 new 的后面,用其隐含实现一个接口或实现一个类。

开发中,最常用到的内部类就是匿名内部类了,以接口为例,当你使用一个接口时,都得做如下几步操作:

(1)定义子类

(2)重写接口中的方法

(3)创建子类对象

(4)调用重写后的方法

而匿名类就可以把以上四步合成一步。

前提:匿名内部类必须继承一个父类或者实现一个父接口

2、语法格式

定义格式:

new 父类名或者接口名(){

// 方法重写
@Override
public void method() {
// 执行语句
}
};

特殊:声明匿名内部类与创建它的对象是一起完成的,即匿名内部类只有唯一的对象。

匿名内部类的特点:

(1)匿名内部类必须继承父类或实现接口;

(2)匿名内部类只能有一个对象;

(3)匿名内部类对象只能使用多态形式引用;

3、使用案例

以接口为例,匿名内部类的使用。

定义接口:

1 public abstract class FlyAble{
2 public abstract void fly();
3 }

创建匿名内部类,并调用:

1 public class InnerDemo {
2 public static void main(String[] args) {
3 /*
4 1.等号右边:是匿名内部类,定义并创建该接口的子类对象
5 2.等号左边:是多态赋值,接口类型引用指向子类对象
6 */
7 FlyAble f = new FlyAble(){
8   public void fly() {
9   System.out.println("我飞了~~~");
10   }
11 };
12 //调用 fly方法,执行重写后的方法
13 f.fly();
14 }
15 }

通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。如下所示

1 public class InnerDemo2 {
2 public static void main(String[] args) {
3 /*
4 1.等号右边:定义并创建该接口的子类对象
5 2.等号左边:是多态,接口类型引用指向子类对象
6 */
7 FlyAble f = new FlyAble(){
8   public void fly() {
9   System.out.println("我飞了~~~");
10   }
11 };
12 // 将f传递给showFly方法中
13 showFly(f);
14 }
15 public static void showFly(FlyAble f) {
16 f.fly();
17 }
18 }

将以上两步,也可以简化为一步:

1 public class InnerDemo3 {
2 public static void main(String[] args) {
3 /*
4 创建匿名内部类,直接传递给showFly(FlyAble f)
5 */
6 showFly( new FlyAble(){
7 public void fly() {
8 System.out.println("我飞了~~~");
9 }
10 });
11 }
12 public static void showFly(FlyAble f) {
13 f.fly();
14 }
15 }

4、

五、