java中对象的创建和销毁

最近在看Effective Java 一书    该文是第二章的读后感  

一 对象的创建

在java中对象的创建  一般都都是使用new 关键字 +对象的构造方法     java对象中有一个或多个构造方法,可以生成 不同类型的对象 
 例:
public class Person implements Serializable {
    private String  name ;
    private String  hex;
    private int     age;

    public Person(){
        this("zhangsan","men",20);
    }

    public Person(String hex){
        this("zhangsan",hex,20);
    }

    public Person(String name, String hex) {
        this(name,hex,20);
    }

    public Person(String name, int age) {
       this(name,"men",age);
    }
    public Person(int age,String hex){
       this("zhangsan",hex,age);
    }

    public Person(String name, String hex, int age) {
        this.name = name;
        this.hex = hex;
        this.age = age;
    }
}

在上述例子中 存在着这样两个构造方法

 public Person(String name, int age) {
       this(name,"men",age);
    }
    public Person(int age,String hex){
       this("zhangsan",hex,age);
    }

这样做虽然可以达成目的,但是不方便阅读和记忆 。
在这里可以通过使用静态工厂的方法完美解决这个问题,代码如下:

 public static Person valueOfMen(String name,int age){
        return new Person(name,"men",age);
 }
因为静态工厂方法有具体的方法名,通过合理的命名方法名即可完美的解决上述问题。

同时,使用静态工厂方法还有以下两点好处:
1.使用静态工厂方法可以不用频繁的创建新的对象(如单例,通过该方法获取的都是同一个对象)
2.使用静态工厂方法可以返回一个子类类型(参考Executor线程池的创建)  

当然,静态工厂也是有缺点的:
静态工厂方法和普通的静态方法并没有什么不同,它并没有像构造函数一样被单独定义,在编写文档的时候不方便。
注:书中提到不含有共有的或者受保护权限的构造函数就不能被子例化,博主亲自实验,如果构造函数是私有(private),则该类是不能够被继承的自然无法被子例化。如果使用默认权限,如果子类和父类在同一个包下,则可以被继承和被实例化,否则则不行。在这里,不含有共有的或者受保护权限的构造函数 根本不能够成为父类,所以我认为这不算是静态工厂模式的缺点。

上面提到了私有(private) 构造方法,使用私有构造方法可以阻止外部创建该类对象,强化不可实例化的能力,一般的用于单例模式中。

补充:构建器
如果你的对象需要超过 3 个构造参数,那就使用构建器来构造对象。同时使用构建器还可以完美处理必须属性和可选属性问题 例:
public class Person implements Serializable {
    private String  name;//可选
    private String  hex;//必须要属性
    private int     age;//必须属性

    public Person(Builder builder){
        hex=builder.hex;
        age=builder.age;
        if(builder.getName()!=null&&!builder.getName().equals("")){
            name = builder.getName();
        }
    }

    static class Builder {
        private String  name ;
        private String  hex;
        private int     age;
        Builder (String hex,int age) {
           this.age=age;
            this.hex=hex;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

二 对象的销毁
对象的创建会在jvm上占用一段内存,而jvm的内存并不是无限的,因此一些无用的对象需要被及时销毁,如果存在无用对象,且并没有被销毁就有可能会引发OOM异常。

说起对象的销毁,就不得不提到java 的垃圾回收机制。在这里只是简单的介绍下:java的gc机制是在jvm感觉内存不足的情况下开始运行,gc只会回收new 创建并且无法到达的对象,回收的时候会检查是否复写了finalize() 方法,如果没有会直接回收,如果有则运行finalize()方法,并在下次启动的时候进行回收该对象(gc不定时启动,只会在jvm感觉内存不足时启动,一般的finalize()方法一定会运行,但不能保证即时运行(程序崩溃另算))。
因为java 垃圾回收机制的特性,很有可能会内存泄露。内存泄漏主要是未回收无用对象造成的,主要表现在未回收过期对象,缓存使用和只设置监听不取消监听。
例1:

    public class Stack {
        private Object[] elements;
        private int size=0;
        public Stack(int initialCapacity){
            this.elements=new Object[initialCapacity];
        }
        public void push(Object e){
            ensureCapacity();
            elements[size++]=e;
        }

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

        private void ensureCapacity(){
            if(elements.length==size){
                Object[] oldElements=elements;
                elements=new Object[2*elements.length+1];
                System.arraycopy(oldElements,0,elements,0,size);
            }
        }
}

上面的例子便是因为未回收过期引用,会存在内存泄漏问题。因为在pop弹栈的时候只是返回了一个数组中的对象,而数组中依然存在着该对象的引用。虽然我们(程序员,调用者)知道后面的对象没用了,但是jvm不知道,所以就不会回收该对象,解决方案如下:

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

只要将该数组中的引用消除即可解决该问题。另外的 缓存和监听器设置问题也比较容易。监听器只需要生命周期结束前取消监听即可,缓存的解决方案需要分两类:
1. 只要外部还存在该条目的引用就仍需要存在于缓存中 使用weakHashMap
2. 使用后台线程 定时 清理缓存 或者再添加新的缓存时 清理不需要的缓存(和1的情况相反)

最后这里说一下关于中介函数(finalize)。关于终结函数一般的不推荐使用,因为使用finalize()方法会造成一些性能问题,而且和该方法有关的两个函数也存在着重大缺陷已经被废弃了。同时jvm执行finalize的线程处于较低优先级,很难执行到,释放资源不及时。不过finalize函数还是有使用场景的,其使用也只是用来做一道保险。
因为在java的gc机制中,gc只会回收java new创建的对象,因此对于其他的耗内存如:inputStream ,database和native方法创建的对象(JNI  C/C++创建的对象)无法回收。因为只能通过内部暴露共有方法进行手动回收。finalize方法就是这样的一道保险,防止程序员忘记手动调用而设置的保险,毕竟延迟释放比永不释放要强。
注:终结函数守卫者是和继承相关的,防止子类没有调用父类的中介函数的一道保险。具体的是显示创建一个私有的final的内部类,手动调用外部的finalize方法。
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值