当前位置: 首页 > news >正文

小厂Java开发面经解析

文章目录

    • i++线程安全问题
    • a=a+b和a+=b的区别
    • synchronized
    • volatile关键字
    • jdk1.8新增的特性
    • 常量和变量
      • 1. **定义与声明**
        • 常量
        • 变量
      • 2. **值的可变性**
      • 6. **作用域**
      • 2. **未正确关闭的资源**
      • 3. **内部类和匿名类的非静态实例**
      • 4. **缓存引起的内存泄露**
      • 5. **监听器和回调的未注销**
      • 6. **长生命周期的对象持有短生命周期的对象引用**
      • 7. **使用 ThreadLocal 造成的内存泄露**
      • 结论

群u分享的小厂面经,我感觉还是很拷打的很基础的,没有很难的。
在这里插入图片描述

i++线程安全问题

1 ++操作符在单线程下面是安全的,在多线程下面不安全。
i++这个代码设计三个操作,首先需要到内存中读取遍历,将变量拿出来再++。最后再放到内存中去。操作不是原子性的,所以在单线程的下面是安全的,在多线程下面一定是不安全的。下面模拟了1000个线程同时操作i++。
在这里插入图片描述
在这里插入图片描述

a=a+b和a+=b的区别

这个其实拷打的是byte类型和int类型的数据范围问题,

在这里插入图片描述
在这里插入图片描述

b=b+1会发生数据类型的转化,会上线进行转型,如果还要变为byte需要进行强转。
a+=b 不会发生类型转换,还是保证的类型a;

synchronized

锁关键字,synchronized可以作用在代码块和方法上面。
这样就能保证线程安全问题了。
在这里插入图片描述
在这里插入图片描述

volatile关键字

在多线程环境下,volatile关键字可以保证共享数据的可见性,但是并不能保证对数据操作的原子性。

jdk1.8新增的特性

lamada表达式用来简化接口的实列化
stream流,可以用来统计数据,集合操作。
在这里插入图片描述
在这里插入图片描述

常量和变量

在Java中,常量和变量的区别可以从以下几个角度进行分析:

1. 定义与声明

常量
  • 使用关键字:常量使用 final 关键字进行定义。
  • 示例
    final int MAX_VALUE = 100;
    
    这里,MAX_VALUE 被声明为常量,一旦赋值,不能再改变。
变量
  • 普通变量声明:变量不使用 final 关键字,可以自由改变其值。
  • 示例
    int value = 50;
    value = 60; // 可以改变变量的值
    

2. 值的可变性

常量

  • 不可变性:常量在初始化后,其值不可改变。
  • 示例
    final double PI = 3.14159;
    // PI = 3.14; // 错误,不能重新赋值
    

变量

  • 可变性:变量的值可以在程序执行过程中多次改变。
  • 示例
    int count = 10;
    count = 20; // 合法,可以重新赋值
    
  1. 命名规范
    常量
  • 命名约定:常量通常使用全大写字母,单词之间用下划线分隔,以增加可读性。
  • 示例
    final int MAX_SPEED = 120;
    

变量

  • 命名约定:变量名通常使用小写字母开头,遵循驼峰命名法。
  • 示例
    int maxSpeed = 120;
    
  1. 内存分配
    常量
  • 内存优化:编译器会进行优化,对于常量通常在编译时就分配内存,并且可能在多处引用时只存储一份。
  • 示例
    final String CONSTANT_STRING = "Hello, World!";
    

变量

  • 动态分配:变量的内存分配是在运行时进行的,值的改变会导致内存中的内容改变。
  • 示例
    String greeting = "Hello";
    greeting = "Hi";
    
  1. 应用场景
    常量
  • 使用场景:用于表示程序中不会改变的值,如物理常数、配置参数等。
  • 示例
    final int DAYS_IN_WEEK = 7;
    

变量

  • 使用场景:用于表示程序中会变化的数据,如计数器、用户输入等。
  • 示例
    int userAge = 25;
    userAge = 26; // 例如用户过了一年
    

6. 作用域

常量

  • 作用域范围:常量可以是类常量(静态常量)或实例常量。
  • 示例
    public class Constants {public static final double PI = 3.14159; // 类常量
    }
    

变量

  • 作用域范围:变量可以是局部变量、实例变量或类变量(静态变量)。
  • 示例
    public class Example {public int instanceVar; // 实例变量public static int staticVar; // 类变量
    }
    
  1. 线程安全
    常量
  • 线程安全性:由于常量的不可变性,它们是天然线程安全的,不需要额外的同步机制。
  • 示例
    public static final String APP_NAME = "MyApplication";
    

变量

  • 线程安全性:变量在多线程环境下需要注意同步问题,确保线程安全。
  • 示例
    public int sharedCounter;
    

结论
常量和变量在Java中有着明显的区别和不同的应用场景。常量用于表示不可变的数据,提高代码的可读性和维护性;变量用于存储和操作可变的数据,灵活性更强。理解并合理使用常量和变量,可以编写出更健壮、可维护的代码。

在Java中,内存泄露指的是程序中已经不再使用的对象没有被及时回收,继续占用内存,最终可能导致内存耗尽。虽然Java有垃圾回收机制(Garbage Collection,GC),但某些编程错误或设计缺陷仍可能导致内存泄露。以下是几种容易造成内存泄露的常见情况:### 1. **静态集合类持有对象引用**
静态集合类(如 `HashMap`, `ArrayList`)会在整个应用程序生命周期内存活,如果将对象放入这些集合中,而没有及时清理,就会导致内存泄露。
```java
public class MemoryLeak {private static List<Object> list = new ArrayList<>();public void addToList(Object obj) {list.add(obj);}
}

解决方案:确保在对象不再需要时,从集合中移除或清理集合。

2. 未正确关闭的资源

未关闭的文件流、数据库连接、网络连接等,会导致资源无法释放,从而占用内存。

public void readFile(String fileName) throws IOException {BufferedReader reader = new BufferedReader(new FileReader(fileName));// 未关闭 reader,可能导致内存泄露
}

解决方案:使用 try-with-resources 语句,确保资源在使用后被正确关闭。

public void readFile(String fileName) throws IOException {try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {// 读取文件内容}
}

3. 内部类和匿名类的非静态实例

非静态内部类和匿名类会隐式地持有外部类的引用,如果内部类对象的生命周期超过外部类对象,会导致外部类对象无法被垃圾回收。

public class OuterClass {private class InnerClass {// 内部类持有外部类的引用}
}

解决方案:将内部类声明为静态类,避免持有外部类的引用。

public class OuterClass {private static class InnerClass {// 静态内部类不会持有外部类的引用}
}

4. 缓存引起的内存泄露

缓存中的对象如果没有适时清理,会导致内存泄露。

public class Cache {private Map<String, Object> cache = new HashMap<>();public void addToCache(String key, Object value) {cache.put(key, value);}
}

解决方案:使用弱引用(WeakHashMap)或定期清理缓存策略。

public class Cache {private Map<String, Object> cache = new WeakHashMap<>();public void addToCache(String key, Object value) {cache.put(key, value);}
}

5. 监听器和回调的未注销

注册的监听器或回调未及时注销,会导致对象引用无法释放。

public class EventSource {private List<EventListener> listeners = new ArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}
}

解决方案:在不再需要时,及时注销监听器或回调。

public class EventSource {private List<EventListener> listeners = new ArrayList<>();public void addListener(EventListener listener) {listeners.add(listener);}public void removeListener(EventListener listener) {listeners.remove(listener);}
}

6. 长生命周期的对象持有短生命周期的对象引用

长生命周期的对象持有短生命周期的对象引用,会导致短生命周期对象无法及时被回收。

public class Manager {private List<Employee> employees = new ArrayList<>();public void addEmployee(Employee employee) {employees.add(employee);}
}

解决方案:尽量避免长生命周期对象持有短生命周期对象的引用,或及时清理引用。

public class Manager {private List<WeakReference<Employee>> employees = new ArrayList<>();public void addEmployee(Employee employee) {employees.add(new WeakReference<>(employee));}
}

7. 使用 ThreadLocal 造成的内存泄露

ThreadLocal 对象如果未及时移除,可能会导致内存泄露,尤其是在使用线程池时。

private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));

解决方案:在使用完 ThreadLocal 变量后,调用 remove() 方法。

private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));// 使用后移除
formatter.remove();

结论

虽然Java的垃圾回收机制能够自动管理内存,但并非万能。开发者需要关注程序设计和代码实现中的细节,避免不必要的对象引用,及时清理资源,以防止内存泄露。通过合理设计和使用上述策略,可以有效减少内存泄露的风险。

## 拦截器和过滤器的区别
拦截器(Interceptor)和过滤器(Filter)是两种常见的处理机制,用于在请求处理过程中执行一些预处理或后处理的操作。尽管它们在某些情况下可以实现相似的功能,但它们的设计目标、应用场景和实现方式都有所不同。下面从多个角度对它们进行详细比较:### 1. **定义和用途**
- **拦截器(Interceptor)**:- **定义**:拦截器是基于面向切面编程(AOP)的一种实现方式,主要用于拦截方法调用或行为,在方法执行前后进行处理。- **用途**:常用于业务逻辑层面,例如日志记录、权限校验、事务管理等。- **过滤器(Filter)**:- **定义**:过滤器是基于Servlet规范的一种机制,用于在请求到达Servlet之前或响应离开Servlet之后进行预处理和后处理。- **用途**:常用于处理与HTTP请求/响应相关的任务,如字符编码设置、请求日志、输入验证、安全检查等。### 2. **应用层级**
- **拦截器**:- 作用于方法调用层级,可以用于控制器层、服务层等任意Java方法。- 主要用于Spring MVC、Struts等框架中的方法调用拦截。- **过滤器**:- 作用于Web层级,处理HTTP请求和响应。- 主要用于Servlet、JSP等Web组件的请求过滤。### 3. **生命周期**
- **拦截器**:- 生命周期由框架管理。拦截器在框架初始化时注册,方法调用前后执行特定代码。- 执行顺序可通过配置顺序控制。- **过滤器**:- 生命周期由Servlet容器管理。在Web应用启动时初始化,销毁时清理。- 执行顺序通过`web.xml`或注解配置,按照配置顺序执行。### 4. **实现方式**
- **拦截器**:- 在Spring中,拦截器实现`HandlerInterceptor`接口。- 示例:```javapublic class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 前处理逻辑return true; // 返回true继续处理请求,返回false终止请求}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 后处理逻辑}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 完成后处理逻辑}}```- **过滤器**:- 过滤器实现`javax.servlet.Filter`接口。- 示例:```javapublic class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {// 初始化逻辑}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 预处理逻辑chain.doFilter(request, response); // 继续处理请求// 后处理逻辑}@Overridepublic void destroy() {// 清理资源逻辑}}```### 5. **配置方式**
- **拦截器**:- 在Spring中,通过配置类或XML文件进行配置。- 示例(Java配置):```java@Configurationpublic class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");}}```- **过滤器**:- 通过`web.xml`或注解进行配置。- 示例(注解配置):```java@WebFilter(filterName = "myFilter", urlPatterns = "/*")public class MyFilter implements Filter {// 过滤器实现代码}```### 6. **适用场景**
- **拦截器**:- 适用于需要处理控制器或服务层方法调用的场景,如权限验证、日志记录、事务管理等。- 更适合业务逻辑处理,紧密结合应用框架。- **过滤器**:- 适用于需要处理HTTP请求和响应的场景,如请求日志、跨域处理、字符编码设置、安全检查等。- 更适合Web应用层的处理,紧密结合Servlet容器。### 总结
拦截器和过滤器各有其适用的场景和特点。拦截器主要用于业务逻辑层面的方法调用拦截,而过滤器则主要用于Web层面的HTTP请求和响应处理。了解两者的区别和适用场景,可以更好地选择和应用它们,以实现预期的功能和性能优化。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何连接到公司的服务器?
  • 模板语法指令语法——02
  • 【Leetcode--旋转矩阵】
  • tkinter-TinUI-xml实战(12)pip可视化管理器
  • 新书速览|Vue.js 3.x+Express全栈开发:从0到1打造商城项目
  • 数据结构与算法(1):递归函数的设计技巧
  • PostgreSQl 物化视图
  • 秒懂设计模式--学习笔记(9)【结构型-装饰器模式】
  • nginx正向代理、反向代理、负载均衡
  • 【C++】构造函数详解
  • 机器人及其相关工科专业课程体系
  • 批量提取PDF指定区域内容到 Excel , 根据PDF文件第一行文字来自动重命名v1.3-附思路和代码实现
  • qt 返回上级页面
  • Java如何使用 HttpClientUtils 发起 HTTP 请求
  • C++(week11): C++基础 第六章:关联式容器 set、map
  • 【编码】-360实习笔试编程题(二)-2016.03.29
  • CentOS7 安装JDK
  • java8 Stream Pipelines 浅析
  • js学习笔记
  • linux学习笔记
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • php面试题 汇集2
  • python3 使用 asyncio 代替线程
  • React 快速上手 - 07 前端路由 react-router
  • scala基础语法(二)
  • 初识 webpack
  • 经典排序算法及其 Java 实现
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 如何进阶一名有竞争力的程序员?
  • 一起参Ember.js讨论、问答社区。
  • 用jquery写贪吃蛇
  • 1.Ext JS 建立web开发工程
  • 关于Android全面屏虚拟导航栏的适配总结
  • 数据库巡检项
  • ###C语言程序设计-----C语言学习(3)#
  • #、%和$符号在OGNL表达式中经常出现
  • #define
  • ( 10 )MySQL中的外键
  • (4)Elastix图像配准:3D图像
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (leetcode学习)236. 二叉树的最近公共祖先
  • (poj1.3.2)1791(构造法模拟)
  • (精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标
  • (力扣)循环队列的实现与详解(C语言)
  • (一一四)第九章编程练习
  • (转)大型网站架构演变和知识体系
  • .a文件和.so文件
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET Core中的去虚
  • .NET DataGridView数据绑定说明
  • .NET Framework杂记
  • .NET 解决重复提交问题
  • .net连接oracle数据库
  • .Net下的签名与混淆