小厂Java开发面经解析

群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`接口。
  - 示例:
    ```java
    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 前处理逻辑
            return true; // 返回true继续处理请求,返回false终止请求
        }

        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            // 后处理逻辑
        }

        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // 完成后处理逻辑
        }
    }
    ```

- **过滤器**:
  - 过滤器实现`javax.servlet.Filter`接口。
  - 示例:
    ```java
    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            // 初始化逻辑
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            // 预处理逻辑
            chain.doFilter(request, response); // 继续处理请求
            // 后处理逻辑
        }

        @Override
        public void destroy() {
            // 清理资源逻辑
        }
    }
    ```

### 5. **配置方式**
- **拦截器**:
  - 在Spring中,通过配置类或XML文件进行配置。
  - 示例(Java配置):
    ```java
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public 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请求和响应处理。了解两者的区别和适用场景,可以更好地选择和应用它们,以实现预期的功能和性能优化。
  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想要打 Acm 的小周同学呀

您的支持是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值