I hope you won’t experience those pitfalls: Are you sure the resources are released correctly?

Author : Senior Mingming Ruyue, CSDN blog expert, senior Java engineer of Dachang, author of "Performance Optimization Methodology", "Unlocking Dachang Thinking: Analysis of "Alibaba Java Development Manual"", "Relearning Classics: Exclusive Analysis of "EffectiveJava"" Columnist.

Recommended popular articles :

Are you sure you released the resources correctly?  ?  .png

1. Background

Recently, I was conducting a code review on a certain piece of code, and I accidentally discovered a "magic operation" that made me laugh or cry!
The student's code uses the most standard method of releasing resources, but the resources are not released correctly.
This article will simulate this problem and describe the reasons behind it. I hope everyone will pay special attention to this problem when coding.

2. Scene recurrence

2.1 Example 1

package com.demo.demo;

import okhttp3.*;

import java.io.IOException;

public class OkHttpExample {
    
    

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    OkHttpClient client = new OkHttpClient();

    public SomeResult  post(String url, String json) throws IOException {
    
    
        RequestBody body = RequestBody.create(JSON, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = null;
        try{
    
    
            return someMethod(response,request);
        }finally {
    
    
            if(response != null){
    
    
                response.close();
            }
        }

    }

    private SomeResult someMethod(Response response, Request request) throws IOException {
    
    
         response = client.newCall(request).execute();

         // 其他逻辑
         return   SomeUtils.toSomeResult(response);
    }


    public static void main(String[] args) {
    
    
        OkHttpExample example = new OkHttpExample();
        String json = "{\"name\":\"mkyong\"}";
        String response;
        try {
    
    
            response = example.post("https://api.yourdomain.com/v1/api", json);
            System.out.println(response);
        } catch (IOException e) {
    
    
            // 打印错误日志
        }
    }
}

The above code seems to be very correct, and it releases resources in finally in a very professional way. However, if you read the code carefully, you may find problems.

I'll give you 2 minutes to think about what the problem is.

[2 minutes]

2.2 Example 2

If you still can’t see the reason, please tell me the result of running the following code:

public class CatDemo {
    
    
    public static void main(String[] args) {
    
    

        Cat cat = new Cat();
        cat.setName("tom");
        System.out.println(cat);

    }

    private void test(Cat cat){
    
    
        cat = new Cat();
        cat.setName("cat");
    }
}



public class Cat {
    
    

    private String name;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    @Override
    public String toString() {
    
    
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

the answer is:
Cat{name='tom'}

If you still can’t figure it out at this point, it means there is a problem with the foundation and it needs to be strengthened.

3. Background knowledge

In Java, all method parameters are passed by value. This means that when you pass a variable to a method, you are actually passing a copy of the variable.
But we need to distinguish between two cases: passing variables of primitive type and passing object reference variables .

Passing primitive type variables

For primitive types (such as int, double, charetc.), passing by value is intuitive. You create a variable of primitive type, and when you pass it to a method, the method receives a new variable that contains a copy of the original variable.
If the method modifies this new variable, it does not affect the original variable.

Example:

public class Main {
    
    
    public static void main(String[] args) {
    
    
        int a = 5;
        modify(a);
        System.out.println(a);  // 输出:5
    }

    public static void modify(int number) {
    
    
        number = 10;
    }
}

In the above code, although modifythe method modifies numberthe value of the variable, this does not affect the value of the variable mainin the method .a

image.png

Note: The picture is only for illustrating the problem, and the details may not be reasonable. If there is any discrepancy, please do not take it seriously.

Pass object reference variable

For object reference variables, the situation is slightly different. Although it is passed by value, the value of the object reference is passed, not the object itself . This means that the method receives a copy of the original object reference. Therefore, the method can modify the state of the original object through this reference.

However, if the method attempts to assign a new object to its object reference variable, this will not affect the original object reference variable because it only modifies what the copy points to, not what the original reference points to.

Example:

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Cat cat = new Cat();
        cat.setName("Tom");
        modifyReference(cat);
        System.out.println(cat.getName());  // 输出:Tom

        modifyObject(cat);
        System.out.println(cat.getName());  // 输出:Jerry
    }

    public static void modifyReference(Cat cat) {
    
    
        cat = new Cat();
        cat.setName("Spike");
    }

    public static void modifyObject(Cat cat) {
    
    
        cat.setName("Jerry");
    }

    static class Cat {
    
    
        private String name;

        public String getName() {
    
    
            return name;
        }

        public void setName(String name) {
    
    
            this.name = name;
        }
    }
}

In the above code:

  • modifyReferenceThe method attempts to modify catthe reference variable, but this does not affect the variable mainin the method cat.
  • modifyObjectMethods catmodify Catthe state of an object by referencing variables, which actually affects the object mainwithin the method cat.

image.png

4. Reveal the answer

4.1 Example 1

So, in example one:

    private SomeResult someMethod(Response response, Request request) throws IOException {
    
    
         response = client.newCall(request).execute();

         // 其他逻辑
         return   SomeUtils.toSomeResult(response);
    }

client.newCall(request).execute();A new instance is created and assigned to the object reference variable response, but the copy is modified to point to the new object.
therefore:

       Response response = null;
        try{
    
    
            return someMethod(response,request);
        }finally {
    
    
            // 永远为 null,从来没有调用到 close 方法
            if(response != null){
    
    
                response.close();
            }
        }

4.1 Example 2

    public static void modifyObject(Cat cat) {
    
    
        cat.setName("Jerry");
    }

The copy of the cat reference in this method also points to the cat object, and its name can be modified.

  public static void modifyReference(Cat cat) {
    
    
        cat = new Cat();
        cat.setName("Spike");
    }

This method is similar to Example 1. The reference copy points to the new object and does not affect the original reference.

5. Solutions

Once you know the reason, it is easy to modify it.

Recommended approach:

try (Response response = client.newCall(request).execute()) {
    
    

    if(response.success() && response.body() != null){
    
    
        return  SomeUtils.toSomeResult(response.body().string()) ;
    }

    return null;
}

You can also use finally inside someMethod to release resources.

6. Enlightenment

6.1 Pay attention to IDEA warnings

In fact, as long as you don't turn a blind eye to IDEA's warnings, this kind of problem can basically be avoided.
image.png
There are two very obvious tips. One is that if here warns that the condition is always false, you need to analyze why.

It also prompts responds always null when calling.
image.png
Also, responds in the someMethod method is gray, which means that the incoming reference is meaningless and can be defined as a local variable.
image.png

6.2 If the foundation is not strong, the ground will shake.

Many people hate asking questions about "eight-part essay" during interviews, thinking it is a waste of time.
However, in actual work, many problems arise because the so-called eight-part essay is not mastered well.

6.3 Follow best practices

CloseableOne is that for classes that implement the interface, it is recommended to use try-with-resource to use and automatically release resources.
When writing code, if it is not necessary, please do not define an empty object as a parameter and pass it to the downstream, and finally take out the value in the object for use. If you really need to do this, it is recommended to use the Holder class or context class.

7. Summary

Although this problem is not difficult, you will still see many similar errors at work.
I hope that I can develop good coding habits in Canada, and that I can truly integrate knowledge with practice and apply what I have learned.


Creation is not easy. If this article is helpful to you, please like, collect and follow it. Your support and encouragement are the biggest motivation for my creation.
Insert image description here

Welcome to join my Knowledge Planet, Knowledge Planet ID: 15165241 (it has been in operation for more than five years and will continue to operate) to communicate and learn together.
https://t.zsxq.com/Z3bAiea marked it as coming from CSDN when applying.

Guess you like

Origin blog.csdn.net/w605283073/article/details/133041506