1.面向过程和面向对象的区别
- 面向过程:是分析问题的解决步骤,然后用函数把这些步骤一步步实现,最后在使用的时候一一调用即可。性能较高,
- 面向对象:把构成问题的事务分解成各个对象,而建立这些对象目的也不是为了完成一个个步骤,而是为了描述某个事务在解决整个问题的过程中所发生的行为。面向对象有三大特点:封装、继承、多态。所以相对面向过程来说易维护、易复用、易扩展,但从 性能上来说,比面向对象的性能低。
- 封装:隐藏对象的属性和实现细节,只对外公开一些操作内部属性的方法。其目的就是为了增强安全性、简化编程,使用者不需要了解具体的实现细节,只需要通过外部接口的调用,就可以完成对类的内部成员的操作。
- 继承:把已存在类的定义作为基础创建类,新类继承旧类的所有属性和方法,可以增加新的属性和方法,也可以重写父类的方法,但不能选择性的继承父类,其目的是能够非常方便地服用以前的代码,大大提高开发效率。
- 多态:一个方法根据对象的不同会有不同的行为方式,常用在通过指向父类的指针来调用在不同子类中的方法,其中继承是多态的基础。重写和重载就是典型的多态的实现
- 重写:是父类与子类之间多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Override)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。
- 重载:重载(Overload)是一个类中多态性的一种表现。如果在一个类中定义了多个同名的方法,它们参数列表不同,则称为方法的重载(Overload)。
2.重载和重写的区别
- 重写必须继承;重载则不用。
- 重写的方法名、参数数目相同、参数类型兼容;重载的方法名相同,参数列表不同
- 重写的方法修饰符大于等于父类的方法;重载和修饰符无关
- 重写是子类的方法覆盖父类的的方法,要求方法名和参数都相同;重载是在同一个类中的两个或两个以上的方法,拥有相同的方法名,但是参数却不相同,方法体也不相同。
3.equals和==的区别
- ==:比较的变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指向同一个对象。比较的是真正意义上的指针操作。
- equals:用来比较的是两个对象的内容是否是同一个对象。即比较的是值是否相同。
4.String、StringBuffer和StringBuilder的区别
- String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。每次+操作相当于隐式在堆上new了一个跟源字符串相同的StringBuilder对象,再调用append方法拼接+后面的字符。
- StringBuffer和StringBuilder他们都继承了AbstractStringBuilder抽象类,从该抽象类中可以看出,他们的底层都是可变的字符数组,所以再进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。另外StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
5.ArrayList和LinkedList的区别
List有两个重要的实现类就是ArrayList和LinkedList
- ArrayList从底层可以看出是一个能够自动增长容量的数组,因为数组是基于索引的数据结构,它在使用索引在数组中搜索和读取数据很快,但是删除数据却是开销很大,因为这需要重排数组中的所有数据,即删除数据以后将后面的所有数据前移。
- LinkedList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,但在get和set方面弱于ArrayList,当然这些对比都是基于数据量很大或者操作很频繁的时候。
6.HashMap和HashTable的区别
- 两者父类不同:HashMap继承于AbstractMap类,而HashTable继承与Dictionary类。不过它们都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
- 对外提供的接口不同:HashTable比HashMap多提供了elements()和contains()两个方法。elements()方法继承Dictionary类,用于返回HashTable中value的枚举;contains()方法判断该HashTable是否包含传入的value。作用与containsValue()一致。事实上containsValue()就只是调用了contains()方法。
- 对null的支持不同:HashTable中key和value都不可为null。HashMap中key可以为null,但这样的key只能有一个,因为必须保证key的唯一性;而且可以有多个key值对应的value为null
- 安全性不同:HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁,所以需要开发人员自己处理多线程的安全问题。HashTable是线程安全的,它每个方法上都有synchronized关键字,可直接用于多线程中,所以在单线程场景下它的效率远低于HashMap,当需要多线程操作时可以使用线程安全的ConcurrentHashMap,因为ConcurrentHashMap由于使用了分段锁的结构,并不对整个数据进行锁定,所以效率比HashTable要高。
7.Collection和Collections的区别
- Collection:集合类的上级接口,子接口有Set、List、LinkedList、ArrayList、Vector、Stack等
- Collections:集合类的一个帮助类。包含各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。不可实例化,就像一个工具类,服务于Collection框架。
8.List,Set,Map三者的区别
- List:接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set:不允许重复的集合,不会有多个元素引用相同的对象
- Map:使用键值对存储,Map会维护与key有关联的值。两个key可以引用相同的对象,但key不可以重复。
9.抽象类和接口的区别
- 默认方法实现:抽象类可以有默认的方法实现,接口完全是抽象的,不存在方法的实现。
- 实现:子类使用extends关键字来继承抽象类,如果子类不是抽象类,它需要提供抽象类中所有声明的方法的实现;子类使用关键字implements来实现接口,他需要提供接口中的所有声明的方法的实现。
- 构造器:抽象类可以有构造器,接口不能有构造器
- 访问修饰符:抽象类可以有public、protected和default这些修饰符;接口只能使用public,不可以使用其他的修饰符。
- 添加新方法:抽象类中添加新的方法,你可以给他提供默认的实现,不需要改变现有的代码。接口中添加方法,那么必须改变实现该接口的类。
- 多继承:抽象类可以继承一个类和实现多个接口,接口只可以实现一个或多个接口。
10.HashMap底层原理
JDK1.7的底层原理
HashMap对象在实例化后,底层从创建一个长度为16的一维数组Entry[],当执行put(key1,value1)方法添加数据时,首先调用key1所在类的hashcode()方法计算key1的哈希值,此哈希值经过底层算法的计算之后可以得到对应Entry数组中的存放位置。
- 如果该位置上数据为空,此时key1-value1添加成功
- 如果此位置上数据不为空(意味着此位置上存在一个或多个数据(多个数据以链表的形式存在)),则比较key1和已存在的数据的哈希值,如果key1的哈希值与已存在的哈希值都不相同,此时key1-value1添加成功,即key1-value1和原来的数据会以链表的形式存储;如果key1的哈希值和某个已经存在的数据(如key2-value2)的哈希值相同,则调用key1所在类的equals()方法,返回key1和key2的比较结果,如果返回为false,此时key1-value1添加成功,同样也是和原来的数据以链表的形式存储;如果返回的时true,则会使用value1替换value2.
在不断添加数据的过程中,会涉及到数组扩容问题。当添加数据超出临界值(且数据要存放的位置非空时),需要对数组扩容。默认扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
JDK1.8及以后相较于JDK1.7底层实现方面的不同
- 使用空参构造器创造HashMap对象时,底层没有直接创建一个长度为16的数组,而是在首次调用put()方法时才会创建。
- JDK1.8底层存储数据的数组变为Node[].
- JDK1.7底层为数据+链表,JDK1.8底层数据结构为:数组+链表+红黑树
- 当存储数据形成链表时,链表的结构也不相同,JDK1.7的链表结构是新的数据指向旧的数据。JDK1.8的链表结构是:旧的数据指向新的数据
- 当底层数组的某个索引上的元素以链表形式存在的数据个数大于8,且当前数组的长度大于64时,此时这个索引位置上的数据改为红黑树存储