Java面试题-J2SE基础
Posted JOE-1992
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试题-J2SE基础相关的知识,希望对你有一定的参考价值。
最近在为自己实习准备,看了网上各种面试经验贴,也和身边的小伙伴一起参加了不少牛逼IT企业的面试,这篇文章就将面试遇到的一些比较常见的问题整理一下,给大家一些参考,也为自己整理整理。
J2SE基础
1.九种基本数据类型的大小,以及他们的封装类。
Java的九种基本数据类型:
byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。
int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。
double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。
boolean:只有true和false两个取值。
char:16位,存储Unicode码,用单引号赋值。
void:对应的包装类 java.lang.Void,不过无法直接对它们进行操作。
相应的包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double,Void
关于void是否算基本类型,这个问题大家的看法各异,官方的文件也没有做特别声明。
2.Switch能否用string做参数?
可以。switch语句后的控制表达式只能是short、char、int、long整数类型和枚举类型,不能是float,double和boolean类型。String类型从java7开始支持。
3.equals与==的区别。
基本数据类型,也称原始数据类型。他们之间的比较,应用双等号(==),比较的是他们的值。
复合数据类型(类) ,当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。 JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的存放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
4.Object有哪些公用方法?
protected Object clone()
// 创建并返回一个对象的copy。
boolean equals(Object obj)
// 判断其他对象与当前对象是否相等。
protected void finalize()
// 垃圾收集器调用在一个对象上,确定其没有其他对象的引用。
Class<?> getClass()
// 返回一个对象的运行时类。
int hashCode()
// 返回一个对象的HashCode值。
void notify()
// 唤醒一个正在等待对象监视器的线程。
void notifyAll()
// 唤醒所有的正在等待对象监视器的线程。
String toString()
// 返回一个对象的字符串表示。
void wait()
// 使当前线程进入等待直到另一个线程调用notify()方法或者notifyAll()方法唤醒当前线程。
void wait(long timeout)
// 使当前线程进入等待直到另一个线程调用notify()方法或者notifyAll()方法唤醒当前线程,或者通过参数指定时间运行。
void wait(long timeout, int nanos)
// 使当前线程进入等待直到另一个线程调用notify()方法或者notifyAll()方法唤醒当前线程,或者其他线程中断当前线程,或者通过参数指定时间运行。
5.Java的四种引用,强弱软虚,用到的场景。
(1) 强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
(2)弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
(3)软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
(4)虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
应用场景
WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。
WeakReference是弱引用,其中保存的对象实例可以被GC回收掉。这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。
最近在JDK的Proxy类的实现代码中也发现了Weakrefrence的应用,Proxy会把动态生成的Class实例暂存于一个由Weakrefrence构成的Map中作为Cache。
SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。
6.Hashcode的作用。
public native int hashCode();
由hashCode声明可以看出这是一个本地方法,返回值为int。下面看一下String类中重写的hashCode方法:
public int hashCode() {
int h = hash; // String类中的私有变量,
if (h == 0 && value.length > 0) {
// private final char value[];
// Sting类中保存的字符串内容的的数组
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];// hash值计算
}
hash = h;
}
return h;
}
String源码中使用private final char value[];保存字符串内容,因此String是不可变的。
《Effective Java》中说明:
- 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
- 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
- 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。
简单来说,哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。
7.ArrayList、LinkedList、Vector的区别。
(1)线程安全方面
Vector:线程安全
ArrayList、LinkedList:非线程安全
(2)实现方式
LinkedList:双向链表
ArrayList,Vector:数组
(3)扩容方面
ArrayList和Vector使用数组实现,当数组长度不够时,其内部会创建一个更大的数组,然后将原数组中的数据拷贝至新数组中。
详细请看: ArrayList内部实现
8.String、StringBuffer与StringBuilder的区别。
Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
9.Map、Set、List、Queue、Stack的特点与用法。
Set集合类似于一个罐子,”丢进”Set集合里的多个对象之间没有明显的顺序。
List集合代表元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。
Stack是Vector提供的一个子类,用于模拟”栈”这种数据结构(LIFO后进先出)
Queue用于模拟”队列”这种数据结构(先进先出 FIFO)。
Map用于保存具有”映射关系”的数据,因此Map集合里保存着两组值
10.HashMap和HashTable的区别。
Hashtable是基于陈旧的Dictionary类的,HashMap是Map接口的一个实现 。Hashtable的方法是线程同步的,而HashMap的方法不是。 只有HashMap可以让你将空值作为一个表的条目的key或value 。
11.HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
详细请阅读这里:ConcurrentHashMap实现原理
12.TreeMap、HashMap、LindedHashMap的区别。
Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。也可以在构造时用带参数,按照应用次数排序 。
TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
13.Collection包结构,与Collections的区别。
Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
14.try catch finally,try里有return,finally还执行么?
会执行,在方法返回调用者前执行。
注意:在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,就会返回修改后的值。显然,在finally中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java中也可以通过提升编译器的语法检查级别来产生警告或错误,Eclipse中可以在如图所示的地方进行设置,强烈建议将此项设置为编译错误。
15.Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。
Java异常架构图
- Throwable
Throwable是 Java 语言中所有错误或异常的超类。
Throwable包含两个子类: Error 和 Exception 。它们通常用于指示发生了异常情况。
Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。 - Exception
Exception及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。 - RuntimeException
RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
16.Override和Overload的含义去区别。
Overload:顾名思义,就是Over(重新)——load(加载),所以中文名称是重载。它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;或者说可以改变参数、类型、返回值但是函数名字依然不变。
Override:就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。
17.Interface与abstract类的区别。
抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
18.Static class 与non static class的区别。
某个对象的特性分为类特性与实例特性。类特性是与类相关的,而实例特性是每个对象本身自己的特性。
static类应当使用类名来引用。而non-static类必须使用对象实例名来引用。
static类只能引用类的static数据成员;而non-static类既可以引用类的static数据成员,也可以引用对象自身的数据。
static与non-static method在overload方面是一样的。
而static与non-static method在override方面则完全不同。static方法是与类相关的,不是通过this引用的,所以它不能被override。其引用在编译期就得确定。而non-static方法才有可能被override。
19.java多态的实现原理。
靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。
JVM规范中关于对象内存布局的说明:
The Java Virtual Machine does not require any particular internal structure for objects. In Sun’s current implementation of the Java Virtual Machine, a reference to a class instance is a pointer to a handle that is itself a pair of pointers: one to a table containing the methods of the object and a pointer to the Class object that represents the type of the object, and the other to the memory allocated from the Java heap for the object data.
SUN目前的JVM实现机制,类实例的引用就是指向一个句柄(handle)的指针,这个句柄是一对指针:
一个指针指向一张表格,实际上这个表格也有两个指针(一个指针指向一个包含了对象的方法表,另外一个指向类对象,表明该对象所属的类型);
另一个指针指向一块从java堆中为分配出来内存空间。
20.线程同步的方法:sychronized、lock、reentrantLock等。
(1)什么是sychronized
sychronized是java中最基本同步互斥的手段,可以修饰代码块,方法,类。
在修饰代码块的时候需要一个reference对象作为锁的对象。
在修饰方法的时候默认是当前对象作为锁的对象。
在修饰类时候默认是当前类的Class对象作为锁的对象。
在并发量比较小的情况下,使用synchronized是个不错的选择,但是在并发量比较高的情况下,其性能下降很严重,此时ReentrantLock是个不错的方案。
(2)什么ReentrantLock
以对象的方式来操作对象锁,相对于sychronized需要在finally中去释放锁 。
(3)synchronized和ReentrantLock的区别
除了synchronized的功能,ReentrantLock多了三个高级功能。
等待可中断,公平锁,绑定多个Condition。
1.等待可中断
在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待。 tryLock(long timeout, TimeUnit unit)
2.公平锁
按照申请锁的顺序来一次获得锁称为公平锁。synchronized的是非公平 锁,ReentrantLock可以通过构造函数实现公平锁。 new RenentrantLock(boolean fair)
3.绑定多个Condition
通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能。通过await(),signal()。
如果synchronized关键字适合程序,尽量使用它,可以减少代码出错的几率和代码数量
21.锁的等级:方法锁、对象锁、类锁。
所谓方法锁也就是对象锁。这里主要介绍对象锁与类锁的区别。
对象锁也就是针对某个对象的,在类A中有synchronized methodA() 这样的方法,那么当这个方法被调用的时候,那就是获得了对象锁了。但是注意,例如有ClassA A = new ClassA(); ClassA B = new ClassA(); 那么A对象与B对象可以同时调用methodA()方法,因为他们是不同的对象。
类锁是锁住整个类的,当有多个线程来声明这个类的对象的时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁。这个时候在被阻塞住的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住。例如,类A有一个方法synchronized static methodA();注意是静态的,也就是说对于类A的所有对象它都是公用的,如果当前有对象调用该方法,其他对象想调用就得等着它的锁被释放,这就感觉是整个类被锁住了一样了。
22.写出生产者消费者模式。
多线程中的生产者消费者问题
生产者消费者问题是一个流行的面试题,面试官会要求你实现生产者消费者设计模式,以至于能让生产者应等待如果队列或篮子满了的话,消费者等待如果队列或者篮子是空的。这个问题可以用不同的方式来现实,经典的方法是使用wait和notify方法在生产者和消费者线程中合作,在队列满了或者队列是空的条件下阻塞,Java5的阻塞队列(BlockingQueue)数据结构更简单,因为它隐含的提供了这些控制,现在你不需要使用wait和nofity在生产者和消费者之间通信了,阻塞队列的put()方法将阻塞如果队列满了,队列take()方法将阻塞如果队列是空的。
使用阻塞队列实现生产者消费者模式
阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。
23.ThreadLocal的设计理念与作用。
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。
如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。
下面来看一个hibernate中典型的ThreadLocal的应用:
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到service和dao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,put到map中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。
24.ThreadPool用法与优势。
合理利用线程池能够带来三个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。
线程池的使用
我们可以通过ThreadPoolExecutor来创建一个线程池。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
corePoolSize :线程池的基本大小
maximumPoolSize:线程池最大大小
ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。
keepAliveTime:线程活动保持时间
TimeUnit:线程活动保持时间的单位
向线程池提交任务
使用execute提交的任务,但是execute方法没有返回值,所以无法判断任务是否被线程池执行成功。
threadsPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
});
使用submit 方法来提交任务,它会返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。
Future<Object> future = executor.submit(harReturnValuetask);
try {
Object s = future.get();
} catch (InterruptedException e) {
// 处理中断异常
} catch (ExecutionException e) {
// 处理无法执行任务异常
} finally {
// 关闭线程池
executor.shutdown();
}
线程池的关闭
我们可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池,它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。但是它们存在一定的区别,shutdownNow首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表,而shutdown只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
25.Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
ArrayBlockingQueue是一个由数组支持的有界阻塞队列。在读写操作上都需要锁住整个容器,因此吞吐量与一般的实现是相似的,适合于实现“生产者消费者”模式。
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行。CountDownLatch如其所写,是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。
26.wait()和sleep()的区别。
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
27.foreach与正常for循环效率对比。
在固定长度或长度不需要计算的时候for循环效率高于foreach。
在不确定长度,或计算长度有性能损耗的时候,用foreach比较方便。
并且foreach的时候会锁定集合中的对象.期间不能修改。
28.Java IO与NIO。
NIO包(java.nio.*)引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题。
1. Buffer:它是包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作。
2. Charset:它提供Unicode字符串影射到字节序列以及逆影射的操作。
3. Channels:包含socket,file和pipe三种管道,它实际上是双向交流的通道。
4. Selector:它将多元异步I/O操作集中到一个或多个线程中(它可以被看成是Unix中select()函数或Win32中WaitForSingleEvent()函数的面向对象版本)。
在以前的 Java IO 中,都是阻塞式 IO,NIO 引入了非阻塞式 IO。
29.反射的作用于原理。
要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。
Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。
Java 反射机制主要提供了以下功能
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法。
反射的常用类和函数:Java反射机制的实现要借助于4个类:Class,Constructor,Field,Method;其中class代表的是类对象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象,通过这四个对象我们可以粗略的看到一个类的各个组成部分。其中最核心的就是Class类,它是实现反射的基础。
30.泛型常用特点,List< String>能否转为List< Object>。
因为这里利用类都继承自Object,所以使用是每次调用里面的函数都要通过强制转换还原回原来的类,这样既不安全,运行速度也慢。
.int boolean double因为不是类没有继承Object 不能直接强制转换 ,需要先包装成 Integer Boolean Double 才行。
31.解析XML的几种方式的原理与特点:DOM、SAX、PULL。
(1)SAX解析
Sax定义
SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于android等移动设备 SAX全称是Simple API for Xml,既是指一种接口,也是一个软件包 作为接口,sax是事件驱动型xml解析的一个标准接口
Sax特点
1. 解析效率高,占用内存少 2.可以随时停止解析 3.不能载入整个文档到内存 4.不能写入xml 5.SAX解析xml文件采用的是事件驱动
sax并不需要解析完 整个文档,在按内容顺序解析文档的过程中,sax会判断当前读到的字符是否合法xml语法中的某部分,如果符合就会触发事件
Sax工作原理
Sax的工作原理简单的说,就是对文档进行顺序扫描,扫描到文档(document)开始与结束,扫描到元素(element)开始、结束等地方时调用事件处理,处理函数做相应动作,然后继续扫描,直到文档结束。
(2)DOM解析
DOM简介
dom全称Document Object Model ,为xml文档的已解析版本定义了一组接口。解析器读入整个文档,然后构建一个主流内存的树结构, 然后代码就可以使用dom接口来操作这个树结构
DOM的特点
优点 整个文档树在内存中,便于操作;支持删除、修改、重新排列等多种功能 通过树形结构存取xml文档 可以在树的某个节点上向前或向后移动 缺点 将整个文档调入内存(包括无用的节点),浪费时间和空间 适用场合 一旦解析了文档还需多次访问这些数据;硬件资源充足(内存,cpu)
(3)PULL解析
pull解析器简介
1.pull解析器是android内置的解析器,解析原理与sax类似 2.pull它提供了类似的事件。 如:开始元素和结束元素事件,使用parse.next()可以进入下一个元素并触发相应的事件,事件将作为数值代码被发送
因此可以使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法获取下一个Text类型节点的值
pull与sax的不同之处
1.pull读取xml文件后触发相应的事件调用方法返回的是数字。 2.pull可以在程序中控制,想解析到哪里就可以停止到哪里。 3.Android中更推荐使用pull解析。
32.Java1.7与1.8新特性。
33.设计模式:单例、工厂、适配器、责任链、观察者等等。
34.JNI的使用。
Java原生接口(Java Native Interface)
以上是关于Java面试题-J2SE基础的主要内容,如果未能解决你的问题,请参考以下文章