Java面试题合集

Java基础

1. 面向对象和面向过程的区别
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低

2. Java的四个基本特性(抽象、封装、继承,多态)
抽象:就是把现实生活中的某一类东西提取出来,用程序代码表示,我们通常叫做类或者接口。抽象包括两个方面:一个是数据抽象,一个是过程抽象。数据抽象也就是对象的属性。过程抽象是对象的行为特征。
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行封装隐藏。封装分为属性的封装和方法的封装。
继承:是对有着共同特性的多类事物,进行再抽象成一个类。这个类就是多类事物的父类。父类的意义在于抽取多类事物的共性。
多态:允许不同类的对象对同一消息做出响应。方法的重载、类的覆盖正体现了多态。

3. hashCode和equals方法的关系
equals相等,hashcode必相等;hashcode相等,equals可能不相等。

4. 抽象类和接口的区别
语法层次
抽象类和接口分别给出了不同的语法定义。
设计层次
抽象层次不同,抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
跨域不同
抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在”is-a”
关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已,”like-a”的关系。

5. 什么是泛型、为什么要使用以及泛型擦除
泛型,即“参数化类型”。
创建集合时就指定集合元素的类型,该集合只能保存其指定类型的元素,避免使用强制类型转换。
Java编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。泛型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
类型擦除的主要过程如下:
1).将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
2).移除所有的类型参数。

6. object中定义了哪些方法?
clone(), equals(), hashCode(), toString(), notify(), notifyAll(), wait(), finalize(), getClass()

7. short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
对于short s1 = 1; s1 = s1 + 1; 由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告需要强制转换类型的错误。
对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。

8. 使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。例如,对于如下语句:

final StringBuffer a=new StringBuffer("immutable");
#执行如下语句将报告编译期错误:
a=new StringBuffer("");
#但是,执行如下语句则可以通过编译:
a.append(" broken!"); 
#有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:
public void method(final  StringBuffer  param){} 
#实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:
param.append("a");

数据结构

1.红黑树实现原理,它的size方法是如何实现的?有没有更好方案?
size方法返回是不准确的,平时也不会用到这个方法,应该可以用AtomicInteger变量进行记录,每次插入或删除的时候,操作这个变量
面试官:哦,是么,那如果我觉得这个AtomicInteger这个变量性能不好,还能再优化么?
我:volitile变量

多线程

1 .为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object类里?
Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法

2. 为什么wait(), notify()和notifyAll()必须在同步方法或者同步块中被调用?
当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

3. 为什么Thread类的sleep()和yield()方法是静态的?
Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

4.线程池的实现原理
5.线程池中的coreNum和maxNum有什么不同
6.超时返回
Feature.get(timeout,time)

1.了解Java中的什么锁
比如Synchronized和ReentrantLock,balabala
2.Synchronized的实现原理
3.ReentrantLock的实现原理
ReentrantLock是基于AQS实现的
4.什么是AQS?
5.CAS的实现原理,方法参数含义,操作系统级别是如何实现的?
CAS是通过unsafe类的compareAndSwap方法实现的
第一个参数是要修改的对象,第二个参数是对象中要修改变量的偏移量,第三个参数是修改之前的值,第四个参数是预想修改后的值

6.ABA问题解决
java并发包中提供了一个带有标记的原子引用类”AtomicStampedReference”,它可以通过控制变量值的版本来保证CAS的正确性

并发包

1.ConcurrentHashMap实现原理

缓存

1。redis有没有用过,常用的数据结构以及在业务中使用的场景,redis的hash怎么实现的,rehash过程讲一下和JavaHashMap的rehash有什么区别?redis cluster有没有了解过,怎么做到高可用的?redis的持久化机制,为啥不能用redis做专门的持久化数据库存储?
1.Redis 数据结构和主要命令
2.浅谈Redis中的Rehash机制

Web

1. http中的post、get有什么区别?base64过后的字符串可以通过get传输吗?
1、两种动作不一样,get是获取资源,post是提交资源
2、get参数在URL中不安全,post是放在http body中的相对安全
3、get传输字节数受限于URL长度,post无限制
4、后台获取数据的方式get只能是QueryString,post可从InputStream中获取 base64编码后有+=特殊符号的会转码不能经get传输,如果是改进的base64会替换掉特殊符号可以用get传输。

JVM

1. JVM对频繁调用的方法做了哪些优化?
java虚拟机最开始是通过解释器进行解释执行的,当虚拟机发现某个方法或者代码块的运行特别频繁时,就会把这些代码认定为”热点代码”,为了提高热点代码的执行效率,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(JIT)。

Dubbo

1. RPC的原理
客户端:知道接口名和方法名,以及参数类型、调用参数的值 ,但是本地并没有该接口的实现
服务端:这里有接口的实现

调用时,客户端使用JDK动态代理,利用接口创建了一个代理对象
创建代理对象需要两个东西:
1、一个就是接口的全限定名(包名+接口名),
2、还需要一个调用处理接口 InvocationHandler 的实现

说明:每次使用代理对象调用方法时,其实调用的是 InvocationHandler 接口的实现中的invoke 方法
invoke 方法中的实现了远程调用:
1、根据服务端的IP 和端口号,创建一个socketServer ,
2、将接口全限定名、方法名、参数类型、参数值、通过socket 传递到服务端(序列化)
3、 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化)
4、服务端存根( server stub)根据解码结果获得接口的实现类的实例对象,使用反射调用该方法,得到执行结果;
5、 本地服务执行并将结果返回给服务端存根( server stub);
6、服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);
7、将执行结果写入socket ,传递给客户端
8、客户端得到结果返回结果

2、史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官!

Netty

1.Netty线程模型
netty的线程模型么?
netty通过Reactor模型基于多路复用器接收并处理用户请求(能讲就多讲一点),内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件(通过口述加画图的方式,把请求的执行过程大概描述了一遍,时间有限,也不可能把所有的细节都说完,挑重点讲,挑记忆深刻的讲)

2.有没有遇到什么困难,或者是觉得有挑战的地方?如何解决的?
当时做这个项目时,对netty的不过熟悉,把请求的业务逻辑放在work线程池的线程中进行处理,进行压测的时候,发现qps总是上不去,后来看了源码之后才发现,由于业务逻辑的处理比较耗时,完全占用了work线程池的资源,导致新的请求一直处于等待状态。
最后把处理业务的逻辑封装成一个task提交给一个新建的业务线程池中执行,执行完之后由work线程池执行请求的write事件。

3.Nio中Selector可能触发Bug么?如何解决?
在NIO中通过Selector的轮询当前是否有IO事件,根据JDK NIO api描述,Selector的select方法会一直阻塞,直到IO事件达到或超时,但是在Linux平台上这里有时会出现问题,在某些场景下select方法会直接返回,即使没有超时并且也没有IO事件到达,这就是著名的epoll bug,这是一个比较严重的bug,它会导致线程陷入死循环,会让CPU飙到100%,极大地影响系统的可靠性,到目前为止,JDK都没有完全解决这个问题。
但是Netty有效的规避了这个问题,经过实践证明,epoll bug已Netty框架解决,Netty的处理方式是这样的:
记录select空转的次数,定义一个阀值,这个阀值默认是512,可以在应用层通过设置系统属性io.netty.selectorAutoRebuildThreshold传入,当空转的次数超过了这个阀值,重新构建新Selector,将老Selector上注册的Channel转移到新建的Selector上,关闭老Selector,用新的Selector代替老Selector,详细实现可以查看NioEventLoop中的selector和rebuildSelector方法

其他

1. 用户取名不重复

其他:Java面试场景整理收录

技术讨论 & 疑问建议 & 个人博客

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议,转载请注明出处!

参考:
http://www.codeceo.com/article/back-ali-interview.html
https://www.jianshu.com/p/1b2f63a45476

    原文作者:Jacknolfskin
    原文地址: https://www.jianshu.com/p/284a5b67bc03#comments
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞