现在的位置: 首页 > 综合 > 正文

Java中一些常被忽视的特性 Java中一些常被忽视的特性

2018年05月23日 ⁄ 综合 ⁄ 共 14105字 ⁄ 字号 评论关闭
 

Java中一些常被忽视的特性

说明:

1 参考自stackoverflow上的一个帖子以及广大网友的回复: http://stackoverflow.com/questions/15496/hidden-features-of-java?page=4&tab=votes#tab-top

2 在这些回复中,我选择了一些写到这篇博客中,当然也有很多回复我认为没有必要翻译出来.

3 在翻译的过程中,为了快速完成,主要把代码贴过来,只要是对Java比较熟悉的, 看到代码基本上就能理解这个特性.对其他解释性的文字翻译的比较少, 如果感兴趣可以查看原文, 原文地址在上面已经给出.

正文:

1 使用Class<T>进行运行时类型检查

如果一个实例是根据配置文件中的类型创建出来的, 那么在代码中并不能明确确定该实例的类型,使用该方法可以在运行时检查类型
[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public interface SomeInterface {  
  2.   void doSomething(Object o);  
  3. }  
  4. public abstract class RuntimeCheckingTemplate<T> {  
  5.   private Class<T> clazz;  
  6.   protected RuntimeChecking(Class<T> clazz) {  
  7.     this.clazz = clazz;  
  8.   }  
  9.   
  10.   public void doSomething(Object o) {  
  11.     if (clazz.isInstance(o)) {  
  12.       doSomethingWithGeneric(clazz.cast(o));  
  13.     } else {  
  14.       // log it, do something by default, throw an exception, etc.  
  15.     }  
  16.   }  
  17.   
  18.   protected abstract void doSomethingWithGeneric(T t);  
  19. }  
  20.   
  21. public class ClassThatWorksWithStrings extends RuntimeCheckingTemplate<String> {  
  22.   public ClassThatWorksWithStrings() {  
  23.      super(String.class);  
  24.   }  
  25.   
  26.   protected abstract void doSomethingWithGeneric(T t) {  
  27.     // Do something with the generic and know that a runtime exception won't occur   
  28.     // because of a wrong type  
  29.   }  
  30. }  

2 整数自动装箱

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. Integer a = 1;  
  2. Integer b = 1;  
  3. Integer c = new Integer(1);  
  4. Integer d = new Integer(1);  
  5.   
  6. Integer e = 128;  
  7. Integer f = 128;  
  8.   
  9. assertTrue (a == b);   // again: this is true!  
  10. assertFalse(e == f); // again: this is false!  
  11. assertFalse(c == d);   // again: this is false!  

3 格式化字符串

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

4 匿名内部类中可以访问final类型的局部变量

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

5 数组类型的对象调用clone方法可以复制一个数组

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

6 通过反射可以访问私有成员

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class Foo {  
  2.     private int bar;  
  3.   
  4.     public Foo() {  
  5.         setBar(17);  
  6.     }  
  7.   
  8.     private void setBar(int bar) {  
  9.         this.bar=bar;  
  10.     }  
  11.   
  12.     public int getBar() {  
  13.         return bar;  
  14.     }  
  15.   
  16.     public String toString() {  
  17.         return "Foo[bar="+bar+"]";  
  18.     }  
  19. }  

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. import java.lang.reflect.*;  
  2.   
  3. public class AccessibleExample {  
  4.     public static void main(String[] args)  
  5.         throws NoSuchMethodException,IllegalAccessException, InvocationTargetException, NoSuchFieldException {  
  6.         Foo foo=new Foo();  
  7.         System.out.println(foo);  
  8.   
  9.         Method method=Foo.class.getDeclaredMethod("setBar"int.class);  
  10.         method.setAccessible(true);  
  11.         method.invoke(foo, 42);  
  12.   
  13.         System.out.println(foo);  
  14.         Field field=Foo.class.getDeclaredField("bar");  
  15.         field.setAccessible(true);  
  16.         field.set(foo, 23);  
  17.         System.out.println(foo);  
  18.     }  
  19. }  

打印结果:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

7 使用Scanner解析字符串

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

8 可以使用非英文给标示符命名

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. String Überschrift="";  

译者注: 中文也可以.

9 访问局部变量比访问成员变量快

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class Slow {  
  2.   /** Loop counter; initialized to 0. */  
  3.   private long i;  
  4.   
  5.   public static void main( String args[] ) {  
  6.     Slow slow = new Slow();  
  7.   
  8.     slow.run();  
  9.   }  
  10.   
  11.   private void run() {  
  12.     while( i++ < 10000000000L )  
  13.       ;  
  14.   }  
  15. }  

以上代码平均耗时18.018s


优化后的代码:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class Fast {  
  2.   /** Loop counter; initialized to 0. */  
  3.   private long i;  
  4.   
  5.   public static void main( String args[] ) {  
  6.     Fast fast = new Fast();  
  7.   
  8.     fast.run();  
  9.   }  
  10.   
  11.   private void run() {  
  12.     long i = getI();  
  13.   
  14.     while( i++ < 10000000000L )  
  15.       ;  
  16.   
  17.     setI( i );  
  18.   }  
  19.   
  20.   private long setI( long i ) {  
  21.     this.i = i;  
  22.   }  
  23.   
  24.   private long getI() {  
  25.     return this.i;  
  26.   }  
  27. }  

以上代码平均耗时10.509s


10 const是一个关键字, 但是你不能使用它

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

11 C风格的打印语句

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

12 匿名内部类可以直接调用子类中定义的方法

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. (new Object() {  
  2.     public String someMethod(){   
  3.         return "some value";  
  4.     }  
  5. }).someMethod();  
[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

尽管创建的匿名内部类实例并没有实现一个独立的接口, 但是能直接调用不在父类Object中的方法


13 List.subList()创建原有list的一个视图

操作一个list的一部分, 并且对subList的修改会反映到原有list中

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

以上代码删除原有list中从from到to位置的元素.


译者注: 可查看jdk中ArrayList的源码来验证这一结论.下面贴出部分源码:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public List<E> subList(int fromIndex, int toIndex) {  
  2.         subListRangeCheck(fromIndex, toIndex, size);  
  3.         return new SubList(this0, fromIndex, toIndex);  
  4.     }  

SubList作为ArrayList的一个内部类:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. private class SubList extends AbstractList<E> implements RandomAccess {  
  2.         private final AbstractList<E> parent;  
  3.         private final int parentOffset;  
  4.         private final int offset;  
  5.         int size;  
  6.   
  7.         SubList(AbstractList<E> parent,  
  8.                 int offset, int fromIndex, int toIndex) {  
  9.             this.parent = parent;  
  10.             this.parentOffset = fromIndex;  
  11.             this.offset = offset + fromIndex;  
  12.             this.size = toIndex - fromIndex;  
  13.             this.modCount = ArrayList.this.modCount;  
  14.         }  
  15. // ......  
  16.   
  17.         public void add(int index, E e) {  
  18.             rangeCheckForAdd(index);  
  19.             checkForComodification();  
  20.             parent.add(parentOffset + index, e);  
  21.             this.modCount = parent.modCount;  
  22.             this.size++;  
  23.         }  
  24.   
  25.         public E remove(int index) {  
  26.             rangeCheck(index);  
  27.             checkForComodification();  
  28.             E result = parent.remove(parentOffset + index);  
  29.             this.modCount = parent.modCount;  
  30.             this.size--;  
  31.             return result;  
  32.         }  
  33.   
  34. // ......  

从add和remove方法可以看出, 对subList的修改反映到了原有list(parent)中.


14 自限定泛型

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

15 可以在方法中定义一个内部类

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

16 Java中可以使用标号

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

上面的http:是一个标号, //是注释


17 没有main方法的HelloWorld

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

运行结果:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. $ java WithoutMain  
  2. Look ma, no main!!  

18 Shutdown钩子

在虚拟机停掉之前被调用:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

19 URL类的equals方法

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

上述代码结果为true


20 在没有声明抛出任何异常的方法中抛出受检查异常

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. import java.rmi.RemoteException;  
  2.   
  3. class Thrower {  
  4.     public static void spit(final Throwable exception) {  
  5.         class EvilThrower<T extends Throwable> {  
  6.             @SuppressWarnings("unchecked")  
  7.             private void sneakyThrow(Throwable exception) throws T {  
  8.                 throw (T) exception;  
  9.             }  
  10.         }  
  11.         new EvilThrower<RuntimeException>().sneakyThrow(exception);  
  12.     }  
  13. }  
  14.   
  15. public class ThrowerSample {  
  16.     public static void main( String[] args ) {  
  17.         Thrower.spit(new RemoteException("go unchecked!"));  
  18.     }  
  19. }  

21 可以抛出null

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

22 自动装箱中的一些规则

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. Long value = new Long(0);  
  2.         System.out.println(value.equals(0));  //false  
  3.           
  4.         Integer i = 0;  
  5.         System.out.println(i.equals(0));     //true  
  6.           
  7.         Integer j = new Integer(0);  
  8.         System.out.println(j.equals(0));    //true  
  9.         System.out.println(j.equals(i));    //true  
  10.         System.out.println(i == j);         //false  
  11.           
  12.         Integer m = 0;  
  13.         System.out.println(i.equals(m));    //true  
  14.         System.out.println(i == m);         //true  
  15.           
  16.         Integer x = 128;  
  17.         Integer y = 128;  
  18.         Integer z = new Integer(128);  
  19.         System.out.println(x.equals(y));    //true  
  20.         System.out.println(x == y);         //fasle  
  21.         System.out.println(x.equals(z));    //true  
  22.         System.out.println(x == z);     //false  

23 在finally块中返回

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. @SuppressWarnings("finally")  
  2. public static int returnSomething() {  
  3.     try {  
  4.         throw new RuntimeException("foo!");  
  5.     } finally {  
  6.         return 1;  
  7.     }  
  8.       
  9.     //在这里可以不写return语句  
  10. }  
  11.   
  12. public static void main(String[] args) {  
  13.     System.out.println(returnSomething());   // 返回1  
  14. }  

24 数组初始化的误区

以下代码是正确的:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class Foo {  
  2.     public void doSomething(String[] arg) {}  
  3.   
  4.     public void example() {  
  5.         String[] strings = { "foo""bar" };  
  6.         doSomething(strings);  
  7.     }  
  8. }  

而以下代码是错误的:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class Foo {  
  2.   
  3.     public void doSomething(String[] arg) {}  
  4.   
  5.     public void example() {  
  6.         doSomething({ "foo""bar" });  
  7.     }  
  8. }  

25 内部类中使用this访问外部类成员

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. import java.util.Comparator;  
  2.   
  3. public class ContainerClass {  
  4. boolean sortAscending;  
  5. public Comparator createComparator(final boolean sortAscending){  
  6.     Comparator comparator = new Comparator<Integer>() {  
  7.   
  8.         public int compare(Integer o1, Integer o2) {  
  9.             if (sortAscending || ContainerClass.this.sortAscending) {  
  10.                 return o1 - o2;  
  11.             } else {  
  12.                 return o2 - o1;  
  13.             }  
  14.         }  
  15.   
  16.     };  
  17.     return comparator;  
  18. }  
  19. }  

26 final局部变量可以不再声明的同时初始化

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

27 使用初始化块快速创建对象

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. JFrame frame = new JFrame(){{  
  2.          add( new JPanel(){{  
  3.                add( new JLabel("Hey there"){{   
  4.                     setBackground(Color.black);  
  5.                     setForeground( Color.white);  
  6.                 }});  
  7.   
  8.                 add( new JButton("Ok"){{  
  9.                     addActionListener( new ActionListener(){  
  10.                         public void actionPerformed( ActionEvent ae ){  
  11.                             System.out.println("Button pushed");  
  12.                         }  
  13.                      });  
  14.                  }});  
  15.         }});  
  16.     }};  

28 java1.5中引入可变参数

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

29 枚举可以实现一个接口

[html] view
plain
copy在CODE上查看代码片派生到我的代码片

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

30 泛型方法可以显示指定类型参数

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. Collections.<String,Integer>emptyMap()  

31 枚举中可以有构造方法和普通方法

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. enum Cats {  
  2.   FELIX(2), SHEEBA(3), RUFUS(7);  
  3.   
  4.   private int mAge;  
  5.   Cats(int age) {  
  6.     mAge = age;  
  7.   }  
  8.   public int getAge() {  
  9.     return mAge;  
  10.    }  
  11. }  

32 finally块中的return语句阻止try中异常的抛出

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

33 instanceof关键字不用判断null

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. ifnull != aObject && aObject instanceof String )  
  2. {  
  3.     ...  
  4. }  

可以使用以下代码代替(不用判断null)

[html] view
plain
copy在CODE上查看代码片派生到我的代码片

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

34 java1.5中引入的协变返回类型

如果子类覆盖了父类的方法, 子类中的方法可以返回父类中方法的返回值类型或其子类型

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

35 java中可以使用标号进行流程控制

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. // code goes here  
  2.   
  3. getmeout:{  
  4.     for (int i = 0; i < N; ++i) {  
  5.         for (int j = i; j < N; ++j) {  
  6.             for (int k = j; k < N; ++k) {  
  7.                 //do something here  
  8.                 break getmeout;  
  9.             }  
  10.         }  
  11.     }  
  12. }  

36 初始化块和静态初始化块

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

  1. public class App {  
  2.     public App(String name) { System.out.println(name + "'s constructor called"); }  
  3.   
  4.     static { System.out.println("static initializer called"); }  
  5.   
  6.     { System.out.println("instance initializer called"); }  
  7.   
  8.     static { System.out.println("static initializer2 called"); }  
  9.   
  10.     { System.out.println("instance initializer2 called"); }  
  11.   
  12.     public static void main( String[] args ) {  
  13.         new App("one");  
  14.         new App("two");  
  15.   }  
  16. }  

打印结果如下:

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

37 在泛型的类型限定中使用&限定接口

[html] view
plain
copy在CODE上查看代码片派生到我的代码片

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

意为:T是Foo的子类型, 并且实现了Bar接口

[java] view
plain
copy在CODE上查看代码片派生到我的代码片

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

在foo方法中, 可以使用Comparable接口中的compareTo方法

抱歉!评论已关闭.