1.九种基本数据类型的大小,以及他们的封装类:
也有说Java中的八种基本数据类型的(八种的说法认为不包含void类型)。
上面的数据类型所占用的存储空间是固定的,这也是保证Java跨平台的一个重要因素。另外基本数据类型可以表示的赋值范围也是固定的(注意考虑有符号和无符号)。
2.switch能否用String做参数?
在Java低版本中 switch(str){…}是不支持使用String作为参数执行操作的。这里说的低版本指的是Java版本小于7的版本。在高版本中是支持的,比如Java7和Java8。下面附上图片:
低版本:
在低版本中我们也可以通过枚举类型去替换String类型
高版本:
3.equals与==的区别?
在Java中默认的equals方法实现如下:
public boolean equals(Object obj) {
return (this == obj);
}
而String类重写了这个方法,其实就是比较两个字符串的内容是否都相同:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n- != 0){
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
看了上面的代码,我们在来总结一下两者的区别:
1、equals比较的是两个对象的值是否相同;
2、== 引用类型(外加String类型)比较的是两个对象是否相同,比较的是地址和值;基本数据类型(String类型除外)比较的是值是否相同,其实比较的是值的地址是否相同;
3、equals是方法,==是运算符;
总之一句话,==比较地址,equals比较值;另外,如果一个类没有重写equals方法的话,那么这个类的equals方法和==效果一样;
4.Object有哪些公有方法?
1、
clone( )
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常
2、
equals( )
在Object中与==是一样的,子类一般需要重写该方法
3、
hashCode( )
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到
4、
getClass( )
final方法,获得运行时类型
5、
wait( )
使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生:
1. 其他线程调用了该对象的notify方法
2. 其他线程调用了该对象的notifyAll方法
3. 其他线程调用了interrupt中断该线程
4. 时间间隔到了
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常
6、
notify( )
唤醒在该对象上等待的某个线程
7、
notifyAll( )
唤醒在该对象上等待的所有线程
8、
toString( )
转换成字符串,一般子类都有重写,否则打印句柄
5.Java的四种引用,强软弱虚,用到的场景
首先我们看一下他们之间的关系:
强引用
强引用不会被GC回收,实际开发中接触最多的也就是强引用。Object obj = new Object();这就是强引用。即便内存不足的时候也不会被回收,JVM宁愿抛出OOM异常。使用场景就是创建对象的时候。
软引用
在内存空间不足的时候会被回收。软引用可以用来实现内存敏感的高速缓存。软引用可以和一个引用队列联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
Object obj = new Object();
ReferenceQueue refQueue = new ReferenceQueue();
SoftReference softRef = new SoftReference(obj, refQueue);
弱引用
弱引用现在已经不提倡使用了。因为当垃圾回收器(一个线程)一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只有弱引用的对象。同样的在弱引用对象被回收之后,Java虚拟机会把这个弱引用加入到与之关联的引用队列中。
Object obj = new Object();
ReferenceQueue refQueue = new ReferenceQueue();
WeakReference weakRef = new WeakReference(obj, refQueue);
虚引用
虚引用不同于其他几种引用,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。软引用与软引用和弱引用的一个区别就在于:虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象的时候,如果发现该对象有虚引用,就会在回收对象的内存之前,把这个虚引用添加到与之关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被回收。如果程发现某个虚引用已经被加入到与之关联的引用队列中,那么就可以在这个对象的内存被回收前才去必要的措施(这个要看产品的需求)。
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object, queue);
下面我们具体看一下如何使用这些引用:
part one : 如何使用软引用
SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾回收器线程对该Java对象的回收。也就是说,一旦SoftReference保存了一个Java对象的软引用后,在垃圾回收器线程对这个Java对象回收前,SoftReference类所提供的get()方法会返回Java对象的强引用(其实就是软引用对象调用自身的get()方法,能获取到Java对象的强引用。不理解这句话的还是直接看代码吧)。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。
看下面代码:
// 创建一个Java对象
MyObject myObj = new MyObject();
// 第一种方式
// 直接将一个Java对象保存在一个软引用对象中,aSoftRef对象自身其实也是一个强引用,因为它也是直接new出来的
SoftReference aSoftRef=new SoftReference(myObj);
// 第二种方式
// 将一个Java对象放在软引用对象中,并且提供一个引用队列给此软引用。在垃圾回收器回收掉Java对象(myObj)后,该引用队列(queue)中会保存有软引用对象(ref),在此之前该队列中什么都没有
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(myObj, queue);
此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,所以这个MyObject对象是强可及对象。
随即,我们可以结束aReference对这个MyObject实例的强引用:
myObj = null;
经过上面的一步操作,这个MyObject对象成为了软可及对象(也就是能通过软引用去获取的对象)。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对象的引用着myObj而始终保留myObj对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。因此在回收这些对象之前,我们可以通过:
// 如果没有被回收通过get()方法还能得到myObj强引用,否则得到的就是null
MyObject anotherRef=(MyObject)aSoftRef.get();
// 如果我们在创建软引用对象的时候使用的是 第一种方式 那我们就只能通过上面的方式去获取Java对象的强引用了,然后依照获取结果去判断Java对象是否已经被回收
// 如果我们使用的是 第二种方式(也就是在创建软引用的时候添加了一个引用队列queue) 创建的软引用。那我们在获取Java对象之前,我们可以先通过判断引用队列中是否为null,来判断Java对象是否被回收
SoftReference tempRef = null; // 这里为了直观定义一个空对象
while ((tempRef = queue.poll()) != null) {
// queue的poll()方法会将队列中的软引用对象返回,如果垃圾回收器已经回收了Java对象,那么poll()方法就会返回和Java对象关联的软引用对象,否则就返回null。所以如果判断为非空,就证明垃圾回收器已经回收了Java对象,我们要做的就是清除ref对象,避免内存泄露,因为ref对象其实也是一个强引用(ref也是被直接new出来的)
ref = null;
}
Part One : 使用软引用创建高速缓存
此处是从 http://zhangjunhd.blog.51cto.com/113473/53092/ 学习的
首先,创建一个类,我们将用这个Java类去创建对象,高速缓存的对象
public class Employee {
private String id;// 雇员ID
... // 一堆雇员的属性
// 构造方法
public Employee(String id) {
this.id = id;
getDataFromlnfoCenter();
}
public String getId(){
return this.id;
}
// 到数据库中取得雇员信息
private void getDataFromlnfoCenter() {
// 和数据库建立连接井查询该雇员的信息,将查询结果赋值
// 给相关属性
// 数据库操作一般是比较耗时的,因此如果我们每使用或查找一个Employee对象的时候,如果每次都去做这些操作其实是非常不合适的
}
}
然后,定义一个对Employee对象进行缓存的缓存器
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;
public class EmployeeCache {
private static EmployeeCache cache;// 单例
private Hashtable<String, EmployeeRef> emloyeeRefsHashTable;// 用于保存每一个雇员对象的软引用对象
private ReferenceQueue<Employee> queue;// 引用队列
// 私有化构造
private EmployeeCache() {
emloyeeRefsHashTable = new Hashtable<String, EmployeeRef>();
queue = new ReferenceQueue<Employee>();
}
// 创建缓存器单一实例
public static EmployeeCache getInstance() {
if(cache == null){
cache = new EmployeeCache();
}
return cache;
}
// 以软引用的方式对一个Employee对象的实例进行软引用操作
private void cacheEmployee(Employee em){
clearCache(); // 清理垃圾引用
EmployeeRef ref = new EmployeeRef(em, queue);
employeeRefsHashTable.put(em.getId(), ref); // 将相应的软引用对象保存
}
// 根据ID,重新获取相应的Employee对象的实例(强引用)
public Employee getEmployee(String id) {
Employee em = null;
// 缓存中是否有该Employee实例的软引用,如果有,从软引用中取得
if(employeeRefsHahsTable.containsKey(id)) {
EmployeeRef ref = (EmployeeRef)employeeRefsHashTable.get(id);
em = (Employee) ref.get();
}
// 如果没有软引用,或者从软引用中get到的是null,就重新创建一个,并保存这个雇员对象到软引用中
if(em == null) {
em = new Employee(id);
this.cacheEmployee(em);
}
return em;
}
private void clearCache(){
EmployeeRef ref = null;
while((ref = (EmployeeRef)queue.poll()) != null) {
employeeRefsHashTable.remove(ref._key);
}
}
// 清除全部内容
public void clearAllCache() {
clearCache();
employeeRefsHashTable.clear();
System.gc();
System.runFinalization();
}
/**
* 自定义一个软引用类
* 每创建一个雇员对象就创建一个该类型的软引用对象
*
* _key 标识 雇员的id,和HashTable里面的key相同
*/
private class EmployeeRef extends SoftReference<Employee> {
private String _key = "";
public EmployeeRef(Employee em, ReferenceQueue<Employee> q) {
super(em, q);
_key = em.getId();
}
}
}
Part Two : 弱引用的使用
首先,现在对于弱引用我们已经不推崇使用了。因为弱引用极易被回收,使用起来也不方便,但是如果项目中有内存泄露的问题,并且影响了项目的正常运行,使用弱引用也是不错的选择。
最常见的就是 *- - - 使用弱引用构建非敏感数据的缓存 - - -*
生命周期的不同是引起内存泄露的主要原因,简单点说内存泄露就是该消失的没有消失(或者说该被回收的没有被回收,并且还不受控制了,就是内存泄露)。
如果项目中有Map发生了内存泄露,我们可以使用Java提供的另外一个类来代替Map,那就是java.util.WeakHashMap。
在Java集合中有一种特殊的Map类型—WeakHashMap,在这种Map中存放了键对象的弱引用,当一个键对象被垃圾回收器回收时,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用来缓存那些非必须存在的数据。
// 构建方式 WeakHashMap<_key, _value>
Map<Object, Object> map = new WeakHashMap<>();
WeakHashMap 有一个名为 expungeStaleEntries() 的方法,大多数 Map 操作中会调用它,用于去掉map中所有失效的引用,并删除关联的映射。
小结:u a n a !