字节跳动面试题整理

1、前端的密码是直接传吗?为什么要用MD5加密,MD5的缺点是什么?数据库中存的密码是明文还是密文?

前端的密码不可能直接用明文传,这样密码的安全性太低,通常前端的密码进行了加密传输。

前端密码加密传输的几种方式有DES、3DES、AES、RSA、BASE64、MD5、SHA1等。

AES(Advanced Encryption Standard):对称密钥加密(DES、3DES也是),前端使用后端传过来的密钥和密钥偏移量进行加密,后端用该密钥进行解密。

RSA:非对称密钥加密,公钥加密私钥解密,需加密的文件长度可变。

MD5:散列算法(SHA1、HMAC也是),主要用于验证、防止信息被篡改,不可逆。它可用作数字签名(签名需要可信赖的第三方身份认证机构,发送者将摘要信息用私钥加密与原文一起传给接收者,接收者用发送者的公钥进行解密确认身份,然后用哈希函数对原文生成摘要信息,与解密的摘要信息对比来证明信息完整且未被篡改)。

MD5的缺点是容易受冲撞攻击(collision attack),即在摘要信息较短时,计算机可以在一定时间内找到哈希碰撞对,不过成本较高。

数据库中存储的密钥是通过MD5进行加密的密文,为了确保安全性。

对称密钥算法——>非对称密钥算法(非对称算法生成的密钥对对称密钥进行加密传输,eg:RSA)——>通过认证(eg:X.509数字证书)来防止中间人攻击

2、XSS的解决方案?如何防止cookies被劫持?流量防刷如何处理?

XSS攻击(Cross Site Scripting)即跨站脚本攻击,它和SQL注入一样都是利用WEB页面的漏洞进行脚本攻击。解决方案:

  • 1)对URL、查询关键字、HTTP头、REFER、POST数据等进行过滤。
  • 2)使用HTTPOnly防止js获取cookies中的session Id信息。

HTTP是无状态的协议。数据交换完毕后,连接就会断开,而Cookies和Session是分别在客户端和服务器端记录信息并确定用户身份的。Cookies相当于钥匙,Session相当于锁芯,登陆用户在请求页面时会带上服务器颁发的凭证Cookies,服务器根据传过来的Cookies信息和Session信息进行匹配来确定用户身份,一旦泄漏Cookies,黑客可以冒充用户身份。使用HTTPOnly的cookies(只会在浏览器保存cookies但不会向客户端开放访问权限);或者使cookies和IP绑定降低cookies泄漏风险。

流量防刷可以通过缓存对每一位用户设置访问次数,如果当天访问次数超过一定值则判定是爬虫攻击,永久加入黑名单。

3、数据库索引,索引结构,如何实现范围查询(底层原理)

索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构。索引是用来提高查询数据效率的,索引分为聚集索引(主键索引)、非聚集索引、联合索引(联合索引遵循最左前缀原则。!=、%在前、用索引计算...不走索引)。索引的底层数据结构采用的是B+树,索引是在磁盘上进行存储的,如果数据结构选的不好访问索引的成本会很高,树型结构的搜索效率最高O(logn),至于为什么选用B树而不用二叉搜索树,那是由于数据量过多时,二叉搜索树深度较高,B树是平衡多路查找树,可以减少树的深度从而避免过多的磁盘I/O,而B+树是在B树的基础上对空间复杂度上做了优化。

范围查询可以采用select * from table where 字段 between value1 and value2,但是如果是有交集的两个范围查询数据量一大可能会导致慢查询,可以通过in代替其中的一个范围查询或者使用冗余字段去区分。

索引原理及查询优化参考链接:MySQL索引原理以及查询优化 - 割肉机 - 博客园

4、如何避免回表?

InnoDB有两大索引:聚集索引(clustered index)和普通索引(secondary index)。InnoDB聚集索引的叶子节点存储行记录,普通索引的叶子节点存储主键值,普通索引无法定位行记录,所以需要先定位主键值再定位行记录,这种操作就属于回表查询,相当于扫两边索引树,性能比扫一遍索引树更低。通过索引覆盖能够避免回表,索引覆盖的常见方法就是将被查询的字段建立到联合索引里去。

参考链接:MySQL优化:如何避免回表查询?什么是索引覆盖? (转) - myseries - 博客园

不同索引引擎区别:

  • InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  • MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  • Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
  • NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
  • Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;

行锁和表锁属于范围锁,参考链接:深入理解数据库行锁与表锁 - 知乎

5、Volatile关键字

被volatile修饰的变量在内存中是共享的,在编译器上通过volatile关键字可以禁止指令重排序,在硬件上则是通过内存屏障禁止指令重排序。volatile不会执行加锁操作,因此不会阻塞线程,它是比synchronized关键字更轻量级的同步机制。增加了volatile之后建立了两个线程的happen-before先后顺序。

指令重排序参考链接:指令重排序和内存屏障 - 张小云的博客 - 博客园

happen-before参考链接:面试官为什么总是问happens-before规则,看完这篇文章你就懂了

6、ClassLoader有哪几种,双亲委派机制是为了解决什么问题?如何打破双亲委派机制?

ClassLoader有启动类加载器(BootStrap ClassLoader,加载核心库)、扩展类加载器(Extension ClassLoader,加载jre/lib/ext目录下的扩展jar)、应用类加载器(Application ClassLoader,默认,加载应用程序主函数类)、自定义类加载器(User ClassLoader)。

双亲委派机制就是:如果一个类加载器收到了类加载的请求,这个类加载器不会先尝试加载这个类,而是会先把这个请求委派给自己的父类加载器去完成,在每个层次的类加载器都是依此类推的。比如,在加载一个类时先去判断是否用启动类加载器加载过,若已经加载过则不再加载;再判断是否被扩展类加载器加载过,若已加载则不再加载...

双亲委派机制的优点是:1、保证了JVM提供的核心类不被篡改,保证class执行安全。2、防止重复加载同一个class。

打破双亲委派机制:1、重写loadClass()方法。2、线程上下文加载器。参考链接:【JVM笔记】如何打破双亲委派机制?_小七的博客-CSDN博客_如何打破双亲委派机制

7、spring的三级缓存是什么?为什么要用三级缓存?

spring三级缓存是为了解决对象间的循环依赖问题。在DefaultSingletonBeanRegistry类中定义了三个集合singletonObjects、earlySingletonObjects、singletonFactories,它们就是三级缓存,一级缓存存放已经属性赋值初始化后的单例bean,二级缓存存放已经实例化但还未属性赋值的单例,三级缓存存放单例bean的工厂。

8、spring下的事务传播级别

事务:访问/更新数据库中数据项的最小程序执行单元。

脏读:一个事务访问数据并对数据进行修改,但事务还未提交,另一个事务也在读这个数据,这样会造成脏读。

不可重复读:在同一个事务内,多次读同一个数据。比如第一个事务两次读取同一个数据,但第二个事务的修改导致第一个事务两次读取的数据不一致。

幻读:事务不独立执行时发生。比如第一个事务修改表中的全部数据,第二个事务也修改了表中的数据向其添加一行,然后第一个事务发现表中还有没有修改的数据行。

解决方案:调整事务隔离级别。

TransactionDefinition接口中定义了七个事务传播行为:

  • PROPAGATION_REQUIRED :如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
  • PROPAGATION_SUPPORTS :如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
  • PROPAGATION_MANDATORY :如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
  • PROPAGATION_REQUIRES_NEW :总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
  • PROPAGATION_NOT_SUPPORTED :总是非事务地执行,并挂起任何存在的事务。
  • PROPAGATION_NEVER :总是非事务地执行,如果存在一个活动事务,则抛出异常
  • PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行

参考链接:Spring五个事务隔离级别和七个事务传播行为-阿里云开发者社区

Isolation 属性一共支持五种事务设置:

  • DEFAULT —— 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
  • 未提交读(READ_UNCOMMITTED) —— 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
  • 已提交读(READ_COMMITTED) ——  会出现不可重复读、幻读问题(锁定正在读取的行),Oracle默认隔离级别
  • 可重复读(REPEATABLE_READ) —— 会出幻读(锁定所读取的所有行),Mysql默认隔离级别
  • 可串行化(SERIALIZABLE) —— 保证所有的情况不会发生(锁表)

9、如果一个非事务方法调用了另一个事务方法会怎样?JDK动态代理和CGLIB动态代理区别?

spring同一个类中一个非事务方法调用另一个事务方法(加了@Transactional(rollbackFor = Exception.class))会失效,原因是因为spring的声明式事务使用的是AOP生成了一个代理对象,只有调用那个代理对象的方法,事务才有效。

JDK动态代理和CGLIB动态代理区别,1、JDK针对实现了接口的类生成代理,CGLIB针对指定的类生成代理;2、当Bean实现接口时Spring采用JDK动态代理;当Bean未实现接口时Spring使用CGLIB实现动态代理;3、CGLIB底层采用ASM字节码生成框架,虽然字节码生成效率更高,但是在JDK1.8后对JDK进行了优化,调用次数少时性能优于CGLIB动态代理。

动态代理参考链接:JDK动态代理与CGLib动态代理的区别 - 水木湛清华 - 博客园

10、除了JDK和CGLIB两种AOP方案,还有什么方法可以实现AOP?

还有AspectJ静态编译织入,静态代理的缺点就是我们需要对每一个方法编写我们的代理逻辑,造成了工作的繁琐和复杂,AspectJ解决了这样的问题,在编译class字节码时在方法周围加上业务逻辑,复杂的工作由特定的编译器来做。通过@Aspect+@Before/@After/@AfterRunning/@AfterThrowing/@Around对方法进行前置通知/后置通知/返回通知/异常通知/环绕通知。

参考链接:Java JDK代理、CGLIB、AspectJ代理分析比较_蜗牛的专栏-CSDN博客

11、Redis ZSET的内部实现

Redis对象由redisObject结构体表示,Redis中的每个键值对的键和值都是一个redisObject。如下

typedef struct redisObject {
    unsigned type:4;            // 对象的类型,包括 /* Object types */
    unsigned encoding:4;        // 底部为了节省空间,一种type的数据,可以采用不同的存储方式
    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;         // 引用计数
    void *ptr;
} robj;

Redis共有五种类型的对象:字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(SortedSet),其中有序集合也叫ZSET。

/* The actual Redis Object */
#define OBJ_STRING 0
#define OBJ_LIST 1 
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4

ZSET内部实现是通过跳表(Skip List)的数据结构实现,它是基于并联的链表,前提条件是有序集合。可参看前面一篇文章。

12、链表反转实现(递归)

public class ReverseList {
    static class ListNode {
        volatile int val;
        volatile ListNode next;
        ListNode(int x) {
            val = x;
        }
    }

    public static void main(String[] args) {
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        ListNode newHead = reverse(head);
        ListNode cur = newHead;
        while (cur != null){
            System.out.println(cur.val);
            cur = cur.next;
        }
    }

    public static ListNode reverse(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode temp = head.next;
        ListNode newHead = reverse(head.next);
        temp.next = head;
        head.next = null;
        return newHead;
    }
}

13、https加密过程

HTTPS(全称: Hypertext Transfer Protocol Secure,超文本传输安全协议),是以安全为目标的HTTP通道。采用对称密钥加密+非对称密钥加密结合的方式保护浏览器和服务端的通信安全。

加密过程:客户端发起握手请求,以明文传输请求信息(版本信息、加密套件候选列表、压缩算法候选列表、随机数、扩展字段等)——> 服务器端向CA认证机构申请证书,并返回协商结果信息给客户端(包括协议版本、加密套件、压缩算法、随机数以及证书) ——> 客户端验证证书合法性,并用证书中的公钥给通信用的对称密钥进行加密,再发送给服务器端 ——> 服务器接收后用私钥解密拿到对称密钥,然后服务器端和客户端可以用对称密钥加密信息通信。

参考链接:HTTPS加密(握手)过程 - 简书

14、输入http到浏览器的流程

输入http协议的网址到浏览器的流程:

  1. 请求后,浏览器首先会解析这个域名,查看本机/etc/hosts文件看有没有与域名对应的规则,如果有则用hosts文件里的ip地址。
  2. 如果hosts文件没有,浏览器会请求到本地DNS(Domain Name System)服务器,如果本地DNS服务没有,则本地DNS向DNS根服务器查询,查到IP后返回给浏览器,并记录缓存记录。
  3. 客户端和服务器端建立TCP连接。
  4. 再进行HTTP请求和HTTP响应报文。
  5. 释放TCP连接。

15、DNS解析的方式有哪些?(递归、迭代)

DNS 查询以各种不同的方式进行解析。客户机有时也可通过使用从以前查询获得的缓存信息就地应答查询。DNS 服务器可使用其自身的资源记录信息缓存来应答查询,也可代表请求客户机来查询或联系其他 DNS 服务器,以完全解析该名称,并随后将应答返回至客户机。这个过程称为递归。
另外,客户机自己也可尝试联系其他的 DNS 服务器来解析名称。如果客户机这么做,它会使用基于服务器应答的独立和附加的查询,该过程称作迭代,即DNS服务器之间的交互查询就是迭代查询。

参考链接:DNS原理及其解析过程 - 极客小乌龟 - 博客园

16、Java实现线程安全的三种方式?

1、同步代码块。2、同步方法。3、Lock锁机制。

17、线程池的七个参数?自定义RejectedExecutionHandler有哪几种实现?

  • corePoolSize 线程池核心线程大小
  • maximumPoolSize 线程池最大线程数量
  • keepAliveTime 空闲线程存活时间
  • unit 空闲线程存活时间单位
  • workQueue 工作队列(①基于数组的有界阻塞队列ArrayBlockingQueue;②基于链表的无界阻塞队列LinkedBlockingQuene;③不缓存任务的阻塞队列SynchronousQuene;④具有优先级的无界阻塞队列PriorityBlockingQueue)
  • threadFactory 线程工厂
  • handler 拒绝策略(①CallerRunsPolicy—除非线程池关闭,否则直接执行被拒绝任务;②AbortPolicy—直接丢弃任务,并抛出RejectedExecutionException异常,默认策略;③DiscardPolicy—直接丢弃任务,什么都不做;④DiscardOldestPolicy—抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列)

自定义RejectedExecutionHandler的实现——实现RejectedExecutionHandler接口,重写​rejectedExecution方法。用法:1、获取线程信息;2、获取线程的上下文信息。

18、网盘项目断点续传的功能是如何实现?

断点续传是由服务器给客户端一个已经上传的位置标记position,然后客户端再将文件指针移动到相应的position,通过输入流将文件剩余部分读出来传输给服务器。

19、进程、线程和协程的区别?进程间通信方式?

  • 进程是任务调度的最小单位,每个进程各自都独立一块内存;(同步)
  • 线程是程序执行流的最小单位,是处理器调度和分派的最小单位;(同步)
  • 协程是一种用户态的轻量级线程,协程拥有自己的寄存器上下文和栈;(异步,保留上一次调用时的状态)

进程间通信方式:

  • 管道:半双工,Go语言和Java在并发处理上多了双向通道channel来在goroutine间传输数据。
  • 消息队列:消息队列允许不同进程以消息队列的形式发送给任意的进程。
  • 共享内存:每个进程都有自己的虚拟内存空间,不同的进程映射到不同的物理内存空间。
  • 信号量:信号量实际上是一个计数器,信号量主要实现进程之间的同步和互斥,而不是存储通信内容。信号量两种操作:p操作为申请资源,会将数值减去M,表示这部分被他使用了,其他进程暂时不能用;v操作是归还资源操作,告知归还了资源可以用这部分。
  • 信号:在操作系统中,不同信号用不同的值表示,每个信号设置相应的函数,一旦进程发送某一个信号给另一个进程,另一进程将执行相应的函数进行处理。
  • 套接字:套接字Socket, Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。

20、select和epoll?

select,poll,epoll都是IO多路复用的机制。参考链接:select、poll、epoll之间的区别(搜狗面试) - aspirant - 博客园

21、数据库事务的级别,为什么RR隔离级别下还会出现幻读?

  • DEFAULT —— 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
  • 未提交读(READ_UNCOMMITTED) —— 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
  • 已提交读(READ_COMMITTED) ——  会出现不可重复读、幻读问题(锁定正在读取的行),Oracle默认隔离级别
  • 可重复读(REPEATABLE_READ) —— 会出幻读(锁定所读取的所有行),Mysql默认隔离级别
  • 可串行化(SERIALIZABLE) —— 保证所有的情况不会发生(锁表)

可重复读隔离级别并没有完全解决幻读问题,如果同一事务里只存在普通的select快照读是不会产生幻读。如果在这个事务里通过当前读或者更新后快照读就会产生幻读。

补充:

普通读(快照读,Consistent Read,undo log + MVCC 实现),执行方式是生成 ReadView,直接利用 MVCC 机制来进行读取,并不会对记录进行加锁。

当前读(锁定读,Locking Read,行记录锁+间隙锁 实现)

参考链接:当前读和快照读的区别_Data & Analysis-CSDN博客_快照读和当前读

MVCC:多版本并发控制,为了提高数据库并发性能,更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。参考链接:https://www.jianshu.com/p/8845ddca3b23

22、JVM内存分区

23、JVM内存溢出有哪几种,什么时候会出现栈内存溢出,如何排查?

  1. 栈溢出(StackOverflowError):方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。对于栈内存溢出,根据《Java 虚拟机规范》中文版:如果线程请求的栈容量超过栈允许的最大容量的话,Java 虚拟机将抛出一个StackOverflow异常;如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。
  2. 堆溢出(OutOfMemoryError:Java heap space):我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。
  3. 持久代溢出(OutOfMemoryError:PermGen spce):Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。
  4. OutOfMemoryError:unable to create native thread:1)程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。2)给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。

参考:[转]JVM内存溢出的几种方式比较 - DarrenChan陈驰 - 博客园

24、linux中如何进行字符串查找?vim中?

cat filename | grep -C 5 '关键字' (显示日志里匹配字串那行以及前后5行)

cat filename | grep -B 5 '关键字' (显示匹配字串及前5行)

cat filename | grep -A 5 '关键字' (显示匹配字串及后5行)

25、文件句柄是什么?

在文件I/O中,要从一个文件读取数据,应用程序首先要调用操作系统函数并传送文件名,并选一个到该文件的路径来打开文件。该函数取回一个顺序号,即文件句柄(file handle),该文件句柄对于打开的文件是唯一的识别依据。要从文件中读取一块数据,应用程序需要调用函数ReadFile,并将文件句柄在内存中的地址和要拷贝的字节数传送给操作系统。当完成任务后,再通过调用系统函数来关闭该文件。一个句柄就是一个文件、设备、套接字(socket)或管道的一个名字。

26、淘宝的数据库设计?

27、高并发如何优化?

高并发常见的优化办法:

  • 硬件升级
  • 负载均衡
  • 服务器集群
  • 数据库读写分离——主库增删改(INSERT、DELETE、UPDATE)操作,从库SELECT查询操作。
  • 数据库分库分表——垂直切分(垂直分库、垂直分表);水平切分(库内分表、分库分表)。参考链接:数据库分库分表,何时分?怎样分?详细解读,一篇就够了_azhuyangjun的博客-CSDN博客_数据库分库分表
  • 表建立相应的索引
  • 页面静态化——静态资源(html、图片、css样式、文本、js等),避免重复请求这些资源,减少http请求,例如CDN。参考链接:页面静态化_yinbucheng的博客-CSDN博客_页面静态化
  • 缓存技术(MemCache、Redis)
  • 禁止外部盗链——别人盗用你的链接转发流量,解决方法:1、判断引用地址(Request.UrlReferrer属性);2、使用登陆验证;3、使用Cookies;4、使用Post下载;5、使用图形验证码;6、使用动态文件名;7、打包下载
  • 控制大文件下载——大文件下载会占用很大流量,耗费CPU资源,使得网站相应能力下降

28、一致性哈希原理?

29、缓存击穿、缓存穿透、缓存雪崩区别?

  • 缓存击穿:缓存中没有的数据在数据库中,但由于访问量过多导致数据库瞬间压力增大。解决方案:缓存中设置热点数据永不过期。
  • 缓存穿透:缓存和数据库中都没有的数据,但用户不断发起请求从而导致数据库压力过大。解决方案:用户鉴权校验。
  • 缓存雪崩:缓存中大批量数据过期,压力全到了数据库服务器引起宕机。解决方案:过期时间设置随机,防止大批量数据同一时间过期;热点数据永不过期。

30、TCP四次挥手细节?

31、delete、drop、truncate区别?

执行速度上drop > truncate >> delete;

delete为数据操作语言(DML,Data Manipulation Language)删除记录,同时将删除操作作为事务记录在redo log(记录事务操作的物理日志)和undo log(保存数据操作之前版本,用于回滚),其他,binlog是主从复制中用主库的binlog进行重播达到主从同步。

truncate和drop是数据定义语言(DDL,Data Definition Language),drop是删除数据库表,立刻释放磁盘空间无法找回;truncate是快速清空一个表并重置auto_increment的值,delete不会。

32、同步辅助类CountDownLatch CyclicBarrier Semaphore区别?

  • CountDownLatch类:通过继承了AbstractQueuedSynchronizer的同步器的计数值来控制。countDown方法计数值减一,await方法会获取共享锁进入等待状态直到计数值减为0。
  • Semaphore类:实现最多指定数量的线程并发访问,超过count数的线程需要等到count数个线程中有线程执行完毕且释放资源才可以。线程执行调用acquire方法,释放调用release方法。
  • CyclicBarrier类:它没有像前面两个类使用AQS而是用了ReentrantLock。使指定数量的线程等待直到线程到齐,然后一起执行下一步。可循环执行,当等到指定数量的线程调用await方法。

33、Redis持久化机制RDB和AOF区别?

RDB(Redis DataBase):把数据以快照的形式保存在磁盘上(二进制文件dump.rdb)。触发方式:save(新替旧)、besave(默认,fork子进程保存)、自动化(配置)。缺点:全量备份。

AOF(Append Only File):redis会将每一个收到的写命令都通过write函数追加到文件(日志记录)中,fork新进程来重写。触发方式:always(每次修改同步)、everysec(异步,每秒记录)、no(从不同步)。缺点:日志文件比RDB快照文件大,恢复慢。

参考链接:详解Redis中两种持久化机制RDB和AOF(面试常问,工作常用)

34、bean的作用域?

  • 单例(singleton):它是默认的选项,在整个应用中,Spring只为其生成一个Bean的实例。
  • 原型(prototype):当每次注入,或者通过Spring IoC容器获取Bean时,Spring都会为它创建一个新的实例。
  • 会话(session):在Web应用中使用,就是在会话过程中Spring只创建一个实例。
  • 请求(request):在Web应用中使用的,就是在一次请求中Spring会创建一个实例,但是不同的请求会创建不同的实例。

35、bean的声明周期?

36、bean的初始化?

初始化执行顺序:构造方法->Before Initialization->使用PostConstruct注解->InitializingBean接口->init-method指定的初始化方法->After Initialization

37、Java8新特性有哪些?

  • lambda表达式:函数作为参数传递进方法中,使代码简洁。
  • 方法引用:使用一对冒号“::”,eg:ArrayList.forEach(System.out::println)
  • 函数式接口:如Runnable、Callable、Comparator、FileFilter等。eg:Predicate接口,相当于通过predicate传递的参数表达式过滤出对应的数据。
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
        for(Integer n: list) {
            //test方法评估参数表达式,返回布尔类型
            if(predicate.test(n)) {
                System.out.println(n + " ");
            }
        }
}
  • 默认方法:方法名前加default关键字。解决接口的修改与现有的实现不兼容的问题。
  • Stream(流):以一种声明的方式处理数据。流的来源可以是是集合,数组,I/O channel,产生器generator等,流的聚合操作有如filter, map, reduce, find,match, sorted。
public static void main(String[] args) {
    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
}
  • Optional类:Optional 是个容器,它可以保存类型T的值,或者仅仅保存null,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
public static void main(String args[]) {
        Integer value1 = null;
        Integer value2 = new Integer(10);
        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> a = Optional.ofNullable(value1);
        // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
        Optional<Integer> b = Optional.of(value2);
        System.out.println(getSum(a, b));
    }
  • Nashorn:javascript 引擎,从JDK1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎,性能提升了2到10倍。
        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
        ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
        String name = "Runoob";
        Integer result = null;
        try {
            nashorn.eval("print('" + name + "')");
            result = (Integer) nashorn.eval("10 + 2");

        } catch (ScriptException e) {
            System.out.println("执行脚本错误: " + e.getMessage());
        }
        System.out.println(result.toString());
  • 日期时间API——Date-Time: 旧版java.util.Date 是非线程安全,LocalDate线程安全。Local(本地) − 简化了日期时间的处理,没有时区的问题;Zoned(时区) − 通过制定的时区处理日期时间。
LocalDateTime currentTime = LocalDateTime.now();
        System.out.println("当前时间: " + currentTime);
        LocalDate date1 = currentTime.toLocalDate();
        System.out.println("date1: " + date1);
        Month month = currentTime.getMonth();
        int day = currentTime.getDayOfMonth();
        int seconds = currentTime.getSecond();
        System.out.println("月: " + month + ", 日: " + day + ", 秒: " + seconds);
  • Base64:内置了 Base64 编码的编码器和解码器。
            // 编码
            String base64encodedString = Base64.getEncoder().encodeToString("runoob?java8".getBytes("utf-8"));
            System.out.println("Base64 编码字符串 (基本) :" + base64encodedString);
            // 解码
            byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
            System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));

38、Redis的四种模式——单机、主从、哨兵、集群?

单机模式:一台Redis服务器部署,简单,数据天然一致。但单节点有宕机风险,性能受限于CPU的处理能力。

主从模式:将一台Redis服务器的数据复制到其他节点,数据复制只能从主节点(Master)复制到从节点(Slave)。但当主节点宕机,从节点晋升为主节点后需要修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,整个过程需要人工干涉。

哨兵模式:实现了自动化的故障修复。哨兵节点不存储数据每秒一次向所有主从节点以及哨兵节点发送ping命令不断检查是否正常运行。如果一个实例距最后一次回复ping命令的时间超过down-after-milliseconds指定的值,则这个实例会被监测的哨兵节点Sentinel标记为主观下线。达到配置文件指定的数量观测的主观下线的主服务器才会被标记为客观下线。当主服务器被标记为客观下线时,Sentinel向下线的主服务器的所有从服务器每隔10秒发送一次INFO命令。Sentinel和其他 Sentinel 协商主节点的状态并选出新的主节点,将告知原有主节点的从节点指向新的主节点进行数据复制。哨兵模式主要问题是较难支持在线扩容。

集群模式:通过数据分片的方式进行数据共享,同时提供数据复制和故障转移功能。集群的键空间被分割为16384个slots(即hash槽),通过hash的方式将数据分配到不同的分片上(HASH_SLOT = CRC16(key) & 16384),位运算速度高于取模运算。

39、什么是跨域问题,如何解决?

当一个请求url的协议域名端口三者之间任意一个与当前页面url不同即为跨域(浏览器的同源策略same-origin policy限制)。

当前页面url被请求页面url是否跨域原因
http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/百度一下,你就知道跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)

同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。

解决方案:

  • 设置document.domain解决无法读取非同源网页的 Cookie问题
  • 跨文档通信 API:window.postMessage()
  • JSONP(JSON with Padding)
  • CORS(Cross-Origin Resource Sharing)

开发过程中有次调试就遇到跨域问题,使用谷歌浏览器输入chrome://flags/,搜索SameSite by default cookies,把那两项禁用后重启浏览器(右下角Relaunch),即可解决这个问题。

40、迭代器Iterator失效问题?

无论是序列容器还是关联容器,最常见的操作就是遍历容器中存储的元素,大部分都是通过迭代器来实现。迭代器失效即容器在进行新增/删除元素时,必须对迭代器再次初始化或者赋值,否则就认为迭代器失效了。

41、Redis字符串和Java字符串的区别?

Java字符串jdk1.8之前是char数组,jdk1.8之后是byte数组;Redis字符串底层是简单动态字符串(SDS,Simple Dynamic String)的抽象类型,Redis是c语言开发,SDS结构体如下,

struct sdshdr {
    // 记录buf数组中已使用字节的数量,等于SDS所保存字符串长度
    int len;
    // 记录buf数组中未使用字节数量
    int free;
    // 字节数组,用户保存字符串
    char buf[]
};

空间预分配用于优化SDS的字符串操作:当SDS的API对一个SDS进行修改。并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所必须的空间,还会为SDS分配额外的未使用空间free,这样就避免了连续执行字符串添加带来的性能消耗。

惰性空间释放:每次分配都会分配多余的空间,那会不会造成内存泄漏或浪费?所以还需考虑内存释放问题。惰性空间释放用来优化SDS的字符串缩短操作,当SDS的API需要缩短字符串时,程序并不会立即使用内存重新来回收缩短后多出来的字节,而是使用free属性将这些字节数据记录起来,并等待将来使用。

Redis以键值对存储信息,每个键值对都维护了一个dictEntry,其源码如下:

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

这里的key是一个字符串,但未使用C中的字符数组,而是使用了Redis自己的SDS。

value会对应一个redisObject。Redis通过redisObject来实现不通数据类型的存储,其源码如下:

typedef struct redisObject {
    unsigned type:4; //对象的数据类型,比如:字符串、哈希等
    unsigned encoding:4; //具体的存储编码类型,后续会从不同的数据类型详细讲解
    unsigned lru:LRU_BITS; //对象最后一次被访问的时间,后续内存淘汰策略会说明
    int refcount;
    void *ptr;
} robj;

42、CountDownLatch的await方法底层实现原理?

43、如果想让时间最短的线程停止后,安全的通知其他线程停止(同时主线程也要安全的停止)?

44、共享内存如何实现?

45、redis网络模型?

46、where和having区别?

  • where可以用于select、update、delete、insert语句,having只能用于select语句。
  • where是在执行分组之前使用,having是在执行分组之后使用。eg: 
SELECT Customer,SUM(OrderPrice) FROM Orders
WHERE Customer='Bush' OR Customer='Adams'
GROUP BY Customer
HAVING SUM(OrderPrice)>1500
  • WHERE 子句用来筛选 FROM 子句中指定的操作所产生的行,GROUP BY 子句用来分组 WHERE 子句的输出,HAVING 子句用来从分组的结果中筛选行。

参考链接:SQL HAVING 子句

其他题目:java基础、集合、线程(java面试必备)_逆流而上Mr李-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值