1. Java注释:代码的自我说明书
在Java开发中,注释是程序员与代码对话的重要方式。好的注释能让代码像一本打开的书,而糟糕的注释则像加密的电报。Java支持三种注释形式,每种都有其独特的应用场景。
1.1 单行注释:简洁的代码旁白
单行注释以双斜杠//开头,适用于简短的说明或临时禁用代码行。例如:
// 计算用户年龄(以秒为单位) long ageInSeconds = age * 365 * 24 * 60 * 60;提示:单行注释应该与代码保持同一缩进级别,注释符号后保留一个空格再开始文字说明
在IDE中,单行注释常用于快速调试:
// System.out.println("调试信息"); // 临时禁用日志输出1.2 多行注释:代码段的详细说明
当需要更详细的说明时,可以使用/* ... */形式的多行注释。这种注释适合:
- 方法实现的详细说明
- 复杂算法的步骤解释
- 临时注释大段代码
/* * 采用快速排序算法实现 * 时间复杂度:O(n log n) * 空间复杂度:O(log n) */ public void sort(int[] arr) { // 实现代码... }注意:多行注释不应包含星号组成的装饰线,这会影响代码可读性。保持每行以*开头并统一缩进即可
1.3 文档注释:专业的API文档
/** ... */形式的文档注释是Java特有的强大功能,通过javadoc工具可以自动生成API文档。完善的文档注释应包含:
/** * 计算两个数的最大公约数 * * @param a 第一个正整数 * @param b 第二个正整数 * @return 最大公约数 * @throws IllegalArgumentException 当参数小于等于0时抛出 * @see <a href="https://en.wikipedia.org/wiki/Euclidean_algorithm">欧几里得算法</a> */ public static int gcd(int a, int b) { if (a <= 0 || b <= 0) { throw new IllegalArgumentException("参数必须为正整数"); } // 实现代码... }文档注释支持HTML标签和特定标签(如@param、@return等),是现代Java开发中不可或缺的部分。在IntelliJ IDEA等IDE中,输入/**后按回车会自动生成文档注释模板。
2. 类:Java的蓝图
类是面向对象编程的基石,理解类的本质是掌握Java的关键。一个设计良好的类就像精心设计的建筑图纸,决定了对象的行为和结构。
2.1 类的组成要素
典型的Java类包含以下部分:
// 包声明 package com.example.project; // 导入语句 import java.util.List; /** * 用户实体类 * <p>代表系统中的一个注册用户</p> */ public class User { // 静态变量 private static int userCount = 0; // 实例变量 private String username; private String email; // 构造方法 public User(String username, String email) { this.username = username; this.email = email; userCount++; } // 方法 public void sendEmail(String content) { // 发送邮件实现 } // 静态方法 public static int getUserCount() { return userCount; } }类的设计原则:
- 单一职责原则:一个类只做一件事
- 开放封闭原则:对扩展开放,对修改关闭
- 高内聚低耦合:内部紧密相关,外部依赖最少
2.2 类的修饰符
Java提供了多种类修饰符来控制类的可见性和行为:
| 修饰符 | 作用域 | 说明 |
|---|---|---|
| public | 所有包 | 完全公开 |
| protected | 同包和子类 | 受保护的 |
| (default) | 同包 | 包级私有 |
| private | 同类 | 完全私有 |
| abstract | - | 抽象类,不能实例化 |
| final | - | 不可继承 |
| static | - | 嵌套类专用 |
经验:除非有明确需求,否则优先使用最严格的访问权限。public类应该越少越好,这符合封装原则
2.3 内部类的妙用
内部类可以分为四种类型,每种都有特定用途:
- 成员内部类:作为外部类的成员存在
class Outer { class Inner { // 可以访问Outer的所有成员 } }- 静态嵌套类:与普通类相似,但声明在另一个类内部
class Outer { static class Nested { // 只能访问Outer的静态成员 } }- 方法局部内部类:在方法内定义的类
class Outer { void method() { class Local { // 只能在该方法内使用 } } }- 匿名内部类:没有类名的即时实现
new Runnable() { @Override public void run() { // 实现代码 } };内部类常用于:
- 事件处理(如GUI编程)
- 实现特殊数据结构(如链表节点)
- 封装只在特定上下文使用的功能
3. 对象:类的具体实例
如果说类是蓝图,那么对象就是根据蓝图建造的房子。理解对象生命周期对编写健壮的Java程序至关重要。
3.1 对象的创建与初始化
对象创建过程分为几个关键阶段:
- 内存分配:JVM在堆中分配内存空间
- 默认初始化:成员变量设为默认值(0、false、null等)
- 显式初始化:执行初始化块和赋值语句
- 构造方法执行:完成最终初始化
public class Person { private String name = "未知"; // 显式初始化 { // 初始化块 System.out.println("初始化块执行"); } public Person(String name) { this.name = name; // 构造方法初始化 System.out.println("构造方法执行"); } }对象创建示例:
Person p = new Person("张三");3.2 对象的内存模型
理解Java对象内存模型有助于避免常见错误:
栈内存(Stack) 堆内存(Heap) +---------------+ +------------------------+ | 引用变量p | ---> | Person对象 | | (存储地址) | | +-------------------+ | +---------------+ | | 对象头 | | | +-------------------+ | | | name="张三" | | | +-------------------+ | +------------------------+关键点:
- 基本类型变量直接存储在栈中
- 引用类型变量存储的是堆中对象的地址
- 数组也是对象,遵循同样的存储规则
3.3 对象比较的陷阱
==和equals()的区别是Java面试常见考点:
String s1 = "hello"; String s2 = new String("hello"); String s3 = "hello"; System.out.println(s1 == s2); // false,比较引用 System.out.println(s1 == s3); // true,字符串常量池复用 System.out.println(s1.equals(s2)); // true,比较内容实现正确的equals()方法需要遵循:
- 自反性:x.equals(x)必须为true
- 对称性:x.equals(y)与y.equals(x)结果相同
- 传递性:如果x.equals(y)且y.equals(z),则x.equals(z)
- 一致性:多次调用结果相同
- 非空性:x.equals(null)必须为false
4. 高级特性与最佳实践
4.1 不可变对象设计
不可变对象具有线程安全、易于缓存等优点。创建不可变类的规则:
- 所有字段设为final和private
- 不提供setter方法
- 禁止子类覆盖方法(类声明为final)
- 如果包含可变对象引用,需要进行防御性拷贝
public final class ImmutablePoint { private final int x; private final int y; private final Date created; // 可变对象 public ImmutablePoint(int x, int y, Date created) { this.x = x; this.y = y; this.created = new Date(created.getTime()); // 防御性拷贝 } public Date getCreated() { return new Date(created.getTime()); // 返回拷贝 } }4.2 对象池模式
对于创建成本高的对象,可以使用对象池优化性能:
public class ConnectionPool { private static final int MAX_SIZE = 10; private static final List<Connection> pool = Collections.synchronizedList(new ArrayList<>()); public static Connection getConnection() throws SQLException { if (!pool.isEmpty()) { return pool.remove(0); } if (pool.size() < MAX_SIZE) { return DriverManager.getConnection(DB_URL); } throw new RuntimeException("连接池已满"); } public static void releaseConnection(Connection conn) { if (conn != null) { pool.add(conn); } } }4.3 常见问题排查
- NullPointerException预防:
- 使用Optional类包装可能为null的值
- 采用空对象模式代替null
- 使用Objects.requireNonNull()进行参数校验
- 内存泄漏识别:
- 静态集合长期持有对象引用
- 未关闭的资源(如数据库连接)
- 监听器未正确注销
- 序列化问题:
- transient关键字控制序列化字段
- serialVersionUID保证版本兼容
- 考虑使用外部化(Externalizable)替代Serializable
在大型项目中,合理使用注释、精心设计类结构、正确管理对象生命周期,是保证代码质量的关键。记住,好的Java代码不仅需要正确运行,更应该易于理解和维护。