随着游戏产业的快速发展,游戏大厂对于开发人员的要求越来越高,特别是在Java技术的掌握上。从多线程编程到JVM调优,再到设计模式的应用,Java开发者需要在实际工作中具备深厚的技术基础和优化能力。本文将详细解答一些常见的Java面试问题,帮助你准备面试。
一、Java创建线程的方式  编辑
编辑
在Java中,创建线程的方式有两种: 编辑
编辑
- 继承Thread类- 通过继承Thread类并重写run()方法来实现。
- 优点:简单、直观。
- 缺点:Java是单继承的,继承Thread类会导致无法继承其他类。
 
- 通过继承
java class MyThread extends Thread { public void run() { System.out.println("线程正在运行"); } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } } 
- 实现Runnable接口- 实现Runnable接口的run()方法,然后通过Thread类来启动线程。
- 优点:能够实现多继承,灵活性更高。
- 缺点:代码稍微复杂,但对资源管理更好。
 
- 实现
java class MyRunnable implements Runnable { public void run() { System.out.println("线程正在运行"); } } public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } } 
二、线程的状态
Java中的线程有七种状态: 编辑
编辑
- 新建(New) :线程对象被创建,但还没有调用start()方法。
- 就绪(Runnable):线程已被启动,但等待CPU调度,处于准备执行的状态。
- 运行(Running) :线程正在执行run()方法。
- 阻塞(Blocked):线程被阻塞,通常是因为等待获取锁。
- 等待(Waiting) :线程进入等待状态,通常是调用Object.wait()或Thread.join()等方法。
- 超时等待(Timed Waiting) :线程进入等待状态并设定了时间限制,比如Thread.sleep()或Thread.join(long millis)。
- 终止(Terminated):线程执行完毕或被中断,进入终止状态。
三、单例模式  编辑
编辑
单例模式(Singleton Pattern)是设计模式中的一种,它确保一个类只有一个实例,并提供全局访问点。Java中实现单例模式有几种常见的方式:
- 懒汉式(线程不安全)
 ```java
 public class Singleton {
 private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
- 懒汉式(线程安全)
 ```java
 public class Singleton {
 private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
- 饿汉式(线程安全)
 ```java
 public class Singleton {
 private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
```
四、类加载过程
Java的类加载过程分为以下几个步骤: 编辑
编辑
- 加载(Loading):类加载器将类的字节码文件加载到内存中。
- 链接(Linking) :将字节码文件中的符号引用转换为直接引用。链接分为三个步骤:
- 验证 :确保字节码文件符合JVM规范。
- 准备 :为类变量分配内存,并设置默认值。
- 解析:将符号引用转换为实际的内存地址或方法地址。
 
- 初始化(Initialization) :执行类构造器<clinit>方法,初始化类的静态变量。
五、双亲委派模型
Java的类加载器采用双亲委派模型 ,即: 编辑
编辑
- 每个类加载器在加载类时,首先会委派给父类加载器。
- 如果父类加载器能够加载该类,则直接返回;否则,才会自己加载该类。
这种设计的好处是避免了类的重复加载,确保了Java类的统一性和一致性。
六、Java会出现内存泄露吗?
Java虽然有垃圾回收机制,但仍然可能出现内存泄漏,特别是以下情况:
- 长生命周期对象持有短生命周期对象的引用:如使用静态变量或单例模式持有某些对象引用,使得这些对象无法被回收。
- 集合类未及时清理 :比如HashMap、ArrayList等集合类的元素未被及时清理。
- Listener或Callback未解除注册:导致对象无法被垃圾回收。
七、如何定位和解决OOM  编辑
编辑
**OOM(Out Of Memory)**通常是由于内存溢出造成的,常见的解决方法包括:
- 堆内存溢出 :增加JVM的堆内存配置(如-Xmx和-Xms),检查是否有内存泄漏。
- MetaSpace溢出 :增加-XX:MaxMetaspaceSize来解决。
- 栈内存溢出 :调整栈大小,增加-Xss。
- 内存泄漏排查 :使用工具如VisualVM、**MAT(Memory Analyzer Tool)**来分析堆转储。
八、Java的GC中什么场景下使用CMS和G1
- CMS(Concurrent Mark-Sweep) :适合对低延迟要求较高的应用,如游戏、金融等系统。CMS通过并发标记和清除来尽量减少停顿时间。
- G1 GC :适合大内存应用,特别是需要高吞吐量的应用。它将堆划分为多个区域,通过并行和并发的方式进行垃圾回收。
九、HashMap
HashMap是Java中常用的键值对集合,其底层是通过哈希表实现的。常见特性:
- 线程不安全:多线程环境下可能出现并发问题。
- 哈希冲突 :当不同的键有相同的哈希值时,HashMap通过链表或红黑树解决冲突。
- 负载因子:负载因子控制HashMap扩容的频率,默认是0.75。
十、JVM参数调优  编辑
编辑
通过调整JVM参数,可以优化内存管理和垃圾回收的性能:
- 堆内存调整 :-Xms设置初始堆大小,-Xmx设置最大堆大小。
- 垃圾回收器选择 :-XX:+UseG1GC、-XX:+UseParallelGC等。
- 永久代/元空间大小 :-XX:MaxMetaspaceSize。
- GC日志 :-Xlog:gc*打印GC日志,分析性能瓶颈。 编辑 编辑
十一、Minor GC和Full GC
- Minor GC:发生在年轻代,通常不需要暂停太长时间。其目的是回收年轻代的垃圾。
- Full GC:发生在整个堆(年轻代、老年代)上,通常需要较长的停顿时间。主要用于回收老年代垃圾和永久代/元空间的垃圾。
十二、线程池
Java的线程池是通过ExecutorService接口实现的,常见的线程池实现包括:
- FixedThreadPool:固定数量的线程池,适合任务数量已知的场景。
- CachedThreadPool:根据需要创建线程,适合任务量不确定且任务执行时间较短的场景。
- ScheduledThreadPool:支持定时任务和周期性任务。
总结
Java面试不仅考察基础语法,还要深入理解Java虚拟机(JVM)、内存管理、并发编程、设计模式等核心技术。在游戏大厂的面试中,能够展示对这些技术的深刻理解和应用,才能更好地展现自己在高性能系统开发中的优势。