两者的主要区别 fail-fast and fail-safe 迭代器是指是否可以修改集合 while it is being iterated. Fail-safe iterators allow this; fail-fast iterators do not.
Fail-fast 迭代器直接作用于集合本身. During iteration, 快速失败迭代器一旦意识到集合被修改就会失败.e.(当意识到成员已被添加、修改或删除时),并抛出一个 并发修改异常. Some examples include ArrayList, HashSet, and HashMap (most JDK1.4 .集合的实现是快速故障的).
ArrayList and Vector 每个都使用一个数组来存储列表中的元素. As a result, 当一个元素被插入(或从)列表中间移除时, 接下来的元素都必须进行相应的移动. Vector 是同步的,所以如果线程安全的实现是 not 需要时,建议使用 ArrayList rather than Vector.
LinkedList另一方面,它是使用双重链表实现的. As a result, 插入或删除元素只需要更新被插入或删除元素前后的链接.
However, 值得注意的是,如果性能是如此关键, 最好使用数组并自己管理它, 或者使用一个高性能的第三方软件包,比如 Trove or HPPC.
3.
为什么存储敏感数据(如密码、社会保险号等)会更安全.),而不是在字符串中?
View answer
In Java, Strings are immutable 并存储在String池中. What this means is that, once a String is created, 它会一直留在内存池中,直到被垃圾收集. 因此,即使在处理完字符串值(e.g., the password), 此后,它在内存中可用一段不确定的时间, 直到你无法真正控制的垃圾收集. Therefore, 任何有权访问内存转储的人都有可能提取敏感数据并加以利用.
In contrast, 如果你使用可变对象,比如字符数组, for example, to store the value, 您可以将其设置为空白,一旦您完成它,它将不再保留在内存中.
A single ThreadLocal 实例可以为每个线程独立存储不同的值. 的每个线程 get() or set() method of a ThreadLocal 实例正在访问它自己的、独立初始化的变量副本. ThreadLocal 实例通常是类中的私有静态字段,它们希望将状态与线程(例如线程)相关联.g.(用户ID或事务ID). The example below, from the ThreadLocal Javadoc,生成每个线程的本地唯一标识符. 线程的id是在它第一次调用时分配的 ThreadId.get() 在随后的呼叫中保持不变.
public class ThreadId {
//下一个要分配的线程ID
private static final AtomicInteger nextId = new AtomicInteger(0);
//包含每个线程ID的线程局部变量
private static final ThreadLocal threadId =
new ThreadLocal() {
@覆盖受保护的整数initialValue() {
return nextId.getAndIncrement();
}
};
//返回当前线程的唯一ID,必要时赋值
public static int get() {
return threadId.get();
}
}
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, 它的线程本地实例的所有副本都服从于垃圾收集(除非存在对这些副本的其他引用)。.
5.
What is the volatile keyword? 你会怎么用,为什么用?
View answer
在Java中,每个线程都有自己的堆栈,包括它可以访问的变量的副本.
当创建线程时,它将所有可访问变量的值复制到自己的堆栈中. The volatile 关键字基本上对JVM说"警告,这个变量可能在另一个线程中被修改".
In Java 5 or later, volatile 读和写建立一个 happens-before 关系,很像获取和释放互斥锁.
Using volatile 可能比锁快,但在某些情况下不起作用. The range of situations in which volatile is effective was expanded in Java 5; in particular, double-checked locking now works correctly.
的一个常见示例 volatile 是否使用标志来终止线程. If you’ve started a thread, 您希望能够从另一个线程安全地中断它, 您可以让线程定期检查一个标志(例如.e.要阻止它,就把旗子设为 true). 通过使旗子易挥发, 您可以确保检查其值的线程将看到它已被设置为 true 甚至不需要使用同步块. For example:
类Foo扩展线程
Private volatile Boolean close = false;
public void run() {
while(!close) {
// do work
}
}
public void close() {
close = true;
// interrupt here if needed
}
}
6.
Compare the sleep() and wait() 方法,包括何时以及为什么使用一个方法与. the other.
View answer
sleep() 是一个阻塞操作,保持在监视器/锁定的共享对象的指定毫秒数.
wait(),另一方面,简单 pauses the thread until either (a)指定的毫秒数已经过去 or (b)它从另一个线程接收到所需的通知(以先到者为准); without 保持对共享对象的监视器/锁的持有.
Is a finally 类抛出异常时执行的块 try block that does not have a catch block, and if so, when?
View answer
A finally 块执行,即使抛出异常或将异常传播到调用代码块.
Example:
公共类FinallyExecution {
public static void main(String[] args) {
try{
FinallyExecution.divide(100, 0);}
finally{
System.out.println("finally in main");
}
}
Public static void divide(int n, int div){
try{
int ans = n/div; }
finally{
System.out.Println(“finally of divide”);
}
}
}
输出可以有所不同,可以是:
finally of divide
finally in main
线程"main"中的异常.lang.arithmelceexception: / by 0
at exceptions.FinallyExecution.divide(FinallyExecution.java:20)
at exceptions.FinallyExecution.main(FinallyExecution.java:9)
…or…
线程"main"中的异常.lang.arithmelceexception: / by 0
at exceptions.FinallyExecution.divide(FinallyExecution.java:20)
at exceptions.FinallyExecution.main(FinallyExecution.java:9)
finally of divide
finally in main
公共抽象类Widget {
private final int cachedWidth;
private final int cachedHeight;
public Widget() {
this.cachedWidth = width();
this.cachedHeight = height();
}
受保护的抽象int宽度();
受保护抽象int height();
}
这似乎是一个抽象Widget的良好开端:它允许子类填充 width and height,并缓存它们的初始值. 然而,看看当你指定一个典型的子类实现,像这样:
公共类SquareWidget扩展Widget {
private final int size;
公共SquareWidget(int size) {
this.size = size;
}
@Override
protected int width() {
return size;
}
@Override
protected int height() {
return size;
}
}
现在我们引入了一个微妙的bug: Widget.cachedWidth and Widget.cachedHeight will always be zero for SquareWidget instances! This is because the this.size = size assignment occurs after the Widget constructor runs.
避免在抽象类的构造函数中调用抽象方法, 因为它限制了这些抽象方法的实现方式.
13.
泛型类型参数的变化是多么大啊? Java在这方面给了你多大的控制权?
View answer
Java的泛型类型参数是 invariant. 这意味着对于任何不同的类型 A and B, G 不是子类型还是超类型 G. As a real world example, List 不是超类型还是子类型 List. So even though String extends (i.e. is a subtype of) Object,以下两个赋值将无法编译:
List strings = Arrays.
Java确实以形式提供了一些控制 use-site variance. 在个别方法上,我们可以使用 ? extends Type to create a covariant parameter. Here’s an example:
public double sum(List extends Number> numbers) {
double sum = 0;
for (Number Number: Number) {
sum += number.doubleValue();
}
return sum;
}
List longs = Arrays.asList(42L, 128L, -10L);
双sumOfLongs = sum(长);
Even though longs is a List and not List, it can be passed to sum.
Similarly, ? super Type lets a method parameter be contravariant. 考虑一个带回调参数的函数:
public void forEachNumber(Callback super Number> callback) {
callback.call(50.0f);
callback.call(123123);
callback.call((short) 99);
}
forEachNumber allows Callback to be a subtype of Callback ,这意味着任何处理超类型的回调 Number will do:
public static final Map FEATURE_FLAGS;
static {
Map flags = new HashMap<>();
flags.把(“挫伤用户”,假);
flags.把(“reticulate-splines”,真正的);
flags.put(...);
FEATURE_FLAGS =集合.unmodifiableMap(flags);
}
Within the same class, 您可以重复声明静态字段并立即初始化它的这种模式, 因为允许使用多个静态初始化式.
15.
If one needs a Set,你如何选择 HashSet vs. TreeSet?
View answer
At first glance, HashSet 几乎在各方面都很优秀。 add, remove and contains, vs. O(log(N)) for TreeSet.
However, TreeSet 当您希望维护插入元素的顺序或查询集合内的一系列元素时,是必不可少的.
Consider a Set of timestamped Event objects. They could be stored in a HashSet, with equals and hashCode based on that timestamp. 这是一种高效的存储方式,允许根据特定的时间戳查找事件, 但是你如何得到在某一天发生的所有事件? 这需要O(n)遍历 HashSet,但它只是一个O(log(n))的运算 TreeSet using the tailSet method:
public class Event implements Comparable {
私有最终长时间戳;
公共事件(长时间戳){
this.timestamp = timestamp;
}
@Override public int compareTo(事件){
return Long.compare(this.timestamp, that.timestamp);
}
}
...
SortedSet events = new TreeSet<>();
events.addAll(...); // events come in
//今天发生的所有事件
long midnightToday = ...;
events.tailSet(新事件(midnightToday));
If Event 碰巧是一个我们不能扩展或者没有实现的类 Comparable, TreeSet 允许我们通过我们自己的 Comparator:
SortedSet events = new TreeSet<>(
(left, right) -> Long.compare(left.timestamp, right.timestamp));
Generally speaking, TreeSet 当顺序很重要,当读取与增加的写入成本相平衡时,这是一个好的选择吗.
However, stop matches the signature of Runnable.run (void 返回类型,没有参数),因此我们可以引入一个方法引用 stop method of that specific SomeBusyService instance:
onShutdown(service::stop);
这是简洁的(与冗长的代码相反),并清楚地传达了正在发生的事情.
方法引用不需要绑定到特定的实例, either; one can also use a method reference to an arbitrary object, which is useful in Stream operations. 例如,假设我们有a Person 类,并且只需要一组人的小写名字:
List people = ...
List names = people.stream()
.map(Person::getName)
.map(String::toLowerCase)
.collect(toList());
Person Person = new Person("Doug", "Sparling", 31);
Map values = new HashMap<>();
for (Field field : person.getClass().getDeclaredFields()) {
values.put(field.getName(), field.get(person));
}
//打印{firstName=Doug, lastName=Sparling, age=31}
System.out.println(values);
乍一看,这个设计看起来不错 WidgetParserFactory 用嵌套类隐藏解析器的实现细节 WidgetParserImpl. However, WidgetParserImpl is not static, and so if WidgetParserFactory 后立即丢弃 WidgetParser 创建时,工厂将泄漏,以及它保存的所有引用.
两者的区别是什么 String s = "Test" and 字符串s = new String("Test")? Which is better and why?
View answer
In general, String s = "Test" 使用效率比 字符串s = new String("Test").
In the case of String s = "Test",将在String池中创建一个值为“Test”的String. 如果创建了另一个具有相同值的String (e.g., String s2 = "Test"),它将引用String池中的同一对象.
However, if you use 字符串s = new String("Test"), 除了在String池中创建值为“Test”的String之外, 然后将该String对象传递给String对象的构造函数(i.e., new String("Test")),并使用该值创建另一个String对象(不在String池中). 因此,每次这样的调用都将创建一个额外的String对象(例如.g., String s2 = new String("Test") 会创建一个附加字符串对象吗, 而不仅仅是从String池中重用同一个String对象).
面试不仅仅是棘手的技术问题, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, 回答所有问题也不能保证成为A级考生. At the end of the day, 招聘仍然是一门艺术,一门科学,需要大量的工作.