University of Virginia, Department of Computer Science
CS655: Programming Languages
Spring 2000

Position Paper 1
Michael Walker
20 January 1999

Why Java is Not My Favorite Programming Language

A newly designed language has recently been the subject of much excitement in the programming world. This language – Java – claims to be safer, more portable, and on the whole, better designed than languages currently used for large-scale applications, and because of these claims, many programmers have been quick to pick up the language in preparation for a newfound demand in Java programmers. The demand, although growing, has not yet reached the level many programmers first anticipated, and the reasons are neither coincidental or trivial. In fact, Java may never be in demand like C and C++ have been for years, because Java is not suitable for many critical programming tasks.

Speed

By its nature, Java is an interpreted language. This means that user code is temporarily compiled into "Java byte code", and does not become executable code until the program is actually run. The most important benefit of interpreted languages like Java is the fact that the code can execute on any computer architecture that is equipped with the Java run-time compiler, thus adding code portability and spawning the popular phrase, "write once, run anywhere."

However, the portability does not come without a price. The need to compile code at run time causes a tremendous slow-down in execution speed. Java doesn’t improve on the execution speed of most older languages; in fact, Java doesn’t even come close.

Some proponents of Java argue that execution time is less of an issue these days, as processor speeds continue to double every 18 or so months, in synchronization with Moore’s law. This is a ludicrous claim—speed always has been and always will be an issue in programming languages. As long as processor speeds double, there will be an application that will take full advantage of the new power in processing computations. Additionally, the current trend towards embedded applications gives more reason for quickly executable code, as most embedded processors will not necessarily match the speed and power of today’s quickest CPU’s.

I cannot deny that there are places where interpreted code is more appropriate for the situation. When speed is not an issue, and portability is key, there is nothing wrong with using Java, or one of many other preexisting interpreted languages for the task. However, the severe decrease in performance should be enough for many serious applications programmers to steer clear of interpreted languages.

Object-Oriented Programming

No one can deny the value of Object-Oriented programming (OOP) in its contribution to programming methods. OOP encourages all the fundamental coding practices that make large-scale applications programming possible (encapsulation, data hiding, etc.). However, Java’s take on OOP differs in significant ways from its predecessors, and some of these differences introduce fundamental problems in Java.

Java supports only a limited version of class inheritance, and this limitation does more than just create confusion; it forces Java to lose generality. Firstly, if a class is to inherit from a base class, the base class must be declared "abstract". However, in large-scale applications, it is often cumbersome to keep tabs on which classes have been declared as such, and this can reduce readability and code reuse significantly.

Furthermore, Java does not support any type of multiple inheritance. This is a fundamental design decision that limits OOP and Object-Oriented design (OOD) for programmers, and reduces Java’s generality.

Operator overloading is not possible for user-defined classes in Java. Again, readability is hindered when, for instance, the summation of an enumeration or record type must be calculated from a summation of each element in the list, rather than with an overloaded "+" operator.

Finally, Java class implementation cannot be separated from class specification. Packaging both together goes against a sound principle of OOD: information hiding. A library user may not want have to know the implementation to understand how to use the library’s classes, but without a specification file, the use of these classes may not be apparently clear. Even worse, the implementation might be proprietary information that needs to be hid from its users. A company might value their implementation as code that should remain in-house, but there is no easy way in Java to supply the class specifications without including the implementation with it.

Garbage Collection

One of the most popular claims of Java proponents is that Java eliminates the use of pointers. This statement is partly true and partly mistaken. It is true that the programmer does not have the ability to explicitly use pointer indirection to manipulate variables by their address in memory. However, Java uses memory addressing for objects and variables all the time; this referencing is simply abstracted away from the programmer, rather than remaining exposed. In turn, the memory allocation is abstracted away from the programmer as well, and is instead handled by a built-in garbage collector.

Garbage collectors are not entirely new; in fact, there are garbage collector libraries freely available for C++. However, Java included garbage collection as part of its core language, and by doing so, has introduced a fundamental flaw in its language design.

Firstly, garbage collection greatly increases the overhead of maintaining allocated memory. Each cell in the heap must include an extra indicator bit for the garbage collection algorithm, requiring more space in memory [1]. Additionally, the garbage collector must trace every pointer in the program back to the heap, to mark the heap as used (non-garbage) storage. Thus, garbage collection requires more space and time to implement.

However, the Java garbage collector does more than just increase time and space requirements—it adds the element of unpredictable performance in program code. For reasons beyond the programmer’s control, the garbage collector may choose to begin performing its costly collection operations at any time during the execution of the program. This is not a problem for code without performance requirements, but it makes consistent performance impossible to predict. Consider the effect this has on real-time computing applications: how could you possibly guarantee a deadline will be met when the timeliness of garbage collection is nondeterministic? Thus, Java is completely unsuitable for any type of real-time computing.

Some people are willing to grant Java code this unpredictable behavior, and receive increased safety in memory references in return. Java won’t allow programmers to misuse pointers because it doesn’t allow pointers in the first place. This is good for beginning programmers who make memory referencing errors, but not for application programmers who demand serious performance in their running apps. With all its safety features, perhaps Java is best suited to serve as an instructional language for beginning programmers, replacing Pascal in that respect [2], and it should leave the programming of large-scale, reliable code to others (*).

 

[1] Sebesta, Robert. Concepts of Programming Languages. Reading: Addison-Wesley, 1999, p. 250-251.

[2] Kernighan, Brian W. "Why Pascal is Not My Favorite Programming Language", AT&T Bell Laboratories, 1981.

* The opinions expressed in this paper are somewhat exaggerated to make a point, but I have to admit (at least) that the absence of pointers in Java can be an added security feature useful to more than just beginning programmers.