美文网首页java我爱编程
Java面试题总结-2018-04-12

Java面试题总结-2018-04-12

作者: cd13856c86e2 | 来源:发表于2018-04-12 07:18 被阅读74次

转载的一些题,答案回答简单了;面试互联网公司高级,还需要深入原理和源码

本文收集了一些Java常见面试题,希望能帮助大家搞定面试。

Java中 == , equals()和 hashCode() 的区别

== 在比较基本数据类型时比较的是值,而在比较引用类型的数据时比较的是引用的地址,即比较两个引用是否指向同一个对象。
equal() 是java.lang.Object的方法,默认与 == 比较方式相同,可以被程序员重写该方法实现自定义比较方式。
hashCode() 方法给对象返回一个hashcode值。当两个对象相等时,hashcode一定相同,但是反过来不一定成立。

String、StringBuffer、StringBuilder区别

String对象一旦创建不可更改,对String的任何操作都不会影响原对象。
StringBuffer与StringBuilder存储的内容是可以更改的。
StringBuilder是线程不安全的,StringBuffer带有synchronized关键字是线程安全的。

什么是内部类?内部类的作用

定义在一个类的内部的类我们就叫内部类。内部类可以很好的实现隐藏。一般的非内部类,是不允许有 private 与protected权限的,但内部类可以。内部类拥有外围类的所有元素的访问权限,可以实现多重继承,可以避免修改接口而实现同一个类中两种同名方法的调用。

抽象类和接口区别

1)抽象类是单一继承,接口是多重实现。 Java中子类只能有一个父类,而子类可以实现多个接口。
2)继承抽象类表示“从属”关系,实现接口表示“组合”关系
3)接口中全是抽象方法,抽象类中可以有抽象方法,也可有方法体的方法。
4)接口中无构造方法,不可继承,可实现;抽象类可有构造方法,不可被实例化。
5)抽象类的抽象方法不能使用private,final,static修饰,方法不能用private,final修饰。接口的属性默认是用public,static,final修饰,接口中方法是默认用public,abstract修饰。

抽象类的意义

在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。

抽象类与接口的应用场景

抽象类的应用场合

1)定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
2) 某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
3) 规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能。

接口的应用场合

1)类与类之前需要特定的接口进行协调,而不在乎其如何实现。
2)作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
3) 需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
4) 需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。

抽象类是否可以没有方法和属性?

抽象类即可以没有方法也可以没有属性。

泛型中extends和super的区别

<? extends T>限定参数类型的上界:参数类型必须是T或T的子类型
<? super T> 限定参数类型的下界:参数类型必须是T或T的超类型

父类的静态方法能否被子类重写

父类的静态方法可以被子类继承,但是不能重写。

final,finally,finalize的区别

final 修饰对象不可改变,修饰类时不可继承。
finally通常和try catch搭配使用,保证不管有没有发生异常,资源都能够被释放(释放连接、关闭IO流)。
finalize是Object类中的一个方法,子类可以重写finalize()方法实现对资源的回收。

静态属性和静态方法是否可以被继承?是否可以被重写?

Java中静态属性和方法可以被继承,但是不能被重写。

哪些情况下的对象会被垃圾回收机制处理掉?

1)该对象的最后一个引用指向了另一个对象或null;
2)该对象的最后一个引用的作用域结束。

静态代理和动态代理的区别

静态代理:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。

Java中实现多态如何实现?

Java提供了编译时多态和运行时多态两种多态机制。前者是通过方法重载实现的,后者是通过方法的覆盖实现的。

说说你对Java反射的理解

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

说说你对依赖注入的理解

依赖注入也称控制反转。当某个角色(可能是一个Java实例,调用者)需要另一个角色(另一个Java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在Spring里,创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者实例的工作通常由Spring容器来完成,然后注入调用者,因此也称为依赖注入。

String为什么要设计成不可变的?

1)字符串池(String pool)的需求: 在Java中,当初始化一个字符串变量时,如果字符串已经存在,就不会创建一个新的字符串变量,而是返回存在字符串的引用。
2)缓存字符串hashcode码的需要: 字符串的hashcode是经常被使用的,字符串的不变性确保了hashcode的值一直是一样的,在需要hashcode时,就不需要每次都计算,这样会很高效。
3)出于安全性考虑: 字符串经常作为网络连接、数据库连接等参数,不可变就可以保证连接的安全性。

Java中Set与List有什么不同?

Set不允许重复元素存在,List允许。
Set没有索引,List有索引。
Set仅仅允许一个null值,List允许多个null值

为什么Map接口不继承Collection 接口?

Set是无序集合,并且不允许重复的元素。
List是有序的集合,并且允许重复的元素。
而Map是键值对,它被视为是键的set和值的set的组合。
Map被设计为键值对的集合,所以不需要继承Collection 接口。

Comparable和Comparator的不同之处?

Comparable和Comparator接口被用来对对象集合或者数组进行排序。
Comparable接口被用来提供对象的自然排序,我们可以使用它来提供基于单个逻辑的排序。
Comparator接口被用来提供不同的排序算法,我们可以选择需要使用的Comparator来对给定的对象集合进行排序。

为什么Collection不能继承Cloneable和Serializable?

Collection表示一个集合,包含了一组对象。如何存储和维护这些对象是由具体实现来决定的。因为集合的具体形式多种多样,例如list允许重复,set则不允许。而克隆(clone)和序列化(serializable)只对于具体的实体,对象有意义,不能去把一个接口,抽象类克隆,序列化甚至反序列化。所以具体的collection实现类是否可以克隆,是否可以序列化应该由其自身决定,而不能由其超类强行赋予。
如果collection继承了clone和serializable,那么所有的集合实现都会实现这两个接口,而如果某个实现它不需要被克隆,甚至不允许它序列化(序列化有风险),那么就与collection矛盾了。

List和Map的实现方式以及存储方式

List使用可变长数组实现方式;
Map使用数组加链表的数据结构实现。

能否使用任何类作为Map的key?

可以,然而在使用它们之前,需要考虑以下几点:

1)如果类重写了equals()方法,它也应该重写hashCode()方法。
2)类的所有实例需要遵循与equals()和hashCode()相关的规则。请参考之前提到的这些规则。
3)如果一个类没有使用equals(),你不应该在hashCode()中使用它。
4)用户自定义key类的最佳实践是使之为不可变的,这样,hashCode()值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode()和equals()在未来不会改变,这样就会解决与可变相关的问题了。

HashMap的问题

关于HashMap的面试问题比较集中。推荐一篇博客https://www.cnblogs.com/chengxiao/p/6059914.html对于HashMap做了较为详细的介绍。不过想要更深入理解HashMap最好还是亲自查看源码。

是否可以往TreeSet或者HashSet中添加null元素?

可以往HashSet中添加一个null元素。
TreeSet也允许一个null值

ArrayMap和HashMap的对比

1)存储方式不同
HashMap内部有一个HashMapEntry<K, V>[]对象,每一个键值对都存储在这个对象里,当使用put方法添加键值对时,就会new一个HashMapEntry对象。
2)添加数据时扩容时的处理不一样。HashMap重新创建对象,开销很大。ArrayMap用的是copy数据,所以效率相对要高。
3)ArrayMap提供了数组收缩的功能,在clear或remove后,会重新收缩数组,释放空间。

HashMap和HashTable的区别

1)HashMap允许key和value为null,而HashTable不允许。
2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。
3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。
4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。

HashMap与HashSet的区别

1)HashMap实现了Map接口,HashSet实现了Set接口。
2)HashMap储存键值对,HashSet仅仅存储对象使用put()方法将元素放入map中,使用add()方法将元素放入set中。
3)HashMap中使用键对象来计算hashcode值。HashSet使用成员对象来计算hashcode值。
4)HashMap比较快,因为是使用唯一的键来获取对象HashSet较HashMap来说比较慢。

BlockingQueue是什么?

BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。

什么是深拷贝和浅拷贝

浅拷贝

浅拷贝又叫浅复制,将对象中的所有字段复制到新的对象(副本)中。其中,值类型字段(java中8中原始类型)的值被复制到副本中后,在副本中的修改不会影响到源对象对应的值。而引用类型的字段被复制到副本中的还是引用类型的引用,而不是引用的对象,在副本中对引用类型的字段值做修改会影响到源对象本身。

深拷贝

深拷贝将对象中的所有字段复制到新的对象中。不过,无论是对象的值类型字段,还是引用类型字段,都会被重新创建并赋值,对于副本的修改,不会影响到源对象本身。

开启线程的三种方式?

1.继承Thread类
2.实现Runnable接口
3.直接在函数体使用(匿名内部类)。

线程和进程

进程

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

run()和start()方法区别

start():

start()方法的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。

run():

run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程。

如何控制某个方法允许并发访问线程的个数

使用Semaphore信号量控制数目。

Runnable接口和Callable接口的区别

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已。
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

在Java中wait()和sleep()方法的不同

1)调用sleep()方法正在执行的线程主动让出CPU,并不会释放同步资源锁;wait()方法则是指当前线程让自己暂时退让出同步资源锁,以便其他正在等待该资源的线程得到该资源进而运行,只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行;
2) sleep()方法可以在任何地方使用;wait()方法则只能在同步方法或同步块中使用;

什么导致线程阻塞?

1)线程执行了Thread.sleep(int millsecond);方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行。
2)线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
3)线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
4)线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。

线程如何关闭?

1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2)使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。
3)使用interrupt方法中断线程。

同步方法有哪些

1)同步方法:即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
2)同步代码块:即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
3)wait与notify方法保证线程同步。
4)使用volatile:volatile关键字为域变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新。
5)使用重入锁实现线程同步:ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
6)使用局部变量实现线程同步:使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
7)使用阻塞队列实现线程同步:JavaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。

synchronized和volatile关键字的区别

1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法。
3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性。
4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。

synchronized与Lock的区别

1)同步代码块其实自身是具有自动上锁、自动解锁功能的。Lock锁机制则是手动解锁,手动上锁的
2)用synchronized修饰的同步代码块还有同步方法是有同步锁对象的。Lock锁机制是没有同步锁对象的。
3)因为synchronized修饰的同步代码块还有同步方法是具有锁对象的,因此,可以调用notify()、wait()、notifyAll()的方法。但是因为Lock锁机制是不具有锁对象的,因此是不可以去调用notify()、wait()、notifyAll()方法的,否则会发生报错。

产生死锁的原因

1)因为系统资源不足。
2)进程运行推进的顺序不合适。
3)资源分配不当等。

产生死锁的四个必要条件:

1)互斥条件:一个资源每次只能被一个进程使用。
2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

怎么避免死锁?

1)死锁检测和恢复
2)死锁预防
3)死锁避免

多线程与线程池

推荐博客http://mp.weixin.qq.com/s/mA59X7bOotyWwvf2V6zMIA

如何保证多线程读写文件的安全?

写线程中需要获取独占锁。
读线程中不需要做任何特殊的处理。

作者:进击的Hello_World
链接:https://www.jianshu.com/p/a6ae3c815951
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章

网友评论

    本文标题:Java面试题总结-2018-04-12

    本文链接:https://www.haomeiwen.com/subject/sewtkftx.html