赢了!美的一面,被我狠狠拿捏了
美的(不是美团)开发岗的校招薪资普遍是 17.5k x 14,办公地点主要是在二线城市,比如佛山、武汉、顺德,总包 24.5w 在二线城市过的相当舒服。
再来聊聊,美的校招的面试流程:
笔试:选择题+编程题为主,跟Java相关的比较多,考察了Java基础,spring,数据库,算法,并发,JVM,操作系统,网络,智力题等,比较全面,范围很广。 测评:常规测评题,性格、价值观、智商方面的 一面:技术面为主,以项目+八股拷打为主,一场技术面最多就半小时,大部分是 20 分钟左右 二面:聊天局,聊聊经历和项目,比较少会再问八股细节了。 终面:领导面,面试还是关注岗位本身的工作内容与简历内容的匹配度
那美的技术面到底难度如何呢?因为美的技术面考察时间就 20 分钟左右,所以最多就问 10 个技术问题,面试的强度还是比互联网中大低的。
这次来看看一位训练营同学的 「美的 Java 软开的一面」,考察的知识点主要是 Java、Spring、MySQL、项目这些内容,同学最后是通过了,成功进入到二面。
你们觉得难度如何?
美的一面(25分钟)
什么是继承和多态?
| 特性 | 继承 | 多态 |
|---|---|---|
| 核心目的 | 复用代码,构建层次结构 | 统一接口,多样化实现 |
| 操作对象 | 类与类的关系 | 方法与对象的关系 |
| 关键语法 | extends 关键字 | 父类引用 = new 子类对象() |
继承是面向对象编程中代码重用的一种重要机制,在 Java 中通过extends关键字来实现继承,子类可以直接使用父类中已定义好的属性和方法,还可以添加自己特有的属性和方法,实现功能的扩展。继续也具备传递性,如果类 C 继承自类 B,类 B 继承自类 A,那么类 C 不仅继承了类 B 的属性和方法,也间接继承了类 A 的属性和方法。
多态是指同一个操作在不同对象上可以表现出不同的行为。例如,同样是 “发声” 这个操作,“狗” 类的对象调用会发出 “汪汪” 声,“猫” 类的对象调用会发出 “喵喵” 声。多态实现方式有两种:
运行时多态:基于方法重写(Override),通过父类引用调用子类对象,比如,父类 Animal中有void sound()方法,子类Dog重写这个方法后实现自己特有的 “汪汪” 叫的声音。编译时多态:方法重载(Overload),同一类中有同名不同参数的方法,例如,一个 Calculator类中可以有add(int a, int b)和add(double a, double b)等方法。
Java是单继承还是多继承?
是单继承,就是说一个类只能有一个直接父类。
class Animal { } class Dog extends Animal { } // ✅ 正确:单继承
// class Cat extends Animal, Plant { } // ❌ 错误:Java不支持类多继承
单继承的好处是可以避免多继承引发的钻石问题,这样一个类从多个父类继承相同成员,引发二义性,如类 D 同时继承类 B 和类 C,而 B 和 C 又都继承自类 A,且 B 和 C 对 A 中的某个方法进行了不同实现,那么 D 在调用该方法时就无法确定应使用 B 还是 C 的实现。
有用过泛型吗,讲一下?
用过,泛型允许开发者编写类型参数化的代码,让同一套逻辑可以安全地操作多种数据类型,泛型的主要目的是在编译时提供更强的类型检查,并且在编译后能够保留类型信息,避免了在运行时出现类型转换异常。
比如,把泛型定义在类上,定义格式为class 类名<类型参数>{},如class Box<T>{},使用时需在创建对象时指定具体类型,如Box<String> stringBox = new Box<>();,这时候遇到与指定类型不符的代码,就会在编译的时候报错。
// 泛型类的定义
public class Box<T> {
private T content; public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
// 使用类型参数(实例化时指定类型)
Box<String> stringBox = new Box<>();
stringBox.setContent("Java"); // ✔️ 编译时检查类型
// stringBox.setContent(123); ❌ 编译报错
String value = stringBox.getContent(); // 无需强制转换
泛型的价值体现在下面这三个地方:
| 特性 | 解决的问题 | 实际应用场景 |
|---|---|---|
| 类型参数化 | 避免重复编写类似代码 | 通用容器类(如 List<T>、Map<K,V>) |
| 编译时类型检查 | 防止类型错误扩散到运行时 | 集合操作、工具方法设计 |
| 通配符 | 提升方法参数的灵活性 | 通用算法(如集合工具类 Collections) |
什么是反射?有用过吗
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
比如,获取一个Class对象。Class.forName(完整类名)。通过Class对象获取类的构造方法,class.getConstructor。根据Class对象获取类的方法,getMethod和getMethods。使用Class对象创建一个对象,class.newInstance等。
反射具有以下特性:
运行时类信息访问:反射机制允许程序在运行时获取类的完整结构信息,包括类名、包名、父类、实现的接口、构造函数、方法和字段等。 动态对象创建:可以使用反射API动态地创建对象实例,即使在编译时不知道具体的类名。这是通过Class类的newInstance()方法或Constructor对象的newInstance()方法实现的。 动态方法调用:可以在运行时动态地调用对象的方法,包括私有方法。这通过Method类的invoke()方法实现,允许你传入对象实例和参数值来执行方法。 访问和修改字段值:反射还允许程序在运行时访问和修改对象的字段值,即使是私有的。这是通过Field类的get()和set()方法完成的。
反射的优点就是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
springboot的常用注解有哪些?
Bean 相关:
@Component:将一个类标识为 Spring 组件(Bean),可以被 Spring 容器自动检测和注册。通用注解,适用于任何层次的组件。 @ComponentScan:自动扫描指定包及其子包中的 Spring 组件。 @Controller:标识控制层组件,实际上是 @Component 的一个特化,用于表示 Web 控制器。处理 HTTP 请求并返回视图或响应数据。 @RestController:是 @Controller 和 @ResponseBody 的结合,返回的对象会自动序列化为 JSON 或 XML,并写入 HTTP 响应体中。 @Repository:标识持久层组件(DAO 层),实际上是 @Component 的一个特化,用于表示数据访问组件。常用于与数据库交互。 @Bean:方法注解,用于修饰方法,主要功能是将修饰方法的返回对象添加到 Spring 容器中,使得其他组件可以通过依赖注入的方式使用这个对象。
依赖注入:
@Autowired:用于自动注入依赖对象,Spring 框架提供的注解。 @Resource:按名称自动注入依赖对象(也可以按类型,但默认按名称),JDK 提供注解。 @Qualifier:与 @Autowired 一起使用,用于指定要注入的 Bean 的名称。当存在多个相同类型的 Bean 时,可以使用 @Qualifier 来指定注入哪一个。
读取配置:
@Value:用于注入属性值,通常从配置文件中获取。标注在字段上,并指定属性值的来源(如配置文件中的某个属性)。 @ConfigurationProperties:用于将配置属性绑定到一个实体类上。通常用于从配置文件中读取属性值并绑定到类的字段上。
Web相关:
@RequestMapping:用于映射 HTTP 请求到处理方法上,支持 GET、POST、PUT、DELETE 等请求方法。可以标注在类或方法上。标注在类上时,表示类中的所有响应请求的方法都是以该类路径为父路径。 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping:分别用于映射 HTTP GET、POST、PUT、DELETE 请求到处理方法上。它们是 @RequestMapping 的特化,分别对应不同的 HTTP 请求方法。
其他常用注解:
@Transactional:声明事务管理。标注在类或方法上,指定事务的传播行为、隔离级别等。 @Scheduled:声明一个方法需要定时执行。标注在方法上,并指定定时执行的规则(如每隔一定时间执行一次)。
springboot相比spring的优势在哪里?
Spring Boot 不是一个新框架,而是在 Spring 框架基础上的高级封装,通过「开箱即用」的设计哲学,让开发者专注于业务代码而非基础配置。以下是详细对比:
| 对比点 | Spring | Spring Boot |
|---|---|---|
| 项目初始化 | 手动配置 XML/JavaConfig | 内置自动配置,提供 start.spring.io 快速生成项目结构 |
| 依赖管理 | 手动维护版本,易冲突 | Starter POMs 统一管理依赖版本 |
| 内嵌服务器 | 需手动部署到 Tomcat 等 | 默认内嵌 Tomcat/Jetty,无需外部部署 |
| 配置复杂度 | 需配置大量 Bean 和集成代码 | 约定优于配置,自动配置大部分组件 |
| 监控与管理 | 需集成第三方工具 | 内置 Actuator 提供健康检查、指标收集等 |
| 打包与部署 | 需生成 WAR 包并部署到服务器 | 可执行 JAR,java -jar 一键启动 |
Spring Boot 的核心竞争优势在于提升开发效率,通过以下设计实现:
约定优于配置:Spring Boot 采用 “约定优于配置” 原则,通过自动配置机制,根据项目依赖自动识别并配置相关 Bean。例如,引入 Spring Data JPA 依赖,Spring Boot 会自动配置好数据源、实体管理器等相关组件,而在 Spring 中则需手动配置大量的 XML 或 Java 配置类。 构建即运行的一体化体验:Spring Boot 提供依赖启动器 “starter”,在构建项目时,只需选择对应的场景依赖启动器,内部会自动包含并拉取相关依赖,而且 Spring Boot 内嵌 Tomcat、Jetty 等服务器,可将项目打成 JAR 包,使用 “java -jar xx.jar” 直接运行。Spring 应用通常需要部署到外部容器中才能运行,部署过程相对复杂。
这样设计的好处是,对于开发者:避免重复造轮子,聚焦业务逻辑,对于企业:加速产品迭代,降低维护成本。
Spring框架如何解决循环依赖问题?
循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。
循环依赖问题在Spring中主要有三种情况:
第一种:通过构造方法进行依赖注入时产生的循环依赖问题。 第二种:通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。 第三种:通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。
只有【第三种方式】的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常。
Spring 解决单例模式下的setter循环依赖问题的主要方式是通过三级缓存解决循环依赖。
三级缓存指的是 Spring 在创建 Bean 的过程中,通过三级缓存来缓存正在创建的 Bean,以及已经创建完成的 Bean 实例。具体步骤如下:
实例化 Bean:Spring 在实例化 Bean 时,会先创建一个空的 Bean 对象,并将其放入一级缓存中。 属性赋值:Spring 开始对 Bean 进行属性赋值,如果发现循环依赖,会将当前 Bean 对象提前暴露给后续需要依赖的 Bean(通过提前暴露的方式解决循环依赖)。 初始化 Bean:完成属性赋值后,Spring 将 Bean 进行初始化,并将其放入二级缓存中。 注入依赖:Spring 继续对 Bean 进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的 Bean 实例。
通过三级缓存的机制,Spring 能够在处理循环依赖时,确保及时暴露正在创建的 Bean 对象,并能够正确地注入已经初始化的 Bean 实例,从而解决循环依赖问题,保证应用程序的正常运行。
对注解解析的底层实现了解吗?
注解本质上是一种特殊的接口,它继承自 java.lang.annotation.Annotation 接口,所以注解也叫声明式接口,例如,定义一个简单的注解:
public @interface MyAnnotation {
String value();
}
编译后,Java 编译器会将其转换为一个继承自 Annotation 的接口,并生成相应的字节码文件。
根据注解的作用范围,Java 注解可以分为以下几种类型:
源码级别注解 :仅存在于源码中,编译后不会保留( @Retention(RetentionPolicy.SOURCE))。类文件级别注解 :保留在 .class文件中,但运行时不可见(@Retention(RetentionPolicy.CLASS))。运行时注解 :保留在 .class文件中,并且可以通过反射在运行时访问(@Retention(RetentionPolicy.RUNTIME))。
只有运行时注解可以通过反射机制进行解析。
当注解被标记为 RUNTIME 时,Java 编译器会在生成的 .class 文件中保存注解信息。这些信息存储在字节码的属性表(Attribute Table)中,具体包括以下内容:
RuntimeVisibleAnnotations :存储运行时可见的注解信息。 RuntimeInvisibleAnnotations :存储运行时不可见的注解信息。 RuntimeVisibleParameterAnnotations 和 RuntimeInvisibleParameterAnnotations :存储方法参数上的注解信息。
通过工具(如 javap -v)可以查看 .class 文件中的注解信息。
注解的解析主要依赖于 Java 的反射机制。以下是解析注解的基本流程:
1、获取注册信息:通过反射 API 可以获取类、方法、字段等元素上的注解。例如:
Class<?> clazz = MyClass.class;
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
if (annotation != null) {
System.out.println(annotation.value());
}
2、底层原理:反射机制的核心类是 java.lang.reflect.AnnotatedElement,它是所有可以被注解修饰的元素(如 Class、Method、Field 等)的父接口。该接口提供了以下方法:
getAnnotation(Class<T> annotationClass):获取指定类型的注解。getAnnotations():获取所有注解。isAnnotationPresent(Class<? extends Annotation> annotationClass):判断是否包含指定注解。
这些方法的底层实现依赖于 JVM 提供的本地方法(Native Method),例如:
native Annotation[] getDeclaredAnnotations0(boolean publicOnly);native <A extends Annotation> A getAnnotation(Class<A> annotationClass);
JVM 在加载类时会解析 .class 文件中的注解信息,并将其存储在内存中,供反射机制使用。
因此,注解解析的底层实现主要依赖于 Java 的反射机制和字节码文件的存储。通过 @Retention 元注解可以控制注解的保留策略,当使用 RetentionPolicy.RUNTIME 时,可以在运行时通过反射 API 来解析注解信息。在 JVM 层面,会从字节码文件中读取注解信息,并创建注解的代理对象来获取注解的属性值。
Mysql的索引结构了解吗?
MySQL InnoDB 引擎是用了B+树作为了索引的数据结构。
B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个双向链表。
主键索引的 B+Tree 如图所示:
比如,我们执行了下面这条查询语句:
select * from product where id= 5;
这条语句使用了主键索引查询 id 号为 5 的商品。查询过程是这样的,B+Tree 会自顶向下逐层进行查找:
将 5 与根节点的索引数据 (1,10,20) 比较,5 在 1 和 10 之间,所以根据 B+Tree的搜索逻辑,找到第二层的索引数据 (1,4,7); 在第二层的索引数据 (1,4,7)中进行查找,因为 5 在 4 和 7 之间,所以找到第三层的索引数据(4,5,6); 在叶子节点的索引数据(4,5,6)中进行查找,然后我们找到了索引值为 5 的行数据。
数据库的索引和数据都是存储在硬盘的,我们可以把读取一个节点当作一次磁盘 I/O 操作。那么上面的整个查询过程一共经历了 3 个节点,也就是进行了 3 次 I/O 操作。
B+Tree 存储千万级的数据只需要 3-4 层高度就可以满足,这意味着从千万级的表查询目标数据最多需要 3-4 次磁盘 I/O,所以B+Tree 相比于 B 树和二叉树来说,最大的优势在于查询效率很高,因为即使在数据量很大的情况,查询一个数据的磁盘 I/O 依然维持在 3-4次。
介绍一下项目,你负责了哪些部分
按简历所写的内容,重新念了一遍,主要就是后端开发的职责,开发了 xxx、xxx、xx 模块。
说一下项目的亮点和难点
还好机智,提前针对简历上的面试做好了总结,每次面试真的必问,直接按照之前准备的内容流畅回答完了。
反问
问了面试官三个问题:
咱们部门主要做什么业务的?技术栈是怎么样的? 对我这场面试的评价?
如果你也想要参加训练营,可以找我聊聊。
关注和加小白微信,
一手消息和知识,备注【进群】拉你进划水交流群👇
备注【报名】欢迎加入go/java后端训练营
点击下文了解详情