JavaRush /Java 博客 /Random-ZH /Java 核心。面试问题,第 3 部分
Vadim625
第 27 级

Java 核心。面试问题,第 3 部分

已在 Random-ZH 群组中发布
在前两篇文章中,我们讨论了面试中最常被问到的一些重要问题。是时候继续讨论剩下的问题了。
Java 核心。 面试问题,第 3 - 1 部分

深复制和浅复制

原件的精确副本是它的克隆。在Java中,这意味着能够创建与原始对象具有相似结构的对象。该方法clone()提供了此功能。浅复制复制尽可能少的信息。默认情况下,Java 中的克隆是浅克隆,即 Object class不知道它正在复制的类的结构。克隆时,JVM 执行以下操作:
  1. 如果一个类仅具有基本类型的成员,则将创建该对象的全新副本,并返回对该对象的引用。
  2. 如果一个类不仅包含基本类型的成员,还包含任何其他类类型,则复制对这些类的对象的引用。因此,两个对象将具有相同的引用。
深度复制会复制一切。深复制是两个集合,其中一个复制原始集合的所有元素。我们希望制作一个副本,以便对副本的任何元素进行更改都不会影响原始集合。 深度克隆需要遵循以下规则:
  1. 无需单独复制原始数据;
  2. 原始类中的所有成员类都必须支持克隆。super.clone()对于每个类成员,在方法被重写时必须调用clone()
  3. 如果类的任何成员不支持克隆,则在克隆方法中,您需要创建该类的一个新实例,并将其每个成员及其所有属性复制到一个新的类对象,一次一个。
在此处了解有关克隆的更多信息

什么是同步?对象级锁定和类级锁定?

同步是指多线程。同步代码块一次只能由一个线程执行。Java 允许您同时处理多个线程。这可能会导致两个或多个线程想要访问同一字段。同步有助于避免内存资源使用不当时发生的内存错误。当一个方法被声明为同步时,线程将持有其监视器。如果此时另一个线程尝试访问同步方法,则该线程将被阻塞并等待监视器释放。Java 中的同步是通过特殊的synchronized关键字来完成的。您可以通过这种方式标记类中的各个块或方法。Synchronized 关键字不能与类变量或属性一起使用。 当您想要同步非静态方法或非静态代码块以便只有一个线程可以在给定的类实例上执行代码块时,对象级锁定是一种机制应该始终这样做以使类实例线程安全。 类级锁定可防止多个线程进入该类的所有可用实例的同步块。例如,如果 DemoClass 类有 100 个实例,则在给定时间只有 1 个线程能够使用其中一个变量来执行 demoMethod()。应始终这样做以确保静态线程安全。 在此处了解有关同步的更多信息。

sleep() 和 wait() 有什么区别?

Sleep()是一种用于将进程延迟几秒钟的方法。在 的情况下wait(),线程处于等待状态,直到我们调用notify()or方法notifyAll()。主要区别在于wait()它释放监视器锁而不sleep()释放锁。Wait()用于多线程应用程序,sleep()仅用于暂停线程执行。 Thread.sleep()将当前线程置于“不可运行”状态一段时间。该线程保存调用此方法之前监视器的状态。如果另一个线程调用t.interrupt(),“睡着”的线程将被唤醒。请注意,这sleep()是一个静态方法,这意味着它始终影响当前线程(执行该方法的线程sleep())。一个常见的错误是调用t.sleep()where tis another thread; 即使调用该方法的当前线程sleep()不是t线程。 Object.wait()将当前线程发送到“不可运行”状态一段时间,就像 一样sleep(),但有一些细微差别。Wait()调用对象,而不是线程;我们称这个对象为“锁对象”。在调用之前lock.wait(),当前线程必须与“锁对象”同步;wait()之后,它释放该锁,并将该线程添加到与该锁关联的“等待列表”中。稍后,另一个线程可以与同一个锁对象同步并调用lock.notify(). 该方法将“唤醒”仍在等待的原始线程。原则上,wait()/可以与/notify()进行比较,只是活动线程不需要直接指向休眠线程的指针,它只需要知道共享锁对象。 请阅读此处的详细差异。sleep()interrupt()

是否可以将 null 分配给引用变量?

你不能。在Java中,赋值运算符的左边必须是变量。“This”是一个特殊的关键字,它始终给出类的当前实例。这不仅仅是任何变量。同样,不能使用“super”关键字或任何其他类似关键字将 null 分配给变量。

&& 和 & 有什么区别?

&- 按位和&&- 逻辑。
  1. &评估操作的双方;
  2. &&评估操作的左侧。如果为真,它将继续评估右侧。
看看这里以获得更深入的理解。

如何重写 equals() 和 hach​​Code() 方法?

hashCode()equals()方法在类中定义Object,该类是 Java 对象的父类。因此,所有 Java 对象都会继承方法的默认实现。该方法hashCode()用于获取给定对象的唯一整数。当需要存储某个对象时,该整数用于确定该对象的位置,例如存储到HashTable。默认情况下,hashCode()返回integer存储对象的内存位置的地址表示形式。equls()顾名思义,该方法用于简单地测试两个对象是否相等。默认实现检查对象引用以查看它们是否相等。以下是重新加载这些方法的重要指南:
  1. hashCode()生成和时始终使用相同的对象属性equals()
  2. 对称。那些。x如果它对某些对象返回 true y x.equals(y),那么它y.equals(x)应该返回 true;
  3. 反身性。对于任何对象都x x.equals(x)必须返回true;
  4. 一致性。对于任何对象,如果比较中使用的信息没有改变,则返回相同的东西xy x.equals(y)
  5. 传递性。对于任何对象x,yz,如果x.equals(y)它返回 true 并且y.equals(z)返回 true,那么它x.equals(z)应该返回 true;
  6. 每当在应用程序执行期间对同一对象调用方法时,它都应该返回相同的数字,除非使用的信息发生变化。hashCode可以在不同的应用程序实例中为相同的对象返回不同的值;
  7. 如果两个对象相等,根据equals,那么它们hashCode必须返回相同的值;
  8. 相反的要求是可选的。两个不相等的对象可以返回相同的 hashCode。但是,为了提高性能,最好让不同的对象返回不同的代码。
在这里阅读有关这些方法的有趣事实。

告诉我们有关访问修饰符的信息

Java 类、字段、构造函数和方法可以具有四种不同的访问修饰符之一: private 如果方法或变量被标记为private,则只有同一类中的代码才能访问该变量或调用该方法。子类内的代码不能访问变量或方法,也不能从任何其他类访问它。private 访问修饰符最常用于构造函数、方法和变量。 default如果根本没有指定修饰符,则声明默认访问修饰符 。此修饰符意味着对给定类的字段、构造函数和方法的访问可以通过类本身内部的代码、同一包中的类内部的代码来获取。如果子类声明为default,则子类无法访问超类的方法和成员变量,除非子类与超类位于同一包中。 protected protected 修饰符的作用与default相同,只不过子类也可以访问超类的 protected 方法和变量。即使子类与超类不在同一个包中,这种说法也是正确的。 public public 访问修饰符意味着所有代码都可以访问该类、其变量、构造函数或方法,无论该代码位于何处。 Java 核心。 面试问题,第 3 - 2 部分

什么是垃圾收集器?我们可以打电话给他吗?

垃圾收集是许多现代编程语言中自动内存管理的一个功能,例如Java和.NET.Framework中的语言。使用垃圾收集的语言通常在 JVM 等虚拟机中解释垃圾收集。垃圾收集有两个目的:应释放任何未使用的内存,如果程序仍在使用内存,则不应释放内存。您可以手动运行垃圾收集吗?不,System.gc()它为您提供尽可能多的访问权限。最好的选择是调用该方法System.gc(),该方法将提示垃圾收集器它需要运行。由于垃圾收集器是不确定的,因此无法立即运行它。另外,根据文档,OutOfMemoryError如果虚拟机在完全垃圾收集后未能释放内存,则不会转发。 在此处了解有关垃圾收集器的更多信息。

原生关键字是什么意思?详细解释

native关键字用于指示该方法是用Java文件以外的编程语言实现的。 过去曾使用本机方法。在当前版本的 Java 中,很少需要这样做。目前,在以下情况下需要本机方法:
  1. 您必须从 Java 调用用另一种语言编写的库。
  2. 您需要访问只能使用另一种语言(通常是 C)访问的系统或硬件资源。事实上,很多与真实计算机交互的系统函数(比如磁盘或者网络数据)只能通过本机方法来调用。
使用本机方法库的缺点也很明显:
  1. JNI/JNA 可能会破坏 JVM 的稳定性,尤其是当您尝试执行复杂的操作时。如果您的本机方法出现错误,则 JVM 可能会崩溃。此外,如果从多个线程调用本机方法,可能会发生不好的事情。等等。
  2. 使用本机代码调试程序更加困难。
  3. 本机代码需要单独构建框架,这可能会在移植到其他平台时产生问题。

什么是序列化?

在计算机科学中,在数据存储和传输的背景下,序列化是将数据结构或对象的状态转换为可以存储并随后在另一个计算环境中检索的格式的过程。收到一系列位后,它们会根据序列化格式重新计算,并可用于创建原始对象的语义相同的克隆。Java提供了自动序列化,这需要对象实现接口java.io.Serializable。接口实现将类标记为“可序列化”。java.io.Serialized 接口没有序列化方法,但可序列化类可以选择定义将在序列化/反序列化过程中调用的方法。对类进行更改时,您需要考虑哪些类与序列化兼容,哪些不兼容。您可以在此处阅读完整说明。我将给出最重要的一点: 不兼容的更改:
  1. 删除一个字段;
  2. 在层次结构中向上或向下移动类;
  3. 将非静态字段更改为静态字段或将非瞬态字段更改为瞬态字段;
  4. 更改声明的原始数据类型;
  5. 更改方法WriteObjectReadObject使它们不再默认写入或读取字段;
  6. 更改类别SerializableExternalizable反之亦然;
  7. 将枚举类更改为非枚举类,反之亦然;
  8. 删除SerializableExternalizable
  9. 向类添加writeReplace方法。readResolve
兼容的更改:
  1. 添加字段;
  2. 添加/删除类;
  3. 添加方法WriteObject/ReadObject[方法defaultReadObjectdefaultWriteObject必须在开头调用];
  4. 去除方法WriteObject/ReadObject
  5. 添加java.io.Serializable;
  6. 更改现场访问权限;
  7. 将静态字段更改为非静态字段或将瞬态字段更改为非瞬态字段
前面部分的链接: Java Core。面试问题,第 1 部分 Java 核心。面试问题,第 2 部分 原创文章 学习愉快!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION