As Java nerds, we are more interested in introducing the conceptual details of Java and the JVM than the practical skills. So I would like to recommend Lukas Eder in jooq.org published original works to everyone.
Do you always use Java from a very early start? Do you remember the past? At that time, Java also called Oak,oo is a hot topic, C + + folk think Java is impossible to fire, Java development of small applications Applets is also concerned about.
I bet you don't know half of the things I'm going to introduce. Let's explore the mysteries of Java in depth.
1. There is no such thing as checking for anomalies
That's right! The JVM will not know about these things, only the Java statement knows.
Now everyone thinks it's a mistake to check for exceptions. As Bruce Eckel said at the closing of Geecon in Prague, Java has no other language to check for exceptions, and even Java 8 does not do this in the new Stream API (if your Lambda uses IO and JDBC, this is actually a bit of a pain).
How do I verify that the JVM does not clearly check for anomalies? Try the following code:
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;
}
}
This can not only be compiled through, it can also throw SQLException. You don't even need a Lombok @SneakyThrows to do it.
This article can be seen in more detail, or on Stack Overflow.
2. You can define overloaded functions that differ only in return values
This kind of code can't be compiled, right?
Class Test {
Object x () {return ' abc ';}
String x () {return ' 123 ';}
}
Right. The Java language does not allow two methods to "equivalent overloading" in the same class, ignoring potential differences such as throws's own or return type.
View Class.getmethod (String, Class ...) for Javadoc. The explanations are as follows:
Note that there may be multiple matching methods in a class, because the Java language prohibits declaring multiple methods with the same signature but with different return types in one class, but not for Java virtual machines. The increased flexibility in virtual machines can be used to implement a variety of language features. For example, the bridge method can be used to implement the covariant parameter return; The bridging method and the overridden method will have the same signature but have a different return type.
Wow, that makes sense. In fact, the following code hides a lot of things:
Abstract class Parent<t> {
Abstract T x ();
}
Class Child extends Parent<string> {
@Override
String x () {return ' abc ';}
}
Take a look at the bytecode generated for child:
Method descriptor #15 () ljava/lang/string;
Stack:1, Locals:1
Java.lang.String x ();
0 LDC </string><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:1
Bridge 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]
In fact, in bytecode, T is really just an Object. That's good to understand.
The synthetic bridge method is actually generated by the compiler, because the return type in the Parent.x () signature is exactly Object when it is actually called. Introducing generics without this bridge method will not be compatible with binary. So, changing the JVM to allow this feature would be less painful (the side effect is allowing the covariance to override everything) is smart, isn't it?
Have you read the internal details of the language? Take a look and discover more interesting things here.
3. All of these are two-dimensional arrays!
Class Test {
Int[][] A () {return new int[0][];}
Int[] B () [] {return new int[0][];}
int C () [] [] {return new int[0][];}
}
Yes, it's true. Even if your brain parser doesn't immediately understand the return type of the above methods, they are all the same! Similar to these are the following code snippets:
Class Test {
Int[][] A = {{}};
Int[] b[] = {{}};
int c[][] = {{}};
}
You think this is crazy? Imagine using the Jsr-308/java 8 type annotation above. The probability of grammar increases exponentially!
@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 [] = {{}};
}
Type annotations. It seems mysterious, but it's not hard to understand.
Or in other words:
When I did the last commit, it was before my 4-week holiday.
For you, the above content has been found in your actual use.
4. Special cases of conditional expressions
Probably most people will think:
Object O1 = true? New Integer (1): New Double (2.0);
is equivalent to:
Object O2;
if (true)
O2 = new Integer (1);
Else
O2 = new Double (2.0);
However, that is not the case. Let's test it and we'll find out.
System.out.println (O1);
System.out.println (O2);
Output Result:
1.0
1
This shows that the three-mesh conditional operator will raise the operand type if necessary. Note that this is done only when necessary, otherwise the code may throw a nullpointerexception null reference exception:
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. You still don't understand the compound assignment operator
Is it strange? Take a look at the following two lines of code:
i + = j;
i = i + j;
Intuitively it seems that they are equivalent, right? But in fact they are not equivalent! JLS explained as follows:
The compound assignment expression in the form of E1 op= E2 is equivalent to E1 = (t) ((E1) op (E2)), where T is the type of E1, and E1 is calculated only once.
Very well, I would like to cite the answer to this question on the Peter Lawrey Stack Overflow:
Examples of calculations using *= or/=
byte B = 10;
b *= 5.7;
System.out.println (b); Prints 57
Or
byte B = 100;
b/= 2.5;
System.out.println (b); Prints 40
Or
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 '
Do you see how it works now? I'll multiply the strings in the application. Because, you know ...
6. random integers
Now there is a more difficult puzzle to solve. Don't go to the answer and see if you can find the answer for yourself. If you run the following program:
for (int i = 0; i < i++) {
System.out.println ((Integer) i);
}
... "Sometimes," I'll get the following output:
92
221
45
48
236
183
39
193
33
84
How is that possible??
. Spoiler ... Continue to answer ...
Well, here's the answer (https://blog.jooq.org/2013/10/17/add-some-entropy-to-your-jvm/), which must rewrite the JDK's Integer cache through reflection, then use Auto-boxing and unpacking. Don't do such things at home! Or, we should do this in a different way.
7. GOTO
This is one of my favorites. Java also has goto! Enter next Try ...
int goto = 1;
Will output:
Test.java:44:error: <identifier> expected
int goto = 1;
^
This is because Goto is an unused keyword, just in case ...
But this is not the most exciting part. The exciting part is that you can use break, continue, and tag blocks to implement the Goto function:
Jump forward:
Do stuff
if (check) break label;
Do more Stuff
}
The format in the bytecode is as follows:
2 Iload_1 [Check]
3 ifeq 6//Jumping forward
6..
Jump backwards:
Label:do {
Do stuff
if (check) continue label;
Do more Stuff
Break label;
} while (true);
The format in the bytecode is as follows:
2 Iload_1 [Check]
3 Ifeq 9
6 goto 2//Jumping backward
9..
8. Java has type aliases
In other languages (such as Ceylon), we can easily define aliases for types:
Interface people = set<person>;
The people type is generated here, using it just like using set<person>:
People? P1 = null;
Set</person><person>? P2 = p1;
People? P3 = p2;
In Java we cannot define type aliases at the top-level scope, but we can do this in a class or method scope. If we don't like the names of integers, Long and so on, but want to use shorter I and L, it's simple:
Class Test<i extends Integer> {
<l extends long> void x (I I, l l) {
System.out.println (
I.intvalue () + "," +
L.longvalue ()
);
}
}
In the above program, Integer in the scope of the Test class is given an "alias" like I, similarly, Long in the X () method is given an "alias" such as L. We can then invoke the method as follows:
New Test (). x (1, 2L);
This kind of technology is certainly not very much appreciated. In this case, both the Integer and Long are final types, that is, I and L are de facto aliases (basically assignment compatibility requires only one possibility to be considered). If we use a non-final type (such as Object), it is generic.
These tricks have been enough. Now take a look at something really great!
9. Certain types of relationships are not deterministic!
Well, this is going to be a big draw, just a cup of coffee to refresh yourself. Think of the following two types:
A helper type. You could also just use List
Interface Type<t> {}
Class C implements Type<type <? Super C>> {}
Class D<p> implements Type<type <? Super D<d<p>>>> {}
Now tell me, what are types C and D exactly?
They are recursive and are a recursive approach similar to Java.lang.Enum (but slightly different). Look:
Public abstract class Enum<e extends Enum<e>> {...}
In the above description, the enum is actually just a simple syntactic sugar:
This
Enum MyEnum {}
is really just sugar for this
Class MyEnum extends Enum<myenum> {...}
With that in mind, we look back at the two types mentioned earlier, and the code below compiles into what?
Class Test {
type<? Super c> C = new C ();
type<? Super d<byte>> D = new d<byte> ();
}
Very difficult question to answer, but Ross Tate has already answered. The answer to this question is not to be judged:
C is type< The subclass of 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:
D is type<? The subclass of 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) type<type<? Super C<c>>> <?: type<? Super c<c>>
Step 4) d<d<byte>> <?: type<? Super d<d<byte>>>
Step ... (Expand Forever)
Try compiling in Eclipse and it will crash! (Don't worry, I submitted a BUG report)
Let this thing sink down ...
Some types of relationships in Java are ambiguous!
If you're curious about the use of Java, take a look at Ross Tate's "using wildcards in the Java type System" (co-authored with Alan Leung and Sorin Lerner), and we're also discussing the correlation subclass polymorphism in generic polymorphism.
type intersection
Java has a very strange feature called type intersection. You can declare a (generic) type, which is actually the intersection of two types, such as:
Class Test<t extends Serializable & cloneable> {
}
The generic type parameter T that is bound to an instance of the Test type must implement Serializable and cloneable. For example, String does not meet the requirements, but dete satisfies:
doesn ' t compile
test<string> s = null;
Compiles
Test<date> d = null;
This feature is already in use in Java 8. Is that useful? It's almost useless, but if you want a LAMBDA expression to be this type, there's no other way. Let's say your method has this crazy type constraint:
<t extends Runnable & serializable> void execute (T t) {}
You want to get a Runnable object that can be serialized (Serializable) by executing it. Lambda and serialization are also a bit strange.
Lambda can sequence by:
If both the target type and the parameter type of the lambda can be serialized, you can serialize the lambda
But even then, they cannot automatically implement the Serializable tag interface. You must cast the type. But when you just throw it to Serializable ...
Execute ((Serializable) ((), {}));
... Then Lambda will no longer be Runnable.
So to convert it to two types:
Execute ((Runnable & Serializable) ((), {}));
Conclusion
In a word, this article is:
Java happens to be a seemingly mysterious language, but it's not.
Source: Code Agricultural Network
10 things you don't know about Java