关于Java常量定义的一点思考

前言

最近在分析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,才能使用接口名+字段的这样一种方式来访问该成员变量。

Java中如何定义常量

我的认为比较好的一种作法是:

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";
}
关于上述定义的几点说明:
  1. class的类型为final,表示该类是不能够继承的;
  2. 定义了一个私有的构造函数,避免实例化该类;
  3. 常量的类型为public final static;

关于如何使用常量?

在须要使用该常量的地方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

[4] http://en.wikipedia.org/wiki/Constant_interface

[5] http://www.javapractices.com/topic/TopicAction.do?Id=32

相关文章
相关标签/搜索