10 Things You Didn't Know About Java(java中的10个坑,你知道几个?)
来源:互联网 发布:penbeat专用笔淘宝 编辑:程序博客网 时间:2024/06/06 00:45
原文地址 http://java.dzone.com/articles/10-things-you-didnt-know-about
So, you’ve been working with Java since the very beginning? Remember the days when it was called “Oak”, when OO was still a hot topic, when C++ folks thought that Java had no chance, when Applets were still a thing?
I bet that you didn’t know at least half of the following things. Let’s start this week with some great surprises about the inner workings of Java.
1. There is no such thing as a checked exception
That’s right! The JVM doesn’t know any such thing, only the Java language does.
Today, everyone agrees that checked exceptions were a mistake. As Bruce Eckel said on his closing keynote at GeeCON, Prague, no other language after Java has engaged in using checked exceptions, and even Java 8 does no longer embrace them in the new Streams API (which can actually be a bit of a pain, when your lambdas use IO or JDBC).
Do you want proof that the JVM doesn’t know such a thing? Try the following code:
01.
public
class
Test {
02.
03.
// No throws clause here
04.
public
static
void
main(String[] args) {
05.
doThrow(
new
SQLException());
06.
}
07.
08.
static
void
doThrow(Exception e) {
09.
Test.<RuntimeException> doThrow0(e);
10.
}
11.
12.
@SuppressWarnings
(
"unchecked"
)
13.
static
<E
extends
Exception>
14.
void
doThrow0(Exception e)
throws
E {
15.
throw
(E) e;
16.
}
17.
}
Not only does this compile, this also actually throws the SQLException
, you don’t even need Lombok’s @SneakyThrows
for that.
More details about the above can be found in this article here, or here, on Stack Overflow.
2. You can have method overloads differing only in return types
That doesn’t compile, right?
1.
class
Test {
2.
Object x() {
return
"abc"
; }
3.
String x() {
return
"123"
; }
4.
}
Right. The Java language doesn’t allow for two methods to be “override-equivalent” within the same class, regardless of their potentially differingthrows
clauses or return
types.
But wait a second. Check out the Javadoc of Class.getMethod(String, Class...)
. It reads:
Note that there may be more than one matching method in a class because while the Java language forbids a class to declare multiple methods with the same signature but different return types, the Java virtual machine does not. This increased flexibility in the virtual machine can be used to implement various language features. For example, covariant returns can be implemented with bridge methods; the bridge method and the method being overridden would have the same signature but different return types.
Wow, yes that makes sense. In fact, that’s pretty much what happens when you write the following:
1.
abstract
class
Parent<T> {
2.
abstract
T x();
3.
}
4.
5.
class
Child
extends
Parent<String> {
6.
@Override
7.
String x() {
return
"abc"
; }
8.
}
Check out the generated byte code in Child
:
01.
// Method descriptor #15 ()Ljava/lang/String;
02.
// Stack: 1, Locals: 1
03.
java.lang.String x();
04.
0
ldc <String
"abc"
> [
16
]
05.
2
areturn
06.
Line numbers:
07.
[pc:
0
, line:
7
]
08.
Local variable table:
09.
[pc:
0
, pc:
3
] local:
this
index:
0
type: Child
10.
11.
// Method descriptor #18 ()Ljava/lang/Object;
12.
// Stack: 1, Locals: 1
13.
bridge synthetic java.lang.Object x();
14.
0
aload_0 [
this
]
15.
1
invokevirtual Child.x() : java.lang.String [
19
]
16.
4
areturn
17.
Line numbers:
18.
[pc:
0
, line:
1
]
So, T
is really just Object
in byte code. That’s well understood.
The synthetic bridge method is actually generated by the compiler because the return type of the Parent.x()
signature may be expected to Object
at certain call sites. Adding generics without such bridge methods would not have been possible in a binary compatible way. So, changing the JVM to allow for this feature was the lesser pain (which also allows covariant overriding as a side-effect…) Clever, huh?
Are you into language specifics and internals? Then find some more very interesting details here.
3. All of these are two-dimensional arrays!
1.
class
Test {
2.
int
[][] a() {
return
new
int
[
0
][]; }
3.
int
[] b() [] {
return
new
int
[
0
][]; }
4.
int
c() [][] {
return
new
int
[
0
][]; }
5.
}
Yes, it’s true. Even if your mental parser might not immediately understand the return type of the above methods, they are all the same! Similar to the following piece of code:
1.
class
Test {
2.
int
[][] a = {{}};
3.
int
[] b[] = {{}};
4.
int
c[][] = {{}};
5.
}
You think that’s crazy? Imagine using JSR-308 / Java 8 type annotations on the above. The number of syntactic possibilities explodes!
01.
@Target
(ElementType.TYPE_USE)
02.
@interface
Crazy {}
03.
04.
class
Test {
05.
@Crazy
int
[][] a1 = {{}};
06.
int
@Crazy
[][] a2 = {{}};
07.
int
[]
@Crazy
[] a3 = {{}};
08.
09.
@Crazy
int
[] b1[] = {{}};
10.
int
@Crazy
[] b2[] = {{}};
11.
int
[] b3
@Crazy
[] = {{}};
12.
13.
@Crazy
int
c1[][] = {{}};
14.
int
c2
@Crazy
[][] = {{}};
15.
int
c3[]
@Crazy
[] = {{}};
16.
}
Type annotations. A device whose mystery is only exceeded by its power
Or in other words:
When I do that one last commit just before my 4 week vacation
I let the actual exercise of finding a use-case for any of the above to you.
4. You don’t get the conditional expression
So, you thought you knew it all when it comes to using the conditional expression? Let me tell you, you didn’t. Most of you will think that the below two snippets are equivalent:
1.
Object o1 =
true
?
new
Integer(
1
) :
new
Double(
2.0
);
… the same as this?
1.
Object o2;
2.
3.
if
(
true
)
4.
o2 =
new
Integer(
1
);
5.
else
6.
o2 =
new
Double(
2.0
);
Nope. Let’s run a quick test
1.
System.out.println(o1);
2.
System.out.println(o2);
This programme will print:
1.01
Yep! The conditional operator will implement numeric type promotion, if“needed”, with a very very very strong set of quotation marks on that“needed”. Because, would you expect this programme to throw a NullPointerException
?
1.
Integer i =
new
Integer(
1
);
2.
if
(i.equals(
1
))
3.
i =
null
;
4.
Double d =
new
Double(
2.0
);
5.
Object o =
true
? i : d;
// NullPointerException!
6.
System.out.println(o);
More information about the above can be found here.
5. You also don’t get the compound assignment operator
Quirky enough? Let’s consider the following two pieces of code:
1.
i += j;
2.
i = i + j;
Intuitively, they should be equivalent, right? But guess what. They aren’t! The JLS specifies:
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
This is so beautiful, I would like to cite Peter Lawrey‘s answer to this Stack Overflow question:
A good example of this casting is using *= or /=
byte b = 10;b *= 5.7;System.out.println(b); // prints 57or
byte b = 100;b /= 2.5;System.out.println(b); // prints 40or
char ch = '0';ch *= 1.1;System.out.println(ch); // prints '4'or
char ch = 'A';ch *= 1.5;System.out.println(ch); // prints 'a'
Now, how incredibly useful is that? I’m going to cast/multiply chars right there in my application. Because, you know…
6. Random integers
Now, this is more of a puzzler. Don’t read the solution yet. See if you can find this one out yourself. When I run the following programme:
1.
for
(
int
i =
0
; i <
10
; i++) {
2.
System.out.println((Integer) i);
3.
}
… then “sometimes”, I get the following output:
922214548236183391933384
How is that even possible??
.
.
.
.
.
. spoiler… solution ahead…
.
.
.
.
.
OK, the solution is here and has to do with overriding the JDK’s Integer
cache via reflection, and then using auto-boxing and auto-unboxing. Don’t do this at home! Or in other words, let’s think about it this way, once more
When I do that one last commit just before my 4 week vacation
7. GOTO
This is one of my favourite. Java has GOTO! Type it…
1.
int
goto
=
1
;
This will result in:
Test.java:44: error: <identifier> expected int goto = 1; ^
This is because goto
is an unused keyword, just in case…
But that’s not the exciting part. The exciting part is that you can actually implement goto with break
, continue
and labelled blocks:
Jumping forward
1.
label: {
2.
// do stuff
3.
if
(check)
break
label;
4.
// do more stuff
5.
}
In bytecode:
2 iload_1 [check]3 ifeq 6 // Jumping forward6 ..
Jumping backward
1.
label:
do
{
2.
// do stuff
3.
if
(check)
continue
label;
4.
// do more stuff
5.
break
label;
6.
}
while
(
true
);
In bytecode:
2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 ..
8. Java has type aliases
In other languages (e.g. Ceylon), we can define type aliases very easily:
1.
interface
People => Set<Person>;
A People
type constructed in such a way can then be used interchangably with Set<Person>
:
1.
People? p1 =
null
;
2.
Set<Person>? p2 = p1;
3.
People? p3 = p2;
In Java, we can’t define type aliases at a top level. But we can do so for the scope of a class, or a method. Let’s consider that we’re unhappy with the namings of Integer
, Long
etc, we want shorter names: I
and L
. Easy:
1.
class
Test<I
extends
Integer> {
2.
<L
extends
Long>
void
x(I i, L l) {
3.
System.out.println(
4.
i.intValue() +
", "
+
5.
l.longValue()
6.
);
7.
}
8.
}
In the above programme, Integer
is “aliased” to I
for the scope of the Test
class, whereas Long
is “aliased” to L
for the scope of the x()
method. We can then call the above method like this:
1.
new
Test().x(
1
, 2L);
This technique is of course not to be taken seriously. In this case, Integer
and Long
are both final types, which means that the types I
and L
areeffectively aliases (almost. assignment-compatibility only goes one way). If we had used non-final types (e.g. Object
), then we’d be really using ordinary generics.
Enough of these silly tricks. Now for something truly remarkable!
9. Some type relationships are undecidable!
OK, this will now get really funky, so take a cup of coffee and concentrate. Consider the following two types:
1.
// A helper type. You could also just use List
2.
interface
Type<T> {}
3.
4.
class
C
implements
Type<Type<?
super
C>> {}
5.
class
D<P>
implements
Type<Type<?
super
D<D<P>>>> {}
Now, what do the types C
and D
even mean?
They are somewhat recursive, in a similar (yet subtly different) way thatjava.lang.Enum
is recursive. Consider:
1.
public
abstract
class
Enum<E
extends
Enum<E>> { ... }
With the above specification, an actual enum
implementation is just mere syntactic sugar:
1.
// This
2.
enum
MyEnum {}
3.
4.
// Is really just sugar for this
5.
class
MyEnum
extends
Enum<MyEnum> { ... }
With this in mind, let’s get back to our two types. Does the following compile?
1.
class
Test {
2.
Type<?
super
C> c =
new
C();
3.
Type<?
super
D<Byte>> d =
new
D<Byte>();
4.
}
Hard question, and Ross Tate has an answer to it. The question is in fact undecidable:
Is C a subtype of Type<? 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)
And then:
Is D a subtype of Type<? 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)
Try compiling the above in your Eclipse, it’ll crash! (don’t worry. I’ve filed a bug)
Let this sink in…
Some type relationships in Java are undecidable!
If you’re interested in more details about this peculiar Java quirk, read Ross Tate’s paper “Taming Wildcards in Java’s Type System” (co-authored with Alan Leung and Sorin Lerner), or also our own musings on correlating subtype polymorphism with generic polymorphism
10. Type intersections
Java has a very peculiar feature called type intersections. You can declare a (generic) type that is in fact the intersection of two types. For instance:
1.
class
Test<T
extends
Serializable & Cloneable> {
2.
}
The generic type parameter T
that you’re binding to instances of the classTest
must implement both Serializable
and Cloneable
. For instance,String
is not a possible bound, but Date
is:
1.
// Doesn't compile
2.
Test<String> s =
null
;
3.
4.
// Compiles
5.
Test<Date> d =
null
;
This feature has seen reuse in Java 8, where you can now cast types to ad-hoc type intersections. How is this useful? Almost not at all, but if you want to coerce a lambda expression into such a type, there’s no other way. Let’s assume you have this crazy type constraint on your method:
1.
<T
extends
Runnable & Serializable>
void
execute(T t) {}
You want a Runnable
that is also Serializable
just in case you’d like to execute it somewhere else and send it over the wire. Lambdas and serialisation are a bit of a quirk.
Lambdas can be serialised:
You can serialize a lambda expression if its target type and its captured arguments are serializable
But even if that’s true, they do not automatically implement theSerializable
marker interface. To coerce them to that type, you must cast. But when you cast only to Serializable
…
1.
execute((Serializable) (() -> {}));
… then the lambda will no longer be Runnable.
Egh…
So…
Cast it to both types:
1.
execute((Runnable & Serializable) (() -> {}));
- 10 Things You Didn't Know About Java(java中的10个坑,你知道几个?)
- 5 things you didn't know about ... the Java Collections API
- 5 things you didn't know about ... Java performance monitoring, Part 2
- 100 Things You Probably Didn't Know About Oracle Database
- 10 things you didn't know you can do with Unreal Engine 4
- The 10 Things You Need to Know About Java SE 6 'Mustang'
- The 10 Things You Need to Know About Java SE 6 'Mustang'
- Top 10 New Things You Need to Know About Java 6
- concurrent five things that you didn't know
- notes on ibm:dw"5 things you didn't know about ... Command-line flags for the JVM"
- 10 things you should know about every Linux installation(ZT)
- 10 things you should know about every Linux installation
- 10 Things You Should Know About Microsoft's Silverlight
- 10 Things You Should Know About Using a BlackBerry!
- 10 Things You Need To Know About Cocoa Autolayout
- 10 Things You Need To Know About Cocoa Auto Layout
- 我所不知道的 Chrome 开发者工具 英文原文:Things I didn’t Know about Chrome DevTools
- A few git tips you didn't know about
- easy ui 使用总结
- debug调试技巧 作者daimojingdeyu
- valuestack,stackContext,ActionContext.之间的关系
- Python图像处理库:Pillow 初级教程
- windows和linux服务器上iis和apache的WordPress固定链接的设置方法
- 10 Things You Didn't Know About Java(java中的10个坑,你知道几个?)
- libyuv编译
- ArcGIS教程:地图制图之创建地图册
- spring+quartz定时器任务调度(时间配置比timer灵活的多)
- 过滤器:探究多介质过滤器对反洗空气量的影响
- jfadsljfdasljfdlasfjsdaljfsdalfjadslfjdas
- 嵌入式 iostat命令详解
- OpenWrt DownLoad
- dfdjhdsafldlfdjfldsjfldjfdlafjsdalfsd