1. String 和StringBuilder 、StringBuffer 的区别
String 对象是不可变的(immutable) ,类中每一个修改String 值的方法,都是创建了一个全新的String 对象。StringBuilder、StringBuffer 支持可变的字符串,StringBuffer 是线程安全的。字符串串联(+ )是通过 StringBuilder (或 StringBuffer )类及其 append() 方法实现的,在动态构造字符数据(循环方式)时应使用 StringBuilder (或StringBuffer )。
2 . Collection 和 Collections 的区别 。
Collection 是集合结构的根接口,继承它的接口主要有Set 和List 。
Collections 是集合类的一个帮助类,提供了一系列的静态方法,实现对各种集合的搜索、排序、同步等操作。
3. Error 、Exception 、RuntimeException 有何异同?列举常见的RuntimeException 、Error 。
Error 、Exception 继承自所有异常的超类Throwable ,RuntimeException 是Exception 的一个特殊子类。Error 和RuntimeException 是未受检异常,在程序中不需要也不应该被捕获。按照惯例,Error 被JVM 保留用于表示资源不足、约束失败,或其他严重错误;RuntimeException 表明编程错误。Exception 表示程序运行过程中可能出现的非正常状态,是受检异常。对可恢复的情况应使用受检异常,对编程错误使用RuntimeException 。
常见RuntimeException :NullPointerException 、IllegalArgumentException 、IllegalStateException 、IndexOutOfBoundsException 、UnsupportedOperationException 、 NoSuchElementException 、ClassCastException 、BufferOverflowException 。
常见Error :NoClassDefFoundError 、UnsatisfiedLinkError 、OutOfMemoryError 、StackOverflowError 、UnknownError 。
4. final, finally, finalize 的区别。
final 用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally 是异常处理语句结构的一部分,表示总是执行。
finalize 是Object 类的一个方法,在垃圾收集器执行时会调用被回收对象的此方法。
5. Overload 和Override 的区别。Overload 的方法是否可以改变返回值的类型?
如果子类中定义的方法与父类有相同的名称、参数和返回类型( 自Java 5 开始支持协变返回类型) ,称覆盖(Override) 了父类方法,子类的对象使用这个方法时,将调用子类中的定义。
如果在一个类中定义了多个同名的方法,它们有不同的参数个数或参数类型,则称为方法的重载(Overload) 。
Overload 的方法可以改变返回值的类型,但不能以返回值区分重载方法。
6. (&,|) 与 (&&,||) 的区别。
(&,| )是位操作符,(&&,||) 是逻辑操作符。
逻辑操作只能应用于布尔型, 而位操作可以应用于布尔型和整型;对于布尔型位操作符与逻辑操作符效果相同,但位操作不会产生短路
7. List 、Set 、Map 三者的异同。
List 、Set 继承自Collection 接口,保存单一的元素。List 以特定的顺序保存一组元素,可有重复元素。Set 不保证迭代顺序,不能有重复元素,元素必须定义equals() 方法。Map 保存key-value 值,Map 不能包含重复的键,每个键最多只能映射一个值,键必须定义equals() 方法。
8. ArrayList 、LinkedList 、CopyOnWriteArrayList 的存储性能和特性。
三者均按照插入顺序保存元素。ArrayList 使用数组方式存储数据,随机访问速度快,插入和移除元素较慢。LinkedList 使用双向链表实现存储,插入和移除元素快,随机访问较慢,另外LinkedList 可用作堆栈、队列或双端队列。CopyOnWriteArrayList 是ArrayLis 线程安全的变体,其中所有可变操作都是通过对数组进行复制来实现的,只对修改操作加锁,因此修改和读取操作可同时发生。
Vector 也是线程安全的List ,是Java 早期版本的实现,在新程序中不应该使用。Collections 类提供了同步的静态方法,可以用来同步不同类型的容器,对修改和读取操作都会进行同步。在没有修改操作或有少量的修改操作时,CopyOnWriteArrayList 的速度更快,因此应该尽量使用CopyOnWriteArrayList 。
9. HashSet 、LinkedHashSet 、TreeSet 、CopyOnWriteArraySet 的区别。
HashSet 基于HashMap 实现,查找速度最快,元素必须定义hashCode() 和equals() 方法。
LinkedHashSet 继承自HashSet ,基于LinkedHashMap 实现,按照插入顺序保存对象,具有HashSet 的查询速度,对于插入操作LinkedHashSet 比HashSet 代价更高。元素必须定义hashCode() 和equals() 方法。
TreeSet 基于TreeMap 实现,元素按照升序排序, 或者根据创建时提供的Comparator 进行排序。元素必须实现Comparable 接口。
CopyOnWriteArraySet 是线程安全的,是使用CopyOnWriteArrayList 实现的。
10. HashMap 、LinkedHashMap 、TreeMap 、WeakHashMap、ConcurrentHashMap 的区别。
HashMap 是Map 基于散列表的实现,对速度进行了优化,查询速度最快,如没有其他的限制,应成为默认的选择。HashMap 键必须定义hashCode() 和equals() 方法。HashMap 允许null 值和null 键。
LinkedHashMap 是HashMap 的子类,基于散列表和双向链表实现,默认按照插入顺序保存对象,允许null 元素。如果在LinkedHashMap 的构造方法中指定排序模式为true, 则迭代顺序为最近访问的顺序,很适合用来构建LRU 缓存。由于LinkedHashMap 增加了维护链表的开支,其性能稍逊于HashMap ,但迭代访问时反而更快。
TreeMap 基于红黑树实现,按照键值升序保存对象,键必须实现Comparable 接口,也可以在构造器中指定Comparator 。TreeMap 键值不允许为null 。
WeakHashMap弱键映射,允许垃圾回收器自动清理键和值。映射会自动使用WeakReference包装添加的键和值。
ConcurrentHashMap 是线程安全的,不涉及synchronized ,仅对修改操作加锁,允许并发的读取和写入,性能有了很大的提升。ConcurrentHashMap 依靠分段策略,每段单独同步;get() 方法未使用锁同步,而是使用轻量级同步volatile 原语sun.misc.Unsafe.getObjectVolatile(Object, long) 来保证读到的是最新的对象。ConcurrentHashMap 不允许null 键或null 值。(更多信息请参考http://hill007299.iteye.com/blog/1490779 )
Hashtable 是Map 的早期实现版本,是线程安全的,在新程序中不应使用。
11. abstract 的method 是否可同时是static, 是否可同时是native ,是否可同时是synchronized?
都不能。
抽象的方法是要被子类实现的,而static 与子类无关。
native 表示该方法要用另外一种依赖平台的编程语言实现的,不存在被子类实现的问题,所以它不能是抽象的。
synchronized 作用在一个具体的方法上才有意义。
12. Nested Class 和 Inner Class 的不同。
Nested Class 是被声明为静态的内部类,要实例化嵌套类,并不需要外围类的对象;不能从嵌套类的对象中访问非静态的外围类对象。而普通的内部类需要在外部类实例化后才能实例化,并且不能有static 数据或字段,也不能包含嵌套类。
13. abstract class 和interface 有什么区别?
含有 abstract 修饰符的类即为抽象类, abstract 类不能创建实例对象。含有 abstract 方法的类必须定义为 abstract class , abstract class 类中的方法不必是抽象的。 abstract class 类中定义的抽象方法必须在具体 (Concrete) 子类中实现,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为 abstract 类型。
接口( interface )是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为 public abstract 类型,接口中的成员变量默认为 public static final 类型。
抽象类允许包含某些方法的实现,而接口则是完全抽象的类,不能实现默认的行为。抽象类中的抽象方法的访问类型可以是public 、protected ,但接口中的抽象方法只能是public 类型的。Java 只允许单继承,但一个类可以实现多个接口。接口只用于定义类型,抽象类常用作模板。接口灵活性好,抽象类演变的容易性好。
14. 描述一下 JVM 加载class 文件的原理机制 ?
类加载是由 ClassLoder 和其子类实现的 , 类加载器负责查找和装入类文件 。
数组类的Class 对象不是由类加载器创建的,而是由Java 运行时根据需要自动创建。数组类的类加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器。
JVM 在运行时会产生三个 ClassLoader : Bootstrap class loader ( C++ 实现,不是 ClassLoder 的子类) - 负责加载 Jre 的核心类库( rt.jar,charsets.jar 等), ExtClassLoader- 负责加载 JRE ext 目录下的扩展类, AppClassLoader- 负责加载应用类 。 这三个类装载器存在父子层级关系,即根装载器是 ExtClassLoader 的父装载器, ExtClassLoader 是 AppClassLoader 的父装载器。
Java 类加载使用“全盘负责委托机制”。“全盘负责”是指当一个 ClassLoder 装载一个类时,除非显示的使用另外一个 ClassLoder ,该类所依赖及引用的类也由这个 ClassLoder 载入 。 “委托机制”指 ClassLoader 实例在亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器 。
类装载步骤:
1 )装载:查找和导入 Class 文件
2 )链接:
( a )校验:检查载入的 class 是否有良好的格式,是否有一份正确的符号表,还会检查是否符合 Java 语言和 Java 虚拟机的语义要求。
( b )准备:涉及分配静态存储区和 JVM 内部使用的任何数据结构,比如方法表。
( c )解析(可选的):加载引用的其他类和接口,检查到其他类和接口的符号引用是否正确。
3 )初始化:对静态变量、静态代码块执行初始化工作
15. 写clone() 方法时,通常都有一行代码 ,是什么?
super.clone() ,类似于构造器调用链,确保Object 的clone() 方法被调用。Object 的clone() 方法负责产生正确大小的空间,并逐位复制,创建正确的对象类型,此方法执行的是“浅表复制”,而不“深层复制”操作。
16. 什么是java 序列化,如何实现java 序列化?
Java 序列化将实现Serializable 接口的对象转换成一个字节序列,并能在以后将这个字节序列完全恢复为原来的对象。这一过程也可通过网络进行。序列化实现了对象的轻量级持久化。
序列化的实现:将需要被序列化的类实现Serializable 接口,该接口没有需要实现的方法,实现 Serializable 只是为了标注该对象是可被序列化的。然后使用一个OutputStream ( 如FileOutputStream) 来构造一个 ObjectOutputStream 对象,接着使用ObjectOutputStream 对象的writeObject() 方法即可将对象序列化。要恢复的话则需要将一个InputStream 封装在ObjectInputStream 内,然后调用readObject() 。
17 . heap 和stack 有什么区别。
Java 的内存分为两类,一类是栈内存,一类是堆内存。
每个JVM 线程有一个私属的栈,当线程启动时创建。当程序调用一个方法时,使用栈来存储局部变量和部分结果;当方法结束时,会释放这些内存。Java 系统必须知道存储在栈内所有项目的确切生命周期,以便执行进栈、出栈操作。对象引用、基本类型数据存储于栈中。
堆是所有JVM 线程共享的内存池,当虚拟机启动时创建,用于存放所有的Java 对象。编译器不需要知道存储在堆里的数据会存活多长时间。堆内存由垃圾回收器自动回收。
18 .Java 垃圾回收的优点和原理 ,考虑2 种回收机制。有什么办法主动通知虚拟机进行垃圾回收?
Java 垃圾回收器负责自动回收无用对象(没有被引用的)占据的内存资源,使得程序员在编写程序时不再需要考虑内存管理。Java 中的对象不再有“作用域”的概念,只有对象引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的利用内存。垃圾回收器通常是作为一个低优先级的后台线程运行,当堆充满或达到临界比例时执行垃圾回改。
回收机制有分代复制式和标记清扫式。Java 虚拟机采用自适应的垃圾回收技术,当所有对象都很稳定,切换到“标记- 清扫”方式;当堆空间出现很多碎片时,会切换回“停止- 复制”式。
可以执行System.gc() ,通知GC 运行,但是Java 语言规范并不保证GC 一定会执行。
19 . 线程有几种实现方法, 都是什么?
在Jdk5 中,线程可以继承Thread 类、实现Runnable 接口或实现 Callable 接口 。Callable 有 返回值并可能抛出异常,必须使用 ExecutorService.submit() 方法调用它 。
第一种:
new Thread() {
public void run() {
}
}.start();
第二种:
new Thread(new Runnable() {
public void run() {
}
}).start();
自 Java5 开始,还有以下创建多线程的方式, Executors 是启动任务的优选方法:
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
exec.execute(new Runnable() {
public void run() {
}
});
}
exec.shutdown();
CachedThreadPool 为每个任务都创建一个线程,是 Executor 的首选,只有当这种方式会引发问题时,才需要切换到 FixedThreadPool 。调用 shutdown() 方法可以防止向 Executor 增加新任务。
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
exec.execute(new Runnable() {
public void run() {
}
});
}
exec.shutdown();
FixedThreadPool 使用有限的线程集来执行所提交的任务,一次性预先执行代价高昂的线程分配,可以节省时间。
ExecutorService exec = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
exec.execute(new Runnable() {
public void run() {
}
});
}
exec.shutdown();
SingleThreadExecutor 相当于线程数量为 1 的 FixedThreadPool ,如果向 SingleThreadExecutor 提交了多个任务,那么这些任务将排队,一个任务结束才能开始新的任务,不需要在共享资源上处理同步。
20 . 同步的实现方法?
同步的实现方法有synchronized 和lock 。
synchronized 与wait 和notify 结合使用。
wait() 是Object 类的方法,调用wait() 后线程被挂起,对象的锁被释放。只有其他线程调用此对象的notify() 或notifyAll( )后,线程才会被唤醒。只能在同步方法或同步控制块内调用wait() 、notify() 和notifyAll() 。
notify() 唤醒在此对象监视器上等待的单个线程,并不能确切的唤醒某一个等待状态的线程,而是由JVM 确定唤醒哪个线程。为了使用notify() ,所有任务必须等待相同的条件,因为如果有多个任务在等待不同的条件,就不知道是否唤醒了恰当的任务。
notityAll() 唤醒在此对象监视器上等待的所有线程。注意并不是给所有唤醒的线程一个对象的锁,而是让它们竞争。
Java SE5 引入了新的lock 机制,调用lock 的newCondition() 方法可以获得Condition 实例,支持多个相关的Condition 对象。使用lock 时,可组合Condition 的await() 、signal() 、signalAll() 方法。
21 . 简述synchronized 和Lock 的异同?
相同点:Lock 能完成synchronized 所实现的所有功能。
不同点:Lock 必须被显示的创建、锁定和释放,且必须在finally 子句中释放锁。Lock 与synchronized 相比,允许更灵活的结构,有更细粒度的控制力,通常情况下也更高效。Synchronized 可读性好。
22 . sleep() 和 wait() 有什么区别?
sleep (Thread.sleep 、Java SE5 新引入的TimeUnit.xxx.sleep )使当前线程暂停执行指定时间,使线程调度器可以切换到其他线程,到时后会自动恢复,sleep 不会释放对象锁。
wait() 是Object 类的方法,调用wait() 后线程被挂起,对象的锁被释放。只有其他线程调用此对象的notify() 或notifyAll( )后,线程才会被唤醒。只能在同步方法或同步控制块内调用wait() 、notify() 和notifyAll() 。
有三种形式的wait():
wait()
导致当前线程等待,直到其他线程调用此对象的notify() 或notifyAll() 方法,wait() 实际上调用了wait(0) 。
wait(long timeout)
导致当前线程等待,直到其他线程调用此对象的notify() 或notifyAll() 方法,或者超过指定的时间。
wait(long timeout, int nanos)
此方法类似于一个参数的wait 方法,能够更好地控制等待时间。 nanos 为附加时间,单位为纳秒。
23 . stop() 、 suspend() 和resume() 方法为何不推荐使用 ?
这三个方法均为Thread 类的过期方法。
stop() 不安全,用stop 来终止线程将释放它已经锁定的所有监视器( 锁) ,如果被这个监视器保护的任何对象处于不一致的状态,其他线程可以在这种状态下浏览和修改它们,这样产生问题时也很难查找。
suspend() 和resume() 用来阻塞和唤醒线程,但容易发生死锁。调用suspend() 不会释放锁,在目标线程重新开始以前,其他任何线程都不能访问锁定的资源。如果恢复目标线程的线程想在调用resume 前锁定该监视器,则会发生死锁。
24 . 当一个线程进入一个对象的一个synchronized 方法后,其他线程是否可进入此对象的其它方法?
在这个线程从该方法返回之前(方法执行完毕或内部调用了wait ,即锁被释放),其他线程不能进入此对象的任何synchronized 方法,可以进入未加synchronized 关键字的方法。
static 方法用的锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this 。
25 . 线程方法简要说明。
yield() 对线程调度器的一种建议,可以切换执行其他线程,不释放锁。
join() 某个线程在另一个线程t 上调用t.join(), 此线程将被挂起,直到线程t 结束才恢复。
26 . JSP 中动态INCLUDE 与静态INCLUDE 的区别?
动态INCLUDE 用jsp:include 动作实现( <jsp:include page="included.jsp" flush="true" /> ),它总会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数。
静态INCLUDE 用<%@ include file="" %> 编译指令实现,编译时完全导入被包含页面的代码,不会检查所含文件的变化。
27 . JSP 的内置对象。
request 表示HttpServletRequest 对象,它封装了有关浏览器请求的信息。
response 表示HttpServletResponse 对象,代表服务器对客户端的响应,常用于重定向。
out 表示javax.jsp.JspWriter 对象,用于向浏览器输出结果。
page 代表页面本身,相当于servlet 中的this 。
pageContext 表示javax.servlet.jsp.PageContext 对象,代表jsp 页面的上下文。主要用于访问页面共享数据,使用pageContext 可以直接访问request ,session ,application 范围的属性。
session 表示javax.servlet.http.HttpSession 对象,用于跟踪用户会话信息。
applicaton 表示javax.servle.ServletContext 对象,代表web 应用本身。
config 表示javax.servlet.ServletConfig 对象,代表jsp 配置信息。
exception 代表jsp 页面的异常
28 . Servlet 的生命周期。
a) 加载和实例化:当客户端请求或容器启动时加载和实例化
b) 初始化:实例化后容器调用init 方法
c) 响应客户请求:调用service 方法,service 方法自动运行与请求对应的doXXX 方法(doGet ,doPost 等)
d) 销毁:调用servlet 的destroy 方法,通常在关闭servlet 容器时销毁servlet
29. http 请求和响应的组成
请求由三部分组成:
1) 请求方法/URI/ 协议版本
2) 请求头
3) 请求正文
响应由三部分组成
1) 协议/ 状态代码/ 状态描述
2) 响应头
3) 响应正文
30 . XML 文档定义有几种形式?它们之间有何区别?解析XML 文档有哪几种方式?
XML 文档定义有两种形式dtd 和schema 。
它们之间的区别是schema 本身是xml 的,可以被XML 解析器解析。
解析方式有DOM 、SAX 、STAX 等。
DOM 解析文件时必须把整个文档装入内存,形成DOM 的树结构,适合对XML 的随机访问,当处理大型文件时会消耗大量的内存,性能下降明显。
SAX ( Simple API for XML )是事件驱动型的XML 解析方式。它顺序读取XML 文件,不需要一次全部装载整个文件。SAX 是一种推模型分析API, 当遇到像文档开头、文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML 文件,适合对XML 的顺序访问。
STAX(Streaming API for XML) , 是用 Java 语言处理 XML 的最新标准( JSR173 ),在 JDK6 中引入。 StAX 基于一种拉分析模型,分析事件是由应用程序生成的。作为一种面向流的方法,无论从性能还是可用性上都优于 DOM 和 SAX 。
Stax 可以看一下这篇文章 http://anzuo.blog.sohu.com/75909963.html
31 . 名词解释
JNDI :(Java Naming & Directory Interface ) Java 命名和目录接口
JMS :(Java Message Service )Java 消息服务
JTA :(Java Transaction API )提供分布式事务服务,应用程序只需调用其提供的接口( UserTransaction ) 即可。
IOC (inversion of control )控制反转,又称依赖注入(Dependency Injection ,DI )
AOP (Aspect-Oriented Programming )面向方面编程
DBCP ( Database Connection Pool )数据库连接池
CMT (Container-Managed Transaction )容器管理事务
RMI (Remote Method Invocation )远程方法调用
JAXP( Java API for XML Processing ) 定义了在Java 中使用DOM, SAX, XSLT 的通用的接口。这样在程序中只要使用这些通用的接口,当你需要改变具体的实现时候也不需要修改代码。
JAXM(Java API for XML Messaging) 用以通过 XML( 以及 SOAP) 发送和接收消息,支持同步消息和异步消息 。
WSDL( Web Services Description Language ), Web Service 的 XML 描述语言,用于描述 Web Service 的服务、接口绑定等。
SOAP(Simple Object Access Protocol) ,它是用于交换XML 编码信息的轻量级协议。
UDDI( Universal Description, Discovery and Integration ) 通用描述、发现与集成服务,是一种目录服务,可以使用它对 Web services 进行注册和搜索。
32 . Java 实例化对象
A、 new Object () ;
B、 运用reflection 机制
Class.newInstance() (无参数)
Class. getConstructor( new Class[] {}).newInstance( new Object[] {})( 有参数)
“Class” object 的取得途径
a) object.getClass();
b) class.getSuperclass();
c) class.forName(“java.lang.String”);
d) .class ( 如String.class)
e) 包装类的TYPE 语法 如Integer.TYPE;
33. Spring 实例化bean
A、 反射调用类构造器
B、 调用类的静态工厂方法
C、 实例工厂方法
34. 事务特性
a) atomic (原子性)操作全成功或全失败
b) consistency (一致性)只有合法数据可以写入
c) isolation (隔离性)对同一数据的并发访问,不破坏数据的正确性和完整性,事务修改必须与其他并行事务修改相互独立
Dirty reads 脏读取:读取了另一个并行事务未提交的数据
Non-repeatable reads 不可重复读:一个事务再次读取之前曾经读取的数据,但该数据已经被另一个事务修改
Phantom reads 虚读:一个事务重新执行查询,返回一套符合条件的记录,但其中包含了其他事务提交而产生的新记录。
隔离级别
|
Dirty reads 脏读取 |
Non-repeatable 不可重复读 |
phantom 虚读 |
READ UNCOMMITTED |
可能 |
可能 |
可能 |
READ COMMITTED |
不可能 |
可能 |
可能 |
REPEATABLE READ |
不可能 |
不可能 |
可能 |
SERIALIZABLE |
不可能 |
不可能 |
不可能 |
Oracle支持READ-COMMITTED和SERIALIZABLE两个级别, MySQL和SQL Server实现了四种隔离级别。Oracle和SQL Server默认为READ-COMMITTED,MySQL默认为REPEATABLE-READ。
d) durability (持久性)
35 . Tomcat 的优化。
1 )外部环境调优
优化 JVM 内存:设置合适的堆大小,对于 SUN 和 HP 等虚拟机,推荐将最小堆和最大堆的大小设置为同一值,这样可以避免经常调整堆大小浪费虚拟机资源,如 JAVA_OPTS="-Xms256m -Xmx256m" 。
操作系统优化,调整对 Tomcat 有影响的参数。
与其他 WEB 服务器整合: Tomcat 处理静态页面的性能远比不上 Apache 等,把 Tomcat 和 Apache 集成起来,由 Apache 处理静态页面,这样整体性能会有很大的提升。
2 ) Tomcat 自身优化
禁用 Connector 的 DNS 查询, enableLookups="false" (默认值)。
调整 Connector 的线程数:最大线程数 maxThreads 、当所有可以使用的处理请求线程都被占用时,可以放到处理队列中的请求数 acceptCount 、 minSpareThreads 等。
使用 APR 。
提前将 JSP 编译为 Servlet 。
36 . 介绍一下常用的设计模式及设计模式的分类。
Adapter (适配器模式), Bridge (桥接模式), Builder (生成器模式),
Chain Of Responsibleity (责任链模式), Command (命令模式),
Composite (组合模式), Decorator (装饰者模式), Facade (外观模式),
Factory (工厂模式), Flyweight (享元模式), Interpreter (解释器模式),
Iterator (迭代器模式), Mediator (中介者模式), Memento (备忘录模式),
Observer (观察者模式), Prototype (原型模式), Proxy (代理模式),
Singleton (单例模式), State (状态模式), Strategy (策略模式),
Template Method (模板方法模式),Visitor (访问者模式)
根据模式目标分成三类:创建型、行为型、结构型
创建型:涉及到将对象实例化,将客户从所需要实例化的对象中解偶。
Singleton 、Abstract Factory 、Factory Method 、Builder 、Prototype
行为型:涉及到类和对象如何交互及分配职责。
Template Method 、Command 、Observer 、Iterator 、State 、Strategy 、Visitor 、Mediator 、Interpreter 、Memento 、Chain of Responsibilty
结构型:把类或对象组合到更大的结构中。
Decorator 、Composite 、Proxy 、Facade 、Adapter 、Flyweight 、Bridge
另一种分类方式:类模式,对象模式
类模式:描述类之间的关系如何通过继承定义。类模式的关系是在编译时建立的。
Template Method 、Factory Method 、Adapter 、Interpreter
对象模式:描述对象之间的关系,而且主要是利用组合定义。对象模式的关系通常在运行时建立,而且更加动态、更有弹性。
Composite 、Decorator 、Iterator 、Command 、Proxy 、Façade 、Strategy 、Observer 、State 、Prototype 、Abstract Factory 、Singleton 、Visitor 、Memento 、Chain of Responsibility 、Bridge 、Mediator 、Flyweight 、Builder
37 . 介绍一下JavaEE 。
JavaEE(Java Platform, Enterprise Edition) 是开发企业和Web 应用的平台。JavaEE 代表了企业信息技术的一个标准,可以方便地开发、部署和管理企业应用。JavaEE 6 精简了过期的技术(EJB 实体bean 、Java API for XML Registries (JAXR) 等),引入了Profile ,更具灵活性,能更好地处理轻量级Web 应用。
38 . 写一个函数要求输入一个字符串和一个字符长度,对该字符串进行分隔,并返回分割结果。
public String[] split(String str, int len) {
int n = (str.length() - 1 + len) / len;
String ret[] = new String[n];
for ( int i = 0; i < n; i++) {
if (i < n - 1) {
ret[i] = str.substring(i * len, (i + 1) * len);
} else {
ret[i] = str.substring(i * len);
}
}
return ret;
}
39 . 写出如下代码的运行结果。
public static void main(String[] args) {
String s0 = "kvill" ;
String s1 = "kvill" ;
String s2 = "kv" + "ill" ;
System. out .println(s0 == s1);
System. out .println(s0 == s2);
String s3 = "kvill" ;
String s4 = new String( "kvill" );
String s5 = "kv" + new String( "ill" );
System. out .println(s3 == s4);
System. out .println(s3 == s5);
System. out .println(s4 == s5);
}
结果为:
true
true
false
false
false
40 . char 型变量中能不能存贮一个中文汉字 ? 为什么 ?
char 型变量是用来存储 Unicode 编码的字符的, unicode 编码字符集中包含了汉字,所以 char 型变量中可以存储汉字。不过,如果某个特殊的汉字没有包含在 unicode 编码字符集中,那么这个 char 型变量中就不能存储这个特殊汉字。补充说明: unicode 编码占用两个字节,所以 char 类型的变量也是占用两个字节。
41. 用最有效率的方法算出 2 乘以 8 等於几 ?
2 << 3
因为将一个数左移 n 位,就相当于乘以了 2 的 n 次方,那么一个数乘以 8 只要将其左移 3 位即可,而位运算是 cpu 直接支持的,效率最高。
42. 下面代码的输出结果是什么?
import java.util.Date;
public class Test extends Date{
public static void main(String[] args) {
new Test().test();
}
public void test(){
System.out .println(super .getClass().getName());
}
}
结果为 Test 。
由于 Object 类中的 getClass() 方法定义为 final 的,子类不能覆盖该方法,所以在 test 方法中调用 getClass().getName() 方法,其实就是在调用从父类继承的 getClass() 方法,等效于调用 super.getClass().getName() 方法,所以 super.getClass().getName() 方法返回的也是 Test 。
如果想得到父类的名称,应该用如下代码:
getClass().getSuperClass().getName();
43 . 写出下面代码的输出结果。
public static void main(String[] args) {
System.out.println(new Test().test());
}
static int test() {
int x = 1;
try {
return x;
} finally {
++x;
System.out.println(x);
}
}
结果为:
2
1
44. 输出字符串中重复字符的个数
public static String duplicateChar(String arg) {
if (arg == null || arg.length() == 0) {
throw new IllegalArgumentException();
}
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0; i < arg.length(); i++) {
String c = String.valueOf(arg.charAt(i));
if (map.containsKey(c)) {
int num = map.get(c);
map.put(c, ++num);
} else {
map.put(c, 1);
}
}
StringBuffer result = new StringBuffer();
for (String key : map.keySet()) {
int count = map.get(key);
if (count > 1) {
result.append(key + ": " + count + "\n");
}
}
return result.toString();
}
45. 如果一个字符串正着读和反着读,字母顺序是一样的,那么就称这种字符串是对称的。比如,neveroddoreven。如果一个字符串包含了一个对称字符串的所有字母(不考察字母顺序),那么这种字符串称作可对称化的字符串。比如,ondervedenvor。要求:写一个方法,输入参数为字符串,输出true或false,代表该字符串是否为可对称化字符串。字符为a~z。
public static boolean symmetry(String str) {
if (str == null) {
return true;
}
int len = str.length();
if (len <= 1) {
return true;
}
char[] ch = str.toCharArray();
int check[] = new int[26];
for (int i = 0; i < len; i++) {
check[ch[i] - 'a']++;
}
int sum = 0;
for (int i = 0; i < 26; i++) {
if (check[i] % 2 == 1) {
sum++;
}
}
if (sum > 1) {
return false;
}
return true;
}
46. 一个人爬n阶的楼梯,一次可以跨一阶,也可以跨两阶,最多一次可以跨三阶,求总共有多少种走法?请写出程序,打印出所有可能的走法。
问题分析
如果只有1阶楼梯,只有一种走法;如果有2阶楼梯,可以每次跨一阶,或一次跨两阶,有两种走法;如果有3阶楼梯,可以一阶一阶地上,也可以先跨两阶+后跨一阶,也能先跨一阶+后跨两阶,还可以一次跨三阶,有 4 种走法。
如果有n阶楼梯,第一步可以跨一阶楼梯,那么有F(n-1)种走法;如果第一步跨2阶楼梯,那么有F(n-2)种走法;如果第一步跨3阶楼梯,那么有F(n-3)种走法。
故:F(n) = F(n - 1) + F(n - 2) + F(n - 3),很明显这是一个递归问题。
程序实现
public class Lift{
public static void main(String[] args) {
getMethodCount(new LinkedList<Integer>(), 4, 3);
}
public static void getMethodCount(LinkedList<Integer> stack, int total, int maxStep) {
for (int j = 1; j <= maxStep; j++) {
stack.push(j);
if (total == j) {
System.out.println(stack);
stack.pop();
break;
} else {
getMethodCount(stack, total - j, maxStep);
stack.pop();
}
}
}
}
47. 求一个数组中最大数和第二大数。
public static void GetSecondMaxNumber(int[] arr) {
int max = arr[0];
int second = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
second = max;
max = arr[i];
} else {
if (arr[i] > second) {
second = arr[i];
}
}
}
System.out.println(max + ";" + second);
}
48. 求两个集合的交集。
可参考apache的CollectionUtils.intersection(a, b)。
49. 求两个二叉树节点的最近公共父节点。
http://eriol.iteye.com/blog/1170465
50. 有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,至少要多少只小白鼠才能在24小时鉴别出哪瓶水有毒?
需要10只。
方法:给每个瓶子按二进制编号,如:
0000000001 (第1瓶)
0000000010 (第2瓶)
0000000011 (第3瓶)
......
1111101000 (第1000瓶)
从编号最后1位是1的瓶子里各取出一滴混合,依次直到将第一位是1的瓶子中的水取出混合。然后让小白鼠喝混合后的水,24小时后,挂了的代表1,未挂的代表0,组合后的编号即为有毒瓶子的编号。
51. 分布式缓存的Hash 算法
1) 根据余数计算分布 Hash(key) mod n
求得键的整数哈希值,再除以服务器台数,根据其余数来选择服务器。
根据余数计算分散的缺点:
余数计算的方法简单,数据的分散性也相当优秀,但当添加或移除服务器时,缓存重组的代价相当巨大。添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而影响缓存的命中率。
2 )一致性哈希算法
一致性哈希 将整个哈希值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织。
下一步将各个服务器使用哈希函数H 计算哈希值,具体可以选择服务器的ip 或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。
将数据key 使用相同的函数H 计算出哈希值h ,根据h 确定在环上的位置,从此位置沿环顺时针“行走”,遇到的第一台服务器就是其应该定位到的服务器。
当添加或移除服务器时,仅在这台服务器到前一台服务器(逆时针方向)间的数据受影响。具有较好的容错性和可扩展性。
3 )虚拟节点
一致性哈希算法当服务节点太少时, 容易因为节点分部不均匀而造成数据倾斜问题。
为解决数据倾斜问题引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可在服务器ip 或主机名的后面增加编号来实现。数据定位算法不变,只是多了一步虚拟节点到实际节点的映射。
可参考http://www.codinglabs.org/html/consistent-hashing.html
52. CAP 与BASE
为了保证数据完整性,大多数经典数据库系统都是以事务为基础的。这全方位保证了数据管理中数据的一致性。这些事务特性被称为ACID (A 原子性、C 一致性、I 隔离性、D 持久性)。然而,ACID 兼容系统的向外扩展已成为一个问题。
CAP :
C: Consistency 一致性,所有客户端看到的数据是同一个版本。
A: Availability 可用性, 所有客户端总能找到所请求数据的至少一个版本,即使集群中某些机器已经宕机。
P: Tolerance of network Partition 分区容忍性,即使是被部署到不同服务器上,整个系统也保持自己的特征。
在分布式系统中不可能同时满足一致性、可用性和分区容错性这三个需求,最多只能同时满足两个。
很多NOSQL 数据库放宽了对于一致性(C )的要求,以期得到更好的可用性(A )和分区容忍性(P )。这产生了被称为BASE 的系统。它们没有经典意义上的事务,并且在数据模型上引入了约束,以支持更好的分区模式。
BASE :
Basically Availble-- 基本可用性
Soft-state-- 软状态/ 柔性事务
Eventual Consistency-- 最终一致性
53.平面中的一个圆,切一刀后最多可以变成2瓣,切两刀最多变成4瓣,请问切100刀后最多变成多少瓣。
切 1 刀 分为 2 块
切 2 刀 分为 4 块
切 3 刀 分为 7 块
切 4 刀 分为 11 块
切 5 刀 分为 16 块
……
切100刀 分为[(1+100)*100/2]+1=5051块
解释:每新的一刀都与之前的每一刀相交,将新刀数块饼分为两半。
54. 有三个线程名字分别是A、B、C,每个线程只能打印自己的名字,在屏幕上顺序打印 ABC,打印10次。不准使用线程的sleep()。
方法一
public class MyThread extends Thread {
private static Object o = new Object();
private static int count = 0;
private int id;
private int num = 0;
public MyThread(int id, String name) {
this.id = id;
setName(name);
}
public void run() {
synchronized (o) {
while (num < 10) {
if (count % 3 == id) {
System.out.print(getName());
++count;
++num;
o.notifyAll();
} else {
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
(new MyThread(0, "A")).start();
(new MyThread(1, "B")).start();
(new MyThread(2, "C")).start();
}
}
方法二
public class CycliBarrierTest implements Runnable {
private static List<CycliBarrierTest> list = new ArrayList<>();
private CyclicBarrier barrier;
private String name;
public CycliBarrierTest(CyclicBarrier barrier, String name) {
this.barrier = barrier;
this.name = name;
list.add(this);
}
public void run() {
try {
while (!Thread.interrupted()) {
barrier.await();
}
} catch (InterruptedException | BrokenBarrierException e) {
System.out.println(e.getMessage());
}
}
public String getName() {
return name;
}
public static void main(String[] args) {
final ExecutorService exec = Executors.newCachedThreadPool();
CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
private int count = 0;
public void run() {
count++;
for (CycliBarrierTest cycliBarrierTest : list) {
System.out.print(cycliBarrierTest.getName());
}
System.out.println();
if (count == 10) {
exec.shutdownNow();
}
}
});
exec.execute(new CycliBarrierTest(barrier, "A"));
exec.execute(new CycliBarrierTest(barrier, "B"));
exec.execute(new CycliBarrierTest(barrier, "C"));
}
}
方法三
public class SemaphoreThread extends Thread {
private Semaphore current;
private Semaphore next;
public SemaphoreThread(String name, Semaphore current, Semaphore next) {
super(name);
this.current = current;
this.next = next;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
current.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(getName());
next.release();
}
}
public static void main(String[] args) {
Semaphore a = new Semaphore(1);
Semaphore b = new Semaphore(0);
Semaphore c = new Semaphore(0);
new SemaphoreThread("A", a, b).start();
new SemaphoreThread("B", b, c).start();
new SemaphoreThread("C", c, a).start();
}
}
55.不通第三个数交换两个数的值。
方法一
int a = 4;
int b = 2;
a = a + b;
b = a - b;
a = a - b;
此方法可能会溢出
方法二
int a = 4;
int b = 2;
a = a ^ b;
b = a ^ b;
a = a ^ b;