文章目录
为什么要学习内部类,这是有原因的,因为内部类
是类的五大成员之一。
类的五大成员:属性(就是我们之前讲的name、age这样的成员变量)、方法(就是我们之前所说的行为,例如 eat()
方法等等)、构造方法(在创建对象的时候使用到的)、代码块、内部类。
一、什么是内部类?
内部类(Inner Class):在一个类的里面,再定义一个其他的类,这个其他的类就称之为内部类
。
例如:在A类的内部定义B类,B类就被称为内部类。
例如下图代码中,Outer
就被称之为 外部类
,Inner
被称之为 内部类
。与此同时,跟这两个类都无关的所有的类,我们都统称为:外部其他类。
所以说在以后,我们就可以在 外部其他类
中去创建 内部类
的对象,并调用它的方法。
二、为什么要学习内部类?
需求:写一个Javabean类描述汽车。
属性:汽车的品牌,车龄,颜色,发动机(engine)的品牌,使用年限。
有的同学肯定觉得想都不用想,直接定义一个 Car类
就行了,并在成员位置定义题目中的属性即可。
这种写法乍看上去没有什么问题,但是细品,这个里面的发动机,它是一个独立的个体,跟车本身其实还是有点区别的。
所以说,跟发动机相关的属性,我们不应该跟车定义在一起,那该怎么办呢?
有的同学又会想:那这个简单,我再定义一个发动机的类(Engine)不就ok了吗?
是的,你说的没错,但是还不够,因为发动机是需要依赖车才能存在的,发动机如果单独出现,它其实是没有什么实际意义的。
因此最好的解决方案就是把发动机 Engine类
定义在 Car类
的里面,这样就满足要求了。
这样就表示:车里面有发动机,而发动机又是一个独立的个体。
此时,Car
就是 外部类
,Engine
就是 内部类
。
三、内部类的使用
我们以后在定义内部类的时候,一般会遵守这样的规则:
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有什么实际的意义
内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 但是外部类不能直接访问内部类的成员。如果外部类要访问内部类的成员,必须要创建对象
四、代码示例
1)需求
写一个Javabean类描述汽车。
属性:汽车的品牌,车龄,颜色,发动机的品牌,使用年限。
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
2)代码示例
技巧:右击 Test.java
,然后点击 Split and Move Right
,将它移动到右边去。
一左一右方便观看。
以下的代码可以按注释的顺序观看。
Test.java
package com.itheima.a01innerclassdemo1;
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.carName = "宾利";
c.carAge = 1;
c.carColor = "粉色";
c.show();
}
}
Car.java
package com.itheima.a01innerclassdemo1;
public class Car {
privateString carName;
int carAge;
String carColor;
// 5.这是因为,在外部其他类中创建Car对象,然后调用show()方法时,方法里面其实是有一个隐藏的 this,这个this在当前方法被调用的时候,虚拟机会把调用者的地址值赋值给这里的this。
public void show(Car this){
// 3.在外部类的 show() 方法中,如果你获取的是外部类的成员,一点问题都没有
// 6.因此,如果我们在方法中直接调用成员变量的话,前面也有一个隐含的this。
// 7.此时虚拟机就知道了,这里的this.carName是打印调用者车的名字:宾利
System.out.println(this.carName);
// 4.但是,如果你想要获取内部类的,它就会直接报错
// 8.这个时候就需要思考:在代码中,有没有发动机的对象呢?是没有的
// 既然没有发动机的对象,这里的engineName就不知道打印什么了,导致代码报错
System.out.println(e.engineName);
// 因此,如果外部类要访问内部类的成员,必须要创建内部类的对象,然后再使用这个对象去调用engineName
Engine e = new Engine();
System.out.println(e.engineName);
}
class Engine{
String engineName;
int engineAge;
public void show(){
// 1.使用本类中的变量是没有问题的
System.out.println(engineName);
// 2.内部类使用外部类的成员变量也是没有问题的
// 尽管外部类的成员变量是用private修饰的,也是可以正常调用的
System.out.println(carName);
}
}
}
五、内部类该如何使用呢?
1)IDEA图标
接下来带大家看一段Java的源代码,就知道如何使用了。
ctrl + N,搜索 java.util
中的 ArrayList
类。
此时它就会跳转到 ArrayList
的源码。
然后按快捷键 ctrl + F12,这是就会出现 ArrayList
中所有的成员消息的列表。
下面 C
是蓝色的,是 class
的首字母,这就表示后面的 ArrayList
是类名
像这种,前面是粉红色的,里面是 m
的图标(method),表示这个是方法。例如 add()
方法。
有构造方法也有成员方法。如果方法名跟类名一样,就表示是构造方法,而方法名跟类名不一样,那就是普通的成员方法。
细节1
细节2:有的方法在后面,有这种灰色的字体,字体的前面有这种向上的小箭头,或者是向右的小箭头。
向上的箭头:表示重写父类 / 接口中的方法,在 ↑
的后面就标记了父类 / 接口的名称。
因此这个整体就可以这么去理解:add
方法是重写了父类 AbstractList
中的方法。
还有的方法本身就是灰色的:如果我们用鼠标点击这个灰色的方法,它就会跳转到 AbstractList
接口中,相当于这个方法是继承了 AbstractList
里面的方法
因此 ↑
表示这个方法是重写的;→
表示这个方法来自于哪个类 / 接口
如果说,它前面的图标是这种黄色的,里面是 f
的图标,表示的是属性(成员变量 / 常量),f
指的是 field
。
点进来就是一个属性(成员变量)。
绿色的 I
表示 interface
,接口名叫 Entry
,这个接口来自于 Map
集合中
再往下,它前面的图标是这种蓝色的,里面是 c
的图标,这个就表示它是 ArrayList
的内部类。
可以发现,在 ArrayList
中有很多的内部类。
现在要给大家介绍的是第二个:Itr
。
2)Itr
内部类
首先我们要知道,ArrayList
是一个集合,集合的作用就是帮助我们来存储元素的,我们可以把数据往集合里面放,并且可以通过遍历的方式,将数据从集合中获取出来。
但是 ArrayList
的遍历方式有很多很多种,在之前,我们仅仅是用最简单的for循环
进行了遍历。
除了for循环
的遍历方式外,还有很多的遍历方式。
这里我们看到的 Itr(iteration)
,专业叫做 迭代器
,这个就是一种相对比较高级的遍历方式。
这种遍历方式,相对集合来讲,是相对独立的,但是它又属于集合,所以Java就把 迭代器 这个类,设计成了 ArrayList
的内部类。
你要来想,如果集合都没有,那我们遍历个毛啊,它不能单独出现,一定要有集合,才能有迭代器。
这个就是 内部类
在实际中的应用。
六、总结
1、什么是内部类?
写在一个类里面的类就叫做内部类。
2、什么时候用到内部类?
如果B类表示的事物是A类的一部分,而且B类单独存在没有意义,这个时候我们就可以把B类设计成A类的内部类。
比如:汽车的发动机,ArrayList的迭代器,人的心脏等等。这里的发动机、迭代器、心脏,都是属于其他类的一部分,并且它们自己单独存在是没有意义的,所以我们可以把它们设计成内部类。