JAVA基础面试题

目录

一、JDK JRE JVM 区别

1、英文全称

2、包含关系

二、Jdk1.7 - Jdk1.8

1、java虚拟机 变化

2、HashMap 变化(底层)

三、如何实现AOP

四、红黑树 Red-Black Tree

1、特性:

2、例图

3、应用

4、基本操作

五、 Java中的异常体系

六、String、StringBuffer、StringBuild

七、接口和抽象类的区别

八、CopyOnWriteArrayList的底层原理


一、JDK JRE JVM 区别

1、英文全称

JDK
Java Develpment kit                        java开发工具(提供给开发人员使用)

JRE
Java Runtime Environment             java运行时环境(提供给用户运行java程序)

JVM
Java Virtual Machine                        java虚拟机(将class文件解释成机器码)

2、包含关系

二、Jdk1.7 - Jdk1.8

1、java虚拟机 变化

1.7中存在永久代,1.8中没有永久代,替换它的是元空间,元空间所点的内存不在虚拟机内部,而是本地内存空间,这么做的原因是,不管是永久代还是元空间,他们都是方法区的具体实现,之所以元空间所占的内存改成本地内存,官方的说法是为了和JRockit统一,不过额外还有一些原因,比如方法区所存储的类信息通常是比较难确定的,所以对于方法区的大小是比较难指定的,太小了容易出现方法区溢出,太大了又会占用了太多虚拟机的内存空间,而转移到本地内存后则不会影响虚拟机所占用的内存

2、HashMap 变化(底层)

  1. 1.7中底层是数组+链表,1.8中底层是数组+链表+红黑树①,加红黑树的目的是提高HashMap插入和查询整体效率
  2. 1.7中链表插入使用的是头插法,1.8中链表插入使用的是尾插法,因为1.8中插入key和Value时需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使用尾插法。
  3. 1.7中哈希算法比较复杂,存在各种右移与异或运算,1.8中进行了简化,因为复杂的哈希算法的目的就是提高散列性,来提供HashMap的整体效率,而1.8中新增了红黑树,可以适当的简化哈希算法,节省CPU资源。

三、如何实现AOP

利用动态代理技术来实现AOP,比如JDK动态代理或Cglib动态代理,利用动态代理技术,可以针对某个类生成代理对象,当调用代理对象的某个方法时,可以任意控制该方法的执行,比如可以先打印执行时间,再执行该方法,并且该方法执行完成后,再次打印执行时间。
项目中,比如事务、权限控制、方法执行时长日志都是通过AOP技术来实现的,凡是需要对某些方法做统一处理的都可以用AOP来实现,利用AOP可以做到业务无侵入。

四、红黑树 Red-Black Tree

一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

1、特性:

(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

注意

  • 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
  • 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。

2、例图

3、应用

红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常高。
例如,Java集合中的TreeSetTreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

4、基本操作

红黑树的基本操作是添加删除。在对红黑树进行添加或删除之后,都会用到旋转方法。因为添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性
旋转包括两种:左旋 和 右旋

五、 Java中的异常体系

Java中的所有异常都来自顶级父类Throwable。Throwable下有两个子类Exception和Error
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检查异常。

RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。

六、String、StringBuffer、StringBuild

String是final修饰的,不可变,每次操作都会产生新的String对象                                              StringBuffer和StringBuilder都是在原对象上操作
StringBuffer线程安全的,StringBuilder线程不安全的                                                            StringBuffer方法都是synchronized修饰的
性能: StringBuilder > StringBuffer > String

场景:经常需要改变字符串内容时使用后面两个
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer

七、接口和抽象类的区别

  • 抽象类可以存在普通成员函数,而接口中只能存在public abstract方法。
  • 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型。
  • 抽象类只能单继承,接口可以多实现
     

接口的设计目的,是对类的行为进行约束("有"约束,因为接口不能规定类不可以有什么行为),可以强制要求不同的类具有相同的行为,不对如何实现行为进行限制
抽象类的设计目的,是代码复用。当不同的类具有某些相同的行为(记为行为集合A),且其中一部分行为的实现方式一致时(A的非真子集,记为B),可以让这些类都派生于一个抽象类。在这个抽象类中实现了B,避免让所有的子类来实现B,这就达到了代码复用的目的。而A减B的部分,留给各个子类自己实现。正是因为A-B在这里没有实现,所以抽象类不允许实例化出来(否则当调用到A-B时,无法执行)。
抽象类是对类本质的抽象,表达的是is a的关系,比如:五菱宏光 is a 车。抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。
而接口是对行为的抽象,表达的是like a的关系。比如: 鸟 like a 飞机(可以飞)。接口的核心是定义行为,即实现类可以做什么,至于实现类主体是谁、是如何实现的,接口并不关心。

使用场景:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。

八、CopyOnWriteArrayList的底层原理

ArrayList线程不安全

  1. 首先CopyOnWriteAraylit内部也是用过数组来实现的,在向CopyOnWriteAravList添加元素时,会复制一个新的数组,写操作在新数组上进行,读操作在原数组上进行
  2. 并且,写操作会加锁,防止出现并发写入丢失数据的问题
  3. 写操作结束之后会把原数组指向新数组
  4. CopyOnWriteArrayList允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,但是CopyOnWriteArayList会比较占内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景
     

九、5种IO模型

当“A”向"B" 发送一条消息:

第一步:应用A把消息发送到 TCP发送缓冲区。

第二步: TCP发送缓冲区再把消息发送出去,经过网络传递后,消息会发送到B服务器的TCP接收缓冲区。

第三步:B再从TCP接收缓冲区去读取属于自己的数据。

阻塞IO | 非阻塞IO

应用之间发送消息是间断性,如果应用B的TCP缓冲区未接收到消息,而应用B向TCP缓冲接受区发送read请求。是等待还是停止?

  • 阻塞IO:在应用调用recvfrom读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回,在此期间一直会等待,进程从调用到返回这段时间内都是被阻塞。
  • 非阻塞IO:在应用调用recvfrom读取数据时,如果该缓冲区没有数据的话,就会直接返回一个EWOULDBLOCK错误,不会让应用一直等待中。在没有数据的时候会即刻返回错误标识,那也意味着如果应用要读取数据就需要不断的调用recvfrom请求,直到读取到它数据要的数据为止。

多路复用IO

如果在并发的环境下,可能会N个人向应用B发送消息,这种情况下我们的应用就必须创建多个线程去读取数据,每个线程都会自己调用recvfrom 去读取数据。如果在并发的环境下,可能会N个人向应用B发送消息,这种情况下我们的应用就必须创建多个线程去读取数据,每个线程都会自己调用recvfrom 去读取数据。可以由一个线程监控多个网络请求,这样就可以只需要一个或几个线程就可以完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据,这么做就可以节省出大量的线程资源出来,这个就是IO复用模型的思路。

IO复用模型的思路就是系统提供了一种函数可以同时监控多个网络请求的操作:select、poll、epoll函数。有了这个函数后,应用线程通过调用select函数就可以同时监控多个网络请求,select函数监控的网络请求中只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时询问线程再去通知处理数据的线程,对应线程此时再发起recvfrom请求去读取数据。

进程通过将一个或多个网络请求传递给select,阻塞在select操作上,select帮我们侦测多个网络请求是否准备就绪,当有网络请求准备就绪时,select返回数据可读状态,应用程序再调用recvfrom读取数据。

信号驱动IO模型

信号驱动IO不是用循环请求询问的方式去监控数据就绪状态,而是在调用sigaction时候建立一个SIGIO的信号联系,当内核数据准备好之后再通过SIGIO信号通知线程数据准备好后的可读状态,当线程收到可读状态的信号后,此时再向内核发起recvfrom读取数据的请求,因为信号驱动IO的模型下应用线程在发出信号监控后即可返回,不会阻塞,所以这样的方式下,一个应用线程也可以同时监控多个网络请求,就可以避免大量无效的数据状态轮询操作。

首先开启套接口信号驱动IO功能,并通过系统调用sigaction执行一个信号处理函数,此时请求即刻返回,当数据准备就绪时,就生成对应进程的SIGIO信号,通过信号回调通知应用线程调用recvfrom来读取数据。

异步IO模型 

应用告知内核启动某个操作,并让内核在整个操作完成之后,通知应用,这种模型与信号驱动模型的主要区别在于,信号驱动IO只是由内核通知我们合适可以开始下一个IO操作,而异步IO模型是由内核通知我们操作什么时候完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值