System, but they may not be reproduced for publication



Yüklə 83 Mb.
Pdf görüntüsü
səhifə30/82
tarix19.04.2023
ölçüsü83 Mb.
#106251
1   ...   26   27   28   29   30   31   32   33   ...   82
Java A Beginner’s Guide, Eighth Edition ( PDFDrive )

var = expression1;
else


var = expression2;
Here, the value assigned to var depends upon the outcome of the condition controlling
the if.
The ? is called a ternary operator because it requires three operands. It takes the
general form
Exp1 ? Exp2 : Exp3;
where Exp1 is a boolean expression, and Exp2 and Exp3 are expressions of any type
other than void. The type of Exp2 and Exp3 must be the same (or compatible), though.
Notice the use and placement of the colon.
The value of a ? expression is determined like this: Exp1 is evaluated. If it is true, then
Exp2 is evaluated and becomes the value of the entire ? expression. If Exp1 is false,
then Exp3 is evaluated and its value becomes the value of the expression. Consider this
example, which assigns absval the absolute value of val:
Here, absval will be assigned the value of val if val is zero or greater. If val is negative,
then absval will be assigned the negative of that value (which yields a positive value).
The same code written using the if­else structure would look like this:
Here is another example of the ? operator. This program divides two numbers, but will
not allow a division by zero.
The output from the program is shown here:


Pay special attention to this line from the program:
Here, result is assigned the outcome of the division of 100 by i. However, this division
takes place only if i is not zero. When i is zero, a placeholder value of zero is assigned to
result.
You don’t actually have to assign the value produced by the ? to some variable. For
example, you could use the value as an argument in a call to a method. Or, if the
expressions are all of type boolean, the ? can be used as the conditional expression in
a loop or if statement. For example, here is the preceding program rewritten a bit more
efficiently. It produces the same output as before.
Notice the if statement. If i is zero, then the outcome of the if is false, the division by
zero is prevented, and no result is displayed. Otherwise, the division takes place.
 Chapter 5 Self Test
1.
Show two ways to declare a one­dimensional array of 12 doubles.


2.
Show how to initialize a one­dimensional array of integers to the values 1 through 5.
3.
Write a program that uses an array to find the average of 10 double values. Use any
10 values you like.
4.
Change the sort in 
Try This 5­1
so that it sorts an array of strings. Demonstrate that
it works.
5.
What is the difference between the String methods indexOf( ) and lastIndexOf(
)?
6.
Since all strings are objects of type String, show how you can call the length( ) and
charAt( ) methods on this string literal: "I like Java".
7.
Expanding on the Encode cipher class, modify it so that it uses an eight­character
string as the key.
8.
Can the bitwise operators be applied to the double type?
9.
Show how this sequence can be rewritten using the ? operator.
10.
In the following fragment, is the & a bitwise or logical operator? Why?
11.
Is it an error to overrun the end of an array? Is it an error to index an array with a
negative value?
12.
What is the unsigned right­shift operator?
13.
Rewrite the MinMax class shown earlier in this chapter so that it uses a for­each
style for loop.
14.
Can the for loops that perform sorting in the Bubble class shown in 
Try This 5­1
be converted into for­each style loops? If not, why not?
15.
Can a String control a switch statement?


16.
What type name is reserved for use with local variable type inference?
17.
Show how to use local variable type inference to declare a boolean variable called
done that has an initial value of false.
18.
Can var be the name of a variable? Can var be the name of a class?
19.
Is the following declaration valid? If not, why not.
20.
Is the following declaration valid? If not, why not?
21.
In the show( ) method of the ShowBits class developed in 
Try This 5­3
, the local
variable mask is declared as shown here:
Change this declaration so that it uses local variable type inference. When doing so, be
sure that mask is of type long (as it is here), and not of type int.


Chapter 6
A Closer Look at Methods and Classes
Key Skills & Concepts

Control access to members

Pass objects to a method

Return objects from a method

Overload methods

Overload constructors
History
Topics
Tutorials
Offers & Deals
Highlights
Settings
Support
Sign Out


T

Use recursion

Apply static

Use inner classes

Use varargs
his chapter resumes our examination of classes and methods. It begins by explaining
how to control access to the members of a class. It then discusses the passing and
returning of objects, method overloading, recursion, and the use of the keyword static.
Also described are nested classes and variable­length arguments.
CONTROLLING ACCESS TO CLASS MEMBERS
In its support for encapsulation, the class provides two major benefits. First, it links
data with the code that manipulates it. You have been taking advantage of this aspect of
the class since 
Chapter 4
. Second, it provides the means by which access to members
can be controlled. It is this feature that is examined here.
Although Java’s approach is a bit more sophisticated, in essence, there are two basic
types of class members: public and private. A public member can be freely accessed by
code defined outside of its class. A private member can be accessed only by other
methods defined by its class. It is through the use of private members that access is
controlled.
Restricting access to a class’ members is a fundamental part of object­oriented
programming because it helps prevent the misuse of an object. By allowing access to
private data only through a well­defined set of methods, you can prevent improper
values from being assigned to that data—by performing a range check, for example. It is
not possible for code outside the class to set the value of a private member directly. You
can also control precisely how and when the data within an object is used. Thus, when
correctly implemented, a class creates a “black box” that can be used, but the inner
workings of which are not open to tampering.
Up to this point, you haven’t had to worry about access control because Java provides a
default access setting in which, for the types of programs shown earlier, the members of
a class are freely available to the other code in the program. (Thus, for the preceding
examples, the default access setting is essentially public.) Although convenient for


simple classes (and example programs in books such as this one), this default setting is
inadequate for many real­world situations. Here we introduce Java’s other access
control features.
Java’s Access Modifiers
Member access control is achieved through the use of three access modifiers: public,
private, and protected. As explained, if no access modifier is used, the default access
setting is assumed. In this chapter, we will be concerned with public and private. The
protected modifier applies only when inheritance is involved and is described in
Chapter 8
.
When a member of a class is modified by the public specifier, that member can be
accessed by any other code in your program. This includes by methods defined inside
other classes.
When a member of a class is specified as private, that member can be accessed only by
other members of its class. Thus, methods in other classes cannot access a private
member of another class.
The default access setting (in which no access modifier is used) is the same as public
unless your program is broken down into packages. A package is, essentially, a
grouping of classes. Packages are both an organizational and an access control feature,
but a discussion of packages must wait until 
Chapter 8
. For the types of programs
shown in this and the preceding chapters, public access is the same as default access.
An access modifier precedes the rest of a member’s type specification. That is, it must
begin a member’s declaration statement. Here are some examples:
To understand the effects of public and private, consider the following program:


As you can see, inside the MyClass class, alpha is specified as private, beta is
explicitly specified as public, and gamma uses the default access, which for this
example is the same as specifying public. Because alpha is private, it cannot be
accessed by code outside of its class. Therefore, inside the AccessDemo class, alpha
cannot be used directly. It must be accessed through its public accessor methods:
setAlpha( ) and getAlpha( ). If you were to remove the comment symbol from the
beginning of the following line,
you would not be able to compile this program because of the access violation. Although
access to alpha by code outside of MyClass is not allowed, methods defined within
MyClass can freely access it, as the setAlpha( ) and getAlpha( ) methods show.


The key point is this: A private member can be used freely by other members of its
class, but it cannot be accessed by code outside its class.
To see how access control can be applied to a more practical example, consider the
following program that implements a “fail­soft” int array, in which boundary errors are
prevented, thus avoiding a run­time exception from being generated. This is
accomplished by encapsulating the array as a private member of a class, allowing access
to the array only through member methods. With this approach, any attempt to access
the array beyond its boundaries can be prevented, with such an attempt failing
gracefully (resulting in a “soft” landing rather than a “crash”). The fail­soft array is
implemented by the FailSoftArray class, shown here:


The output from the program is shown here:
Let’s look closely at this example. Inside FailSoftArray are defined three private


members. The first is a, which stores a reference to the array that will actually hold
information. The second is errval, which is the value that will be returned when a call
to get( ) fails. The third is the private method indexOK( ), which determines
whether an index is within bounds. Thus, these three members can be used only by
other members of the FailSoftArray class. Specifically, a and errval can be used only
by other methods in the class, and indexOK( ) can be called only by other members of
FailSoftArray. The rest of the class members are public and can be called by any
other code in a program that uses FailSoftArray.
When a FailSoftArray object is constructed, you must specify the size of the array and
the value that you want to return if a call to get( ) fails. The error value must be a value
that would otherwise not be stored in the array. Once constructed, the actual array
referred to by a and the error value stored in errval cannot be accessed by users of the
FailSoftArray object. Thus, they are not open to misuse. For example, the user cannot
try to index a directly, possibly exceeding its bounds. Access is available only through
the get( ) and put( ) methods.
The indexOK( ) method is private mostly for the sake of illustration. It would be
harmless to make it public because it does not modify the object. However, since it is
used internally by the FailSoftArray class, it can be private.
Notice that the length instance variable is public. This is in keeping with the way Java
implements arrays. To obtain the length of a FailSoftArray, simply use its length
member.
To use a FailSoftArray array, call put( ) to store a value at the specified index. Call
get( ) to retrieve a value from a specified index. If the index is out­of­bounds, put( )
returns false and get( ) returns errval.
For the sake of convenience, the majority of the examples in this book will continue to
use default access for most members. Remember, however, that in the real world,
restricting access to members—especially instance variables—is an important part of
successful object­oriented programming. As you will see in 
Chapter 7
, access control is
even more vital when inheritance is involved.
NOTE
The modules feature added by JDK 9 can also play a role in accessibility. Modules are


discussed in 
Chapter 15
.
Try This 6­1
Improving the Queue Class
You can use the private modifier to make a rather important improvement to the
Queue class developed in 
Chapter 5

Try This 5­2
. In that version, all members of the
Queue class use the default access. This means that it would be possible for a program
that uses a Queue to directly access the underlying array, possibly accessing its
elements out of turn. Since the entire point of a queue is to provide a first­in, first­out
list, allowing out­of­order access is not desirable. It would also be possible for a
malicious programmer to alter the values stored in the putloc and getloc indices, thus
corrupting the queue. Fortunately, these types of problems are easy to prevent by
applying the private specifier.
1. Copy the original Queue class in 
Try This 5­2
to a new file called Queue.java.
2. In the Queue class, add the private modifier to the q array, and the indices putloc
and getloc, as shown here:


3. Changing q, putloc, and getloc from default access to private access has no effect
on a program that properly uses Queue. For example, it still works fine with the
QDemo class from 
Try This 5­2
. However, it prevents the improper use of a Queue.
For example, the following types of statements are illegal:
4. Now that q, putloc, and getloc are private, the Queue class strictly enforces the
first­in, first­out attribute of a queue.
PASS OBJECTS TO METHODS


PASS OBJECTS TO METHODS
Up to this point, the examples in this book have been using simple types as parameters
to methods. However, it is both correct and common to pass objects to methods. For
example, the following program defines a class called Block that stores the dimensions
of a three­dimensional block:
This program generates the following output:
The sameBlock( ) and sameVolume( ) methods compare the Block object passed


as a parameter to the invoking object. For sameBlock( ), the dimensions of the
objects are compared and true is returned only if the two blocks are the same. For
sameVolume( ), the two blocks are compared only to determine whether they have
the same volume. In both cases, notice that the parameter ob specifies Block as its
type. Although Block is a class type created by the program, it is used in the same way
as Java’s built­in types.
How Arguments Are Passed
As the preceding example demonstrated, passing an object to a method is a
straightforward task. However, there are some nuances of passing an object that are not
shown in the example. In certain cases, the effects of passing an object will be different
from those experienced when passing non­object arguments. To see why, you need to
understand in a general sense the two ways in which an argument can be passed to a
subroutine.
The first way is call­by­value. This approach copies the value of an argument into the
formal parameter of the subroutine. Therefore, changes made to the parameter of the
subroutine have no effect on the argument in the call. The second way an argument can
be passed is call­by­reference. In this approach, a reference to an argument (not the
value of the argument) is passed to the parameter. Inside the subroutine, this reference
is used to access the actual argument specified in the call. This means that changes
made to the parameter will affect the argument used to call the subroutine. As you will
see, although Java uses call­by­value to pass arguments, the precise effect differs
between whether a primitive type or a reference type is passed.
When you pass a primitive type, such as int or double, to a method, it is passed by
value. Thus, a copy of the argument is made, and what occurs to the parameter that
receives the argument has no effect outside the method. For example, consider the
following program:


The output from this program is shown here:
As you can see, the operations that occur inside noChange( ) have no effect on the
values of a and b used in the call.
When you pass an object to a method, the situation changes dramatically, because
objects are implicitly passed by reference. Keep in mind that when you create a variable
of a class type, you are creating a reference to an object. It is the reference, not the
object itself, that is actually passed to the method. As a result, when you pass this
reference to a method, the parameter that receives it will refer to the same object as
that referred to by the argument. This effectively means that objects are passed to
methods by use of call­by­reference. Changes to the object inside the method do affect
the object used as an argument. For example, consider the following program:


This program generates the following output:
As you can see, in this case, the actions inside change( ) have affected the object used
as an argument.
Ask the Expert
Q
: Is there any way that I can pass a primitive type by reference?
A
: Not directly. However, Java defines a set of classes that wrap the primitive


types in objects. These are Double, Float, Byte, Short, Integer, Long, and
Character. In addition to allowing a primitive type to be passed by reference,
these wrapper classes define several methods that enable you to manipulate their
values. For example, the numeric type wrappers include methods that convert a
numeric value from its binary form into its human­readable String form, and vice
versa.
Remember, when an object reference is passed to a method, the reference itself is
passed by use of call­by­value. However, since the value being passed refers to an
object, the copy of that value will still refer to the same object referred to by its
corresponding argument.
RETURNING OBJECTS
A method can return any type of data, including class types. For example, the class
ErrorMsg shown here could be used to report errors. Its method, getErrorMsg( ),
returns a String object that contains a description of an error based upon the error
code that it is passed.


Its output is shown here:
You can, of course, also return objects of classes that you create. For example, here is a
reworked version of the preceding program that creates two error classes. One is called
Err, and it encapsulates an error message along with a severity code. The second is
called ErrorInfo. It defines a method called getErrorInfo( ), which returns an Err
object.


Here is the output:
Each time getErrorInfo( ) is invoked, a new Err object is created, and a reference to
it is returned to the calling routine. This object is then used within main( ) to display


the error message and severity code.
When an object is returned by a method, it remains in existence until there are no more
references to it. At that point, it is subject to garbage collection. Thus, an object won’t
be destroyed just because the method that created it terminates.
METHOD OVERLOADING
In this section, you will learn about one of Java’s most exciting features: method
overloading. In Java, two or more methods within the same class can share the same
name, as long as their parameter declarations are different. When this is the case, the
methods are said to be overloaded, and the process is referred to as method

Yüklə 83 Mb.

Dostları ilə paylaş:
1   ...   26   27   28   29   30   31   32   33   ...   82




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə