【Java】Java核心知识点与相应面试技巧(八)——类与对象(三)

Java 内部类

上期面试题解析


上文链接:https://blog.csdn.net/weixin_73492487/article/details/146690712


  1. 以下代码输出什么?
    class A {
        static { System.out.print("1"); }
        { System.out.print("2"); }
        public A() { System.out.print("3"); }
    }
    
    class B extends A {
        static { System.out.print("4"); }
        { System.out.print("5"); }
        public B() { System.out.print("6"); }
    }
    
    public class Test {
        public static void main(String[] args) {
            new B(); 
        }
    }
    

输出:142536

  1. 静态方法能否调用非静态方法?为什么?

答:不能,静态方法调用时可能还没有实例存在

  1. 以下代码是否合法?
    class Test {
        static int a = b; 
        static int b = 10;
    }
    

非法(向前引用

  1. 何时选择抽象类?何时选择接口?

需要共享代码 → 抽象类
需要定义类型规范 → 接口
需要多继承 → 接口

  1. 以下代码是否合法?为什么?
    abstract class A {
        abstract void method1();
        final void method2() {}
    }
    
    interface B {
        default void method3() {}
        static void method4() {}
    }
    

合法。抽象类可包含final方法,接口支持默认和静态方法


1. 内部类的定义以及分类

定义:

内部类是定义在另一个类内部的类。内部类可以访问外部类的成员(包括私有成员),内部类为外部类服务,封装或组织逻辑。

分类和基本特性:

类型 定义位置 访问权限 内存依赖 典型应用场景
成员内部类 类内部(非static) 可访问外部类所有成员 依赖外部类实例,创建时需要外部类实例 迭代器实现/深度封装
静态内部类 类内部,static修饰 只能访问外部类静态成员 与外部类实例无关,静态成员 工具类/关联紧密的辅助类
局部内部类 方法/代码块内部 访问外部类成员、方法的 final 局部变量 仅在方法内有效,依赖外部类实例 方法内部专用逻辑
匿名内部类 即时实现,无类名 同局部内部类 依赖外部类实例,通常用于即时创建 事件监听/一次性实现

2. 各种内部类的详解

2.1 成员内部类/普通内部类(Member Inner Class)

定义:
成员内部类是定义在外部类的成员位置(即类体内,但不在方法、构造器或代码块中)的一种类。成员内部类可以访问外部类的所有成员(包括私有成员)。成员内部类的实例通常依赖于外部类的实例来创建。

示例:

class Outer {
    private int outerField = 20;
    //成员内部类
    class MemberInner {
        void access() {
            System.out.println(outerField); // 可以访问外部类(包括私有)实例成员
        }
    }
}
public class Main {
    public static void main(String[] args) {
      	// 实例化
		Outer outer = new Outer();
		Outer.MemberInner inner = outer.new MemberInner();
    }
}

应用场景:
(1)处理复杂的逻辑或业务:
在一些复杂的业务逻辑中,成员内部类可以作为外部类的一个补充,来封装特定的功能,而不需要暴露给外部类之外的代码。这样可以减少外部类的复杂度,同时保持类的封装性。
(2)事件监听器:
在 GUI 编程中,尤其是 Swing 或 AWT 中,成员内部类常用于处理按钮点击、窗口关闭等事件,因为它可以访问外部类的成员(比如 GUI 控件)。
(3)迭代器模式:
在实现集合类的迭代器模式时,成员内部类常常用来定义迭代器。这样迭代器就能够直接访问外部类的私有数据。
(4)简化多层嵌套:
在某些情况下,成员内部类可以帮助简化多层嵌套逻辑,使代码结构更清晰,特别是当内部类的功能非常依赖于外部类的状态时。

特点:

  • 内部类能直接访问外部类的私有成员,可以实现更高效的封装和代码分离。
  • 在需要频繁访问外部类成员的情况下,成员内部类可以减少外部类与内部类之间的交互复杂度。
  • 内部类无需显式传递外部类的成员,可以直接访问外部类的私有字段和方法。

2.2 静态内部类(Static Nested Class)

定义:
静态内部类是定义在另一个类内部,并且声明为static的类。与普通的内部类不同,静态内部类没有隐式地持有外部类的实例,它不需要通过外部类的实例来创建。静态内部类只能访问外部类的静态成员(字段、方法)
示例:

class Outer {
    private static int staticField = 10;
    //静态内部类,由static修饰
    static class StaticInner {
        void access() {
            System.out.println(staticField); // 只能访问外部类静态成员
        }
    }
}

// 使用:不依赖于外部类的实例,可以通过外部类的类名直接访问
Outer.StaticInner inner = new Outer.StaticInner();

特点

  • 独立于外部类实例存在,静态内部类不依赖于外部类的实例,因此可以通过外部类的类名直接访问
  • 可声明静态成员,只能访问外部类的静态成员,不能访问外部类的实例成员
  • 常用于工具类(如Map.Entry)

2.3 局部内部类(Local Inner Class)

定义:
局部内部类是定义在方法内部的类,它的作用范围仅限于该方法内。局部内部类通常用于在特定的方法中定义一个类,而不需要让这个类在外部类的其他部分可见。

示例:

class OuterClass {
    private String outerField = "Outer Field";
    // 方法内定义局部内部类
    public void Method() {
    	//局部变量
        String localVar = "Local Variable";
        // 局部内部类定义在方法内
        class LocalInnerClass {
            void display() {
                System.out.println("Accessing outer class field: " + outerField);
                System.out.println("Accessing local variable: " + localVar);
            }
        }

        // 创建局部内部类的实例
        LocalInnerClass inner = new LocalInnerClass();
        inner.display();
    }
}

public class Main {
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        //通过调用外部类的方法来使用局部内部类
        outer.someMethod(); 
        // 输出:Accessing outer class field: Outer Field
        // 输出:Accessing local variable: Local Variable
    }
}

特点

  • 局部作用域: 局部内部类只能在它定义的那一个方法中使用,方法结束后该类的实例就不能再使用。
  • 访问方法中的局部变量: 局部内部类可以访问方法的局部变量,但这些局部变量必须是 final 或隐式 final(即不会被修改,但 Java 8+ 已经隐式地带有final属性)。
  • 不能有静态成员: 局部内部类不能定义静态成员,因为它是与外部方法的执行过程关联的,不能和方法的生命周期脱离
  • 访问外部类成员: 局部内部类可以访问外部类的成员(如字段、方法等),包括私有成员。

2.4 匿名内部类(Anonymous Inner Class)

定义:
匿名内部类是Java中一种特殊的内部类,它没有名字,并且通常在创建对象的同时定义类的实现。它是一个类的实例化表达式,通常用于实现接口或继承某个类
语法:

new 接口或类名() {
    // 重写接口或类中的方法
};

示例:

//实现接口
interface ClickListener {
    void onClick();
}
//外部类
class Button {
    void setListener(ClickListener listener) {
     /*...*/
      }
}

// 使用:匿名内部类实现接口
new Button().setListener(new ClickListener() {
    @Override
    public void onClick() {
        System.out.println("Button clicked!");
    }
});

特性

  • 没有显式类名,不能有构造函数,通常用于只需要一次性使用的类定义
  • 只能实现一个接口或继承一个类,并且必须在构造时直接实现这些方法
  • 作用域仅限于创建它的代码块,所以只能在定义时使用
  • Java 8+可用Lambda替代(函数式接口时)

3. 核心机制

3.1 访问外部类成员原理

  • 成员内部类通过Outer.this访问外部类实例
  • 编译器自动生成合成访问方法(synthetic accessor)

3.2 内存泄漏风险

class Outer {
    class Inner { /*...*/ }
}

// 长期持有内部类实例会导致外部类无法回收

解决方案

  • 使用静态内部类
  • 使用弱引用(WeakReference)

3.3 字节码分析

  • 内部类编译为独立.class文件(如Outer$Inner.class
  • 自动生成桥接方法访问外部类私有成员

⚡ 高频面试题

  1. 为什么局部内部类访问的局部变量必须final?

  2. 如何避免内部类导致的内存泄漏?

  3. 以下代码是否正确?

    class Outer {
        int val = 10;
        class Inner {
            int val = 20;
            void print() {
                System.out.println(Outer.this.val); 
            }
        }
    }
    
  4. 匿名内部类能否实现多接口?


你可能感兴趣的:(java,面试,开发语言)