最近在分析httpclient(v4.2)源碼的時候,發現了一個比較有意思的事情,那就是關於java中如何定義常量的問題。我想在Java中定義常量並使用常量是很是很是常見的,那麼如此常見的問題,又有什麼好探討的呢?但即使是這樣常見的一個問題,若是仔細的去分析並加以總結的話,也會以爲很是的有趣。 html
爲了弄清楚該問題,我先在osc的討論區發了一個討論帖http://www.oschina.net/question/271937_112971 java
咱們先來看一下httpclient中是如何定義並使用常量的,如下爲截取的部分代碼片斷: apache
/** * Parameter names for HTTP client parameters. * * @since 4.0 */ public interface ClientPNames { ... /** * Defines whether redirects should be handled automatically * <p> * This parameter expects a value of type {@link Boolean}. * </p> */ public static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects"; /** * Defines whether relative redirects should be rejected. HTTP specification * requires the location value be an absolute URI. * <p> * This parameter expects a value of type {@link Boolean}. * </p> */ public static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect"; /** * Defines the virtual host to be used in the <code>Host</code> * request header instead of the physical host. * <p> * This parameter expects a value of type {@link org.apache.http.HttpHost}. * </p> * If a port is not provided, it will be derived from the request URL. */ public static final String VIRTUAL_HOST = "http.virtual-host"; /** * Defines the timeout in milliseconds used when retrieving an instance of * {@link org.apache.http.conn.ManagedClientConnection} from the * {@link org.apache.http.conn.ClientConnectionManager}. * <p> * This parameter expects a value of type {@link Long}. * <p> * @since 4.2 */ public static final String CONN_MANAGER_TIMEOUT = "http.conn-manager.timeout"; }
ClientPNames的繼承結構是: oracle
/** * Collected parameter names for the HttpClient module. * This interface combines the parameter definitions of the HttpClient * module and all dependency modules or informational units. * It does not define additional parameter names, but references * other interfaces defining parameter names. * <br/> * This interface is meant as a navigation aid for developers. * When referring to parameter names, you should use the interfaces * in which the respective constants are actually defined. * * @since 4.0 */ @SuppressWarnings("deprecation") public interface AllClientPNames extends CoreConnectionPNames, CoreProtocolPNames, ClientPNames, AuthPNames, CookieSpecPNames, ConnConnectionPNames, ConnManagerPNames, ConnRoutePNames { // no additional definitions }
在ClientPNames中定義了一些常量,且該接口只有一個繼承接口AllClientPNames。接下來咱們看一小段使用該變量的代碼片斷: app
virtualHost = (HttpHost) origWrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST);
以上就是HttpClient中如何定義常量並使用常量的。 socket
看到這裏是否是以爲和咱們平時的定義常量方式不太同樣?至少和個人作法不太同樣,我通常的作法是: ide
public class SystemConstants { public final static int CONNECTION_TIMEOUT = 30000; public final static int SO_TIMEOUT = 60000;// socket timeout public final static String PROP_FILE_PATH = "conf.txt"; }
關於在interface中定義常量的這個問題,我也作了一些相關方面的調研: 函數
stackoverflow上也有一些關於該問題的討論,如http://stackoverflow.com/questions/2659593/what-is-the-use-of-interface-constants,比較有價值的一些見解是: ui
The constant interface pattern is a poor use of interfaces. That a class uses some constants internally is an implementation detail. Implementing a constant interface causes this implementation detail to leak into the class's exported API. It is of no consequence to the users of a class that the class implements a constant interface. In fact, it may even confuse them. Worse, it represents a commitment: if in a future release the class is modified so that it no longer needs to use the constants, it still must implement the interface to ensure binary compatibility. If a nonfinal class implements a constant interface, all of its subclasses will have their namespaces polluted by the constants in the interface. There are several constant interfaces in the java platform libraries, such as java.io.ObjectStreamConstants. These interfaces should be regarded as anomalies and should not be emulated.
常量接口模式是對java接口的一種poor use。一個類內部的使用這些常量是一個實現細節。當一個類實現了該接口,那麼這個接口就會成爲該類公共API的一部分。這個類的內部實現細節不該該暴露給公共API。這個類是否實現了一個常量接口對於用戶來講是可有可無的。事實上,這種作法可能會混淆用戶。更糟糕的是,它表現爲一種義務:若是在將來的發行版中不須要再使用那些常量,可是爲了兼容性該類仍是須要實現這個接口。若是一個nonfinal類實現了一個常量接口,那麼這個常量接口中定義的常量將會污染他的全部子類的的命名空間。在java平臺的一些類庫如java.io.ObjectStreamConstants中,也有不少這種常量接口。可是這些接口應該被看做是不合規範的,而且避免你們效仿這種作法。 this
可是也有一些項目在接口中定義了系統須要使用的常量,而後全部的核心類都實現該接口,他們是經過這種方法定義並使用常量的。
interface中聲明的成員變量爲何默認爲final static的?
關於這個問題stackoverflow上也有一些相關的說明比較有價值:
Interfaces are meant to give only specification. It can not contain any implementations. So To avoid implementing classes to change the specification, it is made final. Since Interface cannot be instantiated, they are made static to access the field using interface name.
所謂的接口就是一些協議,契約的定義,他不可以包含任何的實現。因此爲了不實現類去修改這個契約,因此他必須被聲明爲final,另外由於接口不可以被實例化,因此只有經過聲明爲static,才能使用接口名+字段的這樣一種方式來訪問該成員變量。
public final class SystemConstants { private SystemConstants(){ } public final static int CONNECTION_TIMEOUT = 30000; public final static int SO_TIMEOUT = 60000;// socket timeout public final static String PROP_FILE_PATH = "conf.txt"; }關於上述定義的幾點說明:
關於如何使用常量?
在須要使用該常量的地方import static *****
import static SystemConstants.CONNECTION_TIMEOUT;
而後就能夠在該類中直接使用該常量了。
本文是在閱讀httpclient源碼的時候,發現其java常量定義的方式和咱們平時有些不同,故深刻調研了一些有關java常量定義的相關資料,並整理成博文發表,以便你們參考和討論。
經過此次對java常量定義的一些調研和思考,發現本身之前寫的代碼太隨意了太不嚴謹,考慮的太少,思考的也太少,經過閱讀大師的代碼,一點點的提升。
[1] http://docs.oracle.com/javase/1.5.0/docs/guide/language/static-import.html
[2] http://stackoverflow.com/questions/2659593/what-is-the-use-of-interface-constants
[3] http://stackoverflow.com/questions/2659593/what-is-the-use-of-interface-constants