我的问题是静态关键字的一个特定用法.可以使用static关键字来覆盖不属于任何函数的类中的代码块.例如,以下代码编译:
public class Test {
private static final int a;
static {
a = 5;
doSomething(a);
}
private static int doSomething(int x) {
return (x+5);
}
}
Run Code Online (Sandbox Code Playgroud)
如果你删除static它抱怨的关键字,因为变量a是final.但是,可以删除两个final和static关键字并使其编译.
这两种方式让我感到困惑.我怎么能有一个不属于任何方法的代码部分?如何调用它?一般来说,这种用法的目的是什么?或者更好,我在哪里可以找到关于此的文档?
我正在使用静态代码块来初始化我所拥有的注册表中的某些控制器.因此,我的问题是,我可以保证这个静态代码块只在首次加载类时才会被调用一次吗?我知道我无法保证何时会调用此代码块,我猜它是在Classloader首次加载它时.我意识到我可以在静态代码块中同步类,但我的猜测实际上这是怎么回事?
简单的代码示例是;
class FooRegistry {
static {
//this code must only ever be called once
addController(new FooControllerImpl());
}
private static void addController(IFooController controller) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
或者我应该这样做;
class FooRegistry {
static {
synchronized(FooRegistry.class) {
addController(new FooControllerImpl());
}
}
private static void addController(IFooController controller) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud) java static multithreading synchronization static-initializer
为什么Java不允许从静态初始化块中抛出已检查的异常?这个设计决定背后的原因是什么?
所以我最近一直在研究我的Java技能,并且发现了一些我之前不知道的功能.静态和实例初始化器是两种这样的技术.
我的问题是什么时候会使用初始化程序而不是在构造函数中包含代码?我想到了几个明显的可能性:
static/instance initializers可用于设置"final"静态/实例变量的值,而构造函数则不能
静态初始化程序可用于设置类中任何静态变量的值,这应该比在每个构造函数的开头具有"if(someStaticVar == null)// do stuff"代码块更有效
这两种情况都假设设置这些变量所需的代码比简单的"var = value"更复杂,否则似乎没有任何理由使用初始化器而不是在声明变量时简单地设置值.
然而,虽然这些并非微不足道的收益(特别是设置最终变量的能力),但似乎应该使用初始化程序的情况相当有限.
当然可以在构造函数中使用初始化器来完成很多工作,但我真的没有看到这样做的原因.即使一个类的所有构造函数共享大量代码,使用私有initialize()函数似乎比使用初始化程序更有意义,因为它不会阻止您在编写新代码时运行该代码构造函数.
我错过了什么吗?是否还有许多其他情况需要使用初始化程序?或者它是否真的只是在非常具体的情况下使用的相当有限的工具?
java constructor initializer static-initializer initialization-block
假设一个项目包含几个类,每个类都有一个静态初始化块.这些块以什么顺序运行?我知道在一个类中,这些块按照它们在代码中出现的顺序运行.我已经读过它在各个类中都是一样的,但是我写的一些示例代码不同意这一点.我用过这段代码:
package pkg;
public class LoadTest {
public static void main(String[] args) {
System.out.println("START");
new Child();
System.out.println("END");
}
}
class Parent extends Grandparent {
// Instance init block
{
System.out.println("instance - parent");
}
// Constructor
public Parent() {
System.out.println("constructor - parent");
}
// Static init block
static {
System.out.println("static - parent");
}
}
class Grandparent {
// Static init block
static {
System.out.println("static - grandparent");
}
// Instance init block
{
System.out.println("instance - grandparent");
}
// Constructor
public Grandparent() { …Run Code Online (Sandbox Code Playgroud) 我在博客上看到它建议以下是使用getCode(int)Java枚举进行"反向查找"的合理方法:
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private static final Map<Integer,Status> lookup
= new HashMap<Integer,Status>();
static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() { return code; }
public static Status get(int code) {
return lookup.get(code);
}
}
Run Code Online (Sandbox Code Playgroud)
对我来说,静态地图和静态初始化程序看起来都是个坏主意,我的第一个想法就是将查询编码为:
public enum Status {
WAITING(0),
READY(1),
SKIPPED(-1),
COMPLETED(5);
private int code;
private Status(int code) {
this.code = code;
} …Run Code Online (Sandbox Code Playgroud) 给出以下代码:
public static class Helpers
{
private static Char[] myChars = new Char[] {'a', 'b'};
private static Int32 myCharsSize = myChars.Length;
}
Run Code Online (Sandbox Code Playgroud)
是否保证在我使用其长度分配之前myChars将其初始化?myCharsSize
什么是最好,最干净的方法?具体来说,我需要静态初始化程序块中的一些代码才能在该类中运行,但我希望尽可能使它看起来更干净.
我遇到过我的程序挂起的情况,看起来像死锁.但我尝试用jconsole和visualvm来解决它,但他们没有发现任何死锁.示例代码:
public class StaticInitializer {
private static int state = 10;
static {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
state = 11;
System.out.println("Exit Thread");
}
});
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("exiting static block");
}
public static void main(String...strings) {
System.out.println(state);
}
}
Run Code Online (Sandbox Code Playgroud)
当我在调试模式下执行它时,我可以看到控制到达@Override public void run(){state = 11;
但只要执行state = 11,它就会挂起/死锁.我在stackoverflow中查看了不同的帖子,我认为静态初始化程序是线程安全的,但在这种情况下,jconsole应该报告这一点.关于主线程,jconsole说处于等待状态,那很好.但对于在静态初始化程序块中创建的线程,jconsole表示它处于RUNNABLE状态而未被阻止.我很困惑,这里缺乏一些概念.请帮帮我.
默认情况下,Sun的JVM都懒惰地加载类并且懒惰地初始化(即调用它们的<clinit>方法)它们.考虑以下类,ClinitBomb它Exception在static{}块中抛出一个.
public class ClinitBomb {
static {
explode();
}
private static void explode() {
throw new RuntimeException("boom!");
}
}
Run Code Online (Sandbox Code Playgroud)
现在,考虑如何触发炸弹:
public class Main {
public static void main(String[] args) {
System.out.println("A");
try {
Class.forName("ClinitBomb");
} catch (Exception e) {
e.printStackTrace(System.out);
}
System.out.println("B");
ClinitBomb o2 = new ClinitBomb();
System.out.println("C");
}
}
Run Code Online (Sandbox Code Playgroud)
我们保证爆炸发生在B点之前,因为forName文件说明了这一点; 问题是它是否发生在A点之前(Main加载时).在Sun的JVM中,即使main()包含静态引用ClinitBomb,它也会在A之后发生.
我想要一种方法告诉JVM ClinitBomb一旦初始化就加载并初始化Main(因此炸弹在 A点之前爆炸).一般来说,我想要一种方式来说,"无论何时加载/初始化类X,对任何一个也是如此它引用的Y类."
有没有办法做到这一点?
java ×9
static ×2
c# ×1
class ×1
classloader ×1
coding-style ×1
constructor ×1
deadlock ×1
enums ×1
exception ×1
initializer ×1