找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1746

积分

0

好友

228

主题
发表于 18 小时前 | 查看: 2| 回复: 0

大家好,我是小林。

之前跟大家盘点26届校招薪资的时候,发现有同学留言说想看海康威视的薪资情况。

用户留言求海康威视薪资信息

看到留言后,我立马去收集整理了一波26届海康威视开发岗位的薪资数据。现在就来给大家分享一下,顺便聊聊它的面试难度。

26届海康威视开发岗薪资一览

先看数据。以下是基于我收集到的信息整理的薪资表格,年终奖一般是2-4个月,我这里取个中间值,统一按15薪来计算年总包,方便大家对比。

海康威视26届开发岗薪资表

为了方便理解,我把不同offer档次的薪资范围划分了一下:

  • 绿色部分可以看作是普通Offer,月薪在14k到15k左右。
  • 橙色部分对应SP Offer,月薪在16k到18k。
  • 红色部分则是SSP Offer,月薪达到了19k以上。

总体来看,这个薪资水平在行业中算是“体面”的。虽然谈不上顶尖,但也绝对不低,属于比较典型的中厂待遇范围,月薪集中在15k到20k这个区间。

我也对比了一下去年的数据,可以说基本没什么变化,薪资范围相当稳定。下面是去年25届的几个数据点,大家可以参考:

海康威视25届开发岗薪资示例

除了薪资,还有个福利点值得一提:海康威视是六险一金,并且公积金是顶格12%缴纳,这一点对员工来说还是很不错的。

聊完薪资,相信很多同学更关心的是:海康威视的面试到底难不难?都问些什么?

海康威视后端开发一面真题复盘

从我收集到的信息来看,这场面试的考察点主要集中在JavaSE、排序算法、Redis、MySQL、Java并发、操作系统、Spring这些核心知识上。

面试的风格属于广度大于深度,涉及的知识面很广,但每个知识点通常只问一到两个核心问题,不会在一个点上追问得特别深。所以,只要你基础扎实,对各个知识点都有所了解,应对起来应该不会太吃力。

下面我就把收集到的一道后端开发一面真题,结合我的理解和扩展,给大家详细解析一遍。

海康威视后端开发一面问题列表

1. 你是如何理解Java的面向对象特性?

我认为Java的面向对象特性主要体现在封装、继承、多态这三个方面,这也是日常开发中使用最多的。

  • 封装:核心是把数据和操作数据的方法绑定在一起,对外隐藏实现细节,只暴露必要的接口。例如,将类的属性设为private,通过publicgetter/setter访问,可以在setter中加入数据校验逻辑,保证有效性。封装也提高了代码的可维护性,内部实现变更只要接口不变,外部调用方就无需感知。
  • 继承:允许子类复用父类的代码,避免重复。但需要谨慎使用,因为继承带来了较高的耦合度(父类改动可能影响所有子类)。在实际设计中,如果仅仅是为了代码复用,很多时候组合优于继承
  • 多态:这是面向对象的精髓。它允许同一接口有不同的实现,运行时根据实际对象类型决定调用哪个方法。最常见的应用就是定义接口或抽象类,然后有多个实现类。例如定义一个Payment支付接口,有AliPayWeChatPay等实现。业务代码只依赖接口,传入什么实现就调用什么方法,这使得系统对扩展开放,对修改封闭,非常灵活。

2. 说一下你对泛型的了解?

泛型是Java中用于提供编译时类型安全的机制。它允许在定义类、接口或方法时使用类型参数,在使用时再指定具体类型。

为什么需要泛型?

  1. 代码复用:避免为不同类型编写重复的逻辑。例如,一个加法方法,没有泛型时需要为int, float, double分别重载:

    private static int add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
        return a + b;
    }
    // 还需要为float, double写同样的方法...

    使用泛型后,可以复用为一个方法:

    private static <T extends Number> double add(T a, T b) {
        System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
        return a.doubleValue() + b.doubleValue();
    }
  2. 类型安全:消除强制类型转换,避免ClassCastException。没有泛型时,集合可以放入任意对象,取出时需要强制转换,容易出错:

    List list = new ArrayList();
    list.add("xxString");
    list.add(100d);
    // 取出时需强制转换,可能发生类型转换异常
    String str = (String) list.get(1); // 错误!

    引入泛型后,编译器会在编译期进行检查:

    List<String> list = new ArrayList<String>();
    list.add("xxString"); // OK
    // list.add(100d); // 编译错误!

3. java 集合中 map,list,set的底层数据结构是怎样的?

  • List:最常用的是ArrayListLinkedList
    List的实现类

    • ArrayList底层是动态数组,默认容量10,扩容为原1.5倍。支持O(1)的随机访问,但插入/删除(非末尾)需要移动元素,效率O(n)
    • LinkedList底层是双向链表。插入/删除(已知节点位置)只需修改指针,效率O(1),但随机访问需要遍历,效率O(n)。实际开发中,除非有大量中间位置的插入删除,否则ArrayList通常是更优选择。
  • Set:最常用的是HashSetTreeSet
    Set的实现类

    • HashSet底层基于HashMap,元素作为HashMap的key存储,因此具有O(1)的查找、插入性能,但无序
    • TreeSet底层基于TreeMap(红黑树),元素是有序的(自然顺序或自定义比较器),查找、插入性能为O(log n)
    • LinkedHashSetHashSet基础上增加了一个双向链表来维护插入顺序
  • Map:这是最复杂的集合。
    Map的实现类

    • HashMap:JDK1.8后是“数组+链表/红黑树”结构。默认容量16,负载因子0.75。发生哈希冲突时,先拉链法解决,当链表长度>8且数组长度>=64时,链表会转化为红黑树以提升性能。HashMap非线程安全。
    • ConcurrentHashMap:线程安全的Map。在Java高并发场景下,它比HashtableCollections.synchronizedMap性能好得多。JDK1.8后采用CAS + synchronized锁住单个桶位的方式实现并发控制,粒度更细。
    • TreeMap:基于红黑树,Key有序,支持范围查询。
    • LinkedHashMap:继承自HashMap,并通过双向链表维护了元素的插入顺序或访问顺序(可用于实现简单的LRU缓存)。
    • Hashtable:古老的线程安全Map,方法全部用synchronized修饰,性能较差,且不允许null键值,现已很少使用。

4. 常用的排序算法有哪些,及其时间复杂度

常用排序算法对比表

  • 冒泡排序:相邻元素比较交换,逐步将最大/小值“冒泡”到一端。平均/最坏O(n²),最好O(n),空间O(1),稳定。
  • 插入排序:将元素逐个插入到前面已排序序列的合适位置。平均/最坏O(n²),最好O(n),空间O(1),稳定。
  • 选择排序:每次从剩余元素中选择最小/大值放到已排序序列末尾。始终为O(n²),空间O(1),不稳定。
  • 快速排序:选取基准,划分左右子数组递归排序。平均O(n log n),最坏O(n²)(如已排序数组),空间O(log n),不稳定。
  • 归并排序:分治法,递归划分子数组,然后合并有序子数组。始终O(n log n),空间O(n),稳定。
  • 堆排序:构建最大/小堆,交换堆顶与末尾元素并调整堆。始终O(n log n),空间O(1),不稳定。

5. 谈一谈对redis的理解,高并发下会有什么问题?

Redis是一个基于内存的高性能键值数据库,常用作缓存、分布式锁、消息队列等。其单机QPS可达数万甚至十万级,支持String、List、Hash、Set、ZSet等多种数据结构。

在高并发场景下,使用Redis做缓存通常会遇到以下几个经典问题:

  1. 缓存穿透
    查询一个数据库中根本不存在的数据,导致请求绕过缓存直接打到数据库。
    缓存穿透示意图

    • 解决方案
      • 布隆过滤器:在查询缓存前,先用布隆过滤器判断Key是否存在,不存在则直接返回。
      • 缓存空值:即使数据库查不到,也将这个Key在Redis中缓存为一个空值(如null),并设置一个较短的过期时间。
  2. 缓存击穿(热点Key失效)
    某个访问量巨大的热点Key突然过期,瞬间大量请求穿透缓存,直接访问数据库。
    缓存击穿示意图

    • 解决方案
      • 设置永不过期:对热点Key不设置过期时间,或通过后台任务异步更新。
      • 互斥锁:当缓存失效时,只允许一个线程去查询数据库并重建缓存,其他线程等待或重试。
  3. 缓存雪崩
    大量Key在同一时间段集中过期,或Redis服务宕机,导致所有请求涌向数据库。
    缓存雪崩示意图

    • 解决方案
      • 差异化过期时间:给缓存Key的过期时间加上一个随机值,避免同时失效。
      • 高可用架构:采用Redis主从+哨兵或集群模式,避免单点故障。
      • 服务降级与熔断:当发现数据库压力过大时,对非核心业务进行降级或熔断。
      • 本地缓存兜底:可以使用Guava Cache等本地缓存作为二级缓存,在Redis失效时提供一定保护。
  4. 数据库与缓存一致性问题
    这是最复杂的问题。常见的“先更新数据库,再删缓存”或“先删缓存,再更新数据库”都可能在高并发下产生短暂不一致。

    • 解决方案
      • 延迟双删:先删缓存 -> 更新数据库 -> 休眠一段时间 -> 再删一次缓存。
      • 设置合理的过期时间:对于一致性要求不高的场景,可以接受短暂不一致,通过设置较短的缓存过期时间来最终达成一致。
      • 订阅数据库Binlog:通过Canal等工具监听数据库变更,异步更新或删除缓存(最终一致性)。

6. springboot和springmvc的差异是什么?设计理念的区别?

首先要明确,Spring Boot不是用来替代Spring MVC的,它们是不同层面的概念。

  • Spring MVC是Spring框架中的一个Web MVC框架,专注于处理HTTP请求,实现控制器、模型、视图的分离。
  • Spring Boot是一个快速应用开发脚手架,它整合了Spring技术栈(包括Spring MVC),并通过一系列默认配置和 starter 依赖,极大地简化了基于Spring的应用搭建、开发、部署。

核心差异

  1. 配置方式:Spring MVC需要大量XML或Java Config配置(如DispatcherServlet、视图解析器、数据源等)。Spring Boot推崇约定大于配置,提供大量自动配置,开发者只需极简配置(如一个application.yml)即可运行。
  2. 部署:传统的Spring MVC应用通常需要打包成WAR部署到外部Tomcat等Web容器。Spring Boot应用内嵌了Web服务器(如Tomcat),可以直接打包成可执行的JAR文件运行。
  3. 依赖管理:Spring Boot通过spring-boot-starter-*系列依赖,自动管理版本和传递依赖,解决了传统Spring项目令人头疼的“Jar包地狱”问题。
  4. 生产就绪特性:Spring Boot内置了Actuator模块,提供了健康检查、指标监控、配置刷新等开箱即用的生产级特性,非常适合微服务架构。

设计理念

  • Spring MVC:提供灵活、可定制的Web框架,将控制权交给开发者,适合需要精细控制的项目。
  • Spring Boot:旨在简化开发、快速上手,通过默认最佳实践和自动配置,让开发者能更专注于业务逻辑,提升开发效率。

简单说,Spring Boot是包含了Spring MVC的“一站式”解决方案平台

7. springboot常用注解有哪些?

  • Bean声明与扫描

    • @Component:通用组件注解。
    • @Controller / @RestController:标识控制层组件,后者是@Controller+@ResponseBody
    • @Service:标识业务层组件。
    • @Repository:标识数据访问层组件。
    • @Bean:在配置类中声明方法,将返回值注册为Bean。
    • @ComponentScan:指定扫描包路径。
  • 依赖注入

    • @Autowired:按类型自动注入。
    • @Resource:默认按名称注入。
    • @Qualifier:与@Autowired配合,指定注入的Bean名称。
  • 读取配置

    • @Value:注入单个属性值,如@Value(“${server.port}”)
    • @ConfigurationProperties:将一组配置属性绑定到一个Bean上。
  • Web请求映射

    • @RequestMapping:通用请求映射。
    • @GetMapping, @PostMapping, @PutMapping, @DeleteMapping:分别对应HTTP方法。
  • 其他常用

    • @Transactional:声明式事务管理。
    • @Scheduled:声明定时任务。

8. MySQL常用的sql优化思路是什么?

  1. 使用EXPLAIN分析:这是第一步,通过EXPLAIN命令查看SQL的执行计划,关注type(访问类型)、key(使用的索引)、rows(扫描行数)等字段。
  2. 合理创建与使用索引
    • WHEREORDER BYGROUP BYJOIN ON条件中的字段创建索引。
    • 使用联合索引并遵循最左前缀原则
    • 避免索引失效:如对索引字段进行函数计算、类型转换、使用%开头的LIKE模糊查询、使用OR连接非索引字段等。
  3. 优化查询语句
    • 避免SELECT *,只取需要的列。
    • 尽量使用覆盖索引(索引包含所有查询字段)。
    • 优化JOIN查询,确保被驱动表的关联字段有索引,且小表驱动大表
    • 优化深分页LIMIT M, N,可尝试用WHERE id > last_id LIMIT NJOIN子查询等方式。
  4. 考虑读写分离:主库写,从库读,分摊压力。
  5. 表结构优化
    • 根据业务进行分库分表(水平/垂直拆分),应对单表数据量过大的问题。
    • 进行适度的字段冗余,以减少关联查询。
  6. 引入缓存:使用Redis等缓存热点数据,减少数据库直接访问。

9. 进程和线程的区别是什么?

进程与线程结构对比图

  • 资源分配与调度进程是操作系统资源分配(内存、文件句柄等)的基本单位线程是CPU调度和执行的基本单位
  • 内存与开销:进程拥有独立的地址空间,一个进程崩溃通常不影响其他进程,但创建、切换开销大。线程共享进程的资源,创建、切换开销小,但一个线程崩溃可能导致整个进程崩溃。
  • 通信方式:进程间通信(IPC)需要特殊的机制,如管道、消息队列、共享内存等。线程间通信可以直接读写共享的进程数据,但需要同步机制(如锁)来保证安全。

10. 线程间通信方式有哪些?

Java中线程间通信主要围绕共享变量的同步与协调。

  1. 等待/通知机制

    • Object类的wait(), notify(), notifyAll()方法(需在synchronized同步块中使用)。
    • LockCondition接口:Conditionawait(), signal(), signalAll()方法,比Object的等待/通知更灵活,可以创建多个等待条件。
    // Lock + Condition 示例
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    
    public void consumer() throws InterruptedException {
        lock.lock();
        try {
            while (!conditionMet) {
                condition.await(); // 等待
            }
            // 消费数据
        } finally {
            lock.unlock();
        }
    }
    
    public void producer() {
        lock.lock();
        try {
            // 生产数据
            condition.signal(); // 唤醒一个等待线程
        } finally {
            lock.unlock();
        }
    }
  2. volatile共享变量:通过volatile关键字保证变量的可见性,一个线程修改后,其他线程能立即看到最新值。常用于简单的状态标志位。

  3. 并发工具类

    • CountDownLatch:一个或多个线程等待其他线程完成操作。
    • CyclicBarrier:一组线程相互等待,到达屏障点后一起继续执行。
    • Semaphore:控制同时访问特定资源的线程数量(信号量)。

11. 多线程下, Java资源一致性如何保证?

保证多线程下资源一致性,核心是解决原子性、可见性、有序性问题。

  1. synchronized关键字:JVM内置锁。能同时保证原子性、可见性和有序性。但它是互斥锁,性能有一定开销。

    public synchronized void safeMethod() {
        // 操作共享资源
    }
  2. Lock接口:如ReentrantLock。显式锁,比synchronized更灵活(可尝试获取、可中断、可设置公平性等),但必须手动lock()unlock()

  3. volatile关键字:只能保证可见性有序性(禁止指令重排序),不能保证复合操作的原子性(如i++)。

  4. 原子类:如AtomicInteger。底层使用CAS(Compare And Swap)操作保证单个变量的原子性更新,性能通常优于锁。适合计数器等场景。

    private AtomicInteger count = new AtomicInteger(0);
    public void increment() {
        count.incrementAndGet(); // 原子操作
    }
  5. ThreadLocal:为每个线程创建变量的独立副本,从根本上避免共享,适用于上下文传递(如用户信息)。但需注意使用后及时remove(),防止内存泄漏。

选择哪种机制,取决于具体场景:简单状态用volatile;简单原子操作用原子类;复杂同步用synchronizedLock;线程隔离用ThreadLocal

12. 其他

  • 介绍一下实习项目,讲一下全流程。
  • 对岗位的未来规划。

总结

总的来说,海康威视为应届生提供的薪资待遇在行业中处于一个稳健、体面的水平,其面试则侧重于对Java及后端常用技术栈基础广度的考察。对于正在准备面试求职的同学来说,夯实本文中涉及的基础知识,是应对此类企业面试的关键。

希望这份薪资数据和面试解析能对大家有所帮助!




上一篇:掌握分库分表核心术语:从数据分片到影子库的实战解析
下一篇:FactorMiner:自进化Agent框架,用经验记忆挖掘高质量量化Alpha因子
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-1 21:01 , Processed in 0.553744 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表