关于常量类使用的一个有关问题

关于常量类使用的一个问题。
系统有一个常量类,用来保存一些公用的不可变信息,所有常量都被声明为 public static final,但其中一个常量,用于保存系统的绝对路径(WebRoot),这个常量需要在Servlet启动后加载,所以目前就不能把改常量声明为final,但这存在一个非常大的隐患,就是如果某程序员不小心(或恶意)在代码中修改了改常量,将会导致整个系统无法正常运行(因为系统的配置信息文件都保存在该常量指定的路径中)。

这里有两个问题:
1.这种情况,请问有什么比较好的解决方案;

2.对于常量类,他没有公有的构造方法,没有任何静态或非静态方法,只有static成员变量,请问该常量类在内存中会否存在实例,而它的所有成员变量,究竟是在什么时候被实例化到系统的内存当中?是当Web系统被容器加载的时候吗?还是在该常量被第一次调用的时候才延迟加载?
1 楼 together 2006-12-27  
常量就应该是final的。
如果需要用到webroot,单独写一个方法去调用好了。request.getRealPath虽然会出现warning,也是能用的。
不然就servlet.getServletContext.getRealPath。系统初始化的时候赋值。

要是怕别人随便改动变量值的话,定期检查代码吧。
2 楼 johnnylzb 2006-12-27  
第二个问题?
3 楼 foxty 2006-12-27  
johnnylzb 写道
第二个问题?


如果纯粹的只有static属性的话,这个类是不会在系统种存在实例对象的。

它的成员变量如果都是static final的并且在编译时能确定其值,则在编译时便已经将常量写入倒其他引用此属性的类中了。
4 楼 johnnylzb 2006-12-27  
foxty 写道
johnnylzb 写道
第二个问题?


如果纯粹的只有static属性的话,这个类是不会在系统种存在实例对象的。

它的成员变量如果都是static final的并且在编译时能确定其值,则在编译时便已经将常量写入倒其他引用此属性的类中了。
在编译的时候已经将常量写入到其他引用此属性的类中?

这句话我不大理解,如果其他类是在系统运行的时候才实例化的呢?在这些类都没有实例化的时候,这些常量已经存在于内存当中吗?我还是不清楚这些常量的实例会在什么时候产生的。
5 楼 codeutil 2006-12-27  
不要用  public static final,这个在编译之后就已经写到其它的class里了,比如
A.class 有  public static final String path="123";
B.class用了它.
然后编译好文件,这个时候修改path为"312",只重新编译A.class,不重新编译B.class,这个时候B.class里的path是不变的.



建议限制为通过get方法访问,且set方法只能够在未初始化的时候执行一次赋值(非null的) 操作.




	private static String path = null;

	public static String getPath() {
		return path;
	}

	public static void setPath(String apath) {
		if (path == null) {
			path = apath;
		}
	}

6 楼 LucasLee 2006-12-27  
johnnylzb 写道
系统有一个常量类,用来保存一些公用的不可变信息,所有常量都被声明为 public static final,但其中一个常量,用于保存系统的绝对路径(WebRoot),这个常量需要在Servlet启动后加载,所以目前就不能把改常量声明为final,但这存在一个非常大的隐患,就是如果某程序员不小心(或恶意)在代码中修改了改常量,将会导致整个系统无法正常运行(因为系统的配置信息文件都保存在该常量指定的路径中)。

这里有两个问题:
1.这种情况,请问有什么比较好的解决方案;

2.对于常量类,他没有公有的构造方法,没有任何静态或非静态方法,只有static成员变量,请问该常量类在内存中会否存在实例,而它的所有成员变量,究竟是在什么时候被实例化到系统的内存当中?是当Web系统被容器加载的时候吗?还是在该常量被第一次调用的时候才延迟加载?


这个问题还是比较常见的,有很简单的方法。
1.任何时候你需要控制对变量的访问,你不应该直接暴露这个变量,而是使用getter、setter方法来访问。
  这样你可以将getter方法设置为public,实例变量是private,那么你可以在内部或者其他合适的地方设置此变量值,而其他地方通过getter访问则是只读的。
2.常量变量跟其他变量一样的实例化,不同的只有初始化时能赋值。如果被声明为static的,则是在第一个实例构造之前被创建。
7 楼 foxty 2006-12-27  
johnnylzb 写道
foxty 写道
johnnylzb 写道
第二个问题?


如果纯粹的只有static属性的话,这个类是不会在系统种存在实例对象的。

它的成员变量如果都是static final的并且在编译时能确定其值,则在编译时便已经将常量写入倒其他引用此属性的类中了。
在编译的时候已经将常量写入到其他引用此属性的类中?

这句话我不大理解,如果其他类是在系统运行的时候才实例化的呢?在这些类都没有实例化的时候,这些常量已经存在于内存当中吗?我还是不清楚这些常量的实例会在什么时候产生的。


举个例子;

class A{
  public static int D = 12;
}

public class B{
  public static void main(String[] args)
  {
    int data = A.D;
    //some operations...
  }
}
这种情况下,classloader载入class B的时候,同时也会载入class A,但是这个时候类A并未初始化(注意不是实例化)。这个时候A.D所指的12是放在class A的常量池中,当程序运行至int data = A.D的时候,才会初始化class A(注意不是实例化),同时查找A的字段列表(从类A的常量池中)获取12。

======================================

class A{
  public static final int D = 12;
}
class B同上。
这种情况下,编译器编译B的时候,发现A.D是一个static而且final的常量,那么它会将这个12直接写入倒class B的字节码当中(即int data = A.D 已经就是int data = 12了),类的载入过程同情况1。

======================================

还有一种特殊情况。
class A{
  public static final int D = new java.util.Random().nextInt();
}
class B同上。
这个时候,编译器编译B的时候虽然发现D是一个static且final的属性,但是它的值确需要在运行时才能确定,所以就无法把D的值嵌入倒B的字节码中,当执行B的mian方法时,还是同第一个情况一样,要去查找A的字段列表获取D。类的载入过程同情况1。
8 楼 johnnylzb 2006-12-27  
非常清楚,谢谢
9 楼 凤舞凰扬 2006-12-28  
johnnylzb 写道
1.这种情况,请问有什么比较好的解决方案;

   这其实和你操作普通的成员变量类似,你可以将静态变量设置为default(也就是不设置)或者privated,然后通过静态方法去get/set值就可以了。一般来说,设置为public的静态变量应该是final的。
johnnylzb 写道

2.对于常量类,他没有公有的构造方法,没有任何静态或非静态方法,只有static成员变量,请问该常量类在内存中会否存在实例,而它的所有成员变量,究竟是在什么时候被实例化到系统的内存当中?是当Web系统被容器加载的时候吗?还是在该常量被第一次调用的时候才延迟加载?

常量类一样有实例,只是这个实例在未使用new之前是不会装载的。VM所做的只是将静态变量和静态方法分配固定的空间。一般说来,静态变量的装载是随着所在类被装载时,这并不是被调用时才装载。比如说有个类Aimport了这个常量类,但是并没有用到其中的方法,那么常量类的静态方法和变量就会被VM装载。这种import可以是具体的,也可以是*,比如import java.util.*。这也是Java编程规范中建议不要使用*的原因之一。
10 楼 Readonly 2006-12-28  
johnnylzb 写道
用于保存系统的绝对路径(WebRoot)

题外话,保存系统的WebRoot需要这样做吗?
11 楼 shaucle 2006-12-28  
法1 :只读.
private String property1;
<static>{
//load from property files
}
pucblic String getProperty1();
没有set

法2 :只允许设置一次.
void setProperty(){
if(setted){
return or throw
}
//set...
setted = true;
}
12 楼 wjtang 2006-12-28  
同意楼上各位意见,有个建议,对于这一类的信息为什么不用property file or xml file 去保存.