互联网大厂面试:Java核心知识、框架与中间件大考验
王铁牛怀揣着紧张又期待的心情,走进了互联网大厂的面试间。严肃的面试官正襟危坐,一场对Java知识的严格考验即将拉开帷幕。
第一轮提问
- 面试官:我们先从基础的Java核心知识开始。你能说说Java中多态的实现方式有哪些吗?
- 王铁牛:多态的实现方式主要有两种,一种是方法重载,在一个类中可以有多个方法名相同但参数列表不同的方法;另一种是方法重写,子类重写父类的方法。
- 面试官:回答得不错。那在Java中,String类为什么被设计成不可变的呢?
- 王铁牛:因为不可变有很多好处,比如可以保证字符串的安全性,在多线程环境下不用考虑线程安全问题;还可以实现字符串常量池,提高性能。
- 面试官:很好。那你说说Java中的异常处理机制是怎样的?
- 王铁牛:Java中异常分为受检查异常和不受检查异常。对于受检查异常,必须进行处理,要么使用try-catch块捕获,要么使用throws关键字声明抛出;对于不受检查异常,不强制要求处理。
第二轮提问
- 面试官:接下来我们聊聊JUC和多线程相关的内容。你能说下线程的几种状态吗?
- 王铁牛:线程有新建、就绪、运行、阻塞和死亡这几种状态。新建就是刚创建线程对象;就绪是线程具备了运行条件但还没分配到CPU时间片;运行就是正在执行;阻塞是由于某些原因暂停执行;死亡就是线程执行完毕。
- 面试官:不错。那在多线程环境下,如何保证数据的一致性呢?
- 王铁牛:可以使用synchronized关键字来实现同步,它可以修饰方法或者代码块,保证同一时刻只有一个线程可以访问。
- 面试官:很好。那你对线程池了解多少,线程池有哪些常用的创建方式?
- 王铁牛:线程池可以使用Executors工具类来创建,比如newFixedThreadPool可以创建固定大小的线程池,newCachedThreadPool可以创建可缓存的线程池。
第三轮提问
- 面试官:现在我们谈谈框架相关的知识。你在项目中使用过Spring框架,那Spring的核心特性有哪些?
- 王铁牛:Spring的核心特性有依赖注入和面向切面编程。依赖注入可以降低代码的耦合度,让对象之间的依赖关系由Spring容器来管理;面向切面编程可以在不修改原有代码的基础上增加额外的功能。
- 面试官:那Spring Boot和Spring有什么区别呢?
- 王铁牛:嗯……这个……Spring Boot是在Spring基础上发展来的,它简化了Spring的配置,让开发更方便,好像还内置了服务器什么的。
- 面试官:那MyBatis是如何实现数据库操作的呢?
- 王铁牛:呃……就是通过一些配置文件,还有映射文件,然后和数据库交互,具体怎么交互我有点说不太清楚。
面试接近尾声,面试官看着王铁牛,严肃地说:“今天的面试就到这里了,你回家等通知吧。在面试中,你对一些基础的Java核心知识和多线程方面的内容回答得不错,展现出了一定的知识储备。不过,在框架相关的一些细节问题上,回答得还不够深入和清晰,后续还需要加强这方面的学习。我们会综合考虑你的表现,之后会给你反馈,请耐心等待。”
问题答案
- Java中多态的实现方式有哪些
- 方法重载:在同一个类中,方法名相同,但参数列表不同(参数的类型、个数、顺序不同)。编译器会根据调用方法时传入的实际参数来决定调用哪个方法。例如:
public class OverloadExample {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
- **方法重写**:子类继承父类后,重写父类中的方法。要求方法名、参数列表和返回值类型都相同,访问权限不能比父类的更严格。例如:
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
- Java中String类为什么被设计成不可变的
- 安全性:在多线程环境下,不可变的String对象是线程安全的,多个线程可以同时访问同一个String对象,不用担心数据被修改。例如,在网络编程中,URL、文件名等通常使用String类型,如果String是可变的,可能会导致安全问题。
- 字符串常量池:Java中的字符串常量池是为了提高性能而设计的。当创建一个String对象时,如果常量池中已经存在相同内容的字符串,就会直接引用常量池中的对象,而不是创建新的对象。例如:
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
- Java中的异常处理机制是怎样的
- 异常分类:Java中的异常分为受检查异常(Checked Exception)和不受检查异常(Unchecked Exception)。受检查异常是指在编译时必须进行处理的异常,如IOException、SQLException等;不受检查异常是指在编译时不强制要求处理的异常,如RuntimeException及其子类。
- 异常处理方式:
- try-catch块:用于捕获和处理异常。例如:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("除数不能为零:" + e.getMessage());
}
- **throws关键字**:用于声明方法可能抛出的异常,由调用该方法的代码来处理。例如:
public void readFile() throws IOException {
// 读取文件的代码
}
- 线程的几种状态
- 新建(New):当创建一个Thread对象时,线程处于新建状态。例如:
Thread t = new Thread();
- **就绪(Runnable)**:调用线程的start()方法后,线程进入就绪状态,等待获取CPU时间片。例如:
t.start();
- **运行(Running)**:线程获得CPU时间片后,开始执行run()方法,进入运行状态。
- **阻塞(Blocked)**:线程由于某些原因暂停执行,进入阻塞状态。例如,线程调用了sleep()方法、等待输入输出等。
- **死亡(Terminated)**:线程执行完run()方法或者因为异常退出,进入死亡状态。
5. 在多线程环境下,如何保证数据的一致性 - synchronized关键字:可以修饰方法或者代码块,保证同一时刻只有一个线程可以访问被修饰的方法或代码块。例如:
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
- **Lock接口**:Java提供了Lock接口及其实现类(如ReentrantLock)来实现同步。与synchronized关键字相比,Lock接口提供了更灵活的锁机制。例如:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
- 线程池有哪些常用的创建方式
- newFixedThreadPool:创建一个固定大小的线程池。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(new Task());
}
executor.shutdown();
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
}
- **newCachedThreadPool**:创建一个可缓存的线程池,如果线程池中的线程空闲时间过长,会被回收;如果有新的任务提交,会创建新的线程。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executor.execute(new Task());
}
executor.shutdown();
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
}
- Spring的核心特性有哪些
- 依赖注入(Dependency Injection,DI):是指对象之间的依赖关系由Spring容器来管理,而不是由对象自己创建。通过依赖注入,可以降低代码的耦合度,提高代码的可维护性和可测试性。例如:
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void addUser() {
userDao.addUser();
}
}
- **面向切面编程(Aspect-Oriented Programming,AOP)**:是指在不修改原有代码的基础上,通过切面来增加额外的功能,如日志记录、事务管理等。例如,使用Spring AOP可以在方法执行前后添加日志记录:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("Method " + joinPoint.getSignature().getName() + " executed");
}
}
- Spring Boot和Spring有什么区别
- 配置简化:Spring需要大量的XML配置文件或者Java配置类来进行配置,而Spring Boot通过自动配置和约定优于配置的原则,大大简化了配置过程。例如,在Spring Boot中,只需要添加相关的依赖,就可以自动配置好数据库连接、Web服务器等。
- 内置服务器:Spring Boot内置了Tomcat、Jetty等服务器,不需要额外的服务器配置,直接运行应用程序即可。而Spring应用通常需要部署到外部服务器上。
- 快速开发:Spring Boot提供了一系列的starter依赖,通过添加这些依赖,可以快速集成各种功能,提高开发效率。
- MyBatis是如何实现数据库操作的
- 配置文件:MyBatis需要配置核心配置文件(如mybatis-config.xml)和映射文件(如UserMapper.xml)。核心配置文件主要配置数据库连接信息、插件等;映射文件主要配置SQL语句和Java对象之间的映射关系。
- SqlSessionFactory:通过SqlSessionFactoryBuilder读取核心配置文件,创建SqlSessionFactory对象。例如:
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
- **SqlSession**:通过SqlSessionFactory创建SqlSession对象,SqlSession对象可以执行SQL语句。例如:
import org.apache.ibatis.session.SqlSession;
public class UserDao {
public void addUser() {
SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession();
try {
session.insert("UserMapper.addUser");
session.commit();
} finally {
session.close();
}
}
}