检查字段是否设置为Java中声明的默认值

时间:2018-03-23 01:45:31

标签: java

假设我有一个带有静态字段且具有设置默认值的类:

public class MyClass {

   public static int numb = 10;

}

现在,在程序运行时期间,我如何检查MyClass的{​​{1}}字段是否设置为其默认值(在情况下,numb )?

从我的角度来看,在代码中,我不知道默认情况下该字段的设置是什么,而我不是设置字段的人,所以我不能“简单地检查是否10。”我必须能够检查它是否设置为源代码中声明的内容。

5 个答案:

答案 0 :(得分:7)

单个字段无法实现。我建议添加一个常量来保持默认值,并将其与numb进行比较,看看它是否已更改:

private static final int DEFAULT = 10;
public static int numb = DEFAULT;

public static boolean isChanged() {
    return numb != DEFAULT;
}

答案 1 :(得分:1)

Kröw,您对问题的最新澄清,以下是否满足您的要求?

public class MyClass {

    public static int numb = 10;
}
public class MyTest {

    private static int default_numb = MyClass.numb;

    public static boolean isChanged() {
        return MyClass.numb != default_numb;
    }

    public static void main(String[] args) {
        System.out.println(isChanged() + " " + MyClass.numb);
        MyClass.numb = 20;
        System.out.println(isChanged() + " " + MyClass.numb);
        MyClass.numb = 10;
        System.out.println(isChanged() + " " + MyClass.numb);
    }

}

运行MyTest的输出是:

$ java MyTest
false 10
true 20
false 10

答案 2 :(得分:0)

问题描述说“从我的角度来看,在代码中,我不知道该字段默认设置为什么,

如果在运行时提供 numb 默认值,则以下代码将完成您要查找的内容。

public class MyClass {

    private static int numb;
    private static int def_numb;
    private static boolean initialized = false; // track numb initialize

    public MyClass(int default_value) {
        if (!initialized) {
            numb = default_value;
            def_numb = numb;
            initialized = true;
        } else {
            numb = default_value;
        }
    }

    public void setNumb(int value) {
        numb = value;
    }

    public int getNumb() {
        return numb;
    }

    public boolean isChanged() {
        return numb != def_numb;
    }

    public static void main(String[] args) {
        MyClass m2 = new MyClass(5);
        System.out.println(m2.isChanged() + " " + m2.getNumb());
        MyClass m3 = new MyClass(6);
        System.out.println(m2.isChanged() + " " + m2.getNumb());
        System.out.println(m3.isChanged() + " " + m3.getNumb());
        MyClass m4 = new MyClass(5);
        System.out.println(m3.isChanged() + " " + m3.getNumb());
    }
}

运行此程序将显示以下内容。

$ java MyClass
false 5
true 6
true 6
false 5

答案 3 :(得分:0)

如果您不知道默认值,则应跟踪变量以进行任何更改。由于它是公共和静态变量,我认为不可能。但是,您可以按照以下建议更改解决方案。 使用MyValueChangeListener或Java调试器API

Check this

答案 4 :(得分:0)

当我提出这个问题的时候,我希望得到一个新人的答案,这个人可以反思地发现变量初始化的价值。这有点像反射会支持的东西。但在我发布问题之后,我意识到Java没有充分的理由将Java的初始值永久存储在内存中。

解决方案

无论如何,我伪造了一个在运行时反射访问变量当前值的方法,然后读取程序的字节码以检查该类初始化时的变量。为此,它模拟了类的初始化(<clinit>),然后简单地检查变量的当前值和声明值是否相等。 (它有很多挫折;见下文。)

/**
 * Checks if the current value of a variable is the same as the value it was
 * declared as during class initialization. Rules dictating whether or not this
 * method can check for the default value of a given variable are as follows:
 * <ol>
 * <li>The variable must be static.</li>
 * <li>The variable must have been initialized to the value of a constant
 * expression.</li>
 * <li>The variable must be a primitive type.</li>
 * </ol>
 * 
 * @param varName
 *            The name of the variable to be checked. This variable must be
 *            static and belong to the {@link Class}, <code>clas</code>.
 * @param clas
 *            The class of which a static variable, named <code>varName</code>,
 *            belongs.
 * @return <code>true</code> if the specified variable's value as set in the
 *         bytecode is the same as it is when retrieved reflectively at the
 *         beginning of this method call, <code>false</code> otherwise.
 * @throws UnsupportedOperationException
 *             If the specified variable's type is not primitive.
 * @throws SecurityException
 *             As thrown by {@link Class#getDeclaredField(String)} and any other
 *             reflective operation executed by this method.
 * @throws NoSuchFieldException
 *             If attempting to access the field with the given arguments to
 *             this method, throws a {@link NoSuchFieldException}.
 * @throws IllegalArgumentException
 *             In case the <code>varName</code> parameter does refer to a field
 *             of the given class, but the field is not static.
 * @throws RuntimeException
 *             In case an unknown or unexpected error occurs.
 * @throws IOException
 *             If an IOException occurs while reading from the
 *             <code>.class</code> file of the specified class.
 */
public static boolean isOriginalValue(String varName, Class<?> clas) throws UnsupportedOperationException,
        NoSuchFieldException, SecurityException, IllegalArgumentException, RuntimeException, IOException {
    Object currentValue;

    // Check for the current value reflectively:
    Field field = clas.getDeclaredField(varName);
    if (!field.getType().isPrimitive())
        throw new UnsupportedOperationException("The specified variable's type is not primitive.");
    field.setAccessible(true);
    try {
        // We pass in null because we assume that the field is static. If it isn't, an
        // IllegalArgumentException is thrown.
        currentValue = field.get(null);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("The current value of the variable could not be accessed.", e);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // Map used to store constant pool values:
    Map<Integer, Object> constants = new HashMap<>();

    // Local classes used to represent certain constant pool items:
    class CPoolClass {
        public CPoolClass(int internalNameIndex) {
        }

    }
    class CPoolNameAndType {
        public final int nameIndex;

        public CPoolNameAndType(int nameIndex, int typeIndex) {
            this.nameIndex = nameIndex;
        }

        public String getName() {
            return (String) constants.get(nameIndex);
        }

    }
    class CPoolField {
        public final int nameAndTypeIndex;

        public CPoolField(int classIndex, int nameAndTypeIndex) {
            this.nameAndTypeIndex = nameAndTypeIndex;
        }

        public CPoolNameAndType getNameAndType() {
            return (CPoolNameAndType) constants.get(nameAndTypeIndex);
        }
    }
    class CPoolString {
        public CPoolString(int stringIndex) {
        }
    }

    // Now to read the bytecode of the class for the original value.
    // First, we open a stream to the .class file of clas.
    DataInputStream reader = new DataInputStream(clas.getResourceAsStream(clas.getSimpleName() + ".class"));

    // A .class file starts with the magic hexadecimal code: 0xCafeBabe.
    if (reader.readInt() != 0xCafeBabe)
        throw new RuntimeException("Failed to parse the given class's .class file header.");
    // Next, the file contains four bytes denoting its version.
    reader.readInt();// Read and ditch the four bytes; we don't need them.

    // Parse the constant pool size (2 bytes). We'll go through this many items in
    // the constant pool, and cache the ones we need.
    int constantPoolSize = reader.readShort();

    // Constant pool is of size constantPoolSize-1, so we iterate from 1 to the
    // size.
    for (int i = 1; i < constantPoolSize; i++) {
        byte tag = reader.readByte();
        switch (tag) {
        default:
            throw new RuntimeException(
                    "Could not parse the .class file's constant pool; couldn't determine the initial value of the variable.");
        case 1:// String tag
            constants.put(i, reader.readUTF());
            continue;

        case 3:// Int tag
            constants.put(i, reader.readInt());
            continue;

        case 4:// Float tag
            constants.put(i, reader.readFloat());
            continue;

        case 5:// Long tag
            constants.put(i, reader.readLong());
            i++;
            continue;

        case 6:// Double tag
            constants.put(i, reader.readDouble());
            i++;
            continue;

        case 7:// Class item
            constants.put(i, new CPoolClass(reader.readShort()));
            continue;

        case 8:// Literal String (e.g. "abc", "potato", "a string")
            constants.put(i, new CPoolString(reader.readShort()));
            continue;

        case 9:// Field item
            constants.put(i, new CPoolField(reader.readShort(), reader.readShort()));
            continue;

        case 10:// Method (We don't need to cache this)
        case 11:// Abstract method
            reader.readInt();
            continue;

        case 12:// NameAndType item
            constants.put(i, new CPoolNameAndType(reader.readShort(), reader.readShort()));
            continue;

        case 15:// Method Handle
            reader.readByte();
            reader.readShort();
            continue;

        case 16:// Method Type
            reader.readShort();// Skip 2 bytes
            continue;

        case 18:// Invoke Dynamic
            reader.readInt();
            continue;
        }
    }

    // After the constant pool, there are a few, 2 byte-sized values, a list of
    // interfaces (that the class implements), a list of fields, then the list of
    // methods.
    //
    // We're gonna skip to the methods.

    reader.readInt();// Skip 4 bytes
    reader.readShort();// Skip 2 bytes

    int interfaceListSize = reader.readShort();
    for (int i = 0; i < interfaceListSize; i++)
        reader.readShort();

    int fieldListSize = reader.readShort();
    for (int i = 0; i < fieldListSize; i++) {

        // Skip 6 bytes (total)
        reader.readShort();
        reader.readInt();

        int attributeCount = reader.readShort();
        // Skip through all the attributes.
        for (int j = 0; j < attributeCount; j++) {
            reader.readShort();
            int attributeSize = reader.readInt();
            for (int k = 0; k < attributeSize; k++)
                reader.readByte();
        }

    }

    int methodCount = reader.readShort();

    for (int i = 0; i < methodCount; i++) {

        // Skip the method's modifiers.
        reader.readShort();

        // Static variables are initialized via the <clinit> method, whether or not they
        // are placed in static initialization blocks in the source code. Because of
        // this, we'll have to check the code in this method.
        boolean isClinit = "<clinit>".equals(constants.get((int) reader.readShort()));

        reader.readShort();// Skip over the method descriptor.

        short attributeCount = reader.readShort();

        if (isClinit) {
            // This is the method we want. Iterate over each attribute it has.
            for (int j = 0; j < attributeCount; j++) {
                if ("Code".equals(constants.get((int) reader.readShort()))) {

                    reader.readInt();// Skip over the attribute size. We only need the code table size.
                    reader.readInt();// Skip max stack size and max locals.

                    int codeLength = reader.readInt();

                    // Doubles and longs in the stack will have a null value after them. This
                    // emulates the numbering of an actual stack.
                    Stack<Object> stack = new Stack<Object>() {

                        private static final long serialVersionUID = 1L;

                        // Handles the push method
                        @Override
                        public void addElement(Object item) {
                            super.addElement(item);
                            if (item instanceof Double || item instanceof Long)
                                super.addElement(null);
                        }

                        // Handles the add method
                        @Override
                        public synchronized void insertElementAt(Object obj, int index) {
                            if (obj instanceof Double || obj instanceof Long)
                                super.insertElementAt(null, index);
                            super.insertElementAt(obj, index);
                        }

                        @Override
                        public synchronized Object pop() {
                            Object item = super.pop();
                            if (item == null)
                                return super.pop();
                            return item;
                        }

                        @Override
                        public synchronized Object remove(int index) {
                            Object item = super.remove(index);
                            if (item == null)
                                item = super.remove(index - 1);
                            else if (item instanceof Double || item instanceof Long)
                                super.remove(index);
                            return item;
                        }

                        @Override
                        public synchronized Object peek() {
                            Object item = super.peek();
                            if (item == null)
                                return super.get(size() - 2);
                            return item;
                        }

                    };

                    int[] bytes = new int[codeLength];
                    for (int k = 0; k < codeLength; k++)
                        bytes[k] = reader.readUnsignedByte();

                    // Stack<Object> locals = new Stack<>();
                    // If I supported local vars, I'd feel obligated to support arrays here, as
                    // well, and, right now, I don't have enough time for that. Feel free to edit
                    // this question. Otherwise, I may edit this method to support more opcodes
                    // later.

                    Object variable = null;

                    // Here is where we simulate the class's initialization. This process doesn't
                    // handle things like arrays, method calls, or other variables.
                    for (int k = 0; k < codeLength; k++) {
                        // When parsing through mnemonics, we ignore anything that only puts a reference
                        // onto the stack or removes a reference from the stack, since this method only
                        // supports primitive types.

                        switch (bytes[k]) {

                        default:
                            continue;

                        // Pushing

                        case 0x10:// bipush
                            stack.push(bytes[++k]);
                            break;
                        case 0x11:// sipush
                            stack.push((bytes[++k] << 8) + bytes[++k]);
                            break;
                        case 0x12:// ldc
                        case 0x14:// ldc2_w
                            Object item = constants.get((bytes[++k] << 8) + bytes[++k]);
                            if (item instanceof Integer || item instanceof Float || item instanceof Long
                                    || item instanceof Double)
                                stack.push(item);
                            break;

                        // Doubles

                        case 0xe:// dconst_0
                            stack.push((double) 0);
                            break;
                        case 0xf:// dconst_1
                            stack.push((double) 1);
                            break;

                        case 0x63:// dadd
                            stack.push((double) stack.pop() + (double) stack.pop());
                            break;
                        case 0x6f:// ddiv
                            stack.push((double) stack.pop() / (double) stack.pop());
                            break;
                        case 0x6b:// dmul
                            stack.push((double) stack.pop() * (double) stack.pop());
                            break;
                        case 0x77:// dneg
                            stack.push(-(double) stack.pop());
                            break;
                        case 0x73:// drem
                            stack.push((double) stack.pop() % (double) stack.pop());
                            break;
                        case 0x67:// dsub
                            stack.push((double) stack.pop() - (double) stack.pop());
                            break;

                        case 0x59:// dup
                            stack.push(stack.peek());
                            break;
                        case 0x5a:// dup_x1
                        case 0x5d:// dup2_x1
                            stack.add(stack.size() - 2, stack.peek());
                            break;
                        case 0x5b:// dup_x2
                        case 0x5e:// dup2_x2
                            stack.add(stack.size() - 3, stack.peek());
                            break;

                        // Floats

                        case 0xb:// fconst_0
                            stack.push(0f);
                            break;
                        case 0xc:// fconst_1
                            stack.push(1f);
                            break;
                        case 0xd:// fconst_2
                            stack.push(2f);
                            break;

                        case 0x62:// fadd
                            stack.push((float) stack.pop() + (float) stack.pop());
                            break;
                        case 0x6e:// fdiv
                            stack.push((float) stack.pop() / (float) stack.pop());
                            break;
                        case 0x6a:// fmul
                            stack.push((float) stack.pop() * (float) stack.pop());
                            break;
                        case 0x76:// fneg
                            stack.push(-(float) stack.pop());
                            break;
                        case 0x72:// frem
                            stack.push((float) stack.pop() % (float) stack.pop());
                            break;
                        case 0x66:// fsub
                            stack.push((float) stack.pop() - (float) stack.pop());
                            break;

                        // Integers

                        case 0x2:// iconst_m1
                            stack.push(-1);
                            break;
                        case 0x3:// iconst_0
                            stack.push(0);
                            break;
                        case 0x4:// iconst1
                            stack.push(1);
                            break;
                        case 0x5:// iconst_2
                            stack.push(2);
                            break;
                        case 0x6:// iconst_3
                            stack.push(3);
                            break;
                        case 0x7:// iconst_4
                            stack.push(4);
                            break;
                        case 0x8:// iconst_5
                            stack.push(5);
                            break;

                        case 0x60:// iadd
                            stack.push((int) stack.pop() + (int) stack.pop());
                            break;
                        case 0x7e:// iand
                            stack.push((int) stack.pop() & (int) stack.pop());
                            break;
                        case 0x6c:// idiv
                            stack.push((int) stack.pop() / (int) stack.pop());
                            break;
                        case 0x68:// imul
                            stack.push((int) stack.pop() * (int) stack.pop());
                            break;
                        case 0x74:// ineg
                            stack.push(-(int) stack.pop());
                            break;
                        case 0x80:// ior
                            stack.push((int) stack.pop() | (int) stack.pop());
                            break;
                        case 0x70:// irem
                            stack.push((int) stack.pop() % (int) stack.pop());
                            break;
                        case 0x78:// ishl
                            stack.push((int) stack.pop() << (int) stack.pop());
                            break;
                        case 0x7a:// ishr
                            stack.push((int) stack.pop() >> (int) stack.pop());
                            break;
                        case 0x64:// isub
                            stack.push((int) stack.pop() - (int) stack.pop());
                            break;
                        case 0x7c:// iushr
                            stack.push((int) stack.pop() >>> (int) stack.pop());
                            break;
                        case 0x82:// ixor
                            stack.push((int) stack.pop() ^ (int) stack.pop());
                            break;

                        // Longs

                        case 0x9:// lconst_0
                            stack.push(0l);
                            break;
                        case 0xa:// lconst_1
                            stack.push(1l);
                            break;

                        case 0x61:// ladd
                            stack.push((long) stack.pop() + (long) stack.pop());
                            break;
                        case 0x7f:// land
                            stack.push((long) stack.pop() & (long) stack.pop());
                            break;
                        case 0x6d:// ldiv
                            stack.push((long) stack.pop() / (long) stack.pop());
                            break;
                        case 0x69:// lmul
                            stack.push((long) stack.pop() * (long) stack.pop());
                            break;
                        case 0x75:// lneg
                            stack.push(-(long) stack.pop());
                            break;
                        case 0x81:// lor
                            stack.push((long) stack.pop() | (long) stack.pop());
                            break;
                        case 0x71:// lrem
                            stack.push((long) stack.pop() % (long) stack.pop());
                            break;
                        case 0x79:// lshl
                            stack.push((long) stack.pop() << (long) stack.pop());
                            break;
                        case 0x7b:// lshr
                            stack.push((long) stack.pop() >> (long) stack.pop());
                            break;
                        case 0x65:// lsub
                            stack.push((long) stack.pop() - (long) stack.pop());
                            break;
                        case 0x7d:// lushr
                            stack.push((long) stack.pop() >>> (long) stack.pop());
                            break;
                        case 0x83:// lxor
                            stack.push((long) stack.pop() ^ (long) stack.pop());
                            break;

                        // Stack manipulation

                        // nop is handled by this switch's default case.
                        case 0x58:// pop2
                            stack.pop();
                        case 0x57:// pop
                            stack.pop();
                            break;
                        case 0x5f:// swap
                            Object top = stack.pop(), bottom = stack.pop();
                            stack.push(top);
                            stack.push(bottom);
                            break;

                        // Variable assignment

                        case 0xb3:// putstatic

                            if (varName.equals(((CPoolField) constants.get((bytes[++k] << 8) + bytes[++k]))
                                    .getNameAndType().getName()))
                                variable = stack.pop();
                            break;

                        // Casting

                        case 0x90:// d2f
                            stack.push((float) (double) stack.pop());
                            break;
                        case 0x8e:// d2i
                            stack.push((int) (double) stack.pop());
                            break;
                        case 0x8f:// dtl
                            stack.push((long) (double) stack.pop());
                            break;

                        case 0x91:// i2b
                            stack.push((byte) (int) stack.pop());
                            break;
                        case 0x92:// i2c
                            stack.push((char) (int) stack.pop());
                            break;
                        case 0x87:// i2d
                            stack.push((double) (int) stack.pop());
                            break;
                        case 0x86:// i2f
                            stack.push((float) (int) stack.pop());
                            break;
                        case 0x85:// i2l
                            stack.push((long) (int) stack.pop());
                            break;
                        case 0x93:// i2s
                            stack.push((short) (int) stack.pop());
                            break;

                        case 0x8a:// l2d
                            stack.push((double) (long) stack.pop());
                            break;
                        case 0x89:// l2f
                            stack.push((float) (long) stack.pop());
                            break;
                        case 0x88:// l2i
                            stack.push((int) (long) stack.pop());
                            break;

                        case 0x8d:// f2d
                            stack.push((double) (float) stack.pop());
                            break;
                        case 0x8b:// f2i
                            stack.push((int) (float) stack.pop());
                            break;
                        case 0x8c:// ftl
                            stack.push((long) (float) stack.pop());
                            break;

                        // Comparisons

                        case 0x98:// dcmpg
                            stack.push((double) stack.pop() > (double) stack.pop());
                            break;
                        case 0x96:// fcmpg
                            stack.push((float) stack.pop() > (float) stack.pop());
                            break;

                        case 0x97:// dcmpl
                            stack.push((double) stack.pop() < (double) stack.pop());
                            break;
                        case 0x95:// fcmpl
                            stack.push((float) stack.pop() < (float) stack.pop());
                            break;

                        case 0x94:// lcmp
                            stack.push(((Long) stack.pop()).compareTo((Long) stack.pop()));
                            break;

                        }
                    }

                    return variable.equals(currentValue);

                } else
                    // This isn't the "Code" attribute. Skip it.
                    for (int k = 0; k < reader.readInt(); k++)
                        reader.readByte();

            }
            // If we iterated through each
            throw new RuntimeException(
                    "Couldn't find the static initialization bytecode for the specified class. Any initial assignments or declarations to the specified variable could not be found.");
        } else {
            // We don't want this method.
            for (int j = 0; j < attributeCount; j++) {
                reader.readShort();// Skip the attribute name. We don't care for this method.
                for (int k = 0; k < reader.readInt(); k++)
                    reader.readByte();// Skip the attribute data.
            }
        }
    }

    throw new RuntimeException("The declared value could not be found");
}

此方法通常适用于具有简单声明值的变量(例如public static int x = 56;)。它不支持大量的opcodes,包括一些带参数的(如果这些操作码存在于类的字节码中,则此方法无法正确读取它们)。这种方法可以做到&#34;工作&#34;在非常简单的情况下,更多的是为了概念证明。