java基础:内部类

一、概述

类的五大成员:属性,方法,构造方法,代码块,内部类

1.引言:

当需要描述一个汽车类,属性有汽车品牌,车龄,颜色,发动机品牌,发动机使用年限

 但是发动机应该是一个独立的个体,不应该和汽车的属性定义在一起,发动机应该自成一个类

但发动机类是需要依赖汽车类才能存在的,离开了汽车单独出现就失去了实际意义 ,所以需要把发动机类定义在汽车类内部

2.定义:

在一个类里面定义的类就叫做内部类

3.访问特点 

①内部类可以直接访问外部类的成员,包括私有

②外部类要访问内部类的成员,就必须创建对象

 外部类访问carName,实际隐含了个this,this表示方法调用者的地址值。

在测试类创建Car对象后,this指向该对象,该对象中有carName成员变量,但是没有engineName这个成员变量。必须要创建Engine对象,只有Engine对象里才有engineName成员变量。

4.应用场景:

B类事物表示A类事物的一部分,且B单独存在没有意义 。

比如:汽车的发动机,ArrayList的迭代器,人的心脏

二、分类

1.成员内部类:

写在成员位置的,属于外部类的成员

(1)注意事项:

① 成员内部类和外部类的成员变量,成员方法地位一样,都可以被一些修饰符所修饰,比如private,缺省,protected,public,static等

② 在成员内部类中,JDK16以前是不能定义静态变量的,JDK16开始才可以定义静态变量

(2)获取成员内部类对象的两种方式:

①直接创建(内部类非private修饰时常用)

②外部类编写方法,对外提供内部类对象(内部类private修饰时使用)

由于内部类私有,无法直接用Inner类型接收对象,可采用以下两种办法:

(3)成员内部类获取外部类的成员变量

Question:

 解决办法:通过 Outer.this.变量名进行访问


内存分析:

首先,在加载字节码文件时,内部类和外部类的字节码文件都会加载到方法区,且不是同一个文件名。内部类的字节码文件名是 外部类名$内部类名.class

在内部类的字节码文件中,成员变量除了定义的变量 a ,还有一个隐含的 Outer类型的 this ,为了和代表方法调用者的 this 区分开,实际名字为 this$0 ,这个 this$0 指向外部类对象

 内存图:

由于就近原则,a 会首先找到方法内的局部变量a

this代表方法调用者的地址值,所以this.a 会访问到本类 Inner的成员变量a

通过 Outer.this 来访问外部类的实例时,编译器会将其转换为对应的 this$0 引用找到外部类对象,在访问其成员变量a

★ 虽然在内部类中实际存储的是指向外部类实例的引用 this$0 ,但通过 Outer.this 语法,我们可以更直观地理解这个引用指向的是外部类的实例。

2.静态内部类:

用static修饰的成员内部类称为静态内部类

(1)注意事项:

①静态内部类也是成员的内部类的一种

②静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的必须要创建对象。(静态只能访问静态)

(2)创建静态内部类对象的格式:

(3)调用静态内部类中的方法:

① 调用非静态方法:先创建对象,再用对象调用

② 外部类名.内部类名.方法名();

注意new的对象不是Outer,而是Outer.Inner 

3.局部内部类

定义在方法中的内部类,就叫做局部内部类,类似于方法中的局部变量

注意事项:

① 外界是无法使用局部内部类的,需要在方法内部创建对象并使用。

② 该类可以直接访问外部类的成员,也可以访问方法内的局部变量

③ 修饰局部变量的关键字也可以拿来修饰局部内部类(如 final等),但是其他的关键字就不行(如public,static等)

理解:跟局部变量的性质基本一样,外界无法访问方法内的局部变量,方法内也可以获取外界定义的变量

运行结果:

4.匿名内部类

隐藏了名字的内部类,可以写再成员位置,也可以写在局部位置

所谓匿名,指的是程序员不需要为这个类声明名字。

作用:更方便的创建出一个子类对象

(1)定义格式:

 包含了继承或实现,方法重写,创建对象三部分。

整体就是一个类的子类对象或接口的实现类对象匿名内部类事实上只是 { } 代表的内容

匿名内部类编译时也会产生字节码文件:

 可以看出,匿名内部类并非真的没有名字,只不过不需要自己命名而已,名字为 外部类$序号

 验证:通过反编译查看匿名内部类的详细信息

 因为整体事实上是一个匿名内部类对象,所以可以直接调用该对象的方法,或直接将该对象赋值给变量 

(2)主要使用场景:

通常作为一个参数传递给方法

当方法的参数是接口或类时,

以接口为例,可以传递这个接口的实现类对象,

如果实现类只要使用一次,就可以使用匿名内部类简化代码。

 正常做法:单独创建一个子类继承该父类,然后创建子类对象,再传递给method方法(太麻烦)

使用匿名内部类:直接将匿名内部类作为函数的实参使用,利用多态的的思想,Animal a  = 子类对象;

注意:实际应用中,匿名内部类我们不会主动去用,而是需要的时候才会去用

举个🌰🌰🌰

给一个按钮绑定单击事件监听器,需要创建一个监听器对象。

通过源码发现,监听器是一个接口。

所以,要么创建一个类实现该接口,但不同按钮的点击事件肯定不一样,即创建的实现类只能用一次,太麻烦也没必要。

要么就直接采用匿名内部类直接使用 

所以,在使用API时,而参数是一个对象,而该对象正好是个接口类型,往往就需要使用匿名内部类了。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值