Java面试常问32

  1. 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?
    答:Java虚拟机是一个可以执行Java字节码的虚拟机进程,Java源文件被编译成能被Java虚拟机执行的字节码文件,
    因为Java被编译为字节码文件,由虚拟机解释执行,关于底层硬件平台的东西都由Java虚拟机掌握,真正的Java代码不需要知道,所以与其无关。

  2. Java接口中声明的变量默认是final的,抽象类可以包含非final变量。

  3. Java接口中的成员函数默认是public的,抽象类中的方法可以使四种权限修饰。

  4. 接口和抽象类都不可以被实例化。

  5. 在多线程os中,进程不是一个可执行的实体。

  6. 创建线程有四种方式:

  7. 继承Thread类

    • 实现Runnable接口
    • 使用Executor框架创建线程池
    • 实现Callable接口
  8. 同步方法和同步代码块的区别?
    答:同步方法默认使用this或当前类的class对象作为锁,
    同步代码块可以选择以什么来加锁,比同步方法更加细颗粒化,我们可以选择只在需要同步的代码段进行加锁,而不是整个方法,同步方法使用synchronize关键字进行加锁,而代码块主要是要修饰需要加锁的代码,用synchronize(object){代码内容进行修饰}。

  9. 在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
    答:监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码 块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引 用相关联。线程在获取锁之前不允许执行同步代码。

  10. 如何确保N个线程可以访问N个资源同时又不导致死锁?
    答:使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

  11. 什么是迭代器(Iterator)?
    答:Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都 包含了可以返回迭代器实例的
    迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直 接调用集合的
    remove(Object Obj)删除,可以通过迭代器的remove()方法删除。
    原因:Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原 来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指 针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异 常。
    所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。
    但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

  12. Iterator和ListIterator的区别是什么?
    答:1. Iterator可用来遍历Set和List集合,但是ListIterator只能用 来遍 历List。

  13. Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。

  14. ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元 素, 替换元素,获取前一个和后一个元素的索引,等等。

  15. 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
    答:一:快速失败(fail—fast)
    在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的结构进行了修改(增加、删除),则会抛出Concurrent Modification Exception。
    原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果结构发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
    注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
    场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
    二:安全失败(fail—safe)
    采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
    原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
    缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
    场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

  16. Comparable和Comparator接口是干什么的?列出它们的区别?
    答:Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。 Comparator位于包java.util下,而Comparable位于包 java.lang下 Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口) 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序, 这里的自然顺序就是实现Comparable接口设定的排序方式。 而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。 可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。 比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。

  17. 什么是Java优先级队列(Priority Queue)?
    答:Java中的优先队列底层是一个小顶堆实现的,堆的结构只是在二叉树的基础上做了一些限制,优先队列是不安全的,没有实现同步,使用迭代器遍历的数据是没有经过排序的,只有当堆中元素一个一个弹出的时候才会在外部形成有序序列。

  18. Enumeration接口和Iterator接口的区别有哪些?
    答:Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。
    而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。iterator允许调用者删除底层集合里边的元素。

  19. finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?
    答:垃圾回收器(garbage collector)决定回收某对象时,就会运行该对象的finalize()方法 但是在Java中很不幸,如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说filalize()可能永远不被执行,显然指望它做收尾工作是靠不住的。 那么finalize()究竟是做什么的呢?它最主要的用途是回收特殊渠道申请的内存。Java程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种JNI(Java Native Interface)调用non-Java程序(C或C++),finalize()的工作就是回收这部分的内存。

  20. 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
    答:不会立即释放对象占用的内存。 如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。

  21. Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?
    答:JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。
    堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
    永久代是用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类,永久代中一般包含:
    类的方法(字节码…)
    类名(Sring对象)
    .class文件读到的常量信息
    class对象相关的对象列表和类型列表 (e.g., 方法对象的array).
    JVM创建的内部对象
    JIT编译器优化用的信息
    虚拟机中的共划分为三个代:
    年轻代(Young Generation)、年老代(Old Generation)和持久代(Permanent
    Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系
    不大。年轻代和年老代的划分是对垃 圾收集影响比较大的。
    年轻代:
    所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生
    命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在
    Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这
    个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了
    的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区
    (Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时
    存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第
    一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,
    Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减
    少被放到年老代的可能。
    年老代:
    在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认
    为年老代中存放的都是一些生命周期较长的对象。
    持久代:
    用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应
    用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持
    久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。

  22. 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?
    答:
    Serial 收集器
    Serial 收集器是历史悠久,最基本的收集器。它是一个单线程的收集器(说明:这里的单线程不仅仅是指收集器工作时使用一个CPU或者一条收集线程去收集,并且Serial工作时,必须暂停其他所有的工作线程,也就是“stop the world”,直到垃圾收集完成。)Serial是JVM运行在Client模式默认的新生代收集器。
    throughput收集器
    也叫做Parallel Scavenge 收集器,它的目标是达到一个可控制的吞吐量(throughput),(说明:吞吐量就是CPU用于执行代码的时间和CPU总共消耗时间的比值,即:吞吐量 = 运行代码时间 / (运行代码时间 + 垃圾收集器工作时间)),JVM提供了两个参数以精确的控制吞吐量,-XX:MaxGCPauseMillis 最大收集停顿时间;-XX:GCTimeRatio 垃圾收集时间占总时间的比例。
    总结
    串行收集器在GC时会停止其他所有工作线程(stop-the-world),CPU利用率是最高的,所以适用于要求高吞吐量(throughput)的应用,但停顿时间(pause time)会比较长,所以对web应用来说就不适合,因为这意味着用户等待时间会加长。而并行收集器可以理解是多线程串行收集,在串行收集基础上采用多线程方式进行GC,很好的弥补了串行收集的不足,可以大幅缩短停顿时间,因此对于空间不大的区域(如young generation),采用并行收集器停顿时间很短,回收效率高,适合高频率执行。

  23. JVM的永久代中会发生垃圾回收么?

答:永生代也是可以回收的,条件是 :
1.该类的实例都被回收。
 2.加载该类的classLoader已经被回收
 3.该类不能通过反射访问到其方法,而且该类的java.lang.class没有被引用
 当满足这3个条件时,是可以回收,但回不回收还得看jvm。
  1. 异常处理完成以后,Exception对象会发生什么变化?
异常处理对象在异常处理完后,没有引用指向它,变成了不可达对象.				       					  	它将在接下来JVM进行gc操作时被标记为"不可达", 如果该Exception实例实现了finalize方法,那么就会安排到F-queue队列中等待执行finalize方法(但是由于F-queue所在线程的优先级很低,所以可能一直得不到执行,而长时间留在该队列中);
    再下一次执行gc时,如果Exception对象已经执行完成finalize方法,它将被回收(彻底抹去内存中的数据).
  1. 解释一下Applet的生命周期?
applet可以经历下面的状态:
Init:每次被载入的时候都会被初始化。
Start:开始执行applet。
Stop:结束执行applet。
Destroy:卸载applet之前,做最后的清理工作。
  1. Applet和普通的Java应用程序有什么区别?
答:applet是运行在启用了java的浏览器中,Java应用程序是可以在浏览器之外运行的独立的Java程序。但是,它们都需要有Java虚拟机。
进一步来说,Java应用程序需要一个有特定方法签名的main函数来开始执行。Java applet不需要这样的函数来开始执行。
最后,Java applet一般会使用很严格的安全策略,Java应用一般使用比较宽松的安全策略。
  1. 什么是JDBC?
答:JDBC是Java用于和数据库互联的一个东西,这个东西就是一个规范,具体体现为接口及接口之间的作用,对于Java来说,一个JavaAPI封装的东西是有限的,但是由于数据库有很多的厂商,他们的实现肯定有很多的不同,这就像Java对于操作系统一样,所以Java就自己封装了一套规范,由各个厂家去实现。
  1. try-catch-finally中有关return返回值的表述
答:在三个语句中如果都有异常,则无论是否出现异常,都会以finally语句块中的return语句作为返回值。
@SuppressWarnings("all")
public class Test {
	public static void main(String[] args) {
		System.out.println(fd(2));;
	}
	public static int fd(int a) {
		int b=0;
		try {
			b=10/a;	
			return b;
		}catch(Exception e) {
			e.printStackTrace();
			return 2;
		}finally {
			b+=2;
//			return b;
		}
	}
}


最终的打印结果是5,但是在debug时,程序执行了finally中的值,b变为了7,也就是说,在try中执行return语句,并不是立即返回,而是将这个值复制保存一份,接着执行finally中的语句,
如果finally中含有return语句,则直接执行finally中的return语句,结束,
如果没有return语句,则执行完finally语句块返回执行try中的return,
返回的是之前保存的那一份数据,并不会因为finally而改变。
  1. PreparedStatement比Statement有什么优势?
答:PreparedStatements是预编译的,PreparedStatements实例包涵已编译的sql语句,所以其执行呢速度要快于statement对象。
PreparedStatements作为statement的子类,继承了statement的所有方法,三种方法,excute,excuteQuery和excuteUpdate已被更改以使之不再需要参数。
PreparedStatements的代码可读性,可维护性强于statement.
防止SQL注入,极大的提高了安全性
  1. 什么是Servlet?
servlet是基于java语言的web服务器端编程技术,是sun提供的一种实现动态网页的解决方案。servlet是运行在servlet容器中的java类,它能处理客户端的http请求并产生http响应。
  1. GenericServlet和HttpServlet有什么区别?
答:
1.HttpServlet
1). 是一个 Servlet, 继承自 GenericServlet. 针对于 HTTP 协议所定制.
2). 在 service() 方法中直接把 ServletReuqest 和 ServletResponse 转为 HttpServletRequest 和 HttpServletResponse.
并调用了重载的 service(HttpServletRequest, HttpServletResponse)
在 service(HttpServletRequest, HttpServletResponse) 获取了请求方式: request.getMethod(). 根据请求方式有创建了
doXxx() 方法(xxx 为具体的请求方式, 比如 doGet, doPost)
3). 实际开发中, 直接继承 HttpServlet, 并根据请求方式复写 doXxx() 方法即可.
4). 好处: 直接由针对性的覆盖 doXxx() 方法; 直接使用 HttpServletRequest 和 HttpServletResponse, 不再需要强转.
 
2.GenericServlet
1). 是一个 Serlvet. 是 Servlet 接口和 ServletConfig 接口的实现类. 但是一个抽象类. 其中的 service 方法为抽象方法
2). 如果新建的 Servlet 程序直接继承 GenericSerlvet 会使开发更简洁.
3). 具体实现:
①. 在 GenericServlet 中声明了一个 SerlvetConfig 类型的成员变量, 在 init(ServletConfig) 方法中对其进行了初始化 
②. 利用 servletConfig 成员变量的方法实现了 ServletConfig 接口的方法
③. 还定义了一个 init() 方法, 在 init(SerlvetConfig) 方法中对其进行调用, 子类可以直接覆盖 init() 在其中实现对 Servlet 的初始化. 
④. 不建议直接覆盖 init(ServletConfig), 因为如果忘记编写 super.init(config); 而还是用了 SerlvetConfig 接口的方法,
则会出现空指针异常. 
⑤. 新建的 init(){} 并非 Serlvet 的生命周期方法. 而 init(ServletConfig) 是生命周期相关的方法.
  1. Servlet的生命周期
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。 
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
  1. get和post方法比较?
    • get是从服务器上获取数据,post是向服务器传送数据。
    • get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。
    • 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。
    • get传送的数据量较小,不能大于2KB(受限于浏览器)。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
    • get安全性非常低,post安全性较高。但是执行效率却比Post方法好。
      建议: 1、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式; 2、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式;

什么是web应用程序?

答:Web应用程序是一种可以通过Web访问的应用程序。Web应用程序的一个最大好处是用户很容易访问应用程序。用户只需要有浏览器即可,不需要再安装其他软件。一个Web应用程序是由完成特定任务的各种Web组件(web components)构成的并通过Web将服务展示给外界。在实际应用中,Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。所有这些组件相互协调为用户提供一组完整的服务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值