Java面试考点之Java SE (Java基础)

一、JavaSE部分 (Java基础)

1、map的分类和常见的情况

1.1-HashMap:

最常用的Map,根据键的hashcode值来存储数据,根据键可以直接获得他的值(因为相同的键hashcode值相同,在地址为hashcode值的地方存储的就是值,所以根据键可以直接获得值),具有很快的访问速度,遍历时,取得数据的顺序完全是随机的,HashMap最多只允许一条记录的键为null,允许多条记录的值为null,HashMap不支持线程同步,即任意时刻可以有多个线程同时写HashMap,这样对导致数据不一致,如果需要同步,可以使用synchronziedMap的方法使得HashMap具有同步的能力或者使用线程安全的concurrentHashMap,ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

1.2-HashTable:

与HashMap类似,不同的是,它不允许记录的键或值为空,支持线程同步,即任意时刻只能有一个线程写HashTable,因此也导致HashTable在写入时比较慢!

1.3-LinkedHashMap:

是HahsMap的一个子类,但它保持了记录的插入顺序,遍历时先得到的肯定是先插入的,也可以在构造时带参数,按照应用次数排序,在遍历时会比HahsMap慢,不过有个例外,当HashMap的容量很大,实际数据少时,遍历起来会比LinkedHashMap慢(因为它是链啊),因为HashMap的遍历速度和它容量有关,LinkedHashMap遍历速度只与数据多少有关

1.4-TreeMap:

实现了sortMap接口,能够把保存的记录按照键排序(默认升序),也可以指定排序比较器,遍历时得到的数据是排过序的

什么情况用什么类型的Map:
在Map中插入,删除,定位元素:HashMap
要按照自定义顺序或自然顺序遍历:TreeMap
要求输入顺序和输出顺序相同:LinkedHashMap

2、解释为什么会出现4.0-3.6=0.40000001这种现象?

答:计算机在计算10进制小数的过程中要先转换为2进制进行计算,2进制的小数无法精确的表达10进制小数,这个过程中出现了误差。

3、 Lambda表达式的优缺点

优点:1. 简洁。2. 非常容易并行计算。3. 可能代表未来的编程趋势。
缺点:1. 若不用并行计算,很多时候计算速度没有比传统的 for 循环快。(并行计算有时需要预热才显示出效率优势)2. 不容易调试。3. 若其他程序员没有学过 lambda 表达式,代码不容易让其他语言的程序员看懂。

4 、Java支持的数据类型有哪些?什么是自动拆装箱?

基本数据类型:
整数值型:byte,short,int,long,
字符型:char
浮点类型:float,double
布尔型:Boolean
整数默认int型,小数默认是double型。Float和long类型的必须加后缀。
首先知道String是引用类型不是基本类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。引用类型包括类、接口、数组等。String类还是final修饰的。
而包装类就属于引用类型,自动装箱和拆箱就是基本类型和引用类型之间的转换,至于为什么要转换,因为基本类型转换为引用类型后,就可以new对象,从而调用包装类中封装好的方法进行基本类型之间的转换或者toString(当然用类名直接调用也可以,便于一眼看出该方法是静态的),还有就是如果集合中想存放基本类型,泛型的限定类型只能是对应的包装类型。

5、什么是值传递和引用传递?

public void add(int a) { int b = a; } 这个可以看作是值传递,a是基本数据类型,他把他的值传给了b public void add(Object obj) { Object objTest = obj; } 这个可以看作是址传递,obj是引用数据类型,是把他栈中指向堆中的对象的地址值赋值给了objTest. 这时候就同时有两个引用指向了堆中的某个Object对象 其实这样看来,java应该只有值传递的。如果是基本数据类型,传递的就是实际的值. 如果是引用数据类型,传递的就是该引用的地址值.

6、数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
6.1-存储内容比较:

Array 数组可以包含基本类型和对象类型,ArrayList 却只能包含对象类型
Array 数组在存放的时候一定是同种类型的元素。ArrayList 就不一定了 。

6.2-空间大小比较:

Array 数组的空间大小是固定的,所以需要事前确定合适的空间大小。
ArrayList 的空间是动态增长的,而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。

6.3-方法上的比较:

ArrayList 方法上比 Array 更多样化,比如添加全部 addAll()、删除全部 removeAll()、返回迭代器 iterator() 等。
适用场景:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择 ArrayList。
如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用 ArrayList 就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择 LinkedList。**相对于ArrayList,LinkedList插入是更快的。**因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。类似于插入数据,删除数据时,LinkedList也优于ArrayList。
但是LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。

7 Java中是如何支持正则表达式操作的?

正则表达式就是在进行字符串匹配和处理,Java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作

❤2、关键字

1、锁有了解吗?说一下Synchronized和lock

这道题应该是看你对于这俩者的区别和优缺点还有使用场景上的注意。
首先lock是接口synchronized是关键字,然后lock是显示锁(即加锁和解锁的过程可见并且需要我们自己控制)synchronized是隐式锁。然后synchronized可以用来修饰方法代码块。Lock的话需要它的一些实现类来做到加锁和解锁比如我们很常用的ReentrantLock或者分布式领域会用到的ReentrantReadWriteLock。用法上的话,一般我们synchronized的话不怎么需要关注他的锁释放,因为代码块执行完毕或者报错都会释放锁,而lock的话我们通常需要使用trycatchfinally这种形式在finally中去unlock释放锁。另外synchronized在读写锁方面没有Lock灵活,设想一下ABC三个线程,俩个读文件一个写文件,如果是synchronized的你只能依次来加锁解锁,而Lock可以让读共享,这样不是很好嘛。另外synchronized在1.6之前的话是重量级锁,性能远不如ReentrantLock,在1.6以后做了大幅的优化,引入了偏向锁,轻量级锁,自旋锁,自适应自旋,锁粗化,锁消除等机制,具体想看优化详细的话可以参考书籍周志明老师的深入理解java虚拟机的最后一章。
最后提一点,synchronized和ReentrantLock都属于可重入锁。

2、介绍一下Syncronized锁,如果用这个关键字修饰一个静态方法,锁住了什么?如果修饰成员方法,锁住了什么?

在synchronized里面,包含有三种常见的锁状态:
对于普通的同步方法:锁是当前的对象
对于静态函数的同步方法:锁是指引用当前类的class对象
对于同步方法块的内容:锁是指Synchonized括号里配置的对象

3、介绍一下volatile?

volatile作为java中的关键词之一,用以声明变量的值可能随时会被别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效(非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,线程B读取这个变量的值时可能读到的并不是是线程A更新后的值)。volatile会禁止指令重排,volatile具有可见性、有序性,不具备原子性。 注意,volatile不具备原子性,这是volatile与java中的synchronized、java.util.concurrent.locks.Lock最大的功能差异,这一点在面试中也是非常容易问到的点

❤3、面向对象

3.1-wait方法底层原理

object中的方法,可以暂停线程,期间会释放对象锁,不像sleep方法,线程休眠期依然持有锁,wait方法的线程,必须调用notify或notifyAll方法唤醒线程!

3.2-String为啥不可变?

string是final修饰,不可变,同时string底层是字符串数组也是final修饰,这样做首先是安全,比如hashset中用string做为键,不会出现string变化,导致违反唯一键。另外节约内存。

3.3-类和对象的区别

类是一个抽象的概念,它不存在于现实中的时间/空间里,类只是为所有的对象定义了抽象的属性与行为。就好像“Person(人)”这个类,它虽然可以包含很多个体,但它本身不存在于现实世界上。
对象是类的一个具体。它是一个实实在在存在的东西。
**类是一个静态的概念,类本身不携带任何数据。**当没有为类创建任何对象时,类本身不存在于内存空间中。
**对象是一个动态的概念。每一个对象都存在着有别于其它对象的属于自己的独特的属性和行为。**对象的属性可以随着它自己的行为而发生改变。

3.4-请列举你所知道的Object类的方法。

getClass():用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写;
equals():用于比较两个对象的地址是否相同,即两个引用是否指向同一个对象;
clone():用于创建并返回当前对象的一份拷贝;
toString():返回类的名字@实例的哈希码的16进制字符串;
notify():唤醒等待队列中的其中一个线程;
notifyAll():唤醒线程等待队列中的所有线程;
wait(long timeout):让一个线程等待一段时间。

3.5-”static”关键字是什么意思?Java中是否可以覆盖(override)一个private方法或者是static的方法?

static表示静态的意义,它可以修饰一个变量,一个方法,被其修饰的变量被称为类变量,被其修饰的方法成为类方法,其随着类的加载而被加载。
无法重写被private修饰的方法,因为被private修饰的父类方法在子类中是不可见的。
static修饰的方法是静态绑定的,而方法覆盖是为了实现多态,是动态绑定,所以static修饰的方法不需要被覆盖

6、StringBuffer和StringBuilder有什么区别,底层实现上呢? 
  6.1-StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
  6.2-只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。 
  6.3-在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全,而StringBuffer则每次都需要判断锁,效率相对更低。
  6.4-运行速度:StringBuilder>StringBuffer>String
  6.5-String每次操作都会生成新的对象,这样不仅效率底下,而且大量浪费有限的内存空间。String是不可变字符串,其他都是可变字符串。

7、静态变量存在哪?

堆区:
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

8、类加载机制,双亲委派模型,好处是什么?

类加载,JVM第一次使用到这个类时需要对,这个类的信息进行加载。一个类只会加载一次,之后这个类的信息放在堆空间,静态属性放在方法区。
JVM类加载器从上到下一共分为三类
1.启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
2. 扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
3. 应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
**双亲委派模型,好处是可以保证我们的类有一个合适的优先级,保证要加载的这个类在程序的各个类加载器环境中都是同一个类。**在虚拟机里觉得一个类是不是唯一有两个因素,第一个就是这个类本身,第二个就是加载这个类的类加载器,如果同一个类由不同的类加载器去加载,在虚拟机看来,这两个类是不同的类。

9、讲讲什么是泛型? 

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。避免了强制类型转换,还可以提高代码的可读性和安全性Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。

10、Comparable和Comparator接口是干什么的?列出它们的区别。

参考链接:https://blog.csdn.net/lsqingfeng/article/details/80342620
如果我们想要让一个List可以使用Collections.sort(list) 的方法进行排序,则必须要求集合中的元素类型,实现Comparable接口,也就是让他具备比较能力,这也是为什么Integer类型的数组可以排序,就是因为Integer已经实现了该接口,并且他是按照升序的规则实现的。
Comparable接口和Comparator接口的用法,要注意:
Comparable接口位于 java.lang包下Comparator接口位于java.util包下
Comparable: 内部比较器,一个类如果想要使用 Collections.sort(list) 方法进行排序,则需要实现该接口。
Comparator: 外部比较器用于对那些没有实现Comparable接口或者对已经实现的Comparable中的排序规则不满意进行排序.无需改变类的结构,更加灵活。(策略模式)

11、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

传递的是值,只不过这个值是引用的地址值,地址值指向对象,所以改变的对象的内容,地址并没有改变。

12、Java中,什么是构造函数?什么是构造函数重载?

当新对象被创建的时候,构造方法会被调用。每一个类都有构造方法。在程序员没有给类提供构造方法的情况下,Java编译器会为这个类创建一个默认的构造方法。
Java中构造方法重载和方法重载很相似。可以为一个类创建多个构造方法。每一个构造方法必须有它自己唯一的参数列表。

13、hashCode()和equals()方法有什么联系?

前提: 谈到hashCode就不得不说equals方法,二者均是Object类里的方法。由于Object类是所有类的基类,所以一切类里都可以重写这两个方法。
原则 1 : 如果 x.equals(y) 返回 “true”,那么 x 和 y 的 hashCode() 必须相等 ;
原则 2 : 如果 x.equals(y) 返回 “false”,那么 x 和 y 的 hashCode() 有可能相等,也有可能不等 ;
原则 3 : 如果 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 一定返回 “false” ;
原则 4 : 一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用
原则 5 : 当一个对象类型作为集合对象的元素时,那么这个对象应该拥有自己的equals()和hashCode()设计,而且要遵守前面所说的几个原则。

❤4、集合

1、	hashMap和ConcurrentHashMap的区别?

参考链接:https://www.jianshu.com/p/a7767e6ff2a2
简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。
其实可以看出JDK1.8版本的ConcurrentHashMap的数据结构已经接近HashMap,相对而言,ConcurrentHashMap只是增加了同步的操作来控制并发。

2、	hashMap内部具体如何实现的?

HashMap是对数据结构中哈希表(Hash Table)的实现,Hash表又叫散列表。Hash表是根据关键码Key来访问其对应的值Value的数据结构,它通过一个映射函数把关键码映射到表中一个位置来访问该位置的值,从而加快查找的速度。这个映射函数叫做Hash函数,存放记录的数组叫做Hash表。

3、	如果hashMap的key是一个自定义的类,怎么办?

如果hashMap的key是一个自定义的类,必须重写该类的hashcode()方法和equals()方法,HashMap中,如果要比较key是否相等,要同时使用这两个函数!
HashMap中的比较key是这样的,先求出key的hashcode(),比较其值是否相等,若相等再比较equals(),若相等则认为他们是相等的。若equals()不相等则认为他们不相等。如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。HashMap用来判断key是否相等的方法,其实是调用了HashSet判断加入元素是否相等。

4、	ArrayList和LinkedList的区别,如果一直在list的尾部添加元素,用哪个效率高?

区别:
4.1 ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
4.2 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
4.3 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
因为LinkedList每次增加的时候,会new一个Node对象来存新增加的元素,所以当数据量小的时候,这个时间并不明显,而ArrayList需要扩容,所以LinkedList的效率就会比较高,其中如果ArrayList出现不需要扩容的时候,那么ArrayList的效率应该是比LinkedList高的,当数据量很大的时候,new对象的时间大于扩容的时间,那么就会出现ArrayList的效率比LinkedList高了

5、HashMap底层,负载因子,为啥是2^n?

HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,负载因子默认是0.75, 2^n是为了让散列更加均匀。

5、	什么是迭代器?

迭代器通常被称为“轻量级”对象,因为创建它的代价小。
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

6、	Iterator和ListIterator的区别是什么?

Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator有hasPrevious()和previous()方法既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引。

7、	Java中Collection和Collections的区别

1、java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
2、Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序(Sort)、搜索以及线程安全等各种操作。

以上题目的答案均为参考,如有错误欢迎大家纠正。
Java面试考点之Java SE (Java高级知识)将在下期再见

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值