顺丰科技开奖了,但拒了。。。
创始人
2025-03-07 09:01:09
0

大家好,我是小林。

我在图解网站把在公众号发表过的的企业面经收录到了这里。

https://xiaolincoding.com/

目前网站累计收录了 50 多家企业的真实面经,如下图:

刚好发现互联网中厂面经里缺失了「顺丰科技」的,那这次我们来补上!

先来看看顺丰科技的校招薪资如何?

看有 25 届的硕士 211 的同学爆料是开了 20k x 15 (30w 总包),我也有看到有 17.5k 的,所以整体是在 17k-20k 这样,算是互联网中厂薪资的范畴。

不过,看到不少同学都拒了顺丰,倒不是钱的问题,主要是之前有同学说顺丰卡转正,导致风评不是很好,所以大家都害怕发生在自己身上,对选择顺丰就会比较谨慎的态度了。

那顺丰科技的面试难点如何?

这次来看看顺丰科技Java 后端的凉经,同学准备的不是很充分,答的不理想,最后就挂了,拷打还是比较全方位的,Java集合、并发、JVM、Spring、MySQL、Redis 都各问了几个问题。

顺丰科技(一面凉经)ArrayList 和 LinkedList 的使用场景是什么?

  • ArrayList适用于需要频繁访问集合元素的场景。它基于数组实现,可以通过索引快速访问元素,因此在按索引查找、遍历和随机访问元素的操作上具有较高的性能。当需要频繁访问和遍历集合元素,并且集合大小不经常改变时,推荐使用ArrayList

  • LinkedList适用于频繁进行插入和删除操作的场景。它基于链表实现,插入和删除元素的操作只需要调整节点的指针,因此在插入和删除操作上具有较高的性能。当需要频繁进行插入和删除操作,或者集合大小经常改变时,可以考虑使用LinkedList。

hashmap 什么场景使用?

hashmap的好处是可以以O(1)时间查询复杂度快速查询到数据。

当我们需要频繁访问某些数据,且这些数据的生成或获取成本较高时,可以使用 HashMap作为缓存来提高性能。

例如,在一个计算密集型的应用中,对于一些已经计算过的结果,你可以将其存储在 HashMap中,下次需要使用时直接从 HashMap中获取,而不需要重新计算。

importjava.util.HashMap;

publicclassCacheExample{

privatestaticfinalHashMap cache = newHashMap<>;

publicstaticintfactorial(intn){

if(n == 0|| n == 1) {

return1;

}

if(cache.containsKey(n)) {

returncache.get(n);

}

intresult = n * factorial(n - 1);

cache.put(n, result);

returnresult;

}

publicstaticvoidmain(String[] args){

intnum = 5;

System.out.println("Factorial of "+ num + " is: "+ factorial(num));

}

}

线程休眠的方法有哪些?有什么区别?

方法 所属类 是否需要同步块 是否释放锁 时间单位 异常处理
Thread.sleep Thread

类(静态方法)

毫秒

InterruptedException
TimeUnit

TimeUnit

枚举类

多种(如秒、分等)

InterruptedException
Object.wait Object

类(实例方法)

毫秒

InterruptedException
  • Thread.sleepThread类的静态方法,用于使当前正在执行的线程暂停执行指定的毫秒数。它是 Thread类的静态方法,只能让当前正在执行的线程休眠。会抛出 InterruptedException异常,需要进行异常处理,当线程在休眠期间被其他线程调用 interrupt方法中断时,会抛出该异常。线程休眠期间不会释放对象锁,如果线程持有锁,在休眠期间其他线程无法获取该锁。

  • TimeUnit是 Java 中一个枚举类,位于 java.util.concurrent包下,提供了更方便的时间单位转换和线程休眠方法。本质上也是调用 Thread.sleep方法实现休眠,同样会抛出 InterruptedException异常。同样不会释放对象锁。

  • Object.waitObject类的实例方法,用于使当前线程进入等待状态,直到其他线程调用该对象的 notifynotifyAll方法唤醒它。可以指定等待的时间,若不指定则会一直等待。必须在 synchronized代码块或同步方法中调用,因为它是用来协调线程对共享资源的访问。会释放对象锁,当线程调用 wait方法进入等待状态时,会释放它持有的对象锁,其他线程可以获取该锁。同样会抛出 InterruptedException异常,当线程在等待期间被其他线程调用 interrupt方法中断时,会抛出该异常。

假设有3个线程,需要并行执行,怎么办?
  • 第一种方法:通过实例化Thread对象并调用start方法启动线程。

public classParallelThreadsExample {

publicstaticvoidmain(String[] args){

// 创建三个线程

Thread t1 = newThread(newTask, "Thread-1");

Thread t2 = newThread(newTask, "Thread-2");

Thread t3 = newThread(newTask, "Thread-3");

// 启动线程

t1.start;

t2.start;

t3.start;

}

staticclassTaskimplementsRunnable{

@Override

publicvoidrun{

System.out.println(Thread.currentThread.getName + " 正在执行");

}

}

}

  • 第二种方法:通过ExecutorService管理线程池,复用线程资源。

importjava.util.concurrent.ExecutorService;

importjava.util.concurrent.Executors;

publicclassThreadPoolExample{

publicstaticvoidmain(String[] args){

// 创建固定大小为3的线程池

ExecutorService executor = Executors.newFixedThreadPool(3);

// 提交任务

for(inti = 0; i < 3; i++) {

executor.execute(newTask);

}

// 关闭线程池(等待已提交任务完成)

executor.shutdown;

}

staticclassTaskimplementsRunnable{

@Override

publicvoidrun{

System.out.println(Thread.currentThread.getName + " 正在执行");

}

}

}

  • 第三种方法:通过异步编程实现任务的并行执行:

importjava.util.concurrent.CompletableFuture;

publicclassCompletableFutureExample{

publicstaticvoidmain(String[] args){

CompletableFuture future1 = CompletableFuture.runAsync( -> task("Task-1"));

CompletableFuture future2 = CompletableFuture.runAsync( -> task("Task-2"));

CompletableFuture future3 = CompletableFuture.runAsync( -> task("Task-3"));

// 等待所有任务完成

CompletableFuture.allOf(future1, future2, future3).join;

}

staticvoidtask(String name){

System.out.println(name + " 正在执行,线程:"+ Thread.currentThread.getName);

}

}

JVM内存模型介绍一下

根据 JDK 8 规范,JVM 运行时内存共分为虚拟机栈、堆、元空间、程序计数器、本地方法栈五个部分。还有一部分内存叫直接内存,属于操作系统的本地内存,也是可以直接操作的。

JVM的内存结构主要分为以下几个部分:

  • 程序计数器:可以看作是当前线程所执行的字节码的行号指示器,用于存储当前线程正在执行的 Java 方法的 JVM 指令地址。如果线程执行的是 Native 方法,计数器值为 null。是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域,生命周期与线程相同。

  • Java 虚拟机栈:每个线程都有自己独立的 Java 虚拟机栈,生命周期与线程相同。每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。可能会抛出 StackOverflowError 和 OutOfMemoryError 异常。

  • 本地方法栈:与 Java 虚拟机栈类似,主要为虚拟机使用到的 Native 方法服务,在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。本地方法执行时也会创建栈帧,同样可能出现 StackOverflowError 和 OutOfMemoryError 两种错误。

  • Java 堆:是 JVM 中最大的一块内存区域,被所有线程共享,在虚拟机启动时创建,用于存放对象实例。从内存回收角度,堆被划分为新生代和老年代,新生代又分为 Eden 区和两个 Survivor 区(From Survivor 和 To Survivor)。如果在堆中没有内存完成实例分配,并且堆也无法扩展时会抛出 OutOfMemoryError 异常。

  • 方法区(元空间):在 JDK 1.8 及以后的版本中,方法区被元空间取代,使用本地内存。用于存储已被虚拟机加载的类信息、常量、静态变量等数据。虽然方法区被描述为堆的逻辑部分,但有 “非堆” 的别名。方法区可以选择不实现垃圾收集,内存不足时会抛出 OutOfMemoryError 异常。

  • 运行时常量池:是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,具有动态性,运行时也可将新的常量放入池中。当无法申请到足够内存时,会抛出 OutOfMemoryError 异常。

  • 直接内存:不属于 JVM 运行时数据区的一部分,通过 NIO 类引入,是一种堆外内存,可以显著提高 I/O 性能。直接内存的使用受到本机总内存的限制,若分配不当,可能导致 OutOfMemoryError 异常。

堆上面的空间是怎么划分的?

在 JDK 1.8 中,JVM 堆内存依然采用分代式的设计,主要划分为新生代和老年代,不过有一个显著的变化是,取代了 JDK 8 之前的永久代(Permanent Generation),引入了元空间(Metaspace),但元空间并不属于堆内存,它是直接内存。

新生代是新创建的对象最初分配内存的区域,由于大部分对象的生命周期都很短,所以这个区域的垃圾回收操作比较频繁。新生代又进一步细分为Eden 区和 Survivor 区。

老年代主要用于存放经过多次 Minor GC 仍然存活的对象,以及一些大对象。大对象是指需要大量连续内存空间的对象,例如很长的数组等,这些大对象可能会直接被分配到老年代。当老年代的空间不足时,会触发一次 Full GC,Full GC 会对整个堆内存(包括新生代和老年代)进行垃圾回收,其成本比 Minor GC 高得多,因为它需要扫描和回收更多的对象。

如果有个大对象一般是在哪个区域?

大对象通常会直接分配到老年代。

新生代主要用于存放生命周期较短的对象,并且其内存空间相对较小。如果将大对象分配到新生代,可能会很快导致新生代空间不足,从而频繁触发 Minor GC。而每次 Minor GC 都需要进行对象的复制和移动操作,这会带来一定的性能开销。将大对象直接分配到老年代,可以减少新生代的内存压力,降低 Minor GC 的频率。

大对象通常需要连续的内存空间,如果在新生代中频繁分配和回收大对象,容易产生内存碎片,导致后续分配大对象时可能因为内存不连续而失败。老年代的空间相对较大,更适合存储大对象,有助于减少内存碎片的产生。

springboot的好处是什么?

  • Spring Boot 提供了自动化配置,大大简化了项目的配置过程。通过约定优于配置的原则,很多常用的配置可以自动完成,开发者可以专注于业务逻辑的实现。

  • Spring Boot 提供了快速的项目启动器,通过引入不同的 Starter,可以快速集成常用的框架和库(如数据库、消息队列、Web 开发等),极大地提高了开发效率。

  • Spring Boot 默认集成了多种内嵌服务器(如Tomcat、Jetty、Undertow),无需额外配置,即可将应用打包成可执行的 JAR 文件,方便部署和运行。

索引为什么可以提升查询速度?

索引类似于书籍的目录,可以减少扫描的数据量,提高查询效率。如果查询的时候,没有用到索引就会全表扫描,这时候查询的时间复杂度是On。

如果用到了索引,那么查询的时候,可以基于二分查找算法,通过索引快速定位到目标数据, mysql 索引的数据结构一般是 b+树,其搜索复杂度为O(logdN),其中 d 表示节点允许的最大子节点个数为 d 个。

组合索引的好处是什么?

可以利用覆盖索引查询的优化,来避免回表,提升查询的效率。

比如,联合索引是(a ,b),那么针对下面这个查询:

select a, b fromtablewhere a =? and b =?

是覆盖索引的查询,查询不需要回表。

redis基本数据类型和使用场景说一下?

Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)

随着 Redis 版本的更新,后面又支持了四种数据类型:BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。Redis 五种数据类型的应用场景:

  • String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。

  • List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。

  • Hash 类型:缓存对象、购物车等。

  • Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。

  • Zset 类型:排序场景,比如排行榜、电话和姓名排序等。

Redis 后续版本又支持四种数据类型,它们的应用场景如下:

  • BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;

  • HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;

  • GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;

  • Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。

redis hash的好处是什么?

Redis Hash 可以将多个相关的字段存储在一个键下,相比为每个字段单独设置一个键,Hash 结构能更紧凑地存储数据。

例如,在存储用户信息时,如果每个用户有多个属性(如姓名、年龄、地址等),使用 Hash 可以将这些属性存储在一个键下,而不是为每个属性创建一个独立的键,减少了键名的存储开销。

Hash 的字段查找和更新操作的时间复杂度都是 O(1),这意味着无论 Hash 中存储了多少个字段,查找和更新操作的性能都非常高。这使得 Redis Hash 非常适合存储需要频繁读写的数据,如缓存用户信息、配置信息等。

# 快速获取用户的年龄

HGET user:1age

Hash 可以将相关的数据组织在一起,形成一个逻辑上的分组。例如,在一个电商系统中,可以使用 Hash 来存储每个商品的详细信息,包括商品名称、价格、库存等,将这些信息关联在一起,方便管理和查询。

# 存储商品信息

HMSET product:1name "iPhone 14"price 999stock 100

通过将相关数据存储在一个 Hash 中,可以更方便地对这些数据进行管理和维护,例如批量删除、遍历等操作。

相关内容

热门资讯

十分钟了解!wpk外挂是真的还... 十分钟了解!wpk外挂是真的还是假的,德扑计算软件,wpk德州插件(辅助透视)-哔哩哔哩是一款可以让...
3分钟机器人!we-poker... 您好,WepOker这款游戏可以开挂的,确实是有挂的,需要了解加微【485275054】很多玩家在这...
记者揭秘"aa扑克有... 您好,Wepoke代码这款游戏可以开挂的,确实是有挂的,需要了解加微【757446909】很多玩家在...
玩家必看透视!佛手在线透视辅助... 玩家必看透视!佛手在线透视辅助器(辅助挂)原来真的真的是有挂(2022已更新)(哔哩哔哩);一、佛手...
原创 国... 国乒美女名将钱天一为何无法成为主力?答案很明显了,很多人还没发现!网友热议一针见血地指出:钱天一右手...
程序员教你!Wepoker辅助... 程序员教你!Wepoker辅助透视免费,wepoker有透视功能,大神讲解(有挂教学)-哔哩哔哩;相...
安徽安利材料:AI 检测效率提... 【人工智能创新应用为产业发展注入新动能】在安徽安利材料科技股份有限公司,待出厂的合成革迅速进入浪潮云...
六分钟安装!we poker辅... 六分钟安装!we poker辅助器(透视器),wEPoker原来确实是有挂,高科技教程(有挂经验)是...
两分钟透视辅助!pokerrr... 两分钟透视辅助!pokerrrr2辅助神器,aapoker辅助工具存在,德扑之星有规律(软件透明挂)...
发现一款!中至乐平麻将小程序技... 发现一款!中至乐平麻将小程序技巧,线上德州ai机器人,详细教程(有挂推荐)-哔哩哔哩;中至乐平麻将小...