在 Java 中 类包(Package) 是一种用于组织和管理类、接口、枚举和注解的机制。包的主要目的是将相关的类和接口组织在一起,避免命名冲突,并提供一种层次化的命名空间。
default 访问修饰符只能在同一个包内访问)import 语句可以方便地使用其他包中的类在 Java 中包通过 package 关键字定义。包的声明必须是 Java 文件中的第一条语句(除了注释和空行)
// 定义一个包
package com.example.myapp;
// 定义一个类
public class MyClass {
// 类的内容
}Java 标准库提供了许多预定义的包,例如:
java.lang:包含 Java 核心类(如 String、Object 等),默认导入。java.util:包含集合框架、日期时间工具等java.io:包含输入输出相关的类java.net:包含网络编程相关的类在文件系统中,包对应的目录结构必须与包名一致。如果包名是 com.example.myapp,则类文件必须放在 com/example/myapp 目录下。
包命名规范:
. 分隔(如 com.example.myapp)com.example.myapp 对应的文件路径是 com/example/myappcom.example.myapp),这是 Java 的命名约定class、public 等)目录结构示例:
项目根目录/
├── com/
│ └── example/
│ └── myapp/
│ ├── MyClass.java
│ └── Utils.java
└── Main.java默认包:
如果类文件没有声明 package 语句,则该类属于默认包(default package)。默认包中的类可以直接访问,但通常不推荐使用默认包,因为:
注意事项:
package 声明package 声明必须是文件的第一条语句(注释和空行除外)com.example 和 com.Example 是不同的包要使用其他包中的类,可以使用 import 语句。import 语句必须放在 package 声明之后,类声明之前。
导入方式:
// 1. 导入单个类
import com.example.myapp.MyClass;
// 2. 导入整个包(不推荐,可能导致命名冲突)
import com.example.myapp.*;
// 3. 静态导入(导入静态成员)
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.System.out;
// 4. 静态导入整个类的静态成员
import static java.lang.Math.*;使用示例:
package com.example.test;
import com.example.myapp.MyClass;
import static java.lang.Math.PI;
import static java.lang.System.out;
public class Test {
public static void main(String[] args) {
MyClass obj = new MyClass();
out.println("PI = " + PI); // 使用静态导入,无需 Math.PI
}
}完全限定名:
如果没有使用 import,可以通过完全限定名(Fully Qualified Name)来使用类:
com.example.myapp.MyClass obj = new com.example.myapp.MyClass();导入冲突处理:
当导入的类名冲突时,可以使用完全限定名来区分:
import java.util.Date;
import java.sql.Date; // 编译错误:类名冲突
// 解决方法:使用完全限定名
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());导入最佳实践:
import package.*)java.lang 包中的类会自动导入,无需显式导入Java 中有四种访问修饰符,其中 default(包访问权限)与包密切相关:
| 访问修饰符 | 同一类 | 同一包 | 子类 | 不同包 |
|---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
包访问权限示例:
package com.example.package1;
public class ClassA {
private int privateField = 1; // 仅本类可访问
int defaultField = 2; // 同一包可访问
protected int protectedField = 3; // 同一包和子类可访问
public int publicField = 4; // 所有地方可访问
}
class ClassB { // 同一包中的类
void test() {
ClassA a = new ClassA();
// a.privateField; // 错误:无法访问
// a.defaultField; // 正确:同一包可访问
// a.protectedField; // 正确:同一包可访问
// a.publicField; // 正确:所有地方可访问
}
}为了方便分发和复用,可以将多个类文件打包成 JAR(Java Archive)文件。JAR 文件可以包含多个包,并且可以通过 CLASSPATH 环境变量或构建工具(如 Maven、Gradle)来管理依赖。
JAR 文件特点:
.class 文件和资源文件META-INF/MANIFEST.MF 清单文件java -jar 命令直接运行(如果指定了主类)使用 JAR 文件:
# 创建 JAR 文件
jar cvf myapp.jar com/example/myapp/*.class
# 运行 JAR 文件
java -jar myapp.jar
# 将 JAR 文件添加到类路径
java -cp myapp.jar com.example.myapp.Main在 Java 中内部类(Inner Class) 是定义在另一个类内部的类。内部类提供了一种更紧密的关联方式,允许一个类访问另一个类的成员(包括私有成员)。
内部类可以分为以下几种类型:
成员内部类(Member Inner Class)是定义在另一个类的内部,但不在任何方法或代码块中的类。
特点:
基本示例:
class Outer {
private String outerMessage = "Message from Outer class";
private int outerValue = 100;
// 成员内部类
class Inner {
private String innerMessage = "Message from Inner class";
void display() {
// 可以访问外部类的私有成员
System.out.println("Inner class: " + outerMessage);
System.out.println("Outer value: " + outerValue);
// 可以调用外部类的方法
outerMethod();
}
void innerMethod() {
System.out.println("Inner method called");
}
}
void outerMethod() {
System.out.println("Outer method called");
// 外部类可以访问内部类的私有成员
Inner inner = new Inner();
System.out.println("Access inner private: " + inner.innerMessage);
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer(); // 创建外部类对象
Outer.Inner inner = outer.new Inner(); // 创建内部类对象
inner.display(); // 调用内部类的方法
outer.outerMethod();
}
}内部类访问外部类的方式:
class Outer {
private String name = "Outer";
class Inner {
private String name = "Inner";
void display() {
System.out.println(name); // Inner(内部类的成员)
System.out.println(this.name); // Inner(显式指定)
System.out.println(Outer.this.name); // Outer(外部类的成员)
}
}
}成员内部类的编译:
.class 文件外部类名$内部类名.classOuter$Inner.class静态内部类(Static Nested Class)是使用 static 关键字定义的内部类。
特点:
基本示例:
class Outer {
private static String staticMessage = "Static message from Outer class";
private String instanceMessage = "Instance message from Outer class";
// 静态内部类
static class StaticNested {
private static String nestedStatic = "Nested static field";
void display() {
System.out.println("Static Nested class: " + staticMessage); // 可以访问静态成员
System.out.println("Nested static: " + nestedStatic);
// System.out.println(instanceMessage); // 错误:无法访问非静态成员
}
static void staticMethod() {
System.out.println("Static method in nested class");
}
}
}
public class Main {
public static void main(String[] args) {
// 直接创建静态内部类对象,无需外部类实例
Outer.StaticNested nested = new Outer.StaticNested();
nested.display();
// 调用静态内部类的静态方法
Outer.StaticNested.staticMethod();
}
}访问外部类非静态成员:
如果需要访问外部类的非静态成员,需要传入外部类实例:
class Outer {
private String instanceMessage = "Instance message";
static class StaticNested {
void display(Outer outer) {
System.out.println("Accessing: " + outer.instanceMessage);
}
}
}静态内部类的优势:
局部内部类(Local Inner Class)是定义在方法或代码块中的类。
特点:
final 或有效 final(effectively final)的局部变量public、private 等)基本示例:
class Outer {
private String outerMessage = "Message from Outer class";
void display() {
final String localMessage = "Message from method"; // 必须是 final 或有效 final
int localValue = 10; // 有效 final(赋值后不再修改)
// 局部内部类
class LocalInner {
void printMessages() {
System.out.println("Local Inner class: " + outerMessage);
System.out.println("Local variable: " + localMessage);
System.out.println("Local value: " + localValue);
}
}
LocalInner inner = new LocalInner(); // 创建局部内部类对象
inner.printMessages();
}
void displayWithParameter(final int param) {
// 局部内部类可以访问方法参数(必须是 final 或有效 final)
class LocalInner {
void show() {
System.out.println("Parameter: " + param);
}
}
new LocalInner().show();
}
}
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
outer.display();
outer.displayWithParameter(100);
}
}有效 final(Effectively Final):
从 Java 8 开始,局部内部类可以访问"有效 final"的变量,即变量在初始化后不再被修改:
void method() {
int value = 10; // 有效 final
// value = 20; // 如果取消注释,value 不再是有效 final,编译错误
class LocalInner {
void display() {
System.out.println(value); // 可以访问有效 final 变量
}
}
}输出:
Local Inner class: Message from Outer class
Local variable: Message from method
Local value: 10
Parameter: 100匿名内部类(Anonymous Inner Class)是没有名字的内部类,通常用于实现接口或继承类。
特点:
final/有效 final 的局部变量示例 1:实现接口
interface Greeting {
void sayHello();
}
public class Main {
public static void main(String[] args) {
// 匿名内部类实现接口
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello from Anonymous Inner Class!");
}
};
greeting.sayHello();
}
}输出:
Hello from Anonymous Inner Class!示例 2:继承类
class Animal {
void makeSound() {
System.out.println("Animal sound");
}
}
public class Main {
public static void main(String[] args) {
// 匿名内部类继承类
Animal dog = new Animal() {
@Override
void makeSound() {
System.out.println("Woof! Woof!");
}
};
dog.makeSound();
}
}示例 3:作为方法参数
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
// 匿名内部类作为方法参数
list.forEach(new java.util.function.Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("Item: " + s);
}
});
}
}示例 4:访问局部变量
public class Main {
public static void main(String[] args) {
final String message = "Hello"; // 必须是 final 或有效 final
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(message); // 可以访问 final 局部变量
}
};
new Thread(task).start();
}
}从 Java 8 开始,对于函数式接口(只有一个抽象方法的接口),可以使用 Lambda 表达式替代匿名内部类,代码更简洁:
// 使用匿名内部类
Runnable task1 = new Runnable() {
@Override
public void run() {
System.out.println("Task 1");
}
};
// 使用 Lambda 表达式(更简洁)
Runnable task2 = () -> System.out.println("Task 2");
// 使用匿名内部类
java.util.function.Consumer<String> consumer1 = new java.util.function.Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
// 使用 Lambda 表达式
java.util.function.Consumer<String> consumer2 = s -> System.out.println(s);何时使用匿名内部类 vs Lambda 表达式:
内部类的特点总结:
| 类型 | 是否依赖外部类实例 | 是否可以访问外部类成员 | 是否有名字 | 是否可以定义静态成员 | 使用场景 |
|---|---|---|---|---|---|
| 成员内部类 | 是 | 是(包括私有成员) | 有 | Java 16 起可(此前仅编译时常量) | 需要与外部类紧密关联时 |
| 静态内部类 | 否 | 只能访问静态成员 | 有 | 是 | 不需要外部类实例时 |
| 局部内部类 | 是 | 是(仅限 final 局部变量) | 有 | Java 16 起可 | 方法内部需要定义类时 |
| 匿名内部类 | 是(通常是静态方法除外) | 是(仅限外部类的成员) | 无 | 否 | 一次性实现接口或继承类时 |
优点:
缺点:
.class 文件,增加编译产物数量1. 事件监听器(GUI 编程)
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ButtonExample {
private JButton button;
public ButtonExample() {
button = new JButton("Click me");
// 使用匿名内部类实现事件监听
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});
}
}2. 迭代器模式
class Container {
private String[] items = {"A", "B", "C"};
// 成员内部类实现迭代器
class Iterator {
private int index = 0;
boolean hasNext() {
return index < items.length;
}
String next() {
return items[index++];
}
}
Iterator getIterator() {
return new Iterator();
}
}3. Builder 模式(静态内部类)
class Person {
private String name;
private int age;
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
// 静态内部类实现 Builder
static class Builder {
private String name;
private int age;
Builder name(String name) {
this.name = name;
return this;
}
Builder age(int age) {
this.age = age;
return this;
}
Person build() {
return new Person(this);
}
}
}
// 使用
Person person = new Person.Builder()
.name("John")
.age(30)
.build();4. 回调接口
interface Callback {
void onComplete(String result);
}
class Task {
void execute(Callback callback) {
// 执行任务
String result = "Task completed";
callback.onComplete(result);
}
}
// 使用匿名内部类实现回调
new Task().execute(new Callback() {
@Override
public void onComplete(String result) {
System.out.println(result);
}
});class Outer {
private String message = "Hello from Outer";
// 成员内部类
class Inner {
void display() {
System.out.println("Inner class: " + message);
}
}
// 静态内部类
static class StaticNested {
void display() {
System.out.println("Static Nested class: Cannot access Outer's instance members");
}
}
// 方法中的局部内部类
void localInnerClassExample() {
final String localMessage = "Hello from method";
class LocalInner {
void display() {
System.out.println("Local Inner class: " + message + ", " + localMessage);
}
}
LocalInner inner = new LocalInner();
inner.display();
}
}
public class Main {
public static void main(String[] args) {
// 成员内部类
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
// 静态内部类
Outer.StaticNested nested = new Outer.StaticNested();
nested.display();
// 局部内部类
outer.localInnerClassExample();
// 匿名内部类
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous Inner Class: Task executed");
}
};
new Thread(task).start();
}
}输出:
Inner class: Hello from Outer
Static Nested class: Cannot access Outer's instance members
Local Inner class: Hello from Outer, Hello from method
Anonymous Inner Class: Task executed// 错误:包名是 com.example,但文件不在 com/example/ 目录下
package com.example;
public class MyClass { }解决方法:确保包名与目录结构一致。
import java.util.Date;
import java.sql.Date; // 编译错误:类名冲突解决方法:使用完全限定名或只导入需要的类。
class Outer {
private String message = "Hello";
static class StaticNested {
void display() {
// System.out.println(message); // 错误:无法访问非静态成员
}
}
}解决方法:静态内部类只能访问外部类的静态成员,或通过参数传入外部类实例。
void method() {
int value = 10;
// value = 20; // 如果取消注释,value 不再是有效 final
class LocalInner {
void display() {
System.out.println(value); // 只能访问 final 或有效 final 变量
}
}
}解决方法:确保局部变量是 final 或有效 final 的。
class Outer {
class Inner {
// 内部类持有外部类引用
}
}
// 如果内部类对象生命周期长于外部类对象,可能导致内存泄漏解决方法:
// 错误:匿名内部类不能定义构造方法
Runnable task = new Runnable() {
public Runnable() { // 编译错误
// ...
}
@Override
public void run() {
// ...
}
};解决方法:使用初始化块或工厂方法。
包命名规范
com.company.project.module导入语句
import package.*包的组织结构
com.company.project/
├── config/ # 配置类
├── controller/ # 控制器层
├── service/ # 服务层
│ └── impl/ # 服务实现
├── repository/ # 数据访问层
├── model/ # 数据模型
│ ├── entity/ # 实体类
│ ├── dto/ # 数据传输对象
│ └── vo/ # 视图对象
├── util/ # 工具类
└── exception/ # 自定义异常| 需求 | 推荐方案 | 说明 |
|---|---|---|
| 需要访问外部类实例成员 | 成员内部类 | 可以访问外部类的所有成员 |
| 不需要外部类实例 | 静态内部类 | 不持有外部类引用,内存更优 |
| 方法内部使用 | 局部内部类 | 作用域限制在方法内 |
| 一次性使用 | 匿名内部类或 Lambda | Lambda 更简洁(函数式接口) |
| Builder 模式 | 静态内部类 | 链式调用,构建复杂对象 |
避免过度使用内部类
内存管理
可读性优先
从 Java 9 开始,引入了模块化系统(JPMS,Java Platform Module System),包可以被组织在模块中。
模块的关键概念:
module-info.java 定义module-info.java 文件,声明模块的名称、导出的包、依赖的模块等模块声明示例:
// module-info.java
module com.example.myapp {
// 导出的包(其他模块可以使用)
exports com.example.myapp.api;
exports com.example.myapp.model;
// 依赖的模块
requires java.sql; // 依赖 java.sql 模块
requires java.logging; // 依赖 java.logging 模块
requires transitive java.base; // 传递依赖
// 使用的服务
uses com.example.myapp.spi.Service;
// 提供的服务实现
provides com.example.myapp.spi.Service
with com.example.myapp.impl.ServiceImpl;
}模块与包的关系:
exports)requires)模块化的优势:
使用建议:
public class Calculator {
private int result;
// 使用内部类实现闭包
public Runnable createAdder(int value) {
// 匿名内部类捕获了外部变量 value
return new Runnable() {
@Override
public void run() {
result += value; // 访问外部类的实例变量
System.out.println("Result: " + result);
}
};
}
public int getResult() {
return result;
}
}
// 使用示例
Calculator calc = new Calculator();
Runnable add5 = calc.createAdder(5);
Runnable add10 = calc.createAdder(10);
add5.run(); // Result: 5
add10.run(); // Result: 15
add5.run(); // Result: 20interface Visitor {
void visit(Book book);
void visit(Fruit fruit);
}
abstract class Item {
abstract void accept(Visitor visitor);
}
class Book extends Item {
private String title;
private double price;
Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() { return title; }
public double getPrice() { return price; }
@Override
void accept(Visitor visitor) {
visitor.visit(this);
}
}
class Fruit extends Item {
private String name;
private double weight;
Fruit(String name, double weight) {
this.name = name;
this.weight = weight;
}
public String getName() { return name; }
public double getWeight() { return weight; }
@Override
void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 使用匿名内部类实现不同的访问者
public class ShoppingCart {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
public double calculateTotal() {
final double[] total = {0.0};
Visitor priceVisitor = new Visitor() {
@Override
public void visit(Book book) {
total[0] += book.getPrice();
}
@Override
public void visit(Fruit fruit) {
total[0] += fruit.getWeight() * 2.5; // 假设每千克 2.5 元
}
};
for (Item item : items) {
item.accept(priceVisitor);
}
return total[0];
}
}
// 使用示例
ShoppingCart cart = new ShoppingCart();
cart.addItem(new Book("Java Programming", 59.99));
cart.addItem(new Fruit("Apple", 1.5));
cart.addItem(new Fruit("Banana", 2.0));
System.out.println("Total: $" + cart.calculateTotal());class Order {
private OrderState state;
// 使用内部类实现状态模式
interface OrderState {
void handle(Order order);
}
// 新订单状态
class NewOrderState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Processing new order");
order.state = new ProcessingState();
}
}
// 处理中状态
class ProcessingState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order is being processed");
order.state = new ShippedState();
}
}
// 已发货状态
class ShippedState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order has been shipped");
order.state = new DeliveredState();
}
}
// 已送达状态
class DeliveredState implements OrderState {
@Override
public void handle(Order order) {
System.out.println("Order has been delivered");
}
}
public Order() {
this.state = new NewOrderState();
}
public void process() {
state.handle(this);
}
}
// 使用示例
Order order = new Order();
order.process(); // Processing new order
order.process(); // Order is being processed
order.process(); // Order has been shipped
order.process(); // Order has been delivered减少内部类实例创建
避免不必要的内部类
内存泄漏预防
public class Outer {
private List<Inner> inners = new ArrayList<>();
class Inner {
// 持有外部类引用
}
// 错误:可能导致内存泄漏
public void createInner() {
inners.add(new Inner()); // 持续累积内部类对象
}
// 正确:及时清理
public void cleanup() {
inners.clear();
}
}查看编译后的内部类
# 编译后,内部类会生成独立的 .class 文件
# 成员内部类:Outer$Inner.class
# 静态内部类:Outer$StaticNested.class
# 匿名内部类:Outer$1.class, Outer$2.class, ...
# 局部内部类:Outer$1LocalInner.class使用 javap 反编译查看内部类结构
javap -p Outer\$Inner.class在 IDE 中查看内部类
java -jar xxx.jar 启动。| 特性 | 旧版(Java 8) | Java 9/21 |
|---|---|---|
| 模块化 | 无(classpath 时代) | JPMS 模块(Java 9):module-info.java 声明依赖与导出 |
| JAR 运行 | java -jar | jlink 自定义运行时镜像(Java 9)、未命名模块兼容旧 classpath |
| 反射访问 | 无限制 | 模块封装:跨模块反射需 opens(Java 9+,17 默认强封装) |
| 内部类捕获 | final/事实上 final | 不变;record(Java 16)与密封类(Java 17)丰富嵌套场景 |