Java的隐藏特性

Hidden Features of Java

在阅读了C.I的隐藏特性之后,我想知道,Java的一些隐藏的特性是什么?


几个月前,当我第一次发现它时,双支撑初始化让我感到惊讶,以前从未听说过它。

线程局部变量通常不被广泛地称为存储每个线程状态的方法。

由于JDK 1.5 Java已经有了非常好的实现和健壮的并发工具,它们不仅仅是锁,它们还存在于JavaUTIL并发中,一个特别有趣的例子是JavaUTI.CONCURNET.Audio子包,它包含实现比较和交换操作的线程安全原语,并且可以映射到实际的本地硬件支持。这些操作的ED版本。


类型参数方差中的联合:

1
public class Baz<T extends Foo & Bar> {}

例如,如果您希望采用一个既具有可比性又具有集合的参数:

1
2
3
4
public static <A, B extends Collection<A> & Comparable>
boolean foo(B b1, B b2, A a) {
   return (b1.compareTo(b2) == 0) || b1.contains(a) || b2.contains(a);
}

如果两个给定集合相等或其中一个集合包含给定元素,则此人为方法返回true,否则返回false。需要注意的是,您可以在参数b1和b2上调用Comparable和Collection方法。


前几天我对实例初始值设定项感到惊讶。我删除了一些代码折叠的方法,最终创建了多个实例初始值设定项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class App {
    public App(String name) { System.out.println(name +"'s constructor called"); }

    static { System.out.println("static initializer called"); }

    { System.out.println("instance initializer called"); }

    static { System.out.println("static initializer2 called"); }

    { System.out.println("instance initializer2 called"); }

    public static void main( String[] args ) {
        new App("one");
        new App("two");
  }
}

执行main方法将显示:

1
2
3
4
5
6
7
8
static initializer called
static initializer2 called
instance initializer called
instance initializer2 called
one's constructor called
instance initializer called
instance initializer2 called
two'
s constructor called

如果您有多个构造函数并且需要公共代码,我想这些方法会很有用

它们还提供了初始化类的语法框架:

1
2
3
4
5
6
List<Integer> numbers = new ArrayList<Integer>(){{ add(1); add(2); }};

Map<String,String> codes = new HashMap<String,String>(){{
  put("1","one");
  put("2","two");
}};


jdk 1.6_07+包含一个名为visualvm(bin/jvisualvm.exe)的应用程序,它是许多工具之上的一个不错的GUI。它似乎比JConsole更全面。


Java 6以来的类路径通配符。

1
java -classpath ./lib/* so.Main

而不是

1
java -classpath ./lib/log4j.jar:./lib/commons-codec.jar:./lib/commons-httpclient.jar:./lib/commons-collections.jar:./lib/myApp.jar so.Main

请参阅http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html


对于大多数我采访的JavaDeaveDebug标记的块来说,这是非常令人惊讶的。下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
// code goes here

getmeout:{
    for (int i = 0; i < N; ++i) {
        for (int j = i; j < N; ++j) {
            for (int k = j; k < N; ++k) {
                //do something here
                break getmeout;
            }
        }
    }
}

谁说Java中的EDOCX1 0是一个关键字?:)


自JDK1.5以来就存在的协变返回类型如何?这是一个相当不公开的,因为它是一个不受欢迎的添加,但据我所知,它对于泛型的工作是绝对必要的。

实际上,编译器现在允许子类将重写方法的返回类型缩小为原始方法返回类型的子类。所以这是允许的:

1
2
3
4
5
6
7
8
9
10
11
12
class Souper {
    Collection<String> values() {
        ...
    }
}

class ThreadSafeSortedSub extends Souper {
    @Override
    ConcurrentSkipListSet<String> values() {
        ...
    }
}

您可以调用子类的values方法,获得String的已排序的线程安全Set,而不必向下强制转换为ConcurrentSkipListSet


在finally块中传输控制权会丢弃任何异常。以下代码不会引发RuntimeException——它会丢失。

1
2
3
4
5
6
7
8
9
public static void doSomething() {
    try {
      //Normally you would have code that doesn't explicitly appear
      //to throw exceptions so it would be harder to see the problem.
      throw new RuntimeException();
    } finally {
      return;
    }
  }

来自http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html


还没有看到任何人提到实例的实现方式不需要检查空值。

而不是:

1
2
3
4
if( null != aObject && aObject instanceof String )
{
    ...
}

只需使用:

1
2
3
4
if( aObject instanceof String )
{
    ...
}


在枚举中允许方法和构造函数让我吃惊。例如:

1
2
3
4
5
6
7
8
9
10
11
enum Cats {
  FELIX(2), SHEEBA(3), RUFUS(7);

  private int mAge;
  Cats(int age) {
    mAge = age;
  }
  public int getAge() {
    return mAge;
   }
}

甚至可以有一个"常量特定的类体",它允许特定的枚举值重写方法。

此处提供更多文档。


可以像这样显式地指定泛型方法的类型参数:

1
Collections.<String,Integer>emptyMap()


可以使用枚举来实现接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface Room {
   public Room north();
   public Room south();
   public Room east();
   public Room west();
}

public enum Rooms implements Room {
   FIRST {
      public Room north() {
         return SECOND;
      }
   },
   SECOND {
      public Room south() {
         return FIRST;
      }
   }

   public Room north() { return null; }
   public Room south() { return null; }
   public Room east() { return null; }
   public Room west() { return null; }
}

编辑:几年后……

我在这里使用这个功能

1
public enum AffinityStrategies implements AffinityStrategy {

https://github.com/peter-lawrey/java-thread-affinity/blob/master/src/main/java/vanilla/java/affinity/affinitystrategies.java

通过使用接口,开发人员可以定义自己的策略。使用enum意味着我可以定义一个(五个)内置的集合。


至于Java 1.5,Java现在有了更为简洁的语法来编写变量的函数。因此,现在您可以执行以下操作,而不只是传递一个数组

1
2
3
4
public void foo(String... bars) {
   for (String bar: bars)
      System.out.println(bar);
}

条形图将自动转换为指定类型的数组。虽然不是一场巨大的胜利,但仍然是一场胜利。


我最喜欢的:将所有线程堆栈跟踪转储到标准输出。

Windows:CCTL RAB在Java CMD/控制台窗口中

Unix:kill -3 PID


一些人已经发布了关于实例初始值设定项的内容,下面是一个很好的用途:

1
2
3
4
Map map = new HashMap() {{
    put("a key","a value");
    put("another key","another value");
}};

是一种快速初始化地图的方法,如果你只是做一些简单快捷的事情。

或者使用它创建一个快速摆动框架原型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
JFrame frame = new JFrame();

JPanel panel = new JPanel();

panel.add( new JLabel("Hey there"){{
    setBackground(Color.black);
    setForeground( Color.white);
}});

panel.add( new JButton("Ok"){{
    addActionListener( new ActionListener(){
        public void actionPerformed( ActionEvent ae ){
            System.out.println("Button pushed");
        }
     });
 }});


 frame.add( panel );

当然可以滥用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    JFrame frame = new JFrame(){{
         add( new JPanel(){{
               add( new JLabel("Hey there"){{
                    setBackground(Color.black);
                    setForeground( Color.white);
                }});

                add( new JButton("Ok"){{
                    addActionListener( new ActionListener(){
                        public void actionPerformed( ActionEvent ae ){
                            System.out.println("Button pushed");
                        }
                     });
                 }});
        }});
    }};


动态代理(添加在1.3中)允许您在运行时定义符合接口的新类型。它的使用次数惊人。


可以推迟最终初始化。

它确保即使在逻辑返回值的复杂流中也始终设置。很容易漏掉一个案子,不小心就归零。这并不意味着返回空值是不可能的,只是很明显它是故意的:

1
2
3
4
5
6
7
8
9
10
11
public Object getElementAt(int index) {
    final Object element;
    if (index == 0) {
         element ="Result 1";
    } else if (index == 1) {
         element ="Result 2";
    } else {
         element ="Result 3";
    }
    return element;
}


我认为Java的另一个"被忽视"的特性是JVM本身。它可能是可用的最佳虚拟机。它支持许多有趣和有用的语言(Jython、JRuby、scala、groovy)。所有这些语言都可以轻松无缝地协作。

如果您设计了一种新的语言(如scala中的情况),那么您将立即拥有所有现有的库,因此从一开始您的语言就是"有用的"。

所有这些语言都使用热点优化。虚拟机是非常好的监控和调试。


您可以定义一个匿名子类并直接调用它的方法,即使它不实现任何接口。

1
2
3
4
5
new Object() {
  void foo(String s) {
    System.out.println(s);
  }
}.foo("Hello");


java.util.Arrays中的aslist方法允许varargs、泛型方法和autoboxing的良好组合:

1
List<Integer> ints = Arrays.asList(1,2,3);


使用此关键字从内部类访问包含类的字段/方法。在下面这个人为的例子中,我们希望使用匿名内部类中容器类的sortscending字段。使用containerClass.this.SortAscending而不是this.SortAscending可以做到这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Comparator;

public class ContainerClass {
boolean sortAscending;
public Comparator createComparator(final boolean sortAscending){
    Comparator comparator = new Comparator<Integer>() {

        public int compare(Integer o1, Integer o2) {
            if (sortAscending || ContainerClass.this.sortAscending) {
                return o1 - o2;
            } else {
                return o2 - o1;
            }
        }

    };
    return comparator;
}
}


不是一个功能,而是我最近在一些网页上发现的一个有趣的技巧:

1
2
3
4
5
6
7
8
9
10
class Example
{
  public static void main(String[] args)
  {
    System.out.println("Hello World!");
    http://Phi.Lho.free.fr

    System.exit(0);
  }
}

是一个有效的Java程序(虽然它生成警告)。如果你不明白原因,看看格雷戈里的回答!;-)好吧,这里突出显示的语法也会给出提示!


这不完全是"隐藏的特性",也不是非常有用,但在某些情况下可能非常有趣:类Sun.MISCUnStabor将允许你在Java中实现直接内存管理(如果你尝试了很多,你甚至可以用它编写自修改Java代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UnsafeUtil {

    public static Unsafe unsafe;
    private static long fieldOffset;
    private static UnsafeUtil instance = new UnsafeUtil();

    private Object obj;

    static {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);

            unsafe = (Unsafe)f.get(null);
            fieldOffset = unsafe.objectFieldOffset(UnsafeUtil.class.getDeclaredField("obj"));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}


在Swing中工作时,我喜欢隐藏的ctrl-shift-f1功能。

它转储当前窗口的组件树。(假设您没有将该击键绑定到其他对象上。)


每个类文件都以十六进制值0xcafebabe开头,以将其标识为有效的JVM字节码。

(解释)


我投票给java.util.concurrent,它的并发集合和灵活的执行器允许线程池、计划的任务和协调的任务。DelayQueue是我个人的最爱,在这里元素在指定的延迟之后可用。

java.util.timer和timertask可以安全地停止运行。

而且,不是完全隐藏,而是在与日期和时间相关的其他类的不同包中。在纳秒、微秒、毫秒和秒之间转换时,java.util.concurrent.timeUnit非常有用。

它读起来比通常的somevalue*1000或somevalue/1000好很多。


语言级别断言关键字。


不是Java语言的一部分,但是与Sun的JDK一起使用的JavAP反汇编程序并不是广为人知或使用的。


在1.5中添加for each循环结构。我<3。

1
2
3
4
// For each Object, instantiated as foo, in myCollection
for(Object foo: myCollection) {
  System.out.println(foo.toString());
}

并且可以在嵌套实例中使用:

1
2
3
for (Suit suit : suits)
  for (Rank rank : ranks)
    sortedDeck.add(new Card(suit, rank));

for-each构造也适用于数组,其中它隐藏索引变量而不是迭代器。以下方法返回int数组中的值之和:

1
2
3
4
5
6
7
// Returns the sum of the elements of a
int sum(int[] a) {
  int result = 0;
  for (int i : a)
    result += i;
  return result;
}

链接到Sun文档


我个人很晚才发现java.lang.Void--与泛型(如Callable)一起提高代码可读性。


也许最令人惊讶的隐藏特性是sun.misc.unsafe类。

http://www.docjar.com/html/api/classlib/common/sun/misc/unsafe.java.html

你可以;

  • 在不调用构造函数的情况下创建对象。
  • 在不担心方法上的throws子句的情况下抛出任何异常甚至异常。(我知道还有其他方法可以做到这一点)
  • 获取/设置对象中随机访问的字段,而不使用反射。
  • 分配/释放/复制/调整内存块的大小(64位)。
  • 获取对象中字段的位置或类中静态字段的位置。
  • 独立锁定和解锁对象锁定。(就像没有块的同步)
  • 从提供的字节代码定义类。而不是类加载器来确定字节代码应该是什么。(你也可以通过反射来做到这一点)

顺便说一句:不正确地使用这个类会杀死JVM。我不知道哪个JVM支持这个类,所以它不可移植。


这是我的清单。

我最喜欢(也是最可怕)的隐藏特性是,您可以从不声明抛出任何内容的方法中抛出选中的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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!"));
    }
}

你也可能想知道你可以扔"空"…

1
2
3
public static void main(String[] args) {
     throw null;
}

猜猜这是什么照片:

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

然后,猜猜这会带来什么:

1
2
3
4
5
6
7
public int returnSomething() {
    try {
        throw new RuntimeException("foo!");
    } finally {
        return 0;
    }
}

上面的内容不应该让优秀的开发人员感到惊讶。

在爪哇中,可以以有效的方式声明数组:

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

因此遵循Java代码是完全有效的:

1
2
3
4
5
6
7
8
public class Foo {
    public void doSomething(String[] arg) {}

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

以下代码不应该是有效的,有什么正当理由吗?

1
2
3
4
5
6
7
8
public class Foo {

    public void doSomething(String[] arg) {}

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

我认为,上述语法将是对Java 5中引入的ValARGS的有效替代。并且,更符合先前允许的数组声明。


关闭挂钩。这允许注册一个线程,该线程将立即创建,但仅在JVM结束时启动!所以它是一种"全局JVM终结器",您可以在这个线程中提供有用的东西(例如,关闭像嵌入式HSQL LDB服务器那样的Java资源)。这可以与system.exit()或ctrl-c/kill-15一起使用(当然,在Unix上不能与kill-9一起使用)。

而且很容易设置。

1
2
3
4
5
            Runtime.getRuntime().addShutdownHook(new Thread() {
                  public void run() {
                      endApp();
                  }
            });;


价值:

1
new URL("http://www.yahoo.com").equals(new URL("http://209.191.93.52"))

true

(来自Java拼图器)


如果您进行了大量的JavaBean开发并使用了属性更改支持,那么您通常会编写许多这样的setter:

1
2
3
4
5
public void setFoo(Foo aFoo){
  Foo old = this.foo;
  this.foo = aFoo;
  changeSupport.firePropertyChange("foo", old, aFoo);
}

我最近偶然发现了一个博客,它建议更简洁地实现这一点,从而使代码更容易编写:

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

它实际上简化了一些事情,使我能够在Eclipse中调整setter模板,从而自动创建方法。


静态导入以"增强"语言,因此可以以类型安全的方式执行好的文字操作:

1
List<String> ls = List("a","b","c");

(也可以使用地图、数组和集合)。

http://gleichmann.wordpress.com/2008/01/13/building-your-own-literals-in-java-lists-and-array/

更进一步:

1
List<Map<String, String>> data = List(Map( o("name","michael"), o("sex","male")));


如果不使用默认的初始化器,Java处理在变量定义上会做一个巧妙的把戏。

1
2
3
4
5
6
7
8
9
{
   int x;

   if(whatever)
      x=1;

   if(x == 1)
      ...
}

这将在编译时给您一个错误,您有一个x未正确定义的路径。这对我有几次帮助,我已经开始考虑这样的默认初始化:

1
2
int x=0;
String s=null;

因为它阻止了这种有用的检查,所以它是一个坏模式。

这就是说,有时很难绕过——我不得不回去在默认情况下在=null中进行编辑,但我再也没有在第一次通过时输入它。


作为初学者,我非常欣赏Java 6中的JTAG监控软件,它已经解决了我的一些问题,我一直在寻找新的用途。

显然,J控制台已经在Java 5中出现了,但我认为它现在得到了改进,至少现在工作得更加稳定。

Java 5中的J控制台:Java 5中的J控制台

Java 6中的J控制台:Java 6中的J控制台

在您学习的同时,请仔细阅读本系列中的其他工具:Java 6故障排除工具


不是很隐蔽,但很有趣。

你可以在没有主方法的情况下拥有一个"你好,世界"(它抛出了nosuchmethoderror思想)

最初由Russelw发布在最奇怪的语言特性上

1
2
3
4
5
6
7
8
9
public class WithoutMain {
    static {
        System.out.println("Look ma, no main!!");
        System.exit(0);
    }
}

$ java WithoutMain
Look ma, no main!!


这并不是一个隐藏的特性,但当我看到这个编译好的特性时,它确实给了我一个很大的惊喜:

1
2
3
4
public int aMethod(){
    http://www.google.com
    return 1;
}

它编译的原因是,行http://www.google.com"http:"部分被编译器视为标签,行的其余部分是注释。

因此,如果您想编写一些bizzare代码(或模糊代码),只需在那里放置大量的HTTP地址。;-)


可以在方法中声明类:

1
2
3
4
5
6
7
public Foo foo(String in) {
    class FooFormat extends Format {
        public Object parse(String s, ParsePosition pp) { // parse stuff }
    }
    return (Foo) new FooFormat().parse(in);

}


他们花了足够长的时间来支持这一点,

系统托盘


我非常喜欢从Java 1.6重写线程API。打电话很好。它们基本上是带有返回值的线程。


自绑定泛型:

1
2
class SelfBounded<T extends SelfBounded<T>> {
}

http://www.artima.com/weblogs/viewpost.jsp?线程=136394


哦,我差点忘了这个小宝石。在任何运行的Java进程上尝试:

jmap-历史:实时PID

您将得到给定虚拟机中活动堆对象的柱状图。作为一种快速计算某些类型内存泄漏的方法,它是非常宝贵的。我用来防止它们的另一种技术是创建和使用所有集合类的大小绑定子类。这会导致失控集合中很容易识别的快速失败。


list.sublist返回原始列表上的视图

列表的一个记录在案但鲜为人知的特性。这允许您使用列表中包含原始列表中镜像更改的部分。

列表子列表(int-fromindex,int-toindex)

"This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:

1
       list.subList(from, to).clear();

Similar idioms may be constructed for indexOf and lastIndexOf, and all of the algorithms in the Collections class can be applied to a subList."


我喜欢方法的静态导入。

例如,创建以下util类:

1
2
3
4
5
6
7
8
9
10
11
12
13
package package.name;

public class util {

     private static void doStuff1(){
        //the end
     }

     private static String doStuff2(){
        return"the end";
     }

}

然后像这样使用。

1
2
3
4
5
6
7
8
9
10
import static package.name.util.*;

public class main{

     public static void main(String[] args){
          doStuff1(); // wee no more typing util.doStuff1()
          System.out.print(doStuff2()); // or util.doStuff2()
     }

}

静态导入适用于任何类,甚至数学…

1
2
3
4
5
6
7
8
9
10
import static java.lang.Math.*;
import static java.lang.System.out;
public class HelloWorld {
    public static void main(String[] args) {
        out.println("Hello World!");
        out.println("Considering a circle with a diameter of 5 cm, it has:");
        out.println("A circumference of" + (PI * 5) +"cm");
        out.println("And an area of" + (PI * pow(5,2)) +"sq. cm");
    }
}


一个可以为Java控制台应用程序显示飞溅屏幕的特性。

使用命令行工具javajavaw和选项-splash一起使用。

如:

1
java -splash:C:\myfolder\myimage.png -classpath myjarfile.jar com.my.package.MyClass

每当执行"com.my.package.my class"类时,C:\myfolder\myimage.png的内容将显示在屏幕的中心。


通过静态导入,您可以做一些很酷的事情,比如:

1
2
3
List<String> myList = list("foo","bar");
Set<String> mySet = set("foo","bar");
Map<String, String> myMap = map(v("foo","2"), v("bar","3"));


这不是一个真正的特性,但它让我笑了,因为goto是一个保留字,除了提示javac戳你的眼睛之外,它什么也不做。只是提醒你,你现在在OO土地上。


JavaDoc——如果编写得当(不幸的是,有些开发人员并不总是这样),它会给您一个清晰、一致的描述,说明代码应该做什么,而不是它实际做什么。然后可以将其转换为一组很好的可浏览HTML文档。如果您使用持续集成等,它可以定期生成,这样所有开发人员都可以看到最新的更新。


可以在匿名内部类上定义和调用方法。

好吧,它们没有那么隐藏,但是很少有人知道它们可以用于在类中定义一个新方法并像这样调用它:

1
2
3
4
5
(new Object() {
    public String someMethod(){
        return"some value";
    }
}).someMethod();

可能不是很常见,因为它也不是很有用,您只能在定义方法(或通过反射)时调用它。


strictfp关键字。(但我从未见过它在实际应用中使用过:)

您可以使用以下符号来获取基元类型的类:int.class,float.class等在进行反射时非常有用。

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

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


c-style printf():)

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

输出:3 2.718282 2.7183

二进制搜索(它的返回值)

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

与c类似,如果在数组中找不到"2",它将返回一个负值,但如果您对返回的值取1的补数,则实际上会得到可以插入"2"的位置。

在上面的示例中,position=-2,~ position=1是应该插入2的位置……它还允许您在数组中找到"最近"的匹配项。

我觉得很漂亮…:)


我知道Java 6包含脚本支持,但我最近才发现JRunScript,它可以交互地解释和运行javascript(其中一种假设是其他脚本语言,如groovy),有点像Ruby中的python shell或irb。


部分特性,部分麻烦:Java的字符串处理使它成为"本地"类型(使用它们上的运算符,+,+ =)

能够写:

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

是非常方便,但只是简单的句法糖分(即编译为):

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

遍历一个对象实例化和两个方法调用以实现简单的连接。想象一下这样在一个循环中构建一个长字符串!?并且所有StringBuffer的方法都声明为已同步。谢天谢地,在(我认为)Java 5中,他们引入了与StringBuffer没有相同同步化的StringBuilder。

一个循环,如:

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

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

1
2
3
4
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%)


我知道这是在1.5版中添加的,但是新的枚举类型是一个很好的特性。不必使用旧的"int-enum模式"对我的代码有很大的帮助。查看JLS 8.9,了解土豆上的甜肉汁!


它并不完全隐藏,但是反射是非常有用和强大的。使用一个简单的class.forname("…".newInstance()类类型是可配置的,这很好。编写这种工厂实现很容易。


今天我刚刚了解到,$是Java中的方法或变量的合法名称。与静态导入相结合,它可以使代码的可读性略高,这取决于您对可读性的看法:

http://garbagecollected.org/2008/04/06/dollamaps/


最终变量:

对于多线程代码非常有用,它使讨论实例状态和正确性变得容易得多。在工业环境中没有见过它,而且在Java类中经常没有想到。

静态某物;:

用于初始化静态成员(另外,我更喜欢使用静态方法进行初始化(因为它有一个名称)。没有想到。


"const"是一个关键字,但您不能使用它。

1
2
int const = 1;   //"not a statement"
const int i = 1; //"illegal start of expression"

我想编译器的作者认为将来可能会用到它,他们最好保留它。


这是一个优化技巧,使代码更容易维护,也不容易受到并发性错误的影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Slow {
  /** Loop counter; initialized to 0. */
  private long i;

  public static void main( String args[] ) {
    Slow slow = new Slow();

    slow.run();
  }

  private void run() {
    while( i++ < 10000000000L )
      ;
  }
}

$JAVA慢< BR/>实0.15.397秒
$JAVA慢< BR/>实0.20.012s
$JAVA慢< BR/>实0 m18.645

平均:18.018s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Fast {
  /** Loop counter; initialized to 0. */
  private long i;

  public static void main( String args[] ) {
    Fast fast = new Fast();

    fast.run();
  }

  private void run() {
    long i = getI();

    while( i++ < 10000000000L )
      ;

    setI( i );
  }

  private long setI( long i ) {
    this.i = i;
  }

  private long getI() {
    return this.i;
  }
}

$JAVA快< BR/>实0m12.003s
$JAVA快< BR/>实0米9.840秒
$JAVA快< BR/>实0m9.686s

平均:10.509s

与方法范围变量相比,它需要更多的字节码来引用类范围变量。在关键循环之前添加方法调用只会增加很少的开销(而且编译器可能会内联调用)。

这种技术(总是使用访问器)的另一个优点是它消除了slow类中的潜在bug。如果第二个线程要连续地将i的值重置为0(例如,通过调用slow.setI( 0 )),则slow类永远不会结束其循环。调用访问器并使用局部变量可以消除这种可能性。

在Linux2.6.27-14上使用J2SE1.6.0_进行测试。


当不需要StringBuilder中包含的同步管理时,使用StringBuilder而不是StringBuffer。它将提高应用程序的性能。

对于Java 7的改进甚至比任何隐藏的Java特性更好:

  • 菱形语法:链接

声明时不要使用这些无限<>语法:

1
2
3
4
5
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();

// Can now be replaced with this:

Map<String, List<String>> anagrams = new HashMap<>();
  • 开关中的字符串:链接

在switch中使用string,而不是old-c int:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String s ="something";
switch(s) {
 case"quux":
    processQuux(s);
    // fall-through

  case"foo":
  case"bar":
    processFooOrBar(s);
    break;

  case"baz":
     processBaz(s);
    // fall-through

  default:
    processDefault(s);
    break;
}
  • 自动资源管理链接

这个旧代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void copy(String src, String dest) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dest);
        try {
            byte[] buf = new byte[8 * 1024];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

现在可以用这个更简单的代码替换:

1
2
3
4
5
6
7
8
9
static void copy(String src, String dest) throws IOException {
    try (InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dest)) {
        byte[] buf = new byte[8192];
        int n;
        while ((n = in.read(buf)) >= 0)
            out.write(buf, 0, n);
    }
}


您选择的编码中的属性文件如何?以前,当您加载属性时,提供了一个输入流,load()方法将其解码为ISO-8859-1。实际上,您可以使用其他编码来存储文件,但在加载后,您必须使用类似这样令人讨厌的黑客来正确解码数据:

1
String realProp = new String(prop.getBytes("ISO-8859-1"),"UTF-8");

但是,从JDK 1.6开始,有一个load()方法采用的是读卡器而不是输入流,这意味着从一开始就可以使用正确的编码(还有一个store()方法采用的是编写器)。这对我来说似乎是个大问题,但它似乎是偷偷溜进了JDK,根本没有吹嘘。几周前我才偶然发现了它,一次快速的谷歌搜索仅仅是一次偶然的提及。


真正让我吃惊的是定制的序列化机制。

虽然这些方法是私有的!!,它们在对象序列化期间被JVM"神秘地"调用。

1
2
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

这样,您就可以创建自己的自定义序列化,使其更"随意"(安全、快速、罕见、简单等)。

如果必须通过节点传递大量信息,那么这确实应该考虑。可以更改序列化机制以发送一半的数据。很多时候,瓶颈不在平台上,但在通过网络发送的数据量上,可以节省上千个硬件DLL。

这是一篇文章。http://java.sun.com/developer/technicalArticles/programming/serialization/


标识符可以包含诸如umlauts之类的外语字符:

不是写:

1
String title="";

有人可以写:

1
String überschrift="";


我可以添加扫描仪对象。它是最好的解析方法。

1
2
3
4
5
6
7
String input ="1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*");
System.out.println(s.nextInt());
System.out.println(s.nextInt());
System.out.println(s.next());
System.out.println(s.next());
s.close();


当人们意识到有可能使用反射调用私有方法和访问/更改私有字段时,有时会有点惊讶…

考虑以下类别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Foo {
    private int bar;

    public Foo() {
        setBar(17);
    }

    private void setBar(int bar) {
        this.bar=bar;
    }

    public int getBar() {
        return bar;
    }

    public String toString() {
        return"Foo[bar="+bar+"]";
    }
}

正在执行此程序…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.*;

public class AccessibleExample {
    public static void main(String[] args)
        throws NoSuchMethodException,IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        Foo foo=new Foo();
        System.out.println(foo);

        Method method=Foo.class.getDeclaredMethod("setBar", int.class);
        method.setAccessible(true);
        method.invoke(foo, 42);

        System.out.println(foo);
        Field field=Foo.class.getDeclaredField("bar");
        field.setAccessible(true);
        field.set(foo, 23);
        System.out.println(foo);
    }
}

…将产生以下输出:

1
2
3
Foo[bar=17]
Foo[bar=42]
Foo[bar=23]


大多数人不知道他们可以克隆一个数组。

1
2
int[] arr = {1, 2, 3};
int[] arr2 = arr.clone();


来自Java 6的注释处理API看起来非常适合代码生成和静态代码验证。


可以访问初始化块和本地类方法中的最终局部变量和参数。考虑一下:

1
2
3
4
5
6
    final String foo ="42";
    new Thread() {
        public void run() {
             dowhatever(foo);
        }
    }.start();

有点像闭幕式,不是吗?


实际上,我喜欢Java的是隐藏的技巧。这是一种非常明显的语言。如此之多以至于15年后,我能想到的几乎每一个都列在这几页上。

也许大多数人都知道collections.synchronizedList()将同步添加到列表中。除非您阅读文档,否则您不知道的是,通过在列表对象本身上进行同步,您可以安全地迭代该列表的元素。

CopyOnWriteArrayList可能对某些人来说是未知的,而Future表示一种抽象多线程结果访问的有趣方法。

您可以通过各种管理、代理和附加API附加到vms(本地或远程)、获取有关gc活动、内存使用、文件描述符甚至对象大小的信息。

虽然TimeUnit可能比Long好,但我更喜欢Wicket的Duration类。


可以使用string.format()构建string sprintf样式。

1
2
String w ="world";
String s = String.format("Hello %s %d", w, 3);

当然,您也可以使用特殊的说明符来修改输出。

这里有:http://java.xun.com /j2s/1.5.0/DOCS/API/Java/UTL/Frastal.html语法


jdk发行版中bin目录中的jvisalvm。监视甚至剖析任何Java应用程序,即使是一个没有用任何特殊参数启动的应用程序。仅在Java6SE JDK的最新版本中。


您对垃圾收集器的控制能力以及它如何管理对象收集非常强大,特别是对于长时间运行和时间敏感的应用程序。它从java.lang.ref包中的弱引用、软引用和幻影引用开始。看看这些,尤其是构建缓存(已经有了java.util.weakhashmap)。现在,在引用队列中再深入一点,您将开始拥有更多的控制权。最后,抓取垃圾收集器本身上的文档,您就可以控制它运行的频率、不同集合区域的大小以及所使用的算法类型(对于Java 5,参见http://java.xun.com /DOCS/Heave/GC5.0/GCcTungEng5.5html)。


Joshua Bloch的新的有效Java是一个很好的资源。


同一类的实例可以访问其他实例的私有成员:

1
2
3
4
5
6
7
class Thing {
  private int x;

  public int addThings(Thing t2) {
    return this.x + t2.x;  // Can access t2's private value!
  }
}


一些控制流技巧,finally围绕return声明:

1
2
3
4
int getCount() {
  try { return 1; }
  finally { System.out.println("Bye!"); }
}

确定分配规则将检查是否始终通过简单的控制流分析分配最终变量:

1
2
3
4
5
6
final int foo;
if(...)
  foo = 1;
else
  throw new Exception();
foo+1;

没读过这个

1
2
3
4
5
6
7
8
9
10
11
Integer a = 1;
Integer b = 1;
Integer c = new Integer(1);
Integer d = new Integer(1);

Integer e = 128;
Integer f = 128;

assertTrue (a == b);   // again: this is true!
assertFalse(e == f); // again: this is false!
assertFalse(c == d);   // again: this is false!

通过搜索Java的整数池(内部缓存)从128到127进行自动装箱,或者查看整数。


源代码URL。例如,这里有一些合法的Java源代码:

1
http://google.com

(是的,它是在Java拼图中。我笑了……


字符串参数化类工厂。

1
Class.forName( className ).newInstance();

从部署JAR文件加载资源(属性文件、XML、XSLT、图像等)。

1
this.getClass().getClassLoader().getResourceAsStream( ... ) ;

Java 6(来自Sun)附带了一个嵌入式JavaScript解释器。

http://java.xun.com /javas/6 /DOCS/TeNeNeks/Cudio/Script/CudithPrave/index。


交集类型允许您(有点像sorta)执行具有继承层次结构的枚举。不能继承实现,但可以将其委托给助手类。

1
2
3
4
5
6
enum Foo1 implements Bar {}
enum Foo2 implements Bar {}

class HelperClass {
   static <T extends Enum<T> & Bar> void fooBar(T the enum) {}
}

当有许多不同的枚举实现某种模式时,这很有用。例如,一些具有父子关系的枚举对。

1
2
3
4
5
enum PrimaryColor {Red, Green, Blue;}
enum PastelColor {Pink, HotPink, Rockmelon, SkyBlue, BabyBlue;}

enum TransportMedium {Land, Sea, Air;}
enum Vehicle {Car, Truck, BigBoat, LittleBoat, JetFighter, HotAirBaloon;}

您可以编写表示"好的,给定一个枚举值(它是某些其他枚举值的父项)的泛型方法,子类型的所有可能的子枚举中,有多少百分比将此特定的父项值作为其父项?"并确保所有类型都是安全的,并且不需要强制转换。(例如,"海"是所有可能的车辆的33%,而"绿色"是所有可能的糊状物的20%)。

代码看起来像这样。这很讨厌,但有办法让它变得更好。请注意,"leaf"类本身是非常整洁的——泛型类有非常难看的声明,但是您只编写它们onece。一旦泛型类存在,那么使用它们就很容易了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import java.util.EnumSet;

import javax.swing.JComponent;

public class zz extends JComponent {

    public static void main(String[] args) {
        System.out.println(PrimaryColor.Green +"" + ParentUtil.pctOf(PrimaryColor.Green) +"%");
        System.out.println(TransportMedium.Air +"" + ParentUtil.pctOf(TransportMedium.Air) +"%");
    }


}

class ParentUtil {
    private ParentUtil(){}
    static <P extends Enum<p>
 & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
    float pctOf(P parent) {
        return (float) parent.getChildren().size() / //
                (float) EnumSet.allOf(parent.getChildClass()).size() //
                * 100f;
    }
    public static <P extends Enum<p>
 & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
    EnumSet<C> loadChildrenOf(P p) {
        EnumSet<C> cc = EnumSet.noneOf(p.getChildClass());
        for(C c: EnumSet.allOf(p.getChildClass())) {
            if(c.getParent() == p) {
                cc.add(c);
            }
        }
        return cc;
    }
}

interface Parent<P extends Enum<p>
 & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
    Class<C> getChildClass();

    EnumSet<C> getChildren();
}

interface Child<P extends Enum<p>
 & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
    Class<p>
 getParentClass();

    P getParent();
}

enum PrimaryColor implements Parent<PrimaryColor, PastelColor> {
    Red, Green, Blue;

    private EnumSet<PastelColor>    children;

    public Class<PastelColor> getChildClass() {
        return PastelColor.class;
    }

    public EnumSet<PastelColor> getChildren() {
        if(children == null) children=ParentUtil.loadChildrenOf(this);
        return children;
    }
}

enum PastelColor implements Child<PrimaryColor, PastelColor> {
    Pink(PrimaryColor.Red), HotPink(PrimaryColor.Red), //
    Rockmelon(PrimaryColor.Green), //
    SkyBlue(PrimaryColor.Blue), BabyBlue(PrimaryColor.Blue);

    final PrimaryColor  parent;

    private PastelColor(PrimaryColor parent) {
        this.parent = parent;
    }

    public Class<PrimaryColor> getParentClass() {
        return PrimaryColor.class;
    }

    public PrimaryColor getParent() {
        return parent;
    }
}

enum TransportMedium implements Parent<TransportMedium, Vehicle> {
    Land, Sea, Air;

    private EnumSet<Vehicle>    children;

    public Class<Vehicle> getChildClass() {
        return Vehicle.class;
    }

    public EnumSet<Vehicle> getChildren() {
        if(children == null) children=ParentUtil.loadChildrenOf(this);
        return children;
    }
}

enum Vehicle implements Child<TransportMedium, Vehicle> {
    Car(TransportMedium.Land), Truck(TransportMedium.Land), //
    BigBoat(TransportMedium.Sea), LittleBoat(TransportMedium.Sea), //
    JetFighter(TransportMedium.Air), HotAirBaloon(TransportMedium.Air);

    private final TransportMedium   parent;

    private Vehicle(TransportMedium parent) {
        this.parent = parent;
    }

    public Class<TransportMedium> getParentClass() {
        return TransportMedium.class;
    }

    public TransportMedium getParent() {
        return parent;
    }
}

在Java 1.6更新10和以后发现的下一代Java插件具有一些非常整洁的特性:

  • 传递java_arguments参数以将参数传递给创建的jvm。这允许您控制给小程序的内存量。
  • 为每个小程序创建单独的类加载器,甚至是单独的JVM。
  • 指定要使用的JVM版本。
  • 在只需要完整Java库功能的子集的情况下安装部分Java内核。
  • 更好的Vista支持。
  • 支持(实验性)将小程序从浏览器中拖出,并在离开时使其保持运行。

这里还记录了许多其他内容:http://jdk6.dev.java.net/plugin2/

此版本的更多信息请访问:http://jdk6.dev.java.net/6u10ea.html


阅读Joshua Bloch的"Java拼图",你会既开悟又惊骇。


已经提到,可以使用最后一个数组从匿名内部类中传递变量。

另一种可以说是更好且不那么难看的方法是使用java.util.concurrent.atomic包中的atomicreference(或atomicboolean/atomicinteger/…)类。

这样做的好处之一是,这些类还提供了诸如compareAndSet之类的方法,如果您正在创建可以修改同一变量的多个线程,那么这些方法可能很有用。

另一个有用的相关模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
final AtomicBoolean dataMsgReceived = new AtomicBoolean(false);
final AtomicReference<Message> message = new AtomicReference<Message>();
withMessageHandler(new MessageHandler() {
    public void handleMessage(Message msg) {
         if (msg.isData()) {
             synchronized (dataMsgReceived) {
                 message.set(msg);
                 dataMsgReceived.set(true);
                 dataMsgReceived.notifyAll();
             }
         }
    }
}, new Interruptible() {
    public void run() throws InterruptedException {
        synchronized (dataMsgReceived) {
            while (!dataMsgReceived.get()) {
                dataMsgReceived.wait();
            }
        }
    }
});

在这个特定的示例中,我们可以简单地等待消息变为非空,但是空值通常是一个有效值,然后需要使用单独的标志来完成等待。

上面的waitMessageHandler(…)是另一个有用的模式:它在某个地方设置了一个处理程序,然后开始执行可能引发异常的可中断操作,然后删除finally块中的处理程序,如下所示:

1
2
3
4
5
6
7
8
9
10
11
private final AtomicReference<MessageHandler> messageHandler = new AtomicReference<MessageHandler>();
public void withMessageHandler(MessageHandler handler, Interruptible logic) throws InterruptedException {
    synchronized (messageHandler) {
        try {
            messageHandler.set(handler);
            logic.run();
        } finally {
            messageHandler.set(null);
        }
    }
}

在这里,我假设消息处理程序的(如果不是空的话)handleMessage(…)方法在收到消息时由另一个线程调用。messagehandler不能只是messagehandler类型:这样,您将在一个变化的变量上进行同步,这显然是一个错误。

当然,它不需要是InterruptedException,它可以是类似于IOException的东西,或者在特定代码段中有意义的东西。


  • 本地类。
  • 从包含类的外部实例化Java内部类。


逗号数组。它是合法语法:string s[]=
"123",
"234",
};


您可以使用Class对象添加泛型类型的运行时检查,当在某个配置文件中的某个位置创建类,并且无法为该类的泛型类型添加编译时检查时,这非常方便。如果应用程序配置错误,你不希望类在运行时爆炸,也不希望所有的类都充满检查实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public interface SomeInterface {
  void doSomething(Object o);
}
public abstract class RuntimeCheckingTemplate<T> {
  private Class<T> clazz;
  protected RuntimeChecking(Class<T> clazz) {
    this.clazz = clazz;
  }

  public void doSomething(Object o) {
    if (clazz.isInstance(o)) {
      doSomethingWithGeneric(clazz.cast(o));
    } else {
      // log it, do something by default, throw an exception, etc.
    }
  }

  protected abstract void doSomethingWithGeneric(T t);
}

public class ClassThatWorksWithStrings extends RuntimeCheckingTemplate<String> {
  public ClassThatWorksWithStrings() {
     super(String.class);
  }

  protected abstract void doSomethingWithGeneric(T t) {
    // Do something with the generic and know that a runtime exception won't occur
    // because of a wrong type
  }
}

swingworker用于轻松管理来自后台线程的用户界面回调。


函数非常酷。它们与函数指针非常接近,在Java中,每个人通常都会说不可能。

Java中的函子


您可以在枚举类的方法定义中切换(这个)。让我喊"为什么!"当我发现这真的有效的时候,我大声地说。


显然,对于一些调试构建,有一个选项可以从热点转储本机(JIT)程序集代码:http://weblogs.java.net/blog/kohsuke/archive/2008/03/deep_dive_into.html

不幸的是,我无法通过那个帖子中的链接找到这个版本,如果有人能找到更精确的URL,我很乐意使用它。


因为还没有人说过(我想)我最喜欢的功能是自动拳击!

1
2
3
4
5
6
7
8
9
10
public class Example
{
    public static void main(String[] Args)
    {
         int a = 5;
         Integer b = a; // Box!
         System.out.println("A :" + a);
         System.out.println("B :" + b);
    }
}


几年前,当我不得不做Java(1.4。x)时,我想要一个EVAL()方法,而SunsJavac是(?)用Java编写的,它只是链接ToeS.JAR,并使用它周围的一些胶粘代码。


您可以重写一个方法并让超类构造函数调用它(这可能会给C++程序员带来惊喜)。

例子


我喜欢

  • JavaDoc的Taglet和Doclet使我们能够自定义JavaDoc输出。
  • JDK工具:jstat、jstack等。

  • JavaBean属性访问器方法不必从"GET"和"SET"开始。

    即使Josh Bloch在有效的Java中也会出错。


    当我第一次注意到三元运算符时,我很惊讶,它等于一个简单的if-then-else语句:

    1
    minVal = (a < b) ? a : b;


    让我惊讶的是,一个接口可以扩展多个接口,但类只能扩展一个类。