http://www.javacoffeebreak.com/articles/thinkinginjava/comparingc++andjava.html
Comparing C++ and Java
Many developers already have experience with an object-oriented
programming language like C++. As you make the transition to Java, you will
encounter many differences, despite some strong similarities. In this excerpt
from "Thinking in Java", Bruce Eckel highlights the
important differences that C++ programmers should be aware of.
|
As a C++ programmer, you already have the basic idea of object-oriented
programming, and the syntax of Java no doubt looks familiar to you. This makes
sense since Java was derived from C++. However, there are a surprising number
of differences between C++ and Java.
These differences are intended to be significant improvements, and if you
understand the differences you'll see why Java is such a beneficial programming
language. This article takes you through the important features that
distinguish Java from C++.
- The biggest potential
stumbling block is speed: interpreted Java runs in the range of 20 times
slower than C. Nothing prevents the Java language from being compiled and
there are just-in-time compilers appearing at this writing that offer
significant speed-ups. It is not inconceivable that full native compilers
will appear for the more popular platforms, but without those there are
classes of problems that will be insoluble with Java because of the speed
issue.
- Java has both kinds of
comments like C++ does.
- Everything must be in a
class. There are no global functions or global data. If you want the
equivalent of globals, make static methods and static data
within a class. There are no structs or enumerations or unions, only
classes.
- All method definitions are
defined in the body of the class. Thus, in C++ it would look like all the
functions are inlined, but they’re not (inlines are noted later).
- Class definitions are
roughly the same form in Java as in C++, but there’s no closing semicolon.
There are no class declarations of the form class foo, only class
definitions.
6. class aType {
7. void aMethod( ) { /* method body */ }
8. }
- There’s no scope resolution
operator :: in Java. Java uses the dot for everything, but can get
away with it since you can define elements only within a class. Even the
method definitions must always occur within a class, so there is no need
for scope resolution there either. One place where you’ll notice the
difference is in the calling of static methods: you say ClassName.methodName( );.
In addition, package names are established using the dot, and to
perform a kind of C++ #include you use the import keyword.
For example: import java.awt.*;. (#include does not directly
map to import, but it has a similar feel to it).
- Java, like C++, has
primitive types for efficient access. In Java, these are boolean, char,
byte, short, int, long, float, and double.
All the primitive types have specified sizes that are machine independent
for portability. (This must have some impact on performance, varying with
the machine.) Type-checking and type requirements are much tighter in
Java. For example:
1. Conditional expressions can be only boolean, not integral.
2. The result of an expression like X + Y must be used; you can’t just say
"X + Y" for the side effect.
- The char type uses
the international 16-bit Unicode character set, so it can automatically
represent most national characters.
- Static quoted strings are
automatically converted into String objects. There is no
independent static character array string like there is in C and C++.
- Java adds the triple right
shift >>> to act as a "logical" right shift by
inserting zeroes at the top end; the >> inserts the sign bit
as it shifts (an "arithmetic" shift).
- Although they look similar,
arrays have a very different structure and behavior in Java than they do
in C++. There’s a read-only length member that tells you how big
the array is, and run-time checking throws an exception if you go out of
bounds. All arrays are created on the heap,
and you can assign one array to another (the array handle is simply
copied). The array identifier is a first-class object, with all of the
methods commonly available to all other objects.
- All objects of
non-primitive types can be created only via new. There’s no
equivalent to creating non-primitive objects "on the stack" as
in C++. All primitive types can be created only on the stack, without new.
There are wrapper classes for all primitive classes so that you can create
equivalent heap-based objects via new. (Arrays of primitives are a
special case: they can be allocated via aggregate initialization as in
C++, or by using new.)
- No forward declarations are
necessary in Java. If you want to use a class or a method before it is
defined, you simply use it – the compiler ensures that the appropriate
definition exists. Thus you don’t have any of the forward referencing
issues that you do in C++.
- Java has no preprocessor.
If you want to use classes in another library, you say import and
the name of the library. There are no preprocessor-like macros.
- Java uses packages in place
of namespaces. The name issue is taken care of by putting everything into
a class and by using a facility called "packages" that performs
the equivalent namespace breakup for class names. Packages also collect
library components under a single library name. You simply import a
package and the compiler takes care of the rest.
- Object handles defined as
class members are automatically initialized to null. Initialization
of primitive class data members is guaranteed in Java; if you don’t
explicitly initialize them they get a default value (a zero or
equivalent). You can initialize them explicitly, either when you define them
in the class or in the constructor. The syntax makes more sense than that
for C++, and is consistent for static and non-static members
alike. You don’t need to externally define storage for static
members like you do in C++.
- There are no Java pointers
in the sense of C and C++. When you create an object with new, you
get back a reference (which I’ve been calling a handle in this
book). For example:
String s = new
String("howdy");
However, unlike C++ references that must be
initialized when created and cannot be rebound to a different location, Java
references don’t have to be bound at the point of creation. They can also be
rebound at will, which eliminates part of the need for pointers. The other
reason for pointers in C and C++ is to be able to point at any place in memory
whatsoever (which makes them unsafe, which is why Java doesn’t support them).
Pointers are often seen as an efficient way to move through an array of
primitive variables; Java arrays allow you to do that in a safer fashion. The
ultimate solution for pointer problems is native methods (discussed in Appendix
A). Passing pointers to methods isn’t a problem since there are no global
functions, only classes, and you can pass references to objects.
The Java language promoters initially said "No pointers!", but when
many programmers questioned how you can work without pointers, the promoters
began saying "Restricted pointers." You can make up your mind whether
it’s "really" a pointer or not. In any event, there’s no pointer arithmetic.
- Java has constructors that
are similar to constructors in C++. You get a default constructor if you
don’t define one, and if you define a non-default constructor, there’s no
automatic default constructor defined for you, just like in C++. There are
no copy constructors, since all arguments are passed by reference.
- There are no destructors in
Java. There is no "scope" of a variable per se, to indicate when
the object’s lifetime is ended – the lifetime of an object is determined
instead by the garbage collector. There is a finalize( )
method that’s a member of each class, something like a C++ destructor, but
finalize( ) is called by the garbage collector and is supposed
to be responsible only for releasing "resources" (such as open
files, sockets, ports, URLs, etc). If you need something done at a
specific point, you must create a special method and call it, not rely
upon finalize( ). Put another way, all objects in C++ will be
(or rather, should be) destroyed, but not all objects in Java are garbage
collected. Because Java doesn’t support destructors, you must be careful
to create a cleanup method if it’s necessary and to explicitly call all
the cleanup methods for the base class and member objects in your class.
- Java has method overloading
that works virtually identically to C++ function overloading.
- Java does not support
default arguments.
- There’s no goto in
Java. The one unconditional jump mechanism is the break label
or continue label, which is used to jump out of the middle
of multiply-nested loops.
- Java uses a singly-rooted
hierarchy, so all objects are ultimately inherited from the root class Object.
In C++, you can start a new inheritance tree anywhere, so you end up with
a forest of trees. In Java you get a single ultimate hierarchy. This can
seem restrictive, but it gives a great deal of power since you know that
every object is guaranteed to have at least the Object interface.
C++ appears to be the only OO language that does not impose a singly
rooted hierarchy.
- Java has no templates or
other implementation of parameterized types. There is a set of
collections: Vector, Stack, and Hashtable that hold Object
references, and through which you can satisfy your collection needs, but
these collections are not designed for efficiency like the C++ Standard
Template Library (STL). The new collections in Java 1.2 are more complete,
but still don’t have the same kind of efficiency as template
implementations would allow.
- Garbage collection means
memory leaks are much harder to cause in Java, but not impossible. (If you
make native method calls that allocate storage, these are typically not
tracked by the garbage collector.) However, many memory leaks and resource
leaks can be tracked to a badly written finalize( ) or to not
releasing a resource at the end of the block where it is allocated (a
place where a destructor would certainly come in handy). The garbage
collector is a huge improvement over C++, and makes a lot of programming
problems simply vanish. It might make Java unsuitable for solving a small
subset of problems that cannot tolerate a garbage collector, but the
advantage of a garbage collector seems to greatly outweigh this potential
drawback.
- Java has built-in
multithreading support. There’s a Thread class that you inherit to
create a new thread (you override the run( ) method). Mutual
exclusion occurs at the level of objects using the synchronized
keyword as a type qualifier for methods. Only one thread may use a synchronized
method of a particular object at any one time. Put another way, when a synchronized
method is entered, it first "locks" the object against any other
synchronized method using that object and "unlocks" the
object only upon exiting the method. There are no explicit locks; they
happen automatically. You’re still responsible for implementing more
sophisticated synchronization between threads by creating your own
"monitor" class. Recursive synchronized methods work
correctly. Time slicing is not guaranteed between equal priority threads.
- Instead of controlling
blocks of declarations like C++ does, the access specifiers (public,
private, and protected) are placed on each definition for
each member of a class. Without an explicit access specifier, an element
defaults to "friendly," which means that it is accessible to
other elements in the same package (equivalent to them all being C++ friends)
but inaccessible outside the package. The class, and each method within
the class, has an access specifier to determine whether it’s visible
outside the file. Sometimes the private keyword is used less in
Java because "friendly" access is often more useful than
excluding access from other classes in the same package. (However, with
multithreading the proper use of private is essential.) The Java protected
keyword means "accessible to inheritors and to others in this
package." There is no equivalent to the C++ protected keyword
that means "accessible to inheritors only" (private
protected used to do this, but the use of that keyword pair was
removed).
- Nested classes. In C++,
nesting a class is an aid to name hiding and code organization (but C++
namespaces eliminate the need for name hiding). Java packaging provides
the equivalence of namespaces, so that isn’t an issue. Java 1.1 has inner
classes that look just like nested classes. However, an object of an
inner class secretly keeps a handle to the object of the outer class that
was involved in the creation of the inner class object. This means that
the inner class object may access members of the outer class object
without qualification, as if those members belonged directly to the inner
class object. This provides a much more elegant solution to the problem of
callbacks, solved with pointers to members in C++.
- Because of inner classes
described in the previous point, there are no pointers to members in Java.
- No inline methods.
The Java compiler might decide on its own to inline a method, but you
don’t have much control over this. You can suggest inlining in Java by
using the final keyword for a method. However, inline
functions are only suggestions to the C++ compiler as well.
- Inheritance in Java has the
same effect as in C++, but the syntax is different. Java uses the extends
keyword to indicate inheritance from a base class and the super
keyword to specify methods to be called in the base class that have the
same name as the method you’re in. (However, the super keyword in
Java allows you to access methods only in the parent class, one level up
in the hierarchy.) Base-class scoping in C++ allows you to access methods
that are deeper in the hierarchy). The base-class constructor is also
called using the super keyword. As mentioned before, all classes
are ultimately automatically inherited from Object. There’s
no explicit constructor initializer list like in C++, but the compiler
forces you to perform all base-class initialization at the beginning of
the constructor body and it won’t let you perform these later in the body.
Member initialization is guaranteed through a combination of automatic
initialization and exceptions for uninitialized object handles.
35. public class Foo extends Bar
36. {
37. public Foo(String msg) {
38. super(msg); // Calls base constructor
39. }
40.
41. public baz(int i) { // Override
42. super.baz(i); // Calls base method
43. }
44. }
- Inheritance in Java doesn’t
change the protection level of the members in the base class. You cannot
specify public, private, or protected inheritance in
Java, as you can in C++. Also, overridden methods in a derived class
cannot reduce the access of the method in the base class. For example, if
a method is public in the base class and you override it, your
overridden method must also be public (the compiler checks for
this).
- Java provides the interface
keyword, which creates the equivalent of an abstract base class filled
with abstract methods and with no data members. This makes a clear
distinction between something designed to be just an interface and an
extension of existing functionality via the extends keyword. It’s
worth noting that the abstract keyword produces a similar effect in
that you can’t create an object of that class. An abstract class may
contain abstract methods (although it isn’t required to contain any), but
it is also able to contain implementations, so it is restricted to single
inheritance. Together with interfaces, this scheme prevents the need for
some mechanism like virtual base classes in C++.
To create a version of the interface that can be instantiated, use
the implements keyword, whose syntax looks like inheritance:
47. public interface Face {
48. public void smile();
49. }
50.
51. public class Baz extends Bar implements Face {
52. public void smile( ) {
53. System.out.println("a warm smile");
54. }
55. }
- There’s no virtual
keyword in Java because all non-static methods always use dynamic
binding. In Java, the programmer doesn’t have to decide whether to use
dynamic binding. The reason virtual exists in C++ is so you can
leave it off for a slight increase in efficiency when you’re tuning for
performance (or, put another way, "If you don’t use it, you don’t pay
for it"), which often results in confusion and unpleasant surprises.
The final keyword provides some latitude for efficiency tuning – it
tells the compiler that this method cannot be overridden, and thus that it
may be statically bound (and made inline, thus using the equivalent of a
C++ non-virtual call). These optimizations are up to the compiler.
- Java doesn’t provide
multiple inheritance (MI), at least not in the same sense that C++ does.
Like protected, MI seems like a good idea but you know you need it
only when you are face to face with a certain design problem. Since Java
uses a singly-rooted hierarchy, you’ll probably run into fewer situations
in which MI is necessary. The interface keyword takes care of
combining multiple interfaces.
- Run-time type
identification functionality is quite similar to that in C++. To get
information about handle X, you can say, for example:
X.getClass().getName();
To perform a type-safe downcast you say:
derived d =
(derived)base;
just like an old-style C cast. The compiler automatically invokes the
dynamic casting mechanism without requiring extra syntax. Although this
doesn’t have the benefit of easy location of casts as in C++ "new
casts," Java checks usage and throws exceptions so it won’t allow bad
casts like C++ does.
- Exception handling in Java
is different because there are no destructors. A finally clause can
be added to force execution of statements that perform necessary cleanup.
All exceptions in Java are inherited from the base class Throwable,
so you’re guaranteed a common interface.
60.
61. public void f(Obj b) throws IOException {
62. myresource mr = b.createResource();
63. try {
64. mr.UseResource();
65.
66. } catch (MyException e) {
67. // handle my exception
68. } catch (Throwable e) {
69. // handle all other exceptions
70. } finally {
71. mr.dispose(); // special cleanup
72. }
}
- Exception specifications in
Java are vastly superior to those in C++. Instead of the C++ approach of
calling a function at run-time when the wrong exception is thrown, Java
exception specifications are checked and enforced at compile-time. In
addition, overridden methods must conform to the exception specification
of the base-class version of that method: they can throw the specified
exceptions or exceptions derived from those. This provides much more
robust exception-handling code.
- Java has method
overloading, but no operator overloading. The String class does use
the + and += operators to concatenate strings and String expressions
use automatic type conversion, but that’s a special built-in case.
- The const issues in
C++ are avoided in Java by convention. You pass only handles to objects
and local copies are never made for you automatically. If you want the
equivalent of C++’s pass-by-value, you call clone( ) to
produce a local copy of the argument (although the clone( ) mechanism
is somewhat poorly designed – see Chapter 12). There’s no copy-constructor
that’s automatically called.
To create a compile-time constant value, you say, for example:
static
final int SIZE = 255;
static final int BSIZE = 8 * SIZE;
- Because of security issues,
programming an "application" is quite different from programming
an "applet." A significant issue is that an applet won’t let you
write to disk, because that would allow a program downloaded from an
unknown machine to trash your disk. This changes somewhat with Java 1.1
digital signing, which allows you to unequivocally know everyone
that wrote all the programs that have special access to your system (one
of which might have trashed your disk; you still have to figure out which
one and what to do about it.). Java 1.2 also promises more power for
applets
- Since Java can be too
restrictive in some cases, you could be prevented from doing important
tasks such as directly accessing hardware. Java solves this with native
methods that allow you to call a function written in another language
(currently only C and C++ are supported). Thus, you can always solve a
platform-specific problem (in a relatively non-portable fashion, but then
that code is isolated). Applets cannot call native methods, only
applications.
- Java has built-in support
for comment documentation, so the source code file can also contain its
own documentation, which is stripped out and reformatted into HTML via a
separate program. This is a boon for documentation maintenance and use.
- Java contains standard
libraries for solving specific tasks. C++ relies on non-standard
third-party libraries. These tasks include (or will soon include):
- Networking
- Database Connection
(via JDBC)
- Multithreading
- Distributed Objects
(via RMI and CORBA)
- Compression
- Commerce
The availability and standard nature of these
libraries allow for more rapid application development.
- Java 1.1 includes the Java
Beans standard, which is a way to create components that can be used in
visual programming environments. This promotes visual components that can
be used under all vendor’s development environments. Since you aren’t tied
to a particular vendor’s design for visual components, this should result
in greater selection and availability of components. In addition, the
design for Java Beans is simpler for programmers to understand;
vendor-specific component frameworks tend to involve a steeper learning
curve.
- If the access to a Java
handle fails, an exception is thrown. This test doesn’t have to occur
right before the use of a handle; the Java specification just says that
the exception must somehow be thrown. Many C++ runtime systems can also
throw exceptions for bad pointers.
- Generally, Java is more
robust, via:
- Object handles
initialized to null (a keyword)
- Handles are always
checked and exceptions are thrown for failures
- All array accesses
are checked for bounds violations
- Automatic garbage
collection prevents memory leaks
- Clean, relatively
fool-proof exception handling
- Simple language
support for multithreading
- Bytecode verification
of network applets
Bruce Eckel is the author of the highly successful Thinking in Java
and Thinking in C++ books. Thinking in Java is a comprehensive Java
reference, and tutorial for beginner Java programmers. A free, electronic
edition of Thinking in Java is available from his website, and the print
edition is available from all good bookstores (including Amazon.com).
Bruce also conducts hands on training seminars, and offers a free
object-oriented programming newsletter from his website. If you'd like to know
more, or to download a free copy of Thinking in Java, please visit his website
at:- http://www.bruceeckel.com/