一.内部类
1.分类:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
2.内部类的概念
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
3.内部类的特点(何时用)
比如汽车的发动机,ArrayList的迭代器,人的心脏等。
- 内部类表示的事物是外部类的一部分
- 内部类单独出现没有任何意义
4.内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
例如:
public class Car {
private String carName;
int carAge;
String carColor;
public void show(Car){//调用时都会隐含this用以指代调用者,这里完整时"Car this"
//是打印调用者车的名字:宾利
System.out.println(this.carName);
Engine e = new Engine();
System.out.println(e.engineName);
//System.out.println(engineName);因此如果像这样直接调用内部类,内部类不知道调用的对象
}
class Engine{
String engineName;
int engineAge;
public void show(){
System.out.println(engineName);
System.out.println(carName);
}
}
}
二.成员内部类的书写
1.注意点:
- 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
- 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
- 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。
2.获取内部类对象的两种方式
**方式一:**外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
**例如:**调用Outer
的内部类Inner
Outer.Inner oi = new Outer().new Inner();
演示:
方式一:
public class Test {
public static void main(String[] args) {
// 宿主:外部类对象。
// Outer out = new Outer();
// 创建内部类对象。
Outer.Inner oi = new Outer().new Inner();
oi.method();//内部类中的方法被调用了
}
}
class Outer {
// 成员内部类,属于外部类对象的。
// 拓展:成员内部类不能定义静态成员。
public class Inner{
// 这里面的东西与类是完全一样的。
public void method(){
System.out.println("内部类中的方法被调用了");
}
}
}
**方式二:**在外部类中定义一个方法提供内部类的对象
**使用场景:**内部类被private修饰,外界无法直接获取内部类的对象的时候
案例演示
方式二:
public class Outer {
String name;
private class Inner{
static int a = 10;
}
public Inner getInstance(){
return new Inner();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
System.out.println(o.getInstance());//打印的是地址值
}
}
三.外部类成员变量和内部类成员变量重名时的访问
注意:内部类访问外部类对象的格式是:外部类名.this
访问方法:System.out.println(Outer.this.变量名)
public class Test {
public static void main(String[] args) {
Outer.inner oi = new Outer().new inner();
oi.method();
}
}
class Outer { // 外部类
private int a = 30;
// 在成员位置定义一个类
class inner {
private int a = 20;
public void method() {
int a = 10;
System.out.println(???); // 10 答案:a
System.out.println(???); // 20 答案:this.a
System.out.println(???); // 30 答案:Outer.this.a
}
}
}
1.成员内部类内存图
- 内部类和外部类的字节码文件是分开的,分别是两个
.class
。 -
Outer.inner oi = new Outer().new inner();
new创建对象的顺序是:先创建外部类对象,然后再创建内部类对象。 - 其中,内部类对象里面有个隐藏的
Outer.this
,用来记录外部类的地址值001
,最终赋值的是002
内部类的地址值 - 当使用
Outer.this.a
调用对象时,就等于时外部类地址值.a
直接调用外部类的a了
四.静态内部类
注意事项:
1.静态内部类也是成员内部类中得到一种
2.静态内部只能访问外部类的静态成员和静态方法,如果要访问需要创建外部类的对象。
3.静态内部类中没有隐藏的Outer.this
。
静态内部类的创建:
public class Outer {
static class Inner {//这个就是静态内部类了
.......
}
}
创建静态内部类对象的创建格式:
外部类名.内部类 变量 = new 外部类.内部类名;
例子:OUter.Inner oi = new Outer.Inner();
调用静态内部类内方法的格式:
-
调用其中的非静态方法的格式:先创建对象,用对象调用
Outer.Inner oi = new Outer.Inner();//赋予的地址值是Inner的。 oi.show1();
-
调用其中的静态方法的格式:外部类名.内部类名.方法名();(一路点到尾)
外部类.内部类.静态方法名();
例如:
Outer.Inner.show2();
案例演示:
// 外部类:Outer0
public class Outer {
//int a = 10;
//static int b = 20;
//静态内部类
static class Inner {
public void show1(){
System.out.println("非静态的方法被调用了");
}
public static void show2(){
System.out.println("静态的方法被调用了");
}
}
}
//测试类Test
public class Test {
public static void main(String[] args) {
Outer.Inner oi = new Outer.Inner();
oi.show1();
//静态方法
Outer.Inner.show2();
}
}
五.局部内部类
1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
2.外界是无法直接使用局部内部类的,需要在其所在方法内部创建对象并使用。
3.该类可以直接访问外部类的成员方法,也可以访问方法内的局部变量。
1.定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
public void show(){
class 内部类 {
// 成员变量
// 成员方法
}
}
}
}
2.调用:
直接创建对象调用其方法即可
类名 对象名 = new 类名();
对象名.方法名
六.匿名内部类
1.定义
- 隐藏了名字的内部类(并不是真的没有名字,而是由java自动取,名字大概是
成员内部类$序号
) - 可以写在成员位置(方法外面的写法)
- 也可以写在局部内部类(方法里面的写法)
2.细节
包含了继承或实现,方法重写,创建对象
- 内部重写的方法是对类的继承或对接口的实现
- 匿名类指的:不是这个整体,而是两个花括号之间的东西
- 匿名内部类整体就是一个类的子类对象,或者接口的实现类对象(整体就是个对象,类名/接口名就是对象名)
-
简单来说,这个整体就是对象
-
因此还能这么玩,直接将末尾的
;
换成.
直接调用new 类名/接口名() { 重写方法; }.方法名
-
3.匿名内部类前提和格式
匿名内部类必须继承一个父类或者实现一个父接口。
匿名内部类格式
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
4.匿名内部类的使用场景
1.通常在方法的形式参数是接口或者抽象类时,可以将匿名内部类作为参数传递。
代码如下:
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 普通方式传入对象
// 创建实现类对象
Student s = new Student();
goSwimming(s);
// 匿名内部类使用场景:作为方法参数传递
Swim s3 = new Swim() {
@Override
public void swimming() {
System.out.println("蝶泳...");
}
};
// 传入匿名内部类
goSwimming(s3);
// 完美方案: 一步到位
goSwimming(new Swim() {
public void swimming() {
System.out.println("大学生, 蛙泳...");
}
});
goSwimming(new Swim() {
public void swimming() {
System.out.println("小学生, 自由泳...");
}
});
}
// 定义一个方法,模拟请一些人去游泳
public static void goSwimming(Swim s) {
s.swimming();
}
}