<免费>搜狐畅游JAVA校招面经(往年)

1.集合类都哪些? 

Java中的集合类主要由Collection和Map这两个接口派生而出,其中Collection接口又派生出三个子接口,分别是Set、List、Queue。所有的Java集合类,都是Set、List、Queue、Map这四个接口的实现类,这四个接口将集合分成了四大类,其中

Set代表无序的,元素不可重复的集合;

List代表有序的,元素可以重复的集合;

Queue代表先进先出(FIFO)的队列;

Map代表具有映射关系(key-value)的集合。

这些接口拥有众多的实现类,其中最常用的实现类有HashSet、TreeSet、ArrayList、LinkedList、ArrayDeque、HashMap、TreeMap等。

2.HashMap怎么解决哈希冲突的?

为了解决碰撞,数组中的元素是单向链表类型。当链表长度到达一个阈值时,会将链表转换成红黑树提高性能。而当链表长度缩小到另一个阈值时,又会将红黑树转换回单向链表提高性能。

参考:

JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建

一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的

值加到链表中即可。

jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)

时并且数组长度达到64时,将链表转化为红黑树,以减少搜索时间。扩容

resize( ) 时,红黑树拆分成的树的结点数小于等于临界值6个,则退化成链表

 

3.HashMap什么时候会进行扩容,扩容机制是什么?

1.在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次每次扩容都是达到了扩容阈值(数组长度 *

0.75)

2.每次扩容的时候,都是扩容之前容量的2倍;

3.扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中

1>没有hash冲突的节点,则直接使用 e.hash & (newCap - 1) 计算新数组的索引位置

2>如果是红黑树,走红黑树的添加

3>如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash &oldCap)是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上

 

4.HashMap初始化是多少?

源码:

public HashMap (int initialCapacity) {

    this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

HashMap的默认大小为16

5.HashMap是线程安全的吗?怎么解决呢?

不是

(【为什么有线程安全问题?】

①调用put方法

假如有两个线程A和B,A希望插入一个key-value到HashMap中,首先会通过A的key得到桶的索引坐标,然后获取该桶的链表头结点,线程A的时间片用完,而此时B线程被调用执行,和线程A一样执行,只不过线程B成功的将数据插入到桶里面。假设线程A插入时候计算的坐标和B线程要插入的索引坐标是一致的,那么当B线程成功插入以后,线程A再次被调用运行的时候,它依然持有原来的链表头,但是它对B线程插入的过程一无所知,那么线程A就会对此坐标上的数据进行覆盖,那么线程B插入的数据就会消失,造成数据不一致的行为。

 

②扩容

JDK7版本中的HashMap扩容时使用头插法,假设此时有元素一指向元素二的链表,当有两个线程使用HashMap扩容的时,若线程一在迁移元素时阻塞,但是已经将指针指向了对应的元素,线程二正常扩容,因为使用的是头插法,迁移元素后将元素二指向元素一。此时若线程一被唤醒,在现有基础上再次使用头插法,将元素一指向元素二,形成循环链表。若查询到此循环链表时,便形成了死锁。而JDK8版本中的HashMap在扩容时保证元素的顺序不发生改变,就不再形成死锁,但是注意此时HashMap还是线程不安全的。)

 

1.使用Collections工具类,将线程不安全的Map包装成线程安全的Map;

2.使用java.util.concurrent包下的Map,如ConcurrentHashMap;

3.不建议使用Hashtable,虽然Hashtable是线程安全的,但是性能较差。

 

6.栈和队列各自的特性,怎么用栈实现队列的入队和出队?

栈是仅在仅在表尾进行插入和删除的线性表,也称为先进后出的线性表,允许插入和删除的一端为栈顶,另一端为栈底。

(队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表,也叫做先进先出表。

队列遵循“先进先出”的原则,栈遵循“后进先出”的原则,基于队列和栈的特点,可以通过两个栈实现队列的功能。

将栈分为存储栈和改变栈,存储栈负责“入队”的相关操作,将需要入队的元素通过栈的push()函数存入存储栈。当需要出队时,首先要判断改变栈是否为空,若为空则将存储栈中的元素全部push()到改变栈中,实现了位序颠倒,此时改变栈中的出栈次序已成为我们所需要的出队次序,当改变栈不为空时,则先要将改变栈中的元素进行出栈,此时的出栈操作等同于我们所要实现的出队操作。

实现上述操作还要判断所涉及的栈是否为空,只有当两个栈同时为空时,我们所设计的队列才为空。)

————————————————

版权声明:本文为CSDN博主「楠缘北谪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/m0_64258885/article/details/128393507

7.Java创线程都有哪几种方式?

创建线程有三种方式,分别是继承Thread类、实现Runnable接口、实现Callable接口。

1.通过继承Thread类来创建并启动线程的步骤如下:

定义Thread类的子类,并重写该类的run()方法,该run()方法将作为线程执行体。

创建Thread子类的实例,即创建了线程对象。

调用线程对象的start()方法来启动该线程。

2.通过实现Runnable接口来创建并启动线程的步骤如下:

定义Runnable接口的实现类,并实现该接口的run()方法,该run()方法将作为线程执行体。

创建Runnable实现类的实例,并将其作为Thread的target来创建Thread对象,Thread对象为线程对象。

调用线程对象的start()方法来启动该线程。

3.通过实现Callable接口来创建并启动线程的步骤如下:

创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值。然后再创建Callable实现类的实例。

使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

使用FutureTask对象作为Thread对象的target创建并启动新线程。

调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

 

8.Callable和Runable有什么区别?返回结果,抛出异常,内部方法

1、两者最大的不同点是:Callable的call()方法却必须有一个返回值,实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果,Runnable的run()方法没有返回值,只是一个void类型的方法;
2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

9.线程池有了解吗?

线程池(Thread Pool):把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销。

为什么要使用线程池?

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性。

————————————————

版权声明:本文为CSDN博主「time Friend」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/tumu6889/article/details/125257712

10.核心参数有哪些?

参考链接文章

11.如果说有一个线程池核心线程数为5,最大线程数为10,一共请求有15个并发进来的话,线程池有多少个线程在运行。

参考链接文章

12.final关键字的作用

final关键字可以修饰类、方法、变量,以下是final修饰这3种目标时表现出的特征:

final类:final关键字修饰的类不可以被继承。

final方法:final关键字修饰的方法不可以被重写。

final变量:final关键字修饰的变量,一旦获得了初始值,就不可以被修改。

 

扩展阅读

变量分为成员变量、局部变量。

final修饰成员变量:

类变量:可以在声明变量时指定初始值,也可以在静态初始化块中指定初始值;

实例变量:可以在声明变量时指定初始值,也可以在初始化块或构造方法中指定初始值;

final修饰局部变量:

 

可以在声明变量时指定初始值,也可以在后面的代码中指定初始值。

注意:被 final 修饰的任何形式的变量,一旦获得了初始值,就不可以被修改!

 

13.数据库的话在并发情况会产生哪些情况?

第一是脏读, 当一个事务正在访问数据并且对数据进行了修改,而这种修改

还没有提交到数据库中,这时另外一个事务也访问了这个数据,因为这个数

据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依

据“脏数据”所做的操作可能是不正确的。

 

第二是不可重复读:比如在一个事务内多次读同一数据。在这个事务还没有

结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之

间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。

这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重

复读。

 

第三是幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务

(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在

随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就

好像发生了幻觉一样,所以称为幻读。

 

14.幻读是什么意思?

幻读与不可重复读类似。它发生在一个事务 (T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在 随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

 

15.数据库是怎么解决这些问题的?

设置数据库隔离级别,对事物进行隔离。

 

四个隔离级别分别是:

第一个是,未提交读(read uncommitted)它解决不了刚才提出的所有问

题,一般项目中也不用这个。

 

第二个是读已提交(read committed)它能解

决脏读的问题的,但是解决不了不可重复读和幻读。

 

第三个是可重复读 (repeatable read)它能解决脏读和不可重复读,但是解决不了幻读,这个也是mysql默认的隔离级别。

 

第四个是串行化(serializable)它可以解决刚才提出来的所有问题,但是由于让是事务串行执行的,性能比较低。

 

所以,我们一般使用的都是mysql默认的隔离级别:可重复读

 

16.隔离级别的底层是用什么来控制这些数据的呢?就是隔离级别怎么保证的? (你解释一下mvcc)

事务的隔离性是由锁和mvcc实现的。

 

其中mvcc的意思是多版本并发控制。指维护一个数据的多个版本,使得读写

操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段,

第二个是undo log日志,第三个是readView读视图

 

隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事

务id),记录每一次操作的事务id,是自增的;另一个字段是roll_pointer(回

滚指针),指向上一个版本的事务版本记录地址;

 

undo log主要的作用是记录回滚日志,存储老版本数据,在内部会形成一个

版本链,在多个事务并行操作某一行记录,记录不同事务修改数据的版本,

通过roll_pointer指针形成一个链表;

 

readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规

则和当前的一些事务id判断该访问那个版本的数据,不同的隔离级别快照读

是不一样的,最终的访问的结果不一样。如果是rc隔离级别,每一次执行快

照读时生成ReadView,如果是rr隔离级别仅在事务中第一次执行快照读时生

成ReadView,后续复用。

 

17.锁的话都有哪几种?我回答了MySQL的锁,应该要回答读锁,写锁。

1. synchronized锁:是Java中最常用的锁,可以用来实现对象级别的同步。

2. ReentrantLock锁:是JDK提供的一种可重入锁,与synchronized锁相比,它提供了更强大的功能,如可中断锁、公平锁、多条件变量等。

3. ReadWriteLock锁:是JDK提供的一种读写锁,可以分离读访问和写访问,可以提高并发性能。

4. StampedLock锁:是JDK1.8新引入的一种乐观锁,它可以提高读访问的并发性能,同时支持读写锁的支持。

5. Semaphore锁和CountDownLatch锁:它们是一些辅助的同步工具,可以协同多线程之间的操作。Semaphore锁可以控制多个线程同时访问某些资源,CountDownLatch锁可以使某个线程等待其他线程完成操作之后再执行。

 

18.MySQL的两大搜索引擎的区别

Innodb引擎

Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于MySQL后台的完整数据库系统,MySQL运行时Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT类型的索引,而且它没有保存表的行数,当SELECT COUNT(*) FROM TABLE时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用Innodb引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表。

 

MyIASM引擎

MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和Innodb不同,MyIASM中存储了表的行数,于是SELECT COUNT(*) FROM TABLE时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么MyIASM也是很好的选择。

 

主要区别:

1、MyIASM是非事务安全的,而InnoDB是事务安全的

 

2、MyIASM锁的粒度是表级的,而InnoDB支持行级锁

 

3、MyIASM支持全文类型索引,而InnoDB不支持全文索引

 

4、MyIASM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyIASM

 

5、MyIASM表保存成文件形式,跨平台使用更加方便

 

应用场景:

1、MyIASM管理非事务表,提供高速存储和检索以及全文搜索能力,如果再应用中执行大量select操作,应该选择MyIASM

2、InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insert和update操作,应该选择InnoDB

————————————————

版权声明:本文为CSDN博主「jayxu无捷之径」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/ls5718/article/details/52248040

19.什么时候会索引失效?

(参考回答)嗯,这个情况比较多,我说一些自己的经验,以前遇到过的比如,索引在使用的时候没有遵循最左匹配法则,第二个是,模糊查询,如果%号在前面也会导致索引失效。如果在添加索引的字段上进行了运算操作或者类型转换也都会导致索引失效。我们之前还遇到过一个就是,如果使用了复合索引,中间使用了范围查询,

右边的条件索引也会失效所以,通常情况下,想要判断出这条sql是否有索引失效的情况,可以使用explain执行计划来分析

 

20.Spring常见注解

第一类是:声明bean,有@Component、@Service、@Repository、

@Controller

第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse

第三类是:设置作用域 @Scope

第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和

@Bean

第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,

@Around,@Pointcut

 

21.Controller和RestController的区别

@RestController是@Controller和 @ResponseBody 的结合体(详情可以查询)

  1. 用Controller配合视图解析器才能返回到指定页面。在对应的方法上加上ResponseBody注解才能返回JSON,XML或自定义mediaType的内容到页面。

 

  1. 不可以只用RestController注解Controller,因为这样会让Controller中的内容不能返回jsp页面,而且会直接返回Return里的内容。

3.RestController相当于Controller和ResponseBod两者合并起来的作用。

22.Spring里面@Autowired的作用,和@Resource的区别

1.作用范围不同

@Autowired:

它的作用范围包括构造函数、方法、参数、字段和注解类型

@Resource:

它的作用范围类/接口,字段,方法。

因此,在类上使用 @Resource 注解可以实现对该类所对应的 Bean 进行自动装配。需要注意的是,@Resource 注解与 @Autowired 注解不同,它不能用于构造函数的自动装配。

 

2.包含的属性不同

@Autowired只包含一个参数:required,表示是否必须进行自动注入。

required 参数用于表示是否必须进行自动注入。当 required 参数设置为 true 时,如果找不到匹配的依赖资源,将会抛出异常。当 required 参数设置为 false 时,如果找不到匹配的依赖资源,将会忽略自动注入

@Resource包含七个参数,其中最重要的两个参数是:name 和 type。

 

 

3.自动装配顺序不一样

@Autowired默认按byType自动装配,如果查找出存在多个匹配的 Bean,然后在用byName。而@Resource默认byName自动装配。

如果需要在使用 @Autowired 注解时使用 byName 方式自动装配,可以和 @Qualifier 注解一起使用,将 Bean 的名称指定为 @Qualifier 注解的 value 值,如下所示:

当 @Autowired 注解与 @Qualifier 注解一起使用时,@Autowired 注解首先会根据属性的类型进行 byType 的匹配,如果找到多个,然后再根据 @Qualifier 注解指定的 Bean 名称进行 byName 的匹配。

 

23.Redis数据结构都有哪些?

1.字符串类型String

字符串类型是Redis中最基本的数据结构,它能存储任何类型的数据,包括二进制数据,序列化后的数据,JSON化的对象甚至是一张图片。最大512M。

 

2.列表类型list

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),底层是个链表结构。

 

3.集合类型set

Redis 的Set是 string 类型的无序无重复集合。

 

4.哈希类型hash

Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

 

5.有序集合类型zset (sorted set)

Redis有序集合zset和集合set一样也是string类型元素的集合,且不允许重复的成员。

不同的是zset 的每个元素都会关联一个分数(分数可以重复),redis通过分数来为集合中的成员进行从小到大的排序。

 

————————————————

版权声明:本文为CSDN博主「848698119」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_45896330/article/details/124853812

24.存对象Redis怎么存?

  1. 将对象序列化后保存到Redis
  2. 将对象用FastJSON转为JSON字符串后存储
  3. 将对象用Hash数据类型存储

 

源码详情查看:https://blog.csdn.net/jike11231/article/details/117790600


 

 

 

你可能感兴趣的:(开发语言,mysql,redis,计算机网络,java)