图解学习网站:https://xiaolincoding.com
大家好,我是小林。
美的(不是美团)开发岗的校招薪资普遍是 17.5k x 14,办公地点主要是在二线城市,比如佛山、武汉、顺德,总包 24.5w 在二线城市过的相当舒服。
再来聊聊,美的校招的面试流程:
那美的技术面到底难度如何呢?因为美的技术面考察时间就 20 分钟左右,所以最多就问 10 个技术问题,面试的强度还是比互联网中大低的。
这次来看看一位同学的 「美的 Java 软开的一面」,考察的知识点主要是 Java、Spring、MySQL、项目这些内容,同学最后是通过了,成功进入到二面。
你们觉得难度如何?
特性 | 继承 | 多态 |
---|---|---|
核心目的 | 复用代码,构建层次结构 | 统一接口,多样化实现 |
操作对象 | 类与类的关系 | 方法与对象的关系 |
关键语法 | extends 关键字 | 父类引用 = new 子类对象() |
继承是面向对象编程中代码重用的一种重要机制,在 Java 中通过extends
关键字来实现继承,子类可以直接使用父类中已定义好的属性和方法,还可以添加自己特有的属性和方法,实现功能的扩展。继续也具备传递性,如果类 C 继承自类 B,类 B 继承自类 A,那么类 C 不仅继承了类 B 的属性和方法,也间接继承了类 A 的属性和方法。
多态是指同一个操作在不同对象上可以表现出不同的行为。例如,同样是 “发声” 这个操作,“狗” 类的对象调用会发出 “汪汪” 声,“猫” 类的对象调用会发出 “喵喵” 声。多态实现方式有两种:
Animal
中有void sound()
方法,子类Dog
重写这个方法后实现自己特有的 “汪汪” 叫的声音。Calculator
类中可以有add(int a, int b)
和add(double a, double b)
等方法。是单继承,就是说一个类只能有一个直接父类。
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
等。
反射具有以下特性:
反射的优点就是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
Bean 相关:
依赖注入:
读取配置:
Web相关:
其他常用注解:
Spring Boot 不是一个新框架,而是在 Spring 框架基础上的高级封装,通过「开箱即用」的设计哲学,让开发者专注于业务代码而非基础配置。以下是详细对比:
对比点 | Spring | Spring Boot |
---|---|---|
项目初始化 | 手动配置 XML/JavaConfig | 内置自动配置,提供 start.spring.io 快速生成项目结构 |
依赖管理 | 手动维护版本,易冲突 | Starter POMs 统一管理依赖版本 |
内嵌服务器 | 需手动部署到 Tomcat 等 | 默认内嵌 Tomcat/Jetty,无需外部部署 |
配置复杂度 | 需配置大量 Bean 和集成代码 | 约定优于配置,自动配置大部分组件 |
监控与管理 | 需集成第三方工具 | 内置 Actuator 提供健康检查、指标收集等 |
打包与部署 | 需生成 WAR 包并部署到服务器 | 可执行 JAR,java -jar 一键启动 |
Spring Boot 的核心竞争优势在于提升开发效率,通过以下设计实现:
这样设计的好处是,对于开发者:避免重复造轮子,聚焦业务逻辑,对于企业:加速产品迭代,降低维护成本。
循环依赖指的是两个类中的属性相互依赖对方:例如 A 类中有 B 属性,B 类中有 A属性,从而形成了一个依赖闭环,如下图。
null
循环依赖问题在Spring中主要有三种情况:
只有【第三种方式】的循环依赖问题被 Spring 解决了,其他两种方式在遇到循环依赖问题时,Spring都会产生异常。
Spring 解决单例模式下的setter循环依赖问题的主要方式是通过三级缓存解决循环依赖。
三级缓存指的是 Spring 在创建 Bean 的过程中,通过三级缓存来缓存正在创建的 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)中,具体包括以下内容:
通过工具(如 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 InnoDB 引擎是用了B+树作为了索引的数据结构。
B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都有两个指针,分别指向下一个叶子节点和上一个叶子节点,形成一个双向链表。
主键索引的 B+Tree 如图所示:
比如,我们执行了下面这条查询语句:
select * from product where id= 5;
这条语句使用了主键索引查询 id 号为 5 的商品。查询过程是这样的,B+Tree 会自顶向下逐层进行查找:
数据库的索引和数据都是存储在硬盘的,我们可以把读取一个节点当作一次磁盘 I/O 操作。那么上面的整个查询过程一共经历了 3 个节点,也就是进行了 3 次 I/O 操作。
B+Tree 存储千万级的数据只需要 3-4 层高度就可以满足,这意味着从千万级的表查询目标数据最多需要 3-4 次磁盘 I/O,所以B+Tree 相比于 B 树和二叉树来说,最大的优势在于查询效率很高,因为即使在数据量很大的情况,查询一个数据的磁盘 I/O 依然维持在 3-4次。
按简历所写的内容,重新念了一遍,主要就是后端开发的职责,开发了 xxx、xxx、xx 模块。
还好机智,提前针对简历上的面试做好了总结,每次面试真的必问,直接按照之前准备的内容流畅回答完了。
问了面试官三个问题: