2023-4-13美团测开二面
1. 自我介绍#
2. 写项目的背景是什么#
3. 为什么使用SpringCloud,主要适用于哪些功能#
4. 为什么用MongoDB#
5. MongoDB和Redis哪个更快#
6. 拷贝数组有几种方式,哪种方式效率更高#
效率从高到低: System.arraycopy 、 clone 、 (Arrays.copyOf、Arrays.copyOfRange) 、 for循环。
7. Integer t1 = 128; Integer t2 = 128; sout(t1==t2)
#
Integer的值为-128-127之间的时候就会自动的从Integer的缓存(IntegerCache)中去取,如果超过这个范围就会新建一个Integer的对象。
所以a==b(a=10,b=10)
为true,c==d(c=150,d=150)
为false。
nteger的值为-128-127的时候用==会返回true 不再这个区间要用equals才会返回true
8. Java8中最大化减少空指针的产生?#
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在 则isPresent()方法会返回true,调用get()方法会返回该对象。
创建Optional类对象的方法:
Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):t可以为null
判断Optional容器中是否包含对象:
boolean isPresent() : 判断是否包含对象
void ifPresent(Consumer consumer) :如果有值,就执行Consumer 接口的实现代码,并且该值会作为参数传给它。
获取Optional容器的对象:
T get(): 如果调用对象包含值,返回该值,否则抛异常
T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
T orElseGet(Supplier other) :如果有值则将其返回,否则返回由 Supplier接口实现提供的对象。
T orElseThrow(Supplier exceptionSupplier) :如果有值则将其返 回,否则抛出由Supplier接口实现提供的异常。
public class OptionalTest {
@Test
public void test() {
//创建 Optional
Girl girl = new Girl();
Optional<Girl> girl1 = Optional.of(girl);
}
@Test
public void test2() {
//创建 Optional
Girl girl = new Girl();
girl = null;
Optional<Girl> girl1 = Optional.ofNullable(girl);
//如果当前这个 Optional内部封装的t是非空的,则返回内部的t
//如果内部的t是空的,则返回prElse 方法的参数t1
Girl girl2 = girl1.orElse(new Girl("赵丽颖"));
System.out.println(girl2);
}
public String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
public String newGetGirlName(Boy boy) {
Optional<Boy> boyOptional = Optional.ofNullable(boy);
//此时的 boy1 一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("乃林")));
Girl girl = boy1.getGirl();
Optional<Girl> girl1 = Optional.ofNullable(girl);
//此时的 girl2 一定非空
Girl girl2 = girl1.orElse(new Girl("嘉然"));
return girl2.getName();
}
public String oldGetGirlName(Boy boy) {
if (boy != null) {
Girl girl = boy.getGirl();
if (girl != null) {
return girl.getName();
}
}
return null;
}
@Test
public void test3() {
Boy boy = new Boy();
String girlName = getGirlName(boy);
System.out.println(girlName);
}
@Test
public void test4() {
Boy boy = new Boy();
boy = null;
String girlName = oldGetGirlName(boy);
System.out.println(girlName);
}
@Test
public void test5() {
// boy 为null 返回乃琳
// boy的参数 girl为null 返回嘉然
//如果正常 就返回 正常参数
Boy boy = new Boy(new Girl("林志玲"));
//boy = null;
String girlName = newGetGirlName(boy);
System.out.println(girlName);
}
}
9. 反射的应用场景#
- 反射实现驱动加载 Class.forName("com.mysql.cj.jdbc.Driver");
- 反射实现配置文件加载
- 反射实现工厂模式
- 反射实现静态代理
- 反射实现动态代理
创建一个要调用的接口实现,获取接口实现的Class。
实现InvocationHandler
接口并重写invoke方法,实例化InvocationHandler
对象,用来处理 Proxy
的方法调用。
通过Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);
实例化一个 proxy
对象。
10. 代码中一个方法使用了很多try-catch、如何优雅的减少try-catch的个数#
@ControllerAdvice可以把异常处理器应用到所有控制器,而不是单个控制器。借助该注解,我们可以实现:在独立的某个地方,比如单独一个类,定义一套对各种异常的处理机制,然后在类的签名加上注解@ControllerAdvice,统一对 不同阶段的、不同异常 进行处理。这就是统一异常处理的原理。
11. springboot中如何获取Service实例#
使用ApplicationContextAware
此方法直接实现了ApplicationContextAware
接口,调用Spring
提供的applicationContext
,将所有在Spring
中有注解的类,均可通过getBean
方式获取到.
12. 拦截器和过滤器的区别#
1、过滤器 (Filter)#
过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。
- init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。
- doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
- destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
2、拦截器 (Interceptor)#
拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。
首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor 接口中也定义了三个方法。
- preHandle() :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
- postHandle():只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 有意思的是:postHandle() 方法被调用的顺序跟 preHandle() 是相反的,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。
- afterCompletion():只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
- 将自定义好的拦截器处理类进行注册,并通过addPathPatterns、excludePathPatterns等属性设置需要拦截或需要排除的 URL。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");
}
}
不同#
-
过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的。
-
使用范围不同 我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。
-
触发时机不同过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
-
拦截的请求范围不同 执行顺序 :Filter 处理中 -> Interceptor 前置 -> 我是controller -> Interceptor 处理中 -> Interceptor 处理后。过滤器Filter执行了两次,拦截器Interceptor只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。
13. 双亲委派是为了解决什么问题#
双亲委派机制是 Java 类加载器的一种实现方式,其核心思想是在类加载器之间建立一种父子关系,父类加载器加载的类能够被子类加载器使用,而子类加载器加载的类不能被父类加载器所使用。具体来说,当一个类需要被加载时,先由当前类的加载器去搜索类,如果没找到,就会委托给父类加载器去搜索,依次往上,直到达到顶层的启动类加载器,如果还没有找到就会抛出 ClassNotFoundException 异常。
双亲委派机制的好处主要有以下几点:
- 避免类的重复加载:在 JVM 中,每个类都由一个唯一的全限定名和一个对应的类加载器确定,类加载器根据全限定名和类路径来确定类的位置。因此,在一个 JVM 实例中,如果有两个类加载器分别加载了同一个类,JVM 会认为这两个类是不同的,从而导致类型转换异常等问题。通过双亲委派机制,父类加载器在加载类之前会先委托给自己的父类加载器去加载,从而保证一个类在 JVM 中只会有一份,并且由其父类加载器所加载。
- 安全性考虑:Java 核心类库(如 java.lang 包下的类)都是由启动类加载器加载的,其他的类都是由其它类加载器加载的。这样,我们就可以保证 Java 核心类库的安全性,因为不同的应用程序无法改变这些类的实现。另外,也可以在类加载过程中做一些安全性检查。
- 模块化开发:在实际应用中,我们经常需要在一个程序中使用多个第三方库,这些库可能会存在同名类。如果使用了双亲委派机制,就可以保证不同的类加载器只会加载自己的类,从而避免了类名冲突的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?