epubjs
Version:
Render ePub documents in the browser, across many devices
222 lines (221 loc) • 24.1 kB
HTML
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Safety of Design</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Safety of Design"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-1-SECT-4"/>Safety of Design</h1></div></div></div><p><a id="I_indexterm1_id633427" class="indexterm"/> <a id="idx10027" class="indexterm"/> <a id="I_indexterm1_id633444" class="indexterm"/> <a id="I_indexterm1_id633450" class="indexterm"/>You have no doubt heard a lot about the fact that Java is
designed to be a safe language. But what do we mean by safe? Safe from
what or whom? The security features that attract the most attention for
Java are those features that make possible new types of dynamically
portable software. Java provides several layers of protection from
dangerously flawed code as well as more mischievous things such as viruses
and Trojan horses. In the next section, we’ll take a look at how the Java
virtual machine architecture assesses the safety of code before it’s run
and how the Java <span class="emphasis"><em>class loader</em></span> (the bytecode loading
mechanism of the Java interpreter) builds a wall around untrusted classes.
These features provide the foundation for high-level security policies
that can allow or disallow various kinds of activities on an
application-by-application basis.</p><p>In this section, though, we’ll look at some general features of the
Java programming language. Perhaps more important than the specific
security features, although often overlooked in the security din, is the
safety that Java provides by addressing common design and programming
problems. Java is intended to be as safe as possible from the simple
mistakes we make ourselves as well as those we inherit from legacy
software. The goal with Java has been to keep the language simple, provide
tools that have demonstrated their usefulness, and let users build more
complicated facilities on top of the language when needed.</p><div class="sect2" title="Simplify, Simplify, Simplify..."><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.1"/>Simplify, Simplify, Simplify...</h2></div></div></div><p><a id="I_indexterm1_id633487" class="indexterm"/> <a id="I_indexterm1_id633494" class="indexterm"/> <a id="I_indexterm1_id633500" class="indexterm"/> <a id="I_indexterm1_id633507" class="indexterm"/> <a id="idx10032" class="indexterm"/> <a id="idx10040" class="indexterm"/> <a id="idx10044" class="indexterm"/>With Java, simplicity rules. Since Java started with a
clean slate, it was able to avoid features that proved to be messy or
controversial in other languages. For example, Java doesn’t allow
programmer-defined operator overloading (which in some languages allows
programmers to redefine the meaning of basic symbols like + and –). Java
doesn’t have a source code preprocessor, so it doesn’t have things like
macros, <a id="I_indexterm1_id633560" class="indexterm"/><code class="literal">#define</code> statements, or
conditional source compilation. These constructs exist in other
languages primarily to support platform dependencies, so in that sense,
they should not be needed in Java. Conditional compilation is also
commonly used for debugging, but Java’s sophisticated runtime optimizations and
features such as <a id="I_indexterm1_id633583" class="indexterm"/><span class="emphasis"><em>assertions</em></span> solve the problem more
elegantly (we’ll cover these in <a class="xref" href="ch04.html" title="Chapter 4. The Java Language">Chapter 4</a>).</p><p>Java provides a well-defined <a id="I_indexterm1_id633602" class="indexterm"/><span class="emphasis"><em>package</em></span> structure for organizing
class files. The package system allows the compiler to handle some of
the functionality of the traditional <a id="I_indexterm1_id633614" class="indexterm"/><span class="emphasis"><em>make</em></span> utility (a tool for building
executables from source code). The compiler can also work with compiled
Java classes directly because all type information is preserved; there
is no need for extraneous source “header” files, as in C/C++. All this
means that Java code requires less context to read. Indeed, you may
sometimes find it faster to look at the Java source code than to refer
to class documentation.</p><p><a id="I_indexterm1_id633631" class="indexterm"/>Java also takes a different approach to some structural
features that have been troublesome in other languages. For example,
Java supports only a single inheritance class hierarchy (each class may
have only one “parent” class), but allows multiple inheritance of
interfaces. An <a id="I_indexterm1_id633646" class="indexterm"/><span class="emphasis"><em>interface</em></span>, like an abstract class in
C++, specifies the behavior of an object without defining its
implementation. It is a very powerful mechanism that allows the
developer to define a “contract” for object behavior that can be used
and referred to independently of any particular object implementation.
Interfaces in Java eliminate the need for multiple inheritance of
classes and the associated problems.</p><p>As you’ll see in <a class="xref" href="ch04.html" title="Chapter 4. The Java Language">Chapter 4</a>, Java is a
fairly simple and elegant programming language and that is still a large
part of its appeal.<a id="I_indexterm1_id633672" class="indexterm"/><a id="I_indexterm1_id633680" class="indexterm"/><a id="I_indexterm1_id633687" class="indexterm"/></p></div><div class="sect2" title="Type Safety and Method Binding"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.2"/>Type Safety and Method Binding</h2></div></div></div><p><a id="idx10004" class="indexterm"/> <a id="idx10023" class="indexterm"/> <a id="idx10034" class="indexterm"/> <a id="idx10042" class="indexterm"/> <a id="I_indexterm1_id633754" class="indexterm"/> <a id="idx10046" class="indexterm"/>One attribute of a language is the kind of <span class="emphasis"><em>type
checking</em></span> it uses. Generally, languages are categorized as
<a id="I_indexterm1_id633774" class="indexterm"/><span class="emphasis"><em>static</em></span> or <a id="I_indexterm1_id633785" class="indexterm"/><span class="emphasis"><em>dynamic</em></span>, which refers to the amount
of information about variables known at compile time versus what is
known while the application is running.</p><p><a id="I_indexterm1_id633796" class="indexterm"/> <a id="I_indexterm1_id633805" class="indexterm"/>In a strictly statically typed language such as C or C++,
data types are etched in stone when the source code is compiled. The
compiler benefits from this by having enough information to catch many
kinds of errors before the code is executed. For example, the compiler
would not allow you to store a floating-point value in an integer
variable. The code then doesn’t require runtime type checking, so it can
be compiled to be small and fast. But statically typed languages are
inflexible. They don’t support collections as naturally as languages
with dynamic type checking, and they make it impossible for an
application to safely import new data types while it’s running.</p><p><a id="I_indexterm1_id633826" class="indexterm"/> <a id="I_indexterm1_id633832" class="indexterm"/>In contrast, a dynamic language such as Smalltalk or Lisp
has a runtime system that manages the types of objects and performs
necessary type checking while an application is executing. These kinds
of languages allow for more complex behavior and are in many respects
more powerful. However, they are also generally slower, less safe, and
harder to debug.</p><p>The differences in languages have been likened to the differences
among kinds of automobiles.<sup>[<a id="learnjava3-CHP-1-FNOTE-2" href="#ftn.learnjava3-CHP-1-FNOTE-2" class="footnote">2</a>]</sup> Statically typed languages such as C++ are analogous to a
sports car: reasonably safe and fast, but useful only if you’re driving
on a nicely paved road. Highly dynamic languages such as Smalltalk are
more like an off-road vehicle: they afford you more freedom but can be
somewhat unwieldy. It can be fun (and sometimes faster) to go roaring
through the backwoods, but you might also get stuck in a ditch or mauled
by bears.</p><p><a id="I_indexterm1_id633861" class="indexterm"/> <a id="I_indexterm1_id633870" class="indexterm"/>Another attribute of a language is the way it binds method
calls to their definitions. In a static language such as C or C++, the
definitions of methods are normally bound at compile time, unless the
programmer specifies otherwise. Languages like Smalltalk, on the other
hand, are called <span class="emphasis"><em>late binding</em></span> because they locate
the definitions of methods dynamically at runtime. Early binding is
important for performance reasons; an application can run without the
overhead incurred by searching for methods at runtime. But late binding
is more flexible. It’s also necessary in an object-oriented language
where new types can be loaded dynamically and only the runtime system
can determine which method to run.</p><p>Java provides some of the benefits of both C++ and Smalltalk; it’s
a statically typed, late-binding language. Every object in Java has a
well-defined type that is known at compile time. This means the Java
compiler can do the same kind of static type checking and usage analysis
as C++. As a result, you can’t assign an object to the wrong type of
variable or call nonexistent methods on an object. The Java compiler
goes even further and prevents you from using uninitialized variables
and creating unreachable statements (see <a class="xref" href="ch04.html" title="Chapter 4. The Java Language">Chapter 4</a>).</p><p>However, Java is fully runtime-typed as well. The Java runtime
system keeps track of all objects and makes it possible to determine
their types and relationships during execution. This means you can
inspect an object at runtime to determine what it is. Unlike C or C++,
casts from one type of object to another are checked by the runtime
system, and it’s possible to use new kinds of dynamically loaded objects
with a degree of type safety. And because Java is a late binding
language, it’s always possible for a subclass to override methods in its
superclass, even a subclass loaded at runtime.<a id="I_indexterm1_id633915" class="indexterm"/><a id="I_indexterm1_id633922" class="indexterm"/><a id="I_indexterm1_id633929" class="indexterm"/><a id="I_indexterm1_id633936" class="indexterm"/><a id="I_indexterm1_id633944" class="indexterm"/></p></div><div class="sect2" title="Incremental Development"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.3"/>Incremental Development</h2></div></div></div><p><a id="idx10015" class="indexterm"/> <a id="idx10030" class="indexterm"/> <a id="idx10039" class="indexterm"/> Java carries all data type and method signature
information with it from its source code to its compiled bytecode form.
This means that Java classes can be developed incrementally. Your own
Java source code can also be compiled safely with classes from other
sources your compiler has never seen. In other words, you can write new
code that references binary class files without losing the type safety
you gain from having the source code.</p><p><a id="I_indexterm1_id634001" class="indexterm"/> <a id="I_indexterm1_id634010" class="indexterm"/>Java does not suffer from the “fragile base class”
problem. In languages such as C++, the implementation of a base class
can be effectively frozen because it has many derived classes; changing
the base class may require recompilation of all of the derived classes.
This is an especially difficult problem for developers of class
libraries. Java avoids this problem by dynamically locating fields
within classes. As long as a class maintains a valid form of its
original structure, it can evolve without breaking other classes that
are derived from it or that make use of it.<a id="I_indexterm1_id634022" class="indexterm"/><a id="I_indexterm1_id634029" class="indexterm"/><a id="I_indexterm1_id634036" class="indexterm"/></p></div><div class="sect2" title="Dynamic Memory Management"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.4"/>Dynamic Memory Management</h2></div></div></div><p><a id="idx10009" class="indexterm"/> <a id="idx10012" class="indexterm"/> <a id="idx10022" class="indexterm"/> <a id="idx10028" class="indexterm"/> <a id="idx10038" class="indexterm"/>Some of the most important differences between Java and
lower-level languages such as C and C++ involve how Java manages memory.
Java eliminates ad hoc “pointers” that can reference arbitrary areas of
memory and adds object garbage collection and high-level arrays to the
language. These features eliminate many otherwise insurmountable
problems with safety, portability, and optimization.</p><p>Garbage collection alone has saved countless programmers from the
single largest source of programming errors in C or C++: explicit memory
allocation and deallocation. In addition to maintaining objects in
memory, the Java runtime system keeps track of all references to those
objects. When an object is no longer in use, Java automatically removes
it from memory. You can, for the most part, simply ignore objects you no
longer use, with confidence that the interpreter will clean them up at
an appropriate time.</p><p><a id="I_indexterm1_id634130" class="indexterm"/>Java uses a sophisticated garbage collector that runs in
the background, which means that most garbage collecting takes place
during idle times, between I/O pauses, mouse clicks, or keyboard hits.
Advanced runtime systems, such as HotSpot, have more advanced garbage
collection that can differentiate the usage patterns of objects (such as
short-lived versus long-lived) and optimize their collection. The Java
runtime can now tune itself automatically for the optimal distribution
of memory for different kinds of applications based on their behavior.
With this kind of runtime profiling, automatic memory management can be
much faster than the most diligently programmer-managed resources,
something that some old-school programmers still find hard to
believe.<a id="I_indexterm1_id634143" class="indexterm"/></p><p>We’ve said that Java doesn’t have pointers. Strictly speaking,
this statement is true, but it’s also misleading. What Java provides are
<span class="emphasis"><em>references</em></span>—a safe kind of pointer. A reference is a
strongly typed handle for an object. All objects in Java, with the
exception of primitive numeric types, are accessed through references.
You can use references to build all the normal kinds of data structures
a C programmer would be accustomed to building with pointers, such as
linked lists, trees, and so forth. The only difference is that with
references, you have to do so in a typesafe way.</p><p>Another important difference between a reference and a pointer is
that you can’t play games (perform pointer arithmetic) with references
to change their values; they can point only to specific objects or
elements of an array. A reference is an atomic thing; you can’t
manipulate the value of a reference except by assigning it to an object.
References are passed by value, and you can’t reference an object
through more than a single level of indirection. The protection of
references is one of the most fundamental aspects of Java security. It
means that Java code has to play by the rules; it can’t peek into places
it shouldn’t and circumvent the rules.</p><p><a id="I_indexterm1_id634176" class="indexterm"/>Java references can point only to class types. There are
no pointers to methods. People sometimes complain about this missing
feature, but you will find that most tasks that call for pointers to
methods can be accomplished more cleanly using interfaces and adapter
classes instead. We should also mention that Java has a sophisticated
Reflection API that actually allows you to reference and invoke
individual methods. However, this is not the normal way of doing things.
We discuss reflection in <a class="xref" href="ch07.html" title="Chapter 7. Working with Objects and Classes">Chapter 7</a>.</p><p><a id="I_indexterm1_id634195" class="indexterm"/>Finally, we should mention that arrays in Java are true,
first-class objects. They can be dynamically allocated and assigned like
other objects. Arrays know their own size and type, and although you
can’t directly define or subclass array classes, they do have a
well-defined inheritance relationship based on the relationship of their
base types. Having true arrays in the language alleviates much of the
need for pointer arithmetic, such as that used in C or C++.<a id="I_indexterm1_id634215" class="indexterm"/><a id="I_indexterm1_id634222" class="indexterm"/><a id="I_indexterm1_id634229" class="indexterm"/><a id="I_indexterm1_id634236" class="indexterm"/></p></div><div class="sect2" title="Error Handling"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.5"/>Error Handling</h2></div></div></div><p><a id="idx10010" class="indexterm"/> <a id="idx10029" class="indexterm"/> <a id="I_indexterm1_id634272" class="indexterm"/>Java’s roots are in networked devices and embedded
systems. For these applications, it’s important to have robust and
intelligent error management. Java has a powerful exception handling
mechanism, somewhat like that in newer implementations of C++.
Exceptions provide a more natural and elegant way to handle errors.
Exceptions allow you to separate error handling code from normal code,
which makes for cleaner, more readable applications.</p><p>When an exception occurs, it causes the flow of program execution
to be transferred to a predesignated “catch” block of code. The
exception carries with it an object that contains information about the
situation that caused the exception. The Java compiler requires that a
method either declare the exceptions it can generate or catch and deal
with them itself. This promotes error information to the same level of
importance as argument and return types for methods. As a Java
programmer, you know precisely what exceptional conditions you must deal
with, and you have help from the compiler in writing correct software
that doesn’t leave them unhandled.<a id="I_indexterm1_id634298" class="indexterm"/></p></div><div class="sect2" title="Threads"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.6"/>Threads</h2></div></div></div><p><a id="I_indexterm1_id634311" class="indexterm"/> <a id="idx10033" class="indexterm"/> <a id="idx10041" class="indexterm"/> <a id="idx10045" class="indexterm"/>Modern applications require a high degree of parallelism.
Even a very single-minded application can have a complex user
interface—which requires concurrent activities. As machines get faster,
users become more sensitive to waiting for unrelated tasks that seize
control of their time. Threads provide efficient multiprocessing and
distribution of tasks for both client and server applications. Java
makes threads easy to use because support for them is built into the
language.</p><p>Concurrency is nice, but there’s more to programming with threads
than just performing multiple tasks simultaneously. In most cases,
threads need to be <a id="I_indexterm1_id634370" class="indexterm"/><span class="emphasis"><em>synchronized</em></span> (coordinated), which can
be tricky without explicit language support. Java supports synchronization based on the <a id="I_indexterm1_id634385" class="indexterm"/><span class="emphasis"><em>monitor</em></span> and
<span class="emphasis"><em>condition</em></span> model—a sort of lock and key system for
accessing resources. The keyword <code class="literal">synchronized</code> designates methods and blocks of
code for safe, serialized access within an object. There are also
simple, primitive methods for explicit waiting and signaling between
threads interested in the same object.</p><p><a id="I_indexterm1_id634406" class="indexterm"/>Java also has a high-level concurrency package that
provides powerful utilities addressing common patterns in multithreaded
programming, such as thread pools, coordination of tasks, and
sophisticated locking. With the addition of the concurrency package and
related utilities, Java provides some of the most advanced
thread-related utilities of any language.</p><p>Although some developers may never have to write multithreaded
code, learning to program with threads is an important part of mastering
programming in Java and something all developers should grasp. See <a class="xref" href="ch09.html" title="Chapter 9. Threads">Chapter 9</a> for a discussion of this topic.<a id="I_indexterm1_id634425" class="indexterm"/><a id="I_indexterm1_id634432" class="indexterm"/><a id="I_indexterm1_id634439" class="indexterm"/></p></div><div class="sect2" title="Scalability"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-1-SECT-4.7"/>Scalability</h2></div></div></div><p><a id="idx10031" class="indexterm"/> <a id="I_indexterm1_id634466" class="indexterm"/> <a id="I_indexterm1_id634472" class="indexterm"/>At the lowest level, Java programs consist of
<span class="emphasis"><em>classes</em></span>. Classes are intended to be small, modular
components. Over classes, Java provides <span class="emphasis"><em>packages</em></span>, a
layer of structure that groups classes into functional units. Packages
provide a naming convention for organizing classes and a second tier of
organizational control over the visibility of variables and methods in
Java applications.</p><p>Within a package, a class is either publicly visible or protected
from outside access. Packages form another type of scope that is closer
to the application level. This lends itself to building reusable
components that work together in a system. Packages also help in
designing a scalable application that can grow without becoming a bird’s
nest of tightly coupled code.<a id="I_indexterm1_id634502" class="indexterm"/><a id="I_indexterm1_id634509" class="indexterm"/></p></div><div class="footnotes"><br/><hr/><div class="footnote"><p><sup>[<a id="ftn.learnjava3-CHP-1-FNOTE-2" href="#learnjava3-CHP-1-FNOTE-2" class="para">2</a>] </sup>The credit for the car analogy goes to Marshall P. Cline,
author of the C++ FAQ.</p></div></div></div></body></html>