重载与重写的区别
**重载:(发生在编译时)**同一个类中,相同名称的多个办法,这些方法的参数类型不用,个数不同,顺序不同,方法返回值和访问修饰符可以不同。
**重写:(发生在运行时)**发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private,则子类不能重写。
(访问修饰符的大小,public protected 缺省 private)
多态:父类的引用指向子类的实例对象,必须在继承关系的状态下产生
Object里面常用的方法
- hashCode() 对象地址值
- equals() 比价对象
- notify() 唤醒线程
- wait() 线程等待
equals()和 == 的区别
- ==的作用:
- 基本类型:比较的就是值是否相等
- 引用类型:比价的就是地址值是否相同
- equals()的作用:
- 引用类型:默认情况下,比较的就是地址值
- 特殊情况:String Integer Date这些类中的equals()被重写,比较的是内容而不是地址
- ArrayList的equals()比较的是内容,相同下标内容值一样,两个相等
装箱和拆箱
装箱:将值类型转换成引用类型的过程
用于垃圾回收堆中存储类型,装箱是值关系类型到Object类型或到此类型所实现的任何接口类型的隐式转换。
拆箱:将引用类型转成成值类型的过程
从Object类型到值类型或从接口类型到实现该接口的值类型的显示转换
作用:
- 为了保证通用性和提高系统的性能
好处:
- 良好的封装可以减少耦合
- 类内部的结构可以自由修改
- 可以对成员进行更精准的控制
- 隐藏信息,实现细节
String
string不可以被继承,因为是string类是final类
String,StringBuffer, StringBuilder的区别
String: 字符串常量,String中的String类中使用final关键字修复字符数组来保存字符串,String对象是不可变的,也就可以理解为常量,线程安全
**StringBuffer: 字符串变量(线程安全)**对方法加了同步锁或者对调用的方法加了同步锁,所以线程是安全的。
**StringBuilder:字符串变量(线程不安全)**并没有对方法进行加同步锁,所以是非线程安全。
小结:
- 如果要操作少量的数据用String
- 多线程下操作字符串缓冲区下操作大量数据用StringBuffer
- 单线程下操作字符串缓冲区下操作大量数据用StringBuilder
接口和抽象类的区别是什么
- **实现:**抽象类的子类使用extends来继承,接口必须使用implements来实现接口
- **构造函数:**抽象类可以有构造函数,接口不能有
- **main方法:**抽象类中有main方法,并且可以运行,接口不能有main方法
- **实现数量:**类可以实现很多个接口,但只能继承一个抽象类
- **访问修饰符:**接口中的方法默认使用public修饰,抽象中的方法可以使任意访问修饰符。
- 抽象类中的方法不一定需要全部实现,如果A是抽象类,B继承了A,B可以不需要实现A的所有方法,抽象类可以包含抽象方法和非抽象方法,如果B是非抽象类,那么B必须实现A的所有抽象方法,如果B没有实现A的所有抽象方法,那么B可以定义成抽象类。
- 实现接口的类必须实现接口的所有方法(如果是抽象类,那么可以不用全部实现接口的所有方法)。
接口的实际作用是在项目建模当中起到一个规范作用,
接口相当于生活中的生活规则,比如早上8点起床洗脸然后去上班等等
它是生活的基石,也是框架的基石。
所以spring在接口层面的设计是所有框架中最优秀的
Spring也有一个叫法叫做面向接口编程
接口的作用更像在人类社会中的法律, 法律可以规范人应该做什么不该做什么
能做什么不能做什么。
抽象类的作用是什么,它既有 接口的优势 也有普通类的优势,
抽象类的作用就是比如根据法律来理解的话,
法律的最终解释权是由 制定者或者执行者来规范的 普通人只需要遵循它就可以了
接口和抽象类 拿 计算机硬件来举例子是最合适 也是最贴切的
比如接口是usb接口 它只能插usb
他不能插别的 因为接口规范定死了 不能改变
抽象类可以在接口上进行继承来实现接口的具体功能,
抽象类有点类似于转接头 在usb接口上可以增加更多的实用性功能比如提供的typec
或者提供的网线 或者提供其他的 接口等等
类的实现就相当于插在接口上的键盘鼠标 或者其他有关外设的东西
final,finally,finalize
- final用于属性,方法,类,分别标识属性不可变,方法不可覆盖,类不可继承
- finally是异常处理语句接口的一部分,一定会执行
- finalize是Object类的一个方法,再垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖方法提供垃圾收集的其他资源回收,比如关闭文件等。
final 修饰的类有什么特点
- final类不能被继承,没有子类,final类中的方法默认是final的
- final方法不能被子类的方法覆盖,但可以被继承
- final成员变量表示常量,只能被赋值一次,赋值后值不能改变
- final不能用于修饰构造方法
final修饰引用,引用地址不能改变,但是对象内容可以改变
集合
集合和数组的区别
- 数组长度固定,集合长度可变
- 数组中存储的是同一种数据类型的元素,可以存储基本数据类型,也可以存储引用数据类型。
- 集合:存储的都是对象,而且对象的数据类型可以不一致,开发当中一一般使用对象较多。
List, Map, Set的区别
List和Set是存储单列数据的集合,Map是存储键值对这样的双列数据的集合
- List中存储的数据是有顺序的,并且值允许重复
- Map中存在的数据是无序的,他的键不允许重复,值可以重复;
- Set中存储的数据是无顺序的,并且不允许重复,但是元素再集合中的位置是有hashCode决定的,(Set集合是根据hashCode进行数据存储的,所以位置是固定的,但是这个位置是用户可以控制的,所以对于用户来说Set中的元素还是无序的)
List是如何删除元素的
- 使用Iterator(迭代器)的hasNext方法进行判断并删除
- 使用Stream流的filter
ArrayList和LinkedList的区别
数据结构
- ArrayList是动态数组的数据结构(查找o1)
- LinkedList是链表的数据结构,查找是on
执行效率:
- 查找数据是,ArrayList效率更高,因为LinkedList需要通过遍历查找,而ArrayList直接用下班查找,
- 对数据进行增加和删除的操作时,LinkedList的效率更高,因为ArrayList是数组,再其中进行增删操作时,会对操作点之后的所有数据下标索引照成影响,需要进行数据的移动。LinkedList只需要改变指向就好。
ArrayList怎么扩容
起始值为10,最大为Max_Value - 8
每次扩容为原长度的1.5倍,扩容时会创建一个1.5倍的新数组,将原数组数据复制过去。
HashMap和TreeMap的区别
是否有序:
- HashMap是无序的
- TreeMap默认的排序为升序,如果要改变其排序可以直接写一个Comparator;
底层结构:
- HashMap是数组+链表+红黑树
- TreeMap是红黑树
线程安全:
两者都是线程不安全的
HashMap,HashTable, ConcurrentHashMap区别
HashMap 和 HashTable 区别
- HashMap是非线程安全的,HashTable是线程安全的。
- HashMap的键和值都允许有null的值存在,而HashTable则不行
- 因为线程安全问题,HashMap的效率比HashTable的更高
- HashTable是同步的,而HashMap不是,因此HashMap更适合于单线程环境,而HashTable适合于多线程环境
现在一般不建议用HashTable
- HashTable是遗留类,内部实现很多没有优化和冗余
- 现在使用ConcurrentHashMap
HashTable和ConcurrentHashMap 的区别
- HashTable使用的是synchronized关键字修饰
- ConcurrentHashMap是JDK1.7使用了锁分段技术来保证线程安全的
- JDK1.8 ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。
- 数据接口跟HashMap的结构类型,数组+链表+红黑树
- synchronized只锁定当前链表或者红黑树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升
Hash冲突:
再向hash表中存放数据时,首先要用Hash函数计算处该数据要存放的地址,但再这个地址中已经有值的存在,就发生了Hash冲突
HashMap的Put流程
- 首先根据Key的值计算Hash值,找到该元素再数组中存储的下班;
- 如果数组是空的,则调用resize进行初始化;
- 如果没有哈希冲突值直接放在对象的数组下标里;
- 如果冲突了,且key已经存在了,则覆盖值,
- 如果冲突后是链表结构,就判断该链表是否大于8,如果大于8并且数组容量小于64,则进行扩容,
- 如果链表节点数量大于8并且数组的容量大于64,则将这个结构转成红黑树;
- 否则,链表插入键值对,诺key存在,就覆盖掉value值。
- 如果冲突后,发现该节点是红黑树,将节点挂在树上。
HashMap的底层原理
- 数组+链表+红黑树实现,主要的目的是提高查找效率;当链表中的元素大于等于8个以后,会将链表转成红黑树,当红黑树节点小于等于6个时又会退化成链表;
- 当new HashMap()底层没有创建数组的,首次调用put()方法示时,底层创建长度为16的数组;
HashMap的扩容机制是什么
HashMap在容量超过负载因子所定义的容量之后,就会扩容;
如果数组长度16,负载因子0.75 那么当容量超过12的时候会触发扩容;
因为HashMap用的数组结构,并且数组是无法自己扩容;
所以HashMap会创建一个原来2倍大小的数组,即32,将旧数据移动过去
多线程
线程和进程的区别
线程:是进程是一个实体,是CPU调度和分派的基本单位,是比进程更小的可以独立运行的基本单位。
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。
特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间内存共享,这使得多线程编程可以拥有更好的性能和用户体验。
线程安全:当多个线程访问一个对象时,如果不考虑这些线程在运行环境下的调用和交替执行,也不需要进行额外的同步,调用这个对象都可以获得正确结果,那么叫线程安全。
创建线程的几种方式
- 继承Thread类并重写run方法创建线程,实现简单但是不可以继承其他类
- 实现Runnable接口并重写run方法
- 实现Callable接口并重写call方法,创建线程,可以获取线程执行结果的返回值,并且可抛出异常
- 使用线程池创建,线程池使用ThreadPoolExecutor;
常用线程池,该线程池包含哪些参数
常用线程池为ThreadPoolExecutor.
参数如下:
- corePoolSize: 核心线程池
- maximumPoolSize: 最大线程数
- keepAliveTime: 最大等待时间
- unit: 最大等待时间单位
- workQueue: 等待队列
- threadFactory: 线程工厂
- handler: 拒绝策略
拒绝/饱和策略有哪些
- AbortPolicy策略:会直接抛出异常,阻止系统正常工作。
- CallerRunPolicy策略:只要线程池没有关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
- DiscardoldestPolicy策略: 将丢弃即将执行的下一个任务,并尝试再次提交当前任务
- DiscardPolicy策略: 默默丢弃没发处理的任务,不予处理。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
*
* @author cyh
* 11:29
*/
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean("doContractExecutor")
public Executor doSomethingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:线程池创建时候初始化的线程数
executor.setCorePoolSize(10);
// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
executor.setMaxPoolSize(20);
// 缓冲队列:用来缓冲执行任务的队列
executor.setQueueCapacity(500);
// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
executor.setKeepAliveSeconds(60);
// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
executor.setThreadNamePrefix("do-contract-");
// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.initialize();
return executor;
}
}