Java的隐藏功能

295

17
请注意,并不总是一个好主意使用这些隐藏的功能;往往会让阅读你代码的其他人感到惊讶和困惑。 - Kevin Bourrillion
1
你(/某人)应该像C#问题那样将答案整洁地总结在问题正文中。 - ripper234
100个回答

30

也许最令人惊讶的隐藏功能是 sun.misc.Unsafe 类。

http://www.docjar.com/html/api/ClassLib/Common/sun/misc/Unsafe.java.html

你可以做到以下几点:

  • 创建一个对象而不调用构造函数。
  • 抛出任何异常,即使是 Exception,也不需要担心方法中的 throws 子句。(我知道还有其他方法可以实现这一点)
  • 在不使用反射的情况下获取/设置对象中的随机访问字段。
  • 分配/释放/复制/调整大小为长达 64 位的内存块。
  • 获取对象中的字段位置或类中的静态字段位置。
  • 独立锁定和解锁对象锁。 (就像没有块的同步)
  • 根据提供的字节代码定义一个类。 而不是类加载器确定字节代码应该是什么。(您也可以使用反射完成此操作)

顺便说一句:不正确地使用此类将导致 JVM 崩溃。 我不知道哪些 JVM 支持此类,因此它不具备可移植性。


7
这不是Java的隐藏功能,而是某些特定JVM实现的隐藏功能。 - Nat
真的,虽然我还没有遇到过没有它的JSE。 如果有人知道这样的,请告诉我。 - Peter Lawrey
你是指oracle.misc.Unsafe吗? :) 我记得当我发现它时(在查看sun jvm AtomicInteger实现时),我感到非常着迷。 - Mauricio
我还没有看到它被称为oracle.*,我猜他们有一天可以自由地重命名它。 - Peter Lawrey
4
最近我使用它来分配一个16GB的内存块(这是Java所无法做到的),填充需要30秒钟。我真的可以看到主存储器作为新的磁盘。 ;) - Peter Lawrey

29

这是我的列表。

我最喜欢(也是最可怕的)隐藏功能是,您可以从未声明要抛出任何异常的方法中抛出已检查的异常。

import java.rmi.RemoteException;

class Thrower {
    public static void spit(final Throwable exception) {
        class EvilThrower<T extends Throwable> {
            @SuppressWarnings("unchecked")
            private void sneakyThrow(Throwable exception) throws T {
                throw (T) exception;
            }
        }
        new EvilThrower<RuntimeException>().sneakyThrow(exception);
    }
}

public class ThrowerSample {
    public static void main( String[] args ) {
        Thrower.spit(new RemoteException("go unchecked!"));
    }
}

此外,您可能想知道,您可以抛出“null”...
public static void main(String[] args) {
     throw null;
}

猜一下这个会打印什么:

Long value = new Long(0);
System.out.println(value.equals(0));

而且,猜猜这会返回什么:
public int returnSomething() {
    try {
        throw new RuntimeException("foo!");
    } finally {
        return 0;
    }
}

以上应该不会让优秀的开发人员感到惊讶。


在Java中,你可以用以下有效的方法声明一个数组:

String[] strings = new String[] { "foo", "bar" };
// the above is equivalent to the following:
String[] strings = { "foo", "bar" };

所以,以下Java代码是完全有效的:

public class Foo {
    public void doSomething(String[] arg) {}

    public void example() {
        String[] strings = { "foo", "bar" };
        doSomething(strings);
    }
}

以下代码为什么不合法呢?有没有什么有效的理由?

public class Foo {

    public void doSomething(String[] arg) {}

    public void example() {
        doSomething({ "foo", "bar" });
    }
}

我认为,上述语法可以有效替代Java 5引入的varargs,并且更加符合先前允许的数组声明。


2
合理的原因是编译器无法推断数组的类型。但是列表很好。 - CurtainDog
难道 doSomething({"",""}) 不是在Java 7中将通过简单的闭包支持的东西吗? - Shervin Asgari
关于您对“好的开发人员”的评论以及永远不会在实际应用中出现的try/finally坏风格谜题... 好的开发人员拥有一个IDE,即使在不完整的AST上也可以实时警告他们,“这样的返回语句(在finally块内)可能掩盖了抛出的异常”。那么,现在是谁在使用劣质的IDE的糟糕开发人员呢?;) - SyntaxT3rr0r
@SyntaxT3rr0r:好的开发者是那些比他们使用的IDE更了解编程,因为大多数逻辑/编码错误并不会被IDE发现。 - Luigi R. Viggiano
在运行时,throw null 应该会导致 NullPointerException - Paŭlo Ebermann

28

Shutdown Hooks 允许注册一个线程,在JVM结束时创建并启动。因此它是一种“全局jvm终结器”,您可以在此线程中执行有用的操作(例如关闭Java资源,如嵌入式hsqldb服务器)。它可以与 System.exit() 以及 CTRL-C / kill -15 一起使用(但在Unix上无法与kill -9一起使用)。

此外,设置也相当简单。

            Runtime.getRuntime().addShutdownHook(new Thread() {
                  public void run() {
                      endApp();
                  }
            });;

1
它们非常棒!如果您保留了引用,您也可以注销它们,以便对资源进行良好的清理。我使用它们 - 与Spring生命周期回调一起,特别是destroy-method属性 - 来终止工作子进程。 - Donal Fellows
请注意,如果调用Runtime.halt(),则不会执行关闭挂钩。 - Esko Luontola
3
如果使用javaw启动应用程序并且发起Windows注销,将不会调用Shutdown hooks。 必须存在一个控制台窗口。 唉。 我试图使用此方法在我们的网站上标记我为“离开办公室”,结果只能接受最小化的控制台窗口。 - Chris Mazzola

27

的价值:

new URL("http://www.yahoo.com").equals(new URL("http://209.191.93.52"))
< p >是< code >真的。

(来自Java Puzzlers)


48
仅在您连接到互联网时才能使用。如果无法解析地址,它会返回 false,因此 URL 类会破坏 equals() 契约。最好使用 java.net 中的 URI 类。 - Jorn
它不应该这样,因为HTTP服务器可以具有虚拟主机,并且可能会有不同的行为。 - rds

26

如果你经常进行JavaBean开发,并且使用属性更改支持,通常会编写很多像这样的setter:

public void setFoo(Foo aFoo){
  Foo old = this.foo;
  this.foo = aFoo;
  changeSupport.firePropertyChange("foo", old, aFoo);
}

我最近偶然发现了一篇博客,建议采用更简洁的实现方式,这可以让代码写起来更容易:

public void setFoo(Foo aFoo){
  changeSupport.firePropertyChange("foo", this.foo, this.foo = aFoo);
}

实际上,这简化了事情,以至于我能够在Eclipse中调整setter模板,使得方法自动创建。


1
Java中参数的执行顺序是否明确定义?否则,这可能会导致混乱。 - Konrad Rudolph
7
是的-参数的顺序或执行非常明确定义。我不确定我是否同意这比在每个JavaBean的每个setter中有三行垃圾代码更令人困惑 - 保持关注你想编写的代码比专注于这种样板更好! - Kevin Day
我不明白为什么较短的版本比较长的版本更容易自动创建。 - Jason Orendorff
Jason - 公平,代码生成可以任意选择实现方式。这似乎是代码生成的重点;-) 但是使用减少样板代码的方式阅读代码仍然更容易(getter和setter的存在仍然是大量样板代码,但这是另一个讨论话题)。 - Kevin Day
5
你可以使用Project Lombok来实现这个。 - Alfred
显示剩余2条评论

25

这种功能如果能直接在集合API中实现就太好了。 - Chris Mazzola
19
这不是语言的一部分;链接中的作者定义了“List”方法以创建一个列表。 - kdgregory
2
这是一个类似的方法: public static <T> List<T> List(T...elems){ return Arrays.asList( elems ); } 你也可以这样写: List<String> myList = new ArrayList<String>(Arrays.asList("One", "Two", "Three")); //如另一篇帖子中所述 - xgMz
这不是工厂设计模式吗?当然非常有用,特别是与可变参数一起使用。 - extraneon
6
静态导入 java.util.Arrays; List<String> names = Arrays.asList("jim", "john") - opsb
@opsb 还需要考虑到 asList 返回的是一个固定大小的基于数组的列表。 - Jeremy

23

虽然不是特别隐蔽,但很有趣。

你可以在没有main方法的情况下拥有一个“Hello, world”程序(尽管会抛出NoSuchMethodError异常)

最初由RusselW在Strangest language feature发布。

public class WithoutMain {
    static {
        System.out.println("Look ma, no main!!");
        System.exit(0);
    }
}

$ java WithoutMain
Look ma, no main!!

3
请在代码中添加 System.exit(0); 以避免出现难看的异常。 - Donal Fellows
8
这只是对静态初始化的简单滥用,我不认为这算是任何功能的特性... - pdinklag

23
作为初学者,我非常欣赏Java 6中的JConsole监控软件,它已经帮我解决了几个问题,而且我还在不断地发现新用途。
显然,JConsole早在Java 5中就已经存在了,但我认为现在它得到了改进,并且至少现在更加稳定运行。
Java 5中的JConsole: Java 5中的JConsole Java 6中的JConsole: Java 6中的JConsole 顺便提一下,还要好好看看系列中的其他工具: Java 6故障排除工具

1
未来版本中(可能是6u10),JConsole将被VisualVM所取代。 - Tom
2
当然,JConsole将被替换或者说是改进,我想从6u7开始。但是许多人仍在使用旧版本的Suns JVM,因此需要JConsole。我仍然没有找到任何支持JVisualVM将支持旧版本JDK的理论。 - PPS

22

如果您未使用默认初始化程序,则Java处理变量定义的一个巧妙技巧。

{
   int x;
if(一些条件) x=1;
if(x == 1) ... }

这将在编译时给您一个错误,指出您有一个路径其中 X 没有正确定义。这帮了我几次忙,所以我已经开始考虑像下面这样的默认初始化:

int x=0;
String s=null;

是一个不好的模式,因为它会阻止这种有用的检查。

话虽如此,有时很难回避——当它作为默认值时是有意义的,我不再在第一次编辑时加入 = null。


3
+1 赞同--由于某些原因,有些人认为不为变量提供初始值是“混乱的”,好像他们认为编译器会秘密地选择一个随机数之类的东西。但正如你所说,这是在编译时发现某些错误的有价值的工具。 - Neil Coffey
这是因为在C的世界中,未初始化的指针是存在的祸根 - 虽然如果我没记错的话,在C++的世界中,如果在堆栈上分配,指针会自动初始化为null。 - Chris K
是的,这就是为什么值得指出它不再是一个好主意,实际上有些适得其反。 - Bill K
5
尽可能使用“final”进行额外检查。 - Wouter Lievens

21

虽然这不是一个真正的隐藏功能,但当我看到它编译成功时,我还是感到非常惊讶:

public int aMethod(){
    http://www.google.com
    return 1;
}
编译器之所以能够编译该代码,是因为其中的 http://www.google.com 这一行,其中的 "http:" 部分被编译器视为标签,而该行剩余的部分被视为注释。 因此,如果你想写一些奇怪的代码(或混淆代码),只需在其中放置许多 http 地址即可。;-)

但已经超过一年了。请参考David Plumpton在2009年5月12日的回答:https://dev59.com/HHVD5IYBdhLWcg3wVKAb#851055(他只得到了2个赞...) - user85421
13
这是重复的答案。 - Shervin Asgari
7
它最多只能与每种方法关联一个HTTP地址,因为标签必须是唯一的。 - Paŭlo Ebermann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接