面试常见问题

一、创建一个不可变类(即final类)需要满足哪些条件?

1、将类声明为final,所以它不能被继承;

2、将所有的成员声明为私有的,这样就不允许直接访问这些成员;

3、对变量不要提供setter方法;

4、将所有可变的成员声明为final,这样只能对它们赋值一次;

5、通过构造器初始化所有成员,进行深拷贝(deep copy);

6、在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝;

二、 什么是线程?什么是进程?以及他们之间的关系

        *进程是指一段程序的运行过程,是资源分配的基本单位,

        *线程是CPU独立运行和调度的基本单位,程序执行的最小单位

两者之间的联系:都是操作系统程序运行的基本单元

两者的区别:

(1)地址空间:进程之间的地址空间独立,线程共享本进程的地址空间

(2)资源拥有:进城之间资源独立,线程之间共享所属进程的资源,如:内存,CPU,IO等(线程有自己的堆和栈)

(3)执行过程:每个进程都有一个程序执行入口;线程是不能独立执行的,必须依存在进程,由程序的多线程机制控制

(4)健壮性:一个进程奔溃不会影响其他进程的执行,一个线程奔溃会导致进程的奔溃

(5)通信:进程间通信CPI,线程间可以直接读写进程数据段(共享空间)进行通信,但是需要线程的同步和互斥手段,以保证数据一致性

(6)调度和切换:线程上下文切换比进程上下文切换要快很多

三、start()和run()的区别?

Start()是启动一个线程(不是立即执行,而是使线程变为可运行态),run()是执行当前线程的方法。调用start()才能实现多线程

四、sleep()和wait()的区别?

      Sleep()是将当前线程阻塞,暂停程序,让出CPU,但不释放锁,无法执行同步方法,指定时间到了若无比它优先级高的线程执行就会恢复运行态,否则需等待

      Wait()是让线程进入等待状态,线程会释放锁,其他线程可以调用其同步方法,只有调用notify()方法后本线程才能进入锁定池准备

五、Sleep()和yield()的区别?

执行sleep()的线程时间未到是不能恢复执行态的,时间确定,较低优先级的线程有机会执行

而yield()有可能从可执行态又马上被执行,时间不定,只让给高优先级线程执行

六、解决线程不安全风险的办法:

  • 不在线程中共享该状态变量,可以将变量封装到方法中。
  • 将状态变量修改为不可变的变量(final)。
  • 访问状态变量时使用同步策略。
  • 使用原子变量类。

七、什么是线程同步?

就是线程发出一个功能调用时,在没有得到结果之前,该调用就不会返回,其他线程也不能调用该方法。

八、解释一下什么是死锁?死锁产生的原因?死锁产生的条件?

死锁是指两个线程A,B争夺同一资源s,线程A开始执行,线程A需要用到线程B执行完后的结果,这时线程A因需等待B的结果而进入阻塞状态,线程B开始运行,但却无法获取资源s,线程B也进入阻塞状态,此时两个线程处于相持状态,即死锁

九、为什么wait, notify 和 notifyAll这些方法不在thread类里面?

首先需要明白wait(),notify()之间的等待和唤醒操作是通过什么联系起来的,对象的同步锁,唤醒线程只有在获取对象的同步锁才能调用notify进行唤醒操作,同步锁是对象锁持有,并且每个对象只有一个,所以将这些方法放在Object对象中,而不是Thread

十、如何避免死锁?

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。

十一、怎么消除死锁?

(1)系统重新启动,代价有些大;(2)撤销进程,剥夺资源(3)进程回退策略

十二、Java8有什么好用的新特性?

  1. JDK语法上:
    • 二进制变量的表示,支持将整数类型用二进制来表示,用Ob开头
    • Switch语句支持String类型
    • Try-with-resource语句 
  2. NIO2的一些特性
  3. JDBC4.1
    • 可以使用try-with-resources自动关闭Connection,ResultSet,Statement资源对象 
    • JDBC-ODBC驱动会在jdbc8中删除
  4. 并发工具增强
    • Fork-join
    • thread local 的使用
  5. 允许给接口添加一个非抽象的方法实现,使用default关键字即可
  6. Lambda表达式
  7. 允许重复注解
  8. 引入Stream API
  9. 用元空间代理了堆中的永久区

**HashMap的再认识

HashMap初始化存储数组table是在第一次添加键值对的时候,即第一次调用put()时,每当要存储一个key时,会用一个hash函数算出这个key存储在hashMap的table数组的哪个位置,这个数组默认长度为16,每个数组位置又是一个链表,当产生index冲突时,就用链表LinkedList存储(链地址法,采用头插法),查找对应的value时,先算出要查找的key的hashcode()算出这个位置后,在去查询链表,一个个往下找,遍历,直到找到对应的key(采用equals()方法),

(1)为什么hashMap默认长度为16

        初始化长度为16,每次自动扩展或手动初始化的时候,长度必须是2的幂。这是为了服务于key映射到index的hash算法,index=HashCode(key)&(length-1);类似除留余数法形式的hash函数,其实key的index值取决于HashCode的最后几位,

(2)如果长度不是16会发生什么情况?

        长度为16可以更合理分配index位置,扩展时长度为2的幂次方,可以使的hashcode与运算以后的index值分配更加均匀

(3)如果HashMap的大小超过了负载因子(loadfactor)定义的容量

        首先,负载因子默认为0.75,这是时间和空间上的一种折衷。当hashMap填满了75%时,和其他集合一样,将会创建原来HashMap大小的二倍的数组,2的幂次方,并将原来的数组对象放入新的数组中,这个过程叫rehashing,因为是调用hash方法来找到新的bucket位置

(4)你了解重新调整HashMap大小存在什么问题吗?

        当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个进程都发现HashMap需要重新调整大小了,他们会同时试着调整大小,在调整的过程中,存储在LinkedList中的元素次序会反过来,因为移动到新的bucket位置时,HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tailtraversing)。如果条件竞争发生了,那么就死循环了

.Hashtable和ConcurrentHashMap有什么分别呢

hashMap是怎么存储null的?

        他们都可以用于多线程环境,但是hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。而CurrentHashMap引入了分割,采取分段锁的方式,仅仅锁定map的某个部分,而其他的线程不需要等待迭代完就可以访问map,在迭代的过程中,currentHashMap仅仅锁定map的某个部分,而HashTable会锁定整个map

.hashmap的哪种遍历方式比较好

hashMap的两种遍历方式:entrySet()和keySet(),但是第一种比第二种效率高,keySet其实是遍历了两次,一次是iterator,一次是从hashMap中取出key所对应的value;而entrySet只是遍历了第一次,他把key和value都放到了entry中,即键值对,所以效率较快

在JDK8中的HashMap:

1.存储形式为:数组+链表+红黑树(当数组长度大于8时自动转换为红黑树存储)

2.重新扩容HashMap时,若原有红黑树处的entry组少于6个,则将红黑树转换为数组

3.数组的插入形式在JDK7为尾插法,JDK8为头插法

4.扩容后存储位置的计算方式不同,JDK7为全部重新计算,JDK8为原位置(新增参与运算的位为0)或者原位置+旧容量(新增参与运算的位为1)

5.插入数据发现容量不足要扩容时:JDK8为扩容前插入数据,转移数据时统一计算hash位置,JDK7为扩容后插入,再单独计算此数据的hash值,即转移数据时无统一计算

6.由于JDK8转移数据操作 = 按旧链表的正序遍历链表、在新链表的尾部一次插入,所以不会出现链表逆序、倒置的情况,故不容易出现环形链表的情况。但它还是线程不安全的,因为无加同步锁保护。

**关于跨域问题:

互联网的认证通常采取的流程如下:

  1. 用户向服务器发送用户名和密码
  2. 服务器验证通过后,在当前对话session里面保存相关数据,比如用户角色,登陆时间等等
  3. 服务器向用户返回一个session_id,写入用户的cookie
  4. 用户随后的每一次请求,都会通过cookie,将session_id传回服务器
  5. 服务器收到session_id,找到前期保存的数据,得知用户的身份

但是这种方法扩展性不好,单机当然没问题,如果是服务器集群,或者是跨域的服务导向架构,就要求session数据共享,每台服务器都能够读取session

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

        一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

        另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。(Json and Token)

JWT原理:服务器认证以后,生成一个Json对象,发回给用户,以后用户与服务器端通信的时候,都要发回这个Json对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名

JWT 的三个部分依次如下。

Header(头部)

Payload(负载)

Signature(签名)

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

Http和Https的区别?

超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,Http协议以明文方式发送内容,故不适合传输一些敏感信息

安全套接字层超文本传输协议HTTPS,是在Http协议基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密

ORM是什么?(Object/Relation Mapping)对象/关系映射

将数据库中表的的记录映射成对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作,因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作

ORM是什么?ORM框架是什么?

ORM模式:是一种为了解决面向对象与数据库存在互不匹配的现象的技术,即将程序中的兑现自动持久化到关系数据库中,那么如何实现持久化呢?

  1. 采取硬编码,JDBC操作数据库,为每一种可能的数据库访问操作提供单独的方法

缺点:①持久层缺乏弹性,一旦业务需求更改,就必须修改持久化层的接口

②持久化层同时与域模型与数据库模型绑定,不管域模型还是关系数据库模型发生变化,都要修改持久层的相关代码,增加了软件维护功能

  1. 采用映射元数据来描述对象关系得到映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。Java的ORM框架有:Hibernate,Mybatis,SpringJDBC,speedframework
  2. ORM的方法论基于三个核心原则:

 简单:以最基本的形式建模数据

 传达性:数据库结构被任何人都能理解的语言文档化

精准性:基于数据模型创建正确标准化了的结构

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值