关于创建、销毁对象⭐Java程序员需要掌握的8个编程好习惯

关于创建、销毁对象⭐Java程序员需要掌握的8个编程好习惯

本文基于Effective Java中创建和销毁对象的章节汇总出8个相关的好习惯(文末附案例地址)

思维导图如下:

静态工厂代替构造器

1.可以自定义名称,见名知意

见名知意的方法更容易理解,比如 getInstanceByCode 根据Code获取实例

2.获取对象时,可以使用单例、享元等思想进行复用

java 复制代码
Boolean value = Boolean.valueOf(true);
​
public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

3.可以返回原类型的子类

java 复制代码
Collections.singletonList(null);
​
public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

4.返回对象可以随着入参发生改变

根据入参key的不同可以获取不同的策略

java 复制代码
public static <T extends XXStrategy> T getStrategyByKey(String key) {
    return (T) strategyFactory.get(key);
}

5.返回对象的类可以在编写静态工厂时不存在

在编写静态工厂中获取对象时,对象可以不存在(未编译),通过反射的手段在运行时加载进来

典型的例子就是JDBC,在使用前通过反射加载驱动

java 复制代码
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("url", "username", "password");

小结

业务中使用场景挺多的,比如:spring的getBean、实现业务的策略工厂(基本上能覆盖1-5特点)

策略工厂可以使用Map维护code以及对应的策略,这样就已经实现1、2、4

实现3时还需要使用泛型进行转换(上面案例给出),使用反射实现5

多个构造器参数考虑建造者模式

构造器的入参太多时容易弄混,还好现在的IDE开发工具可以提示参数名称,如果参数名称取的一团糟也会容易混淆

在这种场景下可以选择建造者模式 Builder

java 复制代码
        //构造器
        Student student = new Student("张三", 18, "男", "北京", "13812345678", "", "12345678");
        
        //Builder
        Student.builder()
                .name("菜菜的后端私房菜")
                .age(18)
                .sex("男")
                .address("北京")
                .phone("13812345678")
                .email("")
                .qq("12345678")
                .build();

提高阅读性、多建一个类的开销、可以防止对象在构造期间逃逸

私有构造或枚举强化单例

私有构造防止调用构造创建

可以通过字段直接获取单例,也可以通过静态工厂方法获取单例 (饿汉式)

java 复制代码
//通过字段
SingletonField singletonField = SingletonField.INSTANCE;
//通过静态工厂方法
SingletonStaticFactory singletonStaticFactory = SingletonStaticFactory.getInstance();
//通过枚举
SingletonEnum singletonEnum = SingletonEnum.INSTANCE;

通过反射、JDK序列化依旧可以调用构造,破坏单例

使用枚举避免反射调用单例

反射在实例化前会判断是否枚举,枚举则抛出异常

java 复制代码
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");

考虑依赖注入

构造器注入,能够灵活使用多种依赖

java 复制代码
    static class DependencyInjectionNotUse {
        private Util util = new Util();
​
        public DependencyInjectionNotUse() {
        }
    }
​
    static class DependencyInjectionUse {
        private Util util;
​
        public DependencyInjectionUse(Util util) {
            this.util = util;
        }
    }

依赖太多在大项目中会很乱,可以使用DI框架如 spring的IOC

避免创建不必要的对象

java 复制代码
//创建了不必要对象
String a = new String("菜菜的后端私房菜");
​
String b = "菜菜的后端私房菜";

自动拆装箱,也会创建不必要对象(先转成long相加,再转Long)

java 复制代码
        Long sum = 0L;
        for (long i = 0; i <= Integer.MAX_VALUE; i++) {
            sum += i;
//            Long.valueOf(sum = sum.longValue() + i);
        }

多使用单例、享元模式,复用资源,避免创建不必要对象:字符串、自动拆装箱等

消除过期的对象引用

Java采用引用计数法,如果不再使用对象时需要置空,否则会认为还要使用,从而导致内存泄漏

java 复制代码
    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        //出栈时需要将元素清除,不再引用
        //elements[size-1] = null
        return elements[--size];
    }

缓存、监听器也可能导致内存泄漏,注意使用弱引用或注意关闭

不使用finalize方法

finalize 由守护线程执行,无法预估执行的时机

finalize 为不可达对象才会执行的方法,如果要清楚资源使用finally关闭资源

感兴趣的同学可以查看这篇文章:如何判断对象"已死"

try-with-resources优于try-finally

twr会自动关闭资源,优先使用

java 复制代码
        //try-finally
        BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            br.close();
        }
​
        //twr try-with-resources
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        }

总结

考虑使用静态工厂方法代替构造器,静态工厂方法能够见名知意、可以使用单例或享元模式返回对象、搭配泛型返回对象可以是原类型子类、返回的对象可以随着入参改变、返回的对象所在的类可以在编译期不存在

在参数较多时可以考虑使用建造者模式,可以代码可读性更高、防止构造对象期间发生逃逸

可以通过私有构造强化单例,但能够被反射、序列号破坏单例;使用枚举单元素强化单例则可以避免破坏(在反射实例化前判断为枚举则抛出异常)

对象依赖的"工具"不是固定的时,可以采用依赖注入DI的方式进行改变,而不是直接写死;频繁使用DI在大项目中会比较混乱,使用DI框架可以解决,比如 Spring 的IOC

避免创建不必要的对象,如String的字符串常量、基本类型与包装类型的自动拆装箱

消除过期的引用对象:不再使用对象时,需要消除引用关系,否则基于引用计数法的Java则无法给对象进行回收,从而导致内存泄漏

不使用finalize方法:finalize方法是只有对象成为不可达对象才会调用,而且由守护线程执行,无法预估执行时机,不要使用其做清理工作

try-with-resources优于try-finally:twr自动关闭资源,避免忘记,在多层嵌套时阅读性也较好

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 Effective Java,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 gitee-StudyJavagithub-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

相关推荐
芒果披萨11 分钟前
El表达式和JSTL
java·el
许野平37 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
duration~1 小时前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
独行soc2 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
暗黑起源喵2 小时前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong2 小时前
Java反射
java·开发语言·反射
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试