当前位置: 首页 > 面试题库 >

最终的定义不正确吗?

公羊玉泽
2023-03-14
问题内容

首先,一个难题:以下代码显示什么?

public class RecursiveStatic {
    public static void main(String[] args) {
        System.out.println(scale(5));
    }

    private static final long X = scale(10);

    private static long scale(long value) {
        return X * value;
    }
}

回答:

0

扰流板如下。

如果您打印X的规模(长),并重新定义X = scale(10) + 3,印刷品会X = 0那么X = 3。这意味着X暂时设置为0,后来又设置为3。这是违反final

静态修饰符与最终修饰符结合使用,还可以定义常量。最后的修饰符指示 此字段 的值 不能更改

来源:https
:
//docs.oracle.com/javase/tutorial/java/javaOO/classvars.html
[添加了重点]

我的问题:这是一个错误吗?是final不明确的?

这是我感兴趣的代码。 X被分配了两个不同的值:03。我相信这是对的违反final

public class RecursiveStatic {
    public static void main(String[] args) {
        System.out.println(scale(5));
    }

    private static final long X = scale(10) + 3;

    private static long scale(long value) {
        System.out.println("X = " + X);
        return X * value;
    }
}

这个问题被标记为Java静态最终字段初始化顺序的可能重复项。我相信这个问题
不是
重复的,因为另一个问题解决了初始化的顺序,而我的问题解决了与final标签结合的循环初始化。仅从另一个问题来看,我将无法理解为什么我的问题中的代码没有出错。

通过查看ernesto得到的输出,这尤其清楚:当a标记为时final,他得到以下输出:

a=5
a=5

这不涉及我的问题的主要部分:final变量如何更改其变量?


问题答案:

一个非常有趣的发现。要理解它,我们需要深入研究Java语言规范(JLS)。

原因是final只允许一次 分配 。但是,默认值为no Assignment 。实际上,每个 这样的变量
(类变量,实例变量,数组组件)从一开始就在 赋值 之前指向其 默认值 。然后,第一个分配更改参考。 __

类变量和默认值

看下面的例子:

private static Object x;

public static void main(String[] args) {
    System.out.println(x); // Prints 'null'
}

我们没有为明确分配值x,尽管它指向null,这是默认值。将其与§4.12.5进行比较:

变量的初始值

每个 类变量* ,实例变量或数组组件在 创建 时都会用 默认值
初始化(第15.9节,第15.10.2节)
*

请注意,这仅适用于此类变量,例如在我们的示例中。它不适用于局部变量,请参见以下示例:

public static void main(String[] args) {
    Object x;
    System.out.println(x);
    // Compile-time error:
    // variable x might not have been initialized
}

在同一JLS段落中:

必须在使用 局部变量
(第14.4节,第14.14节)之前通过初始化(第14.4节)或赋值(第15.26节)为它
明确赋一个值
,并且可以使用确定赋值规则(第§)进行验证。16(确定分配))。

最终变量

现在final,从§4.12.4看:

最终 变量

可以将变量声明为 final 。甲 最终 变量可以仅 分配给一次 。如果将 最终 变量赋值给它,则是编译时错误,除非在
赋值之前绝对未赋值 最终
变量(第16节(确定赋值))。

说明

现在回到您的示例,稍作修改:

public static void main(String[] args) {
    System.out.println("After: " + X);
}

private static final long X = assign();

private static long assign() {
    // Access the value before first assignment
    System.out.println("Before: " + X);

    return X + 1;
}

它输出

Before: 0
After: 1

回想一下我们学到的东西。在方法assign的变量X没有分配 的值呢。因此,由于它是 类变量
,因此它指向其默认值,并且根据JLS,这些变量总是立即指向其默认值(与局部变量相反)。在该assign方法之后,将为变量X分配值,1并且由于final我们无法再对其进行更改。因此,由于以下原因,以下操作将不起作用final

private static long assign() {
    // Assign X
    X = 1;

    // Second assign after method will crash
    return X + 1;
}

JLS中的示例

感谢@Andrew,我找到了一个JLS段落,它完全涵盖了这种情况,它也演示了这一点。

但是首先让我们看一下

private static final long X = X + 1;
// Compile-time error:
// self-reference in initializer

为什么不允许这样做,而从方法中访问却是允许的呢?看看第8.3.3节,它讨论了如果尚未初始化字段时何时限制对字段的访问。

它列出了一些与类变量相关的规则:

对于简单地引用f在class或interface中声明的类变量的引用, 如果出现 以下情况C,则是 编译时错误

* 该引用出现在(§8.7)的类变量初始化器C或静态初始化器中。和C

  • 该引用显示在f自己的声明器的初始化程序中,或者出现在声明器的左侧f;和

* 该引用不在赋值表达式的左侧(第15.26节);和

  • 包含引用的最里面的类或接口是C

很简单,X = X + 1被这些规则捕获,方法访问不被捕获。他们甚至列出了这种情况并给出了一个例子:

不以这种方式检查方法的访问,因此:

class Z {
    static int peek() { return j; }
    static int i = peek();
    static int j = 1;
}
class Test {
    public static void main(String[] args) {
        System.out.println(Z.i);
    }
}

产生输出:

0

因为变量初始化器i使用类方法peek来访问变量的值,j然后j变量初始化器才对其进行了初始化,此时它 仍然具有其默认值
(第4.12.5节)。



 类似资料:
  • 首先,一个难题:下面的代码打印什么? 静态修饰符与最终修饰符结合使用也用于定义常量。最后一个修饰符指示此字段的值不能更改。 来源:https://docs.oracle.com/javase/tutorial/java/javaoo/classvars.html[加重点] 我的问题是:这是一个bug吗?定义不清吗?

  • 我想加密EnteredDetails(java bean)类型的arraylist,并将其序列化到一个文件中。我正在关注AES-128位加密的链接:http://www . code 2 learn . com/2011/06/encryption-and-decryption-of-data-using . html 要使用aes class的encrypt方法,我必须将arrarylist转换

  • 问题内容: 我正在尝试实现基于密码的加密算法,但出现此异常: javax.crypto.BadPaddingException:给定的最终块未正确填充 可能是什么问题? 这是我的代码: (JUnit测试) 问题答案: 如果尝试使用错误的密钥解密填充了PKCS5的数据,然后取消填充(由Cipher类自动完成),则很可能会收到BadPaddingException(可能略小于255/256,约为99.

  • 问题内容: 我需要有关此错误的帮助:给最终块未正确填充。从标题中可以看到,我正在使用AES。 这是错误的行代码: 这是完整的代码: } 问题答案: 根据您的评论,您几乎可以使加密工作。 您需要将IV生成代码从您的加密/解密方法移动到其他地方,就像这样 然后将该ivspec传递到加密和解密方法中(使它们看起来像),这样您就可以为加密和解密使用相同的iv。 另外,不要调用decryptedByteAr

  • 首先,我会告诉你我的主要目标是什么。在客户端使用AES加密部分内容,然后使用RSA公钥加密重要的AES规范,并将AES加密数据和RSA加密的AES规范发送到服务器端。所以在服务器端,我将使用RSA私钥解密AES密钥规范,然后使用这些AES规范,我将解密AES加密数据。通过测试加密和解密,我成功地使RSA部分工作。在此实现RSa之前,我必须使AES art工作。 对于客户端,我使用crypto-js

  • 我正在尝试编写方法来加密或解密字符串(大部分是数字)。它适用于某些文本(例如-'1010000011'、'1010000012'、'1010000013'),但也适用于其他文本(例如-'1010000014'、'1010000018'): javax.crypto.BadPadding异常:给定最后一个块没有正确填充 这是我的代码: 要加密的字符串从文件中读取,并在加密后写入其他文件。这些加密的文