21个人春招实习面试题备忘

Cookie

  1. cookie设置时间可以跨域么?
  2. 如果父页面和子页面传送cookie超时怎么传送?
  3. 跨域的方法
  4. 超时都有哪几种

常用数据结构


主要平时做力扣,不过快排很喜欢问
暂时先用c++写,java大同小异
5. 快排

class Solution {
public:
	void quick_sort(vector<int>& nums,int left,int right) {
		if (left < right)
		{
			int pivotpos = partition(nums, left, right);
			quick_sort(nums, left, pivotpos - 1);
			quick_sort(nums, pivotpos + 1, right);
		}

	}
	int partition(vector<int>& nums, int left, int right)
	{
		int pivot = nums[left];

		while (left < right)
		{
			while (left < right && nums[right] >= pivot)
				right--;
			nums[left] = nums[right];
			while (left < right && nums[left] <= pivot)
				left++;
			nums[right] = nums[left];
		}
		nums[left] = pivot;
		return left;
	}
};

7. 手写一个堆

java基础知识


8. Static final关键字
9. Hashcode,equals方法区别,hashcode底层实现,重写注意事项及为什么
10. Wait(),sleep()区别,wait()底层实现
11. Notify,notifyall的区别,唤醒线程的策略是什么
等待池:
假设线程A调用某个对象的wait方法,线程A就会释放该对象锁,同时线程A进入该对象的等待池中,进入等待池中的线程不会去竞争该对象的锁。
notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会;notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会;

13. Clone,深浅拷贝的区别,使用场景
在这里插入图片描述

  1. Io buffer和一个字节一个字节传出的区别,针对磁盘io, socket io分别解释。
    15. Synchronized和reentrant lock的区别,重入锁底层实现是什么。
    当使用synchronized给对象上锁时,就是将当前调用该方法的线程的信息存入到对象的markword当中,当有另外一个线程需要调用这个对象的方法时,如果该对象markword中已经有其他线程信息,则这个线程会停下来。这就是锁的原理,依赖于jvm。
    现在看synchronized的四种用法,实际上只有两种,一种是锁实例对象的markword(对该实例对象有效),一种是锁类对象(对所有实现该类的对象有效)
    区别:
  2. 锁的实现
    synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
  3. 性能
    新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
  4. 等待可中断
    当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
    ReentrantLock 可中断,而 synchronized 不行。
  5. 公平锁
    公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
    synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
  6. 锁绑定多个条件
    一个 ReentrantLock 可以同时绑定多个 Condition 对象。

使用选择
除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

17. 方法 sleep、join 和 yield 的区别有哪些?
方法 sleep 的作用是使当前线程暂停执行一段时间,让其他线程有机会继续执行;方法 join 的作用是阻塞调用该方法的线程,直到当前线程执行完毕之后,调用该方法的线程再继续执行;方法 yield 的作用是暂停当前正在执行的线程对象,并执行其他线程。

18. hashmap
19. 多线程
线程池的好处
在开发过程中,合理地使用线程池可以带来 3 个好处。
降低资源消耗。重复利用线程池中已经创建的线程,可以避免频繁地创建和销毁线程,从而减少资源消耗。
提高响应速度。由于线程池中有已经创建的线程,因此当任务到达时,可以直接执行,不需要等待线程创建。
提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

corePoolSize:核心线程数,定义了最少可以同时运行的线程数量,当有新的任务时就会创建一个线程执行任务,当线程池中的线程数量达到 corePoolSize 之后,到达的任务进入阻塞队列。
maximumPoolSize:最大线程数,定义了线程池中最多能创建的线程数量。
keepAliveTime:等待时间,当线程池中的线程数量大于 corePoolSize 时,如果一个线程的空闲时间达到 keepAliveTime 时则会终止,直到线程池中的线程数不超过 corePoolSize。
unit:参数 keepAliveTime 的单位。
workQueue:阻塞队列,用来存储等待执行的任务。
threadFactory:创建线程的工厂。
handler:当拒绝处理任务时的策略。

上述操作中提到了两个概念,「核心线程」和「非核心线程」。核心线程和非核心线程的最大数目在创建线程池时被指定。核心线程和非核心线程的区别如下。
向线程池提交任务时,首先创建核心线程运行任务,直到核心线程数到达上限,然后将任务放入阻塞队列
只有在核心线程数到达上限,且阻塞队列满的情况下,才会创建非核心线程运行任务

20. 手写单例模式

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

另外,需要注意 uniqueInstance 采用 volatile 关键字修饰也是很有必要。

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

为 uniqueInstance 分配内存空间
初始化 uniqueInstance
将 uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

22. 阻塞队列有哪些,线程池用的是哪一个阻塞队列
原文链接:https://blog.csdn.net/xiewenfeng520/article/details/106954169
在这里插入图片描述
线程池 阻塞队列
FixedThreadPool LinkedBlockingQueue
SingleThreadExecutor LinkedBlockingQueue
CachedThreadPool SynchronousQueue
ScheduledThreadPool DelayedWorkQueue
SingleThreadScheduledExecutor DelayedWorkQueue

  1. JVM
    在这里插入图片描述
    挂起和睡眠是主动的,挂起恢复需要主动完成,睡眠恢复则是自动完成的,因为睡眠有一个睡眠时间,睡眠时间到则恢复到就绪态。而阻塞是被动的,是在等待某种事件或者资源的表现,一旦获得所需资源或者事件信息就自动回到就绪态。

睡眠和挂起是两种行为,阻塞则是一种状态。

  1. JAVA类加载机制
    来源:https://snailclimb.gitee.io/javaguide-interview/#/./docs/b-4jvm添加链接描述
    在这里插入图片描述
    Step1:类加载检查
    虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

Step2:分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

内存分配的两种方式:(补充内容,需要掌握)
选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

内存分配并发问题(补充内容,需要掌握)
在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:
CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
TLAB: 为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配

Step3:初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

Step4:设置对象头
初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

Step5:执行 init 方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始, 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

  1. Full gc发生场景

27.HotSpot 为什么要分为新生代和老年代?
主要是为了提升 GC 效率。上面提到的分代收集算法已经很好的解释了这个问题。
当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
在这里插入图片描述

数据库


23. 数据库锁
MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。
但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大。
在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。

锁的类型:
1.读写锁 (行级) X/S
互斥锁(Exclusive),简写为 X 锁,又称写锁。
若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。(我修改时,别人就等着啥也别干。)

共享锁(Shared),简写为 S 锁,又称读锁。若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。
这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。(我读,别人也可以一起读,但你不能改东西。)

2.意向锁 (表级)IX/IS
意向锁的出现是为了更好细化锁的层级粒度,并且能提高锁处理性能:在数据库处理事务A请求的行级锁之前,会先申请该资源对应根目录资源的意向锁,以便能在其他事务B中再次请求表锁时做到阻塞。
通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。

3.封锁协议
一级封锁协议
事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。
可以解决丢失修改(俩事务T1,T2对A进行修改,T2的修改覆盖掉了T1的修改结果)问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。

二级封锁协议
在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。
可以解决读脏数据(T2读到了T1还未提交的修改)问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。

三级封锁协议
在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。
可以解决不可重复读(T2 读取一个数据,T1 对该数据做了修改,如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同)的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。

25. 数据库索引
聚集索引(主键索引):在数据库里面,所有行数都会按照主键索引进行排序。
非聚集索引:就是给普通字段加上索引。
组合索引:就是好几个字段组成的索引,称为组合索引。

组合索引 (最左前缀原则)
  *遵循“最左前缀”原则,把最常用作为检索或排序的列放在最左,依次递减,组合索引相当于建立了col1,col1col2,col1col2col3三个索引,而col2或者col3是不能使用索引的。
  *在使用组合索引的时候可能因为列名长度过长而导致索引的key太大,导致效率降低,在允许的情况下,可以只取col1和col2的前几个字符作为索引
 在这里插入图片描述
B+树存索引,只在叶子结点存放数据,中间节点全是索引。
优势:
单个节点可以存储更多的数据,减少I/O的次数。
查找性能更稳定,因为都是要查找到叶子结点。
叶子结点形成了有序链表,便于查询。

26. 数据库事务
ACID
A-原子性(Atomicity)
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用回滚日志(Undo Log)来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。

C-一致性(Consistency)
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。

I-隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其它事务是不可见的。

D-持久性(Durability)
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
系统发生崩溃可以用重做日志(Redo Log)进行恢复,从而实现持久性。与回滚日志记录数据的逻辑修改不同,重做日志记录的是数据页的物理修改。

  1. 事务的传播性
    28. 事务的隔离级别
    1.未提交读(READ UNCOMMITTED)
    事务中的修改,即使没有提交,对其它事务也是可见的。

2.提交读(READ COMMITTED)
一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。

3.可重复读(REPEATABLE READ)
保证在同一个事务中多次读取同一数据的结果是一样的。

4.可串行化(SERIALIZABLE)
强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。
该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行。

29.数据库引擎区别
来源:https://www.zhihu.com/question/20596402/answer/211492971

  1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;
  2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
  3. InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
  5. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一

30.聚集与非聚集索引
来源: https://zhuanlan.zhihu.com/p/23624390

我们平时建表的时候都会为表加上主键, 在某些关系数据库中, 如果建表时不指定主键,数据库会拒绝建表的语句执行。 事实上, 一个加了主键的表,并不能被称之为「表」。一个没加主键的表,它的数据无序的放置在磁盘存储器上,一行一行的排列的很整齐, 跟我认知中的「表」很接近。如果给表上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,也就是上面说的「平衡树」结构,换句话说,就是整个表就变成了一个索引。没错, 再说一遍, 整个表变成了一个索引,也就是所谓的「聚集索引」。 这就是为什么一个表只能有一个主键, 一个表只能有一个「聚集索引」,因为主键的作用就是把「表」的数据格式转换成「索引(平衡树)」的格式放置。

非聚集索引和聚集索引一样, 同样是采用平衡树作为索引的数据结构。索引树结构中各节点的值来自于表中的索引字段, 假如给user表的name字段加上索引 , 那么索引就是由name字段中的值构成,在数据改变时, DBMS需要一直维护索引结构的正确性。如果给表中多个字段加上索引 , 那么就会出现多个独立的索引结构,每个索引(非聚集索引)互相之间不存在关联。

31.数据库几种设计范式
来源:https://www.zhihu.com/question/24696366

在创建一个数据库的过程中,范化是将其转化为一些表的过程,这种方法可以使从数据库得到的结果更加明确。这样可能使数据库产生重复数据,从而导致创建多余的表。范化是在识别数据库中的数据元素、关系以及定义所需的表和各表中的项目等这些初始工作之后的一个细化的过程。
下面是范化的一个例子:


顾客 物品 金额
小明 T恤 40元
小红 鞋子 35元
小花 衬衫 40元
小蓝 裤子 25元

如果上面这个表用于保存物品的价格,而你想要删除其中的一个顾客,这时你就必须同时删除一个价格。范化就是要解决这个问题,你可以将这个表化为两个表,一个用于存储每个顾客及其所买物品的信息,另一个用于存储每件产品和其价格的信息,这样对其中一个表做添加或删除操作就不会影响另一个表。

1 第一范式(1NF)
在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。
简而言之,第一范式就是无重复的列。

2 第二范式(2NF)
第二范式(2NF) 是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。这个唯一属性列被称为主关键字或主键、主码。
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。
简而言之,第二范式就是非主属性完全依赖于主关键字。

函数依赖
我们可以这么理解(但并不是特别严格的定义):若在一张表中,在属性(或属性组)X的值确定的情况下,必定能确定属性Y的值,那么就可以说Y函数依赖于X,写作 X → Y。

用模式分解来拆表。

3 第三范式(3NF)
满足第三范式(3NF) 必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在图3-2的员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。
简而言之,第三范式就是某一属性不依赖于其它非主属性。
解决传递依赖性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值