当前位置: 代码迷 >> Java面试 >> Java口试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(二)
  详细解决方案

Java口试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(二)

热度:568   发布时间:2016-04-17 18:03:03.0
Java面试宝典系列之基础面试题String、变量、类与对象、集合类、SSH(二)

作者:egg

邮箱:xtfggef@gmail.com

微博:http://weibo.com/xtfggef

博客:http://blog.csdn.net/zhangerqing(转载请说明出处)

这章我们还是接着上一章的,继续整理。

1、数组有没有length()这个方法? String有没有length()这个方法? 
数组没有length()这个方法,有length的属性。String有有length()这个方法。


2、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";
答:对于如下代码:
  String s1 = "a";
  String s2 = s1 + "b";
  String s3 = "a" + "b";
  System.out.println(s2 == "ab");
  System.out.println(s3 == "ab");
  第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
  String s = "a" + "b" + "c" + "d";
  System.out.println(s == "abcd");     

最终打印的结果应该为true。关于字符串更多的介绍,请看:深入解读Java字符串  http://blog.csdn.net/zhangerqing/article/details/8093919 


3、try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后? 
也许你的答案是在return之前,但往更细地说,我的答案是在return中间执行,请看下面程序代码的运行结果:

public  class Test {	/**	 * @param args add by zxx ,Dec 9, 2008	 */	public static void main(String[] args) {		// TODO Auto-generated method stub		System.out.println(new Test().test());;	}	static int test()	{		int x = 1;		try		{			return x;		}		finally		{			++x;		}	}}

---------执行结果 ---------

1

运行结果是1,为什么呢?主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。

4、下面的程序代码输出的结果是多少?

public class  smallT{	public static void  main(String args[])	{		smallT t  = new  smallT();		int  b  =  t.get();		System.out.println(b);	}		public int  get()	{		try		{			return 1 ;		}		finally		{			return 2 ;		}	}}

返回的结果是2

我可以通过下面一个例子程序来帮助我解释这个答案,从下面例子的运行结果中可以发现,try中的return语句调用的函数先于finally中调用的函数执行,也就是说return语句先执行,finally语句后执行,所以,返回的结果是2Return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally语句后才真正开始返回。

在讲解答案时可以用下面的程序来帮助分析:

public  class Test {	/**	 * @param args add by zxx ,Dec 9, 2008	 */	public static void main(String[] args) {		// TODO Auto-generated method stub		System.out.println(new Test().test());;	}	int test()	{		try		{			return func1();		}		finally		{			return func2();		}	}		int func1()	{		System.out.println("func1");		return 1;	}	int func2()	{		System.out.println("func2");		return 2;	}	}

-----------执行结果-----------------

func1

func2

2

结论:finally中的代码比return break语句后执行


5、final, finally, finalize的区别

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 内部类要访问局部变量,局部变量必须定义成final类型,例如,一段代码……

finally是异常处理语句结构的一部分,表示总是执行。

finalizeObject类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用


6、运行时异常与一般异常有何异同?

异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。


7、error和exception有什么区别? 

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。


8、Java中的异常处理机制的简单原理和应用

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。

Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.ThrowableThrowable下面又派生了两个子类:ErrorExceptionError 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了,例如,说内存溢出和线程死锁等系统问题。Exception表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常NullPointerException)、类转换异常(ClassCastException);普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。

java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。

提示答题者:就按照三个级别去思考:虚拟机必须宕机的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误;


9、请写出你最常见到的5runtime exception

NullPointerExceptionArrayIndexOutOfBoundsExceptionClassCastException、ClassNotFoundException

更多可以参考本博关于异常的讲解:Java中的异常:http://blog.csdn.net/zhangerqing/article/details/8248186


10、sleep() 和 wait() 有什么区别? 

sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 waitObject类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。) 

sleep就是正在执行的线程主动让出cpucpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。对于wait的讲解一定要配合例子代码来说明,才显得自己真明白。

package com.huawei.interview;public class MultiThread {	public static void main(String[] args) {		// TODO Auto-generated method stub		new Thread(new Thread1()).start();		try {			Thread.sleep(10);		} catch (InterruptedException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		new Thread(new Thread2()).start();			}	private static class Thread1 implements Runnable	{		@Override		public void run() {//由于这里的Thread1和下面的Thread2内部run方法要用同一对象作为监视器,我们这里不能用this,因为在Thread2里面的this和这个Thread1的this不是同一个对象。我们用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时,指向的都是同一个对象。			synchronized (MultiThread.class) {				System.out.println("enter thread1...");				System.out.println("thread1 is waiting");				try {			//释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了synchronized关键字管辖的代码范围,另一种方式就是在synchronized关键字管辖的代码内部调用监视器对象的wait方法。这里,使用wait方法释放锁。					MultiThread.class.wait();				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}					System.out.println("thread1 is going on...");				System.out.println("thread1 is being over!");						}		}		}	private static class Thread2 implements Runnable	{		@Override		public void run() {			// TODO Auto-generated method stub			synchronized (MultiThread.class) {							System.out.println("enter thread2...");								System.out.println("thread2 notify other thread can release wait status..");//由于notify方法并不释放锁, 即使thread2调用下面的sleep方法休息了10毫秒,但thread1仍然不会执行,因为thread2没有释放锁,所以Thread1无法得不到锁。				MultiThread.class.notify();						System.out.println("thread2 is sleeping ten millisecond...");				try {					Thread.sleep(10);				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}								System.out.println("thread2 is going on...");				System.out.println("thread2 is being over!");						}		}		}	}

11、启动一个线程是用run()还是start()? 
启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。 


12、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 
分几种情况:
     1.其他方法前是否加了synchronized关键字,如果没加,则能。
     2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。
     3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
     4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。


13、线程的基本概念、线程的基本状态以及状态之间的关系 
一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程。如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。 
状态:就绪,运行,synchronize阻塞,wait和sleep挂起,结束。wait必须在synchronized内部调用。
调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。 


14、简述synchronized和java.util.concurrent.locks.Lock的异同 ? 
主要相同点:Lock能完成synchronized所实现的所有功能 
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。 
举例说明(对下面的题用lock进行了改写):

package com.huawei.interview;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ThreadTest {	private int j;	private Lock lock = new ReentrantLock();	public static void main(String[] args) {		// TODO Auto-generated method stub		ThreadTest tt = new ThreadTest();		for(int i=0;i<2;i++)		{			new Thread(tt.new Adder()).start();			new Thread(tt.new Subtractor()).start();		}	}	private class Subtractor implements Runnable	{		@Override		public void run() {			// TODO Auto-generated method stub			while(true)			{				/*synchronized (ThreadTest.this) {								System.out.println("j--=" + j--);					//这里抛异常了,锁能释放吗?				}*/				lock.lock();				try				{					System.out.println("j--=" + j--);				}finally				{					lock.unlock();				}			}		}		}	private class Adder implements Runnable	{		@Override		public void run() {			// TODO Auto-generated method stub			while(true)			{				/*synchronized (ThreadTest.this) {				System.out.println("j++=" + j++);					}*/				lock.lock();				try				{					System.out.println("j++=" + j++);				}finally				{					lock.unlock();				}							}					}		}}


15、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序
public class ThreadTest1 { private int j; public static void main(String args[]){    ThreadTest1 tt=new ThreadTest1();    Inc inc=tt.new Inc();    Dec dec=tt.new Dec();    for(int i=0;i<2;i++){        Thread t=new Thread(inc);        t.start(); 		   t=new Thread(dec);        t.start();        }    } private synchronized void inc(){    j++;    System.out.println(Thread.currentThread().getName()+"-inc:"+j);    } private synchronized void dec(){    j--;    System.out.println(Thread.currentThread().getName()+"-dec:"+j);    } class Inc implements Runnable{    public void run(){        for(int i=0;i<100;i++){        inc();        }    } } class Dec implements Runnable{    public void run(){        for(int i=0;i<100;i++){        dec();        }    } } } 

16、子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序
public class ThreadTest {	/**	 * @param args	 */	public static void main(String[] args) {		// TODO Auto-generated method stub		new ThreadTest().init();	}	public void init()	{		final Business business = new Business();		new Thread(				new Runnable()				{					public void run() {						for(int i=0;i<50;i++)						{							business.SubThread(i);						}											}									}				).start();				for(int i=0;i<50;i++)		{			business.MainThread(i);		}			}		private class Business	{		boolean bShouldSub = true;//这里相当于定义了控制该谁执行的一个信号灯		public synchronized void MainThread(int i)		{			if(bShouldSub)				try {					this.wait();				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}									for(int j=0;j<5;j++)			{				System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);			}			bShouldSub = true;			this.notify();				}						public synchronized void SubThread(int i)		{			if(!bShouldSub)				try {					this.wait();				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}								for(int j=0;j<10;j++)			{				System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);			}			bShouldSub = false;							this.notify();					}	}}
17、介绍Collection框架的结构

这道题不用说了,去看我关于集合类的讲解就能答好了:http://blog.csdn.net/zhangerqing/article/details/8122075


18、Collection框架中实现比较要实现什么接口

comparable/comparator


19、ArrayList和Vector的区别
这两个类都实现了List接口(List接口继承了Collection接口),他们都是有序集合,即存储在这两个集合中的元素的位置都是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,,并且其中的数据是允许重复的,这是HashSet之类的集合的最大不同处,HashSet之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素(本来题目问的与hashset没有任何关系,但为了说清楚ArrayList与Vector的功能,我们使用对比方式,更有利于说明问题)。

接着才说ArrayList与Vector的区别,这主要包括两个方面:. (1)同步性:
Vector是线程安全的,也就是说是它的方法之间是线程同步的,而ArrayList是线程序不安全的,它的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用Vector,因为不需要我们自己再去考虑和编写线程安全的代码。

备注:对于Vector&ArrayList、Hashtable&HashMap,要记住线程安全的问题,记住Vector与Hashtable是旧的,是java一诞生就提供了的,它们是线程安全的,ArrayList与HashMap是java2时才提供的,它们是线程不安全的。所以,我们讲课时先讲老的。(2)数据增长:
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。Vector默认增长为原来两倍,而ArrayList的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的1.5倍)。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
    总结:即Vector增长原来的一倍,ArrayList增加原来的0.5倍。


20、HashMap和Hashtable的区别 && List 和 Map

去看我关于集合类的讲解就能答好了:http://blog.csdn.net/zhangerqing/article/details/8122075


21、Collection 和 Collections的区别 
Collection是集合类的上级接口,继承与他的接口主要有Set 和List. 
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。


22、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? 
Set里的元素是不能重复的,元素重复与否是使用equals()方法进行判断的。 
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。

去看我关于集合类的讲解有很详细的回答:http://blog.csdn.net/zhangerqing/article/details/8122075


23、说出一些常用的类,包,接口,请各举5个 

这种弱智的问题,总会有人考,没办法,有人考,你就得会。

要让人家感觉你对java ee开发很熟,所以,不能仅仅只列core java中的那些东西,要多列你在做ssh项目中涉及的那些东西。就写你最近写的那些程序中涉及的那些类。

常用的类:BufferedReader  BufferedWriter  FileReader  FileWirter  String  Integer 
java.util.Date,System,Class,List,HashMap

常用的包:java.lang   java.io  java.util  java.sql ,javax.servlet,org.apache.strtuts.action,org.hibernate
常用的接口:Remote  List  Map  Document  NodeList ,Servlet,HttpServletRequest,HttpServletResponse,Transaction(Hibernate)、Session(Hibernate),HttpSession


24、java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类? 

字节流,字符流。字节流继承于InputStream OutputStream,字符流继承于InputStreamReader OutputStreamWriter。在java.io包中还有许多其他的流,主要是为了提高性能和使用方便


25、字节流与字符流的区别
要把一片二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一片二进制数据,不管输入输出设备是什么,我们要用统一的方式来完成这些操作,用一种抽象的方式进行描述,这个抽象描述方式起名为IO流,对应的抽象类为OutputStream和InputStream ,不同的实现类就代表不同的输入和输出设备,它们都是针对字节进行操作的。在应用中,经常要完全是字符的一段文本输出去或读进来,用字节流可以吗?计算机中的一切最终都是二进制的字节形式存在。对于“中国”这些字符,首先要得到其对应的字节,然后将字节写入到输出流。读取时,首先读到的是字节,可是我们要把它显示为字符,我们需要将字节转换成字符。由于这样的需求很广泛,人家专门提供了字符流的包装类。底层设备永远只接受字节数据,有时候要写字符串到底层设备,需要将字符串转成字节再进行写入。字符流是字节流的包装,字符流则是直接接受字符串,它内部将串转成字节,再写入底层设备,这为我们向IO设别写入或读取字符串提供了一点点方便。字符向字节转换时,要注意编码的问题,因为字符串转成字节数组,其实是转成该字符的某种编码的字节形式,读取也是反之的道理。下面是一个小的案例:

import java.io.BufferedReader;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.FileReader;import java.io.FileWriter;import java.io.InputStreamReader;import java.io.PrintWriter;public class IOTest {	public static void main(String[] args) throws Exception {		String str = "中国人";		/*FileOutputStream fos  = new FileOutputStream("1.txt");				fos.write(str.getBytes("UTF-8"));		fos.close();*/				/*FileWriter fw = new FileWriter("1.txt");		fw.write(str);		fw.close();*/		PrintWriter pw = new PrintWriter("1.txt","utf-8");		pw.write(str);		pw.close();				/*FileReader fr = new FileReader("1.txt");		char[] buf = new char[1024];		int len = fr.read(buf);		String myStr = new String(buf,0,len);		System.out.println(myStr);*/		/*FileInputStream fr = new FileInputStream("1.txt");		byte[] buf = new byte[1024];		int len = fr.read(buf);		String myStr = new String(buf,0,len,"UTF-8");		System.out.println(myStr);*/		BufferedReader br = new BufferedReader(				new InputStreamReader(					new FileInputStream("1.txt"),"UTF-8"						)				);		String myStr = br.readLine();		br.close();		System.out.println(myStr);	}}


26、什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用

我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将java对象存储到硬盘或者传送给网络上的其他计算机,这个过程我们可以自己写代码去把一个java对象变成某个格式的字节流再传输,但是,jre本身就提供了这种支持,我们可以调用OutputStream的writeObject方法来做,如果要让java 帮我们做,要被传输的对象必须实现serializable接口,这样,javac编译时就会进行特殊处理,编译的类才可以被writeObject方法操作,这就是所谓的序列化。需要被序列化的类必须实现Serializable接口,该接口是一个mini接口,其中没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的。 

例如,在web开发中,如果对象被保存在了Session中,tomcat在重启时要把Session对象序列化到硬盘,这个对象就必须实现Serializable接口。如果对象要经过分布式系统进行网络传输或通过rmi等远程调用,这就需要在网络上传输对象,被传输的对象就必须实现Serializable接口。


27、GC是什么为什么要有GC? 

本博客关于垃圾回收的详细介绍:http://blog.csdn.net/zhangerqing/article/details/8214365


28、什么时候用assert 
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,assert将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion检查通常是关闭的。 

package com.huawei.interview;public class AssertTest {	/**	 * @param args	 */	public static void main(String[] args) {		// TODO Auto-generated method stub		int i = 0;		for(i=0;i<5;i++)		{			System.out.println(i);		}		//假设程序不小心多了一句--i;		--i;		assert i==5;			}}

29、java中会存在内存泄漏吗,请简单描述 
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:

package com.huawei.interview;import java.io.IOException;public class GarbageTest {	/**	 * @param args	 * @throws IOException 	 */	public static void main(String[] args) throws IOException {		// TODO Auto-generated method stub		try {			gcTest();		} catch (IOException e) {			// TODO Auto-generated catch block			e.printStackTrace();		}		System.out.println("has exited gcTest!");		System.in.read();		System.in.read();				System.out.println("out begin gc!");				for(int i=0;i<100;i++)		{			System.gc();			System.in.read();				System.in.read();			}	}	private static void gcTest() throws IOException {		System.in.read();		System.in.read();				Person p1 = new Person();		System.in.read();		System.in.read();				Person p2 = new Person();		p1.setMate(p2);		p2.setMate(p1);		System.out.println("before exit gctest!");		System.in.read();		System.in.read();				System.gc();		System.out.println("exit gctest!");	}	private static class Person	{		byte[] data = new byte[20000000];		Person mate = null;		public void setMate(Person other)		{			mate = other;		}	}}

java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。检查java中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。下面内容来自于网上(主要特点就是清空堆栈中的某个元素,并不是彻底把它从数组中拿掉,而是把存储的总数减少,本人写得可以比这个好,在拿掉某个元素时,顺便也让它从数组中消失,将那个元素所在的位置的值设置为null即可):我实在想不到比那个堆栈更经典的例子了,以致于我还要引用别人的例子,下面的例子不是我想到的,是书上看到的,当然如果没有在书上看到,可能过一段时间我自己也想的到,可是那时我说是我自己想到的也没有人相信的。


30、

作者:egg

邮箱:xtfggef@gmail.com

微博:http://weibo.com/xtfggef

博客:http://blog.csdn.net/zhangerqing(转载请说明出处)


大家有什么补充的,在下面回复,同时也希望提出宝贵的意见!

题目和答案有些来自网络和传智播客张孝祥老师的整理,本人收集的,如果有回答不恰当的地方,还望及时指出、改正!

  相关解决方案