Java灵魂拷问13个为什么,你都会哪些?

简介: 【11月更文挑战第6天】本文回答了一些常见的 Java“灵魂拷问”,包括 Java 跨平台的原因、垃圾回收机制的作用、接口不能有实例变量的原因、字符串不可变的好处、异常处理机制的意义、类加载机制的双亲委派模型、多线程同步机制的重要性、重写方法访问修饰符的限制、包装类的存在意义、`equals()` 和 `hashCode()` 方法一起重写的原因、静态方法不能被重写的原因、`ArrayList` 扩容策略的权衡,以及 `final` 关键字的多种用途。

以下是对一些常见的 Java “灵魂拷问” 的回答:


  1. 为什么 Java 是跨平台的?
  • Java 之所以能够跨平台,是因为 Java 程序不是直接在操作系统上运行,而是运行在 Java 虚拟机(JVM)上。JVM 是一个抽象的计算机,它有自己的指令集(字节码)。当我们编写 Java 代码并编译时,生成的是字节码文件(.class 文件)。不同操作系统有对应的 JVM 实现,如 Windows JVM、Linux JVM、Mac JVM 等。字节码可以在任何安装了相应 JVM 的操作系统上运行,JVM 会将字节码解释或者编译成该操作系统能够理解的机器码,从而实现 “一次编写,到处运行” 的特性。
  1. 为什么 Java 有垃圾回收(GC)机制?
  • 在 Java 中,对象的内存分配和回收是由 JVM 的垃圾回收器自动管理的。这是因为手动管理内存容易出现内存泄漏(对象不再使用但内存没有释放)和悬空指针(指针指向已经释放的内存区域)等问题。垃圾回收器通过跟踪对象的引用关系,能够自动识别哪些对象不再被引用,然后回收这些对象所占用的内存空间。这样可以提高程序的稳定性和开发效率,让程序员将更多精力放在业务逻辑上,而不是复杂的内存管理上。
  1. 为什么 Java 中的接口(interface)不能有实例变量?
  • 接口的主要目的是定义一组行为规范(方法签名),供实现类去实现。接口强调的是一种契约关系,不应该包含具体的实现细节。如果允许有实例变量,就会涉及到具体的状态,这与接口的抽象性和契约性相违背。接口中的变量默认是public static final,它们是常量,所有实现类共享这些常量的值,这样可以保证接口的纯洁性和一致性。
  1. 为什么 Java 中的字符串(String)是不可变的?
  • 字符串不可变有很多好处。首先,安全性方面,不可变的字符串可以避免被意外修改,例如作为参数传递给方法时,调用者不用担心字符串在方法内部被改变。其次,在哈希表(如HashMap)等数据结构中作为键使用时,不可变的字符串可以保证其哈希值在其生命周期内不会改变,这样可以提高数据结构的性能和正确性。另外,字符串常量池也依赖于字符串的不可变性,相同的字符串字面量可以共享内存空间,节省内存。
  1. 为什么 Java 有异常处理机制?
  • 异常处理机制是为了让程序能够在遇到错误情况时以一种可控的方式进行处理。在没有异常处理的情况下,程序一旦遇到错误(如数组越界、文件读取错误等)就会直接崩溃。通过使用try - catch块,程序可以捕获异常并采取相应的措施,如记录错误日志、给用户显示友好的错误信息、尝试恢复程序的正常运行等。finally块还可以确保一些必要的清理操作(如关闭文件、释放资源)一定会被执行。
  1. 为什么 Java 的类加载机制是双亲委派模型?
  • 双亲委派模型的主要目的是保证 Java 类的唯一性和安全性。当一个类加载器收到加载类的请求时,它首先会将请求委派给它的父类加载器。只有当父类加载器无法完成加载任务时,子类加载器才会尝试自己加载。这样可以避免同一个类被多次加载,并且可以防止恶意代码替换核心 Java 类。例如,自定义的java.lang.String类如果没有双亲委派模型,可能会被加载并替换掉 Java 核心库中的String类,这会导致系统的混乱和安全问题。
  1. 为什么 Java 中的多线程需要同步机制?
  • 在多线程环境下,多个线程可能会同时访问和修改共享资源。如果没有同步机制,就会导致数据不一致的问题,例如两个线程同时对一个共享变量进行写操作,可能会导致变量的值不符合预期。通过使用synchronized关键字或者java.util.concurrent包中的锁机制(如ReentrantLock),可以保证在同一时刻只有一个线程能够访问被保护的代码块或者资源,从而保证数据的完整性和一致性。
  1. 为什么 Java 中重写(override)方法时,访问修饰符不能比父类中更严格?
  • 这是为了遵循里氏替换原则(Liskov Substitution Principle)。如果子类重写方法的访问修饰符比父类更严格,那么在通过父类引用调用该方法时,可能会出现无法访问的情况。例如,父类中的方法是public,子类重写后变成private,当使用父类引用指向子类对象并调用该方法时,由于private方法不能在类外部访问,就会导致程序逻辑错误。
  1. 为什么 Java 中的包装类(如 Integer、Double 等)存在?
  • 首先,Java 的基本数据类型(如intdouble)不是对象,不具有对象的特性,如不能调用方法。包装类将基本数据类型包装成对象,这样就可以在需要对象的场景中使用,例如在集合类(ArrayListHashMap等)中存储基本数据类型的值。另外,包装类还提供了一些实用的方法,如Integer.parseInt()可以将字符串转换为整数,Double.valueOf()可以将字符串转换为双精度浮点数等。
  1. 为什么 Java 中的equals()hashCode()方法常常一起重写?


  • 在 Java 中,equals()方法用于比较两个对象是否相等,hashCode()方法用于生成对象的哈希值。在一些数据结构(如HashMapHashSet)中,先根据对象的哈希值来确定对象在数据结构中的存储位置,当两个对象的哈希值相同时,再通过equals()方法来判断是否真正相等。如果只重写equals()方法而不重写hashCode()方法,可能会导致在这些数据结构中出现不符合预期的行为,例如两个相等的对象(根据equals()判断)具有不同的哈希值,会导致它们在HashSet中被错误地存储为两个不同的元素。


  1. 为什么 Java 中静态方法不能被重写?


  • 静态方法是属于类的方法,而不是属于对象的方法。重写是基于对象的多态性概念。当调用非静态方法时,是根据对象的实际类型来确定调用哪个方法。而静态方法是通过类名来调用的,它在编译时就已经确定了要调用的方法版本,与对象的实际类型无关。所以静态方法不存在重写的概念,不过可以在子类中定义一个与父类中静态方法签名相同的静态方法,这是方法的隐藏,而不是重写。


  1. 为什么 Java 的ArrayList在扩容时要按照一定的倍数(通常是 1.5 倍)进行?


  • ArrayList在扩容时按照一定倍数进行主要是为了平衡性能和空间利用率。如果每次只增加一个固定的小容量(如每次增加 1 个元素的空间),那么在频繁添加元素的情况下,会频繁地进行扩容操作,这会导致大量的内存复制和性能损耗。而如果扩容倍数过大,又会导致空间的浪费。1.5 倍左右的扩容倍数是一种经过权衡的选择,可以在一定程度上减少扩容的频率,同时也不会造成过多的空间浪费。


  1. 为什么 Java 中final关键字可以用于修饰类、方法和变量?


  • final用于修饰类时,该类不能被继承。这可以保证类的完整性和安全性,防止子类对其进行修改。用于修饰方法时,该方法不能被重写,这可以保证方法的行为在继承体系中是固定的,有助于维护代码的稳定性。当用于修饰变量时,对于基本变量,其值不能被改变;对于引用变量,其引用不能被改变(即不能再指向其他对象),这样可以确保变量的值或者引用在其生命周期内是固定的,避免意外的修改。


相关文章
|
23天前
|
存储 安全 Java
Java灵魂拷问13个为什么,你都会哪些?
大家好,我是 V 哥。今天分享 13 个 Java 编程中的常见问题,包括 `BigDecimal` 的 `equals` 方法、`HashMap` 初始化容量、`Executors` 创建线程池等。这些问题都是 V 哥在日常编码中总结的经验,希望能帮助大家提升代码质量和性能。如果内容对你有帮助,请点赞关注,让我们在 Java 路上共同进步。
Java灵魂拷问13个为什么,你都会哪些?
|
存储 安全 Java
灵魂拷问:为什么 Java 字符串是不可变的?
在逛 programcreek 的时候,发现了一些精妙绝伦的主题。比如说:为什么 Java 字符串是不可变的?像这类灵魂拷问的主题,非常值得深思。 对于绝大多数的初级程序员来说,往往停留在“知其然不知其所以然”的层面上——会用,但要说底层的原理,可就只能挠挠头双手一摊一张问号脸了。 很长一段时间内,我也一直处于这种层面上。导致的局面就是,我在挖一些高深点的技术方案时,往往束手无策;在读一些高深点的技术文章时,往往理解不了作者在说什么。 借此机会,我就和大家一起,对“为什么 Java 字符串是不可变的”进行一次深入地研究。注意了,准备打怪升级了!
灵魂拷问:为什么 Java 字符串是不可变的?
|
存储 Java 程序员
灵魂拷问:Java 的 substring() 是如何工作的?
在逛 programcreek 的时候,我发现了一些小而精悍的主题。比如说:Java 的 substring() 方法是如何工作的?像这类灵魂拷问的主题,非常值得深入地研究一下。 另外,我想要告诉大家的是,研究的过程非常的有趣,就好像在迷宫里探宝一样,起初有些不知所措,但经过一番用心的摸索后,不但会找到宝藏,还会有一种茅塞顿开的感觉,非常棒。 对于绝大多数的初级程序员或者说不重视“内功”的老鸟来说,往往停留在“知其然不知其所以然”的层面上——会用,但要说底层的原理,可就只能挠挠头双手一摊一张问号脸了。
灵魂拷问:Java 的 substring() 是如何工作的?
|
存储 安全 Java
灵魂拷问:为什么 Java 字符串是不可变的?(2)
灵魂拷问:为什么 Java 字符串是不可变的?
86 0
灵魂拷问:为什么 Java 字符串是不可变的?(2)
|
存储 Java 程序员
灵魂拷问:为什么 Java 字符串是不可变的?(1)
灵魂拷问:为什么 Java 字符串是不可变的?
100 0
灵魂拷问:为什么 Java 字符串是不可变的?(1)
|
Java
灵魂拷问:创建 Java 字符串,用""还是构造函数
灵魂拷问:创建 Java 字符串,用""还是构造函数
114 0
灵魂拷问:创建 Java 字符串,用""还是构造函数
|
IDE Java 开发工具
【Java】面试官灵魂拷问:if语句执行完else语句真的不会再执行吗?
最近跳槽找工作的朋友确实不少,遇到的面试题也是千奇百怪,这不,一名读者面试时,被面试官问到了一个直击灵魂的问题:if 语句执行完else语句真的不会再执行吗?这个奇葩的问题把这名读者问倒了!
348 0
【Java】面试官灵魂拷问:if语句执行完else语句真的不会再执行吗?
|
IDE Java 开发工具
灵魂拷问:Java如何获取数组和字符串的长度?length还是length()?
灵魂拷问:Java如何获取数组和字符串的长度?length还是length()?
154 0
|
算法 Java
灵魂拷问:如何检查Java数组中是否包含某个值 ?(2)
灵魂拷问:如何检查Java数组中是否包含某个值 ?
154 0
|
Java 程序员
灵魂拷问:如何检查Java数组中是否包含某个值 ?(1)
灵魂拷问:如何检查Java数组中是否包含某个值 ?
168 0