Java的隐藏功能

295

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

15

这不是一个真正的特性,但它让我感到好笑,goto 是一个保留字,除了提示javac戳你的眼睛外,什么都不做。只是为了提醒你现在处在OO-land。


4
https://dev59.com/HHVD5IYBdhLWcg3wVKAb :-) - Nivas
10
面向对象编程和不使用“goto”之间是否有某种关系? - Adrian Pronk

14

你可以定义并调用匿名内部类上的方法。

虽然它们并不那么隐秘,但很少有人知道它们可以被用来在一个类中定义一个新的方法并像这样调用它:

(new Object() {
    public String someMethod(){ 
        return "some value";
    }
}).someMethod();

这种方法可能不太常见,因为它也不是很有用,你只能在定义它时(或通过反射)调用该方法一次


看起来有点像JavaScript模块化模式 ;) - Johannes Wachter

14

strictfp关键字。(虽然我从未在实际应用中看到过它的使用:))

您可以使用以下表示法获取基本类型的类:int.class,float.class等。在进行反射时非常有用。

最终数组可用于从匿名内部类“返回”值(警告,下面是无用的示例):

final boolean[] result = new boolean[1];
SwingUtilities.invokeAndWait(new Runnable() {
  public void run() { result[0] = true; }
});

使用一个 final 数组作为匿名内部类的返回值可能不被推荐为良好的编程实践... - Chii
Romain Guy?就是那个著名的 Romain Guy 吗?不管怎样,int.class 确实很棒。我之前还以为 Integer.TYPE 是唯一的方法呢。 - Michael Myers
毫无用处。紧接着这段代码的部分很可能会在EDT回调之前被执行,因此它看不到真正的值。 - Tom Hawtin - tackline
2
我自己使用过 strictfp。这是为了一个程序,其中 double 类型在寄存器(80 位)和内存(64 位)之间的潜在移动可能会引起问题。 - Chinmay Kanchi
1
对于 invokeAndWait,这比 invokeLater 更有用。 - Paŭlo Ebermann

13

C风格的 printf() :)

System.out.printf("%d %f %.4f", 3,Math.E,Math.E);

输出: 3 2.718282 2.7183

二分查找(及其返回值)

int[] q = new int[] { 1,3,4,5};
int position = Arrays.binarySearch(q, 2);

类似于C#,如果在数组中找不到'2',则返回负值,但是如果对返回的值进行1's补码,实际上可以获得插入'2'的位置。

在上面的例子中,位置= -2,~位置=1,这是应插入2的位置...它还可以让您在数组中找到“最接近”的匹配项。

我认为这相当巧妙... :)


printf不是隐藏的,binarySearch的工作也不是。 - user85421
之前的回答中没有一个功能是真正“隐藏”的。它们中的大多数只是对于普通的Java程序员来说相对较为陌生。至少我认为这就是问题的意思... - st0le

13

我知道Java 6包含脚本支持,但最近才发现jrunscript,它可以交互式地解释和运行JavaScript(以及其他脚本语言,如Groovy),有点像Python shell或Ruby中的irb。


12

既是特性,又是麻烦:Java中的字符串处理使其“看起来”像原生类型(对其使用操作符+、+=)

能够编写以下代码:

String s = "A";
s += " String"; // so s == "A String"

很方便,但只是语法糖(即被编译成):

String s = new String("A");
s = new StringBuffer(s).append(" String").toString();

因此,对于简单的字符串连接而言,需要进行一个对象实例化和两个方法调用。想象一下在循环中以这种方式构建一个长字符串!?并且所有StringBuffer的方法都被声明为同步的。值得庆幸的是,在(我认为)Java 5中引入了StringBuilder,它与StringBuffer相同,但不具备同步功能。

例如以下循环:

String s = "";
for (int i = 0 ; i < 1000 ; ++i)
  s += " " + i; // Really an Object instantiation & 3 method invocations!

可以(应该)在您的代码中重写为:

StringBuilder buf = new StringBuilder(); // Empty buffer
for (int i = 0 ; i < 1000 ; ++i)
  buf.append(' ').append(i); // Cut out the object instantiation & reduce to 2 method invocations
String s = buf.toString();

而且将比原始循环运行速度快大约80%+!(在我运行的某些基准测试中高达180%)


2
字面值 "A" 真正的类型是 java.lang.String,尽管它的后备字符数组以不同于动态创建字符串的方式进行分配。 - Donal Fellows
我知道这个并且已经测试过了。这是因为在任何面向对象的编程语言中,创建和销毁对象是最昂贵的事情之一。我曾经听说过... - John John Pichler
1
当前的编译器在字符串拼接时会使用 StringBuilder 而不是 StringBuffer。 - Paŭlo Ebermann

12

反射虽然不算是秘密技术,但其功能极为强大实用。利用简单的Class.forName("...").newInstance()语句创建可配置的类类型非常方便,编写此类工厂实现也十分简单。


1
我经常使用反射来完成像 <T> T[] filterItems(T[]) 这样的事情,然后你可以用 items = filterItems(items); 来调用它。方法定义有点丑陋,但它确实使客户端代码更易于阅读。 - Marcus Downing
它非常强大,因为它允许您打破Java、javac和接口提供的每个静态保证和不变量。请极度小心使用。 - Raphael

12

我知道这个功能是在1.5版中添加的,但新的枚举类型是一个很棒的功能。不必再使用旧的“int枚举模式”已经极大地帮助了我的一堆代码。查看JLS 8.9可了解更多信息。


大多数“老派”的Java程序员从不费心去开始使用这个功能,但我认为它非常棒。 - Allain Lalonde

11

1
在大多数编译器中,美元符号$也用于区分内部类和封闭类。 - Dave Jarvis
7
你还可以在变量名中使用英镑符号(£)和欧元符号(€),以及任何UNICODE字母如æ、ø、å等。 - Andrey Adamovich

11

实例变量的final:

对于多线程代码非常有用,使得实例状态和正确性更易于讨论。在工业环境中很少见到它,并且通常没有在Java类中思考。


static {something;}:

用于初始化静态成员(我更喜欢使用一个具有名称的静态方法来进行初始化)。没有被广泛应用。


是的,静态方法也可以让您很好地处理异常,但当您使用静态块时,您别无选择,只能捕获异常(而不能执行任何适当的操作)。 - Graham Edgecombe

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