内部类
什么是内部类
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
什么时候使用内部类
一个事务内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
- 人里面有一颗心脏
- 汽车内部有一个发动机
- 为了实现更好的封装性
内部类的分类
按定义的位置来分
成员内部类
类定义在成员位置。类中方法外称为成员位置,无static修饰的内部类
特点
- 无static修饰的内部类,属于外部类对象的
- 宿主:外部类对象
内部类的使用格式
获取成员内部类对象的方式
方式1:外部直接创建成员内部类的对象
1
| 外部类.内部类 变量 = new 外部类().new 内部类();
|
方式2:在外部类中定义一个方法提供内部类的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 方式一: public class Test { public static void main(String[] args) { Outer.Inner oi = new Outer().new Inner(); oi.method(); } }
class Outer { public class Inner{ public void method(){ System.out.println("内部类中的方法被调用了"); } } }
方式二: 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());
} }
|
成员内部类的细节
编写成员内部类的注意点:
- 成员内部类可以被一些修饰符修饰,比如:private、默认、protectd、public、static等
- 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量
- 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值
详解:
- 内部类被private修饰,外界无法直接获取内部类的对象,只能通过在外部类中定义一个方法提供内部类的对象获取。
- 被其他权限修饰符修饰的内部类一般使用直接使用 外部类.内部类 变量 = new 外部类().new 内部类();
- 内部类被static修饰是成员内部类中的特殊情况,也叫做静态内部类
- 内部类如果想要访问外部类的成员变量,外部类的变量必须使用Final修饰,JDK8以前必须手动写final,JDK1.8之后不需要手写,JDK默认加上
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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(???); System.out.println(???); System.out.println(???); } } }
|
成员内部类内存图

执行步骤:
方法区加载Test.class,JVM自动加载main方法到栈内存
创建一个Outer.Inner oi变量
new Outer().new Inner(),在堆内存中创建对应的内存空间
将内部类对象的地址值赋值给变量oi,oi执行show方法
将show方法加载到栈内存中,show方法的调用者为内部对象
先打印方法中的局部变量a为30
在打印this.a,就是内部类中的成员变量a为20
Outer.this.a,就会从内部类中找到Outer.this,发现这个执行外部类对象的地址,找到外部类对象,并打印该外部类对象的成员变量a,值为10
执行完方法,出栈、堆内存地址不被引用,到时候被垃圾回收机制回收。
静态内部类
类定义在了成员位置。类中方法外称为成员位置,有static修饰的内部类
静态内部类特点
- 静态类内部类是一种特殊的成员内部类
- 有static修饰,属于外部类本身的。
- 总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类
- 静态内部类可以直接访问外部类的静态成员
- 静态内部类不可以直接访问外部类的非静态成员,如果需要访问,要创建外部类对象
- 静态内部类中没有隐含的Outer.this
内部类使用格式
静态内部类对象的创建格式
1
| 外部类.内部类 变量 = new 外部类.内部类构造器;
|
调用方法的格式
- 调用非静态方法的格式:先创建对象,用对象调用
- 调用静态方法的格式:外部类.内部类.方法名();
代码演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Outer01{ private static Stirng sc_name="小米"; public static class Inner01{ private String name; public Inner01(String name){ this,name = name; } public void showName(){ System.out.println(this.name); System.out.println(sc_name); } } }
public class InnerClassDemo01{ public static void main(String[] args){ Outer01.Inner01 in = new Outer01.Inner01("张三"); in.showName(); } }
|
局部内部类
定义在方法中的类
定义格式
1 2 3 4 5 6 7 8 9 10
| class 外部类名{ 数据类型 变量名; 修饰符 返回值类型 方法名(参数列表){ class 内部类{ } } }
|
匿名内部类
没有名字的内部类,可以在方法中,也可以在类中方法外。
概述
匿名内部类是内部类的简化写法。它是一个隐含了名字的内部类。
格式
1 2 3 4 5 6 7 8
| new 类名或者接口名(){ 重写方法; }
1.匿名内部类中{}就表示一个没有名字的类 2.这个没有名字的类要想实现接口或者继承类,直接把类名或者接口卸载{}前 3.创建对象,在java中通过new关键字创建对象,所以在类名或者接口前加上一个new 4.从语法上来讲,这个整体其实是匿名内部类对象
|
匿名内部类包含了:
- 继承或者实现关系
- 方法重写
- 创建对象(从语法上来讲,这个整体其实是匿名内部类对象)
为什么使用匿名内部类
实际上,如果我们希望定义一个只用使用一次的类,就可以考虑使用匿名内部类。匿名内部类的本质作用就是为了简化代码
之前我们使用接口时,似乎做了以下几步操作:
- 定义子类
- 重写接口中的方法
- 创建子类对象
- 调用重写后的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| interface Swim{ public abstract void swimming(); }
class Student implements Swim{ @Override public void swimming(){ System.out.println("狗刨式") } }
public class Test{ public static void main(String[] args){ Student s= new Student(); s.swiming(); } }
|
我们的目的,最终只是为了调用方法,那么能不能简化?匿名内部类就是做这样的快捷方式
匿名内部类前提和格式
匿名内部类必须继承一个父类或者实现一个父接口
1 2 3 4 5 6 7
| new 父类名或者接口名(){ @Override public void method() { } };
|
使用方式
以上述代码为例,匿名内部类使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| interface Swim{ public abstract void swimming(); }
public class Demo{ public static void main(Stringp[] args){ new Swim(){ @Override public void swimming(){ System.out.println("自由泳"); } }.swimming(); Swim s2 = new Swim() { @Override public void swimming() { System.out.println("蛙泳..."); } };
s2.swimming(); s2.swimming(); } }
|
匿名内部类的特点
- 定义一个没有名字的内部类
- 这个类实现了父类,或者父类接口
- 匿名内部类会创建这个没有名字的类对象
匿名内部类使用场景
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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(); } }
|
案例
在控制台输出”HelloWorld” ,在补齐代码处补齐
分析:
- Outer.method() 通过类直接调用的话,可以确定method是一个静态方法
- 方法返回什么类型才能继续调用show方法? 所以这里应该返回Inter接口对象再去调用方法show方法
- 通过一个匿名内部类是创建一个匿名内部类对象,在匿名内部类中去实现Inter接口并重写show方法
- 最后在通过这个对象调用重写的show方法,在重写的show方法中打印HelloWorld
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| interface Inter { void show(); } class Outer { public static Inter method{ return new Inter() { @Override public void show() { System.out.println("HelloWorld"); } }; } } public class OuterDemo { public static void main(String[] args) { Outer.method().show(); } }
|