先来看一段代码:

import java.util.Arrays;
import java.util.EmptyStackException; /**
* 2014年6月28日09:31:59
* @author 阳光小强
*
*/
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 15; public Stack(){
elements = new Object[DEFAULT_INITAL_CAPACITY];
} public void push(Object obj){
ensureCapacity();
elements[size++] = obj;
} public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
return elements[--size];
} /**
* 假设长度超出了默认长度则加倍
*/
private void ensureCapacity(){
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}

这段程序表面上看是没有不论什么错误的,可是它隐藏着一个“内存泄露”问题,随然每次都有pop()从栈里弹出对象,可是栈中的对象还是被引用着,所以不能够及时释放。将上面代码改动例如以下:

	public Object pop(){
if(size == 0){
throw new EmptyStackException();
}
Object result = elements[--size];
elements[size] = null;
return result;
}

再来看一段代码:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader; public class IOTest {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("test.xml");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
if(br.ready()){
System.out.println(br.readLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

成功输出结果例如以下:

这段代码看似没有不论什么问题,可是却存在着内存泄露问题,我们没有关闭对应的资源

再来思考一个问题,代码例如以下:

public class IOTest {
public static void main(String[] args) {
IOTest test = new IOTest();
IOTest.MyThread myThread = test.new MyThread();
myThread.start();
test = null; //这个对象能释放吗?
myThread = null; //线程能自己主动结束吗?为什么?
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(true){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
}
}
}
}

上面对象能释放吗?线程能自己主动结束吗?

在搞清楚上面问题之前,我们将上面代码进行改动,先看例如以下代码:

public class IOTest {
private String data = "阳光小强"; public static void main(String[] args) {
IOTest test = new IOTest();
/*IOTest.MyThread myThread = test.new MyThread();
myThread.start();*/
test = null; //这个对象能释放吗?
//myThread = null; //线程能自己主动结束吗?为什么?
System.gc(); //启动垃圾回收器
while(true){ }
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(true){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
System.out.println(data);
}
}
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("对象销毁了");
}
}

在上面代码中我们重写了IOTest对象的finalize()方法,该方法是Object类的方法(protected),作用是当对象的垃圾回收器执行的时候回调该方法。

上面我们使用了System.gc()方法来通知垃圾回收器进行垃圾回收,执行的结果是输出了“对象销毁了".以下我们将上面代码中的凝视去掉,启动线程后再来执行一次。

public class IOTest {
private String data = "阳光小强"; public static void main(String[] args) {
IOTest test = new IOTest();
IOTest.MyThread myThread = test.new MyThread();
myThread.start();
test = null; //这个对象能释放吗?
myThread = null; //线程能自己主动结束吗?为什么?
System.gc(); //启动垃圾回收器
while(true){ }
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(true){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
System.out.println(data);
}
}
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("对象销毁了");
}
}

能够看到”对象销毁了“这句话没有被输出到控制台,说明我们创建的IOTest对象没有被销毁,在非常多书上都会说,给一个对象赋null值,这个对象就会成为无主对象,就会被垃圾回收器回收,可是为什么这个线程和IOTest对象还是存在的?假设是这种话,我们应该考虑怎样节省我们的内存?再来看一段代码:

public class IOTest {
private String data = "阳光小强"; public static void main(String[] args) {
IOTest test = new IOTest();
IOTest.MyThread myThread = test.new MyThread();
myThread.start();
test = null; //这个对象能释放吗?
myThread = null; //线程能自己主动结束吗?为什么? while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.gc(); //启动垃圾回收器
}
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(i<5){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
System.out.println(data);
}
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("线程对象销毁了");
}
} public void testUsed(){
while(true){ }
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("外部类对象销毁了");
}
}

输出结果:

从上面结果中我们能够看到,仅仅有当线程结束后,线程对象和启动线程的对象才干真正的被垃圾回收器回收,所以在内部类中定义线程类,启动线程,会一直持有外部类的引用。如今我们又会产生一个疑问,是不是全部的内部类都持有外部类的引用呢?以下我们再来做个试验:

public class IOTest {
private String data = "阳光小强"; public static void main(String[] args) {
IOTest test = new IOTest();
IOTest.MyThread myThread = test.new MyThread();
//myThread.start();
test = null; //这个对象能释放吗?
//myThread = null; //线程能自己主动结束吗?为什么? while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.gc(); //启动垃圾回收器
}
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(i<5){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
System.out.println(data);
}
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("线程对象销毁了");
}
} public void testUsed(){
while(true){ }
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("外部类对象销毁了");
}
}

在上面的代码中我没有启动线程,所以此时的MyThread就能够当成一个普通的内部类了,我将外部类的引用置为空,会发现没有不论什么结果输出(说明外部类没有被销毁)

public class IOTest {
private String data = "阳光小强"; public static void main(String[] args) {
IOTest test = new IOTest();
IOTest.MyThread myThread = test.new MyThread();
//myThread.start();
test = null; //这个对象能释放吗?
myThread = null; //线程能自己主动结束吗?为什么? while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.gc(); //启动垃圾回收器
}
} class MyThread extends Thread{
private int i = 0;
@Override
public void run() {
while(i<5){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i++);
System.out.println(data);
}
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("线程对象销毁了");
}
} public void testUsed(){
while(true){ }
} @Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("外部类对象销毁了");
}
}

我再将内部类的引用和外部类的引用都置为空,则输出了以下结果:


至少我能够从上面现象中这样觉得,全部内部类都持有外部类的引用。这个结论还有待进一步的验证。。。

感谢你对“阳光小强"的关注,我的还有一篇博文非常荣幸參加了CSDN举办的博文大赛,假设你觉的小强的博文对你有帮助,请为小强投上你宝贵的一票,投票地址:http://vote.blog.csdn.net/Article/Details?articleid=30101091

Java的结构之美【2】——销毁对象的更多相关文章

  1. Effective Java(1)-创建和销毁对象

    Effective Java(1)-创建和销毁对象

  2. Java的结构之美【1】——构造对象

    当我们遇到多个构造器參数的时候可能会想到用构件器,代码例如以下: /** * 构建器 * @author 阳光小强 * */ public class Lunch { private String c ...

  3. Effective Java 读书笔记之一 创建和销毁对象

    一.考虑用静态工厂方法代替构造器 这里的静态工厂方法是指类中使用public static 修饰的方法,和设计模式的工厂方法模式没有任何关系.相对于使用共有的构造器来创建对象,静态工厂方法有几大优势: ...

  4. effective java 第2章-创建和销毁对象 读书笔记

    背景 去年就把这本javaer必读书--effective java中文版第二版 读完了,第一遍感觉比较肤浅,今年打算开始第二遍,顺便做一下笔记,后续会持续更新. 1.考虑用静态工厂方法替代构造器 优 ...

  5. Effective Java 学习笔记之创建和销毁对象

    一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...

  6. 【Effective Java读书笔记】创建和销毁对象(一):考虑使用静态工厂方法代替构造器

    类可以提供一个静态方法,返回类的一个静态实例,如Boolean包装类的一个获取实例的静态方法 public static Boolean valueOf(boolean b) { return (b ...

  7. Effective Java——(一)创建和销毁对象

    第一条 考虑用静态工厂方法代替构造器 什么叫静态工厂方法,就是通过在类中通过静态方法对对象初始化. 比如说 public class StaticFactory { private String na ...

  8. 【Effective Java】第二章-创建和销毁对象——1.考虑用静态工厂方法代替构造器

    静态工厂方法的优点: 可以赋予一个具有明确含义的名称 可以复用唯一实例,不必每次新建 可以返回原实例类型的子类对象 可以在返回泛型实例时更加简洁 缺点: 类如果不含有共有的或者受保护的构造器,就不能被 ...

  9. Effective Java(一)—— 创建和销毁对象

    在客户端(调用端)获取自身实例的方法: 公有的构造器: 类的静态工厂方法: 1. 使用静态工厂方法代替构造器 Boolean 是对基本类型 boolean 的包装类: public final cla ...

随机推荐

  1. Filezilla出现中文乱码

    使用Filezilla client FTP客户端登陆某些FTP站点会出现中文乱码,原因是FTP服务器端编码与filezilla client端编码不一致造成的,解决方法如下:文件-站点管理-选中要登 ...

  2. Struts2 后台action接收 jsp页面中checkbox中的值

    如前端页面jsp中的标签为: <form action="myurl"> <input type="checkbox" name=" ...

  3. Java中WebService实例

    Web Services是由企业公布的完毕其特定商务需求的在线应用服务,其它公司或应用软件可以通过Internet来訪问并使用这项在线服务. Web Service的关键技术和规则: 1.XML:描写 ...

  4. C# 计算字符串/文件的哈希值(MD5、SHA)

    原文 C# 计算字符串的哈希值(MD5.SHA) 已做修改 一.关于本文 本文中是一个类库,包括下面几个函数: /// 1)计算32位MD5码(大小写):Hash_MD5_32 /// 2)计算16位 ...

  5. Restlet+Fastjson 高速构建轻量级 Java RESTful Webservice

    自己入门Java时做过一个小型RESTful Web Service的项目,这里总结一下. 服务的数据交换格式主要採用JSON,服务为REST风格.连接採用Http协议,数据库使用MySQL,OR M ...

  6. 二、第一个ExtJS程序:helloExtJS

    开发前的准备 下载并解压ExtJS包后,能够得到下图的文件文件夹结构: 在实际开发过程中并不须要全部的文件和文件夹,所需的包括例如以下文件夹就可以: 若使用eclipse进行开发,仅仅需将上述文件拷贝 ...

  7. C++设计模式之建造者模式(三)

    4.引入钩子方法的建造者模式 建造者模式除了逐步构建一个复杂产品对象外.还能够通过Director类来更加精细地控制产品的创建过程.比如添加一类称之为钩子方法(HookMethod)的特殊方法来控制是 ...

  8. 【android】在Eclipse在联想引jar包源代码

    (前提是你有jar包源代码!!) .确保Referenced LIbraies下已经有该jar包,否则的话,右击该jar包选build path->add to build path. 二.右键 ...

  9. SOHO路由器的静态路由的不同

    网络拓扑如下,其中RA与RB皆为TP-LINK家用路由器 最终在TP-LINK官网的官网上找到这么一段话 静态路由是在路由器中手工设置的固定的路由条目.我司路由器静态路由是基于ICMP重定向原理,与其 ...

  10. Linux下一个C(编程入门.h档,.c档,而路多文件的调用)

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdG90b3R1enVvcXVhbg==/font/5a6L5L2T/fontsize/400/fill/I0 ...