Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 用作锁的临时最终字段为空_Java_Serialization_Nullpointerexception_Final_Transient - Fatal编程技术网

Java 用作锁的临时最终字段为空

Java 用作锁的临时最终字段为空,java,serialization,nullpointerexception,final,transient,Java,Serialization,Nullpointerexception,Final,Transient,下面的代码抛出一个NullPointerException import java.io.*; public class NullFinalTest { public static void main(String[] args) throws IOException, ClassNotFoundException { Foo foo = new Foo(); foo.useLock(); ByteArrayOutputStream bu

下面的代码抛出一个
NullPointerException

import java.io.*;

public class NullFinalTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Foo foo = new Foo();
        foo.useLock();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        new ObjectOutputStream(buffer).writeObject(foo);
        foo = (Foo) new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())).readObject();
        foo.useLock();
    }

    public static class Foo implements Serializable {
        private final String lockUsed = "lock used";
        private transient final Object lock = new Object();
        public void useLock() {
            System.out.println("About to synchronize");
            synchronized (lock) { // <- NullPointerException here on 2nd call
                System.out.println(lockUsed);
            }
        }
    }
}

lock
怎么可能为空?

任何声明为
transient
的字段都不会序列化。此外,根据,字段值甚至没有初始化为默认构造函数设置的值。当
瞬态
字段为
最终
时,这会产生一个质询

根据,可以通过实现以下方法来控制反序列化:

private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;
我提出了以下解决方案,基于:

运行它会产生以下输出(无
NullPointerException
):


用作锁的临时最终字段为空

以下是关于瞬态变量的一些事实:

-在实例变量上使用Transient关键字时,将阻止序列化该实例变量。

-反序列化时,瞬态变量将达到其默认值

例如:

  • 对象引用变量到
    null
  • int到
    0
  • 布尔值设置为
    false、
    etc

因此,这就是您得到一个
NullPointerException
的原因,当反序列化它时…

正如前面所指出的,下面的声明并不像人们预期的那样起作用:

transient final Object foo = new Object()
transient
关键字将阻止序列化成员在反序列化过程中,不会使用默认值初始化,因此反序列化后
foo
null

设置成员后,
final
关键字将阻止您修改该成员。这意味着您在反序列化实例上永远只能使用
null

在任何情况下,您都需要删除
final
关键字。这将牺牲不变性,但通常不应成为
private
成员的问题

那么您有两个选择:

选项1:覆盖
readObject()
创建新实例时,
foo
将初始化为其默认值。反序列化时,您的自定义
readObject()
方法将处理该问题

这将适用于JRE,但不适用于Android,因为Android的
Serializable
实现缺少
readObject()
方法

选项2:延迟初始化 声明:

transient Object foo;
关于访问:

if (foo == null)
    foo = new Object();
doStuff(foo);

您必须在代码中访问
foo
的任何地方执行此操作,这可能比第一个选项更容易工作,也更容易出错,但它同样适用于JRE和Android。

@nicholas.hauschild不仅允许自答问题,但也值得鼓励。你引用的博客文章根本没有提到默认值,更不用说你上面所说的了。这甚至没有意义:它们还会被初始化成什么?您的代码解决方案也过于复杂:您不需要对此进行反射。博客文章说,“声明为final的实例成员字段也可能是暂时的,但如果是这样,您将面临一个有点难以解决的问题……当您反序列化对象时,您必须手动初始化字段,[但编译器会抱怨,因为它是最终的]……现在,当您反序列化该类时,您的记录器将是一个空对象,因为它是瞬态的。”这就是我的答案所解决的问题。在没有思考的情况下,你建议我们如何设置
final
字段的值?你的评论也过于尖刻:你不需要为此大惊小怪。博客没有说你说的话。句号。我的评论也没有说你声称的话:剩下的部分可以简化为单词“过度”,这几乎不是“尖刻的”或“尖刻的”。这让我很困惑。对不起,听起来我们对这个超载的词“默认值”有误解在我上面的帖子中,我指的是类定义中设置的值,即构建之前设置的值。请注意,
Serializable
的Android实现缺少
readObject()
方法,因此此解决方案将只在JRE(和完全兼容的实现)上工作,而不会在Android上编译。
transient final Object foo = new Object()
transient Object foo = new Object();

@Override
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    foo = new Object();
}
transient Object foo;
if (foo == null)
    foo = new Object();
doStuff(foo);