这是个人的练习面试题题册
面试练习
文章目录
什么是面向对象?
面向过程是注重具体怎么做,而面向对象会规定这个人该怎么做,该有什么样的行为。
面向对象有以下几个特性:封装,继承,多态。
封装:是将一个类封装在一起,外部看不见内部,外部无需修改或关心内部的实现,有自己的赋值调用方式。
继承:子类可以继承父类的方法,对方法进行重写。
多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同。
需要满足,子类继承父类,重写父类的方法
父类名 变量名=new 子类名;
变量名.方法;
JDK ,JRE,JVM的区别和联系?
JDK是Java开发者工具
JRE是Java运行环境
JVM是Java虚拟机
三者联系:
JDK包括JRE,JRE包括JVM
==与equals的区别
==:对于基本数据类型的比较是对值的比较,对于引用类型的比较是对地址的比较
equals:如果不对equals重写,那么equals和==一样,而我们平常使用的是String类中的equals,它已经对equals进行了重写,是对内容的比较。所以我们使用equals时要对其进行重写。
简述final的作用和为什么局部内部类和匿名内部类只能访问final变量,
final修饰类:该类不可以不继承
final修饰方法时;该方法不能被重写,但可以被重载
final修饰变量:当变量一旦被赋值后就不可以被修改了
为什么局部内部类和匿名内部类只能访问final变量:
因为当使用局部内部类或者匿名内部类时会生成class文件,class文件不会随着方法的结束使用而被销毁。这时就有一个问题,内部类在引用局部变量会随着方法的结束而被销毁,这是Java为了解决这个问题就是在使用内部类的时候复制一份局部变量作为内部类的成员变量。为了保证复制成功,那么复制的成员变量就必须和局部变量一致,如果局部变量改变那么就会出现问题,为了保证一致就使用final修饰。
String,StringBuffer,StringBuilder的区别和使用场景
String是final修饰的,不可变,每次操作都会产生新的String对象
StringBuffer和StringBuilder都在原对象上进行操作的。
StringBuilder与StringBuffer相比,StringBuffer是线程安全的因为他里面的方法被synchronized修饰。但性能没有StringBuilder好
性能相比:
StingBuilder >StringBuffer>String
应用场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
重载和重写的区别:
重载是方法名相同,参数不相同,如无参构造和有参构造。
重写是方法名必须相同,参数也必须相同。
接口和抽象类的区别
抽象类可以存在普通成员函数,而接口只能存在方法。
抽象类中的成员变量是各种类型的,而接口的成员变量只能是public static final类型的
抽象只能继承一个,接口多可以实现多个。
抽象类的设计目的是代码复用,把两个共有的通用的部分抽象出来,差异化的交给子类实现。比如宝马和奔驰他们共有的都是车,就可以把车抽象出来。宝马是车,奔驰也是车。
接口是对实现类的行为约束,告诉你可以干什么,但不能约束实现类不能干什么。比如鸟和飞机共有的就是都可以飞,那么就可以把飞当作一个接口,鸟可以飞,飞机也可以飞。但他们都可以不用飞。
使用场景:当你只关心事物的本质的时候,用抽象类;当你关注一个操作时,用接口。
list和set的区别
list:有序,按对象进入的顺序保存对象,可重复,允许多个null元素对象,可以使用Iterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标元素。
Set:无序,不可重复,最多允许有一个null元素对象,取元素时只能用Iterator接口取所有元素,然后逐一遍历。
hashcode与equals
什么时hashcode:hashcode叫做哈希码,这个码的作用是确定方法在哈希表中的索引位置。hashcode()在每个Java类中都存在。散列表存储的是键值对(key-value),它特点是能够根据键快速的检索出对应的值。就利用到了哈希码。
为什么要有hashCode
以"HashSet如何检查重复"为例子来说明为什么要有hashcode:
对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,看该位置是否有值
如果没有、Hashset会假设对象没有重复出现。但是如果发现有值,这时会调用equals ()方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。
如果没有、Hashset会假设对象没有重复出现。但是如果发现有值,这时会调用equals ()方法来检查两个对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样就大大减少了equals的次数,相应就大大提高了执行速度。
- 如果两个对象相等,则hashcode—定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
- 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
ArrayList和LinkedList的区别
ArrayList: 基于动态数组,连续内存存储,适合下标访问(随机访问),扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList(需要创建大量的node对象)
LinkedList:基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,不适合查询:需要逐一遍历遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过geti)取得某一元素时都需要对list重新进行遍历,性能消耗极大。
另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexlOf对list进行了遍历,当结果为空时会遍历整个列表。
HashMap和HashTable有什么区别?其底层实现是什么?
区别:
HaspMap与HashTable相比,HashTable是线程安全的,他的每个方法被synchronized修饰。
HashMap允许key和value为null,而HashTable不允许。
⒉.底层实现:数组+链表实现
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在
-
计算key的hash值,二次hash然后对数组长度取模,对应到数组下标,
-
如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组,
-
如果产生hash冲突,先进行equal此比较,相同则取代该元素,不同,则判断链表高度插入链表,链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表
-
key为null,存在下标0的位置
数组扩容
当旧的数组不能存储时,会根据底层的扩容因子适当的生成一个新的数组,把原来的数组放进去,然后在加入新的数据。
ConcurrentHashmap原理?及在jdk7与jkd8的区别
jdk7:
数据结构:ReentrantLock+Segment+HashEntry,一个Segment(分段)中包含一个HashEntry数组,每个HashEntry又是一个链表结构
元素查询:二次hash,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部
锁: Segment分段锁Segment继承了ReentrantLock,锁定操作的Segment,其他的Segment不受影响,并发度为segment个数,可以通过构造函数指定,数组扩容不会影响其他的segment
get方法无需加锁,通过volatile保证
jdk8: |
数据结构: synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性查找,替换,赋值操作都使用CAS(乐观锁)
锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发扩容
读操作无锁:
Node的val和next使用volatile修饰,读写线程对该变量互相可见数组用volatile修饰,保证扩容时被读线程感知