1、0513
1.重载和重写的区别?
重载发生在同一类中,同名的方法如果有不同的参数列表(类型不同、个数不同、顺序不同)则视为重载。
重写发生在父子类中,重写要求子类重写之后的方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常。
2.String 和 StringBuffer、StringBuilder 的区别是什么?
可变性:
String类中使用final关键字修饰字符数组来保存字符串,所以String对象是不可变的。
而StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[]value但是没有用final关键字修饰,所以这两种对象都是可变的。
线程安全性:
String中的对象是不可变的,也就可以理解为常量,线程安全。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能:
每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。
StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的风险。
3.== 与 equals 的区别?
对于基本类型和引用类型==的作用效果是不同的,基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同。
equals本质上就是==,只不过String和Integer等重写了equals方法,把它变成了值比较
4.说说自己是怎么使用synchronized 关键字,在项目中用到了吗?
修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
双重校验锁实现对象单例(线程安全)
5.抽象类和接口的区别是什么?
实现:抽象类的子类使用extends来继承;接口必须使用implements来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
实现数量:类可以实现很多个接口;但只能继承一个抽象类【java只支持单继承】。
访问修饰符:接口中的方法默认使用public修饰;抽象类中的抽象方法可以使用Public和
/prəˈtektɪd/
Protected修饰,如果抽象方法修饰符为Private,则报错:The abstract method方法名in type Test can only set a visibility modifier,one of public or protected。接口中除了static、final变量,不能有其他变量,而抽象类中则不一定。
设计层面:抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
2、 0515
(1)Collection和Collections的区别
Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口,方法,所有集合都是它的子类,比如List,Set等。
Collections是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections.sort(list)。/sɔːt/
(2)List、Set、Map之间的区别
元素是否有序,是否允许元素重复
List:元素有序,允许重复
Set:
AbstractSet、HashSet :元素无序,不允许重复
TreeSet:元素有序,不允许重复
Map:
AbstractMap、HashMap:元素无序,key值唯一,value可重复
TreeMap:元素有序,key值唯一,value可重复
(3)HashMap和HashTable的区别
存储:HashMap允许key和value为null,而HashTable不允许。
线程安全:HashTable是线程安全的,而HashMap是非线程安全的。
推荐使用:在Hashtable的类注解可以看到,Hashtable是保留类,不建议使用。推荐在单线程环境下使用HashMap替代,如果需要多线程使用则用ConcurrentHashMap替代。
(4)说一下HashMap的实现原理
HashMap基于Hash算法实现,我们通过put(key,value)存储,get(key)获取。当传入key时,HashMap会根据key.hashCode()计算出hash值,根据hash值将value保存至bucket里。当计算出的hash值相同时,我们称之为hash冲突,HashMap的做法是用链表和红黑树存储相同hash值的value。当hash冲突的个数较少时,使用链表,大于8时使用红黑树。
(5)HashSet的实现原理
HashSet是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成,HashSet不允许重复的值
3、0516
(1)ArrayList和LinkedList的区别
ArrayList:基于动态数组,连续内存存储,适合下标访问(随机访问),
扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList(需要创建大量的node对象)。
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询:需要逐一遍历,遍历LinkedList必须使用iterator,不能使用for循环,因为每次for循环体内通过get(i)取得某一元素时都需要对list重新进行遍历,性能消耗极大。
(2)ConcurrentHashMap原理,jdk7和jdk8版本的区别
ConcurrentHashMap原理解析,jdk1.7与1.8区别_Der_Dream的博客-CSDN博客
jdk7:
/riːˈentrənt/ /ˈseɡmənt/
数据结构:ReentrantLock+Segment+HashEntry,一个Segment中包含了一个HashEntry数组,每个HashEntry又是一个链表结构。
元素查询:二次hash,第一次hash定位到Segment,第二次hash定位元素所在链表的头部。锁:Segment分段锁,Segment继承了ReentrantLock,锁定操作的Segment,其他Segment不受影响,并发度为Segment的个数,可以通过构造函数指定,数组扩容不影响其他Segment get方法无须加锁,volatile保证。
jdk8: /ˈsɪŋkrənaɪzd/ /nəʊd/ /ˈvɒlətaɪl/
数据结构:synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性
查找,替换,赋值操作都使用CAS。
锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有读写操作,并发扩容
读操作无锁:Node的val和next使用volatile修饰,读写线程对该变量互相可见
数组使用volatile修饰,保证扩容时被读线程感知。
(3)哪些集合类是线程安全的?
/ˈvektə(r)/ /stæk/
Vector、Hashtable、Stack都是线程安全的,而像HashMap则是非线程安全的,不过在JDK1.5之后随着Java.util.concurrent并发包的出现,它们也有了自己对应的线程安全类,比如HashMap对应的线程安全类就是ConcurrentHashMap.
(4)创建线程有哪几种方式?
继承Thread重写run方法;
实现Runnable接口;
实现Callable接口。
(5)说一下runnable 和callable有什么区别?
runnable没有返回值,callable可以拿到有返回值,callable可以看作是runnable的补充。
4、0517
(1)线程有哪些状态?
当线程对象对创建后,即进入了新建状态(NEW),;当调用线程对象的start()方法(t.start();),线程即进入就绪状态(Runnable);CPU获取到时间片,进入到运行状态(Running);当线程调用wait()或者sleep()时,进入阻塞状态(Blocked),当休眠时间结束后,或者调用notify或notifyAll时会重新进入就绪状态(Runnable),再重新获取时间片,进入运行状态,线程执行完了或者因异常退出了run()方法,该线程结束生命周期,进入终结状态(Dead)
(2)sleep() 和wait() 有什么区别?
类的不同:sleep()来自Thread,wait()来自Object。
释放锁:sleep()不释放锁;wait()释放锁。
用法不同:sleep()时间到会自动恢复;wait()可以使用notify()/notifyAll()直接唤醒。
(3)notify()和 notifyAll()有什么区别?
notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll()调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
(4)线程的 run() 和 start() 有什么区别?
start()方法用于启动线程,run()方法用于执行线程的运行时代码。run()可以重复调用,而start()只能调用一次。
(5)说一说几种常见的线程池及适用场景?
FixedThreadPool:可重用固定线程数的线程池。(适用于负载比较重的服务器)SingleThreadExecutor:只会创建一个线程执行任务。(适用于需要保证顺序执行各个任务;并且在任意时间点,没有多线程活动的场景。)
CachedThreadPool:是一个会根据需要调整线程数量的线程池。(大小无界,适用于执行很多的短期异步任务的小程序,或负载较轻的服务器)
ScheduledThreadPool:继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。使用DelayQueue作为任务队列。