创建与销毁对象

创建与销毁对象

利用静态方法代替构造方法

对于一个类来说,获取类的实例方法默认是通过其构造函数来获取,但是还有一种方法就是通过静态方法来获取对象的实例,例如Boolean类的valueOf方法就是如此

静态工厂相比于默认的构造方法有优势也有劣势,具体如下:

优点:

  • 静态工厂方法相比于构造方法来说,静态工厂方法是有名字的,传统的构造方法只能通过传入不同的参数来明确被返回的对象,但是如果通过静态方法的名称可以更容易读懂该方法,如果一个类有多个构造,只是传递的参数不同,如果没有具体的文档很难确定使用哪个
  • 使用静态方法创建对象,在一定的情况下可以返回相同的对象 ,在某些情况下(例如使用单例模式)可以通过静态方法来返回同一个对象,但是通过构造方法来说这是无法实现的
  • 静态方法的返回对象可以是其子类,但是构造方法只能是该对象本身 ,因此有更好的灵活性,在java8之后,接口可以拥有静态方法,这样就可以在接口中定义一个静态方法,这样创建的接口就可以放在其本生了
  • 静态方法可以随着传递的参数不同,返回的对象也会随着变化 ,静态方法返回的对象的子类都是可以根据传递的参数的变化产生具体的变化
  • 相对于构造方法来说,静态方法返回的类的子类可以在方法创建的时候不存在,类似于提供了一个接口,但是具体的实现类可以在之后补上,接口只是定义了一种规范而已

缺点:

  • 静态方法返回的对象如果是私有的,那么该对象则无法被子类继承或者实现
  • 静态方法很难被发现,在java生成的api文档中标识除了构造方法,但是对于静态工厂方法并没有,而是和普通的方法一样,下面就是一些常用的静态工厂方法的名称
    • from一一类型转换方法,它只有单个参数,返回该类型的一个相对应的实例
    • of ——聚合方法,带有多个参数,返回该类型的一个实例
    • valueOf一一比from 和of 更烦琐的一种替代方法
    • instance或者getInstance——返回的实例是通过方法的参数来描述的,单例模式用法比较多
    • create 或者newInstance一一对象instance 或者getinstance 一样,但create或者newInstance 能够确保每次调用都返回一个新的实例

遇到多构造参数是考虑使用构造器模式

静态方法和构造函数有个共同的局限性就是对于大量参数的对象无法做很好的扩展,而以下有几种方式可以进行扩展:

  • 重叠构造器是一个方法,但是当许多参数时,客户端代码很难读写,而且使用重叠构造器会是构造器会随着参数的扩展会失去控制,重叠构造方法就是在调用第一个构造方法的时候,构造方法会调用其他的构造方法
  • 使用javaBean模式来完成,先创建一个无参的构造函数,然后通过调用bean的set方法来对对象进行赋值,但是使用这种方法对象在不同时刻处于不一致的状态,而且该对象很难去校验和调试,并且对象的不可变的特性也无法保证
  • 使用builder模式来完成,builder模式的属性是通过javabean来完成属性的注入,然后通过builder方法来构建出要使用的对象,校验的数据都是通过builder来完成的,但是builder模式每次构建一个对象都需要一个builder对象来完成,总之builder构建更加的易读且安全。

用私有构造器或者枚举来强化Singleton属性

Singleton是指被实例化一次的类(单例模式)。Singleton一般是全局唯一的组件。类变成单例的会使测试特别的困难,因为不能给单例完成模拟实现。

单例模式一般会使构造方法变成私有的构造方法,然后通过一个静态方法返回当前类的对象。切当前对象为final的在初始化的时候创建一次,但是在多线程、序列化、或者反射的情况下还会出现非单例情况,因此需要单独处理

还可以使用只有单个元素的枚举来完成,因为单个元素的枚举里面只有一个对象,因此在以上几种情况下都是只有一个值得。

通过私有构造器来强化不可实例化的能力

对于一些工具类来说,他的内部都是静态的方法和对象,因此这些对象都不需要创建实例,但是类本身还是会提供默认的无参的构造函数,对于使用者来说这个构造器和其他的构造器没有任何区别但是没有任何意义

对于把类做成抽象的是一种不可取的行为,因为抽象的类是可以被继承的,且它的子类可以被实例化,甚至还会误导用户这个类就是为了继承而这样设计的。因此只有当类有显示的构造方法,才不会生成默认的无参构造,所以为了使类不可实例化,应该让这个类拥有一个私有的构造函数如果为了避免该类通过反射被实例化,可以在构造方法中添加校验

优先使用依赖注入来引用资源

依赖注入相比于静态工作类和Singleton来说更加的灵活,他可以通过在构建的时候通过动态的传递来保证程序运行的灵活,还可以通过构造器传递静态工厂来动态创建各种工厂对象。但是这样使用依赖注入会使以来注入无法管理,从而感觉类之间的关系很混乱(spring等框架除外)。总而言之,就是通过把资源或者工厂传给构造器来实现以来注入,通过资源和工厂来生产对象,这样可以提高类的类的灵活性,可重用性和可测试性

避免创建不必要的对象

通过new的方式来创建单个对象是很方便的,但是相对于String这种不可变的对象,就可以通过重用的方式来避免同一个String对象被创建多次。

String str = new String("abc");

因为java内存中存在着String的常量池,可以直接使用,但是通过new还会额外创建出多余的对象,造成性能的损失,因此可以使用String str ="abc" 来替换上面的代码实现对象的重用。

对于那些提供了静态工厂方法的类,使用静态工厂方法可以避免创建不必要的对象。

在java中制动拆箱和装箱时另外一种创建多余类的方式,装箱类和基础数据类型在自动拆箱的时候变得模糊,但是还没有完全消除,性能会有一定的差距(见代码),使用装箱类的时候在每次运算的时候,程序都需要把计算结果装箱,然后在拆箱,相比于基础数据类型来说对性能造成了很大的影响。


    public static void sum(){
//        long sum = 0L; 868
        Long sum = 0L; // 8600
        long begion = System.currentTimeMillis();
        for(int i = 0; i < Integer.MAX_VALUE;i++){
            sum += i;
        }
        System.out.println(System.currentTimeMillis()-begion);
        System.out.println(sum);
    }

因此要尽量使用基础数据类型,从而避免无意思的拆箱和装箱

避免创建不必要的对象不是要说明创建对象的代价很大,而是为了尽量避免创建,相比于对象池来说,除非是维护特别重量的对象,否则维护一个对象池来说不是一个好的做法。因为维护对象池会对内存占用,影响性能和程序的代码。

消除过期的对象引用

相比于c++之类的语言来说,java的内存管理都交给了jvm来管理,因此会有种内存不需要管理的错觉。相对于jvm回收内存,因为程序中的内存泄漏会导致内存占用越来越大,程序的性能会产生一定的影响,甚至会产生内存溢出。

在jvm中如果一个对象的引用被保存在了栈中,则它不会被jvm所回收,那么它所应用的对象也就不会被回收,即使只有几个对象也会导致很多对象避免被回收

在程序开发中如果对无效的对象清空引用有可能引起空指针异常,而且为了清空无效对象应用增加额外的代码会感觉特别的乱。因此,清空对象应用并不是一种规范,只能是一种方法

如果要写一些容器类来管理内存的时候就应该警惕内存溢出的问题,如果在容器类清理的时候,类内部的元素没有清除,就会产生溢出的问题

而在程序开发中,如果把对象放到缓存中也会产生内存溢出的问题,因此在使用缓存的时候也要格外注意对象的生命周期,对于超过生命周期的对象可以进行删除

内存泄漏的最后一个主要来源是监听器和回调方法,在使用监听的时候随着监听一个个的累加,如果在程序中没有做出对应的处理则会产生

避免使用终结方法或者和清除方法

在java开发中终结方法和清除方法都是不可预测的,运行时间慢,一般情况下不赞同使用。

在终结方法被调用是在的一个对象已经不可达的情况下才会被调用,这样就会导致程序执行中介方法的时间被无限制的延长,因此一些有时限的代码不应该被放到终结方法里去运行。在一些情况下终结方法就不会被运行,因此更新持久化数据的操作千万不要放到终结方法里去执行。

终结方法的创建会对性能产生严重的问题,在一个有终结方法的对象(550ns)和一个没有终结方法(12ns)的对象执行同一段代码性能相差40多倍,因此如果要在终结方法上增加一道过滤,类似关闭一些流之类的最好考虑一下性能的影响

终结方法有个严重的安全问题:它们为终结方法攻击( finalizer attack ) 打开了类的大门。终结方法攻击背后的思想很简单:如果从构造器或者它的序列化对等体( readObject和readResolve 方法,详见第12 章)抛出异常,恶意子类的终结方法就可以在构造了一部分的应该已经半途夭折的对象上运行。这个终结方法会将对该对象的引用记录在一个静态域中,阻止它被垃圾回收。一旦记录到异常的对象,就可以轻松地在这个对象上调用任何原本永远不允许在这里出现的方法。从构造器抛出的异常,应该足以防止对象继续存在;有了终结方法的存在,这一点就做不到了。这种攻击可能造成致命的后果。fina l 类不会受到终结方法攻击,因为没有人能够编写出final 类的恶意子类。为了防止非final 类受到终结方法攻击, 要编写一个空的final 的finalize 方法。

try-with-resource 优先于 try-finally

在java中 try-with-resource 和try-finally对于关闭资源来说是效果是一样的,try-with-resource只是jdk的一个语法糖,try-with-resource经过jdk编译以后和try-finally的效果是一样的,但是对于程序的开发,阅读更优雅的展示,而且jdk会优先保留第一个异常,以后的异常会被禁止,但是异常信息会被放到堆栈中可以通过getSuppressed方法去调用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值