Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

这是AI翻译的帖子。

제이온

[Effective Java] 項目 6. 避免不必要的物件建立

  • 写作语言: 韓国語
  • 基准国家: 所有国家 country-flag

选择语言

  • 汉语
  • English
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

durumis AI 总结的文章

  • 使用 new 關鍵字建立字串或 Boolean 物件會浪費記憶體,因此最好使用字面值宣告,或使用 Boolean.valueOf() 方法。
  • String.matches() 方法會使用正規表示式,因此可能會導致效能問題,最好快取 Pattern 物件並重複使用。
  • 對於像 keySet() 方法一樣返回檢視物件的情況,使用防禦性複製返回新物件會更安全。

在创建不必要对象的情况下

使用 new String()

String a = new String("hi");
String b = new String("hi");


字符串 a、b、c 都具有“hi”字符串。但是,由于这三个字符串引用的地址都不同,因此会导致为相同数据分配不同内存的浪费。


Untitled


因此,在声明字符串时,应使用字面量,而不是 new 关键字。


String a = "hi";
String b = "hi";


以上代码只使用一个实例。此外,使用这种方法可以确保在同一个 JVM 中使用“hi”字符串字面量的所有代码都重用同一个对象。这是由于 Java 常量池的特性。


使用 new Boolean()

以上代码通过接收字符串作为参数的构造函数来创建 Boolean 实例。Boolean 只有 true 或 false,每次创建实例都会浪费内存。因此,最好使用静态工厂方法 Boolean.valueOf()。



使用 String.matches()

如果创建成本很高,最好缓存并重用它,但我们并不总是能够知道我们创建的对象的成本。例如,如果我们想编写一个方法来检查给定的字符串是否为有效的罗马数字,我们可以使用正则表达式,如下所示。


public static boolean isRomanNumeral(String s) {
    return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");


但是 String.matches() 方法在性能方面存在问题。该方法在内部创建的用于正则表达式的 Pattern 实例在使用一次后就被丢弃,并立即成为垃圾回收的目标。如果该正则表达式被重复使用的频率很高,则创建和丢弃相同的 Pattern 实例的成本就会很高。因此,最好预先缓存 Pattern 实例,并在以后每次调用 isRomanNumeral() 方法时重用它。


public class RomanNumerals {

    private static final Pattern ROMAN = Pattern.compile(
        "^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

    public static boolean isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }


注意

在所有以上示例中,在缓存不必要对象时,我们都将其创建为不可变对象。这是因为这样做才能确保安全地重用它们。但是,在某些情况下,这与重用的直觉相矛盾。


适配器(视图)是一种将实际操作委托给后端对象,而自身充当第二个接口的对象。适配器只需要管理后端对象,因此对于每个后端对象,我们只需要创建一个适配器。


例如,Map 接口的 keySet() 方法返回包含 Map 对象中所有键的 Set 视图。用户可能会认为每次调用 keySet() 方法时都会创建一个新的 Set 实例,但实际上,查看 JDK 实现的内容,会发现它始终返回同一个可变 Set 实例。


这是因为即使返回的 Set 实例是可变的,它执行的功能也都是相同的,并且所有 Set 实例都代表 Map 实例。因此,即使 keySet() 创建多个视图对象,也无关紧要,也没有必要这样做,也没有任何好处。


public class UsingKeySet {

    public static void main(String[] args) {
        Map menu = new HashMap<>();
        menu.put("Burger", 8);
        menu.put("Pizza", 9);

        Set names1 = menu.keySet();
        Set names2 = menu.keySet();

        names1.remove("Burger");
        System.out.println(names1.size()); // 1
        System.out.println(names2.size()); // 1
    }


因此,如果修改 names1 实例,names2 实例也会受到影响。


但是,我个人认为 keySet() 方法的返回值应该使用防御性复制,每次都返回一个新对象。如果从 keySet() 方法接收到的 Set 实例在其他地方也使用,并且有代码更改该实例的状态,那么我们无法确定当前使用的 Set 实例和 Map 实例的值。


此外,除非过度使用 keySet(),否则 Set 接口的每次创建都不会对性能产生致命影响。我认为最好将 Set 接口创建为不可变对象,以便稳定地维护和修复它。


自动装箱

自动装箱是指在程序员混合使用基本类型和包装类型时,自动进行相互转换的技术。但是,自动装箱只会模糊基本类型和包装类型之间的界限,并不能完全消除它们。


public static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    return sum;


从逻辑上讲,没有问题,但在性能上非常低效。其原因在于 sum 的类型和 for 循环中的 i 的类型。


sum 的类型为 Long 类型,i 为 long 类型。这意味着当 long 类型的 i 在循环中不断增加并添加到 sum 中时,每次都会创建一个新的 Long 实例。因此,最好使用基本类型而不是包装类型,并注意避免使用意外的自动装箱。


不要误解

不要误解避免创建不必要对象的含义,即由于创建对象的成本很高,因此应该避免创建它们。


特别是现在 JVM 创建和回收不必要的小对象的成本并不高。因此,除非是数据库连接等成本非常高的对象,否则不要创建自定义对象池。


此外,请记住,在需要防御性复制的情况下重用对象所带来的损失,远大于不必要地重复创建对象所带来的损失。重复创建对象的副作用只会影响代码形式和性能,而防御性复制失败会导致错误和安全问题。


来源

제이온
제이온
제이온
제이온
[Effective Java] 项目 5. 不要显式地使用资源,而应该使用依赖注入 如果类依赖于外部资源,则最好不要使用单例和静态实用程序类。通过依赖注入,可以提高类的灵活性、可重用性和可测试性,而使用工厂方法模式可以使依赖注入更加高效。

2024年4月28日

[Effective Java] 項目 1. 考慮使用靜態工廠方法而非建構函式 靜態工廠方法是一種比建構函式更靈活且有效率的建立實例的方法。它們可以具有名稱,並可返回符合特定條件的實例,並可透過快取來提高效能。與單例模式不同,靜態工廠方法可以建立和返回多種類型的實例,並且可以保持彈性,而無需使用反射。

2024年4月27日

[有效 Java] 項目 4. 要阻止實例化,請使用私有建構函數 僅包含靜態方法和欄位的工具類,最好將建構函數的存取修飾符設定為 private,以阻止實例化。 這可以防止使用者誤認為建構函數是自動生成的,並使繼承變得不可能,從而明確表示類別的意圖。

2024年4月28日

[Javascript] 物件的結構 (V8) JavaScript 的 Object 在 V8 引擎中根據狀態可以被優化為類似結構體的 Fast 模式或以雜湊表運作的 Dictionary 模式。Fast 模式是針對幾乎固定形式的鍵和值進行優化,速度很快,但當新增新鍵或刪除元素等操作時, 會轉換為 Dictionary 模式,速度會變慢。
곽경직
곽경직
곽경직
곽경직
곽경직

2024年3月18日

Rust 如何防止并发错误 Rust 是一种强大的语言,可以解决并发编程的难题。 由于其类型系统和所有权模型,线程之间的数据传递和共享是安全的。 通过内部可变性模式,例如 Mutex、Channel 和 Atomic,可以定义共享变量并安全地使用它们。 Rust 积极解决并发问题,使其广泛应用于大型系统开发。
곽경직
곽경직
곽경직
곽경직
곽경직

2024年3月28日

[非计算机专业,如何成为一名开发者] 14. 新手开发者常问的技术面试内容总结 本指南旨在为新手开发者提供技术面试准备指导。涵盖了面试中常见的概念,例如主内存区域、数据结构、关系型数据库 (RDBMS) 和 NoSQL、过程式编程和面向对象编程、重写和重载、页面替换算法、进程和线程、OSI 七层模型、TCP 和 UDP 等。
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

2024年4月3日

[并发] 原子操作:内存栅栏和内存顺序 这篇博文将解释在原子操作中如何考虑内存顺序,以及排序选项的重要性。 它将详细解释各种排序选项,例如 Relaxed、Acquire、Release、AcqRel 和 SecCst,以及每个选项的优缺点, 并提供使用示例代码。
곽경직
곽경직
곽경직
곽경직
곽경직

2024年4月12日

什麼是數據標註?類型、優點、缺點 數據標註是幫助電腦理解數據的必要過程,就像在狗和貓的照片上分別貼上“狗”和“貓”的標籤一樣, 通過在數據上添加標籤,使電腦學習成為可能。有矩形、點、多邊形等多種標註方式, 通過這些方式,可以在計算機視覺、語音識別、自然語言處理等多個領域提高人工智慧模型的性能。
세상 모든 정보
세상 모든 정보
세상 모든 정보
세상 모든 정보

2024年3月29日

[非理工科背景,如何成为开发者] 16. 新手开发者作品集编写技巧 新手开发者(尤其是非理工科背景)在编写作品集时,除了技术之外,还需要明确说明所开发的服务或功能。例如,在“求职者社区” 项目中,需要包含问答论坛、招聘系统、爬虫机器人开发等具体工作内容,以便面试官更好地了解项目。
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

2024年4月3日