Java后端开发-面试总结(集结版)

发布于:2025-04-10 ⋅ 阅读:(33) ⋅ 点赞:(0)

第一个问题,在 Java 集合框架中,ArrayListLinkedList有什么区别?在实际应用场景中,应该如何选择使用它们?

ArrayList 基于数组,LinkedList 基于双向链表。

在查询方面 ArrayList 效率高,添加和删除频繁场景下 LinkedList 更具优势, ArrayList 需要连续内存空间,LinkedList 不需要。

为什么基于数组的 ArrayList 查询效率高,是因为可以通过索引直接定位元素,时间复杂度为 O (1);

而 LinkedList 查询需要从头或尾遍历链表,时间复杂度为 O (n)。

在添加和删除操作上,LinkedList 只需修改前后节点的指针,时间复杂度为 O (1),而 ArrayList 在中间位置添加或删除元素时,可能需要移动大量元素,时间复杂度为 O (n) 。

举一些实际业务场景例子,比如在报表数据展示,需要频繁查询数据,适合用 ArrayList;在实现一个消息队列,频繁进行消息的入队和出队(添加和删除操作),LinkedList 更合适。

第二个问题,谈谈你对 Java 内存模型(JMM)的理解,它在多线程编程中有什么作用?

 Java 内存模型(Java Memory Model,简称 JMM) 。并且对 JMM 的理解不够全面准确。JMM 不仅仅是保证变量操作对其他线程可见,它定义了线程和主内存之间的抽象关系,规定了一个线程如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步访问共享变量。它主要解决了多线程编程中的原子性、可见性和有序性问题。例如,通过 volatile 关键字修饰的变量,能保证其修改对其他线程的可见性,同时禁止指令重排序,体现了 JMM 在保证可见性和有序性方面的作用;而 synchronized 关键字则既保证了原子性,又保证了可见性。

第三个问题,在 Spring 框架中,依赖注入(DI)有哪些方式?它们各自的优缺点是什么?

常见的依赖注入方式是构造器注入、setter 方法注入和接口注入(不过接口注入在实际开发中较少使用) 。

构造器注入通过类的构造函数来传递依赖对象,优点是依赖关系在对象创建时就确定,对象创建后即处于可用状态,并且可以确保所有依赖都被初始化,缺点是如果依赖过多,构造函数参数列表会很长,可读性变差。

Setter 方法注入通过对象的 setter 方法来设置依赖,优点是灵活性高,可在对象创建后动态设置依赖,缺点是可能会出现依赖未初始化的情况。

接口注入需要实现特定接口来接受依赖,由于其侵入性较强,实际使用相对较少。

说一下集合,比如集合有哪些

在 Java 中,集合框架提供了一组用于存储和操作对象的类和接口,主要分为以下几类:

1. List(列表)

  • ArrayList:基于动态数组实现,允许元素重复且有序。它支持随机访问,查询效率高,时间复杂度为 O (1),因为可以通过索引直接定位元素。但在中间位置插入和删除元素时,可能需要移动大量元素,时间复杂度为 O (n)。例如:

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        System.out.println(list.get(0)); 
    }
}

  • LinkedList:基于双向链表实现,同样允许元素重复且有序。它在插入和删除元素时效率高,只需修改前后节点的指针,时间复杂度为 O (1),但查询效率相对较低,需要从头或尾遍历链表,时间复杂度为 O (n)。例如:

import java.util.LinkedList;
import java.util.List;

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.add("cherry");
        list.add("date");
        list.addFirst("apricot");
        System.out.println(list.removeLast()); 
    }
}

  • Vector:与 ArrayList 类似,也是基于数组实现,但它是线程安全的。不过,由于线程安全机制带来的开销,性能相对 ArrayList 较低。现在在多线程环境下,更推荐使用CopyOnWriteArrayList 替代。例如:

import java.util.Vector;

public class VectorExample {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("grape");
        vector.add("kiwi");
    }
}

2. Set(集合)

  • HashSet:基于哈希表实现,不允许元素重复,元素无序。它通过计算元素的哈希码来确定元素在集合中的存储位置,因此添加、删除和查找操作的平均时间复杂度为 O (1)。例如:

import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("mango");
        set.add("orange");
        set.add("mango"); 
        System.out.println(set.size()); 
    }
}

  • TreeSet:基于红黑树实现,不允许元素重复,元素按自然顺序(或自定义顺序)排序。它的添加、删除和查找操作时间复杂度为 O (log n),适用于需要对元素进行排序的场景。例如:

import java.util.Set;
import java.util.TreeSet;

public class TreeSetExample {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        set.add(3);
        set.add(1);
        set.add(2);
        System.out.println(set); 
    }
}

  • LinkedHashSet:继承自 HashSet,基于哈希表和链表实现,既具有 HashSet 的快速查找特性,又能保持元素插入顺序。例如:

import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetExample {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();
        set.add("peach");
        set.add("plum");
        set.add("pear");
        System.out.println(set); 
    }
}

3. Map(映射)

  • HashMap:基于哈希表实现,存储键值对(key - value),键不允许重复,值可以重复,键值对无序。它通过计算键的哈希码来确定存储位置,对于非哈希冲突情况下,查找、插入和删除操作的平均时间复杂度为 O (1)。例如:

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        System.out.println(map.get("one")); 
    }
}

  • TreeMap:基于红黑树实现,键值对按键的自然顺序(或自定义顺序)排序。它的添加、删除和查找操作时间复杂度为 O (log n),适用于需要按键排序的场景。例如:

import java.util.Map;
import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>();
        map.put(3, "apple");
        map.put(1, "banana");
        map.put(2, "cherry");
        System.out.println(map); 
    }
}

  • LinkedHashMap:继承自 HashMap,基于哈希表和链表实现,能保持键值对的插入顺序或访问顺序。例如:

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapExample {
    public static void main(String[] args) {
        Map<String, Integer> map = new LinkedHashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        map.get("one"); 
        System.out.println(map); 
    }
}

  • ConcurrentHashMap:线程安全的哈希表,允许多个线程同时读,部分线程写。它在 JDK 1.7 中采用分段锁机制,JDK 1.8 中采用 CAS(Compare - And - Swap)和 synchronized 关键字实现,提高了并发性能。例如:

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        System.out.println(map.get("one")); 
    }
}

webfilter是springboot的吗

WebFilter 不是 Spring Boot 特有的。它是 Java Servlet 规范中的一部分,从 Servlet 3.0 开始引入 。

1. 在 Java Servlet 规范中的角色

WebFilter 用于对 Servlet 容器处理的请求和响应进行过滤操作。通过实现 WebFilter 接口,可以在请求到达 Servlet 之前或者在 Servlet 生成响应之后执行一些通用的逻辑,比如日志记录、权限检查、字符编码设置等。例如:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/example/*")
public class MyFilter implements WebFilter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 在请求到达Servlet之前执行的逻辑
        System.out.println("Before Servlet");
        chain.doFilter(request, response);
        // 在Servlet处理请求之后执行的逻辑
        System.out.println("After Servlet");
    }

    // 其他接口方法的默认实现
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void destroy() {}
}

在上述代码中,@WebFilter("/example/*") 注解指定了该过滤器将应用于以 /example/ 开头的所有 URL 路径。doFilter 方法包含了过滤逻辑,FilterChain 的 doFilter 方法用于将请求传递给下一个过滤器或 Servlet 。

2. 在 Spring Boot 中的使用

Spring Boot 作为一个基于 Spring 框架的快速开发框架,很好地支持了 Servlet 规范中的 WebFilter 。在 Spring Boot 项目中,可以很方便地创建和注册自定义的过滤器。

  • 基于注解方式:和普通 Servlet 项目类似,通过 @WebFilter 注解定义过滤器,并使用 @ServletComponentScan 注解扫描包含 @WebFilter 等 Servlet 组件的包,从而使过滤器生效。例如:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan(basePackages = "com.example.demo.filter")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

  • 通过配置类注册:也可以通过创建一个配置类,使用 @Bean 方法注册 FilterRegistrationBean 来注册过滤器。例如:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.Filter;

@Configuration
public class FilterConfig implements WebMvcConfigurer {

    @Bean
    public Filter characterEncodingFilter() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF - 8");
        filter.setForceEncoding(true);
        return filter;
    }
}

在这个例子中,通过 @Bean 方法创建了一个 CharacterEncodingFilter 实例,并将其注册为一个过滤器,用于设置请求和响应的字符编码为 UTF - 8 。

所以,虽然 Spring Boot 对 WebFilter 有很好的支持,但 WebFilter 本身源自 Java Servlet 规范,并非 Spring Boot 所特有。

近日总结:

最近很忙......但是早上又赖床......

但是又不得不起。

昨晚上误入一个大三前端学妹面试,嗯......有个问题,是什么原因让你们不在自己小组的实验室面试,去到其他小组的实验室面试的,这个实验室每天晚上都有人用,你们不用提前看一下吗?

的亏那天晚上没有老师过来讲东西

还有你旁边那个不面试的男生在那里是起了个什么作用,我进去后,小声背东西,那个男生不行动起来和我说,你自己终止面试和我说,我以为我声音已经够小了,我还专门离你远远的坐着,人家面试官啥也没听到,结果你来一句你听到了,就这,还是肯定句,陈述句。

就是无论多大的声音,只要你听到了,就不准人家留在这里是吧,可你们也没问这是其他小组的实验室啊。

就鸠占鹊巢呗,然后人家来了还要赶人家走......

66666666666666666666666666666666666666666666666666666666

而且你面试的确还有很大的进步空间,老给自己挖坑是个什么事儿,没听懂人面试官说的话直接大声 “ 啊?”

人家面试官都否定你了,你还要末尾了小声的来句,“我觉得.....就应该......”

语速也是时快时慢,时大时小,急急躁躁的,就很抽象。

最后受不了了,我去楼道了......

好无语......


网站公告

今日签到

点亮在社区的每一天
去签到