1. 构造器是否可重写
首先,构造器是不能被继承的,因为每个类的类名都不相同,而构造器名称与类名相同,所以根本谈不上继承。
又由于构造器不能继承,所以就不能被重写。但是,在同一个类中,构造器是可以被重载的
2. String类能被继承吗,为什么
不可以,因为String类有final,而final修饰的类是不能被继承的
3. String,StringBuffer,StringBuilder的区别
- String:不可变,每次对String进行操作都会产生新对象,效率低且浪费内存空间
- StringBuffer: 可变字符序列,效率低,线程安全
- StringBuilder: 可变字符序列,效率高,线程不安全
StringBuilder、 StringBuffer继承AbstractStringBuilder,无参数默认初始容量为16,
追加字符串的时候长度超过16则扩容:增加自身长度一倍再加2,如果还放不下,则扩容为所需长度minCapacity
private int newCapacity(int minCapacity) {
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
}
4. 线程池五元组了解吗
线程池五元组指的是在使用线程池时,每个线程都需要维护的五个基本信息,包括线程所属的进程ID(PID)、线程ID(TID)、线程所监听的端口号(PORT)、线程状态和任务队列等信息。
其中,PID是进程ID,用于唯一标识一个进程,是操作系统分配给进程的一个唯一标识符;TID是线程ID,用于唯一标识一个线程,是操作系统分配给线程的一个唯一标识符;PORT是端口号,用于标识一个网络连接,不同的线程可以监听同一个端口号,通过不同的TID进行区分;线程状态是线程当前的运行状态,包括空闲、忙碌、等待等状态;任务队列则是线程池中用于存放待执行任务的队列。
维护这些信息可以让线程池更加高效地管理线程,提高线程的复用率,避免了频繁创建和销毁线程的开销,从而提高程序的性能
5. IO模型有哪些,讲讲你理解的nio,bio,aio的区别是啥,谈谈reactor模型
在计算机网络编程中,常用的IO模型主要有阻塞IO(Blocking IO,BIO)、非阻塞IO(Non-Blocking IO,NIO)、多路复用IO(Multiplexing IO,MIO)和异步IO(Asynchronous IO,AIO)。
- 阻塞IO(BIO):线程会一直阻塞等待直到IO操作完成,期间无法处理其他任务。这种模型适用于并发性较低、并发请求处理较慢的场景。
- 非阻塞IO(NIO):线程不会阻塞等待IO操作完成,而是通过轮询IO状态来判断是否就绪,如果未就绪则可以处理其他任务。但这种方式需要一直轮询,会增加CPU的负担。
- 多路复用IO(MIO):通过一个线程处理多个IO请求,典型的实现方式有select、poll、epoll等。这种方式可以有效地减少CPU的负担,但同时也会存在性能瓶颈。
- 异步IO(AIO):线程发起IO操作后可以立即返回继续执行其他任务,IO操作完成后再回调通知线程。这种方式适用于并发请求较多、并发处理速度较快的场景。
在Java中,常用的IO模型有BIO、NIO和AIO,其中:
- BIO:Java标准IO模型,使用阻塞IO方式,适用于并发性较低、并发请求处理较慢的场景。
- NIO:Java New IO模型,使用多路复用IO方式,适用于并发请求数较多、请求处理速度较快的场景。相较于BIO,NIO可以更好地处理高并发请求。
- AIO:Java Asynchronous IO模型,使用异步IO方式,适用于需要处理大量IO请求的场景。相较于NIO,AIO的效率更高,能够更好地利用CPU资源。
Reactor模式是一种基于事件驱动的IO模型,它使用一个单独的线程来接收和分发事件,而不是为每个连接分配一个线程。当有新的连接到来时,Reactor线程会将其加入到一个事件队列中,然后通过一个或多个IO线程来处理队列中的事件。这种模型可以有效地减少线程的数量,提高系统的并发性和可伸缩性。在Java中,NIO使用的Selector就是一种Reactor模式的实现
6. 介绍下常用的Map
- TreeMap:基于红黑树实现,实现SortedMap接口,默认升序排序
- HashMap:基于哈希表实现
- HashTable:和 HashMap 类似,但它是线程安全的。它是遗留类,不应该去使用它,而是使用 ConcurrentHashMap 来支持线程安全
- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序
6.1 为什么HashMap是非线程安全的?什么情况下会出现线程安全问题?
HashMap会进行resize操作,在resize操作的时候会造成线程不安全
1)put的时候多线程导致数据不一致
2)get操作可能因为resize而引起死循环(cpu100%)
6.2 JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计
Segment继承ReentrantLock,每个锁控制一段,当每个Segment越来越大时,锁定粒度则变大,性能会下降
ConcurrentHashMap底层采用数组+链表+红黑树,大量采用CAS操作,加锁采用synchronized,只对桶的头节点进行加锁,粒度较小。
减少内存开销。如果采用ReentrantLock需要节点继承AQS来获得同步支持,增加内存开销,
内部优化。synchronized是由JVM直接支持的,JVM能够在运行时做相应优化如,锁粗化、锁消除、锁自旋等
6.3 有没有顺序的Map实现类,如果有,他们是怎么保证有序的
TreeMap和LinkedHashMap有序的(TreeMap默认升序,LinkedHashMap记录插入的顺序)
TreeMap是基于比较器Comparator实现有序的,LinkedHashMap是基于链表实现数据插入有序的
6.4 用过哪些Map类,都有什么区别,HashMap是线程安全的吗,并发下使用的Map是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。
HashMap、ConcurrentHashMap、TreeMap、 LinkedHashMap
HashMap线程不安全,由数组(Node数组)+链表+红黑树(链表长度大于8转换为红黑树)实现,采用hash表来存储,key采用hashcode,数量超过threshold时进行2倍扩容,默认容量16,负载因子0.75,桶的数量11,
采用链地址法解决hash冲突,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上
Java 8系列之重新认识HashMap
大量采用CAS操作,JDK1.7对Segment进行加锁,JDK1.8对桶中的头结点进行加锁
ConcurrentHashMap的实现原理
TreeMap和LinkedHashMap有序的(TreeMap默认升序,LinkedHashMap记录插入的顺序)
7. JAVA中的几种基本数据类型是什么,各自占用多少字节。
数据类型 | 字节 | 默认值 |
---|---|---|
byte | 1 | 0 |
short | 2 | 0 |
char | 2 | ‘\u0000’ |
int | 4 | 0 |
float | 4 | 0.0f |
long | 8 | 0 |
double | 8 | 0.0d |
boolean | 4 | false |
PS: JVM规范中,boolean当做int处理,也就是4字节,而boolean数组当做byte数组处理,即boolean类型的数组里面每一个元素占一个字节
8. ArrayList和LinkedList有什么区别
- ArrayList: 基于数组实现的非线程安全的集合。查询速度快,插入、删除中间元素速度慢
- LinkedList: 基于链表实现的非线程安全的集合。查询速度慢,插入、删除中间元素快
- Vector: 基于数组实现的线程安全的集合。采用synchronize加锁,性能比ArrayList差
- CopyOnWriteArrayList: 基于数组实现的线程安全的写时复制集合,采用ReentrantLock加锁,性能比Vector高,适合读多写少的场景
9. 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类非静态变量(父类实例成员变量)
父类构造函数
子类非静态变量(子类实例成员变量)
子类构造函数
10. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么
- 接口只能做方法声明,抽象类既可以做方法声明也可以做方法实现
- 接口里定义的变量只能是公共的静态常量,抽象类中变量是普通变量
- 抽象类里可以没有抽象类,如果一个类中有抽象方法,则一定是抽象类
- 抽象方法要被实现,所以不能是静态的,也不能是私有的
- 接口继承接口,并可多继承接口,但类只能单继承
类不能继承多个类
接口可以继承多个接口
类可以实现多个接口
11. 继承、组合、聚合有什么区别
继承: is-a的关系,指一个类继承另一个类 public class A extends B{}
组合:contains-a,强聚合,A一定有B public class A {B b}
聚合:has-a, A可以有B public class A {List<B> b}
12. 请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设计中的作用。
OO设计理念:封装、继承、多态
关键字 | 类内部 | 本包 | 子类 | 外部包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
13. 这样的a.hashcode() 有什么用,与a.equals(b)有什么关系
hashcode()提供了对象的hashcode值,是一个native函数,返回的默认值与System.identityHashCode(obj)一致
作用是用一个数字标识对象,比如HashMap中的key就是基于hashcode, hashcode只能说是标识对象,在hash算法中可以将对象相对离散些,但不是唯一的,根据hashcode定位到具体的链表后,需要遍历链表,然后通过equals()方法来对比key是否是一致的。
equals相等的两个对象,hashcode一定相等;hashcode相等的两个对象不一定equals相等
14. 有没有可能2个不相等的对象有相同的hashcode
有。同13
15. 深拷贝和浅拷贝区别
如果一个对象内部只有基本数据类型,那么clone()方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那么clone()方法:
浅拷贝 对基本数据类型进行值拷贝,对引用数据类型进行引用传递般的拷贝,此为浅拷贝
深拷贝 对基本数据类型进行值拷贝,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝
- 序列化反序列化实现
- clone方法 对其内部引用类型的变量,在进行一次clone
欢迎关注公众号算法小生