在面试时,即使是经验丰富的开发人员,也可能会发现这是一些很棘手的问题:
1、Java中“transient”关键字的用途是什么?如何才能实现这一目标?
在 Java 中,“transient”关键字用于指示类的特定字段不应包含在对象的序列化形式中。
这意味着当对象被序列化时,其状态将转换为可以写入文件或通过网络发送的字节序列。
通过将字段标记为瞬态,您就告诉 Java 在序列化对象时不应包含该字段的值。
您可能想要使用“transient”关键字的原因有多种。例如,您可能有一个包含临时值的字段,在序列化对象时不需要保留该临时值。或者,可能有一个字段包含敏感数据,出于安全原因,这些数据不应包含在对象的序列化形式中。
public class MyClass implements Serializable { private int myInt; private transient String myTransientString; // 构造器、getters & setters // 其他方法…… }
在这个例子中,字段“myTransientString”被标记为transient,这意味着当MyClass的实例被序列化时,它的值将不会被包含在内。
2、你能解释一下“继承”和“组合”有何不同吗?举例说明。
继承和组合是在面向对象编程中创建类之间关系的两种基本方法。这两种方法都支持代码重用和抽象,但它们的实现以及在类之间创建的关系类型有所不同。
以下是每种方法的简要概述:
- 继承:这是一种通过从现有类派生属性和特征来创建新类的机制。新类(称为子类或派生类)继承现有类(称为超类或基类)的方法和字段。子类还可以重写超类的方法以提供自己的实现。继承在超类和子类之间创建了“is-a”关系。
- 组合:它是一种机制,其中一个类包含另一个类的一个或多个实例作为其字段。所包含的类称为组件或零件类。包含组件类的类称为容器或整个类。该组合在容器类和组件类之间创建了“has-a”关系。
在图中,我们有两个类:“Vehicle”和“Engine”。 Vehicle 类可以使用继承或组合来合并 Engine 类。
- 继承示例:Vehicle类扩展了Engine类,这意味着它继承了Engine类的所有字段和方法。这会在 Vehicle 和 Engine 类之间创建“is-a”关系,其中 Vehicle “是一种”类型的 Engine。
public class Vehicle extends Engine { }
- 组合示例:Vehicle 类包含 Engine 类的一个实例作为其字段。这在车辆和引擎类之间创建了“有一个”关系,其中车辆“有一个”引擎。
public class Vehicle { private Engine engine; public Vehicle(Engine engine) { this.engine = engine; } ... }
一般来说,当类之间存在明确的“is-a”关系,并且子类可以被视为超类的专门版本时,继承更合适。当类之间存在“has-a”关系时,以及当容器类需要使用或管理另一个类的一个或多个实例时,组合更为合适。
3、你能解释一下Java中HashSet和TreeSet的区别吗?另外,解释一下数据是如何在内部存储的。
假设我们有以下整数数据:{7, 3, 9, 4, 1, 8}。
- 对于HashSet,数据内部存储在哈希表中。哈希表使用每个元素的 hashCode() 方法来确定应存储该元素的唯一索引。
在上面的示例图中,哈希表有八个桶,标记为“51”到“56”。每个存储桶都是一个集合,其中包含具有映射到该存储桶的哈希码的元素。例如,索引“53”处的集合包含元素 3 和 4,它们的哈希码都是 [197]。索引“56”处的集合包含元素 7、8 和 9,它们都有哈希码 [195]。
- 对于TreeSet,数据内部存储在红黑树中。树根据元素的自然顺序或传递给 TreeSet 构造函数的自定义比较器定义的顺序进行排序。以下是如何将数据存储在红黑树中的示例:
在这个例子中,红黑树有六个节点,每个节点包含元素{1,3,4,7,8,9}之一。节点颜色为红色或黑色,红色节点表示发生了违反红黑树属性的情况。元素在树中按排序顺序存储,较小的元素位于左侧,较大的元素位于右侧。例如,元素1是最小的元素,存储在最左边的叶子节点,而元素9是最大的元素,存储在最右边的叶子节点。