整理一下最近面试遇到过的问题,有一些想不起来了,希望能给大家一点帮助吧,也给自己留个底,嘿嘿,平时还是得多注意知识的积累,以及技术细节
- 1.JAVA基础类型各占几个字节?
bit 字节
int 32bit 4
short 16bit 2
long 64bit 8
byte 8bit 1
char 16bit 2
float 32bit 4
double 64bit 8
boolean 1bit 1或者4
boolean类型没有给出精确的定义,《Java虚拟机规范》给出了4个字节,和boolean数组1个字节的定义,具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。
- 2.如何保证同步
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。
同步机制实现方式包括但不限于:
①ThreadLocal ② synchronized( ) ③ wait() 与 notify() ④ volatile
- 3.什么是CAS
CAS的全称是Compare And Swap 即比较交换,乐观锁,无锁。其算法核心思想如下
执行函数:CAS(V,E,N)
其包含3个参数
V表示要更新的变量
E表示预期值
N表示新值
CAS操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是执行CAS操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作
- 4.Spring IOC/DI 及其原理
Spring本身就是一个大的容器,来管理各种Bean,通常用来整合其它框架。优秀!
Ioc不是一种具体的技术,而是一种设计思想,Ioc的目的是为了如何指导我们编写出更加松耦合,更加优雅的程序。传统的应用程序使我们在类的内部显式的创建依赖的对象。从而导致类于类之间耦合度过高。而使用了Ioc的设计思想,将对象的创建,查找依赖,以及生命周期的控制权交给了Ioc容器。对象之间耦合较松,更加灵活。
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。DI通过反射实现
- 5.AOP是什么,怎么实现的
AOP面向切面编程,是对业务流程中某一类共性功能的横向抽取。比如事务处理,消息通知。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
- 6.单列索引和组合索引
联合索引-使用结论:
1、查询条件中出现联合索引第一列,或者全部, 则能利用联合索引.
2、条件列中只要条件相连在一起, 无论前后, 都会利用上联合索引,
3、查询条件中没有出现联合索引的第一列,而出现联合索引的第二列,或者第三列,都不会利用联合索引查询。
单一列索引-使用结论:
1、只要条件列中出现索引列,无论在什么位置,都能利用索引查询.
优缺点比较:
1、索引所占用空间:单一列索引相对要小.
2、索引创建时间:单一列索引相对短.
3、索引对insert,update,delete的影响程序:单一列索引要相对低.
4、在多条件查询时,联合索引效率要高.
索引的使用范围:单一列索引可以出现在where 条件中的任何位置,而联合索引需要按一定的顺序来写.
- 7.JVM以及GC
JVM分为:虚拟机栈、本地方法栈、堆、方法区、程序计数器
本地方法栈用于支持native方法的执行,存储了每个native方法的执行状态。
虚拟机栈占用的是操作系统内存,每个线程对应一个虚拟机栈,它是线程私有的,生命周期和线程一样,每个方法被执行时产生一个栈帧(Statck Frame),栈帧用于存储局部变量表、动态链接、操作数和方法出口等信息,当方法被调用时,方法的调用到结束对应着栈帧的入栈到出栈。
堆区是GC最频繁的,也是理解GC机制最重要的区域。堆区由所有线程共享,在虚拟机启动时创建。堆区主要用于存放对象实例及数组,所有new出来的对象都存储在该区域。
方法区存放了要加载的类的信息(如类名、修饰符等)、静态变量、构造函数、final定义的常量、类中的字段和方法等信息。方法区是全局共享的,在一定条件下也会被GC。当方法区超过它允许的大小时,就会抛出OutOfMemory:PermGen Space异常。
程序计数器是线程私有的,记录正在执行的虚拟机字节码指令地址。
判断一个对象是否需要回收:引用计数法,可达性分析。
GC算法:复制算法,标记-清除,标记-整理
GC回收器:
新生代: serial,parnew,parallel(吞吐量优先)
老年代: serial old,parallel old,CMS
新生代+老年代:G1(官方推荐)
- 8.Dubbo的原理,如何运转
Dubbo是阿里巴巴SOA服务化治理方案的核心框架,并被广泛应用于阿里巴巴集团的各成员站点。
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案
0.服务容器负责启动,加载,运行服务提供者。
1.服务提供者在启动时,向注册中心注册自己提供的服务。
2.服务消费者在启动时,向注册中心订阅自己所需的服务。
3.注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4.服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,务消费者,从提供者再选另一台调用。
5.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
- 9.Redis缓存穿透,缓存雪崩,缓存击穿
缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存.
解决方案:缓存空值,设置一个较短的缓存时间
缓存雪崩,是指在某一个时间段,缓存集中过期失效
解决方案:一般是采取不同分类,缓存不同周期。在同一分类中,加上一个随机因子。这样能尽可能分散缓存过期时间,而且,热门类目缓存时间长一些,冷门类目缓存时间短一些,也能节省缓存服务的资源。
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
对于非常热点的key直接设为永不过期/mutex key互斥锁
- 10.HashMap和ConcurrentHashMap
基于JDK1.8,ConcurrentHashMap、HashMap是基于Node<K,V>[]数组和链表/红黑树实现的。不同点在于HashMap线程不安全,ConcurrentHashMap通过使用CAS+Synchronized(1.8以前使用分段锁)来保证线程安全的。
初始容量16,最大容量2^30,默认负载因子0.75,实际容量 = 初始容量负载因子(默认0.75) = 12,链表转换树的阈值默认8,树转链表阈值默认6。
HashMap的构造函数中,都直接或间接的调用了tableSizeFor函数。保证了取索引操作h&(length-1)的最后一位同时有为0和为1的可能性,保证了散列的均匀性。
①创建HashMap,初始容量为16,实际容量 = 初始容量负载因子(默认0.75) = 12;
②调用put方法,会先计算key的hash值:hash = key.hashCode()。
③调用tableSizeFor()方法,保证哈希表散列均匀。
④计算Nodes[index]的索引:先进行index = (tab.length - 1) & hash。
⑤如果索引位为null,直接创建新节点,如果不为null,再判断是否有元素
⑥如果有:则先调用hash()方法判断,再调用equals()方法进行判断,如果都相同则直接用新的Value覆盖旧的;
⑦如果不同,再判断第一个节点类型是否为树节点(涉及到:链表转换成树的阈值,默认8),如果是,则按照红黑树的算法进行存储;如果不是,则按照链表存储;
⑧当存储元素过多时,需要进行扩容:
默认的负载因子是0.75,如果实际元素所占容量占分配容量的75%时就要扩容了。大约变为原来的2倍(newThr =oldThr << 1);
- 11.ArrayList和LinkedList
ArrayList底层是通过数组实现的,继承了AbstractList,初始容量为10,在空参构造器中写死的this(10)指定,线程不安全,ArrayList扩容,添加元素之前判断是否需要扩容,扩容是1.5倍,使用Arrays.copyOf在原数据基础上扩容
ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构,它继承于AbstractSequentialList的双向链表,由于AbstractSequentialList 实现了get(i)、set()、add() 和 remove()这些骨干性函数,这也降低了List接口的复杂程度。
ArrayList与LinkedList都是不是同步的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须保持外部同步。同步的方法就是使用Collections.synchronizedList(Collection c)来“包装”该列表。
对于随机访问get和set,ArrayList绝对优于LinkedList,因为从源码可以看出,ArrayList想要get(int index)元素时,直接返回index位置上的元素;而LinkedList需要通过for循环进行查找,虽然LinkedList已经在查找方法上做了优化,比如index < size / 2,则从左边开始查找,反之从右边开始查找,但是还是比ArrayList(随机查找)要慢。
从源码来看,ArrayList想要在指定位置插入或删除元素时,主要耗时的是System.arraycopy动作,会移动index后面所有的元素;LinkedList主耗时的是要先通过for循环找到index,然后直接插入或删除。这就导致了两者并非一定谁快谁慢!
- 12.如何设计一个接口
一.单一职责原则
二.里氏替换原则
只要父类出现的地方子类就一定可以出现,有子类出现的地方,
三、依赖倒置原则
模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
接口或抽象类不依赖于实现类
实现类依赖接口或抽象类
四、接口隔离原则
客户端不应该依赖它不需要的接口
类之间的依赖关系应该建立在最小的接口上
概括 : 建立单一接口,不要建立臃肿庞大的接口,也就是接口尽量细化,接口中的方法尽量少
五、迪米特法则
迪米特法则也叫最少知识原则,含义是 一个对象应该对其他对象有最少的了解,这个应该很好理解,就是降低各模块之间的耦合
六、开闭原则
模块和函数应该对扩展开放,对修改关闭,开闭原则也是其他五个原则的基石
13.Js闭包是什么
闭包指的是:能够访问另一个函数作用域的变量的函数。清晰的讲:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量
14.非常大的数据量情况下有什么比HashMap更好的数据结构
这个我不知道,据猜测,应该是TreeMap吧,时间复杂度O(logn),极限情况下,HashMap时间复杂度是O(n),最理想O(1)。
- 15.产生0-10000不重复随机整数
个人想到两种方案
一、先随机出来1-10000的整数,将结果放入一个数组,如果随机出来的数重复,继续随机。
这样做的问题就是,当只剩下个别数字没有随机的时候,效率就会很低下
二、建一个1-10000的“数组”,随机这个数组size大小范围的数,取出该位置的数,数组移除这个数。
这样做的话,感觉是维护数组的代价略大。
各位有什么更好的方法的话,欢迎留言解惑。
- 16.SpringMVC和Spring Boot有什么区别
SpringMVC是一个MVC模式的框架,Springboot就是极度简化了spring、springMVC的配置,去掉了繁杂的配置文件,能够快速搭建起一个web应用。
Spring 是一个“引擎”;
Spring MVC 是基于Spring的一个 MVC 框架 ;
Spring Boot 是基于Spring4的一套快速开发整合包。
- 17.SQL优化
1、查询语句中不要使用 *
2、尽量减少子查询,使用关联查询(left join,right join,inner join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替
(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、合理的增加冗余的字段(减少表的联接查询)
6、增加中间表进行优化(这个主要是在统计报表的场景,
后台开定时任务将数据先统计好,尽量不要在查询的时候去统计)
7、建表的时候能使用数字类型的字段就使用数字类型(type,status…),数字类型的字段作为条件查询比字符串的快
8、那些可以过滤掉最大数量记录的条件必须写在WHERE子句的最末尾
- 18.数据库索引
数据库索引是数据库管理系统中的一个排序的数据结构,以协助快速查询,更新数据库表中数据,索引的实现通常使用B树(B-tree)以及其变种B+tree(一些高效率的算法)
–使用索引时有些不生效的情况
1、使用like关键字模糊查询时,% 放在前面索引不起作用,只有“%”不在第一个位置,索引才会生效(like ‘%文’–索引不起作用)
2、使用联合索引时,只有查询条件中使用了这些字段中的第一个字段,索引才会生效
3、使用OR关键字的查询,查询语句的查询条件中只有OR关键字,且OR前后的两个条件中的列都是索引时,索引才会生效,否则索引不生效。
4、尽量避免在where子句中使用!=或<>操作符,否则全表扫描。
5、对查询进行优化,应尽量避免全表扫描,首先应考虑在where以及order by涉及的列上建立索引。
6、应尽量避免在 where 子句中对字段进行表达式操作,否则全表扫描。
7、尽量避免在where子句中对字段进行函数操作,否则全表扫描。
8、不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
9、并不是所有的索引对查询都有效,sql是根据表中的数据来进行查询优化的,当索引列有大量数据重复时,sql查询不会去利用索引
10、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,
11、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
12、mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。
因此数据库默认排序可以符合要求的情况下不要使用排序操作,尽量不要包含多个列的排序,如果需要最好给这些列建复合索引。
13、order by 索引 ,不起作用的问题(除了主键索引之外):
1、 如果select 只查询索引字段,order by 索引字段会用到索引,要不然就是全表排列;
2、如果有where 条件,比如where vtype=1 order by vtype asc . 这样order by 也会用到索引!
19.分布式环境如何保证session一致性
1.ngnix的iphash可以
2.使用spring-session框架来解决,底层采用重写httpclient保证数据共享
3.使用token令牌代替session功能,把数据存放在redis中,每次从redis中获取数据
- 20.有几个非常大的文件,在不同服务器上,找出这些文件中出现频率最高的几条
根据网上类似的答案,都是对每一条数据取Hash值,最后再归并。
希望有大佬能给个详细解决方案,感激不尽!
21.RPC和REST有什么区别
22.索引的实现原理
23.zookeeper的工作原理
Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。
1.当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和server具有相同的系统状态
一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保证。所有的提议(proposal)都在被提出的时候加上了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
2.当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的server都恢复到一个正确的状态。
每个Server启动以后都询问其它的Server它要投票给谁。对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己) 收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改server被选为leader。否则,继续这个过程,直到leader被选举出来
leader就会开始等待server连接
Follower连接leader,将最大的zxid发送给leader
Leader根据follower的zxid确定同步点
完成同步后通知follower 已经成为uptodate状态
Follower收到uptodate消息后,又可以重新接受client的请求进行服务了