内部类是指定义在其他类内部的类。
它是Java中一种特殊的类,可以访问所在外部类的成员变量和方法,包括私有的成员。
内部类可以分为四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。
成员内部类是定义在其他类中的类。
成员内部类具有以下特点:
访问外部类的成员:成员内部类可以直接访问外部类的成员变量和方法,包括私有成员。
这是因为成员内部类持有一个对外部类对象的引用,可以通过该引用访问外部类的成员。
与外部类实例相关:创建成员内部类的实例时,需要先创建外部类的实例,然后通过外部类实例来创建内部类的实例。
每个内部类实例都与外部类的实例相关联,即每个内部类实例都有一个隐式的引用指向创建它的外部类实例。
可访问权限限制:成员内部类可以使用外部类的私有成员,包括私有变量和私有方法。
这为封装和数据隐藏提供了更多的灵活性。
名称空间:成员内部类的命名空间限定于所在的外部类。
即在外部类中可以直接使用内部类的简单名称来引用它,而外部类之间无法直接引用彼此的内部类。
非静态上下文:成员内部类是非静态的,它依赖于外部类的实例。
因此,成员内部类可以访问和操作外部类的成员,但不能声明静态成员(包括方法、变量和嵌套类内部的静态接口)。
成员内部类的使用格式如下:
public class OuterClass {
private int outerVar;
public class InnerClass {
private int innerVar;
public void method() {
outerVar = 10; // 访问外部类的成员变量
}
}
}
其中,OuterClass
是外部类,InnerClass
是成员内部类。成员内部类的定义在外部类的内部,并且在类的成员位置,可以定义自己的成员变量和方法。
通过外部类的实例创建内部类对象:
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
例如:
public class OuterClass {
private int outerVar;
public class InnerClass {
private int innerVar;
public InnerClass(int innerVar) {
this.innerVar = innerVar;
}
public void method() {
System.out.println("InnerClass outerVar: " + outerVar);
System.out.println("InnerClass innerVar: " + innerVar);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass(); // 创建外部类实例
OuterClass.InnerClass inner = outer.new InnerClass(20); // 通过外部类实例创建内部类对象
inner.method(); // 调用内部类对象的方法
}
}
在上述代码中,我们首先创建了外部类
OuterClass
的实例outer
,然后调用outer
的new
方法来创建内部类InnerClass
的对象inner
,同时通过构造函数给inner
的成员变量innerVar
赋值为20
。
最后,我们调用 inner
的 method()
方法,输出以下结果:
InnerClass outerVar: 0
InnerClass innerVar: 20
这表明我们成功创建了成员内部类对象,并可以访问外部类的成员变量 outerVar
和内部类的成员变量 innerVar
。
在外部类的方法内直接创建内部类对象:
public class OuterClass {
// ...
public void createInnerObject() {
InnerClass inner = new InnerClass();
}
// ...
public class InnerClass {
// ...
}
}
在外部类的方法内部,可以直接创建内部类的实例,无需先创建外部类的实例。在上述代码中,我们在外部类的
createInnerObject()
方法内创建了内部类的对象inner
。
下面是一个完整的例子,演示了这两种方式:
public class OuterClass {
private int outerVar;
public class InnerClass {
private int innerVar;
public InnerClass(int innerVar) {
this.innerVar = innerVar;
}
public void method() {
System.out.println("InnerClass outerVar: " + outerVar);
System.out.println("InnerClass innerVar: " + innerVar);
}
}
public void createInnerObject() {
InnerClass inner = new InnerClass(20); // 直接在外部类方法内创建内部类对象
inner.method();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass(); // 创建外部类实例
OuterClass.InnerClass inner = outer.new InnerClass(); // 通过外部类实例创建内部类对象
inner.method();
outer.createInnerObject();
}
}
在上例中,我们先通过外部类的实例
outer
创建了内部类的实例inner
,然后调用了inner
的method()
方法。同时,我们还在外部类的createInnerObject()
方法内直接创建了内部类的实例,并调用了method()
方法。
运行上例的输出如下:
InnerClass outerVar: 0
InnerClass innerVar: 0
InnerClass outerVar: 0
InnerClass innerVar: 20
在使用成员内部类时,有几个注意事项需要注意:
创建成员内部类的对象需要先创建外部类的对象,然后使用外部类对象调用 new
关键字来实例化内部类对象,例如:OuterClass.InnerClass inner = outer.new InnerClass();
成员内部类可以访问外部类的所有成员,包括私有成员,即使是静态的私有成员也可以直接访问。
成员内部类拥有对外部类对象的引用,可以直接访问和修改外部类的成员变量。
在内部类中可以定义与外部类相同名称的成员变量和方法。在内部类方法中,使用 this
关键字****可以引用内部类对象自身,使用 OuterClass.this
可以引用外部类对象。
成员内部类可以被外部类以及其他类继承和实现,具有灵活性。
成员内部类可以是私有的,这样它只能在外部类中访问,对其他类不可见。
如果成员内部类是静态的,那么它可以直接通过外部类名字引用,例如:OuterClass.InnerClass inner = new OuterClass.InnerClass();
,不需要先创建外部类对象。
静态内部类是声明在外部类内部的内部类,并且使用 static
修饰符进行标记。
它具有以下特点:
静态内部类可以单独使用,不需要依赖外部类的实例。
它的创建方式是通过
外部类名.内部类名
来实例化,例如:OuterClass.InnerClass inner = new OuterClass.InnerClass();
静态内部类可以访问外部类的静态成员变量和方法,但不能直接访问外部类的非静态成员变量和方法。
如果需要访问外部类的非静态成员,可以通过创建外部类的对象来间接访问。
静态内部类的对象不持有对外部类对象的引用,因此它的创建不会导致外部类对象的创建。
在静态内部类中,可以定义静态成员变量和方法,这些成员变量和方法可以直接通过内部类名引用
例如:
OuterClass.InnerClass.staticVar
。
静态内部类可以被其他类继承和实现。
静态内部类的作用域仅限于外部类,它对其他类是不可见的。
静态内部类的定义格式如下:
public class OuterClass {
// 外部类成员和方法
public static class StaticInnerClass {
// 静态内部类成员和方法
}
}
在这个定义中,
StaticInnerClass
是一个静态内部类,嵌套在OuterClass
外部类中。注意到在StaticInnerClass
的定义中,使用了static
属性符,使得内部类可以直接访问外部类的静态属性和方法。
使用外部类的类名直接访问内部类:
>可以使用`外部类.内部类`的方式来获得静态内部类的对象。
例如,如果外部类的类名为`OuterClass`,内部类为`StaticInnerClass`,可以使用`OuterClass.StaticInnerClass`来获取对象。
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
在外部类中定义一个方法来返回内部类对象:
>可以在外部类中定义一个方法,通过调用这个方法来获取静态内部类的对象。
public class OuterClass {
// 外部类成员和方法
public static class StaticInnerClass {
// ...
}
public static StaticInnerClass getInstance() {
return new StaticInnerClass();
}
}
// 在其他类中获取内部类对象
OuterClass.StaticInnerClass inner = OuterClass.getInstance();
这种方式通过定义一个静态方法,在方法中实例化内部类对象并返回它,以供外部类和其他类使用。
【注意】
内部类的使用格式
外部类.内部类。
静态内部类对象的创建格式
外部类.内部类 变量 = new 外部类.内部类构造器;
调用方法的格式
在使用静态内部类时,需要注意以下事项:
静态内部类无法访问外部类的非静态成员变量和非静态方法。如果需要访问非静态成员变量和非静态方法,需要先创建一个外部类实例,然后通过实例访问。
如果一个外部类与静态内部类拥有相同的方法名,可以通过添加外部类的类名或静态内部类的类名来调用不同的方法。
例如,假设外部类和静态内部类都有一个方法叫做`method()`,可以使用`OuterClass.method()`或者`StaticInnerClass.method()`来调用不同的方法。
静态内部类可以有静态成员变量和静态方法,它们与外部类和其它内部类的静态成员变量和静态方法一样。
可以通过 外部类.静态内部类
或 new 外部类.静态内部类()
来创建静态内部类的对象。静态内部类的实例化不需要先实例化外部类的对象。
静态内部类可以被其他类继承和实现,并且它的作用域限定在外部类中,不对外部的类造成影响。
局部内部类是一个定义在方法内部的类。
局部内部类的特点如下:
作用域仅限于所在的方法内部
局部内部类的作用域被限制在定义它的方法内部,无法在方法外部或其他方法中访问它。
可以访问所在方法的局部变量
局部内部类可以访问所在方法中的局部变量,但是该局部变量必须为 final 或者是事实上的 final(即不再改变),这是因为局部内部类的实例可以在方法的执行结束后仍然存在,这时局部变量可能已经消失。
可以访问所在方法的参数
局部内部类可以访问所在方法的参数,同样也必须是 final 或者是事实上的 final。
可以访问外部类的成员变量和方法
与静态内部类不同,局部内部类可以访问外部类的成员变量和方法,包括私有的成员。
无法包含静态成员或方法
由于局部内部类的实例存在于方法执行的生命周期中,所以不能包含静态成员和方法。
可以实现接口或者继承抽象类
局部内部类可以实现接口或者继承抽象类,从而具备相应的行为及特性。
局部内部类的定义格式如下:
public class OuterClass {
// 外部类的成员和方法
public void method() {
// 方法内部定义局部内部类
class LocalInnerClass {
// 局部内部类的成员和方法
}
// 在方法内可以使用局部内部类
LocalInnerClass inner = new LocalInnerClass();
}
}
在上述例子中,
LocalInnerClass
是一个定义在方法内部的局部内部类。它被定义在method
方法内部,并且只能在该方法内部被访问和使用。
在方法中创建局部内部类的实例,并返回该实例,外部类通过该方法获得局部内部类的对象。
例如:
public class OuterClass {
public InnerClass getInnerInstance() {
// 方法内部定义局部内部类
class InnerClass {
// 局部内部类的成员和方法
}
// 在方法内部创建局部内部类的实例并返回
return new InnerClass();
}
}
通过在外部类的方法
getInnerInstance()
中创建局部内部类InnerClass
的实例,并将该实例返回给外部类的调用者,从而使外部类获得了局部内部类的对象。
需要注意的是,由于局部内部类的作用域仅限于所在方法内部,因此只能在该方法内部进行创建及访问。
在方法内部将局部内部类的实例作为参数传递给其他方法。这样,在其他方法中就可以直接使用局部内部类的实例了。
public class OuterClass {
public void method(InnerClass inner) {
// 使用传递的局部内部类的实例
inner.hello();
}
public void invokeInner() {
// 方法内部定义局部内部类
class InnerClass {
public void hello() {
System.out.println("Hello, World!");
}
}
// 在方法内部创建局部内部类的实例并传递
InnerClass inner = new InnerClass();
method(inner);
}
}
在上述例子中,通过在
invokeInner
方法中定义局部内部类InnerClass
,然后在该方法内部创建该类的实例inner
并将其传递给method
方法。method
方法中就可以直接使用局部内部类InnerClass
的实例,并调用其方法。
在使用局部内部类时,有一些注意事项需要注意:
作用域限制:局部内部类的作用域仅限于定义它的方法或代码块内部。
在定义它的方法或代码块外部是无法直接使用局部内部类的,包括在方法外部创建它的实例。
可访问的变量限制:局部内部类只能访问被声明为 final 或实质上为 final 的局部变量。
这是因为局部内部类的生命周期可能超过其外部方法的生命周期,为了保证引用的正确性,访问的变量必须是被 final 修饰,或者保证它们不会被修改。
内部类可访问外部类成员:方法内部的局部内部类可以直接访问外部类的成员(包括实例变量和方法),无需额外的限定符或引用,就像访问自己的成员一样。
生命周期和内存管理:局部内部类的生命周期与方法的调用周期相关,在方法调用结束后,局部内部类的对象会随着方法的结束被销毁。
当不再需要局部内部类的实例时,要确保适时释放它的引用,以便垃圾回收器能够回收对应的内存空间。
嵌套层级限制:虽然可以在方法内部定义局部内部类,但不允许在局部内部类中再定义内部类。
也就是说,局部内部类不能再次嵌套声明其他内部类。
匿名内部类是一种在声明的同时实例化的内部类,没有显式的名称。
匿名内部类具有以下特点:
没有显式的类名
匿名内部类没有类名,它是在声明的同时进行实例化,通过实例化的对象使用。
以接口或抽象类为基础
匿名内部类通常是基于接口或抽象类来创建的。它可以实现接口的方法或继承抽象类并覆盖其中的抽象方法。
声明和实例化同时完成
匿名内部类的声明和实例化是一步完成的,通常在需要使用该内部类的地方直接创建,并且可以在创建时指定其特定的行为。
作用域限制
匿名内部类的作用域与它所在的方法或代码块相同,只能在其声明的范围内使用。超出此范围,匿名内部类将不再可见。
可以访问外部类的成员
匿名内部类可以访问外部类的方法、字段和实例变量,就像普通的内部类一样。
可以访问 final 或实质上是 final 的局部变量
匿名内部类可以访问所在方法的 final 或实质上是 final 的局部变量。这是因为匿名内部类生命周期可能超过方法的生命周期,需要保证访问的变量不会改变。
适用于单次使用
匿名内部类通常用于只需要一次使用的场景。由于没有类名,无法再次引用该类,所以适合于只需要创建临时对象的场景。
匿名内部类适用于以下情况:
必须继承一个父类或实现一个接口
由于匿名内部类本身没有名称,所以必须以某种方式继承一个父类或实现一个接口。
只使用一次
匿名内部类通常只使用一次,因为它没有名称,因此没有办法再次使用相同类的实例。
不需要访问外部类的非 final 变量
如果需要访问非 final 变量,那么这个变量必须被拷贝为 final 或实质上是 final 的变量,否则变量在匿名内部类被创建之后被修改后,将会导致内部类得到的变量与期望值不一致。
简单逻辑代码
匿名内部类通常适用于相对简单的逻辑代码。对于较复杂或结构复杂的代码,最好明确地定义一个独立的内部类。
匿名内部类的基本格式如下:
父类或接口名称 对象名 = new 父类或接口名称(){
//匿名内部类的类体部分
};
其中,父类或接口名称
指的是匿名内部类继承的父类或实现的接口的类名,对象名
指的是匿名内部类的对象名,可以随意命名。
【注意】在匿名内部类中,用到父类或接口中的方法、变量时,使用 super
或 interface
关键字进行调用。
匿名内部类的类体部分
是匿名内部类的主体,在其中完成具体的实现。它可以包含匿名内部类所需的变量、方法、构造函数等。在匿名内部类中,可以使用父类或接口中已经定义的方法或变量,也可以重写已经定义的方法,完成特定的逻辑操作。
下面是一个基于抽象类实现一个匿名内部类的示例:
abstract class Shape {
public abstract void draw(String color);
}
public class Main {
public static void main(String[] args) {
Shape circle = new Shape() {
@Override
public void draw(String color) {
System.out.println("Draw a circle with "+color+" color.");
}
};
circle.draw("red");
}
}
在这个例子中,定义了一个抽象类
Shape
,其中有一个抽象方法draw
。然后利用匿名内部类通过继承抽象类Shape
,实现了一个新的Shape
子类,并实现了draw
方法。在main
方法中,创建了一个基于匿名内部类的新对象circle
,并通过调用draw
方法完成其具体的业务逻辑。
要获取匿名内部类的对象,可以通过两种方式:
将匿名内部类赋值给一个变量:通过将匿名内部类实例赋值给一个变量,就可以得到该匿名内部类的对象,以便后续使用。
作为方法的返回值或参数:可以将匿名内部类作为方法的返回值或参数,从而获取匿名内部类的对象,并在其他地方使用。
将匿名内部类赋值给一个变量:
abstract class Shape {
public abstract void draw();
}
public class Main {
public static void main(String[] args) {
Shape circle = new Shape() {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
};
circle.draw(); // 调用匿名内部类的方法
}
}
在这个例子中,通过匿名内部类的方式创建了一个继承自抽象类
Shape
的匿名内部类,并实现了draw
方法。然后将这个匿名内部类的实例赋值给了一个Shape
类型的变量circle
,从而获取了匿名内部类的对象。通过调用circle.draw()
方法,可以执行匿名内部类中具体的逻辑。
作为方法的返回值或参数:
interface Message {
void display();
}
public class Main {
public static void main(String[] args) {
Message message = createMessage();
message.display(); // 调用匿名内部类的方法
}
public static Message createMessage() {
return new Message() {
@Override
public void display() {
System.out.println("Hello, World!");
}
};
}
}
在这个例子中,定义了一个接口
Message
,其中有一个方法display
。然后通过createMessage
方法创建匿名内部类的对象,并将其作为Message
类型的返回值。在main
方法中,调用createMessage
方法,将返回的匿名内部类对象赋值给一个Message
类型的变量message
,从而获取了匿名内部类的对象。通过调用message.display()
方法,可以执行匿名内部类中具体的逻辑。
匿名内部类必须继承一个父类或实现一个接口。
匿名内部类没有构造方法,因为它没有类名。
由于匿名内部类没有类名,也不能定义静态成员。因此,匿名内部类中不能有静态块、静态方法、静态成员变量等。
匿名内部类实现的是一个接口或继承自一个抽象类,因此必须实现其中的抽象方法。
由于匿名内部类在定义时就创建了一个实例,因此无法重复使用,其生命周期也就受限于当前的代码块或方法。
匿名内部类中使用的变量必须为 final 或不可变变量,因为匿名内部类实例中所使用的变量在匿名内部类创建时就确定了,如果在匿名内部类创建之后修改了这些变量的值,可能会导致匿名内部类实例中所使用的变量与预期不一致。