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:var2
(Integer
type) assigned to thenum
(Integer
(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 that
var1
beint
type, whereasvar2
beInteger
types, the trinomial operator will combine their type derivations so that the return value isint
Type. - This means that if
condition
because oftrue
Instead, it will try to set thevar2
(null
) Unboxing intoint
. As a result ofnull
Cannot be unpacked intoint
and 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 benull
,findFirst()
will throwNullPointerException
. This is becausefindFirst()
Returns aOptional
but (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 containsnull
can 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 thenull
If 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 thenull
Many methods also never returnnull
We 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 returnnull
They 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 the
null
, which prevents null pointer exceptions on the consumer side; - Use enumeration constants to replace
Boolean
to avoid null pointer exceptions introduced by unboxing. - Illegal data state, direct short-circuit throws an exception instead of returning the
null
;
Make good use of static analysis tools to assist
Introduce two important notes:@Nullable
cap (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-emptyOptional
If the value isnull
failing agreementNullPointerException
-
(value)
-- if the value isnull
Then get an emptyOptional
Otherwise 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 ofnull
It 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 deepOpinional
After 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 |
---|---|