Java基础题目整理

面试题型大纲

面试题模块儿包含了十九个模块,分别为:java基础、容器、多线程、反射、对象拷贝、java Web模块、异常、网络、设计模式、spring/springMVC、spring Boot/spring Cloud、Hibernate、Mybatis、RabbitMQ、kafka、Zookeeper、Mysql、Redis、JVM

Java基础题目整理

Jdk与jre有什么区别?

JRE是java运行时的环境,包含了java虚拟机、java基础类库,是使用java语言编写的程序运行时所需要的软件环境,是提供给想要运行java程序的用户使用的。

JDK是java开发工具包,是程序员使用java开发java程序所需要的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的javac,还包含了很多Java程序调试和分析的工具:jconsole、jvisualvm等工具软件,还包含了编写java程序所需的文档和案例程序。

= = 和eauals的区别是什么?

对于基本数据类型来说:== 比较的是两者的值是否相等

对于引用数据类型来说(对象):

(1)“==”比较的是引用的地址是否相同(即是否是同一辆汽车(注意,只有一辆汽车));Object中的.equals()方法和”==’功能一样

(2)但是String类中的.equals()方法重写了,比较的是两个引用对象的内容是否相同(即是否是完全相同的汽车(注意,有两辆汽车,且一模一样,完全相同))。

==用于比较数据是否相等,如果用它在比较对象的话,它只能比较对象在栈的内容是否相同。而eqals函数用来比较对象的内容是否相等(堆里的内容)。

(栈用来保存对象的引用,对象的的内容放在堆里面,由栈的引用指向他,java里的数组也是如此。)

两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

不对!两个对象equals相等,则他们的hashCode()必须相等,反之不一定。

​ 详解:

​ hashCode是继承自Object的方法,首先看hashCode方法的定义:

1
public native int hashCode();

​ 可见hashCode方法是一个native方法【”A native method is a Java method whose implementation is provided by non-java code.”】,因为native方法是由非Java语言实现的,所以这个方法的定义中也没有具体的实现。根据jdk文档,该方法的实现一般是“通过将该对象的内部地址转换成一个整数来实现的”,这个返回值就作为该对象的哈希码值返回。

​ 由此可知默认情况下

​ 两个对象==相等,则其hashcode一定相等,反之不一定成立。

​ 两个对象equals相等,则其hashcode一定相等,反之不一定成立。【和上一条等价,因为Object的equals实现用的就是 对象的==相等来判断】

​ 如果equals方法和hashCode方法被重写,则需满足hashCode 的常规协定

​ 1.在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

​ 2.如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。

​ 3.如果根据 equals(java.lang.Object)) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能

​ 所以按规定重写的情况下

​ 两个对象equals相等,则它们的hashcode必须相等,反之则不一定。

​ 两个对象==相等,则它们的hashcode必须相等,反之则不一定。【==相等,则equals必然相等】

​ 所以总的来说,只要按照规定,则有:

​ 两个对象equals相等,则它们的hashcode必须相等,反之则不一定。

Hash散列值有冲突的情况,虽然概率很低。

https://www.cnblogs.com/jesonjason/p/5492208.html

final 在 java 中有什么作用?

final 修饰一个类时,这个类将不会被继承,同时该类的所有成员方法都会被隐式的指定为fianal方法

final修饰一个方法时,表示这个方法不会被重写(可以被重载成多个final方法)此处需要注意的一点是:因为重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时不再产生重写与final的矛盾,而是在子类中重新定义了新的方法。(注:类的private方法会隐式地被指定为final方法。)

final修饰成员变量时,标识这个变量只能被赋值一次,且不会被修改,如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。

java 中的 Math.round(-1.5) 等于多少?

-1

扩展JDK中的java.lang.Math类

round:返回四舍五入,负.5小数返回较大整数,如-1.5返回-1。

ceil:返回小数所在两整数间的较大值,如-1.5返回-1。

tail:返回小数所在两整数间的较小值,如-1.5返回-2。

详解

String 属于基础的数据类型吗?

不属于! String 是final修饰的java类,java的基本类型有8种:

​ 1:字符型: byte char

​ 2:基本类型:short,int,long

​ 3: 浮点类型:float,double

​ 4: 布尔类型: boolean

java 中操作字符串都有哪些类?它们之间有什么区别?

String、StringBuffer、StringBuilder

区别:String是不可变的对象,对每次对String类型的改变时都会生成一个新的对象,StringBuffer和StringBuilder是可以改变对象的。

对于操作效率:StringBuilder > StringBuffer > String

对于线程安全:StringBuffer 是线程安全,可用于多线程;StringBuilder 是非线程安全,用于单线程

不频繁的字符串操作使用 String。反之,StringBuffer 和 StringBuilder 都优于String

String str=”i”与 String str=new String(“i”)一样吗?

不一样!他们不是同一个对象。前者是一个常量,后者重新new了一个对象,内存空间不一样。

如何将字符串反转?

https://www.cnblogs.com/lanseyitai1224/p/6955609.html 七八种方法 见附录

String 类的常用方法都有那些?

eauals(),length(),concat(),charAt(),substring(),contains(),hashCode(),indexOf(),isEmpt() ……

抽象类必须要有抽象方法吗?

抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类

普通类和抽象类有哪些区别?

1.抽象类不能被实例化。

2.抽象类可以有构造函数,被继承时子类必须继承父类的一个构造方法,抽象方法不能被声明成静态方法。抽象类的构造函数用来初始化抽象类的一些字段,而这一切都在抽象类的派生类实例化之前发生。不仅如此,抽线类的构造函数还有一种巧妙应用:就是在其内部实现子类必须执行的代码。

3.抽象方法允许声明,而无需实现,抽象类可以允许普通方法有主体。

4.有抽象方法的类一定为抽象类,抽象类不一定只含有抽象方法。

5.抽象类的子类必须实现抽象类中的所有抽象方法,否则这个类也是抽象类。

抽象类能使用 final 修饰吗?

不可以,final关键字不可以用来修饰抽象类和接口

接口和抽象类有什么区别?

1.接口和抽象类都不能被实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

2.抽象类要被子类继承extends,接口要被类实现

3.接口只能做方法申明,抽象类可以做方法申明,也可以做方法实现

4.接口里定义的变量只能是公共的静态常量,抽象类定义的变量是普通变量 ..

5.抽象类里面的抽象方法必须被子类全部实现,如果子类不能实现父类全部抽象方法,则这个子类也是抽象类;一个类实现接口的时候,如果不能全部实现接口方法,则这个类也是抽象类

6.抽象方法只能声明,不能实现。抽象类是重构的结果,接口是设计的结果。

7.抽象类里面可以没有抽象方法

8.如果一个类里面有抽象方法,那么这个类是抽象类

9.抽象方法要被实现,所以不能是私有的,也不能是静态的

10.接口可以继承接口,并且可以多继承,而抽象类只能单根继承

java 中 IO 流分为几种?

https://www.cnblogs.com/QQ846300233/p/6046388.html

BIO、NIO、AIO 有什么区别?

https://blog.csdn.net/u010310183/article/details/81700405

Files的常用方法都有哪些?

https://www.cnblogs.com/cocoxu1992/p/10460994.html

附录

两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

equals():反映的是对象或变量具体的值,即两个对象里面包含的值–可能是对象的引用,也可能是值类型的值。

hashCode():计算出对象实例的哈希码,并返回哈希码,又称为散列函数。根类Object的hashCode()方法的计算依赖于对象实例的D(内存地址),故每个Object对象的hashCode都是唯一的;当然,当对象所对应的类重写了hashCode()方法时,结果就截然不同了。

之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,很多集合都用到了hashCode,比如HashTable。
两个obj,如果equals()相等,hashCode()一定相等。
两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。
所以:
可以考虑在集合中,判断两个对象是否相等的规则是:
第一步,如果hashCode()相等,则查看第二步,否则不相等;
第二步,查看equals()是否相等,如果相等,则两obj相等,否则还是不相等。

1、首先equals()和hashcode()这两个方法都是从object类中继承过来的。
equals()是对两个对象的地址值进行的比较(即比较引用是否相同)。
hashCode()是一个本地方法,它的实现是根据本地机器相关的。
2、Java语言对equals()的要求如下,这些要求是必须遵循的:
A 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
B 反射性:x.equals(x)必须返回是“true”。
C 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
D 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
3、equals()相等的两个对象,hashcode()一定相等;
反过来:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等。

为什么选择hashcode方法?

———————————————-

以java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。具体过程是这样:

\1. new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。

\2. 比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

可能经过上面理论的讲一下大家都迷糊了,我也看了之后也是似懂非懂的。下面我举个例子详细说明下。

list是可以重复的,set是不可以重复的。那么set存储数据的时候是怎样判断存进的数据是否已经存在。使用equals()方法呢,还是hashcode()方法。

假如用equals(),那么存储一个元素就要跟已存在的所有元素比较一遍,比如已存入100个元素,那么存101个元素的时候,就要调用equals方法100次。

但如果用hashcode()方法的话,他就利用了hash算法来存储数据的。

这样的话每存一个数据就调用一次hashcode()方法,得到一个hashcode值及存入的位置。如果该位置不存在数据那么就直接存入,否则调用一次equals()方法,不相同则存,相同不存。这样下来整个存储下来不需要调用几次equals方法,虽然多了几次hashcode方法,但相对于前面来讲效率高了不少。

为什么要重写equals方法?

——————————————-

因为Object的equal方法默认是两个对象的引用的比较,意思就是指向同一内存,地址则相等,否则不相等;如果你现在需要利用对象里面的值来判断是否相等,则重载equal方法。

说道这个地方我相信很多人会有疑问,相信大家都被String对象的equals()方法和”==”纠结过一段时间,当时我们知道String对象中equals方法是判断值的,而==是地址判断。

那照这么说equals怎么会是地址的比较呢?

那是因为实际上JDK中,String、Math等封装类都对Object中的equals()方法进行了重写。

我们先看看Object中equals方法的源码:

我们都知道所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说使用Object的equals()方法是比较两个对象的内存地址是否相等,即若object1.equals(object2)为true,则表示equals1和equals2实际上是引用同一个对象。虽然有时候Object的equals()方法可以满足我们一些基本的要求,但是我们必须要清楚我们很大部分时间都是进行两个对象的比较,这个时候Object的equals()方法就不可以了,所以才会有String这些类对equals方法的改写,依次类推Double、Integer、Math。。。。等等这些类都是重写了equals()方法的,从而进行的是内容的比较。希望大家不要搞混了。

改写equals时总是要改写hashcode

——————————————-

java.lnag.Object中对hashCode的约定:

  1. 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。
  2. 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。
  3. 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。

根据上一个问题,实际上我们已经能很简单的解释这一点了,比如改写String中的equals为基于内容上的比较而不是内存地址的话,那么虽然equals相等,但并不代表内存地址相等,由hashcode方法的定义可知内存地址不同,没改写的hashcode值也可能不同。所以违背了第二条约定。

又如new一个对象,再new一个内容相等的对象,调用equals方法返回的true,但他们的hashcode值不同,将两个对象存入HashSet中,会使得其中包含两个相等的对象,因为是先检索hashcode值,不等的情况下才会去比较equals方法的。

hashCode方法使用介绍

————————————————

Hash表数据结构常识:
一、哈希表基于数组。
二、缺点:基于数组的,数组创建后难以扩展。某些哈希表被基本填满时,性能下降得非常严重。
三、没有一种简便得方法可以以任何一种顺序遍历表中数据项。
四、如果不需要有序遍历数据,并且可以提前预测数据量的大小,那么哈希表在速度和易用性方面是无与伦比的。

一、为什么HashCode对于对象是如此的重要:
一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的Hash算法相比还不能叫真正的算法,它如何实现它,不仅仅是程序员的编程水平问题,
而是关系到你的对象在存取是性能的非常重要的关系.有可能,不同的HashCode可能会使你的对象存取产生,成百上千倍的性能差别.
先来看一下,在JAVA中两个重要的数据结构:HashMap和Hashtable,虽然它们有很大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性等有着特定的区别,但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来说明:
在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,Hashtable将有比数组性能更高的查询速度.具体原因看下面的内容.
Hashtable在存储数据时,一般先将该对象的HashCode和0x7FFFFFFF做与操作,因为一个对象的HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的长度取模,得到该对象在Hashtable中的索引.
index = (o.hashCode() & 0x7FFFFFFF)%hs.length;
这个对象就会直接放在Hashtable的每index位置,对于写入,这和数组一样,把一个对象放在其中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接从第index取得这个对象,而数组却要做循环比较.所以对于数据量稍大时,Hashtable的查询比数组具有更高的性能.
既然一个对象可以根据HashCode直接定位它在Hashtable中的位置,那么为什么Hashtable还要用key来做映射呢?这就是关系Hashtable性能问题的最重要的问题:Hash冲突.
常见的Hash冲突是不同对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突是,如果一组对象的个数大过了int范围,而HashCode的长度只能在int范围中,所以肯定要有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端的情况是极少见的,可以暂不考虑,但是对于同的HashCode经过取模,则会产中相同的索引,或者不同的对象却具有相同的HashCode,当然具有相同的索引.
所以对于索引相同的对象,在该index位置存放了多个值,这些值要想能正确区分,就要依靠key来识别.
事实上一个设计各好的HashTable,一般来说会比较平均地分布每个元素,因为Hashtable的长度总是比实际元素的个数按一定比例进行自增(装填因子一般为0.75)左右,这样大多数的索引位置只有一个对象,而很少的位置会有几个元素.所以Hashtable中的每个位置存放的是一个链表,对于只有一个对象是位置,链表只有一个首节点(Entry),Entry的next为null.然后有hashCode,key,value属性保存了该位置的对象的HashCode,key和value(对象本身),如果有相同索引的对象进来则会进入链表的下一个节点.如果同一个索引中有多个对象,根据HashCode和key可以在该链表中找到一个和查询的key相匹配的对象.
从上面我看可以看到,对于HashMap和Hashtable的存取性能有重大影响的首先是应该使该数据结构中的元素尽量大可能具有不同的HashCode,虽然这并不能保证不同的HashCode产生不同的index,但相同的HashCode一定产生相同的index,从而影响产生Hash冲突.
对于一个象,如果具有很多属性,把所有属性都参与散列,显然是一种笨拙的设计.因为对象的HashCode()方法几乎无所不在地被自动调用,如equals比较,如果太多的对象参与了散列.
那么需要的操作常数时间将会增加很大.所以,挑选哪些属性参与散列绝对是一个编程水平的问题.
从实现来说,一般的HashCode方法会这样:
return Attribute1.HashCode() Attribute1.HashCode()..[ super.HashCode()],我们知道,每次调用这个方法,都要重新对方法内的参与散列的对象重新计算一次它们的HashCode的运算,如果一个对象的属性没有改变,仍然要每次都进行计算,所以如果设置一个标记来缓存当前的散列码,只要当参与散列的对象改变时才重新计算,否则调用缓存的hashCode,这可以从很大程度上提高性能.
默认的实现是将对象内部地址转化为整数作为HashCode,这当然能保证每个对象具有不同的HasCode,因为不同的对象内部地址肯定不同(废话),但java语言并不能让程序员获取对象内部地址,所以,让每个对象产生不同的HashCode有着很多可研究的技术.
如果从多个属性中采样出能具有平均分布的hashCode的属性,这是一个性能和多样性相矛盾的地方,如果所有属性都参与散列,当然hashCode的多样性将大大提高,但牺牲了性能,而如果只能少量的属性采样散列,极端情况会产生大量的散列冲突,如对”人”的属性中,如果用性别而不是姓名或出生日期,那将只有两个或几个可选的hashcode值,将产生一半以上的散列冲突.所以如果可能的条件下,专门产生一个序列用来生成HashCode将是一个好的选择(当然产生序列的性能要比所有属性参与散列的性能高的情况下才行,否则还不如直接用所有属性散列).
如何对HashCode的性能和多样性求得一个平衡,可以参考相关算法设计的书,其实并不一定要求非常的优秀,只要能尽最大可能减少散列值的聚集.重要的是我们应该记得HashCode对于我们的程序性能有着重要的影响,在程序设计时应该时时加以注意.
请记住:如果你想有效的使用HashMap,你就必须重写在其的HashCode()。
还有两条重写HashCode()的原则:
不必对每个不同的对象都产生一个唯一的hashcode,只要你的HashCode方法使get()能够得到put()放进去的内容就可以了。即“不为一原则”。生成hashcode的算法尽量使hashcode的值分散一些, 不要很多hashcode都集中在一个范围内,这样有利于提高HashMap的性能。即“分散原则”。
掌握了这两条原则,你就能够用好HashMap编写自己的程序了。不知道大家注意没有, java.lang.Object中提供的三个方法:clone(),equals()和hashCode()虽然很典型,但在很多情况下都不能够适用,它们只是简单的由对象的地址得出结果。这就需要我们在自己的程序中重写它们,其实java类库中也重写了千千万万个这样的方法。利用面向对象的多态性——覆盖,Java的设计者很优雅的构建了Java的结构,也更加体现了Java是一门纯OOP语言的特性。
Java提供的Collection和Map的功能是十分强大的,它们能够使你的程序实现方式更为灵活,执行效率更高。

如何将字符串反转?

Java中经常会用到将字符串进行反转的时候,程序员孔乙己总结了7种反转方法,如下:

//方法1 递归方法

1
2
3
4
5
6
7
8
9
public static String reverse1(String s) {
int length = s.length();
if (length <= 1){
return s;
}
String left = s.substring(0, length / 2);
String right = s.substring(length / 2, length);
return reverse1(right) + reverse1(left);
}

//方法2 通过 charAt(int index)返回char值进行字符串拼接

1
2
3
4
5
6
7
public static String reverse2(String s) {
int length = s.length();
String reverse = "";
for (int i = 0; i < length; i++)
reverse = s.charAt(i) + reverse;
return reverse;
}

//方法3 把字符串转换成字符数组倒叙拼接然后返回值

1
2
3
4
5
6
7
public static String reverse3(String s) {
char[] array = s.toCharArray();
String reverse = "";
for (int i = array.length - 1; i >= 0; i--)
reverse += array[i];
return reverse;
}

//方法4 调用StringBuffer中的reverse方法

1
2
3
public static String reverse4(String s) {
return new StringBuffer(s).reverse().toString();
}

//方法5 把字符串转换成字符数组首位对调位置

1
2
3
4
5
6
7
8
9
10
11
public static String reverse5(String orig) {
char[] s = orig.toCharArray();
int n = s.length - 1;
int halfLength = n / 2;
for (int i = 0; i <= halfLength; i++) {
char temp = s[i];
s[i] = s[n - i];
s[n - i] = temp;
}
return new String(s);
}

//方法6

1
2
3
4
5
6
7
8
9
10
11
12
13
public static String reverse6(String s) {
char[] str = s.toCharArray();
int begin = 0;
int end = s.length() - 1;
while (begin < end) {
str[begin] = (char) (str[begin] ^ str[end]);
str[end] = (char) (str[begin] ^ str[end]);
str[begin] = (char) (str[end] ^ str[begin]);
begin++;
end--;
}
return new String(str);
}[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0);

//方法7

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Stack;
public class StringReverse {
public static String reverse7(String s) {
char[] str = s.toCharArray();
Stack<Character> stack = new Stack<Character>();
for (int i = 0; i < str.length; i++)
stack.push(str[i]);

String reversed = "";
for (int i = 0; i < str.length; i++)
reversed += stack.pop();
return reversed;
}
}
-------------本文结束感谢您的阅读-------------