牛客中Java工程师模拟面试整理
- 请说一下EJB规范中EJB禁止的操作有哪些?
EJB规范中禁止的操作如下:
不能操作线程和线程API(线程API指非线程对象的方法如notify,wait等);
不能操作awt;
不能实现服务器功能;
不能对静态属性存取;
不能使用IO操作直接存取文件系统;
不能加载本地库;
不能将this作为变量和返回;
不能循环调用。 - EJB包括SessionBean和EntityBean,请说出他们的生命周期以及EJB是如何管理事务的?
SessionBean:Stateless Session Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个Bean 的实例时,EJB 容器不一定要创建一个新的Bean 的实例供客户机调用,而是随便找一个现有的实例提供给客户机。当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean 实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean 实例。
EntityBean:Entity Beans 能存活相对较长的时间,并且状态是持续的。只要数据库中的数据存在,Entity beans 就一直存活。而不是按照应用程序或者服务进程来说的。即使EJB 容器崩溃了,Entity beans 也是存活的。Entity Beans 生命周期能够被容器或者Beans 自己管理。
EJB 通过以下技术管理实务:对象管理组织(OMG)的对象实务服务(OTS),SunMicrosystems 的Transaction Service(JTS)、Java Transaction API(JTA),开发组(X/Open)的XA 接口。 - 如何理解Hibernate的延迟加载机制?在实际应用中,延迟加载与Session关闭的矛盾是如何处理的?
Hibernate的延迟加载就是在读取的时候不会将数据加载进来,等到使用数据时再加载,使用了虚拟代理机制实现延迟加载。
矛盾处理:
可以选择关闭延迟加载特性;
在session关闭之前先获取需要查询的数据;
使用拦截器或过滤器延长Session的生命周期直到视图获得数据。
https://www.cnblogs.com/cathyqq/p/5217375.html
- 请介绍一下集合类内存
参考回答:
一、集合类。 Java中的集合包含多种数据结构,如链表、队列、哈希表等。从类的继承结构来说,可以分为两大类,一类是继承自Collection接口,这类集合包含List、Set和Queue等集合类。另一类是继承自Map接口,这主要包含了哈希表相关的集合类。
1、List、Set和Queue类的继承结构图:绿色的虚线代表实现,绿色实线代表接口之间的继承,蓝色实线代表类之间的继承。
Collection接口除了实现映射的集合类之外的所有集合类定义了一些方法
List集合类型:描述了一种按位置存储数据的对象,是有序的。用的比较多List包括ArrayList和LinkedList,这两者的区别:ArrayList的底层的通过数组实现,所以其随机访问的速度比较快,但是对于需要频繁的增删的情况,效率就比较低了。而对于LinkedList,底层通过链表来实现,所以增删操作比较容易完成,但是对于随机访问的效率比较低。
Queue:一般可以直接使用LinkedList完成,LinkedList继承自Deque,所以LinkedList具有双端队列的功能。PriorityQueue是为每个元素提供一个优先级,优先级高的元素会优先出队列。
Set:Set与List的主要区别是Set是不允许元素是重复的,而List则可以允许元素是重复的。HashSet和LinkedHashSet的区别在于后者可以保证元素插入集合的元素顺序与输出顺序保持一致。而TresSet的区别在于其排序是按照Comparator来进行排序的,默认情况下按照字符的自然顺序进行升序排列。
Iterable:Collection类继承自Iterable,该接口的作用是提供元素遍历的功能,也就是说所有的集合类(除Map相关的类)都提供元素遍历的功能。Iterable里面包含了Iterator的迭代器。
2、Map类型的集合:最大的优点在于其查找效率比较高,理想情况下可以实现O(1)的时间复杂度。Map中最常用的是HashMap,LinkedHashMap与HashMap的区别在于前者能够保证插入集合的元素顺序与输出顺序一致。这两者与TreeMap的区别在于TreeMap是根据键值进行排序的,其底层的实现也有本质的区别,HashMap底层是一个哈希表,而TreeMap的底层数据结构是一棵树。
二、Java内存区域划分
1.程序计数器: 可以看做是当前线程所执行的字节码的行号指示器。在JVM的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。 每条线程都有一个独立的程序计数器,所以程序计数器是线程私有的内存区域。 如果线程执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果线程执行的是一个Native方法,计数器的值为空。 Java虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。
2.Java虚拟机栈: 描述Java方法执行的内存模型,每个方法执行的同时会创建一个栈帧,栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。Java虚拟机栈是线程私有的,它的生命周期与线程相同。 局部变量表存放了编译时期可知的各种基本数据类型和对象引用。局部变量表所需的内存空间在编译时期完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。 Java虚拟机规范对这个区域规定了两种异常情况: 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常; 如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常;
3.本地方法栈: 本地方法栈与虚拟机栈的区别:虚拟机栈为虚拟机执行Java方法服务(也就是字节码),而本地方法栈为虚拟机使用到的Native方法服务。 Java虚拟机规范对这个区域规定了两种异常情况:StackOverflowError和OutOfMemoryError异常。
4.Java堆: Java堆是被所有的线程共享的一块内存区域,在虚拟机启动时创建。Java堆的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 Java堆是垃圾回收器管理的主要区域,从内存回收的角度看,由于现在收集器基本都采用分代收集算法,所以Java堆可以细分为:新生代、老生代;从内存分配的角度看,线程共享的Java堆可能划分出多个线程私有的分配缓冲区(TLAB)。 Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。 Java虚拟机规范规定,如果在堆上没有内存完成实例分配,并且堆上也无法再扩展时,将会抛出OutOfMemoryError异常。 Java堆内存的OOM异常: 内存泄露:指程序中一些对象不会被GC所回收,它始终占用内存,即被分配的对象引用链可达但已无用。 内存溢出:程序运行过程中无法申请到足够的内存而导致的一种错误。内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。
5.方法区: 被所有的线程共享的一块内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 不需要连续的内存和可以选择固定大小或者可扩展之外,还可以选择不实现垃圾回收。 Java虚拟机规范规定,当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常 - 请介绍一下List和ArrayList的区别
List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。它继承Collection。List有两个重要的实现类:ArrayList和LinkedList。
ArrayList:我们可以将其看作是能够自动增长容量的数组。
1.ArrayList底层采用数组实现,当使用不带参数的构造方法生成ArrayList对象时,实际上会在底层生成一个长度为10的Object类型数组。
2.如果增加的元素个数超过了10个,那么ArrayList底层会新生成一个数组,长度为原数组的1.5倍+1,然后将原数组的内容复制到新数组当中,并且后续增加的内容都会放到新数组当中。当新数组无法容纳增加的元素时,重复该过程。
3.对于ArrayList元素的删除操作,需要将被删除元素的后续元素向前移动,代价比较高。
4.集合当中只能放置对象的引用,无法放置原生数据类型,我们需要使用原生数据类型的包装类才能加入到集合当中。
5.集合当中放置的都是Object类型,因此取出来的也是Object类型,那么必须要使用强制类型转换将其转换为真正的类型(放置进去的类型) -
请问有哪几种垃圾回收算法
垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。 引用:如果Reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。
(1)强引用(Strong Reference):如“Object obj = new Object()”,这类引用是Java程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
(2)软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2之后提供了SoftReference类来实现软引用。
(3)弱引用(Weak Reference):它也是用来描述非须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
(4)虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。
垃圾:无任何对象引用的对象。
判断对象是否是垃圾的算法: 引用计数算法(Reference Counting Collector)、根搜索算法(Tracing Collector)
回收:清理“垃圾”占用的内存空间而非对象本身。
Tracing算法(Tracing Collector) 标记—清除算法:分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的根搜索算法中判定垃圾对象的标记过程。
Compacting算法(Compacting Collector)标记—整理算法:标记的过程与标记—清除算法中的标记过程一样,但对标记后出的垃圾对象的处理情况有所不同,它不是直接对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。
Copying算法(Copying Collector):将内存按容量分为大小相等的两块,每次只使用其中的一块(对象面),当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面(空闲面),然后再把已使用过的内存空间一次清理掉。
Adaptive算法(Adaptive Collector):监控当前堆的使用情况,并将选择适当算法的垃圾收集器。 发生地点:一般发生在堆内存中,因为大部分的对象都储存在堆内存中。
(堆内存为了配合垃圾回收有什么不同区域划分,各区域有什么不同?)
Java的堆内存基于Generation算法(Generational Collector)划分为新生代、年老代和持久代。新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace(Survivor0)和ToSpace(Survivor1)组成。所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。分代收集基于这样一个事实:不同的对象的生命周期是不一样的。因此,可以将不同生命周期的对象分代,不同的代采取不同的回收算法进行垃圾回收(GC),以便提高回收效率。
按执行机制划分Java有四种类型的垃圾回收器:
(1)串行垃圾回收器(Serial Garbage Collector)
(2)并行垃圾回收器(Parallel Garbage Collector)
(3)并发标记扫描垃圾回收器(CMS Garbage Collector)
(4)G1垃圾回收器(G1 Garbage Collector) 发生时间:程序空闲时间不定时回收。 - 请你说一下Java的内存结构是什么,全局变量、临时变量、静态变量分别存在哪里,堆分为哪几块,比如说新生代老生代,那么新生代又分为什么
代码区:是编译器生成的一个exe区段,存放函数体的二进制代码
栈区:存放函数的参数,局部变量的值等,其操作方式类似于数据结构中的栈,const局部变量也是放在栈里
堆区:就是malloc和new之类的内存所在区段,一般由程序员分配释放,分配方式类似于链表
静态数据区:是编译器生成的一个exe区段,初始和未初始化的全局变量和局部变量都放在这里,
常量区:是编译器生成的一个exe区段,const全局变量也放在常量区。
全局变量,临时变量,静态变量分别存在哪里?
局部变量保存在栈中,全局变量和静态变量存储在静态数据区。
堆分为哪几块,比如说新生代老生代,那么新生代又分为什么?
java垃圾收集管理器的主要区域,因此很多时候被称为“GC堆”。 分为新生代和老年代; 新生代分为:Eden和Survivor。 - 请说明Java的接口和C++的虚类的相同和不同处。
参考答案:
由于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。
与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。
网友答案:相同之处:
都不能实例化。不相同之处:
1.一个子类只能继承一个抽象类(虚类),但能实现多个接口
2.一个抽象类可以有构造方法,接口没有构造方法
3.一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有方法声明
4.一个抽象类可以是public、protected,接口只有public
5.一个抽象类中的方法可以是public、private、protected、default,接口中的方法只能是public和default修饰,实际上都是public的abstract方法
补充:
接口是一类特殊的抽象类,是更抽象的抽象类,你可以这样理解。抽象类是一个不完整的类,接口只定义了一些功能。
- 简单谈谈你对内存泄漏的理解
当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
内存泄漏是造成应用程序OOM的主要原因之一。
我们知道Android系统为每个应用程序分配的内存是有限的,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过系统分配的内存限额,这就造成了内存溢出从而导致应用Crash。
常见的内存泄漏:
1、单例造成的内存泄漏 由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
2、非静态内部类创建静态实例造成的内存泄漏 非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,从而导致Activity的内存资源不能被正常回收。
3、Handler造成的内存泄漏
4、线程造成的内存泄漏 如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。
5、资源未关闭造成的内存泄漏 对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。
6、使用ListView时造成的内存泄漏
7、集合容器中的内存泄露
8、WebView造成的泄露
避免内存泄漏:
1、在涉及使用Context时,对于生命周期比Activity长的对象应该使用Application的Context。
2、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
3、对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。
4、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
5、对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏: 1)将内部类改为静态内部类 2)静态内部类中使用弱引用来引用外部类的成员变量 - Hibernate中SessionFactory和Session是线程安全的吗?这两个线程是否能够共享同一个Session?
SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问。SessionFactory一般只会在启动的时候构建。对于应用程序,最好将SessionFactory通过单例模式进行封装以便于访问。
Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工作单元。Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使用ThreadLocal将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个session。Hibernate 3中SessionFactory的getCurrentSession()方法就可以做到。 - 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”
public class Solution { public String LeftRotateString(String str,int n) { int len = str.length(); if (len == 0) return str; n %= len; str += str; return str.substring(n,n+len); } }
2.16
- AOP的原理是什么?
AOP是指面向切面编程,Spring中的AOP主要是通过JDK的动态代理以及cglib来实现AOP功能。JDK动态代理,只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。;cglib主要针对类实现代理,对是否实现接口无要求。原理是对指定的类生成一个子类,覆盖其中的方法。
https://blog.csdn.net/wyl6019/article/details/80136000 - 请问aop的应用场景有哪些?
AOP是指面向切面编程,在某一个方法之前或者之后做一些额外的操作,比如说日志记录,权限判断,异常统计和事务处理等,可以利用AOP将功能代码从业务逻辑代码中分离出来。 - 请问error和exception有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。
Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。