JAVA程序员防翻车指南
一、基础类型与字面量(1-10) int 最大值 2,147,483,647(2³¹-1),最小值 -2,147,483,648(-2³¹) long 最大值 9,223,372,036,854,775,807(2⁶³-1),字面量加 L float 字面量必须加 f,否则默认 double short 范围 -32,768 ~ 32,767,byte 范围 -128 ~ 127 字面量下划线:1_000_000 等于 1000000,仅 Java 7+ 0x 开头是十六进制,0b 开头是二进制,0 开头是八进制(容易误判) char 占 2 字节,Character.SIZE = 16,但 Unicode 扩展字符需 2 个 char(surrogate pair) boolean 只有 true/false,不能和 0/1 互转(和 C 不同) Integer.valueOf(127) == Integer.valueOf(127) 为 true,128 为 false(缓存池 -128~127) Integer.parseInt("") 抛 NumberFormatException,Integer.parseInt(null) 也抛 二、运算符与表达式(11-18) & 和 && 区别:& 两边都执行(位运算或逻辑与),&& 短路 | 和 || 同理,|| 短路 ^ 按位异或,~ 按位取反(包含符号位) >> 算术右移(补符号位),>>> 无符号右移(补 0) i++ 和 ++i 的字节码区别:前者先加载后自增,后者先自增后加载 += 隐式类型转换:short s = 1; s += 1; 合法,s = s + 1; 编译错误 三元运算符 ? : 必须返回相同类型,否则自动类型提升 字符串拼接用 + 在循环内会生成大量 StringBuilder,循环外编译器自动优化 三、String 相关(19-28) String 不可变,底层 char[] 被 final 修饰且不暴露修改方法 字符串常量池在堆中(Java 8+),intern() 手动入池 new String("abc") 创建 2 个对象(常量池 + 堆),"abc" 只创建 1 个(若池中没有) String.equals() 比较内容,== 比较引用地址 String.compareTo() 按字典序比较,返回差值 String.format() 使用 %s %d %f 占位符 substring() 在 Java 7 前共享 char[],Java 7+ 创建新数组(解决内存泄漏) split() 参数是正则,. 需转义为 \\. replace() 替换所有字符,replaceAll() 支持正则 StringBuilder 线程不安全,StringBuffer 线程安全(方法加 synchronized),但慢 四、equals 与 hashCode(29-34) 重写 equals 必须重写 hashCode,否则 HashMap/HashSet 失效 equals 必须满足:自反、对称、传递、一致、非空(x.equals(null) 返回 false) hashCode 相等的两个对象 equals 不一定相等(哈希冲突) Objects.equals(a, b) 自动判空,避免 null.equals() Arrays.deepEquals() 比较多维数组,Arrays.equals() 只比较一维 List.equals() 要求顺序相同,Set.equals() 仅要求元素相同(顺序无关) 五、异常处理(35-44) try-catch-finally 中 finally 在 return 之前执行,但返回值已在 finally 前计算好 finally 中有 return 会覆盖 try/catch 的返回值 finally 中修改返回的引用对象内容会生效(如 List.add()) 不要在 finally 中写 return,也不要在其中抛出异常(会掩盖原始异常) try-with-resources 要求资源实现 AutoCloseable,关闭顺序:先声明的后关闭 受检异常(Exception 子类)必须 try-catch 或 throws,RuntimeException 非受检 异常链:catch (SQLException e) { throw new RuntimeException(e); } 保留堆栈 printStackTrace() 不应用在生产日志中,用 log.error("msg", e) Throwable 包括 Error 和 Exception,Error 不应该捕获(如 OutOfMemoryError) finally 中关闭资源时可能抛异常,要用 try-catch 包裹或使用 try-with-resources 六、集合框架(45-62) ArrayList 初始容量 10,扩容 1.5 倍(oldCapacity + (oldCapacity >> 1)) ArrayList 随机访问 O(1),中间插入删除 O(n),LinkedList 相反 LinkedList 实现了 Deque,可作为队列/栈使用 HashMap 容量为 2 的幂,默认 16,负载因子 0.75,扩容 2 倍 HashMap 索引计算:(n - 1) & hash,hash = key.hashCode() ^ (h >>> 16) Java 8+ HashMap 链表长度 > 8 且容量 >= 64 时转红黑树,< 6 时退化为链表 HashMap 的 key 为 null 时,hash 值为 0,存在 table[0] ConcurrentHashMap 分段锁(Java 7)→ synchronized + CAS(Java 8+) Hashtable 线程安全但全表锁,已淘汰 TreeMap 基于红黑树,有序(按 key 自然序或 Comparator) LinkedHashMap 按插入顺序或访问顺序(accessOrder=true 可实现 LRU) HashSet 底层是 HashMap,TreeSet 底层是 TreeMap PriorityQueue 小顶堆,Comparator.reverseOrder() 可转大顶堆 ArrayDeque 优于 Stack(Stack 已废弃,ArrayDeque 线程不安全但更快) Collections.synchronizedList() 包装为线程安全,但迭代时仍需手动同步 CopyOnWriteArrayList 读多写少场景,写时复制整个数组 List.of() / Set.of()(Java 9+)返回不可变集合,不能增删改 ArrayList.subList() 返回视图,对子列表操作会影响原列表,反之亦然 七、并发与线程(63-80) 创建线程的三种方式:Thread、Runnable、Callable(有返回值) start() 启动线程,run() 只是普通方法调用 synchronized 可锁实例方法、静态方法(锁 Class)、代码块 wait() 释放锁,sleep() 不释放锁,yield() 让出 CPU 但不释放锁 wait()/notify() 必须在 synchronized 块中调用,否则抛 IllegalMonitorStateException notify() 随机唤醒一个,notifyAll() 唤醒全部 死锁四条件:互斥、请求与保持、不可抢占、循环等待 破坏死锁:按固定顺序加锁、tryLock() 超时、使用 ReentrantLock volatile 保证可见性和有序性(禁止指令重排),不保证原子性 AtomicInteger 用 CAS 实现原子操作,incrementAndGet() 底层是 Unsafe.compareAndSwapInt ThreadLocal 用完后必须 remove(),否则线程池复用导致内存泄漏 ThreadLocal 底层是 ThreadLocalMap,key 是弱引用,value 是强引用 ExecutorService 用 shutdown() 优雅关闭,shutdownNow() 立即关闭 线程池核心参数:核心线程数、最大线程数、存活时间、阻塞队列、拒绝策略 四种拒绝策略:AbortPolicy(抛异常)、CallerRunsPolicy(调用者执行)、DiscardPolicy(丢弃)、DiscardOldestPolicy(丢弃最旧) CachedThreadPool 最大线程数 Integer.MAX_VALUE,容易 OOM,慎用 FixedThreadPool 使用无界队列,任务积压可能导致 OOM Future.get() 阻塞,CompletableFuture 异步回调更灵活 八、I/O 与 NIO(81-88) File 只是路径抽象,不表示真实文件,exists() 判断是否存在 FileInputStream 读取字节,FileReader 读取字符(默认编码可能乱码) BufferedReader 用 readLine() 按行读取,Files.readAllLines() 更简洁 InputStreamReader 可指定字符集:new InputStreamReader(new FileInputStream("a.txt"), StandardCharsets.UTF_8) try-with-resources 自动关闭多个资源,用分号分隔 Path/Paths/Files(Java 7+)替代 File,Files.walk() 遍历目录树 NIO 核心:Channel + Buffer + Selector(多路复用) ByteBuffer 读写切换需调用 flip(),clear() 清空,compact() 压缩 九、JVM 与内存(89-95) JVM 内存区域:堆、栈、方法区(元空间)、程序计数器、本地方法栈 堆分代:年轻代(Eden + S0 + S1)→ 老年代,默认比例 8:1:1 OutOfMemoryError 常见原因:堆 OOM、元空间 OOM、直接内存 OOM、栈溢出 栈溢出 StackOverflowError,递归过深或无终止条件 System.gc() 仅建议 GC,不保证执行 finalize() 已废弃(Java 9+),对象自救不靠谱 类加载器:Bootstrap(rt.jar)→ Extension → Application,双亲委派模型 十、日期与时间(96-100) java.util.Date 可变、线程不安全,已废弃,用 java.time.*(Java 8+) LocalDate / LocalTime / LocalDateTime 不含时区,ZonedDateTime 含时区 Instant 时间戳(秒/纳秒),Duration 计算时间差,Period 计算日期差 格式化用 DateTimeFormatter 线程安全,替代 SimpleDateFormat(线程不安全) LocalDateTime.now() 获取当前时间,parse("2026-07-09") 按 ISO 标准解析 十一、语法糖与常见坑(101-110) 泛型编译期擦除,运行时无泛型信息(List<String> 和 List<Integer> 在运行时相同) 可变参数本质是数组,public void method(String... args) 可传数组或逗号分隔 枚举 enum 默认继承 Enum,不能被继承,构造器私有 匿名内部类持有外部类引用(this$0),可能导致内存泄漏 接口默认方法 default,静态方法 static,接口中变量默认 public static final 抽象类可有构造器,接口无构造器 switch 支持 byte/short/char/int/String/enum,不支持 long/float/double break 跳出循环,continue 跳过本次,return 结束方法 标签 label: 可跳出外层循环,但不推荐使用 数组协变:String[] 是 Object[] 的子类,但集合泛型不变 十二、网络与反射(111-118) URL 和 URI 区别:URI 是标识符,URL 是定位符(包含协议) HttpURLConnection 原生 HTTP 客户端,但 Apache HttpClient / OkHttp 更常用 Socket 阻塞 I/O,ServerSocket 监听端口,accept() 阻塞等待连接 反射 Class.forName() 初始化类,ClassLoader.loadClass() 不初始化 Method.invoke() 性能差,但反射能绕过私有访问(setAccessible(true)) Proxy.newProxyInstance() 动态代理只能代理接口,CGLIB 可代理类 注解保留策略:SOURCE(源码)、CLASS(字节码)、RUNTIME(运行时) 序列化实现 Serializable 接口,serialVersionUID 用于版本兼容,不声明则自动生成 十三、设计原则与模式(119-123) 单例双重检查需 volatile,防止指令重排导致半初始化对象被读取 工厂模式解耦,策略模式消除 if-else,观察者模式事件驱动 依赖倒置:依赖抽象而非具体实现 开闭原则:对扩展开放,对修改封闭 里氏替换:子类可以替换父类出现的地方 十四、Linux 与常用命令(124-128) nohup java -jar app.jar & 后台运行,输出到 nohup.out jps 查看 Java 进程,jstack 打印线程堆栈,jmap 查看堆内存,jstat 监控 GC kill -15 优雅关闭(SIGTERM),kill -9 强制杀死(SIGKILL) tail -f app.log 实时查看日志,grep -C 10 "error" app.log 显示上下文 netstat -tlnp 查看端口占用,lsof -i:8080 查看指定端口 十五、数据库与 JDBC(129-133) PreparedStatement 防 SQL 注入,占位符 ?,编译一次执行多次 Statement 拼接字符串有注入风险,不用 ResultSet 游标从 1 开始,next() 移动指针,getString(1) 按索引取 事务隔离级别:读未提交、读已提交、可重复读、串行化 连接池用 HikariCP(默认)、Druid,不要手动创建连接 十六、Maven / Gradle(134-137) Maven 生命周期:clean → compile → test → package → install → deploy provided 作用域表示运行时由容器提供(如 servlet-api) 依赖传递:compile 传递,test 不传递,provided 不传递 版本冲突用 dependencyManagement 锁定版本,或用 exclude 排除 十七、工具与调试(138-142) System.out.println() 性能差,生产用日志框架(SLF4J + Logback) 日志级别:ERROR > WARN > INFO > DEBUG > TRACE assert 默认禁用,需 -ea 开启,生产慎用 Objects.requireNonNull() 判空抛 NullPointerException,比手动 if 简洁 Optional 用来表示可能为 null 的返回值,不要用作字段或参数 十八、编码规范与常识(143-150) 包名全小写,类名大驼峰,方法/变量小驼峰,常量全大写 + 下划线 方法名用动词(getUser),变量名用名词(userName) 布尔变量用 is / has / can 开头(isDeleted) 一个 .java 文件只能有一个 public 类,文件名必须和 public 类名一致 构造器不能 static,不能被 final 修饰 static 块在类加载时执行一次,{} 实例块在构造器前执行 匿名对象(new User())用完即抛,适合单次使用 永远不要在线上环境用 System.out 打印大对象,更不要用 e.printStackTrace()