为什么 NULL 是你代码中的无声杀手?

 互联网   2024-06-21 10:55   543 人阅读  0 条评论

为什么 NULL 是你代码中的无声杀手?  第1张

在软件开发领域,很少有问题像NULL值一样普遍存在且危害巨大。这个看似无害的占位符可能会给代码库带来重大问题,造成难以发现的漏洞,并损害数据完整性。尽管 NULL 有其用途,但处理不当且不了解其含义可能会导致严重问题。我们将在这篇详尽的博客文章中探讨 NULL 被称为代码中的无声杀手的原因,提供代码示例来展示其后果,并讨论减轻其负面影响的方法。

1. 简介

NULL 是一个看似简单的概念,表示没有值。尽管 NULL 很简单,但它却因在软件应用程序中引起大量问题而臭名昭著。Tony Hoare 于 1965 年引入了 NULL 引用的概念,他经常将其称为“十亿美元的错误”。这篇文章旨在探讨 NULL 为何如此成问题,并为开发人员提供有效处理它的实用解决方案。

2. NULL 的概念

NULL 是编程中用于表示变量没有值的特殊标记。它不同于零、空字符串或任何其他“假”值。NULL 的存在可能表示未初始化的变量、缺失的数据或可选字段。

NULL 的问题

NULL 的根本问题是它的歧义性。它可能意味着:

  • 值未知。
  • 值不存在。
  • 值不适用。

这种模糊性可能会导致混乱和错误,尤其是在没有充分检查或处理 NULL 值时。

3. NULL 导致的常见问题

空指针异常

与 NULL 相关的最臭名昭著的问题之一是 NULL 指针异常 (NPE)。当程序尝试使用预期为对象但实际上为 NULL 的引用时,就会发生这种情况。在许多编程语言中,取消引用 NULL 指针会导致运行时错误,从而导致程序崩溃。

Java 示例:

public class NullPointerExample {
    public static void main(String[] args) {
        String str = null;
        try {
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("Caught a NullPointerException");
        }
    }
}


数据不一致

数据库中的 NULL 值可能会导致查询出现不一致和意外结果。例如,涉及 NULL 值的聚合和连接可能无法按预期运行。

SQL 示例:

SELECT AVG(salary) FROM employees WHERE department_id 10;

从员工中选择AVG (薪水) ,其中部门 ID = 10 ;如果某些工资值为空,则平均值计算可能会排除这些条目,从而导致结果偏差。

额外绩效开销

处理 NULL 值通常需要在代码中进行额外的检查和分支,这会带来性能开销。这在性能至关重要的应用程序中尤其成问题,因为每微秒都至关重要。

4. 真实世界的代码示例


Java 中的 NULL

Java 开发人员经常遇到与 NULL 相关的问题,主要是 NULL 指针异常。Java 的类型系统允许将 NULL 分配给任何对象引用,这很容易引入错误。

例子:

public class Employee {
    private String name;
    private Integer age;
    public Employee(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public Integer getAge() {
        return age;
    }
}
public class NullExample {
    public static void main(String[] args) {
        Employee emp = new Employee(nullnull);
        try {
            System.out.println(emp.getName().toUpperCase());
        } catch (NullPointerException e) {
            System.out.println("Caught a NullPointerException");
        }
    }
}


Python 中的 NULL

Python 使用关键字处理 NULL 值None。虽然 Python 是动态类型的,这降低了一些风险,但如果处理不当,NULL 值仍然会导致运行时错误。

例子:

def print_length(s):
    try:
        print(len(s))
    except TypeError:
        print("Caught a TypeError because the input was None")
print_length(None)

SQL 中的 NULL

SQL 数据库使用 NULL 来表示缺失或未知值。但是,涉及 NULL 的运算可能会产生意外结果,尤其是在相等和聚合方面。

例子:

SELECT * FROM employees WHERE manager_id = NULL; --不会返回任何 rows
SELECT * FROM employees WHERE manager_id IS NULL; -- 检查NULL的正确方法


5. 处理 NULL 的策略

使用可选类型

许多现代语言都提供可选或可空类型,以明确处理值的存在或不存在。这种方法鼓励开发人员考虑 NULL 的可能性并进行适当处理。

Java 示例(使用Optional):

import java.util.Optional;
public class Employee {
    private Optional<String> name;
    private Optional<Integer> age;
    public Employee(Optional<String> name, Optional<Integer> age) {
        this.name = name;
        this.age = age;
    }
    public Optional<String> getName() {
        return name;
    }
    public Optional<Integer> getAge() {
        return age;
    }
}
public class OptionalExample {
    public static void main(String[] args) {
        Employee emp = new Employee(Optional.of("John Doe"), Optional.empty());
        emp.getName().ifPresent(name -> System.out.println(name.toUpperCase()));
        emp.getAge().ifPresentOrElse(
            age -> System.out.println("Age: " + age),
            () -> System.out.println("Age not available")
        );
    }
}


默认值和保护

设置默认值并使用保护可以帮助避免与 NULL 相关的问题。这在配置和可选字段中特别有用。

Python 中的示例:

def get_employee_name(employee):
    return employee.get('name''Unknown')
employee = {}
print(get_employee_name(employee))  # Outputs 'Unknown'

NULL 对象模式

NULL 对象模式涉及创建一个表示默认行为的对象,而不是使用 NULL。这可以帮助避免 NULL 检查并使代码更具可读性。

Java 示例:

public interface Employee {
    String getName();
}
public class RealEmployee implements Employee {
    private String name;
    public RealEmployee(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
}
public class NullEmployee implements Employee {
    @Override
    public String getName() {
        return "No Name Available";
    }
}
public class NullObjectPatternExample {
    public static void main(String[] args) {
        Employee emp = getEmployee(false);
        System.out.println(emp.getName());
    }
    public static Employee getEmployee(boolean isReal) {
        if (isReal) {
            return new RealEmployee("John Doe");
        } else {
            return new NullEmployee();
        }
    }
}


数据库约束和默认值

使用数据库约束和默认值可以防止将 NULL 插入不适当的字段。这确保了数据库级别的数据完整性。

SQL 示例:

CREATE TABLE employees (
 id INT PRIMARY KEY,
 name VARCHAR(100) NOT NULL,
 salary DECIMAL(10, 2) DEFAULT 0.00
);
INSERT INTO employees (id, name) VALUES (1, 'John Doe'); -- salary will default to 0.00


6.结论

NULL 通常是代码中的隐形杀手,会导致微妙的错误、崩溃和数据不一致。通过了解它可能引起的问题并实施强大的策略来处理它,开发人员可以显著提高其应用程序的可靠性和可维护性。无论是通过使用可选类型、设置默认值、采用 NULL 对象模式还是强制执行数据库约束,都有许多方法可以减轻与 NULL 相关的风险。


采用这些最佳实践不仅有助于编写更安全、更可预测的代码,还可以提高软件系统的整体质量。请记住,解决问题的第一步是识别问题,对于任何旨在构建弹性和健壮应用程序的开发人员来说,承认 NULL 的潜在陷阱都至关重要。



免责声明:本文内容来源于网络,所载内容仅供参考。转载仅为学习和交流之目的,如无意中侵犯您的合法权益,请及时联系Docker中文社区!



为什么 NULL 是你代码中的无声杀手?  第2张


本文地址:https://dockerworld.cn/?id=382
温馨提示:文章内容系作者个人观点,不代表Docker中文社区对观点赞同或支持。
版权声明:本文为转载文章,来源于 互联网 ,版权归原作者所有,欢迎分享本文,转载请保留出处!

 发表评论


表情

还没有留言,还不快点抢沙发?