【其他】专业问题 - hippowc/hippowc.github.io GitHub Wiki

Java面试题,总有一天会用到,不断更新中。。

Java基础

String,StringBuffer,StringBuilder

String类的内部实现是

private final char value[];
private int hash;

char数组,final类型的,表示不能再次被赋值;hash用来保存String的hash值;

关于String的hash值的计算方法深入研究也很有名堂:每个char的ascii值乘以31的n-i次方,然后相加,为什么是乘以31,目前只能说最好选择一个素数,这样相乘的到的结果值更不容易相同。

StringBuffer是线程安全的,StringBuilder是线程不安全的,内部char数组的初始容量是16,对于内部实现使用数组的,都会有扩容的问题,扩容都是新建一个合理大小的数组,把原来数组copy进去。

Map如何遍历

Map.entrySet(), Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator()等等

HashMap,HashTable,ConcurrentHashMap

HashMap是线程不安全的,key-value可以为nul,HashTable和ConcurrentHashMap都是线程安全的,key-value都不可以为null,HashTable与另外两个父类不同是的Dictionary,每个方法都是同步的,但是这样并不能保证使用的时候完全同步。ConcurrentHashMap对并发做了专门的优化,官方建议无需并发时使用HashMap,需要并发时使用ConcurrentHashMap。

源码时间: HashMap的主要成员变量

transient Node<K,V>[] table
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
transient int modCount;
int threshold;
final float loadFactor;

Node类型的数组,也就是一个外层数组,内层链表的结构。其中Node是Entry的实现。这个数组中的每一个位置被成为bucket(桶),HashMap有两个重要的参数:

  • Capacity Capacity就是buckets的数目,就是数组的长度
  • Load factor Load factor就是buckets填满程度的最大比例,默认是0.75

几个问题:

  • 如何根据key,将该节点放入相应的Node数组中?或者说如何根据key计算对应的数组下标

先获取key的hashCode,hashCode高位不变,低位与高位异或,然后与数组长度进行与运算,获取数组下标;解释:高位与低位异或是为了将高位的hash变相保留下来,但是具体是否更加散列了呢,道理上没办法解释,结果上来看是更散列了。然后与capacity进行与,其实是只保留后面几位,很好的替代了取模。这也是为什么建议容量是2的次方,这样才能更好的保证hash值后面几位被原封不动的保留下来。

  • JDK8后新增来红黑树?是干什么用的?

在一个bucket中Node数量超过8个之后,就会进行桶的树形化 treeifyBin,这是为了提高链表的查询效率。红黑树是一种自平衡二叉查找树,查找效率高于链表(O(Logn))

ConcurrentHashMap如何保证同步?

与HashTable不同,HashTable简单的将每个方法进行同步,也就是说执行每个方法的时候都会锁住整个对象。执行效率地下。ConcurrentHashMap在具体场景下,对并发进行了优化处理,这个例子也告诉我们优化要基于实际场景;散列表一般的应用场景是:除了少数插入操作和删除操作外,绝大多数都是读取操作,而且读操作在大多数时候都是成功的。正是基于这个前提,ConcurrentHashMap 针对读操作做了大量的优化。

ConcurrentHashMap实现特点:

jdk1.8取消了Segemnt分段锁,

  • synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发
  • 首先使用无锁操作CAS(Compare and Swap)插入头结点,如果插入失败,说明已经有别的线程插入头结点了,再次循环进行操作。如果头结点已经存在,则通过synchronized获得头结点锁,进行后续的操作。性能比segment分段锁又再次提升。
  • 使用volatile修饰Node字段,使得一个线程对共享变量修改的状态对另一个线程是可见的;保证并发修改时,其他线程看到时最新修改的值

ps:volatile

  • 共享变量指的是可以同时被多个线程同时访问的变量,共享变量都被存放在堆内存中,volatile只作用于共享变量。
  • 重排序;重排序值得指的是为了提高代码执行性能,指令的执行顺序被编译器或者运行时环境调整顺序的现象
  • 内存的可见性,是指在线程之间的可见性,一个线程对共享变量修改的状态对另一个线程是可见的
  • volatile修饰的变量不允许线程内部缓存和重排序,线程读取数据的时候直接读写内存,同时volatile不会对变量加锁,因此性能会比synchronized好

java并发编程包,用过哪些,怎么用

  • 常用的:ExcutorService(并发调用框架),ConcurrentHashMap,BlockingQueue
  • 同步:synchronize,Lock()
  • 工具:Latch(闭锁),CyclicBarrier(栅栏),Exchanger(交换机),Semaphore(信号量)
  • 其他:原子变量, ThreadLocal

后三种还需要学习下

注解实现原理

编译器会对java源码进行解析并生成class文件,而注解也是在编译时由编译器进行处理,编译器会对注解符号处理并附加到class结构中 ;注解被编译后的本质就是一个继承Annotation接口的接口

synchronized和java.util.concurrent.locks.Lock的异同

  • Lock能完成synchronized所实现的所有功能
  • synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放
  • Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。

一个线程有几个状态?

  • 新生:程序中用构造方法(new操作符)创建一个新线程时,如new Thread(r);此时它已经有了相应的内存空间和其它资源,但是还没有开始执行
  • 就绪:调用该线程的 start()方法就可以启动线程,当线程启动时,线程进入就绪状态
  • 运行:当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。
  • 阻塞:在可执行状态下,如果调用 sleep()、 suspend()、 wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被消除后,线程转入就绪状态。重新到就绪队列中排队等待,这时被CPU调度选中后会从原来停止的位置开始继续执行。
  • 死亡:线程调用 stop()方法、destory()方法或 run()方法执行结束后,线程即处于死亡状态

细分大概有这么几个状态:

  • 就绪(runnable)
  • 阻塞(Blocked):等待锁
  • 阻塞(Waiting):Object#wait() Thread#join() LockSupport#park() 且没有超时参数
  • 阻塞(Time-Waiting 限时等待):Thread#sleep() Object#wait() Thread#join()且有超时参数 LockSupport#parkNanos()等

sleep和wait的区别?

  • sleep()是线程线程类(Thread)的方法,调用会暂停此线程指定的时间,只让出了CPU,不会释放对象锁;wait()是Object的方法,调用会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次获得对象锁才会进入运行状态;
  • sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;

<< >> >>> ^ 怎么使用?(在计算hash的时候经常会看见)

<< : 左移运算符,num << 1,相当于num乘以2

: 右移运算符,num >> 1,相当于num除以2

: 无符号右移,忽略符号位,空位都以0补齐 & : 按照位做与运算 | : 按照位做或运算 ^ : 按照位做异或,位异或:位不同为真,位相同为假;场景:可以快速比较两个值是否相同等,和0异或不变,和1异或取反。

可以使用Integer.toXXString方法,打印二进制、八进制、十进制

ThreadLocal使用注意点

对于ThreadLocal使用后一定要记得remove;否则下一条线程重用这个Thread的时候,很可能get到的是上条线程set的数据而不是自己想要的内容

描述几个数据结构和排序算法

JVM

jvm加载class的过程

  • JVM通过类的全限定名(包命+类名)找到类的.class文件
  • 从下往上查找类是否已经加载,如果找到,直接返回已加载的类,如果没找着接着往上找。
  • 链接:链接就是要把二进制的.class文件转换成可以被jvm执行的Class对象的过程。这个过程又分为:检验、准备、解析。
  • 初始化:执行类或接口中的静态初始化函数(块),将静态变量初始化。这就是我们平时理解的对静态变量赋值。

内存泄露的原因是什么?如何避免?

Java中的内存泄露,广义并通俗的说,就是:不再会被使用的对象的内存不能被回收,就是内存泄露。对象都是有生命周期的,有的长,有的短,如果长生命周期的对象持有短生命周期的引用,就很可能会出现内存泄露。

决的原则就是尽量减小对象的作用域以及手动设置null值。

jvm调优策略 常用的jvm监控命令

如何做内存dump

JVM对于新生代和旧生代的垃圾回收机制

编码/设计/算法

使用过哪些设计模式

如何设计分布式缓存?高并发情况下如何初始化?

数据分片:数据分片就是把数据均匀分散到多个实例中。数据分片可以采用以下几种规则:区间分片、hash分片、 slot分片。但总归还是会有一个前置代理用于分发请求,但是这个代理可以是客户端,也可以是服务器,一般是服务器,那么这个时候这个服务器还是会有单点问题。

一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,对于分布式缓存,不同机器上存储不同对象的数据。为了实现这些缓存机器的负载均衡,可以使用式子来定位对象缓存的存储机器:m = hash(o) mod n;o为对象的名称,n为机器的数量,m为机器的编号,hash为一hash函数;

在大部分时候都可以工作得很好,然而,当机器需要扩容或者机器出现宕机的情况下,事情就比较棘手了。 当机器扩容,需要增加一台缓存机器时,通过这种hash取模的方式,可能使大部分数据无法命中。

一致性hash算法通过一个叫作一致性hash环的数据结构实现。然后将对象和机器通过hash函数都放置到同一个hash环后,在hash环上顺时针查找距离这个对象的hash值最近的机器,即是这个对象所属的机器。

可用性:线上使用过程中,如果出现某些缓存实例不可用,大量请求穿透会给DB带来巨大的压力,极端情况会导致雪崩场景,这需要有更好的方式保证缓存的高可用。于是我们采用用主从(Master/Slave)的架构,实际就是备份机

如何进行幂等控制

和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

如果一个操作不涉及数据更改,那么肯定是幂等的;设计到数据更改的操作,讨论幂等性更有意义。

对数据的操作无非增删改查:查询、更改和删除是天然幂等的,最需要注意的是新增数据,解决方式是增加唯一性索引;或者增加时进行查询(这个时候要考虑加锁)。

在单JVM下如何控制并发,在多JVM下如何控制并发

看到并发控制首先要想到并发的几个策略:

  • 乐观锁
  • 悲观锁
  • 幂等控制

从加锁的角度来考虑:单个JVM可以使用java提供的并发工具来控制。对于多个JVM,可以理解为多个应用,或者分布式应用。并发控制的关键是:如何在一个统一的地方获得锁。譬如使用统一的缓存作为锁:

  • 在需要并发控制的代码外层增加锁操作
  • 调用MemcachedClient.get("lock1"),如果为空或者等于0,则表示没有其它server争用这把锁
  • 调用MemcachedClient.put("lock1","1"),表示有人获取到锁并正在使用
  • 使用完毕后,释放锁调用MemcachedClient.put("lock1","0")
  • 要注意put的时候,对于memcache来说也是并发问题,需要进行同步控制

有几种排序算法?效率分析?

描叙下hash算法?什么是hash碰撞?什么是一致性hash?

在项目中是否采用分层的结构,是怎样划分的,各层之间采用了哪些相关技术? 对哪些设计模式比较熟悉?

通过哪些手段,来保障产品的质量?

如何实现一套单点登录的系统?两个系统之间相互免登如何设计?

  • 首先肯定要又一个独立的登陆认证服务器
  • http协议是无状态的,要区分用户需要记录用户信息,最简单的方案就是cookie
  • 问题是,浏览器是根据访问的域名,决定携带那个cookie,所谓单点要解决的一个关键问题是:含有登陆信息的cookie如何被不同域名所接受
  • 做cookie同步;单点登陆后,需要携带cookie信息302到pass.xxx.com服务器,这个服务器就是验证cookie并同步cookie到这个域名,返回需要set-cookie,然后在302到目标页面

常用的加密算法、签名算法

如何实现数据源动态切换

当我们的一个线程需要针对数据库做一系列操作时,每次都会去调用getConnection()方法获取数据库连接,然后执行完后再释放或归还数据库连接(SqlSessionTemplate就是这么做的),那么很明显,我们需要能够保证每次调用Dao层方法时都能动态的切换数据源,这就需要Spring的AOP:我们定义一个切面,当我们调用Dao层方法时,执行我们的逻辑来判断这次调用的数据源

web开发

get 和post区别?forward() 与redirect()区别?--5

HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作;其实get和post只是语义和用法上有区别,实质并没有什么区别。

forward是服务器行为,redirect是客户端行为

过滤器、拦截器的区别?--5

描述一下一个web请求的处理过程?--5

如何做表单防篡改、防重复提交?--6 7

关于防止重复提交,常常会有这样的情况:由于网络延迟或其他因素,用户点击了多次提交按钮。如何防止这种重复提交呢?

  • 前端控制。譬如设置标识位,点击后就不能再次点击,但是前端控制一般都是防君子不防小人
  • 后端控制:
在返回给客户端页面时,增加一个随机token,放置在页面的隐藏域中
客户端提交数据时,同时将token提交上来;服务端在缓存中查询是否有token的信息,如果有,则表明是重复提交,如果没有则记录token信息,并提交数据

如何实现一个多语言版本的网站?语言切换如何处理?--6 7

servlet的生命周期,是否有线程安全问题,如何解决?(单例,不允许在servlet中定义全局的类对象)--5 6

session 和cookie的区别?HTTP请求中Session实现原理?如果客户端禁止Cookie能实现Session吗?跨域cookie如何访问?--5 6

分别采用cookie和session如何实现web集群,他们分别适用哪些应用场景?--6 7

解释 XSS、 XSRF、sql注入,及举例说明?--5 6

apache和nginx的区别?tomcat 、jeety、jboss的区别?--5 6

http和https的区别?https如何保证链接的安全?一个网站如何部署成https过程?描述一次完整的https请求的过程,客户端、服务端都做了啥?单向认证?双向认证?

DB

常用sql优化策略?如何查看sql资源消耗?如何查看执行计划?

索引的怎么建最合适?是否建立了索引查询一定会快?如何走到指定的索引?--5 6

什么是死锁,如何避免死锁?乐观锁和悲观锁各自的使用场景?有哪些方案实现乐观锁?时间戳、版本、全部copy到内存,提交时比对。--6 7

ibatis如何实现批量插入?--6

事务的特点?采用什么技术实现分布式事务,如何实现,讲讲原理,为什么需要两阶段提交,能解决哪些问题,哪些问题不能解决。spring 提供了#### 哪两种事物处理方式,采用了java的什么技术?-- 6 7

db连接池的原理?解决什么问题?--6

表关联时,内连接,左连接,右连接怎么理解?--5

oracle三层分页的实现?--5

简述spring封装了ibatis的SqlMapClientTemplate类中,执行一个数据库查询语句的主要过程?--5

数据库操作的性能瓶颈通常在哪里,1000万级别访问,在数据库和java程序上考虑哪些来进行性能优化?--6

框架/开源

spring AOP解决了什么问题,spring如何实现AOP?--5

osgi了解吗?优缺点?

spring分几个模块,各个模块之间的关系?--6

spring bean初始化的过程?static 代码块、构造函数、init方法、afterPropertiesSet顺序?单例?spring的单例和设计模式中单例模式区别?设计模式中的单例模式使用过程中的注意点?如何实现?(多线程、序列化) --6 7

spring 提供了哪两种事物处理方式,采用了java的什么技术?--6

IOC的设计理念在spring中是怎么体现的?--6

操作系统/命令

如何统计集群中的pv?

grep 、awk

查看系统进程/查找指定进程?

linux的内存分配策略?

如何查看系统负载?性能监测工具有哪些?

如何查看一个文件?cat、vi、less、more区别?

如何跨机器copy?

架构

如何消除DB单点?数据拆分的方案?

主从数据库。

数据拆分方案: 单点问题:nginx单点,数据库写库也是单点

  • 性能瓶颈
  • 非高可用

单机mysql无法满足存储数据的容量要求或性能要求时,就要考虑进行数据库拆分;

  • 垂直拆分:多个数据表拆分到多个数据库中。按照业务拆分;
  • 水平拆分:一个表的数据拆分到多个表,或者多个数据库的多个表;主要问题是拆完后怎么合起来?

如何保证分布式的一致性

对于分布式事务是没有办法绝对完成事务一致性的问题,我们只能做到更好。

一些常见的解决方案:

  • 2阶段提交:多一个事务管理器TM,用来管理多个系统的事务,第一个阶段:TM向各个系统(资源管理器RM)发送prepare消息,RM接受到消息后,执行本地事务,要么返回失败,要么执行成功停留在commit;第二个阶段是:commit/rollback,如果TM收到失败的消息或者超时,那么TM发送rollback消息,一起回滚;否则发送commit消息,一起提交。

可以发现,其实2阶段提交也是会发生问题:譬如发送commit后,有的RM没有接收到,就导致有些commit有些没有,数据不一致。

设计一套系统故障隔离方案?设计一套流量控制方案?

有没有一种办法,可以看到全链路的调用情况?哪些系统的哪些方法被调用,耗时多少,等等。

在大规模机器集群上,你们如何实现程序发布上线、配置的管理以及程序和机器的监控和健康检查?

您一般会从哪些方面来保证网站的高可用性?有什么关键措施和经验可以提高系统的可用性?

在架构设计中如何应对不断出现的超过预估的数据量和访问量?

在项目的架构设计中,对于未来可能发生的需求变更,你是如何考虑的?如何应对?

在高并发应用中,Cache的作用不可忽视,在Cache的使用上,有哪些问题需要去注意?

模块化来降低耦合性时如何把握模块划分的粒度?如何权衡复用性与粒度过细导致依赖复杂的矛盾?

系统是否有对内或者对外提供服务,如何管理服务的使用权限,如何做服务升级?

性能优化

大促的系统评估方法(全链路压测、QPS/TPS等)?

对于系统优化,平时如何查找瓶颈,最后又如何确定优化后是有效的?

如何做集群的性能调优?

如何设置一个应用的最佳请求数和最佳处理线程数?

降低了RT是否就能提高TPS?如何减少RT?如何提升QPS?

通过什么工具查找热点耗时代码?

如何监控代码的执行效率?又是如何来找到瓶颈的?

产品思考

如何评估一个功能完成后的效果

互联网业务发展过程中用户增长是永恒的主题(联想到自己负责的应用,感觉很悲凉,我负责的就不是)。我们做了一些功能的改进,如何判断我们的功能是否有用,最根本是通过用户是否增长,那么有哪些指标可以用来判断用户是否增长呢?

产品页面类型:

  • 流量分发页面:首页,导航页。注重分发效率
  • 流量承接页面:1、功能性页面。2、攻略性页面

关注指标类型分类:

  • 规模类指标:反应页面的规模体量,pv uv DAU等
  • 黏性指标:反应用户忠诚度:次日复访率、7日复访率、活跃率(DAU/MAU)
  • 页面承接指标:反应用户在页面的行为:点击PV/点击UV、PV/UV点击率、人均点击次数、人均停留时长等
⚠️ **GitHub.com Fallback** ⚠️