Java你不知道的那些事儿—Java隐藏特性
编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。
每种编程语言都有其强大之处,无论你是初学者还是拥有多年项目经验的开发者,总会有未知的角落。就 Java 语言本身而言,即使你已开发多年、对其烂熟于心,是否能保证知晓所有的用法呢?今天我们就来整理一下 Java 中那些鲜为人知的隐藏特性。
一、双括号初始化语法(Double Brace Initialization)
双括号初始化主要适用于集合类(如 List、Map、Set 等)。当我们需要创建一个常量集合,或传递一个常量集合作为参数时,传统做法往往比较繁琐。
传统写法(以 Set 为例):
Set<String> validCodes = new HashSet<String>();
validCodes.add("XZ13s");
validCodes.add("AB21/X");
validCodes.add("YYLEX");
validCodes.add("AR2D");
removeProductsWithCodeIn(validCodes);或在类中初始化常量集合:
private static final Set<String> VALID_CODES = new HashSet<String>();
static {
validCodes.add("XZ13s");
validCodes.add("AB21/X");
validCodes.add("YYLEX");
validCodes.add("AR2D");
}每次这样写是否觉得费时费力?其实有更好的办法,那就是双括号语法:
private static final Set<String> VALID_CODES = new HashSet<String>() {{
add("XZ13s");
add("AB21/X");
add("YYLEX");
add("AR2D");
}};
// 或者直接在方法调用中使用
removeProductsWithCodeIn(new HashSet<String>() {{
add("XZ13s");
add("AB21/X");
add("YYLEX");
add("AR5E");
}});原理解析:
这里涉及两个括号的作用:
- 第一个括号
{}创建了一个新的匿名内部类。 - 第二个括号
{}声明了匿名内部类实例化时运行的实例初始化块(Instance Initializer)。
使用双括号语法需要注意以下几点:
匿名子类限制:如果要在匿名内部类中建立子类,只能用于非
final的类。这不仅局限于集合类,也可以用来实例化任何对象,例如 GUI 对象:add(new JPanel() {{ setLayout(...); setBorder(...); add(new JLabel(...)); add(new JSpinner(...)); }});与
equals(Object o)方法的兼容性:这种语法与常用的equals(Object o)方法可能不兼容。例如,若Example类中有如下方法:public boolean equals(final Object o) { if (o == null) { return false; } else if (!getClass().equals(o.getClass())) { return false; } else { Example other = (Example) o; // Compare this to other. } }那么,使用双括号初始化语法创建的对象(匿名内部类实例)不会与未使用该语法创建的对象相等(因为类类型不同)。因此,如果类中需要严谨的
equals(Object o)方法,建议不要使用这种语法。不过集合类通常没有这个问题,可能是因为集合内部优化了比较逻辑。替代方案:如果你使用的是集合类,且该类构造器接受另一个集合来生成实例,有更惯用的替代方法。例如
List初始化可以使用Arrays.asList():List<String> myList = new ArrayList<String>(Arrays.asList("One", "Two", "Three"));注意:
Arrays.asList返回的是一个长度不可变的列表(固定大小),无法通过add或remove来改变其长度。
总结建议:如果你只是要创建并初始化一个实例,而不是创建一个新类,或者创建任何不添加字段属性或重载方法的匿名类时,双括号语法是一个简洁的选择。
二、类型参数的交集(Type Parameter Joint Union)
Java 支持在泛型参数中绑定多个类型,即类型交集(Intersection Types)。语法如下:
public class ClassName<T extends Class & Interface1 & Interface2 & ...> {}注意:extends 后面只有第一个可以是类(Class),后面通过 & 连接的全部都是接口(Interface),且类的声明必须在接口之前。
示例:
如果你想要一个既是 Collection 类又是 Comparable 接口的参数,实现功能是判断两个给定集合是否相等,或集合中是否包含指定元素,可以使用下面的函数:
public static <A, B extends Collection<A> & Comparable<B>> boolean foo(B b1, B b2, A a) {
return (b1.compareTo(b2) == 0) || b1.contains(a) || b2.contains(a);
}这里 b1 和 b2 同时具有 Collection 和 Comparable 类型,因此既可以使用 Collection 的 contains 方法,也可以使用 Comparable 的 compareTo 方法。
三、VisualVM 监控工具(Java VisualVM)
VisualVM 是 JDK 6.0 Update 7 中自带的监控工具(位于 bin/jvisualvm.exe,Java 启动时不需要特定参数)。它能够监控线程、内存情况,查看方法的 CPU 时间和内存中的对象,以及已被 GC 的对象,甚至支持反向查看分配的堆栈(例如查看 100 个 String 对象分别由哪几个对象分配出来)。
双击打开后,从 UI 上来看,这个软件是基于 NetBeans 开发的。

界面比较简洁:
- 左侧:树形结构,自动显示当前本机所运行的 Java 程序,还可以添加远程的 Java VM(括号里面的 PID 指的是进程 ID)。
- Overview 界面:显示 VM 启动参数以及该 VM 对应的一些属性。
- Monitor 界面:监控 Java 堆大小、PermGen 大小、Classes 和线程数量。
- Profiler 界面:似乎可以动态地对某个 Java 程序进行调优。不过实际体验中,若有代码基础,在 NetBeans 里面调优可能更为自然;若无代码,调优的用处可能有限。
四、Classpath 支持通配符(Setting the class path)
这是 Java 6 开始支持的功能。在工程中,我们经常会有这样的配置:
java -classpath ./lib/log4j.jar:./lib/commons-codec.jar:./lib/commons-httpclient.jar:./lib/commons-collections.jar:./lib/myApp.jar so.Main这种写法比较复杂且容易出错。其实可以用通配符更加简洁方便:
java -classpath ./lib/* so.Main五、协变返回类型(Covariant Return Types)
这是 Java 5 添加的功能。在 Java 5 之前,我们在子类中覆盖基类的方法时,不能改变被覆盖方法的返回类型,即基类和子类的方法签名必须一模一样,想要改变只能在创建对象时进行强制类型转换(Cast)。
Java 5 过后,我们可以改变返回类型了,但需要注意的是:改变后的类型必须是原类型的子类型。举个例子就一目了然了:
public class CovariantReturnTypesTest {
public static void main(String[] args) {
Mill m = new Mill();
Grain g = m.process();
System.out.println(g); // output: Grain
m = new WheatMill();
g = m.process();
System.out.println(g); // output: Wheat
}
}
class Grain {
public String toString() {
return "Grain";
}
}
class Wheat extends Grain {
public String toString() {
return "Wheat";
}
}
class Mill {
Grain process() {
return new Grain();
}
}
class WheatMill extends Mill {
// 这里返回类型改为了 Grain 的子类型 Wheat
Wheat process() {
return new Wheat();
}
}说明:本文涉及的部分特性(如协变返回类型、Classpath 通配符)自 Java 5/6 引入,VisualVM 工具版本较老。在新版 JDK 中,部分工具可能已独立发布或界面有所更新,具体请以官方最新文档为准。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/java-ni-bu-zhi-dao-de-na-xie-shi-er-java-yin-cang-te-xing.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。