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

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

前言

最近在分析httpclient(v4.2)源码的时候,发现了一个比较有意思的事情,那就是关于java中如何定义常量的问题。我想在Java中定义常量并使用常量是非常非常常见的,那么如此常见的问题,又有什么好探讨的呢?但即便是这样常见的一个问题,如果仔细的去分析并加以总结的话,也会觉得非常的有趣。

为了弄清楚该问题,我先在osc的讨论区发了一个讨论帖http://www.oschina.net/question/271937_112971

image.png

我们先来看一下httpclient中是如何定义并使用常量的,以下为截取的部分代码片段:

/**
 * 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的继承结构是:

image.png

/**
 * 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。接下来我们看一小段使用该变量的代码片段:

virtualHost = (HttpHost) origWrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST);

相关调研

以上就是HttpClient中如何定义常量并使用常量的。

看到这里是不是觉得和我们平时的定义常量方式不太一样?至少和我的做法不太一样,我一般的做法是:

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,比较有价值的一些看法是:

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中,也有很多这种常量接口。但是这些接口应该被看作是不合规范的,并且避免大家效仿这种做法。


但是也有一些项目在接口中定义了系统需要使用的常量,然后所有的核心类都实现该接口,他们是通过这种方法定义并使用常量的。

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

目录
相关文章
|
2月前
|
存储 Java
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
|
2月前
|
存储 缓存 Java
Java本地高性能缓存实践问题之如何定义Caffeine的缓存
Java本地高性能缓存实践问题之如何定义Caffeine的缓存
|
6天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
20 5
|
2月前
|
安全 Java 网络安全
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
【认知革命】JAVA网络编程新视角:重新定义URL与URLConnection,让网络资源触手可及!
33 2
|
2月前
|
算法 Java 测试技术
Java零基础教学(15):Java常量详解
【8月更文挑战第15天】Java零基础教学篇,手把手实践教学!
51 5
|
2月前
|
存储 Java 测试技术
Java零基础(16) - Java常量
【8月更文挑战第16天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
32 4
|
2月前
|
Java 容器
07 Java数组与数组操作(定义+遍历+排序+增删改查)(上)
07 Java数组与数组操作(定义+遍历+排序+增删改查)
35 8
|
2月前
|
存储 Java API
07 Java数组与数组操作(定义+遍历+排序+增删改查)(下)
07 Java数组与数组操作(定义+遍历+排序+增删改查)
32 4
|
2月前
|
存储 缓存 Java
Java本地高性能缓存实践问题之定义Caffeine的缓存的问题如何解决
Java本地高性能缓存实践问题之定义Caffeine的缓存的问题如何解决
|
3月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
64 1
下一篇
无影云桌面