Why does a ScriptableObject saves nonserialized fields?

I have the following two classes:

TestBehaviour.cs:

using UnityEngine;

public class TestBehaviour : MonoBehaviour
{
    public TestObject TestObject;
    private bool Value = false;

    private void Start()
    {
        TestObject.TestFunction();
        Debug.Log("MonoBehaviour Value: " + Value);
        Value = true;
    }
}

and TestObject.cs:

using UnityEngine;

[CreateAssetMenu]
public class TestObject : ScriptableObject
{
    private bool value = false;

    public void TestFunction()
    {
        Debug.Log("ScriptableObject Value: " + value);
        value = true;
    }
}

When I load the project and run it the first time, both logs are false as i would expect. But in the second excecution the output of the ScriptableObject is true.

Now I would like to know, how to prevent a ScriptableObject from saving those nonserialized fields I want to use to save the state of my object.

The values are not serialized to disk. It’s just that the object loaded in memory from your assets will of course keep the current value. Try closing the Unity editor, open it again and you will see that the value is back to false. Assets are, from the life cycle point of view, loaded once and remain in memory until your application is shut down.

Technically it is true that when entering playmode Unity does perform an assembly reload. At this point usually all non serialized data would be lost since Unity destroys the whole appdomain including all objects. However when an assembly reload happens Unity does actually save and restore even private fields it can serialize. Otherwise hotreloading wouldn’t be possible at all.

If you specifically want to prevent private fields to survive hot reloading / an assembly reload you have to use the NonSerialized attribute on your field. That will prevent Unity from ever saving this field. This is actually mentioned at the very end of the script serialization documentation. To quote:

When reloading scripts, Unity restores
all variables - including private
variables - that fulfill the
requirements for serialization, even
if a variable has no SerializeField
attribute. In some cases, you
specifically need to prevent private
variables from being restored: For
example, if you want a reference to be
null after reloading from scripts. In
this case, use the NonSerialized
attribute.