最近有一个好消息,小灰的一位读者去某国企面试Java开发岗位,顺利上岸了。
为了帮助到大家,他分享出面试中遇到的几个题目,涉及到Java基础、多线程、MySQL。题目都是选择题,难度中等。
大家在阅读这些题目的时候,建议先自己思考一遍,再去查看后面的答案。
题目一
考虑下面的Java代码片段:
public class TestThread {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready) Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
在没有其他同步机制的情况下,运行上述程序可能会产生哪些结果?
A. 程序无限循环,不输出任何东西。
B. 输出"42"。
C. 不保证总能输出"42",有时可能不输出任何东西。
D. 抛出异常。
正确答案:C. 不保证总能输出"42",有时可能不输出任何东西。
为什么呢?
这个问题深入探讨了Java内存模型(JMM)及其对多线程程序可见性和重排序的影响。在多线程环境中,由于编译器优化、处理器优化以及运行时的JIT编译优化,操作的执行顺序可能会与代码中的顺序不同。这种优化可能会影响到变量的可见性和程序的执行顺序。
A. 程序无限循环,不输出任何东西。 这是一个可能的结果,但并不是因为程序实际上会无限循环,而是因为ready
变量的更新可能对启动的ReaderThread
线程不可见。如果主线程对ready
的更新对ReaderThread
不可见,那么ReaderThread
可能会永远在while
循环中等待ready
变为true
。
B. 输出"42"。 这是在ready
变量的更新对另一个线程可见,并且这个更新在number
变量被设置为42之后被检测到的情况下可能发生的结果。
C. 不保证总能输出"42",有时可能不输出任何东西。 这是最准确的描述。由于缺乏同步措施,ready
的更改可能在另一线程中不可见,或者由于指令重排序,number
的赋值操作和ready
的赋值操作的顺序可能在执行时被颠倒。因此,即使ready
被设置为true
,ReaderThread
看到这个变化时number
可能还没有被设置为42。
D. 抛出异常。这个选项不正确,因为部分异常是在并发修改某些集合时可能抛出的,而与本题讨论的内存可见性和指令重排序无关。
因此,由于Java内存模型的特性,最合理的答案是C。在实际应用中,为了确保多线程之间正确的内存可见性,通常需要使用同步机制(如volatile
关键字、synchronized
块或者java.util.concurrent
包中的类)来防止这种类型的问题。
题目二
考虑以下Java类和接口:
interface Speakable {
String speak();
}
class Animal implements Speakable {
public String speak() {
return "I am an animal";
}
}
class Dog extends Animal {
public String speak() {
return "Woof";
}
}
class Cat extends Animal {
public String speak() {
return "Meow";
}
public String purr() {
return "Purr";
}
}
给定以下代码片段:
Animal myDog = new Dog();
Animal myCat = new Cat();
Speakable something = // 填空
如果我们希望something.speak()
调用返回"Purr"
,应该如何实例化something
变量?选择正确的选项。
A. new Dog()
B. new Cat()
C. myDog
D. myCat
正确答案:B. new Cat()
为什么呢?
本题考察了Java中多态性的理解和应用。多态允许我们通过父类引用来指向子类对象,实现在运行时决定具体调用哪个类的方法,这是面向对象编程中的一个核心概念。
选项解析:
A. new Dog()
- 实例化一个Dog
对象并不会使something.speak()
返回"Purr"
,因为Dog
类的speak
方法被重写为返回"Woof"
。
B. new Cat()
- 这是正确的选项。尽管Cat
类重写了speak
方法返回"Meow"
,但特定于Cat
的方法purr
返回"Purr"
。
题目中的要求可能有些误导,因为Speakable
接口或Animal
类中都没有purr
方法。然而,既然问题是要speak()
返回"Purr"
,实际上只有通过具体地调用Cat
类的purr
方法才能实现,这意味着应该直接创建一个Cat
对象。
但根据题目描述,这里可能是个陷阱,因为Speakable
或Animal
类型的引用不能直接调用purr()
方法。因此,若真的需要something.speak()
返回"Purr"
,选项的表述可能不完全准确,但就题目要求而言,只有Cat
实例能近似满足需求,尽管需要调用purr
而非speak
。
C. myDog
- 这个选项引用了一个Dog
类型的对象,这会导致speak
方法返回"Woof"
。
D. myCat
- 尽管这个选项引用了一个Cat
对象,根据Cat
类的实现,speak
方法将返回"Meow"
而非"Purr"
。正确的答案取决于对问题的理解,但按照字面意义,没有一个选项能直接使speak()
返回"Purr"
,因为purr
是Cat
类特有的方法,而不是Speakable
接口或Animal
类的一部分。正确的做法是直接调用Cat
类的实例上的purr()
方法。然而,考虑到题目的意图,最接近的选项是B,因为只有Cat
类与"Purr"
相关联。
因此,正确答案是B,即使这个答案在技术上并不完美地符合题目的要求。这个问题强调了在设计面向对象系统时清晰和准确地理解类之间关系的重要性,以及多态在动态方法调用中的作用。
题目三、
假设你有一个包含数百万条记录的MySQL数据库表orders
。表结构如下:
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(10) NOT NULL
);
你需要编写一个查询来找出过去30天内每个客户的平均订单金额,但是你注意到查询的响应时间很慢。以下是你编写的查询:
SELECT customer_id, AVG(amount) AS avg_amount
FROM orders
WHERE order_date > DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY customer_id;
你正在考虑对orders表做哪些优化,以提高查询性能。以下哪项措施是最有效的?
A. 在order_id
列上增加一个索引
B. 在order_date
列上增加一个索引
C. 在amount
列上增加一个索引
D. 在status
列上增加一个索引
第三道题的正确答案,小灰暂时先不公布,有兴趣的小伙伴可以在留言区给出你认为的答案选项以及这样选择的原因。
最后,在2024年的金三银四,祝愿大家都能找到好的工作!
欢迎关注公众号程序员小灰,收看更多有趣又有用的内容: