知识搬运---Java面试集合及答案

1. JavaPHP的区别

两者均是网站开发的编程语言。

从语言特征上看:PHP为脚本语言,解释型语言,弱类型,专为Web开发打造。Java为C语言系编程语言,编译型,强类型,有跨平台的特征。从语法简洁性来说,PHP比Java简洁,毕竟PHP诞生比Java晚,同样的逻辑在PHP中表达起来会简洁于Java,这一特性导致同样熟练度的PHPer与Javaer相比PHPer编程速度要快于Javaer。在这里我要强调的是同一熟练度,这个概念很重要,在没有前提假定的情况下比较两种编程语言的熟练度是没有意义的,比如一个新Phper在开发时对PHP语言不熟练,开发过程中需要频繁的查阅相关文档,那么这个新PHPer开发速度肯定比不上***avaer,因为新PHPer在查阅文档的时间内,***avaer可以完全依靠熟练度填平语法上的效率差异甚至超过新PHPer。

从执行速度上看:语言特征决定了执行速度,在执行速度上,网上也有很多对比,总结而言Java优于PHP,但事实上比较两者的执行速度意义不大,因为Web应用的效率瓶颈是在对数据库的相关操作上,需要强调的是PHP与Java同样是作为Web服务器开发语言,功能都是操作数据库服务器以及***页呈现而已,如果网站多数以静态页面的形式呈现给用户,那两者比较毫无意义。

从部署方式上看:PHP典型的部署方式是LAMP,即Linux系统+Apache服务器 +MySQL数据库+PHP语言,而JavaEE的部署方式有Linux系统+Tomcat服务器+Mysql数据库+Java语言,不同之处只在于服务器与语言的选择,Tomcat与Apache都是开源免费的服务器,从部署上来讲两种语言都有可行的解决方案。但在这里重点讲的是虚拟机主机,国内的虚拟主机大多不支持Java,即便支持Java也贵于PHP,而且Java应用大多使用Tomcat服务器,而java虚拟主机是共享Tomcat进程,如果部署到该服务器的其它应用影响到了Tomcat进程会导致所有应用都无法使用Tomcat进程,因而主机服务商维护Java服务器成本高于PHP服务器,所以这也导致了PHP在Web端独大的现状。当然不用虚拟主机则完全没有这样的顾虑,如使用云服务器,云服务器可以自行配置相关环境,而自由是把双刃剑,安全但是价格高。在这里PHPer与Javaer的不同是在编程环境上,有人说PHP可以热部署,不用像Javaer编写Web应用时每次改代码都需要开关服务器,部署应用,再调试,事实上Javaer也可以热部署,在Intellij中只需要一个按钮。

从团队配合上看:在提倡前后端分离的互联网环境下,模板开发方式导致的前后端高耦合越来越遭人摒弃,如今,Web应用后端只需要提供相应的数据接口供前端调用即可,典型思想如RestFul。所以在团队前后端配合角度,PHP与Java并无太大的区别。若Web应用考虑SEO搜索引擎优化仍采用模板开发的模式,则PHP语言对前端的友好程度则高于Java。

从系统架构上看:在系统架构这方面,Java毫无疑问完胜,Java拥有多种架构选择,常见的SSH框架即Struts+Spring+Hibernate、SSM即Spring MVC+Spring+Mybatis、SM即Spring boot+Mybatis都可以作为web应用的框架选型,这些框架能够很好的将后端模块进行分离,根据具体需求的不同选取不同的MVC层、业务层、持久层框架进行开发。有人说PHP也可以做到,说PHP有很多的框架也可以做到分层架构,ThinkPHP不就是国人开发的PHP MVC框架么?PHP有框架这点毋庸置疑,但是PHP从语言特性上来讲并不适合做框架,框架和面向对象编程思想有很强的联系,而PHP语言特性上对面向对象并不友好,这也就导致了PHP在框架这条路上其天花板与Java相去甚远。在这里有些PHPer毫无疑问会义愤填膺,但事实如此,PHP的语言特性偏向与面向过程,其旨在开发特定的业务逻辑,这也是PHP适合Web开发的原因。又有人会说,PHP5.0之后也支持面向对象,但事实上PHP也是不得已而为之。关于面向对象和面向过程的区别此处不表,但总而言之所以对于系统架构而言,PHP和Java都有各自的应用场景,在没有前提假定的情况下去比较两者,难分优劣。

从上面的对比来看,php和Java可以说是各有千秋,php更加适合于快速开发,中小型应用系统,开发成本低、易学。Java更适合于开发大型的应用系统,应用的前景比较广阔,系统易维护、可复用性较好。

2. Java中如何支持正则表达式

Java中的String类提供了支持正则表达式操作的方法 包括:matches() replaceAll() replaceFirst() split() 此外 Java中可以用Pattern类表示正则表达式对象 它提供了丰富的API进行各种正则表达式操作。

3. 简单描述正则表达式及其用途

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。计算机处理的信息更多的时候不是数值而是字符串,正则表达式就是在进行字符串匹配和处理的时候最为强大的工具,绝大多数语言都提供了对正则表达式的支持。

4. 比较JavaJavaScript

看到比较经典的回答有:Java 和 Javascript 的关系就像雷锋和雷峰塔的关系,也像老婆和老婆饼的关系。一般认为,当时 Netscape 之所以将 LiveScript 命名为 JavaScript,是因为 Java 是当时最流行的编程语言,带有 "Java" 的名字有助于这门新生语言的传播。它们的相同之处包括:它们的语法和 C 语言都很相似;它们都是面向对象的(虽然实现的方式略有不同);JavaScript 在设计时参照了 Java 的命名规则;它们的不同之处包括:JavaScript 是动态类型语言,而 Java 是静态类型语言;JavaScript 是弱类型的,Java 属于强类型;JavaScript 的面向对象是基于原型的(prototype-based)实现的,Java 是基于类(class-based)的;JavaScript 除了长得和 Java 比较像之外,语言风格相去甚远。JavaScript 在设计时所参考的对象不包括 Java,而包括了像 Self 和 Scheme 这样的语言。

解释一下强弱类型语言:强类型语言是一种强制类型定义的语言,即一旦某一个变量被定义类型,如果不经强制转换,那么它永远就死该数据类型。而弱类型语言是一种弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过现行强制转换。强类型语言包括:Java、.net、Python、C++等语言。其中Python是动态语言,是强类型定义语言,是类型安全的语言,Java是静态语言,是强类型定义语言,也是;类型安全的语言;弱类型语言包括:VB,PHP,JavaScript等语言。其中VBScript是动态语言,是一种类型不安全的原因。

解释一下动静态类型语言:动态类型语言:动态性语言是指在运行期间才去做数据类型检查的语言,也就是说动态类型语言编程时,永远不用给任何变量指定数据类型,该语言会在第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。

静态类型语言:静态类型语言与动态类则刚好相反,它的数据类型在编译期间检查,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他静态语言还有C#、Java等。

对于动态语言与静态语言的区分,其根本在于判断是在运行期间去做数据类型还是在编译期间检查。

5. Java中如何跳出当前的多重嵌套循环

方法一:要跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体代码中使用带标号的break语句,即可跳出外层循环。

方法二:让外层的循环条件表达式的结果可以受到里层循环体代码的控制

6. 讲讲&&&的区别

&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式。

&还可以用作按位与的运算符,两个表达式的值按二进制位展开,对应的位(bit)按值进行“与”运算,结果保留在该位上。

外加比较|与||的区别:|和||的区别同理,都可以作为逻辑或运算符;|还可以作为按位或的运算符,运算规则与按位与同理;||同样有类似短路的功能,即第一个条件若为true,则不计算后面的表达式。

7. intInteger有什么区别

int是 Java 的 8 个原始数据类型(Primitive Types,boolean(1/8字节)、byte(1字节) 、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、char(2字节,一个字符能存储一个中文汉字))之一。Java 语言虽然号称一切都是对象,但原始数据类型是例外。

Integer 是 int 对应的包装类,它有一个 int 类型的字段存储数据,并且提供了基本操作,比如数***算、int 和字符串之间转换等。

int的默认值为0,而Integer的默认值为null,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。因为int默认值为0,在JSP开发中,用el表达式在文本框中显示结果为0,所以int不适合作为web层的表单数据的类型。

JDK1.5引入了自动装箱与自动拆箱功能,Java可根据上下文,实现int/Integer,double/Double,boolean/Boolean等基本类型与相应对象之间的自动转换,为开发过程带来极大便利。

最常用的是通过new方法构建Integer对象。但是,基于大部分数据操作都是集中在有限的、较小的数值范围,在JDK1.5 中新增了静态工厂方法 valueOf,将int值为-128 到 127 之间的Integer对象进行缓存,在调用时候直接从缓存中获取,进而节省内存和提升性能,也就是说使用该方法后,如果两个对象的int值相同且落在缓存值范围内,那么这个两个对象就是同一个对象;当值较小且频繁使用时,推荐优先使用整型池方法(时间与空间性能俱佳)。

8. web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串。

public String translate(String str){//对传入的str字符串进行转换

String tempStr = "";

try{

tempStr = new String(str.getBytes("ISO-8859-1"), "GBK");//把"ISO-8859-1"转化为“GBK”编码

tempStr = tempStr.trim();

}catch (Exception e){

System.err.println(e.getMessage());

}

return tempStr;

}

9. 说明StringStringBuffer的区别

String 字符串常量;StringBuffer 字符串变量(线程安全);StringBuilder 字符串变量(非线程安全)。

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer ,特别是字符串对象经常改变的情况下。而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的。

在大部分情况下StringBuilder >StringBuffer > String

10. 说明String是最基本的数据类型吗

String不是基本数据类型,而是一个类(class),是Java编程语言中的字符串。String对象是char的有序集合,并且该值是不可变的。因为java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类。

11. 谈谈大O符号并给出不同数据结构的例子

大O符号表示当数据结构的元素增加的时候,算法规模或者性能在最坏场景下有多好。

大O符号也可以用来描述其他行为,比如说内存消耗。因为集合实际上就是一种数据结构,我们一般用大O符号基于时间、性能、内存消耗来选择最好的实现。

大O符号可以对大量数据的性能给出一个很好的说明。

12. 请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候用Array而不是ArrayList

Array:它是数组,申明数组的时候就要初始化并确定长度,长度不可变,而且它只能存储同一类型的数据,比如申明为String类型的数组,那么它只能存储String类型数据

ArrayList:它是一个集合,需要先申明,然后再添加数据,长度是根据内容的多少而改变的,ArrayList可以存放不同类型的数据,在存储基本类型数据的时候要使用基本数据类型的包装类。

当能确定长度并且数据类型一致的时候就可以用数组,其他时候使用ArrayList。

13. 解释什么是值传递和引用传递

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

有个博客解释的很好:

https://blog.csdn.net/w372426096/article/details/82216742

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

8种基本数据类型:byte  8位,short 16位,char  16位,boolean,int 32位,long 64位,float 32位,double 64位;引用类型,包括类、接口、数组,需要注意的是,String不是基本数据类型,而是引用类型,引用类型声明的变量,是指该变量在内存中实际上存储的是个引用地址,创建的对象实际是在堆中。

自动拆装箱,是指基本数据类型和引用数据类型之间的自动转换。如Integer 和 int 可以自动转换; Float和float可以自动转换。

自动拆装箱详解:https://www.cnblogs.com/dolphin0520/p/3780005.html

15. 请解释为什么会出现4.0-3.6=0.40000001这种现象?

这种舍入误差的主要原因是:

浮点数值采用二进制系统表示, 而在二进制系统中无法精确地表示分数 1/10。这就好像十进制无法精确地表示分数 1/3—样。如果在数值计算中不允许有任何舍入误差, 就应该使用 BigDecimal类。浮点数值不适用于无法接受舍入误差的金融计算中。

详解地址:https://blog.csdn.net/qq_40164190/article/details/105338777

16. 请你讲讲一个十进制的数在内存中是怎么存的?

进制补码形式存储,最高位是符号位,正数的补码是它的原码,负数的补码是它的反码加1,在求反码时符号位不变,符号位为1,其他位取反。

整形数据在内存中是以2进制本数的补码存在的;但正整数的补码还是原来的2进制,而负数则要先取反再+1才是它的补码。

正数的原反补一样;

负数的反码是将原码除了符号位的其余位取反,补码是给反码加1.

例:

原码: 1 0110100

反码: 1 1001011

补码: 1 1001100

17. 请你讲讲Lamba表达式的优缺点?

优点:

1. 代码简洁,开发迅速

2. 方便函数式编程

3. 非常容易进行并行计算

4. java引入lambda,改善了集合操作(引入Stream API)

缺点:

1. 代码可读性变差

2 .使得语言学习曲线陡峭,学习难度提升

3. 性能方面,在非并行计算中,很多计算未必有传统的for性能要高

4. 不容易进行调试

18. 你知道java8的新特新吗,请简单介绍一下?

1.Lambda表达式

2.新的日期API

3.引入Optional

4.使用Base64

5.接口的默认方法和静态方法

6.新增方法引用格式

7.新增Stream类

8.注解相关的改变

9.支持并行(parallel)数组

10.对并发类(Concurrency)的扩展。

详解博客:https://blog.csdn.net/ZytheMoon/article/details/89715618?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

19. 请你说明符号“==”比较的是什么?

可扩展到对于Java 中的 ==, equals 与 hashCode 的区别与联系。对于八种基本数据类型的变量,变量直接存储的是“值”。因此,在使用关系操作符 == 来进行比较时,比较的就是“值”本身。在Java中,引用类型的变量存储的并不是“值”本身,而是与其关联的对象在内存中的地址。

若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的值是否相等

若操作数的类型是引用数据类型,则该关系操作符判断的是左右两边操作数的内存地址是否相同。也就是说,若此时返回true,则该操作符作用的一定是同一个对象。

Java 中的 ==, equals与 hashCode 的区别与联系:https://blog.csdn.net/justloveyou_/article/details/52464440

20. 请你解释Object如果不重写hashCode()的话,hashCode()是如何计算出来的?

用于生成哈希值的算法就是哈希算法,所有散列函数都有如下一个基本特性:如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。这个特性是散列函数具有确定性的结果。但另一方面,散列函数的输入和输出不是一一对应的,如果两个散列值相同,两个输入值很可能是相同的,但不绝对肯定二者一定相等(可能出现哈希碰撞)。输入一些数据计算出散列值,然后部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。

个人觉得可以用从数据结构的角度,谈散列表也就是哈希表,两个关键:哈希函数、处理冲突的方法。

也找了个博客地址详解:https://blog.csdn.net/shiyong1949/article/details/85242237?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1

21. 请你解释为什么重写equals还要重写hashcode

hashCode 方法用于散列集合的查找,equals 方法用于判断两个对象是否相等。如果只重写了 equals 方法,两个对象 equals 返回了true,但是如果没有重写 hashCode 方法,集合还是会插入元素。这样集合中就出现了重复元素了。

总结:往HashMap添加元素的时候,需要先定位到在数组的位置(hashCode方法);如果只重写了 equals 方法,两个对象 equals 返回了true,集合是不允许出现重复元素的,只能插入一个;此时如果没有重写 hashCode 方法,那么就无法定位到同一个位置,集合还是会插入元素。这样集合中就出现了重复元素了。那么重写的equals方法就没有意义了。

博客地址:https://blog.csdn.net/xyh269/article/details/69171545

22. 请你介绍一下map的分类和常见的情况?

java为数据结构中的映射定义了一个接口java.util.Map;它有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap.

Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。

Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。

Hashtable与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。

LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.

HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为NULL,允许多条记录的值为NULL。

23. 请你讲讲Java里面final关键字是怎么使用的?

在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量);

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法;

使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了;

对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象,但是它指向的对象的内容是可变的。。

当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。

24. 请你讲讲Synchronizedlock

并发编程中,锁是经常需要用到的,Java中的锁机制:synchronized和lock。

lock不是java语言内置的,synchronized是java语言的关键字,因此是内置特性。lock是一个类,通过这个类可以实现同步访问;

lock和synchronized有一点非常大的不同,采用synchronized不需要用户手动的去释放锁,当synchronized方法或者代码块执行完毕之后,系统会自动的让线程释放对锁的占有,而lock则必须要用户去手动释放锁,如果没有主动的释放锁,就会可能导致出现死锁的现象;

lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不讷讷狗狗响应中断;

通过lock可以知道有没有成功获取锁,而synchronized却无法办到;

lock可以挺高多个线程进行读操作的效率。

25. 请你介绍一下volatile

Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized、volatile、final、concurren包等。

volatile通常被比喻成"轻量级的synchronized",也是Java并发编程中比较重要的一个关键字。和 synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。volatile通常被比喻成"轻量级的synchronized",也是Java并发编程中比较重要的一个关键字。和 synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块等。

volatile与可见性;volatile与有序性;volatile与原子性。

详解:https://juejin.im/entry/5b70e0a96fb9a009c409987b

26. 请你介绍一下Synchronized锁,如果使用这个关键字修饰一个静态方法,锁住了什么,如果修饰成员方法,锁住了什么?

线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。

synchronized关键字最主要有以下3种应用方式:

修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

27. 如果对一个类不重写,它的equals方法是如何比较的?

超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等

public boolean equals(Object obj) {

return (this == obj);

}

所有的对象都拥有标识(内存地址)和状态(数据),同时“==”比较两个对象的的内存地址,所以说使用Object的equals()方法是比较两个对象的内存地址是否相等,即若object1.equals(object2)为true,则表示equals1和equals2实际上是引用同一个对象。

28. 请解释hashCode()equals方法有什么联系?

总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。于是,Java采用了哈希表的原理。哈希(Hash)实际上是个人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,初学者可以简单理解,hashCode方法实际上返回的就是对象存储的物理地址(实际可能并不是)。
这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

简而言之,在集合查找时,hashcode能大大降低对象比较次数,提高查找效率!

Java对象的eqauls方法和hashCode方法是这样规定的:

1、相等(相同)的对象必须具有相等的哈希码(或者散列码)。

2、如果两个对象的hashCode相同,它们并不一定相同。

详解:https://www.cnblogs.com/qian123/p/5703507.html

29. 请解释Java中的概念,什么是构造函数,什么是构造函数重载,什么是复制构造函数?

java中的构造方法是一种特殊类型的方法,用于初始化对象。Java构造函数在对象创建时被调用。 它构造值,即提供对象的数据,这是为什么它被称为构造函数;

构造方法重载是Java中的一种技术,一个类可以有任何数量的参数列表不同的构造函数。编译器通过构造函数参数列表中的参数数量及其类型来区分这些构造函数。

在Java中没有复制构造函数。但是可以将一个对象的值复制到另一个中,就像C++中的复制构造函数。在java中有很多方法可以将一个对象的值复制到另一个对象中。它们分别是:通过构造函数;通过将一个对象的值分配给另一个对象;通过Object类的clone()方法。

30. 请说明Java中的方法覆盖和方法重载是什么意思?

子类重写父类的方法(重写要保证:函数名,返回值类型,参数类型,参数数量都相同),实现多态(编译时的多态)

同一个类中有多个相同函数名但是参数类型与参数数量均不同的方法,程序运行时根据传入的参数数量与参数类型调用不同的方法(运行时的多态)

方法覆盖和方法重载都是Java中实现多态的技术手段。

31. 请说明Query接口中的list方法和iterate方法有什么区别?

①list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的前提下使用查询缓存;iterate()方法可以充分利用缓存,如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销。

②list()方法不会引起N+1查询问题,而iterate()方法可能引起N+1查询问题。

对于Query接口的list()方法与iterate()方法来说,都可以实现获取查询的对象,但是list()方法返回的每个对象都是完整的(对象中的每个属性都被表中的字段填充上了),而iterator()方法所返回的对象中仅包含了主键值(标识符),只有当你对iterator中的对象进行操作时,Hibernate才会向数据库再次发送SQL语句来获取该对象的属性值。

32. 请你谈一下面向对象的“六原则一法则”

单一职责原则、开闭原则、依赖倒转原则、里氏替换原则、接口隔离原则、合成聚合复用原则和迪米特法则。

详解:https://blog.csdn.net/sinat_26342009/article/details/46419873

33. 请说明如何通过反射获取和设置对象私有字段的值

可以通过类对象的getDeclaredField()方法获取字段(Field)对象,然后再通过字段对象的setAccessible(true)将其设置为可以访问,接下来就可以通过get/set方法来获取/设置字段的值了。

34. 请说明重载和重写的区别。重载的方法能否根据返回类型进行区分?

方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。重载的方法一般根据参数进行区分,不能根据返回类型进行区分。

35. 请判断,两个对象相同(x.equals(y)==true),但是却有不同的hashcode,该说法是否正确,为什么?

不对,有相同的 hash code。这是java语言的定义:1) 对象相等则hashCode一定相等;2) hashCode相等对象未必相等

只要了解哈希表的原理就能理解了,同一个散列函数,你输入的值一样,函数的输出值也就一样了,所以两个对象相等,hashcode是一定一样的。

36. 请说明内部类可以引用其他包含类的成员吗,如果可以,有没有什么限制?

完全可以。如果不是静态内部类,那没有什么限制!

37. 请说明Java中如何进行异常处理,关键字:throwsthrowtrycatchfinally分别代表什么意义?在try块中可以抛出异常吗?

Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有

异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常

处理是通过5个关键词来实现的:try、catch、throw、throws和finally。

一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try用来指定一块预防所有“异常”的程序;catch子句紧跟在 try 块后面,用来指定你想要捕捉的“异常”的类型;throw语句用来明确地抛出一个“异常”;throws用来标明一个成员函数可能抛出的各种“异常”;Finally为确保一段代码不管发生什么“异常”都被执行一段代码;可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个 try 语句保护其他代码。每当遇到一个try语句,“异常”的框架就放到堆栈上面,直到所有的 try 语句都完成。如果下一级的try语句没有对某种“异常”进行处理,堆栈就会展开,直到遇到有处理这种“异常”的try语句。

在try中可以抛出异常。

38. 请说明Java中的接口和C++的虚类的相同和不同处?

C++虚类相当于java中的抽象类,与接口的不同之处是:

1.一个子类只能继承一个抽象类(虚类),但能实现多个接口

2.一个抽象类可以有构造方法,接口没有构造方法

3.一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有声明

4.一个抽象类可以是public、private、protected、default,接口只有public

5.一个抽象类中的方法可以是public、private、protected、default,接口中的方法只能是public和default

相同之处是:都不能实例化。

补充说明:接口是一类特殊的抽象类,是更抽象的抽象类,你可能这样理解。抽象类是一个不完整的类,接口只是定义了一些功能。打个比方,用抽象类和接口分别描述,抽象类就是在一般类之前加abstrict说:猪能用四肢跑,猪和能这样或者那样...”,接口的话就只能说:猪会跑,用什么跑就是子类来讲了

39. 请判断当一个对象被当做参数传递给一个方法后,此方法可以改变这个对象的属性,并且可以返回变化后的结果,那么这里到底是值传递还是引用传递?

是值传递。Java编程语言中只有由值传递参数的。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。

40. 请你说说Static Nested ClassInner Class的不同

内部类就是在一个类的内部定义的类,内部类中不能定义静态成员,内部类可以直接访问外部类中的成员变量,内部类可以定义在外部类的方法外面,也可以定义在外部类的方法体中。

在方法体外面定义的内部类的访问类型可以是public,protecte,默认的,private等4种类型,这就好像类中定义的成员变量有4种访问类型一样,它们决定这个内部类的定义对其他类是否可见;在方法体外面定义的内部类的访问类型可以是public,protecte,默认的,private等4种类型,这就好像类中定义的成员变量有4种访问类型一样,它们决定这个内部类的定义对其他类是否可见。

在方法内部定义的内部类前面不能有访问类型修饰符,就好像方法中定义的局部变量一样,但这种内部类的前面可以使用final或abstract修饰符。

在方法外部定义的内部类前面可以加上static关键字,从而成为Static Nested Class,它不再具有内部类的特性,所有,从狭义上讲,它不是内部类。由于static Nested Class不依赖于外部类的实例对象,所以,static Nested Class能访问外部类的非static成员变量(不能直接访问,需要创建外部类实例才能访问非静态变量)。

详解:https://zhuanlan.zhihu.com/p/26245610

41. 请你讲讲abstract  classinterface有什么区别?

1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。

4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。


42. 请说明重载和重写的区别,重载的方法是否可以改变返回值的类型

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。被重载的方法可以改变返回类型,但是无法以返回值类型作为重载函数的区分标准。

43. 请说明一下final\finally\finalize的区别

final 用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.

finally 是异常处理语句结构的一部分,表示总是执行.

finalize 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. JVM不保证此方法总被调用.

44. 请说明面向对象的特征有哪些方面

1.抽象:

抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。

2.继承:

继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。

3.封装:

封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。

4. 多态性:

多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。

45. 请说明ComparableComparator接口的作用以及它们的区别

java中,对集合对象或者数组对象排序,有两种实现方式。即:(1)对象实现Comparable 接口;(2)定义比较器,实现Comparator接口。

Comparable是在集合内部定义的方法实现的排序,位于java.lang下。Comparable 接口仅仅只包括一个函数compareTo()。

Comparator是在集合外部实现的排序,位于java.util下。Comparator接口包含了两个函数compare()和equals()。

Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。

Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

comparable相当于内部比较器。comparator相当于外部比较器。

46. 接口和抽象类的区别是什么

1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。

2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。

3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。

4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

47. 请说明Java是否支持多继承

不支持

48. 请你谈谈如何通过反射创建对象

1.通过Class字节码对象newInstance();(默认通过无参构造创建)

2.通过获取构造器getConstructor(Class<?>..parameterTypes);(通过有参的构造器,参数可以指定具体类型和多个数量)

49. 请你说明是否可以再static环境中访问非static变量?

不可以,因为static变量是属于类的,在类加载的时候就被初始化了,这时候非静态变量并没有加载,故非静态变量不能访问。

50. 请解释一下extendssuper泛型限定符

extends上限通配符,用来限制类型的上限,只能传入本类和子类,add方法受阻,可以从一个数据类型里获取数据;

super下限通配符,用来限制类型的下限,只能传入本类和父类,get方法受阻,可以把对象写入一个数据结构里;

详解:https://blog.csdn.net/qq_40395278/article/details/88603655

51. 请你讲讲什么是泛型

对泛型的印象就是一下尖括号中的东西:

ArrayList<String> strList = new ArrayList<String>();

ArrayList<Integer> intList = new ArrayList<Integer>();

ArrayList<Double> doubleList = new ArrayList<Double>();

泛型的引入就是保证类型的安全。

详解:https://blog.csdn.net/harvic880925/article/details/49872903?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

52. 请说明静态变量存在什么位置

内存到底分几个区?

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由os回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。

3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。

4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放。

5、程序代码区—存放函数体的二进制代码。

53. 请你解释类加载机制,双亲委派模型,好处是什么

这个概念有点懵,直接看博客:https://blog.csdn.net/u014138443/article/details/90581981

54. 请你谈谈StringBufferStringBuilder有什么区别,底层实现上呢?

StringBuffer线程安全,StringBuilder线程不安全,底层实现上的话,StringBuffer其实就是比StringBuilder多了Synchronized修饰符。

55. 请说明String是否能被继承

不能,char数组用final修饰的。

56. 请说明static关键字是什么意思,Java中是否可以覆盖一个private或者是static的方法

“static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。

57. 请列举你所知道的Object类的方法并且做简要说明

Object()默认构造方法。clone() 创建并返回此对象的一个副本。equals(Object obj) 指示某个其他对象是否与此对象相等。finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。getClass()返回一个对象的运行时类。hashCode()返回该对象的哈希码值。 notify()唤醒在此对象监视器上等待的单个线程。 notifyAll()唤醒在此对象监视器上等待的所有线程。toString()返回该对象的字符串表示。wait()导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。wait(long timeout)导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。wait(long timeout, int nanos) 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

58. 请说明类和对象的区别

类是对某一类事物的描述,是抽象的;而对象是一个实实在在的个体,是类的一个实例

59. 请解释一下String为什么不可变

不可变对象是指一个对象的状态在对象被创建之后就不再变化。不可改变的意思就是说:不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。

String 不可变是因为在 JDK 中 String 类被声明为一个 final 类,且类内部的 value 字节数组也是 final 的,只有当字符串是不可变时字符串池才有可能实现,字符串池的实现可以在运行时节约很多 heap 空间,因为不同的字符串变量都指向池中的同一个字符串;如果字符串是可变的则会引起很严重的安全问题,譬如数据库的用户名密码都是以字符串的形式传入来获得数据库的连接,或者在 socket 编程中主机名和端口都是以字符串的形式传入,因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子改变字符串指向的对象的值造成安全漏洞;因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享,这样便不用因为线程安全问题而使用同步,字符串自己便是线程安全的;因为字符串是不可变的所以在它创建的时候 hashcode 就被缓存了,不变性也保证了 hash 码的唯一性,不需要重新计算,这就使得字符串很适合作为 Map 的键,字符串的处理速度要快过其它的键对象,这就是 HashMap 中的键往往都使用字符串的原因。

60. 请讲讲Java有哪些特性,并举一个和多态有关的例子

封装、继承、多态。多态:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

61. 请讲讲wait方法的底层原理

ObjectSynchronizer::wait方法通过object的对象中找到ObjectMonitor对象调用方法 void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS)

通过ObjectMonitor::AddWaiter调用把新建立的ObjectWaiter对象放入到 _WaitSet 的队列的末尾中然后在ObjectMonitor::exit释放锁,接着 thread_ParkEvent->park 也就是wait。

62. 请说明ListMapSet三个接口存取元素的时候,各有什么特点

List特点:元素有放入顺序,元素可重复

Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)

Map特点:元素按键值对存储,无放入顺序(键不能重复,值可以重复)

63. 阐述ArrayListVectorLinkedList的存储性能和特性

ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存 操作,所以索引数据快而插入数据慢,Vector由于使用了 synchronized 方法(线程安全), 通常性能上较ArrayList差,而 LinkedList 使用双向链表实现存储,按序号索引数据需要进 行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。LinkedList 也是线程不安全的。

64. 请判断ListSetMap是否继承自Collection接口

List,Set是,Map不是。

65. 请你讲讲你所知道的常用集合类以及主要的方法

太多了,直接看博客: https://blog.csdn.net/znoone/article/details/79031649

66. 请你说明CollectionCollections的区别

java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。

、java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

67. 请说明ArrayListLinkedList的区别

1. ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;

2.对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;

3.对于添加和删除操作add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。

这道题个人觉得就是根据数据结构里面线性表的顺序存储和链式存储的区别来回答。

68. 请你说明HashMapHashtable的区别

第一、继承不同

第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。

第二、线程安全不一样

Hashtable 中的方法是同步的,而HashMap中的方法在默认情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。

第三、允不允许null值

从上面的put()方法源码可以看到,Hashtable中,key和value都不允许出现null值,否则会抛出NullPointerException异常。

而在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

第四、遍历方式的内部实现上不同

Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

第五、哈希值的使用不同

HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

第六、内部实现方式的数组的初始大小和扩容的方式不一样

HashTable中的hash数组初始大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

69. 请你说说快速失败fail-fast和安全失败fail-safe的区别

详解:https://blog.csdn.net/qq_31780525/article/details/77431970

70. 请你说说IteratorListIterator的区别

一.相同点

都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。

二.不同点

1.使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。

2.ListIterator有add方法,可以向List中添加对象,而Iterator不能。

3.ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。

4.ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

5.都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。

71. 请简单说明一下什么是迭代器

迭代器(Iterator)模式,又叫做游标模式,它的含义是,提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

72. 请解释一下为什么集合类没有实现CloneableSerializable接口

克隆(cloning)或者序列化(serialization)的语义和含义是跟具体的实现相关的。因此应该由集合类的具体实现类来决定如何被克隆或者序列化。

73. 请说明Java集合列框架的基本接口有哪些

详解:https://blog.csdn.net/znoone/article/details/79031649

74. 请你说明一下ConcurrentHashMap的原理

HashMap线程不安全,而Hashtable是线程安全,但是它使用了synchronized进行方法同步,插入、读取数据都使用了synchronized,当插入数据的时候不能进行读取(相当于把整个Hashtable都锁住了,全表锁),当多线程并发的情况下,都要竞争同一把锁,导致效率极其低下。而在JDK1.5后为了改进Hashtable的痛点,ConcurrentHashMap应运而生。

在JDK1.5中,ConcurrentHashMap使用的是分段锁技术,将ConcurrentHashMap将锁一段一段的存储,然后给每一段数据配一把锁(segment),当一个线程占用一把锁(segment)访问其中一段数据的时候,其他段的数据也能被其它的线程访问,默认分配16个segment。默认比Hashtable效率提高16倍。

在JDK1.8中,ConcurrentHashMap取消了segment分段锁,而采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。

75. 请解释一下TreeMap

TreeMap是一个有序的key-value集合,基于红黑树的NavigableMap实现。该映射根据键的自然顺序进行排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。

TreeMap的特性:

①根节点是黑色。

②每个节点只能是红色或者黑色。

③每个叶子节点是黑色。

④如果一个节点是红色的,则它两个节点都是黑色的,也就是在一条路径上不能出现两个红色的节点。

⑤从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

上面这五个特性就是红黑树的特性,附上红黑树的详解:https://www.jianshu.com/p/e136ec79235c

76. 请说明ArrayList是否会越界

会越界,终归到底ArrayList底层实现是数组,并且可以根据索引位置来取值,如果索引位置小于0或者大于size-1,这个时候肯定是会发生越界异常的,看了网上还有就是ArrayList是线程不安全的,同时多个线程对ArrayList进行操作的时候,也会出现这个问题,关于ArrayList线程不安全的详解:https://blog.csdn.net/u012859681/article/details/78206494

77. 请你说明concurrenthashmap有什么优势以及1.71.8的区别

ConcurrentHashMap是线程安全的,在jdk1.7是采用Segment+HashEntry的方式实现的,lock加在segment上, 1.7的size计算是先采用不加锁的方式,连续计算元素的个数,最多计算3次:

(1)如果前后两次计算结果相同,则说明计算出来的元素个数是准确的。

(2)如果前后两次计算结果不同,则给每个Segment进行加锁,再计算一次元素的个数。

1.8中放弃了Segment臃肿的设计,取而代之的是采用Node+CAS+synchronized来保证并发安全进行实现,1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入或删除数据时,会通过addCount()方法更新baseCount,通过累加baseCount和CounterCell数组中的数量,即可得到元素的总个数。

78. 请你说明一下TreeMap的底层实现

直接看第75题

79. 请你说明ConcurrentHashMap锁加在了哪些地方

Hashtable低效主要是因为所有访问Hashtable的线程都争夺一把锁。如果容器有很多把锁,每一把锁控制容器中的一部分数据,那么当多个线程访问容器里的不同部分的数据时,线程之前就不会存在锁的竞争,这样就可以有效的提高并发的访问效率。这也正是ConcurrentHashMap使用的分段锁技术。将ConcurrentHashMap容器的数据分段存储,每一段数据分配一个Segment(锁),当线程占用其中一个Segment时,其他线程可正常访问其他段数据。

80. 请你解释HashMap的容量为什么是2n次幂

HashMap计算添加元素的位置时,使用的位运算,这是特别高效的运算;另外,HashMap的初始容量是2的n次幂,扩容也是2倍的形式进行扩容,是因为容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞,避免形成链表的结构,使得查询效率降低!最好看一下实现的源码。

81. 请你简单介绍一下ArrayListLinkedList的区别,并说明如果一下在list的尾部添加元素,用哪种方式效率更高

ArrayList和LinkedList的区别:1. ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;2.对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;3.对于添加和删除操作add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。这一部分个人觉得就是根据数据结构里面线性表的顺序存储和链式存储的区别来回答。

LinkedList每次增加的时候,会new 一个Node对象来存新增加的元素,所以当数据量小的时候,这个时间并不明显,而ArrayList需要扩容,所以LinkedList的效率就会比较高,其中如果ArrayList出现不需要扩容的时候,那么ArrayList的效率应该是比LinkedList高的,当数据量很大的时候,new对象的时间大于扩容的时间,那么就会出现ArrayList'的效率比Linkedlist高了。

82. 如果hashMapkey是一个自定义的类,怎么办?

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

83. 请你解释一下hashMap具体是如何实现的

还是要先了解一下数据结构中的哈希表,然后配合看看Java中的源码

详解:https://blog.csdn.net/tuke_tuke/article/details/51588156

84. 请你说明一下MapConcurrentHashMap的区别

翻看Java文档会发现ConcurrentHashMap实现了Map接口,ConcurrentHashMap对比其他实现Map接口的类很明显的区别就是ConcurrentHashMap是线程安全的。

详解(HashMap、ConcurrentHashMap、HashTable的区别):https://www.jianshu.com/p/c00308c32de4

85. 如何保证线程安全

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读/写完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

方法一:最简单的方式是加入synchronized关键字,只要将操作共享数据的语句加入synchronized关键字,在某一时段只会让一个线程执行完,在执行过程中,其他线程不能进来执行。

方法二:使用锁Lock。

区别:

a.Lock使用起来比较灵活,但需要手动释放和开启;采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;

b.Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;

c.在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时Lock是个不错的方案。

d.使用Lock的时候,等待/通知 是使用的Condition对象的await()/signal()/signalAll()  ,而使用synchronized的时候,则是对象的wait()/notify()/notifyAll();由此可以看出,使用Lock的时候,粒度更细了,一个Lock可以对应多个Condition。

e.虽然Lock缺少了synchronized隐式获取释放锁的便捷性,但是却拥有了锁获取与是释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized所不具备的同步特性。

86. 请你简要说明一下线程的基本状态以及状态之间的关系

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。

线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

3. 阻塞(BLOCKED):表示线程阻塞于锁。

4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

6. 终止(TERMINATED):表示该线程已经执行完毕。


详解:https://blog.csdn.net/pange1991/article/details/53860651

87. 请你解释一下什么是线程池

线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。

Java中有三个比较常用的线程池,分别是FixedThreadPool,SingleThreadExecutor,CachedThreadPool。

1) FixedThreadPool

这是一个线程数固定的线程池,当这个线程池被创建的时候,池里的线程数就已经固定了。当需要运行的线程数量大体上变化不大时,适合使用这种线程池。固定数量还有一个好处,它可以一次性支付高昂的创建线程的开销,之后再使用的时候就不再需要这种开销。

2) SingleThreadExecutor

这是一个线程数量为1的线程池,所有提交的这个线程池的任务都会按照提交的先后顺序排队执行。单个线程执行有个好处:由于任务之间没有并发执行,因此提交到线程池种的任务之间不会相互干扰。程序执行的结果更具有确定性。

3) CachedThreadPool

一看到Cache就知道这是一个和缓存有关的线程池,每次有任务提交到线程池的时候,如果池中没有空闲的线程,线程池就会为这个任务创建一个线程,如果有空闲的线程,就会使用已有的空闲线程执行任务。有的人可能会有个疑惑:这样线程不就越来越多了吗?其实不是的,这个线程池还有一个销毁机制,如果一个线程60秒之内没有被使用过,这个线程就会被销毁,这样就节省了很多资源。CachedThreadPool是一个比较通用的线程池,它在多数情况下都能表现出优良的性能。以后编码的时候,遇事不决,用缓存(线程池)。

详解:https://zhuanlan.zhihu.com/p/36551354

88. 举例说明同步和异步

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

89. 请介绍一下线程同步和线程调度的相关方法

wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;

notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;

notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

90. 请问当一个线程进入一个对象的Synchronized方法A之后,其他线程是否可以进入此对象的Synchronized方法B

当一个线程进入一个对象多个一个synchronize()方法后,其他线程是否可以进入该对象的其他方法取决于方法本身,如果该方法是非synchronized()方法,那么是可以访问的。

当一个线程进入一个对象多个一个synchronize()方法后,其他线程是否可以进入该对象的其他方法取决于方法本身,如果该方法是synchronized()方法,那么是不可以访问的。

如果其他方法是静态方法,它用的同步锁是当前类的字节码,与非静态的方法不能同步(因为非静态的方法用的是this),因此,静态方法可以被调用

91. 请简述一下线程的sleep()方法和yield()方法有什么区别

1.sleep:Thread类的方法,必须带一个时间参数。会让当前线程休眠进入阻塞状态并释放CPU(阿里面试题 Sleep释放CPU,wait 也会释放cpu,因为cpu资源太宝贵了,只有在线程running的时候,才会获取cpu片段),提供其他线程运行的机会且不考虑优先级,但如果有同步锁则sleep不会释放锁即其他线程无法获得同步锁可通过调用interrupt()方法来唤醒休眠线程。

2.yield:让出CPU调度,Thread类的方法,类似sleep只是不能由用户指定暂停多长时间 ,并且yield()方法只能让同优先级的线程有执行的机会。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。调用yield方法只是一个建议,告诉线程调度器我的工作已经做的差不多了,可以让别的相同优先级的线程使用CPU了,没有任何机制保证采纳。

详解(sleep、yield、wait、join的区别(阿里)): https://www.cnblogs.com/aspirant/p/8876670.html

92. 请回答以下几个问题:第一个问题:Java中有几种方法可以实现一个线程,第二个问题:用什么关键字修饰同步方法,第三个问题:stop()suspend()方法为何不推荐使用,请说明一下原因

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576

用Synchronized关键字修饰同步方法。

stop方法天生就不安全,因为它在终止一个线程时会强制中断线程的执行,不管run方法是否执行完了,并且还会释放这个线程所持有的所有的锁对象。这一现象会被其它因为请求锁而阻塞的线程看到,使他们继续向下执行。这就会造成数据的不一致,我们还是拿银行转账作为例子,我们还是从A账户向B账户转账500元,我们之前讨论过,这一过程分为三步,第一步是从A账户中减去500元,假如到这时线程就被stop了,那么这个线程就会释放它所取得锁,然后其他的线程继续执行,这样A账户就莫名其妙的少了500元而B账户也没有收到钱。这就是stop方法的不安全性。

suspend被弃用的原因是因为它会造成死锁。suspend方法和stop方法不一样,它不会破换对象和强制释放锁,相反它会一直保持对锁的占有,一直到其他的线程调用resume方法,它才能继续向下执行。假如有A,B两个线程,A线程在获得某个锁之后被suspend阻塞,这时A不能继续执行,线程B在获得相同的锁之后才能调用resume方法将A唤醒,但是此时的锁被A占有,B不能继续执行,也就不能及时的唤醒A,此时A,B两个线程都不能继续向下执行而形成了死锁。这就是suspend被弃用的原因。

93. 请分别说明一下多线程和同步有几种实现方法,并且这些实现方法具体内容都是什么

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576

线程同步的实现:

(1)同步代码块
synchronized(object){  代码段 }

(2)同步函数
public synchronized void sale(){
}

wait() : 使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep() : 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

94. 请说出你所知道的线程同步的方法

(1)同步代码块
synchronized(object){  代码段 }

(2)同步函数
public synchronized void sale(){
}

wait() : 使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep() : 使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

95. 启动一个线程是用run()还是start()

启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM 调度并执行,这并不意味着线程就会立即运行。

run()方法是线程启动后要进行回调(callback)的方法。

96. 请使用内部类实现线程设计4个线程,其中两个线程每次对j增加1,另外两个线程每次对j减少1。代码自己写吧。

97. 请说明一下线程中的同步和异步有何异同,并且请举例说明在什么情况下会使用到同步和异步

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就是非阻塞式操作。

98. 请说明一下sleep()wait()有什么区别

sleep与Wait的区别:sleep是线程方法,wait是object方法;看区别,主要是看CPU的运行机制:

它们的区别主要考虑两点:1.cpu是否继续执行、2.锁是否释放掉。

对于这两点,首先解释下cpu是否继续执行的含义:cpu为每个线程划分时间片去执行,每个时间片时间都很短,cpu不停地切换不同的线程,以看似他们好像同时执行的效果。

其次解释下锁是否释放的含义:锁如果被占用,那么这个执行代码片段是同步执行的,如果锁释放掉,就允许其它的线程继续执行此代码块了。

明白了以上两点的含义,开始分析sleep和wait:

sleep ,释放cpu资源,不释放锁资源,如果线程进入sleep的话,释放cpu资源,如果外层包有Synchronize,那么此锁并没有释放掉。

wait,释放cpu资源,也释放锁资源,一般用于锁机制中 肯定是要释放掉锁的,因为notify并不会立即调起此线程,因此cpu是不会为其分配时间片的,也就是说wait 线程进入等待池,cpu不分时间片给它,锁释放掉。

详解(sleep、yield、wait、join的区别(阿里)): https://www.cnblogs.com/aspirant/p/8876670.html

***>请你说明一下在监视器Monitor内部,是如何做到线程同步的,在程序又应该做何种级别的同步呢

监视器和锁在Java虚拟机中是一起使用的。监视器监视同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用相关联。线程在获取锁之前不允许执行同步代码。

100. 请分析一下同步方法和同步代码块的区别是什么

同步方法默认用this或者当前类class对象作为锁;

同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;

同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰;

1.同步方法:

即有synchronized (同步,美 ['sɪŋkrənaɪzd] ) 修饰符修饰的方法。

由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用给方法前,要获取内置锁,否则处于阻塞状态。

例:public synchronized getMoney(){}

注:synchronized修饰静态方法,如果调用该静态方法,将锁住整个类。

2.同步代码块

即有synchronized修饰符修饰的语句块,被该关键词修饰的语句块,将加上内置锁。实现同步。

例:synchronized(Object o ){}

同步是高开销的操作,因此尽量减少同步的内容。通常没有必要同步整个方法,同步部分代码块即可。

同步方法默认用this或者当前类class对象作为锁。

101. 请详细描述一下线程从创建到死亡的几种状态都有哪些

新建状态:

使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序start()这个线程。

就绪状态:

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

运行状态:

如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:

如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

Ø  等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

Ø  同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

Ø  其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

死亡状态:

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

102. 创建线程有几种不同的方式,你喜欢哪一种,为什么

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576

103. 请解释一下Java多线程回调是什么意思

所谓的回调,就是程序员A写了一段程序(程序a),其中预留有回调函数接口,并封装好了该程序。程序员B要让a调用自己的程序b中的一个方法,于是,他通过a中的接口回调自己b中的方法。

这个概念还是有点懵。

104. 请列举一下启动线程有哪几种方式,之后再说明一下线程池的种类都有哪些

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576

Java中有三个比较常用的线程池,分别是FixedThreadPool,SingleThreadExecutor,CachedThreadPool。

1) FixedThreadPool

这是一个线程数固定的线程池,当这个线程池被创建的时候,池里的线程数就已经固定了。当需要运行的线程数量大体上变化不大时,适合使用这种线程池。固定数量还有一个好处,它可以一次性支付高昂的创建线程的开销,之后再使用的时候就不再需要这种开销。

2) SingleThreadExecutor

这是一个线程数量为1的线程池,所有提交的这个线程池的任务都会按照提交的先后顺序排队执行。单个线程执行有个好处:由于任务之间没有并发执行,因此提交到线程池种的任务之间不会相互干扰。程序执行的结果更具有确定性。

3) CachedThreadPool

一看到Cache就知道这是一个和缓存有关的线程池,每次有任务提交到线程池的时候,如果池中没有空闲的线程,线程池就会为这个任务创建一个线程,如果有空闲的线程,就会使用已有的空闲线程执行任务。有的人可能会有个疑惑:这样线程不就越来越多了吗?其实不是的,这个线程池还有一个销毁机制,如果一个线程60秒之内没有被使用过,这个线程就会被销毁,这样就节省了很多资源。CachedThreadPool是一个比较通用的线程池,它在多数情况下都能表现出优良的性能。以后编码的时候,遇事不决,用缓存(线程池)。

详解:https://zhuanlan.zhihu.com/p/36551354

105. 请简要说明一下Java中的cyclicbarriercountdownlatch区别分别是什么

CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。CyclicBarrier: N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。

CountDownLatch是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的.

而CyclicBarrier更像一个水闸, 线程执行就想水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流.

CountDownLatch是两组线程,第一组负责计数器减一,第二组是阻塞线程,当第一组线程将计数器减到0时,第二组线程才开始执行,放行是由第三方控制;CyclicBarrier是只有一组线程,只有当所有线程到达拦截点的时候,才会继续往下执行,放行是由一组线程本身控制。

CountDownLatch放行条件是大于或等于线程数;CyclicBarrier放行条件是等于线程数

CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重 置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数 器,并让线程重新执行一次。

CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier 阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。

106. 请说明一下线程池有什么优势

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

107. 请回答一下Java中有几种线程池,并且详细描述一下线程池的实现过程

Java中有三个比较常用的线程池,分别是FixedThreadPool,SingleThreadExecutor,CachedThreadPool。

1) FixedThreadPool

这是一个线程数固定的线程池,当这个线程池被创建的时候,池里的线程数就已经固定了。当需要运行的线程数量大体上变化不大时,适合使用这种线程池。固定数量还有一个好处,它可以一次性支付高昂的创建线程的开销,之后再使用的时候就不再需要这种开销。

2) SingleThreadExecutor

这是一个线程数量为1的线程池,所有提交的这个线程池的任务都会按照提交的先后顺序排队执行。单个线程执行有个好处:由于任务之间没有并发执行,因此提交到线程池种的任务之间不会相互干扰。程序执行的结果更具有确定性。

3) CachedThreadPool

一看到Cache就知道这是一个和缓存有关的线程池,每次有任务提交到线程池的时候,如果池中没有空闲的线程,线程池就会为这个任务创建一个线程,如果有空闲的线程,就会使用已有的空闲线程执行任务。有的人可能会有个疑惑:这样线程不就越来越多了吗?其实不是的,这个线程池还有一个销毁机制,如果一个线程60秒之内没有被使用过,这个线程就会被销毁,这样就节省了很多资源。CachedThreadPool是一个比较通用的线程池,它在多数情况下都能表现出优良的性能。以后编码的时候,遇事不决,用缓存(线程池)。

详解:https://zhuanlan.zhihu.com/p/36551354

108. 请说明一下Java中都有哪些方式可以启动一个线程

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576

109. 请列举一下创建线程的方法,并简要说明一下在这些方法中哪个方法更好,原因是什么?

Java中有三种方法可以实现一个线程:通过实现 Runnable 接口;通过继承 Thread 类本身;通过Callable和Future创建线程。

详解(这里有六种实现线程的方法):https://blog.csdn.net/king_kgh/article/details/78213576



#Java工程师112道面试常考题##Java#
全部评论
爱你
4 回复 分享
发布于 2020-06-22 12:51
爱你
点赞 回复 分享
发布于 2020-06-22 12:48
收藏没停过
点赞 回复 分享
发布于 2020-06-22 14:40
收藏-吃灰
点赞 回复 分享
发布于 2020-06-22 22:35
爱你?
点赞 回复 分享
发布于 2020-06-25 15:33
点赞 回复 分享
发布于 2020-06-27 20:44
后面线程池部分重复太多了
点赞 回复 分享
发布于 2020-07-01 14:44
厉害!刚了一半顶不住了
点赞 回复 分享
发布于 2020-07-05 18:07
问你个问题,map只有四个实现类吗
点赞 回复 分享
发布于 2020-07-07 14:35
这里  它有4个实现类,分别是1、2、3、4  ,,和它只有4个实现类有啥区别
点赞 回复 分享
发布于 2020-07-08 16:47
这里boolean占1bit准确吗?我看很多博客都没有给明确答案😥
点赞 回复 分享
发布于 2020-07-09 11:54
关于静态变量存放在哪里的问题,我记得在jdk7的时候将类的静态变量放到Class对象里了,为了移除永久代😅
点赞 回复 分享
发布于 2020-07-16 03:09
厉害
点赞 回复 分享
发布于 2020-07-16 06:58
m
点赞 回复 分享
发布于 2020-07-17 14:40
收藏吃灰
点赞 回复 分享
发布于 2020-07-31 10:01
不错不错
点赞 回复 分享
发布于 2023-01-30 23:45 广东

相关推荐

评论
39
347
分享
牛客网
牛客企业服务