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

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

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

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

思维导图如下:

image.png

1.静态工厂代替构造器

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

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

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

Boolean value = Boolean.valueOf(true);
public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

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

Collections.singletonList(null);
public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

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

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

public static <T extends XXStrategy> T getStrategyByKey(String key) {
    return (T) strategyFactory.get(key);
}

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

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

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

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

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

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

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

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

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

3.私有构造或枚举强化单例

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

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

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

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

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

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

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

4.考虑依赖注入

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

    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

5.避免创建不必要的对象

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

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

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

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

6.消除过期的对象引用

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

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

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

7.不使用finalize方法

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

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

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

8.try-with-resources优于try-finally

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

        //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-CaiCaiJavaGithub-CaiCaiJava 感兴趣的同学可以stat下持续关注喔~

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

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

相关文章
|
1月前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
13天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
17天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
50 12
|
14天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
96 2
|
2月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
30天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
30天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
51 3
|
1月前
|
开发框架 安全 Java
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
50 3
|
2月前
|
安全 Java 开发者
Java中的多线程编程:从基础到实践
本文深入探讨了Java多线程编程的核心概念和实践技巧,旨在帮助读者理解多线程的工作原理,掌握线程的创建、管理和同步机制。通过具体示例和最佳实践,本文展示了如何在Java应用中有效地利用多线程技术,提高程序性能和响应速度。
70 1