java语言一直在进化,java类库也越来越庞大,给人的感觉是一直在做加法。
有没有做减法呢?当然也有,一些是设计失误需要撤回的,如可能引起线程安全问题的Thread.stop()等方法;还有是随着时代发展而被淘汰的,比如曾被寄予厚望的Applet。
java类库中这些废弃的内容有没有地方统一归档,方便我们的查找?假如我们要升级java版本,而找出当前代码中使用了哪些新版本废弃的API,就能让准备更加充分。
答案是有的。
java9之后的java API文档,贴心地单独为废弃的内容做了一个页面。如java 21版本对应的链接是 Deprecated List (Java SE 21 & JDK 21)。
顶上有个过滤功能,可以按特定版本来分别过滤该版本废弃了哪些,其中的others选项包含了11之前所有版本的废弃内容。
接下来是目录,根据类型分为接口、(非异常)类、异常类、字段、方法、常量、枚举常量这七种。而最上面的终将废弃( Terminally Deprecated),和下面的七种并不是并列关系,而是将下面七种类型中最终会被删除的部分提取(复制)出来,单独放在这里。
为什么这样做?因为废弃的含义分为两种,一种是后续版本要删除的(即终将废弃),如果你的代码使用了这样的API,如果后续升级类库,你的代码就可能无法通过编译。而另一种是不会删除的API,但不建议你使用,后续版本也会保留,升级类库不会编译失败。作为类库的用户,显然更关注第一种,所以就单独放了一份。(更多可参考 JEP 277: Enhanced Deprecation)
接下来就是具体的内容,分别是名称、版本号和描述,并支持按名称或版本号进行排序。按版本号排序很实用,描述的内容也值得参考,里面会提供废弃的原因,是否有替代品等信息。
简单地浏览一部分:
终将废弃并删除的
java.lang.Thread.stop() 等
还有resume、suspend 以及 ThreadGroup类对应的方法。早在1.2就废弃了。
参考 Java Thread Primitive Deprecation (Java SE 21 & JDK 21) (oracle.com)https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html
java.applet.Applet等
java9废弃,早就没人用了
java.lang.Boolean(boolean) 等构造函数
还有Byte、Character、Double等基本类型的包装类,它们的构造函数都在java 9废弃了。改用静态工厂方法,可以参考 Effective Java 的第一条建议。
java.lang.Object.finalize()
一直没存在感的方法终于在java 9 废弃了,减少了记忆学习的负担。虽然这里建议改用 Cleaner,但 Effective Java 3 第八条告诉我们,Cleaner最好也别去碰。
javax.security.cert.Certificate 等
其实是转正了,包名从 javax... 换成了 java.security.cert 。可见网络安全的地位从可选变成了必选。java 9 废弃。
java.lang.SecurityManager 及其相关
java 17废弃。这安全也是当年java的一大卖点,现在也没落了。
这个的确很少用,页面中给出了废弃原因的链接: JEP 411。
主要就是说,一是用于客户端代码安全,但Servlet早就没人用了;二是用于服务端代码安全,可实际上基本没什么人用。
java类库越来越大,要管的东西太多,我们维护起来太累,放弃了。
另外2020年CWE的top 25安全漏洞,Security Manager有19个都管不了,还要它干嘛?(感觉这个有点强词夺理,也没指望人一个机制能解决所有层面上的漏洞)
发明了20多年,但业界太少人用了,除了少量软件如ElasticSearch和Tomcat。
甚至.NET都不支持了,java废弃它的理由就更充分啦。
废弃但不删除的
java.util.Observer 接口 和 java.util.Observable 类
java9 废弃,理由是功能太局限。
如果要更丰富的事件模型,用 java.beans包。如果要可靠有序,用 java.util.concurrent 包。如果要用响应式流风格编程,用 Flow API。
java.util.Date.getDate() 等方法
从1.1版本起就废弃了。
废弃的有 get/set 年月日时分秒等方法,以及转换成字符串的方法。废弃的原因是这些api不支持国际化。所以要获取年月日等信息,要用Calendar类;而格式化或者日期解析,要用 DateFormat类。
Date类本身剩下的非废弃方法就两三个,只能用于比大小,功能非常有限,感觉类似于废弃。构造函数只剩两个,一个是无参,根据当前时间构造对象,另一个是long毫秒数为参数。还有个java 8加入的静态工厂方法,根据Instant对象生成Date对象。
java.lang.Class.newInstance() 方法
java 9废弃。这个方法用来调用某个类的无参构造器,如果构造器有异常,则newInstance方法会原样抛出来。但newInstance方法本身只声明了两个 checked 异常:InstantiationException 和 IllegalAccessException。这个和java语言规定的“方法必须声明所有自己可能抛出的checked异常”就矛盾了,等于绕过了编译器的校验。
建议改用 Constructor.newInstance 方法,它会把构造器的异常包装成 InvocationTargetException 抛出来,而且也在方法中声明了。
java.lang.Runtime.exec(String)等方法
java 18废弃。并不是所有exec都废弃了,只是废弃了其中几个容易出问题的:
java.lang.Thread.getId()
java19废弃。因为它不是final,可能被覆写。现在加final又太迟了。建议改用 Thread.threadId()。
java.util.Locale的构造器
所有构造器全部在java 19废弃了。替代方式有很多,比如 Local.Builder,还有两个静态工厂方法,或者常量,以及匹配/过滤/查找等方法。
java.net.URL的构造器
所有构造器全部在java 20废弃了。改用URI或URL的一些静态工厂方法。
补充
jdeprscan
相比看文档,想知道当前项目里用了哪些废弃的java类库API,更精确的方法是使用 jdeprscan ,具体可以参见 The jdeprscan Command (oracle.com)
javadoc
新版本的javadoc也支持生成“废弃”页面。比如写这样一个类:
/** @deprecated useless class */
@Deprecated(since="11", forRemoval=true)
public class Hello {
/** @deprecated useless method */
@Deprecated(since="9", forRemoval=true)
public static void main(String[] args) {
System.out.println(Thread.currentThread().getId());
}
}
然后执行
javadoc Hello.java
打开生成的api文档,可以看到
不过相比Oracle提供的文档,我们的文档少了两部分,一是顶部按版本过滤的复选框,二是表格中的版本列。也许是Orcale做了定制,还需要再研究下。