类的成员包含 java_Java 面向对象(十五)类的成员 之 内部类

一、内部类

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、

五、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值