Location>code7788 >text

I work in a large factory CR - how to systematize the prevention and control of air pointer anomalies

Popularity:155 ℃/2024-10-21 02:15:55

Hello, I'm Muwan brother, today I'd like to share with you - code CR against the annoying null pointer exception (NullPointerException) How to do systematic de-control;

What is a null pointer exception

From a memory perspective, the instantiation of an object requires the allocation of space in heap memory. If an object is not created, no memory is allocated, and when the application accesses an empty object, it is actually accessing an "invalid" area of memory, which causes the system to throw an exception.

When we program in Java, null pointer exception is a common runtime error that can be serious and even cause the process to exit. So that's why we have to pay so much attention to it at CR time.

CR What are we going to do?

Muwango believes that CR should focus on three points:

  • Business Logic Correctness
  • Code readability and maintainability
  • Code robustness and stability

OK, going back to the null pointer exception, when CRing, it is more important to cut from the robustness and stability of the code, which can be categorized into:

  • Defensive de-elimination of null pointer exceptions (mostly accessing objects that don't exist)
  • Potential null pointer exceptions due to imperfect knowledge of third-party frameworks or the JDK (more on this from review participants)

Defensive Programming

Defensive programming is very necessary, on the one hand, it can improve the system stability and robustness, on the other hand, it can form a better code specification; at the same time, it is also a very important idea, everyone will be so practiced, the defense against null pointer exceptions at the CR iscommon sense; for example:

1. The defense uses uninitialized objects:

MyObject obj = null;
if (obj!= null){
    ();
}

2. The defense uses fields that are not initialized by the object;

class MyClass {
   String name;
}
MyClass obj = new MyClass();
if ((())){
    // do something
}

3. Defend against attempting to invoke a method or property on the returned object after the method call returns null:

MyObject obj = getMyObject(); // assumed to return null
if (obj ! = null) {
    (); }
}

4. The defense accesses an element that does not exist in the collection:

  • () method returns null
  • Queue methods such as poll() or peek() return null.
Map<String ,String> dummyMap = new HashMap<>();
String value = ("key");
if (.(value)){
    // do something
}

Null Pointer Exception Caused by Improper Use of Tripartite Frameworks or JDKs

This category is more of a three-way framework or the internal mechanism of the JDK is not clear resulting in stepping on the pit, only stepped on this kind of category.

It's only then that it dawns on you, "Oh, so that's it, I'll have to pay attention next time."

Therefore, for this kind of problems, it is more necessary to review the experience of the participants to find out, and the team needs to co-create and build a knowledge system, such as: maintaining the "TOP 100 Treading Pitfalls" in the team space, and so on;

In the previous article, "Why it is recommended to use enumerations to replace Boolean values", Muwango mentioned thatBoolean because ofnull The third result, which occurs when the susceptibility toif Conditional judgment unboxing triggers null pointer issues, and we'll continue to share the rest today:

1. Mimic operator unboxing null pointer problem

int var1 = 20;
Integer var2 = null;
boolean condition = true;
// NullPointerException for trinomial operator unboxing problem
(condition ? var2 : var1); // NullPointerException.

Here:condition because oftrue, so the trinomial operator chooses thevar2(i.e.)null). To wit:var2Integer type) assigned to thenumInteger (Type). Theoretically here it should benum is assigned asnull

However, in Java, the return type of the trinomial operator needs to be deduced from the type:

  • in the event thatvar1 beint type, whereasvar2 beInteger types, the trinomial operator will combine their type derivations so that the return value isint Type.
  • This means that ifcondition because oftrueInstead, it will try to set thevar2null) Unboxing intoint. As a result ofnull Cannot be unpacked intointand therefore throws theNullPointerException

These kinds of typical problems need to be exposed more in advance at CR time to ensure consistent parameter types to avoid unboxing;

2. log printing using fastjson serialization caused by the null pointer problem

Most programmers programming development habits, like to print parameters to the log, but sometimes an inconspicuous Printing logs can potentially cause interface exceptions;

The following prints the log with the resultfastjson Serialization exception that occursNullPointerException

@Test
public void testJSONString() {
    Employee employee = new Employee("jack", 100);
    //fastjson serialization exception,occurrence NullPointerException
    (logger,"{}",(employee));
}

static class Employee {

    private EmployeeId employeeId;
    private String name;
    private Integer salary;

    public Employee(String name, Integer salary) {
         = name;
         = salary;
    }

    public String getName() {
        return name;
    }

    public Integer getSalary() {
        return salary;
    }

    public String getEmployeeId() {
        return ();
    }
}

static class EmployeeId {
    private String id;
    public String getId() {
        return id;
    }
    public void setId(String id) {
         = id;
    }
}

for this reasonfastjson utilization(employee) When serialized into JSON, the underlying layer actually parses it by parsing theget opening method to identify the attribute, i.e., calling theget method to get the attribute'svalue Value; above code:employeeId because ofnull but the serialization executes thegetEmployeeId Null pointer exception raised;

So: especially when people are practicing DDD, because domain models tend to be congestive models, containing not only data but also behaviors, for which it may be customary to haveget Start naming, with special attention to serialization issues when printing the domain model;

3. Null pointer exception caused by imperfect knowledge of Stream stream operation

Be very careful if there are null values in the Stream stream.

For example, if the first element happens to benullfindFirst() will throwNullPointerException. This is becausefindFirst() Returns aOptionalbut (not)Optional Cannot contain null values.

(null, 1, 2).stream().findFirst();//occurrence NullPointerException

max()min() cap (a poem)reduce(), also exhibits similar behavior. Ifnull is the final result, an exception is thrown.

List<Integer> list = (null, 1, 2); var comparator = Comparator.<Integer>nullsLast(());
var comparator = Comparator.<Integer>nullsLast(());
(().max(comparator));//NullPointerException occurred

Another example: we are using theStream When streaming, if the stream containsnullcan be converted totoList() maybetoSet()

However.toMap() Note that null values are not allowed (null Keys are allowed):

Employee employee1 = new Employee("Jack", 10000);
Employee employee2 = new Employee(null, 10000);
//toMap(used form a nominal expression)ValueCannot be empty,hereby anomaly
Map<Integer, String> salaryMap = (employee1, employee2)
    .stream()
    .collect((Employee::getSalary, Employee::getName));

As well:groupingBy() Empty Keys are not allowed:

Employee employee1 = new Employee("Jack", 10000);
Employee employee2 = new Employee(null, 10000);

//groupingBy(used form a nominal expression)KeyCannot be empty,throw an exception here
Map<String, List<Employee>> result = (employee1, employee2)
    .collect((Employee::getName));

It can be seen that there are many pitfalls associated with the use of empty objects in a stream; therefore, when CRing, focus on the data source of the Stream stream to avoid the presence of thenullIf you're not sure, it's recommended to usefilter(Objects::nonNull) Filter them out.

More on air-pointer prevention and control tools

The last chapter is still more from the defense of the null pointer to solve the problem, but can guarantee that everyone is cognitively the same, but also in the CR will be missed; the following code I think everyone will be so as to avoid the null pointer, but inevitably in some overtime to the early hours of the morning day, a cramp in the head to write the opposite :(

if("DEFAULT".equals(var)){
    //do something
}

So, in this chapter, Muwango cuts through the data sources and answers:"Can data inherently exist that is non-null, and methods inherently not return null"?

makes sense from a programmatic point of view; many variables never contain thenullMany methods also never returnnullWe can call them "non-null variables" and "non-null methods" respectively. We can call them "non-null variables" and "non-null methods" (NonNull);

Other variables and methods may in some cases contain or returnnullThey are referred to as "nullable" (Nullable);

Based on this theory, another way solution is provided in solving the null pointer problem:

  • The data comes from a third-party system, and the control is not ours, so: not trustworthy and needs to be programmed for defense;
  • The data comes from itself, the control is ours, and the control data is created i.e. non-empty, therefore: trustworthy; as follows:

Mask null values where possible

Checksums on input values - in public methods and constructors. It is necessary to check the value of the input - in the public methods and constructors - in eachset The entry point of the field is added() Call.requireNonNull() method will be used when its parameter isnull throwNullPointerException

public class Employee {
    public Employee(String name, Integer salary) {
         = (name);
         = (salary);
    }
}

This helps to screen the entrance at thenull Writing of values

If your method accepts a collection as input, you can also traverse the collection at the method entry to make sure it doesn't contain thenull Value:

public void check(Collection<String> data) { 
    (Objects::requireNonNull);
}

Note: This depends on the size of the specific collection and the evaluation of the performance loss;

Same type of scenarios without detailed examples:

  • When your type is a collection, return an empty container instead of returning thenull, which prevents null pointer exceptions on the consumer side;
  • Use enumeration constants to replaceBoolean to avoid null pointer exceptions introduced by unboxing.
  • Illegal data state, direct short-circuit throws an exception instead of returning thenull

Make good use of static analysis tools to assist

Introduce two important notes:@Nullablecap (a poem)@NotNull Annotation:

  • @Nullable Annotation means that the variables expected to be annotated may containnull, or commented methods may returnnull
  • @NotNull The annotation implies that the expected value is nevernull. And provides hints for static analysis

This type of annotation allows potential exceptions to be analyzed in real time in static analysis tools;

interface Processor {
    @NotNull
    String getNotNullValue();

    @Nullable
    String getNullable(); @NotNull

    public void process() {
        //Warning here: the condition is always false, so don't do it more than once!
        if (getNotNullValue() == null) {
            //do something
        }
        //Warning here: trim() calls can cause NullPointerException
        (getNullable().trim());
    }
}

Some more notes on using Optional instead of null

In order to avoid the use ofnull Some developers prefer to useOptional Type. It is possible to set theOptional Think of it as a box that is either empty or contains a nonnull The value of the

There are three standard ways to get an Optional object:

  • () -- Get an emptyOptional
  • (value) -- Get a non-emptyOptionalIf the value isnull failing agreementNullPointerException
  • (value) -- if the value isnull Then get an emptyOptionalOtherwise get a non-null containing the valueOptional

utilizationOptional to prevent null pointers, no big deal.But there are a few details to keep in mind

1. Do not abuse ofNullable

Some developers like to use theofNullable(), because it is considered safer and it never throws exceptions. However, it should not be abused if you already know that your value will never be the value ofnullIt is best to use(). In this case, if you see an exception, you'll know immediately that something is wrong and fix it;

Reduced code readability caused

The following code to get the employee's address is concise but poorly readable, and I wouldn't recommend it for cases where nesting is particularly deepOpinionalAfter all, the code is not only for you to read but also for others to understand the intent at a glance.

String employeeAddress = (employee)
        .map(Employee::getAddress)
        .map(Address::getStreet)
        .map(Street::getNo)
        .map(No::getNumber).orElseThrow(() -> new IllegalArgumentException("Illegal parameters"));

Ex post facto anomaly monitoring

Prohibit writing beforehandnull, the matter defensively programmed null pointer anomalies, but is it really high time to rest on your laurels?

Not necessarily, after the fact so it is very important to have a good anomaly alert mechanism;

I suggest targeting keywords:NullPointerException Do a separate log collection with the appropriate alert level: theoretically, you should intervene to locate a null pointer exception if it occurs once;

Of course, especially during the release cycle, if theN The presence of more thanM Subnull pointer exception then it's definitely time for a quick locate and rollback;

put at the end

Welcome to follow my public number: Programming Apocalypse to get the latest news first;

microsoft public number
image image