关于JAVA你所不知道的10件事
来源:互联网 发布:实况巅峰数据图拉姆 编辑:程序博客网 时间:2024/04/29 21:42
因此,从一开始你就是使用JAVA工作?记住JAVA还被称作“Oak”的日子,当面向对象还是一个热门话题,使用C++编程的人认为JAVA毫无机会,仅仅只能做一些诸如应用小程序之类的事。
我敢打赌你不知道至少一半以下所列的事。让我们开始在这一周中许多让人惊喜的关于JAVA的工作。
1.没有所谓的检查异常
对,你没有看错。除开JAVA语言,JAVA虚拟机并不其他这样的东西。
今天,每个人都认同检查异常是错误。正如Bruce Eckel(Think in JAVA的作者)在布拉格 GeeCon会议上所定的基调,在JAVA之后没有任何语言从事于使用检查异常,甚至JAVA 8在新的流API中也不再围绕它(当你使用IO或者JDBC在你的lambdas中,事实上有一些小的痛苦)。
你是否想证明JVM并不知道任何事,请使用下面的代码测试:
public class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.<RuntimeException> doThrow0(e); } @SuppressWarnings("unchecked") static <E extends Exception> void doThrow0(Exception e) throws E { throw (E) e; }}不要使用这进行编译,它仍然会抛出SQLException。你甚至不需要再看Lombok's @SneakyThrows 来了解它。
更多的细节可以查看此链接 ,或者 查看StackOverflow上的这个解答
2.你可以拥有仅仅是不同返回类型的方法重载
它不能被编译,真的是这样吗?
class Test { Object x() { return "abc"; } String x() { return "123"; }}
是这样的。JAVA语言不允许在同一个类中有相同的方法重载,不管它们是否可能抛出不同的异常或者返回不同类型的值。
但请稍等片刻,看一看JAVA文档中关于 Class.getMethod(String, Class...) 它写道:
"注意这里可能会有不止一个匹配的方法在一个类中,因为当JAVA语言禁止类中声明相同签名但是有不同返回值类型的许多方法,但是JVM并不会这样。这在虚拟机中提高了灵活性,能实现许多的语言特性。比如:协变式返回值可以桥方法实现,桥方法和这个方法使用相同的签名和不同的返回值类型进行重写。"
哇,是的,这样做是有意义的。事实上,当你像如下这样写时就十分的漂亮:
abstract class Parent<T> { abstract T x();} class Child extends Parent<String> { @Override String x() { return "abc"; }}检验在Chind中生成的字节码:
// Method descriptor #15 ()Ljava/lang/String;// Stack: 1, Locals: 1java.lang.String x(); 0 ldc <String "abc"> [16] 2 areturn Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 3] local: this index: 0 type: Child // Method descriptor #18 ()Ljava/lang/Object;// Stack: 1, Locals: 1bridge synthetic java.lang.Object x(); 0 aload_0 [this] 1 invokevirtual Child.x() : java.lang.String [19] 4 areturn Line numbers: [pc: 0, line: 1]
如你所见,T在字节码是确实是对象,这非常容易理解。
合成的桥方法在编译时确实生成了,因为Parent.x()返回类型签名可能预料到Object会在某一点会被调用,添加没有诸如桥方法的范型不可能在一个二进制平台存在。因此,改变JVM使允许这个特性只有少许麻烦(作为一个副作用也允许协变覆盖)。
你深入到语言的细节和内部吗?这里有许多有趣的细节。
3.所有的这些都是二维数组
class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; }}
是的,即使在你的头脑中分析不能马上理解上面方法的返回类型,但它们都是相同的,与下面的代码片段类似:
class Test { int[][] a = {{}}; int[] b[] = {{}}; int c[][] = {{}};}
你认为这不可思议吗?想象使用JSR-308/Java 8注解类型在上面的例子中。语法的数量让你抓狂了吗:
@Target(ElementType.TYPE_USE)@interface Crazy {} class Test { @Crazy int[][] a1 = {{}}; int @Crazy [][] a2 = {{}}; int[] @Crazy [] a3 = {{}}; @Crazy int[] b1[] = {{}}; int @Crazy [] b2[] = {{}}; int[] b3 @Crazy [] = {{}}; @Crazy int c1[][] = {{}}; int c2 @Crazy [][] = {{}}; int c3[] @Crazy [] = {{}};}
类型注解。设备的神秘只是超过了它的力量。
或者用其他的说法:
当我做这最后一次提交仅仅是我的假期四周前
我推荐你寻找一个关于上面的用例进行真实的练习
4.你并不了解条件语句
你以为你已经知道所有使用过的条件语句?让我告诉你,并非如此,你们中的大多数人会认为下面两个片段是等价的:
Object o1 = true ? new Integer(1) : new Double(2.0);
与这一样?
Object o2; if (true) o2 = new Integer(1);else o2 = new Double(2.0);
错,让我们快速测试一下:
System.out.println(o1);System.out.println(o2);程序会打印如下:
1.01
条件操作符会实现数值类型的提升。if"needed"条件中,带有强引用标记。因此,你期望这个程序抛出NullPointException?
Integer i = new Integer(1);if (i.equals(1)) i = null;Double d = new Double(2.0);Object o = true ? i : d; // NullPointerException!System.out.println(o);
关于上面讨论的更多信息可查看这里
5.你同样并不了解混合赋值操作符
很离奇吗,让我们考虑下面两段代码:
i += j;i = i + j;
直觉上,它们是相同的,对吗?你猜怎么着,这两段并不一样,JLS的规定是这样的:
非常地完美,这里我想引用Peter Lawrey's在Stack Overflow上的回答。
一个很好的例子是使用 *= 或者 /=:
byte b = 10;b *= 5.7;System.out.println(b); // prints 57
或者
byte b = 100;b /= 2.5;System.out.println(b); // prints 40
或者
char ch = '0';ch *= 1.1;System.out.println(ch); // prints '4'
或者
char ch = 'A';ch *= 1.5;System.out.println(ch); // prints 'a'
6.随机的整数
这是一个难题,先不要看答案,看看你能否找出解答的办法。当我运行下面的程序时:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i);}
有时,我得到下面的输出:
922214548236183391933384
这怎么可能?
.
.
.
.
.
.
.
.
.
答案在这儿
它通过反射重写了JDK中的Integer cache,然后用自动封箱和自动解箱。
import java.lang.reflect.Field;import java.util.Random;public class Entropy { public static void main(String[] args) throws Exception { // Extract the IntegerCache through reflection Class<?> clazz = Class.forName( "java.lang.Integer$IntegerCache"); Field field = clazz.getDeclaredField("cache"); field.setAccessible(true); Integer[] cache = (Integer[]) field.get(clazz); // Rewrite the Integer cache for (int i = 0; i < cache.length; i++) { cache[i] = new Integer( new Random().nextInt(cache.length)); } // Prove randomness for (int i = 0; i < 10; i++) { System.out.println((Integer) i); } }}
7.GOTO
这是我最喜欢的。Java有GOTO!打印出来:
int goto = 1;结果会是:
Test.java:44: error: <identifier> expected int goto = 1; ^
这是因为goto是Java中未使用的关键字。仅仅因为这个原因。
但这并不是最令人感兴趣的部分,最有趣的是你能真正实现goto通过break、continue和标签区块。
向前:
label: { // do stuff if (check) break label; // do more stuff}在字节码中:
2 iload_1 [check]3 ifeq 6 // Jumping forward6 ..向后退:
label: do { // do stuff if (check) continue label; // do more stuff break label;} while(true);在字节码中:
2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 ..
8.Java有类型别名
其他语言中,我们可以很容易地定义类型别名:interface People => Set<Person>;
一个people类型以如下方式构造,然后用Set<Person>交换
People? p1 = null;Set<Person>? p2 = p1;People? p3 = p2;
在Java中,我们不能在顶层中定义类型别名,但是我们可以在类的范围,或者方法范围更定义。让我们考虑下我们不高兴使用Integer、long等命名,我们想要一个短点的命名,比如I,L。很简单:
class Test<I extends Integer> { <L extends Long> void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); }}
在上面的程序中,Integer在Test类中用别名"I"命名,同样地Long也在x()这个方法中用L重新命名了,我们可以调用上面的方法:
new Test().x(1, 2L);这种方法当然是不被认真对待的,因为,Interger和Long都是final类型的,这意味着类型I和L是有效地别名(大多数,赋值兼容只有一条路径)。如果我们使用非final类型,我们就得使用普通地泛型。
9.一些类型的关系是不可判定的
// A helper type. You could also just use Listinterface Type<T> {}class C implements Type<Type<? super C>> {}class D<P> implements Type<Type<? super D<D<P>>>> {}
类C和类D是什么含义呢?
它们类似递归(但又有不同)java.lang.Enum 是递归。思考:
public abstract class Enum<E extends Enum<E>> { ... }
根据上面的描述,真实的Enum实现仅仅只是单纯的语法糖:
// Thisenum MyEnum {}// Is really just sugar for thisclass MyEnum extends Enum<MyEnum> { ... }
考虑到这一点,让我们回到两种类型,下边的编译吗:
class Test { Type<? super C> c = new C(); Type<? super D<Byte>> d = new D<Byte>();}
困难的问题,Ross Tate有回答,实际上,问题是不可判定的。
类C是<?super C>类型的子类吗:
Step 0) C <?: Type<? super C>Step 1) Type<Type<? super C>> <?: Type (inheritance)Step 2) C (checking wildcard ? super C)Step . . . (cycle forever)
然后
D类是<?super D<Byte>>类型的子类吗
Step 0) D<Byte> <?: Type<? super C<Byte>>Step 1) Type<Type<? super D<D<Byte>>>> <?: Type<? super D<Byte>>Step 2) D<Byte> <?: Type<? super D<D<Byte>>>Step 3) List<List<? super C<C>>> <?: List<? super C<C>>Step 4) D<D<Byte>> <?: Type<? super D<D<Byte>>>Step . . . (expand forever)
让我们写下
一些类型的关系是不可判定的
10. 类型交集
Java有一个很独特的特性叫做类型交集,你可以声明一个泛型类实际上有两种类型的交集,比如:class Test<T extends Serializable & Cloneable> {}
泛型类参数T,你捆绑一个Test类的实例,它必须实现Serializable和Cloneable两个接口,比如:String类型不是适合的参数,但Date适合:
// Doesn't compileTest<String> s = null;// CompilesTest<Date> d = null;
这个特性在Java8中重用了,你现在可以构造相交的特别类型。这有用处吗?几乎没有,但是你想强迫lambda表达式类型,没有其他的方式。让我们假设你使用这个疯狂的类型约束你的方法:
<T extends Runnable & Serializable> void execute(T t) {}
你想实现Runnable同样也实现了Serializable接口,以防你想执行它或者在别的线程发送它,Lambda和系列化真是有些怪异。
Lamdbas表达可以序列化
你可以序列化一个lambda表达化,如果它的目标类型拥有的参数是序列化的
但即使这样,它们并不自动实现Serializable接口,强制它们转化成那种类型,但当你只是转化成Serializable时
execute((Serializable) (() -> {}));lambda不会运行。
因此,要转化成两种类型:
execute((Runnable & Serializable) (() -> {}));
结论
我经常有句关于SQL的话,现在是用下边这句话对这篇文章作出一个结论的时刻了:
Java是一种神秘性超过了它力量的设计。
0 0
- 关于JAVA你所不知道的10件事
- 关于Java你可能不知道的10件事
- 关于java-10件你不知道的事
- PHP你所不知道的10件事
- Java关于多线程你不知道的5件事
- 关于JavaScript数组,你所不知道的3件事
- 关于 Wi-Fi 你所不知道的 8 件事
- 关于Google你可能不知道的10件事
- 关于PHP你可能不知道的10件事
- 关于PHP你可能不知道的10件事
- 关于PHP你可能不知道的10件事
- 关于PHP你可能不知道的10件事
- 你不知道java的10件事
- 你不知道Java的10件事
- Java之关于你所不知道的String
- 你所不知道的有关Service的10件事
- 关于Float你所不知道的
- 关于findViewById你所不知道的
- POJ 3522 Slim Span (最小生成树)
- 【菜鸟】学习Mongodb之——安装Mongodb
- codeforces xxx d
- HDU 2642 Stars 二维树状数组应用
- RakNet学习(26) -- Custom Memory Management
- 关于JAVA你所不知道的10件事
- UIWebView的基本使用
- 149 N个数排列 其中一个数被替换 求复杂度
- 层次聚类之AGNES算法和DIANA算法
- 马化腾内部分享:产品经理必修课
- android MediaScanner详解 ---- MediaScanner扫描得到多媒体信息
- 使用frame分割html页面的时候怎么得到大小固定的区域
- dbscan算法
- android MediaScanner详解