While Java is a very practical language, used nowadays to implement a large array of software applications, it also has its own share of problems. I am not the first and won’t be the last person to touch on the many issues raised by its design, especially in the areas of syntax and support for non-OO concepts.
While design problems are an interesting issue for technical discussion (especially if you are a programming aficionado), it is clearly not the main reason why someone would decide to use a language or not. Mostly, you have to answer pragmatic considerations such as “do you know how to use the language well?” and “does the language provide support for the type of problems I want to solve?”
Despite this, here I will consider only two of the design problems in Java that I haven’t seen discussed in other places.
Safety versus Performance
The first problem is related to the security focus of the language. As Java was initially sold as the language of the Internet, the fact that it was designed for safety looks like a very important issue. After all, nobody wants to download and install software that is unsafe and untrusted.
The mistake of Java, however, is that it tries to achieve safety at the level of byte-code execution, thinking that this is the most important aspect of security – or at least so important that it would be able to fix most of the security issues we might encounter in a networked world. For example, every time you load a Java class (or set of classes), the runtime mechanism has to spend a good amount of time trying to determine if the code is safe or not.
Security checks at the byte-code level take time and effort from the part of the runtime system. The big problem with this, however, is that only a small fraction of all code written in Java really needs the security checks that are in place. For example, even if you’re writing code that will never be in contact with the Internet, you still need to pay the price for this heightened level of security when using the Java runtime. The unintended consequence of such decisions is that the security system exists mostly to slow down the execution of programs, while complicating the class loading process.
Moreover, it is now understood that security at the programming language level is of very little importance for the safety of the overall system. If a malicious programmer wants to do something bad through a downloaded application, there are thousands of avenues that can be pursued, for example, by just getting enough information directly from the user. In most cases this can be done without even having to bother executing unsafe code.
That is why modern mobile applications are digitally signed: to make sure you know its origin. Trust, in the digital world as everywhere else, comes from knowing who created the code. It is part of a reputation system, not simply result of the analysis of the code itself. Moreover, with all the security measures imagined by its designers, Java turned out to become a big vector of malware infection. So much for the supposed safety offered by the language.
Over-Engineering the JVM
Another important innovation of Java is that it made the JVM, its virtual machine, the center of attentions. Unlike other languages that consider the description of the virtual machine simply as a detail that is hidden from users, Java decided to make it a public requirement for implementers, in order to guarantee the compatibility of code written at the JVM level and provide the Java-compatible seal.
While there are interesting advantages in using a well-defined set of operations interpreted by a virtual machine (for example, the ability to transfer compiled code over different architectures), the end result of this type of low-level focus is to bring unnecessary attention to specific areas, in detriment to the overall needs of the language. Java is nowadays certainly paying the price for these early decisions.
The same has been true for other interpreted languages, such as Smalltalk. But this natural evolution of the op-codes and other internal representations of compiled code cannot be easily achieved in Java, because the definition of the JVM over-specifies its execution mechanism. For example, it determines the exact representation of the compiled byte-codes. Therefore, even if a better way of representing and executing Java programs becomes available, it cannot be used by compliant virtual machines.
About the Author
Carlos Oliveira is a software engineer and the author of Objective-C Programmers Reference.
Here are some other books on this topic:
The Pragmatic Programmer: From Journeyman to Master