开发者社区 问答 正文

【百问百答】《〈Java开发手册(泰山版)〉灵魂13问》

  1. 什么是三目运算符
  2. 什么是自动装箱和自动拆箱
  3. 三目运算符为什么会出现 NullPointerException
  4. HashMap 容量初始化流程是什么
  5. HashMap 为什么推荐设置初始化容量
  6. HashMap 初始化容量设置多少合适
  7. HashMap 扩容机制是什么
  8. HashMap 存储结构是什么
  9. 如何在并发场景使用Map
  10. Java Executors 创建线程池方式有哪些
  11. 为什么阿里巴巴禁止使用 Executors 创建线程池
  12. 为什么 Executors 创建线程池有可能会发生OOM
  13. 如何正确创建线程池
  14. 为什么说发生异常(Exception)要比发生错误(Error)好
  15. 什么是fail-fast
  16. 集合和视图的关系是什么
  17. 为什么 subList 返回结果 ArrayList 不能接收
  18. 为什么操作 subList 会发生错误
  19. 为什么循环中不推荐使用 "+" 进行字符串拼接
  20. concat拼接字符串和 "+" 拼接字符串有什么区别
  21. 字符串拼接效率最高方式的是什么
  22. 字符串拼接方式有几种,实际使用中如何选择字符串拼接方式
  23. 什么是foreach 循环
  24. foreach 循环原理是什么
  25. 什么是fail-safe
  26. 为什么在 for 或 foreach 循环中对集合元素做 add/remove 操作会报错
  27. 如何在循环中过滤集合中数据
  28. 目前广泛使用的日志框架有哪些
  29. 什么是门面模式
  30. 什么是日志门面(日志接口)
  31. 为什么要使用日志门面(日志接口)
  32. Java 中常用的日志门面(日志接口)有哪些
  33. 为什么不推荐直接使用日志系统 (Log4j、Logback) 中的 API
  34. 为什么阿里巴巴禁止把 SimpleDateFormat 定义成 static 变量
  35. 如何在并发场景使用日期转换
  36. 什么是POJO
  37. POJO和Java Bean 有什么区别
  38. POJO 类中使用基础数据类型还是包装类型
  39. 什么是Optional
  40. JSON 序列化工具 fastJson、jackson 和 Gson 之间有何 区别
  41. 为什么阿里巴巴禁止开发人员使用 isSuccess 作为变量名
  42. 序列化 Serializable 和 Externalizable 有什么区别
  43. transient 关键字作用是什么
  44. 什么是 serialVersionUID
  45. 为什么要明确 serialVersionUID
  46. 为什么 serialVersionUID 改变的时候会抛异常
  47. Java 代码的复用表现形式有哪些
  48. 什么是里氏代换原则
  49. COUNT(列名)、COUNT(常量) 和 COUNT(*) 之间的区别
  50. 数据库对 COUNT(*) 是如何优化的
  51. COUNT(*) 和 COUNT(1) 有什么区别?建议用哪个?

点击下载《〈Java开发手册(泰山版)〉灵魂13问》: https://developer.aliyun.com/article/758263

展开
收起
一人吃饱,全家不饿 2021-01-05 14:20:12 1533 分享 版权
阿里云 AI 助理回答

您的问题涉及多个Java编程和阿里云技术领域的知识点,我将尽力为您一一解答:

三目运算符

三目运算符(也称条件运算符)是Java中的一种简写形式,用于在一条语句中实现简单的条件判断。其语法为:条件 ? 表达式1 : 表达式2。如果条件为真,则执行表达式1;否则执行表达式2。

自动装箱与自动拆箱

  • 自动装箱:将基本数据类型自动转换为对应的包装器类型。
  • 自动拆箱:将包装器类型自动转换为对应的基本数据类型。这两个过程由Java编译器自动完成,简化了代码编写。

三目运算符与NullPointerException

当使用三目运算符时,如果条件表达式或结果表达式之一可能为null,且后续操作未做空指针检查,就可能出现NullPointerException。

HashMap容量初始化流程

HashMap在创建时如果没有指定初始容量,默认容量为16,并会根据负载因子(默认0.75)预估扩容阈值。如果指定了初始容量,它会计算一个大于该值的最小2的幂作为实际容量。

HashMap推荐设置初始化容量的原因

预先设定合适的容量可以减少HashMap在动态扩容时的重新哈希操作,提高性能,避免多次扩容带来的资源消耗。

初始化容量设置建议

应根据实际需要存储的键值对数量来估算,通常选择略大于预期大小的最小2的幂次方数,以减少扩容次数。

HashMap扩容机制

当HashMap中的元素数量超过扩容阈值时,会进行扩容,新容量通常是原容量的两倍,并重新分配所有键值对。

HashMap存储结构

HashMap采用数组加链表/红黑树的形式存储数据。每个桶(数组元素)可以存放一个链表或红黑树结构,用于解决哈希冲突。

并发场景下的Map使用

推荐使用ConcurrentHashMap,它提供了线程安全的并发访问能力。

Java Executors创建线程池方式

Executors类提供了几种工厂方法,如newFixedThreadPool, newSingleThreadExecutor, newCachedThreadPool等,但阿里巴巴Java开发手册不推荐直接使用这些方法,因为它们可能导致资源耗尽或内存泄漏。

禁止使用Executors创建线程池的原因

主要是因为默认配置下,可能会导致线程池无限增长(如newCachedThreadPool),或者任务队列无界(如newFixedThreadPool),从而引发OOM。

正确创建线程池的方式

应该通过ThreadPoolExecutor手动配置线程池参数,包括核心线程数、最大线程数、等待队列长度、拒绝策略等,以确保线程池的稳定性和安全性。

异常与错误的区别

异常(Exception)通常表示程序可以处理的问题,而错误(Error)表示更严重的系统级问题,一般不可恢复,因此处理异常比错误好,因为它提供了恢复的机会。

fail-fast集合

fail-fast集合在迭代过程中,若集合被修改则抛出 ConcurrentModificationException,保证了快速失败并提示错误。

视图与集合的关系

视图是对集合的一种动态视角,比如subList返回的是原集合的一个视图,对视图的操作会影响原集合。

subList注意事项

subList返回的是原ArrayList的一个视图,不是独立副本,直接赋值给新的ArrayList会导致类型不匹配错误。操作subList时,若原列表被修改,可能抛出ConcurrentModificationException。

字符串拼接

  • 不推荐在循环中使用"+"拼接字符串,因为每次拼接都会生成新的字符串对象,效率低。
  • 使用StringBuilderStringBuffer进行拼接更高效。
  • concat方法用于连接两个字符串,相比"+"操作稍微高效,但在多字符串拼接时不如StringBuilder

foreach循环原理

foreach循环是Java提供的语法糖,内部通过迭代器遍历集合或数组元素,简化了遍历过程。

fail-safe

fail-safe集合(如CopyOnWriteArrayList)在迭代时不抛出ConcurrentModificationException,即使原集合被修改,迭代器看到的是快照。

循环中过滤集合

可以使用Java 8引入的Stream API,如filter()方法配合Lambda表达式,在循环外部完成过滤操作。

日志框架

广泛使用的日志框架有Log4j、Logback、SLF4J等。

门面模式与日志门面

  • 门面模式:提供一个统一的接口,用来访问子系统中的一群接口,简化客户端与子系统的交互。
  • 日志门面:如SLF4J,提供统一的日志接口,使得应用可以在不修改代码的情况下切换底层日志实现。

使用日志门面的好处

解耦日志实现与业务代码,便于维护和切换不同的日志框架。

阿里巴巴禁止Static SimpleDateFormat

因为SimpleDateFormat非线程安全,静态实例在多线程环境下共享会引发并发问题,推荐使用线程局部变量ThreadLocal来封装。

并发日期转换

使用java.time包下的类(如LocalDateTime)替代SimpleDateFormat,它们是线程安全的,或继续使用ThreadLocal包裹SimpleDateFormat

POJO

POJO(Plain Old Java Object)简单老式Java对象,通常指没有遵循特定框架或设计模式的普通Java对象。

POJO与Java Bean区别

Java Bean要求具有无参构造器,属性私有且提供getter/setter方法,而POJO定义更为宽泛,不一定严格遵循Java Bean规范。

POJO中基础数据类型与包装类型

推荐使用包装类型,特别是在集合中,因为基础数据类型无法表示null值,包装类型能更好地表达业务逻辑中的“无值”状态。

JSON序列化工具比较

  • FastJson:阿里巴巴出品,速度快,API简洁。
  • Jackson:功能全面,支持多种数据格式,社区活跃。
  • Gson:Google推出,易用性高,解析JSON字符串到对象方便。

isSuccess禁用原因

命名不够具体明确,建议使用更具描述性的变量名,以增强代码可读性。

Serializable与Externalizable

  • Serializable:Java内置序列化机制,自动实现,但开销大,速度慢。
  • Externalizable:提供更细粒度的控制,需手动实现序列化和反序列化,性能更好。

transient关键字

用于标记不需要序列化的字段,当对象被序列化时,transient修饰的字段会被忽略。

serialVersionUID作用

它是序列化版本的唯一标识,用于验证序列化对象的版本一致性,防止反序列化时因版本不匹配而导致的异常。

改变serialVersionUID的影响

改变后,如果尝试反序列化旧版本的对象,会抛出InvalidClassException,因为序列化ID不匹配。

Java代码复用

主要通过继承、接口实现、抽象类、方法重载/重写、组合等方式实现。

里氏代换原则

子类对象应当能够替换父类对象出现在父类能够出现的任何地方,不破坏程序的正确性。

COUNT函数差异

  • COUNT(列名):统计指定列非NULL值的数量。
  • COUNT(常量):始终返回表行数。
  • COUNT(*):统计所有行,包括NULL值的行,数据库优化器通常对此进行了优化。

如何优化COUNT(*)

数据库引擎针对COUNT(*)进行了优化,特别是InnoDB存储引擎,通常直接从元数据获取行数,非常高效。

COUNT(*)与COUNT(1)建议

两者在大多数情况下效果相同,都很快,但推荐使用COUNT(*),因为它更标准,且易于理解其含义。

以上内容涵盖了您提出的主要问题,希望对您有所帮助。如果您还有其他疑问或需要进一步解释,请随时告知。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答
问答分类:
问答标签:
问答地址: