Java programming language was intended to serve as a novel way to manage software complexity. Many consider Java technology to deliver reasonably well on this promise. However, Java is not without flaws, and it does not universally accommodate all programming styles, environments, or requirements.

Class path

Running a Java program requires all third party supporting libraries to be in the class path. This can be an obstacle to portability, because its syntax is platform-specific: Windows-based systems use backslashes to mark subdirectories and semicolons to separate entries, whereas all other platforms use forward slashes to mark subdirectories and colons to separate entries.

It is also necessary for each .jar or .zip archive required to be explicitly named in the class path. Java provides a way around this by allowing directories listed in the class path to end in an asterisk (*), which will be expanded to the names of all files ending in .jar or .JAR within the directory. However, such an entry does not match .zip or .class files within that directory.


Sun Java's proprietary nature gave it a controversial position in the free software community. Because Sun's implementation of Java was not free software, it could not be included in projects that require a free software or GPL-compatible license, such as Debian main, the $100 laptop, and Fedora Core. Sun announced in JavaOne 2006 that Java will become open source software. The statement was issued by Sun Software Executive Vice President Rich Green: "It's not a question of whether, it's a question of how, and so we'll go do this."

In July 2006, Sun's CTO Robert Brewin commented that Java will be partially open source by June 2007 but the entire platform will take more time to be fully open source. On November 13, 2006, Sun announced that its standard edition Java runtime environment will be released under the GPL by March of 2007. Its source code will be available under the GPL. According to Richard Stallman, this will mean an end to the Java trap. Mark Shuttleworth called the initial press announcement "a real milestone for the free software community."

Resource management

While Java does manage memory, it does not manage all resources, such as JDBC database connections; these must be released just as memory would need to be in C++.

Memory management

Java takes care of memory management. This was done as it makes it harder (but not impossible) for the programmer to create problems such as memory leaks. Java always allocates objects on the heap (unless optimized to the stack or registers by the JIT compiler) and local variables on the stack or in registers. This makes Java less flexible than C++, which allows programmers to choose where objects are allocated.

The garbage collector controls when objects are deleted from memory. Java does not allow programmers to guarantee when garbage collection happens (even with System.gc()), they cannot hold off garbage collection, and they cannot delete one particular object. While this makes programming much simpler and reduces memory leaks, it lacks the flexibility that can, in some cases, result in a more efficient handling of memory. Lower-level languages such as C or assembly language provide this flexibility.

While many programs, such as those written in C++, tend to fall prey to memory leaks, this is not the whole story. Other resource leaks such as file handles, database and network connections are still likely, especially when exceptions are thrown. However, where C++ has the RAII idiom to address both cases, Java programmers need to remember to release resources in finally clauses and must have a good understanding of which resources Java will release and which they must release.

Primitives vs. objects / Autoboxing

Java designers decided not to implement certain features present in other languages (including multiple inheritance, operator overloading, and tuples).

When generics were added to Java 5.0, there was already a large framework of classes (many of which were already deprecated), so generics were chosen to be implemented using erasure to allow backwards compatibility and preservation of these existing classes. This limited the features that could be provided by this addition as compared to other languages.

Java's primitive types are not objects. Primitive types hold their values in the stack rather than being references to values. This was done for performance reasons. Because of this, Java is not considered to be a pure object-oriented programming language and this makes reflection more complicated. However, Java 5.0 supports automatic conversion (autoboxing) of primitive data types to corresponding object form wherever required, during compilation. When autounboxing, a null pointer exception may be thrown. Since this operation occurs implicitly (without a cast or method call), this unchecked exception may not be obvious by inspection of the line of code.

Non-Virtual methods

Java provides no way to make methods non-virtual (although they can be "sealed" by using the final modifier to disallow overriding). This means that there is no way to let derived classes define a new, unrelated method with the same name. This can be a problem when a base class is designed by a different person, and a new version introduces a method with the same name and signature as some method already present in the derived class. This means that the method in the derived class will implicitly override the method in the base class, even though that was not the intent of the designers of either class. To partially accommodate for these versioning problems, Java 5.0 introduced the @Override annotation, but to preserve backwards compatibility it could not be made compulsory by default.

Single paradigm

Java is predominantly a single-paradigm language. The addition of static imports in Java 5.0 accommodates the procedural paradigm better than earlier versions of Java.

Exception handling

Java embraced the concept of exception specifications from C++ where they were optional, but made throws clauses mandatory for any checked exception. While this can be a benefit for small systems, there is not universal agreement that using checked exceptions is a benefit for larger systems. In particular, "higher level" code is often not interested in errors thrown by "lower level" code (eg: NamingException). The coder of the naming classes must make a choice: either force higher level code to deal with naming exceptions as checked exceptions, or allow them to "bubble up" through his own low-level code without compile-time checks.


Finally, while anonymous inner classes provide a basic form of closures, they are not complete and require referenced variables to either be class fields or declared "final". The rationale behind this is that it allows JVM implementors to choose a stack model for variable lifetimes, so that a variable scope is removed when exited, thus preventing real closures. In addition, when using - for instance - "Runnable" as a closure, one has to declare the "run" method and put the code in that: you cannot simply put some code in braces and pass it around.

Floating point arithmetic

While Java's floating point arithmetic is largely based on IEEE 754 (Standard for Binary Floating-Point Arithmetic), certain features are not supported even when using the "strictfp" modifier, such as Exception Flags and Directed Roundings — capabilities mandated by IEEE Standard 754. Many so-called "Java gotchas" are not problems with Java per se, but problems that are inevitable whenever using floating point arithmetic.

Look and feel

The look and feel of GUI applications written in Java using the Swing platform is often different from native applications. While programmers can choose to use the AWT toolkit that displays native widgets (and thus look like the operating platform), the AWT toolkit is unable to meet advanced GUI programming needs by wrapping around advanced widgets and not sacrificing portability across the various supported platforms, each of which have vastly different APIs especially for higher-level widgets.

The Swing toolkit--written completely in Java--both creates the problem of having a different look and feel from native applications, and avoids the problem of being limited by native toolkit capabilities because it reimplements widgets using only the most basic drawing mechanisms that are guaranteed available on all platforms. Unfortunately, the default installations of the JRE (as of August 2006) do not use the system's "native" look and feel, instead defaulting to the built-in Metal Look and Feel. If the programmer doesn't take care to set the native look and feel, users will have applications whose appearance is vastly different from that of their native applications. Apple Computer's own optimized version of the Java Runtime, which is included within the Mac OS X distribution, by default does set the default and implements its "Aqua" look-and-feel, giving Swing applications on the Macintosh a similar appearance to native software. Even in this environment, the programmer must still do some extra work to ensure that that application looks like an Aqua one (for example, they must set system properties to ensure the menubar is rendered in the OS X menubar and not in the application window as it would be on other platforms).


It is impossible to make any generalization about the performance of Java programs, because runtime performance is affected much more by the quality of the compiler or JVM than by any intrinsic properties of the language itself. Java bytecode can either be interpreted at run time by a virtual machine, or it can be compiled at load time or runtime into machine code which runs directly on the computer's hardware. Interpretation is slower than native execution, and compilation at load time or runtime has an initial performance penalty for the compilation.

Lack of language features

There are a few language requirements which incur an unavoidable time penalty, although these features are not unique to Java. Among these are array bounds checking, run-time type checking, and virtual function indirection (although each of these can in some situations be avoided by an optimizing compiler). Also the lack of features can affect performance. For example, Java does not have arrays of structures or a true multi-dimensional array, but only an array of references to objects or further arrays. Nor does Java allow returning more than one value from a function without using an object. The net result is that Java code makes more heap allocations than well-written code in some other languages.

Garbage collection

The use of a garbage collector to automatically delete objects adds overhead compared to manual deallocation and can have a positive or negative impact, or no discernible impact at all, on performance depending upon the garbage collector implementation and the characteristics of the application's use of objects. With the modern generational garbage collectors used in many JVMs, many applications actually experience greater performance because of faster allocation and deallocation algorithms.

Byte code vs. native compilation

Relative performance of JIT compilers as compared to native compilers can be quite close, and is often a subject of debate. The JIT compilation stage may be time consuming, which is inconvenient for applications that are short-lived and/or contain large amounts of code. Once compiled to native code, however, the performance of the program can be comparable to that achieved by a native compiler, even on numerical tasks. Although Java does not support manual inlining of method calls, many JIT compilers perform this optimization at load time and can exploit information from the runtime environment to guide more effective transformations, such as profile-directed inlining. Dynamic recompilation, as provided by Sun's HotSpot JVM, can exceed the performance of the static compilation available in most other languages by exploiting information that is only available at runtime.

Hardware interfacing

Because Java was designed with an emphasis on security and portability, it does not support direct access to the machine architecture and address space. This means working directly with a specific piece of hardware such as a scanner, digital camera, audio recorder, video capture, or virtually any hardware that requires direct memory space control (typically those pieces or hardware installed with drivers), cannot easily be accomplished with Java. An illustration of this issue is seen in version 1.0 of Java as it was not possible to access a printer because the interface code to the various printer drivers was not included in this first JVM.

Interfacing with native code

Clients side or server systems that need to "talk" to the hardware must implement a hybrid solution using Java and C/C++ or assembly language via the Java Native Interface (JNI) libraries to link native code to the Java libraries. An alternate solution is to code the hardware software component in its native C/C++/assembler language and then pass the data via files, databases or a shared memory interface, although this is not an ideal solution.

Using the JNI technique introduces many possible inconsistencies such as: machine dependency, potential deadlock situations, memory allocation leaks, and possibly poor application performance, not to mention code complexity of needing to maintain two different code bases. However, it must be noted that it is a common case for other Virtual machine languages, as for example the .NET Framework Common Language Runtime (see Platform Invocation Services).

Inconsistent JVM implementations

Java is a bytecode language that runs on top of the JVM; ultimately the compatibility of the language and the ability to have it run across different platforms is dependent on the stability and version of the JVM. While Java is touted as running on a large variety of systems, the most up to date JVM (and JRE) are only those actively updated for Windows, Linux and Solaris. HP (such as Java for HP-UX) and IBM (for MVS, AIX, OS/400) provide their own implementations for their family of platforms but do not always mirror the latest Sun releases. Other JVM implementations usually follow, but sometimes lag in months or years with the more common implementations and therefore introduce compatibility issues.

Above article originally from Above article is available under GNU Free Documentation License.