【问题标题】:Java Class workflow [duplicate]Java类工作流程[重复]
【发布时间】:2015-11-20 18:08:15
【问题描述】:

如果我有一个带有常量的类,例如 AppConstants 并使用 public static final 变量从那里像 AppConstants.MY_STRING 它会一直重新创建这个类,或者它在运行时创建它,因为类中的字段是静态的?那么在常量中定义文件还是在“原地”定义文件更好?

【问题讨论】:

  • @AndyBrown 我会说这个问题是相关的,但不回答 OP 的问题,因为 OP 询问类加载。
  • @LuiggiMendoza - 该问题的第一个答案是:“每个 ClassLoader 一个”。似乎它直接回答了这个问题,但如果不是重复的话,在过去 6 年中还有其他几个答案,可能是this one,或this one,或...
  • 如果您认为我很讨厌,我很抱歉,我真的不是。 Closing as duplicate is not a personal attack,是为了帮助人们更容易找到正确的答案,一般有两个问题是considered duplicate if they have the same answer
  • @AndyBrown 您提供的那些链接谈论static 成员。请理解,OP 的询问并非 100% 与static 成员相关,更多的是关于类加载。虽然the answer to this question 解释了回答这个问题的概念,但它并没有直接这样做。因此 OP 提出了一个问题并得到了直接的答案。如果您仍然反对,请在meta 上提出问题并提出相关问题。
  • 我建议您再次查看我的答案,以便更好地了解在需要时加载类的含义。

标签: java


【解决方案1】:

类由类加载器加载一次,通常在需要时1。当一个类被加载时,它的所有static 成员也会被加载并“与类一起存在”,而不是与该类的特定对象引用一起加载。多次使用类的static 字段不会触发类的重新加载。重新加载类的唯一方法是使用自定义类加载器。

在常量中定义文件还是“在原地”更好?

将它们定义为类中的static final 字段或使用enums。


1定义当需要时

如果您像这样声明原始或String 常量:

public class AppConstants {
    public static final int ONE = 1;
    public static final int TWO = 2;
    public static final int TEN = 10;
}

public class ClientTest {
    public static void main(String[] args) {
        System.out.println(AppConstants.ONE);
        System.out.println(AppConstants.TWO);
        System.out.println(AppConstants.TEN);
    }
}

当编译两个类并执行ClientTest 时,AppConstants 类将不会被加载(使用 HotSpot 测试),因为编译器将内联常量。为了评估结果,请执行以下操作:

> javac -cp:. AppConstants.java ClientTest.java
> javap -c ClientTest //you can see the generated bytecode
> java -verbose:class ClientTest //shows you the classes loaded to execute this app

你会看到这个。

来自javap -c ClientTest(正确代码):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   iconst_1 //My Comment: constant with value 1
   4:   invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   7:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  iconst_2 //My Comment: constant with value 2
   11:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   14:  getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   17:  bipush  10 //My Comment: constant with value 10
   19:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
   22:  return

}

来自java -verbose:class ClientTest

[Opened C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Object from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
// Lots of classes from rt.jar
[Loaded java.security.UnresolvedPermission from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.security.BasicPermissionCollection from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded ClientTest from file:/D:/tmp/] //<-- Class being executed
[Loaded sun.launcher.LauncherHelper$FXHelper from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Void from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
1
2
10
[Loaded java.lang.Shutdown from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]

没有AppConstants 类被加载的痕迹,只有ClientTest

但是如果你添加一个引用对象引用的常量,例如List,然后将加载该类。从上面的例子:

public class AppConstants {
    public static final int ONE = 1;
    public static final int TWO = 2;
    public static final int TEN = 10;
    public static final List<String> NAMES = Collections.unmodifiableList(Arrays.asList("Luiggi", "Andy"));
}

public class ClientTest {
    public static void main(String[] args) {
        System.out.println(AppConstants.ONE);
        System.out.println(AppConstants.TWO);
        System.out.println(AppConstants.TEN);
        System.out.println(Foo.NAMES);
    }
}

现在提供java -verbose:class ClientTest的相关输出:

//...
[Loaded ClientTest from file:/D:/tmp/] //our class being executed
[Loaded sun.launcher.LauncherHelper$FXHelper from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.lang.Void from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
1  //output of constants
2
10
[Loaded AppConstants from file:/D:/tmp/] //loads AppConstants class here because it's needed
[Loaded java.util.Arrays$ArrayList from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Loaded java.util.AbstractList$Itr from C:\Program Files\Java\jre1.8.0_51\lib\rt.jar]
[Luiggi, Andy] //output of AppConstants#NAMES

【讨论】:

  • 除了重复的讨论之外,这有点误导。 AFAIK public static final 变量的使用实际上是由编译器在可能的情况下内联的,因此这些引用根本不会加载相应的类。
  • @AndyBrown 我不明白你的评论有什么误导性。
  • OP 说:“如果我有一个带有常量的类......并使用......从那里像 AppConstants.MY_STRING 它会一直重新创建这个类,或者它在运行时创建它因为类中的字段是静态的?”在他的特定情况下,它根本不会创建该类 - 常量将被内联。在您的回答中,您说“loaded ... when needed”,但没有说明在这种情况下它不会被加载,因为它不需要。
  • @AndyBrown 如果你有一个类 AppConstants 并且你不使用它,那么它将不会被加载。如果你有这样一行String s = AppConstants.MY_STRING 并且执行了这一行,那么AppConstants 类被使用,因此它将被加载一次,因为需要该类。
  • 这就是我的确切观点 - 如果这就是你所做的一切,则不会加载该类。该常量是内联的,因此类加载器不会加载该类。试试看 - 如果你愿意,我可以给你发送代码。
猜你喜欢
  • 2017-01-18
  • 2011-02-09
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 2020-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多