Question

The same as this question but for java

Update Based on the comments and responses of a few people, Its clear that Java has very little undefined behaviour.

So I'd like to ask as well what behaviour is not obvious. Please when answering make the distinction between the two :)

Was it helpful?

Solution

Anything to do with threads... :)

Also:

  • Overriding methods and expecting them to be used in the same way between versions
  • Assumptions about underlying platform (file separator, for instance)
  • Details of garbage collection/finalisation
  • Some details about class initialisation
  • Whether Integer.valueOf (and the like) return the same objects
  • Performance, latency and memory usage

OTHER TIPS

There is very, very little undefined behavior in Java, when compared to C/C++, it is a much more well-defined platform. The reason for this is that C/C++ compilers are meant to produce code for very different platforms and therefore were granted rather wide freedoms in order to prevent too stringent requirements that would force a compiler to produce suboptimal code for a given platform.

Java sacrificed some of that by defining almost every behavior in a very exact way and allowing only small degrees of freedom. This of course makes the platform somewhat easier to handle.

The main area where undefined behavior happens is the exact timing and scheduling of multiple threads (as Tom Hawtin already mentioned)..

There are several places where the behavior is not obvious, 'though, so it might look undefined, but isn't (the String comparison examples given by Oscar Reyes are a great example).

And a few places where the behavior is defined to be undefined (for example the order of elements in a HashMap is defined to be implementation dependent and needs not be constant).

I think that Java(TM) Puzzlers: Traps, Pitfalls, and Corner Cases book will be very useful, it explains many hidden points and undefined behaviours of java.

Serialization. It's not undefined, per se (there is a deterministic algorithm). However, it is not at all obvious to the casual observer what will or will not cause a change in the serialVersionUID, thus foiling all your attempts to make use of RMI, JMS and a variety of other acronyms.

So, it is usually a good idea to consider your options when you know that you will need to serialize an object. I'm particularly fond of "always include it as a field" technique:

private static final long serialVersionUID = 1L;

Only change the value of that field when you, the developer know that there has been a compatibility-killing change to your code. Don't let the JDK make that decision for you....

I am not entirely sure what you mean by "undefined behaviors", but as others have pointed out, the core language is very predictable across platforms, and versions of the language and JVM.

This is not true for graphics (Swing, AWT), however, which tend to be unpredictable and not necessarily reproducible across different platforms. I worked on a graphics intensive Java based visualization application and I spent a lot of time "writing once, and debugging everywhere".

Also, Object.clone() has some major problems, and its use is discouraged in most cases. Please see Item 11, from "Effective Java" by Joshua Bloch for a complete answer.

There are two undefined behaviors that I know of:

a) Overloading a method with a parameter that is a subclass of the same parameter in the overloaded method. For example:

void doSomething(Object obj);
void doSomething(String str);

There's no way to know which method will be called in doSomething("Hello world!"), since both signatures are valid. Also, this behavior can change from VM to VM, and even from execution to execution.

b) Calling a non-final method of the same class in the constructor. The undefined behavior occurs if this method is overriden in a subclass. Notice that construction occurs from the superclass to the subclass. The case gets specially nasty if the subclass method uses some local subclass attributes. In case of Oracle VM, the local attributes will be constructed, the superclass constructor will finish its execution then, when the constructor of subclass is reached the attributes will be constructed again, overriding previously defined values.

Well defined but not obvious:

Object test for equality:

== is used to test references ( do these two object references point to the same object )

While equals is used to test object equality.

So for instance

new String("test") == new String("test")  

Is false, while

new String("test").equals( new String("test") )

Is true

String objects are interned so the following returns true:

String a = "test";
String b = "test";

a == b  // returns true 

BUT if the string is created somewhere else ( from a database for instance )

String a = "test";
String b = getFromDataBase(); // internally the remote returns "test"

a == b  // returns false.

The validation is false.

I've seen this happening in jsp with scriplets, and new programmers don't get why the validation

 <%if( param == "continue" ) { %>

Never happens

The one thing I remember is regarding jvm compatibility with jni. We had and application that was developed on jdk1.4 and when installing it on a machine with ibm jvm (jikes I believe), the jni call just puked! That was in 2006 though. I believe this has little to do with java as a language, but more to do with java as a platform.

Add dynamic vs. static binding and how it is applied against overloaded and overridden methods as not obvious.

Not undefined but an unexpected behavior is how doubles are rounded when converted to an integer. 0.6d always rounds down to 0; actually 0.9d also rounds down to 0. But 0.99999999999999995 and greater will round up to 1.

Just an interesting behavior when casting the results of a Math.random() call to beaware of.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top