java面试题

特别说明:该面试题为多处整合而来,我只是一名搬运工!!!
持续更新中…

1.什么是JVM,为什么java被称作平台无关的编程语言?

  • JVM即java虚拟机,是一个可执行java字节码(.class)文件的虚拟机进程。
  • java源代码(.java)被编译器编译成字节码(.class)文件,然后字节码文件被Java虚拟机解释成机器码(不同平台的机器码不一样),最后利用机器码操作硬件和操作系统。
  • 因为不同的平台装有不同的java虚拟机,它能够将相同的字节码文件解释成不同平台所需的机器码,正因为有了JVM的存在,所以java被称为平台无关的编程语言。

2.JDK、JRE、JVM之间的关系?

  • JDK(Java Development Kit),即java开发工具包,它包含了编写java程序所需的编译、运行等开发工具以及JRE。
  • JRE(Java Runtime Environment)。即java运行环境,它包含了java程序运行所需要的软件环境,包括JVM和丰富的类库系统。
  • JVM(Java Virtual Machines),即java虚拟机,它提供了java字节码文件运行所需要的环境支持。
  • 简单的说就是:JDK包含JRE,JRE包含JVM。

3.简述java堆内存和栈内存的区别?

  • java把内存分为两种,一种是栈内存,一种是堆内存。
  • 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放为该变量分配的内存空间,该内存空间可以立即被另作他用。
  • 堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。

4.访问权限修饰符public、protected、private以及不写时(默认)的区别?

  • 如图所示
    在这里插入图片描述

5.介绍一下java GC是在什么时候,对什么东西,做了什么事情?

  • java GC也就是垃圾回收机制,这是java和C++的主要区别之一。在使用java的时候,一般不需要程序员编写内存回收和垃圾清理的代码,因为在java虚拟机中,存在着自动进行内存处理和垃圾回收的机制,称之为gc。
  • 垃圾回收算法主要有以下几种:
    1.标记-清除。 其缺点是每次进行垃圾回收时,会暂停当前用户程序的运行;而且需要间隔性的检查,标记和清除的过程相对缓慢;以及会产生大量的内存碎片,导致一旦需要为较大的对象分配空间时,由于找不到足够大的内存空间,而不得以引发另外一次GC过程。
    2.标记-复制。 其缺点是原有可用空间被降低了一半,空间利用率大大下降;同时也会暂停当前用户程序的运行。
    3.标记-整理。 其缺点也是会暂停当前用户程序的运行,非实时性的回收。
    4.分代回收。 其实是对上面三种算法的灵活应用,本身并无新的思想。
    具体可参考:深入JVM垃圾回收算法
  • gc的工作时间是不可预测的,程序员无法控制,调用System.gc()只会通知gc开始工作,但是gc真正开始的时间是不确定的。
  • gc会对不使用的对象进行垃圾回收处理,所谓不使用的对象,指的是超出了作用域或者引用计数为空的对象,而且经过一次标记、清理,仍然没有复活的对象。 那么如何判断一个对象是否应该被回收呢?
  • 这就是所谓的对象存活性判断,常用的方法有两种:1.引用计数法; 2.对象可达性分析。由于引用计数法存在互相引用导致无法进行GC的问题,所以目前JVM虚拟机多使用对象可达性分析算法。
  • 会对这些对象进行删除操作,进而腾出内存空间给其他需要的对象使用。而具体的如何删除,则会根据对象属于新生代还是老年代进行不同处理。新生代每次死亡对象大概98%,这部分会根据标记-复制算法进行垃圾回收;而老年代大部分对象都是存活状态,则会根据标记-清除或者标记-整理算法进行垃圾回收。

6.什么是面向对象以及类和对象的关系

  • 万物皆对象,每个物体都包含动态的行为和静态的属性,这些就构成了一个对象。
  • 类是对象的抽象,对象是类的具体;类是对象的模板,对象是类的实例。

7.Java中各种数据默认值

  • byte,short,int,long默认是都是0
  • boolean默认值是false
  • char类型的默认值是’’
  • float与double类型的默认是0.0
  • 对象类型的默认值是null

8.Java常用的包有哪些

  • java.lang
  • java.io
  • java.math
  • java.util
  • java.net
  • java.sql

9.你遇到的常见的异常

  • ClassCastException 类型转换异常
  • NullPointException 空指针异常
  • ClassNotFoundException web项目加载时找不到类
  • ArrayIndexOutOfBoundsException 数组下标越界异常
  • ArithmeticException 数字运算异常
  • IllegalArgumentException 方法的参数错误
  • lllegalAccessException 没有访问权限
  • NumberFormatException 数字转字符串异常
  • SQLException 操作数据库异常 sql语句异常
  • .StackOverflowError 栈内存溢出

10.谈谈你对异常的类的了解

  • java中所有的异常类都有一个共同的父类,那就是throwable,throwable是异常类的顶级父类。其下有两个重要的子类,Error(错误)和Exception(异常);
  • Error指的是程序无法处理的问题,比较严重的错误,通常与程序员执行的操作无关,是jvm出现了问题,例如虚拟机运行错误(Virtual MachineError);
  • Exception指的是程序本身可以处理的异常,它有一个比较重要的子类RuntimeException(运行时异常)。如空指针异常、下标越界异常等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
  • 非运行时异常 (编译异常):是运行时异常以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

11.谈谈你对异常的处理机制的了解

  • 异常捕捉:try…catch…finally
  • 异常抛出:throws

12.java中是值传递引用传递

  • 理论上说,java都是引用传递,对于基本数据类型,传递是值的副本,而不是值本身。对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。

13.Static关键字有什么作用

  • Static可以修饰内部类、方法、变量、代码块
  • Static修饰的类是静态内部类
  • Static修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在static方法中不能使用this或者super关键字
  • Static修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存
  • Static修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行

14.String str="aaa"与String str=new String(“aaa”)一样吗

  • 不一样,因为分配内存的方式不一样
  • 第一种,创建的"aaa"是常量,jvm都将其分配在常量池中
  • 第二种创建的是一个对象,jvm将其值分配在堆内存中

15.==与equlas有什么区别

  • 简单地说,==在比较基本数据类型的时候,比较的是它们的数值大小是否相同。在比较引用数据类型(例如对象)的时候,比较的是它们指向的内存地址是否相同。
  • 但是String类中的.equals()方法重写了,比较的是两个引用对象的内容是否相同。

16.接口与抽象类有什么区别

  • 抽象类有构造方法,接口没有构造方法
  • 抽象类只能单继承,接口可以多继承
  • 抽象类可以有普通方法,接口中的所有方法都是抽象方法
  • 接口的属性都是public static final修饰的,而抽象类不是

17.使用Log4j对程序有影响吗

  • 有,log4j是用来日志记录的,记录一些关键敏感的信息,通常会将日志记录到本地文件或者数据库中。记录在本地文件中,会有频繁的io操作,会耗费一些系统资源。记录在数据库中,会频繁地操作数据库表,对系统性能也有一定的影响。但是为了程序安全以及数据的恢复或者bug的跟踪,这点资源消耗是可以承受的。
  • log4j的级别由低到高依次是:debug、info、wran、error

18.ArrayList与LinkedList有什么区别

  • ArrayList与LinkedList都实现了List接口
  • ArrayList是线性表,底层是使用数组实现的,它在尾端插入和访问数据时效率较高
  • Linked是双向链表,他在中间插入或者头部插入时效率较高,在访问数据时效率较低

19.Array与ArrayList有什么不一样

  • Array与ArrayList都是用来存储数据的集合。ArrayList底层是使用数组实现的,但是arrayList对数组进行了封装和功能扩展,拥有许多原生数组没有的一些功能。我们可以理解成ArrayList是Array的一个升级版。

20.Map有什么特点

  • 以键值对存储数据
  • 元素存储循序是无序的
  • 不允许出现重复键

21.HashMap的实现原理

  • HashMap是基于哈希表的Map接口的非同步实现,底层采用数组+链表实现。

  • 存储以及取出元素过程如下:

  • HashMap在底层将key-value当成一个整体对象entry来处理,并在底层将所有的key-value以数组entry[]的形式保存。

  • 当需要存储一个entry时,会根据hash算法(通过计算key的hashcode然后将其对数组的长度取余)决定这个entry在数组中的位置,如果出现冲突(即key的hashcode相同),则根据equals方法决定该entry在该数组位置上的链表中的位置,通过链表解决冲突问题。而在jdk8中,对于链表长度超过8的链表会将其转储位红黑树。

  • 当需要取出一个entry时,会先根据hash算法找到该entry在数组中的位置,然后根据equals方法从该位置的链表中找到entry。

  • 扩展说明:由上可知,hashcode相同的对象,不一定是同一个对象,但是同一个对象的hashcode一定相同。所以重写了equals方法后,一定要重写hashcode方法。

22.HashMap的扩容机制,为什么都是2的次幂

  • 当HashMapde的长度超出了加载因子与当前容量的乘积(默认16*0.75=12)时,通过调用resize方法重新创建一个原来HashMap大小的两倍的newTable数组,最大扩容到2^30+1,并将原先table的元素全部移到newTable里面,重新计算hash,然后再重新根据hash分配位置。这个过程叫作rehash,因为它调用hash方法找到新的bucket位置。
  • 这个过程是很耗时的,所以在使用HashMap是应尽可能确定其长度,避免自动扩容;或者定期进行手动扩容。
  • 2的次幂是为了节约空间以及使entry对象均匀分布。

23.HashMap,HashTable,ConcurrentHashMap的区别

1.HashTable

  • 底层是基于数组+链表实现,key和value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化;
  • 初始size为11,扩容:newsize = oldsize*2+1;
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length。

2.HashMap

  • 底层数组+链表实现,可以存储null键和null值,线程不安全;
  • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂;
  • 计算index方法:index = hash & (tab.length – 1);
  • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀。插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)。

3.ConcurrentHashMap

  • 底层采用分段的数组+链表实现,key和value都不能为null,线程安全;
  • 通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值);
  • ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分段技术。所谓锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问;
  • 有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
  • 扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容。

24.HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么

  • 多线程put时可能会导致get无限循环,具体表现为CPU使用率100%;
    原因:在向HashMap put元素时,会检查HashMap的容量是否足够,如果不足,则会新建一个比原来容量大两倍的Hash表,然后把数组从老的Hash表中迁移到新的Hash表中,迁移的过程就是一个rehash()的过程,多个线程同时操作就有可能会形成循环链表,所以在使用get()时,就会出现Infinite Loop的情况

  • 多线程put时可能导致元素丢失;
    原因:当多个线程同时执行addEntry(hash,key ,value,i)时,如果产生哈希碰撞,导致两个线程得到同样的bucketIndex去存储,就可能会发生元素覆盖丢失的情况。

25.动态代理的两种实现方式,以及区别

  • jdk动态代理
    由java内部的反射机制来实现的。

  • cglib动态代理
    是通过继承来实现的,底层则是借助asm(Java 字节码操控框架)来实现的(采用字节码的方式,给A类创建一个子类B,子类B使用方法拦截的技术拦截所以父类的方法调用)。

  • 总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

详情点击这里

26.Java序列化的方式

  • Json序列化
  • FastJson序列化
  • ProtoBuff序列化

详情点击这里

27.一个ArrayList在循环过程中删除,会不会出问题,为什么

  • 这个跟循环的方式(for循环、增强for循环、迭代器),以及循环的顺序(从左到右或者从右到左)都有关系~

  • 详情点击这里

28.强软弱虚引用的区别以及GC对他们执行怎样的操作

  • 从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

  • 强引用
    如果一个对象具有强引用,那么垃圾回收器绝不会回收它。哪怕内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

  • 软引用
    如果一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

  • 弱引用
    如果一个对象只具有弱引用,垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

  • 虚引用
    "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动。

29.说说TCP和UDP的区别
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的、可靠的、 基于IP的传输层协议。
UDP(User Datagram Protocol 用户数据报协议)是OSI(Open System Interconnection,开放式系统互联)参考模型中一种无连接的传输层协议。

  • TCP面向连接(如打电话要先拨号建立连接),UDP是无连接的,即发送数据之前不需要建立连接;

  • TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付;

  • UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高要求的通信或广播通信;

  • 每一条TCP连接只能是点到点的,UDP支持一对一,一对多,多对一和多对多的交互通信;

  • TCP对系统资源要求较多,UDP对系统资源要求较少。

  • 详情点击这里

30.说说三次握手,四次挥手,为什么要四次挥手

三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收机能正常。

  • 第一次握手:Client什么都不能确认;Server确认了对方发送正常;
  • 第二次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己接收正常,对方发送正常;
  • 第三次握手:Client确认了:自己发送、接收正常,对方发送、接收正常;Server确认了:自己发送、接收正常,对方发送接收正常。

以上三次握手只是说明了每次握手达到了什么样的效果,具体发送信息以及确认信息此处未加说明可参考三次握手四次挥手详解

双方关闭连接要经过双方都同意,首先是客服端给服务器发送FIN,要求关闭连接,服务器收到后会发送一个ACK进行确认。服务器然后再发送一个FIN,客户端发送ACK确认,并进入TIME_WAIT状态。等待2MSL后自动关闭。
就像A和B打电话,A说:“我说完了”,B说:“我知道了”。这时候不能直接把电话挂了,因为不确定B是否说完了。所以,还需要B说:“我说完了”,A说:“我知道了”,才能挂电话。

  • 第一次挥手:首先,客户端发送一个FIN,用来关闭客户端到服务器的数据传送,然后等待服务器的确认;
  • 第二次挥手:服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一;
  • 第三次挥手:关闭服务器到客户端的连接,发送一个FIN给客户端;
  • 第四次挥手:客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

32.session和cookie的区别

Cookie是服务器端产生的,保存在浏览器,Cookie实际上是一小段文本信息,具有不可跨域性。分为临时性和持久性,默认情况下,浏览器关闭后Cookie就销毁了。
Session是基于Cookie的一种会话机制,是服务器上的一块内存空间,可以用来存储数据,有id。

  • cookie存放在浏览器端,一般最多存放20个,数据不安全,可以减轻服务器压力,但是会占用用户磁盘,存放的数据有限,一般小于4k。
  • session数据存放在服务,数据相对安全,但是对服务造成的压力大点,存档的数据多少取决于服务器的内存空间。session默认存在30分钟,如果想拿到同一个session,需要设置它的id,并将其jd存放在cookie上。

33.get请求和post请求的区别

  • get请求会把数据拼接在地址栏上,post请求则不会,是以流的形式写出去的,所以post请求更安全;
  • post请求有请求体,get请求没有。post请求是以流的形式写的,所以要在请求头里面表示写的数据类型以及数据长度;
  • 能提交的数据长度不一样,get请求有长度,一般是1k或者4k;而post请求没有长度,主要取决于服务器的内存空间。

34.什么是redis,以及redis支持哪些数据类型

  • Redis全称为:Remote Dictionary Server(远程数据服务),是一个基于内存的高性能key-value数据库。

支持的数据类型:

  • String(字符串)
  • Set(无序集合)
  • Sorted Set(有序集合)
  • List(列表)
  • Hash(哈希)

35.redis和memcached的区别

  • Memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型;
  • Redis的速度比Memcached快很多,redis每秒读写速度超过10w次;
  • MemCached把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,能保证数据的持久性。

36.redis的数据淘汰策略

将 Redis 用作缓存时, 如果内存空间用满, 就会自动驱逐老的数据,LRU是Redis唯一支持的回收算法。
maxmemory 用于指定 Redis 能使用的最大内存。既可以在 redis.conf 文件中设置, 也可以在运行过程中通过 CONFIG SET 命令动态修改。

达到最大内存限制时(maxmemory), Redis 根据 maxmemory-policy 配置的策略, 来决定具体的行为。

  • volatile-lru: 从已设置过期时间的数据集(server.db[i].expire)中挑选最近最少使用的数据淘汰。
  • volatile-ttl: 从已设置过期时间的数据集(server.db[i].expire)中挑选将要过期的数据淘汰。
  • volatile-random: 从已设置过期时间的数据集(server.db[i].expire)中任意数据淘汰。
  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰。
  • allkeys-random: 从数据集(server.db[i].dict)中挑选任意选择数据淘汰。
  • no-enviction(驱逐): 禁止驱逐数据。

37.说说redis的持久化策略

  • RDB(redis database)快照

  • 在指定的时间间隔对你的数据进行快照存储,默认情况下,使用快照rdb的持久化方式,将内存中的数据以快照的方式写入二进制文件中,默认的文件名是dump.rdb;

  • 这种方式不能完全保证数据持久化,因为是定时保存,所以当redis服务down掉,就会丢失一部分数据,而且数据量大,写操作多的情况下,会引起大量的磁盘IO操作,会影响性能。

  • AOF(append-only file)

  • 使用aof做持久化,每一个写命令都通过write函数追加到appendonly.aof中;

  • 数据安全,aof持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次。通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。

38.谈谈Redis的适用场景

  • 会话缓存(Session Cache),这是最常用的一种使用Redis的情景;
  • 全页缓存(FPC);
  • 队列;
  • 排行榜/计数器…

39.JDBC操作的步骤

  • 加载数据库驱动类( DriverManager.registerDriver(new com.mysql.jdbc.Driver()))

  • 打开数据库连接( conn = DriverManager.getConnection(“jdbc:mysql://localhost/student”, “root”, “root”);)

  • 创建statement , 跟数据库打交道,一定需要这个对象( st = conn.createStatement();)
    注意:statement 现在已经很少直接使用了,而是使用PrepareStatement 来替代它。PrepareStatement 是 Statement的子接口。一般企业级应用开发都会采用PrepareStatement , 之所以这么做,是处于以下几个原因考虑:

  • PreparedStatement 可以写动态参数化的查询

  • PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中

  • PreparedStatement 可以防止SQL注入式攻击

  • 比起Statement 凌乱的字符串追加似的查询,PreparedStatement查询可读性更好、更安全。

  • 执行sql语句(rs = st.executeQuery(sql);)

  • 处理返回结果

  • 关闭资源(rs.close();)

  • 在jdbc内使用CallableStatement可以调用一个存储过程

40.说说你用到过的数据库连接池

  • Dbcp,c3p0等,用的最多还是c3p0,因为c3p0比dbcp更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。当连接的数据库信息发生改变时,不需要再更改程序代码就实现了数据库信息的更新。

41.谈谈final、finalize()、finally三者的区别

  • final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变
  • finalize 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会,但是什么时候调用 finalize 没有保证
  • finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常

42.abstract不能个哪些关键字共存

  • static,被abstract修饰的方法无方法体,被static修饰的方法可以类名.调用,但是类名.调用抽象方法是没有意义的;
  • final,被abstract修饰的方法强制子类重写,而被final修饰的方法不让子类重写,二者矛盾;
  • private, 被abstract修饰是为了让子类看到并重写,而被private修饰的不让子类访问二者矛盾。

43.抽象类和接口的区别

  • 抽象类是“自下而上”的设计,接口是“自上而下”的设计;
  • 抽象类是单继承,接口可以多实现;
  • 抽象类可以有构造方法,接口没有构造方法;
  • 抽象类的成员变量既可以是变量也可以是常量,接口的成员变量只能是常量,并且都是默认public static final;
  • 抽象类中成员方法可以是抽象的,也可以有方法的实现;而JDK1.8之前,接口中只能有抽象方法;
  • 二者都不能被实例化

44.说说JDK1.8的新特性

  • 允许接口添加默认的实现,用default修饰,default可以被子接口继承以及覆写,亦可以被其他实现类所调用
  • 接口中的静态方法不会被继承或者实现,但是静态变量会被继承
  • 引入了Lambda表达式,也叫闭包,它允许讲一个函数作为方法的参数,大大减少了代码量
  • 强大的Stream API, Stream是多个元素的序列,支持串行和并行操作
  • 新增Optional类(java.util.optional),是一个容器,代表一个值存在或者不存在,原来用null表示。现在optional可以更好地表达此概念,可避免空指针异常。

45.说说事务的特性

46.谈谈MongoDB和MySql的区别

  • 首先mongodb是一个有C++编写的高性能的文档型数据库,为Web应用提供可扩展数据库解决方案
  • 区别主要在于以下几点:1.mongodb按照collection存储,一个collection中包含多个document;而mysql按照table存储,一个table中包含很多纪录。2.mongodb很灵活,可存储任意json格式的数据;mysql的row中每列的数据类型是定死的,不够灵活。3.mongodb对数据字段扩展零消耗,mysql消耗很大。4.mongodb在4.0之后开始支持事务。最后,一般情况下,mongodb的读写性能都略高于mysql。

47.MySql相关面试题

48.谈谈SpringMVC的工作原理

  • 客户端请求提交到DispatcherServlet
  • 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
  • DispatcherServlet将请求提交到Controller
  • Controller调用业务逻辑处理后,返回ModelAndView
  • DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
  • 视图负责将结果显示到客户端

SpringMVC组件的说明:
1.DispatcherServlet接口:Spring提供的前端控制器,所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具体的Controller。
2.HandlerMapping接口:能够完成客户请求到Controller映射。
3.Controller接口:需要为并发用户处理上述请求,因此实现Controller接口时,必须保证线程安全并且可重用。Controller将处理用户请求,这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求,则返回ModelAndView对象给DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和视图(View)。
4.ViewResolver接口:Spring提供的视图解析器(ViewResolver)在Web应用中查找View对象,从而将相应结果渲染给客户。

49.什么是Spring,谈谈你对Spring的理解

  • Spring是一个开源的轻量级的应用开发框架,其目的是用于简化企业级应用开发,降低代码的侵入性和耦合度。
  • Spring提供的IOC和AOP功能,可以将容器内的组件耦合度降低至最低,即解耦,为将来的工程代码的维护提供了方便。
  • Spring为系统提供了一个整体的解决方案,开发者除了可以利用它本身具有的功能外,还可以与第三方框架和技术进行整合应用,可以自由选择使用哪种技术开发。
  • Spring的主要核心是:
    1.控制反转(IOC):以前传统的java开发模式中,当需要一个对象时我们,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象,而在Spring开发模式中,Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。实例化一个java对象有三种方式:使用类构造器,使用静态工厂方法,使用实例工厂方法,当使用spring时我们就不需要关心通过何种方式实例化一个对象,spring通过控制反转机制自动为我们实例化一个对象。
    2.依赖注入(DI):Spring使用java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。
    3.面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。

50.谈谈IOC的实现原理

  • 所谓控制反转是指,本来被调用者的实例是由调用者来创建的,这样的缺点是耦合性太强,IOC则是统一交给spring来管理创建,将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。

51.AOP的原理

  • AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码,属于静态代理

52.什么是JPA

  • JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。Sun引入新的JPA ORM规范出于两个原因:其一,简化现有Java EE和Java SE应用开发工作;其二,Sun希望整合ORM技术,实现天下归一 。 其实在5.0出现发布之前,市面上有诸如、Hibernate 、OpenJPA 、TopLink 等一些列 ORM框架,并且hibernate已经很受大众喜爱了,后来hibernate在3.2版本正式接入JPA 规范,表示自己即属于JPA的框架的具体实现。 一句话概括: JPA 是一种规范、 Hibernate 是这种规范的最好实现。
  • Dao最受欢迎的两个框架: 1.MyBatis ----不是JPA体系 ----- 需要写sql语句 ;2.Hibernate — JPA 体系 -----可以不用写sql语句
  • ORM(Object Relational Mapping) : 对象关系映射 。 ORM的思想其实就是让 表 和 JavaBean 形成一种映射关系 , 并且让表里面的字段 和JavaBean 里面的成员形成映射关系。

53.什么是线程

  • 线程是操作系统能够进行运算调度的最小单位,包含在进程之中,是进程的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。
  • 多线层能发挥多核CPU的优势、防止阻塞以及便于建模。

54.实现多线程的方法有哪些,区别是什么

  • 分别是继承Thread类或者实现Runnable接口;
  • 继承Thread实现多线程代码比较简单,但是如果已经继承了其他类就无法再继承Thread类了,但是仍然可以实现Runnable接口。所以如果需要继承其他类,当然选择通过实现Runnable接口的方法实现多线程。

55.sleep()和wait()方法的区别

  • 两者之间最大的不同在于wait()会释放锁而sleep()不会释放锁,wait()是Object类的,sleep()是Thread类的;
  • wait()常用于线程之间的交互,sleep()通常被用于暂停执行。
  • wait()需要notif()或者notifyAll()唤醒,而slppe()一段时间过后线程会自动醒来继续执行。

56.start()和run()方法的区别

  • 只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。

57.如何实现线程之间的资源共享

  • 可通过实现Runnable接口,实现多线程之间的资源共享,使用多线程的时候要注意线程安全问题,可通过synchronized同步锁进行处理。

58.线程的状态都有哪些

  • 新建状态
    当用new操作符创建一个线程时。此时程序还没有开始运行线程中的代码。

  • 就绪状态
    一个新创建的线程并不会自动开始运行,必须调用start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
    处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序来调度的。

  • 运行状态(running)
    当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。

  • 阻塞状态(blocked)
    所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

  • 死亡状态(dead)
    可以使用isAlive方法判断线程是否死亡,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。

59.说说你对Callable和Future的了解

60.线程池的参数有哪些,在线程池创建一个线程的过程

相关参数:

  • corePoolSize(核心线程数)
    1.核心线程会一直存活,及时没有任务需要执行;
    2.当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理;
    3.设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。

  • queueCapacity(任务队列容量,即阻塞队列)
    当核心线程数达到最大时,新任务会放在队列中排队等待执行。

  • maxPoolSize(最大线程数)
    1.当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务;
    2.当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常。

  • keepAliveTime(线程空闲时间)
    1.当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize;
    2.如果allowCoreThreadTimeout=true,则会直到线程数量=0。

  • allowCoreThreadTimeout(允许核心线程超时)

  • rejectedExecutionHandle(拒绝任务处理)

通过线程池创建线程:

  • 通过Executors.可以创建线程池
//创建线程池对象
ExecutorService service = Executors.newCachedThreadPool();//包含2个线程对象

一般使用线程池,按照如下顺序依次考虑(只有前者不满足场景需求,才考虑后者):

newCachedThreadPool–>newFixedThreadPool(int threadSize)–>ThreadPoolExecutor 。
newCachedThreadPool不需要指定任何参数
newFixedThreadPool需要指定线程池数(核心线程数==最大线程数)
ThreadPoolExecutor需要指定核心线程数、最大线程数、闲置超时时间、队列、队列容量,甚至还有回绝策略和线程工厂。

61.synchronized和volitile关键字的区别

  • volatile轻量级,只能修饰变量;synchronized重量级,还可修饰方法。
  • volatile只能保证数据的可见性,不能保证原子性,不能用来同步,因为多个线程并发访问volatile修饰的变量不会阻塞。synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。

62.ABC三个线程如何保证顺序执行

  • 用Thread.join() 方法 确定该线程执行完毕;
  • 线程池 newSingleThreadExecutor 这个线程处理完一个任务后接着处理下一个任务。

63.Object类有哪些方法

持续更新中…

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐