一些比较冷门但非常实用的java技巧

Java有很多十分有用但不太为人知道的语法
只要稍微学过JavaSE便可以轻松读懂本文内容,并且在实战用派上用场

1.基础

写一个内部类
通过生成一个和外部类的实例保持关联的实例,使得外部类的实例与内部类的实例之间,能够保持一种【类-实例】似的关系

public class Outer {
	public class Inner {
	}
}

如上写了一个内部类的话,可以像下面一样生成实例

Outer o = new Outer();
Inner i = o.new Inner();

在new内部类的实例之前,先生成一个外部类的实例
虽然如果用this代替o来传值的话,就要写成this.new这样的形式,但是this.是可以被省略的,所以用Outer类的方法来new一个Inner类的实例的情况,可以省略this.new,单纯用new Inner()就行

public class Outer {
	public class Inner {
		public class Inner2 {
			public class Inner3 {
			}
		}
	}
	public void echo() {
		Inner i = new Inner(); //省略this.new直接写new
		Inner.Inner2 i2 = i.new Inner2();
		Inner.Inner2.Inner3 i3 = i2.new Inner3();
	}
}

当然了,多层生成也是非常实用的 (连续用.连接的感觉很爽吧)

new Outer().new Inner().new Inner2().new Inner3();

不写main方法来执行程序 
因为在main方法被调用之前,class会先被初始化,在那个过程之中写入代码的话,就能够实现没有main方法的情况下执行程序

public class Starter {
	static {
		System.out.println("Hello!");
	}
}

在这个例子中使用了最传统的锁定static初始化方式
这也是short coding或者code golf的选手最常用的手法之一,在命令行中我们可以如下一样执行:
C:\hoge>java Starter
Hello!
Exception in thread “main” java.lang.NoSuchMethodError: main
Hello!被输出在初始化类的阶段,在那之后的NoSuchMethodError中被提示不存在main方法
我们还能如下一样调用static的方法来执行程序

public class Starter {
	static int i = echo();

	private static int echo() {
		System.out.println("Hello!");
		return 0;
	}
}

System.out.println方法
System.out可以用System.setOut()方法来替换
写一个继承java.io.PrintStream的类再稍微调整一下的话,就能愉快地使用System.out.println()啦!(比如添加一个有趣的语尾)

注释
我们都知道可以用//来注释一行内容。通过在注释中放置unicode-escape过的换行符,可以让代码伪装在注释中
比如下面这段代码:

public class Test {
    public static void main(String[] args) {
        int i = 5;
        // #start unsafe# http://java.sun.com\u000a\u002f\u002a
        int *ip = &i;
        *ip += 3;
        i = *ip;
        // #end unsafe# http://java.sun.com\u002a\u002fi+=\u0033\u003b
        System.out.println(i);
    }
} 

声明
变量的声明可以用过注解来实现。首先,我们在@Target里写上包含LOCAL_VARIABLE的注解

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Target(ElementType.LOCAL_VARIABLE)
public @interface Echo {

}

这样的话,我们就能在注解中声明变量啦 *1
@Echo Echo Echo = Echo();

其次,我们可以通过使用transient关键字和volatile关键字,来吓一吓不是很熟悉语法的人
不过用泛型的效果会更加好一些
public class Echo {
Echo Echo;
}
注意,这里的Echo并不是Echo型的,而是Echo泛型变量~

编译程序
java的命令有几个增添的option
如果在option中使用-g:none,那么debug的信息便不会被输出,stacktrace中的行号也会被变成unknown
而option里的-processor是关联着注解处理的

执行程序
同样地,在执行命令中也有几个很方便的option
例如-D可以调试系统属性,推荐改写path.separator
-javaagent也是有趣的一个option

2.数字

数字的表现
在java的数字表现中,8进制是比较推荐使用的
System.out.println(012);
结果:
10
没有使用特别的符号,因此比较被难以察觉,能够让读代码的人多思考代码的含义

四则运算
希望记住的是Double.NaN
这是double类型的其中一种,作用是声明不是一种数字,在0.0/0.0这样的运算中发生
同样地,NaN和其他的值产生的运算结果也是NaN,在处理过程中添加NaN的话,便可以避开复杂的运算得到人为可见的结果
同时,NaN==NaN的情况下,结果是false也是一个需要注意的地方,在某个Java问答中,有人就是利用了这个特性出了下面这个题目:
问题29:循环大佬的花嫁(Bride of Looper)
请将下列循环通过声明i变成无限循环

while(i != i) {
}

增值和减值操作符
下列代码可能招致完型崩坏(semantic satiation)

int i = 0;
i = i++ + ++i;
System.out.println(i);

3.字符串

字符串和Object之间的加号会自动运用对象的toString方法
在Eclipse这些IDE的Debuger中确认变量的值的时候,也是用这个,因此适当地随便override一下会让debug更能沉淀时间

4.数组

在Java5之后使用数组的机会少了很多,但是依然存在着用可变长参数的机会

public class Hoe<E> {
    private Class<E> type;

    public Hoe(E... e) {
        @SuppressWarnings("unchecked")
        Class<E> type = (Class<E>) e.getClass().getComponentType();
        this.type = type;
    }

    public Class<E> getType() {
        return type;
    }
}

这样的话,便能获得到具体的泛型
比如:

Hoe<Fuga> hoe = new Hoe<Fuga>();

之中,hoe.getType()便是Fuga.class(好用吧)
这个方法是通过在构造器中放置一个长度可变的参数,在调用new Hoe()的时候传递一个大小位0的Fuga[]给构造器,再从那之中获得具体的泛型类型

5.哈希

稍微修改Object#hashCode()和Object#equals可以让标准API的java.util.HashMap产生一些奇妙反应
java.lang.Comparable或者java.util.Comparator中,如果使得不等价的对象返回0时,可以消灭java.util.TreeMap的put进去的对象

6.控制

if
通过在条件式中运用逻辑互斥或^(XOR)来让代码更加有高级感

if (a == null ^ b == null) {
    throw new NullPointerException();
}

for
在控制中知名度比较低的应该属于标签(label)了吧?在for和while之中我们都会使用break和countinue,但是在多重循环中,我们应该学会控制某个单独的循环

yLoop: for (int y=0; y<height; y++) {
    xLoop: for (int x=0; x<width; x++) {
        if (map[x][y] == Type.BOMB) {
            break yLoop;
        }
    }
}

正好可以通过标签+注释的方式,让代码看着像URL,有整洁感

http://example.jp/
for (int i=0; i<size; i++) {
}

break并不是只能在循环体中使用:

hoge: {
	piyo: {
		break hoge;
	}
}

7.方法

多使用strictfp关键字
用synchronized关键字的话比较土避开比较好

8.异常处理

在finally之中用return和throw之类提高速度的关键字

9.反射

通过java.lang.reflect包,可以参照动态的变量以及调用动态的方法
setAccessible方法甚至可以在外部调用private方法

10.类加载器

通过自己写一个类加载器,可以在执行时更换类的内容
例如defineClass方法可以从byte数组中生成类,重写这个方法可以给byte数组里面加一些料

*1: 通常,Java的命名规范里变量通常是小写字母开始,在这里故意写成大写的,便能实现注释/类型/变量名/构造器名四个Echo拥有各自的功能
*2 Java5以后的泛型比起数组更常用List接口,长度可变的形参传递依然是在数组中实现所以用起来很麻烦

你可能感兴趣的:(一些比较冷门但非常实用的java技巧)