java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java内部类

Java中的内部类超详细讲解

投稿:daisy

内部类是定义在另一个类内部的类,可分为成员、静态、局部和匿名内部类,这篇文章主要介绍了Java中内部类的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

什么是内部类?

内部类就是定义在另一个类内部的类。写在成员位置的,属于外部类的成员。

想象一下我们的日常生活:一台电脑包含CPU、内存、硬盘等组件。从面向对象的角度看,我们可以将电脑看作一个类(Computer),而CPU、内存等组件则可以看作电脑内部的类(CPUMemoryHardDisk)。这些组件属于电脑,在电脑内部工作,与电脑有紧密的联系。

这就像Java中的内部类概念:一个类定义在另一个类的内部,形成一种"类中有类"的结构。

生活中的内部类例子

// 电脑类
public class Computer {
    private String brand;
    private double price;
    
    // CPU内部类
    class CPU {
        private String model;
        private int cores;
        
        public void run() {
            // CPU可以访问电脑的品牌信息
            System.out.println(brand + "电脑的" + model + "处理器正在运行...");
        }
    }
    
    // 显卡内部类
    class GraphicsCard {
        private String model;
        private int memory;
        
        public void display() {
            // 显卡也可以访问电脑的信息
            System.out.println(brand + "电脑的" + model + "显卡正在渲染画面...");
        }
    }
    
    // 电脑使用内部组件
    public void start() {
        CPU cpu = new CPU();
        cpu.model = "Intel i7";
        cpu.run();
        
        GraphicsCard gc = new GraphicsCard();
        gc.model = "NVIDIA RTX 3080";
        gc.display();
    }
}

在这个例子中:

这种设计反映了现实世界中的"整体-部分"关系,内部类就像是外部类的一个组成部分。

public class Outer {  // 外部类
    private int num = 10;
    
    class Inner {  // 内部类
        // 内部类的代码
    }
}

为什么需要内部类?

理解内部类存在的原因,可以通过更多的生活例子来理解:

生活中的例子

内部类的存在意义

内部类的分类

Java中的内部类主要分为四种类型:

1. 成员内部类

什么是成员内部类?

成员内部类是定义在类成员位置的内部类,就像一个普通的成员变量一样。

成员内部类的特点

如何使用成员内部类?

方式一:在外部类中直接创建内部类对象并使用

public class Outer {
    private int num = 10;
    
    // 成员内部类
    class Inner {
        public void show() {
            System.out.println("外部类成员变量num=" + num);
        }
    }
    
    // 在外部类中使用内部类
    public void method() {
        Inner i = new Inner();
        i.show();
    }
}

方式二:在其他类中使用成员内部类

public class Test {
    public static void main(String[] args) {
        // 先创建外部类对象
        Outer outer = new Outer();
        
        // 再创建内部类对象
        Outer.Inner inner = outer.new Inner();
        
        // 使用内部类方法
        inner.show();
    }
}

成员内部类访问外部类同名成员

如果内部类和外部类有同名的成员变量,可以使用外部类名.this.成员变量来访问外部类的成员:

public class Outer {
    private int num = 10;
    
    class Inner {
        private int num = 20;
        
        public void show() {
            int num = 30;
            System.out.println("局部变量:" + num);  // 30
            System.out.println("内部类成员变量:" + this.num);  // 20
            System.out.println("外部类成员变量:" + Outer.this.num);  // 10
        }
    }
}

2. 静态内部类

什么是静态内部类?

静态内部类是使用static关键字修饰的内部类,是一种特殊的成员内部类。

静态内部类的特点

静态内部类的使用

创建静态内部类对象的格式

外部类名.内部类名 对象名 = new 外部类名.内部类名();

示例代码

public class Car {  // 外部类
    private String carName;
    private static int carAge = 10;
    
    // 静态内部类
    static class Engine {
        private String engineName;
        
        public void show() {
            // 可以访问外部类静态成员
            System.out.println("汽车年龄:" + carAge);
            
            // 不能访问外部类非静态成员
            // System.out.println(carName); // 编译错误
        }
    }
}

// 使用静态内部类
public class Test {
    public static void main(String[] args) {
        // 直接创建静态内部类对象,不需要外部类对象
        Car.Engine engine = new Car.Engine();
        engine.show();
    }
}

静态内部类的方法调用

public class Outer {
    // 静态内部类
    static class Inner {
        // 静态方法
        public static void staticMethod() {
            System.out.println("静态内部类的静态方法");
        }
        
        // 非静态方法
        public void normalMethod() {
            System.out.println("静态内部类的非静态方法");
        }
    }
}

// 调用方法
public class Test {
    public static void main(String[] args) {
        // 调用静态方法
        Outer.Inner.staticMethod();
        
        // 调用非静态方法
        Outer.Inner inner = new Outer.Inner();
        inner.normalMethod();
    }
}

3. 局部内部类

什么是局部内部类?

局部内部类是定义在方法中的类,像局部变量一样,只能在定义它的方法内部使用。

局部内部类的特点

局部内部类的使用

public class Outer {
    private int outerField = 10;
    
    public void method() {
        final int localVar = 20;  // final局部变量
        int effectivelyFinal = 30;  // effectively final变量(不会被修改)
        
        // 局部内部类
        class LocalInner {
            public void show() {
                // 访问外部类成员
                System.out.println("外部类成员:" + outerField);
                
                // 访问方法中的局部final变量
                System.out.println("局部变量:" + localVar);
                
                // 访问effectively final变量
                System.out.println("Effectively final变量:" + effectivelyFinal);
            }
        }
        
        // 创建局部内部类对象并调用方法
        LocalInner inner = new LocalInner();
        inner.show();
        
        // 注意:这里不能修改effectivelyFinal的值
        // effectivelyFinal = 40; // 这样会导致编译错误
    }
}

4. 匿名内部类

什么是匿名内部类?

匿名内部类是隐藏了名字的内部类,本质上是一个没有名字的局部内部类,它必须继承一个类或实现一个接口。

匿名内部类的特点

匿名内部类的格式

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

匿名内部类的使用场景

当接口的实现类(或父类的子类)只使用一次时,可以使用匿名内部类简化代码。

匿名内部类示例

1. 实现接口的匿名内部类

public class Test {
    public static void main(String[] args) {
        // 使用匿名内部类实现Runnable接口
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("这是匿名内部类实现的run方法");
            }
        };
        
        new Thread(r).start();
        
        // 更简洁的写法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("直接创建匿名内部类");
            }
        }).start();
    }
}

2. 继承类的匿名内部类

abstract class Animal {
    public abstract void eat();
}

public class Test {
    public static void main(String[] args) {
        // 使用匿名内部类继承抽象类
        Animal a = new Animal() {
            @Override
            public void eat() {
                System.out.println("狗吃骨头");
            }
        };
        
        a.eat();
    }
}

3. 带参数的匿名内部类

interface Calculator {
    int calculate(int a, int b);
}

public class Test {
    public static void main(String[] args) {
        // 使用匿名内部类实现带参数的接口
        Calculator c = new Calculator() {
            @Override
            public int calculate(int a, int b) {
                return a + b;
            }
        };
        
        System.out.println("计算结果:" + c.calculate(10, 20));
    }
}

内部类的实际应用

1. 实现多重继承

Java不支持类的多继承,但可以通过内部类模拟实现:

class A {
    public void methodA() {
        System.out.println("来自A类的方法");
    }
}

class B {
    public void methodB() {
        System.out.println("来自B类的方法");
    }
}

// 通过内部类实现对A和B功能的同时使用
class C {
    // 继承A的内部类
    private class InnerA extends A {
        public void methodA() {
            super.methodA();
        }
    }
    
    // 继承B的内部类
    private class InnerB extends B {
        public void methodB() {
            super.methodB();
        }
    }
    
    // 对外提供方法
    public void methodA() {
        new InnerA().methodA();
    }
    
    public void methodB() {
        new InnerB().methodB();
    }
}

2. 封装实现细节

内部类可以用来隐藏实现细节,如集合类中的迭代器实现:

public class MyArrayList<E> {
    private Object[] elements;
    private int size;
    
    // 其他代码...
    
    // 使用内部类实现迭代器
    private class MyIterator implements Iterator<E> {
        private int cursor;
        
        @Override
        public boolean hasNext() {
            return cursor < size;
        }
        
        @Override
        public E next() {
            if (cursor >= size) {
                throw new NoSuchElementException();
            }
            return (E) elements[cursor++];
        }
    }
    
    // 对外提供获取迭代器的方法
    public Iterator<E> iterator() {
        return new MyIterator();
    }
}

3. 回调机制

匿名内部类常用于实现事件监听和回调:

// 在Swing中使用匿名内部类处理按钮点击
JButton button = new JButton("点击我");
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击了!");
    }
});

内部类与Lambda表达式

在Java 8之后,对于只有一个抽象方法的接口(函数式接口),可以使用Lambda表达式代替匿名内部类,使代码更加简洁:

// 使用匿名内部类
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("使用匿名内部类");
    }
};

// 使用Lambda表达式
Runnable r2 = () -> System.out.println("使用Lambda表达式");

// 启动线程
new Thread(r1).start();
new Thread(r2).start();

// 直接使用Lambda创建线程
new Thread(() -> System.out.println("直接使用Lambda")).start();

内部类编译后的文件命名规则

编译含有内部类的Java文件后,会生成多个.class文件:

例如,如果有以下类定义:

public class Outer {
    class Inner {}
    static class StaticInner {}
    
    public void method() {
        class LocalInner {}
        
        new Runnable() {
            public void run() {}
        };
    }
}

编译后将生成以下文件:

内部类面试常见问题

1. 内部类的各种差异比较

特性成员内部类静态内部类局部内部类匿名内部类
定义位置类成员位置类成员位置方法内部方法内部
访问修饰符可以使用可以使用不能使用不能使用
是否可静态JDK16前不可可以不可以不可以
是否需要外部类对象需要不需要需要需要
是否可以访问外部类非静态成员可以不可以可以可以
是否可以访问外部类静态成员可以可以可以可以

2. 为什么局部内部类只能访问final局部变量?

这是因为局部变量在方法结束后就会被销毁,而局部内部类对象可能在方法结束后仍然存在。如果允许内部类修改局部变量,当变量已经被销毁后,内部类却还引用这个变量,会导致数据不一致。为了解决这个问题,Java要求局部内部类访问的局部变量必须是final的(Java 8后可以是effectively final)。

内部类与外部类的关系

内部类与外部类的关联

// 编译前的代码
public class Outer {
    private int x = 10;
    
    class Inner {
        void access() {
            System.out.println(x);  // 访问外部类的私有成员
        }
    }
}

// 编译器处理后的逻辑(简化表示)
public class Outer {
    private int x = 10;
    
    // 为内部类提供的访问方法
    static int access$000(Outer outer) {
        return outer.x;
    }
    
    class Inner {
        final Outer this$0;  // 持有外部类引用
        
        Inner(Outer outer) {
            this$0 = outer;  // 保存外部类引用
        }
        
        void access() {
            System.out.println(Outer.access$000(this$0));  // 通过特殊方法访问
        }
    }
}

总结

内部类是Java中一个强大的特性,它允许我们在一个类中定义另一个类,增强了封装性和代码的组织结构。主要分为四种类型:

内部类存在的主要意义

使用内部类的建议

随着Java的发展,内部类与Lambda表达式、方法引用等新特性结合使用,可以使代码更加简洁和易读。掌握内部类是成为Java高级开发者的必备技能。

到此这篇关于Java中内部类的文章就介绍到这了,更多相关Java内部类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文