我们在外部类 Sky
的内部定义了一个成员内部类 Engine
,在 Engine
下面有一个 fly()
方法,其功能是打印输出一行字符串:“发动机启动了!”。
另外,需要注意的是,与普通的 Java 类不同,含有内部类的类被编译器编译后,会生成两个独立的字节码文件:
Car$Engine.class
Car.class
内部类 Engine
会另外生成一个字节码文件,其文件名为:外部类类名 $ 内部类类名.class。
内部类在外部使用时,无法直接实例化,需要借助外部类才能完成实例化操作。关于成员内部类的实例化,有 3 种方法:
实例演示
// 外部类 Car
public class Car {
// 内部类 Engine
private class Engine {
private void run() {
System.out.println(“发动机启动了!”);
}
}
public static void main(String[] args) {
// 1.实例化外部类后紧接着实例化内部类
Engine engine = new Car().new Engine();
// 2.调用内部类的方法
engine.run();
}
}
运行结果:
发动机启动了!
public static void main(String[] args) {
// 1.实例化外部类
Car car = new Car();
// 2.通过外部类实例对象再实例化内部类
Engine engine = car.new Engine();
// 3.调用内部类的方法
engine.run();
}
编译执行,成功调用了内部类的 fly () 方法:
$javac Car.java
java Car
发动机启动了!
getEngine()
,然后通过外部类的实例对象调用这个方法来获取内部类的实例:实例演示
package com.caq.oop.demo10;
public class Outer {
private int id = 10;
public void out(){
System.out.println(“这是外部类的方法”);
}
//一个java类中可以有多个class类,但只能有一个public class
public clas
s In {
public void in() {
System.out.println(“这是内部类的方法”);
}
}
public static void main(String[] args) {
//实例化内部类,调用内部类的方法
In In = new Outer().new In();
In.in();
}
}
运行结果:
这是内部类的方法
这种设计在是非常常见的,同样可以成功调用执行 fly()
方法。
成员内部类可以直接访问外部类的成员,例如,可以在内部类的中访问外部类的成员属性:
实例演示
// 外部类 Sky
public class Sky {
String name;
public Engine getEngine() {
return new Engine();
}
// 内部类 Engine
private class Engine {
// 发动机的起动方法
private void fly() {
System.out.println(name + “的发动机启动了!”);
}
}
public static void main(String[] args) {
// 实例化外部类
Sky Sky = new Sky();
// 为实例属性赋值
Sky.name = “张三”;
// 获取内部类实例
Engine engine = Sky.getEngine();
// 调用内部类的方法
engine.fly();
}
}
观察 Engine
的 fly()
方法,调用了外部类的成员属性 name
,我们在主方法实例化 Sky
后,已经为属性 name
赋值。
运行结果:
大奔奔的发动机启动了!
相同的,除了成员属性,成员方法也可以自由访问。这里不再赘述。
还存在一个同名成员的问题:如果内部类中也存在一个同名成员,那么优先访问内部类的成员。可理解为就近原则。
这种情况下如果依然希望访问外部类的属性,可以使用外部类名.this.成员
的方式,例如:
实例演示
// 外部类 Sky
public class Sky {
String name;
public Engine getEngine() {
return new Engine();
}
// 汽车的跑动方法
public void fly(String name) {
System.out.println(name + “跑起来了!”);
}
// 内部类 Engine
private class Engine {
private String name = “引擎”;
// 发动机的起动方法
private void fly() {
System.out.println(“Engine中的成员属性name=” + name);
System.out.println(Sky.this.name + “的发动机启动了!”);
Sky.this.fly(Sky.this.name);
}
}
public static void main(String[] args) {
// 实例化外部类
Sky Sky = new Sky();
// 为实例属性赋值
Sky.name = “张三”;
// 获取内部类实例
Engine engine = Sky.getEngine();
// 调用内部类的方法
engine.fly();
}
}
运行结果:
Engine中的成员属性name=引擎
张三的发动机启动了!
大奔奔跑起来了!
请观察内部类 fly()
方法中的语句:第一行语句调用了内部类自己的属性 name
,而第二行调用了外部类 Sky
的属性 name
,第三行调用了外部类的方法 fly()
,并将外部类的属性 name
作为方法的参数。
静态内部类也称为嵌套类,是使用 static
关键字修饰的内部类。如下代码中定义了一个静态内部类:
public class Car1 {
// 静态内部类
static class Engine {
public void run() {
System.out.println(“我是静态内部类的run()方法”);
System.out.println(“发动机启动了”);
}
}
}
静态内部类的实例化,可以不依赖外部类的对象直接创建。我们在主方法中可以这样写:
// 直接创建静态内部类对象
Engine engine = new Engine();
// 调用对象下run()方法
engine.run();
运行结果:
我是静态内部类的run()方法
发动机启动
在静态内部类中,只能直接访问外部类的静态成员。例如:
实例演示
public class Sky1 {
String brand = “宝马”;
static String name = “外部类的静态属性name”;
// 静态内部类
static class Engine {
public void fly() {
System.out.println(name);
}
}
public static void main(String[] args) {
Engine engine = new Engine();
engine.fly();
}
}
在 fly()
方法中,打印的 name
属性就是外部类中所定义的静态属性 name
。编译执行,将会输出:
外部类的静态属性name
对于内外部类存在同名属性的问题,同样遵循就近原则。这种情况下依然希望调用外部类的静态成员,可以使用外部类名.静态成员
的方式来进行调用。这里不再一一举例。
如果想要访问外部类的非静态属性,可以通过对象的方式调用,例如在 fly()
方法中调用 Sky1
的实例属性 brand
:
public void run() {
// 实例化对象
Car1 car1 = new Car1();
System.out.println(car1.brand);
}
方法内部类,是定义在方法中的内部类,也称局部内部类。
如下是方法内部类的代码:
实例演示
public class Sky2 {
// 外部类的fly()方法
public void fly() {
class Engine {
public void fly() {
System.out.println(“方法内部类的fly()方法”);
System.out.println(“发动机启动了”);
}
}
// 在Sky2.fly()方法的内部实例化其方法内部类Engine
Engine engine = new Engine();
// 调用Engine的fly()方法
engine.fly();
}
public static void main(String[] args) {
Sky2 Sky2 = new Sky2();
Sky2.fly();
}
}
运行结果:
方法内部类的run()方法
发动机启动了
如果我们想调用方法内部类的 fly()
方法,必须在方法内对 Engine
类进行实例化,再去调用其 fly()
方法,然后通过外部类调用自身方法的方式让内部类方法执行。
与局部变量相同,局部内部类也有以下特点:
方法内定义的局部内部类只能在方法内部使用;
方法内不能定义静态成员;
不能使用访问修饰符。
也就是说,Sky2.getEngine()
方法中的 Engine
内部类只能在其方法内部使用;并且不能出现 static
关键字;也不能出现任何的访问修饰符,例如把方法内部类 Engine
声明为 public
是不合法的。
匿名内部类就是没有名字的内部类。使用匿名内部类,通常令其实现一个抽象类或接口。
实例演示
// 定义一个交通工具抽象父类,里面只有一个fly()方法
public abstract class Transport {
public void fly() {
System.out.println(“交通工具fly()方法”);
}
public static void main(String[] args) {
// 此处为匿名内部类,将对象的定义和实例化放到了一起
Transport Sky = new Transport() {
// 实现抽象父类的fly()方法
@Override
public void fly() {
System.out.println(“汽车跑”);
}
};
// 调用其方法
Sky.fly();
Transport airPlain = new Transport() {
// 实现抽象父类的fly()方法
@Override
public void fly() {
System.out.println(“飞机飞”);
}
};
airPlain.fly();
}