每一次通过命令行执行java class就会做一次类加载。假如类变量是一个UUID的串,每一次java class都不一样。
类变量和static块仅在类第一次加载执行一次。类加载之后,才会有实例化。所以可以理解为每一次类的实例化只有第一次实例化的时候,会导致类加载并执行static块,其它的都不会执行static块
static块的场景:用来初始化一些对所有实例都有用的公共数据结构。
结论:static块中的操作是线程安全的,类加载的阶段还没有实例被创建,也就不存在实例的线程中修改类变量的情况存在。也不会存在jvm中相同的类加载多次。
类变量什么时候不是线程安全的呢?
当类加载完成,static块执行完毕后,开始实例化多个对象,而这些对象都可以修改static变量。这个时候,需要同步。
示例1.weibo的配置初始化不需要同步
static
{
final String CLIENT_ID = "×××××";
final String CLIENT_SERCRET = "××××××××××××××××××";
final String BASEURL = "https://api.weibo.com/2/";
final String ACCESSTOKENURL = "https://api.weibo.com/2/oauth2/access_token";
final String AUTHORIZEURL = "https://api.weibo.com/2/oauth2/authorize";
WeiboConfig.updateProperties("client_ID", CLIENT_ID);
WeiboConfig.updateProperties("client_SERCRET", CLIENT_SERCRET);
WeiboConfig.updateProperties("baseURL", BASEURL);
WeiboConfig.updateProperties("accessTokenURL", ACCESSTOKENURL);
WeiboConfig.updateProperties("authorizeURL", AUTHORIZEURL);
};
示例2:下面的map写入不需要同步,因为对于enum,这个map其时只会在类加载的时候写一次。以后都是只读的操作。所以不存在同步。
public enum TestEnum {
T11("1", "1"), T12("1", "2"),T21("2","1");
public static Map<String, List<TestEnum>> map = new HashMap<String, List<TestEnum>>();
static
{
System.out.println("static init begin");
for (TestEnum test : EnumSet.allOf(TestEnum.class))
{
if(map.get(test.getKey())!=null){
map.get(test.getKey()).add(test);
}else{
List<TestEnum> list=new ArrayList<TestEnum>();
list.add(test);
map.put(test.getKey(),list);
}
}
System.out.println("static init end");
}
private String key;
private String val;
private TestEnum(String key, String val)
{
this.key = key;
this.val = val;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public String getVal()
{
return val;
}
public void setVal(String val)
{
this.val = val;
}
public static List<TestEnum> getTestEnumBy(String key){
return map.get(key);
}
}
Static除了修饰静态变量方法之外,还可以用来修改内部类,修改内部类的好处是实例化写起来方便。如果需要回调嵌套类的实例化方法,则不能使用static修饰。
A类有个叫B的内部类(非静态)
A a= new A();
a.B b = new a.B();
b就是B的一个对象。
假如B是内部静态类:
A.B b= new A.B();