1.接口和抽象类的区别
1.抽象类只能通过子类使用extends继承来实现,接口使用implements关键字来实现
2.抽象类可以有main函数,但是我们不能使用,接口没有main函数
3.抽象类有构造方法,接口没有构造方法
4.一个类可以实现多个接口,但是只能继承一个抽象类
2.List、Set、Map的区别
1.List、Set都是继承Collection的接口,他们都是单列集合。List接口允许元素重复且有序,Set接口不允许元素重复,且没有顺序(因为其通常维护自己内部的顺序,eg:HashSet是通过哈希表、TreeSet是通过红黑树来维护自己的内部顺序,所有对set随机访问是没有任何意义的)
2.Map是储存键值对(key-value)的双列集合。key和value是一一对应的关系,key值不能相同,value值可以相同,不允许元素重复(原因和set一样,下面的实现类会解释)
3.List的实现类是ArrayList、LinkedList、Vector、Stack。Set的实现类是HashSet、TreeSet、LinkedHashSet。Map的实现类是HashMap、HashTable、TreeMap
3.List、Set、Map的实现类
List的实现类是ArrayList、LinkedList、Vector、Stack
ArrayList:ArrayList是一个动态数组,是我们最常用的集合,它允许任何符合规则的元素插入,甚至包括NULL。每一个ArrayList都有一个初始长度10,当到达10会进行扩容,扩容之后的长度是原来的1.5倍。ArrayList擅长查找元素,不擅长删除和插入操作,ArrayList是非同步的,所以线程不安全。
LinkedList:LinkedList是一个双向链表,他除了满足ArrayList基本操作方法还提供了get、insert、remove方法在双向链表的头部和尾部。由于实现方式不同,LinkedList所有操作都需要从头遍历,因此其更擅长删除和插入操作,不擅长查找元素。LinkedList是非同步的,所以线程不安全。
Vector:与ArrayList相似,可以理解为线程安全的ArrayList
Stack:继承于Vector,实现了一个先进后出的栈。提供了push(进栈)pop(出栈) research (查找栈内元素)peek(查看栈顶元素) empty(判断栈内元素是否为空)操作
Set的实现类是HashSet、TreeSet、LinkedHashSet
HashSet:堪称查询速度最快的集合,是基于哈希表实现的,其内部是由HashCode决定的,元素允许为NULL,但是只能有一个,由于它是由HashCode决定顺序的,因此他不能保证set的迭代顺序。
TreeSet:是基于红黑树(平衡的二叉搜索树)实现的。元素有序,不允许为NULL
LinkedHashSet:是基于哈希表和双线链表实现的。哈希表用来存储元素,双向链表用于管理元素顺序,使元素看起来像是根据插入顺序存储的。根据元素添加顺序遍历该集合。
Map的实现类是HashMap、HashTable、TreeMap
HashMap:通过哈希表实现的(重点了解一下HashMap的实现原理),高效,支持NULL值和NULL键,线程不安全。
HashTable:线程安全,低效,不支持NULL值和NULL键。
LinkedHashMap:线程不安全,是HashMap的子类,保存了记录的插入顺序。
TreeMap:线程不安全,能够把它保存的记录根据键排序,默认是键值的升序排序。
4.说一下HashMap的实现原理
1.底层使用哈希表数据结构,即数组+链表+(红黑树)
jdk1.8之前使用拉链法(数组+链表)jdk1.8之后采用数组+链表+红黑树,当链表长度大于8且数组长度大于64,则从链表转化为红黑树
2.添加数据时,计算key值确定元素在数组的下标,key相同则替换,不同则存入链表或红黑树中,获取数据通过key的hash计算数组下标获取元素。
5.JDK、JRE、JVM的区别
jdk是Java开发工具包,包含了Java的开发环境和运行环境
JRE是Java运行环境
JVM是Java虚拟机,Java多平台运行就是通过JVM实现的
6.重载和重写的区别
重载:即在一个类中,方法名必须相同,参数列表和返回值可以不同
重写:子类重写父类的方法,方法名必须相同,参数列表和返回值必须相同
7.String、StringBuffer、StringBuilder的区别
String:是通过final修饰的,字符串常量(线程安全)
StringBuffer:字符串变量(对方法或者调用的方法加了同步锁,所以线程安全)
StringBuilder:字符串变量(没有对方法加同步锁,所以线程不安全)
如果操作少量数据使用String
如果操作单线程字符串缓冲区下的大量数据应该使用StringBuilder
如果操作多线程字符串缓冲区下的大量数据应该使用StringBuffer
8.Java中==和equals的区别
==:在基本数据类型比较的是值,在引用数据类型的比较时比较的是内存存放的地址
equals:默认比较的是内存存放的地址,但是可以对其进行重写
9.同步锁、死锁、乐观锁、悲观锁
同步锁:当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况的发生,我们要保证线程互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。java中使用synchronized关键字来取得一个对象的同步锁
死锁:多个线程同时被堵塞,他们中的一个或多个都在等待资源的释放
乐观锁:总是假设最好的情况,即每次去拿数据都认为数据没有发生变化,但是在更新的时候需要判断一下在此期间这个数据是否被修改。使用版本号机制和CAS算法来实现
悲观锁:总是假设最坏的情况,即每次去拿数据都认为数据发生了变化,因此每次去拿数据的时候都要加锁,这样当别人想要拿数据就会被堵塞。
10.Java异常
Throwable是所有Java程序中错误处理的父类,其有两个子类:Error、Exception
Error:这是由JVM检测到的无法预期的错误,是属于JVM层次的严重错误,因此无法捕获只能抛出异常,且无法采取任何恢复操作
Exception:
1)运行时异常:都是RuntimeException类及其子类异常。eg:NullPioneerException空指针异常、IndexOutOfBounds下标越界异常。这些异常都属于不检查异常,程序可以选择捕获处理也可以选择不处理,这是逻辑错误引起的,因此在逻辑角度应该尽可能避免这些错误。运行时Java编译器不会去检查它,即使没有使用try…catch捕获它或throws语句抛出它,程序也会编译通过。
2)编译异常:除RuntimeException之外的异常,属于Exception的子类,这类异常必须处理,如果不处理,则不会编译通过。eg:IOEException、SQLException以及自定义异常
11.什么是单例模式
即类的实例在多线程环境下只被创建一次
饿汉式单例模式:一开始初始化,线程安全
懒汉式单例模式:延迟初始化,线程不安全
双检锁单例模式:延迟初始化,线程安全
12.Java基本数据类型
整型:byte int short long
浮点型:float double
布尔型:boolean
字符型:char
13.BIO、NIO、AIO的区别
BIO:BlockIO,同步阻塞式IO,就是我们平常使用的传统IO,特点是模式简单,使用方便,并发处理能力低
NIO:NewIO,同步非阻塞IO,是传统IO的升级,客户端和服务器端通过channel(通道)通讯实现了多路复用
AIO:AsynchronousIO,是NIO的升级,也叫NIO2,实现了异步非阻塞IO,异步IO的操作基于事件和回调机制
14.泛型
泛型,即参数化类型,解决不确定对象具体类型的问题。Java编译器生成的字节码是不包含泛型信息的,泛型类型信息在编译过程会被擦除,这个过程就叫做泛型擦除。