⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 chapter01.html

📁 java 是一个很好的网络开发环境。由于它是通过解释的方法
💻 HTML
📖 第 1 页 / 共 5 页
字号:
<A NAME="Heading28"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Object landscapes <BR>and lifetimes</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Technically, OOP is just about
abstract data typing, inheritance and polymorphism, but other issues can be at
least as important. The remainder of this section will cover these
issues.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the most important factors
is the way objects are created and destroyed. Where is the data for an object
and how is the lifetime of the object controlled? There are different
philosophies at work here. C++ takes the approach that control of efficiency is
the most important issue, so it gives the programmer a choice. For maximum
run-time speed, the storage and lifetime can be determined while the program is
being written, by placing the objects on the stack (these are sometimes called
<I>automatic </I>or <I>scoped</I> variables) or in the static storage area. This
places a priority on the speed of storage allocation and release, and control of
these can be very valuable in some situations. However, you sacrifice
flexibility because you must know the exact quantity, lifetime and type of
objects <I>while</I> you&#8217;re writing the program. If you are trying to
solve a more general problem such as computer-aided design, warehouse management
or air-traffic control, this is too restrictive.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The second approach is to create
objects dynamically in a pool of memory called the <I>heap</I>. In this approach
you don&#8217;t know until run time how many objects you need, what their
lifetime is or what their exact type is. Those are determined at the spur of the
moment while the program is running. If you need a new object, you simply make
it on the heap at the point that you need it. Because the storage is managed
dynamically, at run time, the amount of time required to allocate storage on the
heap is significantly longer than the time to create storage on the stack.
(Creating storage on the stack is often a single assembly instruction to move
the stack pointer down, and another to move it back up.) The dynamic approach
makes the generally logical assumption that objects tend to be complicated, so
the extra overhead of finding storage and releasing that storage will not have
an important impact on the creation of an object. In addition, the greater
flexibility is essential to solve the general programming
problem.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">C++ allows you to determine whether
the objects are created while you write the program or at run time to allow the
control of efficiency. You might think that since it&#8217;s more flexible,
you&#8217;d always want to create objects on the heap rather than the stack.
There&#8217;s another issue, however, and that&#8217;s the lifetime of an
object. If you create an object on the stack or in static storage, the compiler
determines how long the object lasts and can automatically destroy it. However,
if you create it on the heap the compiler has no knowledge of its lifetime. A
programmer has two options for destroying objects: you can determine
programmatically when to destroy the object, or the environment can provide a
feature called a <I>garbage collector</I> that automatically discovers when an
object is no longer in use and destroys it. Of course, a garbage collector is
much more convenient, but it requires that all applications must be able to
tolerate the existence of the garbage collector and the other overhead for
garbage collection. This does not meet the design requirements of the C++
language and so it was not included, but Java does have a garbage collector (as
does Smalltalk; Delphi does not but one could be added. Third-party garbage
collectors exist for C++).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The rest of this section looks at
additional factors concerning object lifetimes and
landscapes.</FONT><A NAME="_Toc375545199"></A><A NAME="_Toc408018396"></A><BR></P></DIV>
<A NAME="Heading29"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Collections and iterators</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you don&#8217;t know how many
objects you&#8217;re going to need to solve a particular problem, or how long
they will last, you also don&#8217;t know how to store those objects. How can
you know how much space to create for those objects? You can&#8217;t, since that
information isn&#8217;t known until run time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution to most problems in
object-oriented design seems flippant: you create another type of object. The
new type of object that solves this particular problem holds handles to other
objects. Of course, you can do the same thing with an array, which is available
in most languages. But there&#8217;s more. This new object, generally called a
<I>collection</I> (also called a <I>container</I>, but the AWT uses that term in
a different sense so this book will use &#8220;collection&#8221;), will expand
itself whenever necessary to accommodate everything you place inside it. So you
don&#8217;t need to know how many objects you&#8217;re going to hold in a
collection. Just create a collection object and let it take care of the
details.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Fortunately, a good OOP language
comes with a set of collections as part of the package. In C++, it&#8217;s the
Standard Template Library (STL). Object Pascal has collections in its Visual
Component Library (VCL). Smalltalk has a very complete set of collections. Java
also has collections in its standard library. In some libraries, a generic
collection is considered good enough for all needs, and in others (C++ in
particular) the library has different types of collections for different needs:
a vector for consistent access to all elements, and a linked list for consistent
insertion at all elements, for example, so you can choose the particular type
that fits your needs. These may include sets, queues, hash tables, trees,
stacks, etc.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All collections have some way to
put things in and get things out. The way that you place something into a
collection is fairly obvious. There&#8217;s a function called &#8220;push&#8221;
or &#8220;add&#8221; or a similar name. Fetching things out of a collection is
not always as apparent; if it&#8217;s an array-like entity such as a vector, you
might be able to use an indexing operator or function. But in many situations
this doesn&#8217;t make sense. Also, a single-selection function is restrictive.
What if you want to manipulate or compare a set of elements in the collection
instead of just one?</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution is an <I>iterator</I>,
which is an object whose job is to select the elements within a collection and
present them to the user of the iterator. As a class, it also provides a level
of abstraction. This abstraction can be used to separate the details of the
collection from the code that&#8217;s accessing that collection. The collection,
via the iterator, is abstracted to be simply a sequence. The iterator allows you
to traverse that sequence without worrying about the underlying structure
&#8211; that is, whether it&#8217;s a vector, a linked list, a stack or
something else. This gives you the flexibility to easily change the underlying
data structure without disturbing the code in your program. Java began (in
version 1.0 and 1.1) with a standard iterator, called <B>Enumeration</B>, for
all of its collection classes. Java 1.2 has added a much more complete
collection library which contains an iterator called <B>Iterator</B> that does
more than the older <B>Enumeration</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">From the design standpoint, all you
really want is a sequence that can be manipulated to solve your problem. If a
single type of sequence satisfied all of your needs, there&#8217;d be no reason
to have different kinds. There are two reasons that you need a choice of
collections. First, collections provide different types of interfaces and
external behavior. A stack has a different interface and behavior than that of a
queue, which is different from that of a set or a list. One of these might
provide a more flexible solution to your problem than the other. Second,
different collections have different efficiencies for certain operations. The
best example is a vector and a list. Both are simple sequences that can have
identical interfaces and external behaviors. But certain operations can have
radically different costs. Randomly accessing elements in a vector is a
constant-time operation; it takes the same amount of time regardless of the
element you select. However, in a linked list it is expensive to move through
the list to randomly select an element, and it takes longer to find an element
if it is further down the list. On the other hand, if you want to insert an
element in the middle of a sequence, it&#8217;s much cheaper in a list than in a
vector. These and other operations have different efficiencies depending upon
the underlying structure of the sequence. In the design phase, you might start
with a list and, when tuning for performance, change to a vector. Because of the
abstraction via iterators, you can change from one to the other with minimal
impact on your code.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the end, remember that a
collection is only a storage cabinet to put objects in. If that cabinet solves
all of your needs, it doesn&#8217;t really matter <I>how</I> it is implemented
(a basic concept with most types of objects). If you&#8217;re working in a
programming environment that has built-in overhead due to other factors (running
under Windows, for example, or the cost of a garbage collector), then the cost
difference between a vector and a linked list might not matter. You might need
only one type of sequence. You can even imagine the &#8220;perfect&#8221;
collection abstraction, which can automatically change its underlying
implementation according to the way it is
used.</FONT><A NAME="_Toc375545200"></A><A NAME="_Toc408018397"></A><BR></P></DIV>
<A NAME="Heading30"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
The singly-rooted hierarchy</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One of the issues in OOP that has
become especially prominent since the introduction of C++ is whether all classes
should ultimately be inherited from a single base class. In Java (as with
virtually all other OOP languages) the answer is &#8220;yes&#8221; and the name
of this ultimate base class is simply <B>Object</B>.<B> </B>It turns out that
the benefits of the <I>singly-rooted hierarchy</I> are many.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All objects in a singly-rooted
hierarchy have an interface in common, so they are all ultimately the same type.
The alternative (provided by C++) is that you don&#8217;t know that everything
is the same fundamental type. From a backwards-compatibility standpoint this
fits the model of C better and can be thought of as less restrictive, but when
you want to do full-on object-oriented programming you must then build your own
hierarchy to provide the same convenience that&#8217;s built into other OOP
languages. And in any new class library you acquire, some other incompatible
interface will be used. It requires effort (and possibly multiple inheritance)
to work the new interface into your design. Is the extra
&#8220;flexibility&#8221; of C++ worth it? If you need it &#8211; if you have a
large investment in C &#8211; it&#8217;s quite valuable. If you&#8217;re
starting from scratch, other alternatives such as Java can often be more
productive.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All objects in a singly-rooted
hierarchy (such as Java provides) can be guaranteed to have certain
functionality. You know you can perform certain basic operations on every object
in your system. A singly-rooted hierarchy, along with creating all objects on
the heap, greatly simplifies argument passing (one of the more complex topics in
C++).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A singly-rooted hierarchy makes it
much easier to implement a garbage collector. The necessary support can be
installed in the base class, and the garbage collector can thus send the
appropriate messages to every object in the system. Without a singly-rooted
hierarchy and a system to manipulate an object via a handle, it is difficult to
implement a garbage collector.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since run-time type information is
guaranteed to be in all objects, you&#8217;ll never end up with an object whose
type you cannot determine. This is especially important with system level
operations, such as exception handling, and to allow greater flexibility in
programming.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You may wonder why, if it&#8217;s
so beneficial, a singly-rooted hierarchy isn&#8217;t in C++. It&#8217;s the old
bugaboo of efficiency and control. A singly-rooted hierarchy puts constraints on
your program designs, and in particular it was perceived to put constraints on
the use of existing C code. These constraints cause problems only in certain
situations, but for maximum flexibility there is no requirement for a
singly-rooted hierarchy in C++. In Java, which started from scratch and has no
backward-compatibility issues with any existing language, it was a logical

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -