为什么静态域与网络传输无关

首先静态域与网络传输没有关系,静态域不可以被序列化,也不会在网络中被传输。我们先通过一个例子验证这个问题,后面再分析静态域到底是怎样的一种存在。

以下是一个socket通信过程,Client端负责实例的构造和发送,Server端负责监听和接收来自端口的实例,User类拥有静态变量和成员变量,这里将User的实例作为目标进行传输。

import java.io.*;
import java.net.Socket;

/**
 * User: zzzz76
 * Date: 2018-07-07
 */
public class Client {
    public static void main(String[] args) throws Exception {
        User.setTag("client");
        for (int i = 0; i < 5; i++) {
            Socket socket = null;
            BufferedOutputStream bos = null;
            ObjectOutputStream oos = null;
            try {
                socket = new Socket("localhost", 10000);
                bos = new BufferedOutputStream(socket.getOutputStream());
                oos = new ObjectOutputStream(bos);

                User user = new User("user_" + i, "password_" + i);
                oos.writeObject(user);
                oos.flush();

            } catch (IOException ex) {
                ex.printStackTrace();
            } finally {
                IOUtils.closeQuietly(oos);
                IOUtils.closeQuietly(bos);
                IOUtils.closeQuietly(socket);
            }
        }
    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * User: zzzz76
 * Date: 2018-07-07
 */

public class Server {
    public static void main(String[] args) {
        User.setTag("server");
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(10000);
            serverSocket.setSoTimeout(10000);
            // Listen for the connection to accept socket
            while (true) {
                Socket socket = serverSocket.accept();
                launch(socket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(serverSocket);
        }
    }

    /**
     * Launch socket to receive the input of task
     *
     * @param socket
     */
    private static void launch(Socket socket) {
        new Thread(() -> {
            BufferedInputStream bis = null;
            ObjectInputStream ois = null;
            try {
                bis = new BufferedInputStream(socket.getInputStream());
                ois = new ObjectInputStream(bis);

                Object obj = ois.readObject();
                User user = (User) obj;
                System.out.println("Get socket user: " + user.getTag() +
                                "/" + user.getName() +
                                "/" + user.getPassword());

            } catch (IOException | ClassNotFoundException ex) {
                ex.printStackTrace();
            } finally {
                IOUtils.closeQuietly(ois);
                IOUtils.closeQuietly(bis);
                IOUtils.closeQuietly(socket);
            }
        }).start();
    }
}
import java.io.Serializable;

/**
 * User: zzzz76
 * Date: 2018-07-07
 */
public class User implements Serializable {
    //类加载时在方法区分配内存,并初始化
    private static String tag = "clinit";
    static {
        System.out.println("Init static tag: " + tag);
    }

    private String name;
    private String password;

    //构造实例时在堆上分配内存,并初始化
    public User() {
        System.out.println("Init instance user");
    }
    public User(String name, String password) {
        this.name = name;
        this.password = password;
        System.out.println("Init Instance user: " + name + "/" +password);
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public User setPassword(String password) {
        this.password = password;
        return this;
    }

    public static String getTag() {
        return tag;
    }

    public static void setTag(String tag) {
        System.out.println("Update static tag: " + tag);
        User.tag = tag;
    }
}

输出结果:

➜  Desktop java Client      
Init static tag: clinit
Update static tag: client
Init Instance user: user_0/password_0
Init Instance user: user_1/password_1
Init Instance user: user_2/password_2
Init Instance user: user_3/password_3
Init Instance user: user_4/password_4
➜  Desktop java Server
Init static tag: clinit
Update static tag: server
Get socket user: server/user_2/password_2
Get socket user: server/user_3/password_3
Get socket user: server/user_4/password_4
Get socket user: server/user_0/password_0
Get socket user: server/user_1/password_1

从输出结果可以看出:

  • 在Server端中,User的构造函数没有经过调用,说明User实例是从端口处获取的。
  • 静态域在传输开始之前就已经产生并且经过初始化,每一个User实例都指向位于本JVM进程中的静态域,而并非网络端口(其他JVM进程),所以静态域没有到参与整个传输过程中。

其实这里产生了两个JVM实例,每个实例都具有独立的堆栈,其中静态域就位于堆栈的方法区中。User实例在不同的JVM实例上指向不同的静态域,导致通信前后静态变量的不同。关于静态域和类加载的过程可以看看我的上篇文章

原文地址:https://www.cnblogs.com/zzzz76/p/9338671.html