james gosling’s java -...
TRANSCRIPT
101010100010011101001010010001001001000100001001010111010101001010001000100100100000001111110001011101110101010010101000101010101010101000101001010101110101010101000101101010101010111101010101011000101010111010110110110101010111011011010101001010110010010101010010101010101000000101011010111111010010010010101001001001010101010101000100101010100000010010101010101111010010010101010100101010100001011010100101010101010100010101010101011110101111111110000010101001010jklzxcvbnmqwertyuiopasdfghjklzxcv
James Gosling’s Java
CSC 415: Programming Languages
11/20/2012
James Cain
Object-oriented programming, as complex as it pronunciation, is one of the most useful
and interesting subjects to learn, teach, and, most importantly, put to use. Evidence of this can
be found in the vast array of existing programming languages that make use of this language
structure, whether completely object-oriented, or supporting object orientation to some degree.
The usage of these languages in software around the world cannot be overstated -- they are used
in automobiles, hospital devices, video games, and a large number of other applications. As
numerous as the languages are, there seems to be one in particular that clearly demonstrates how
object-oriented programming can be brought to its full capacity in terms of readability,
writability, reliability, and cost: James Gosling’s Java.
History of Java
Like many programming languages, Java was designed for an application for which there
appeared to be no satisfactory existing language. In 1990, Sun Microsystems determined there
was a need for a programming language for embedded consumer electronic devices (Sebesta,
p.91). Object-oriented programming was decided to be a necessity for this, but C did not provide
support for it, and C++ was judged to be too large and complex, partly because it supported two
different styles of programming (Sebesta, p.92). In addition, neither C nor C++ was believed to
provide the necessary level of reliability (Sebesta, p.92). These circumstances brought about a
new language, named “Oak”, after a tree that grew outside James Gosling’s office window. It
was later discovered that there already was a computer language by this name. When a group of
Sun workers visited a local coffee shop, the name “Java” was suggested, and it stuck (Sebesta,
p.92).
Java evolved from C++, which evolved from C, which evolved from BCPL and B
(Deitel, p.8). Each of Java’s predecessors was used in the development of computer operating
systems and compilers for other languages, but it was not until the development of C++ that
capabilities for object-oriented programming were added to C’s imperative programming style.
Java’s designers started with C++, added some new constructs, and removed or changed some of
its existing ones. The goal of this language was to provide greater simplicity and reliability than
C++ seemed to provide (Sebesta, p.92).
Overview of Java’s Design, Syntax, and Semantics
Names, Bindings, and Scopes
Like those in many other languages, names (or identifiers) in Java must be typed as a
letter followed by a string of characters consisting of letters, digits, and/or underscore characters,
and this string may be of any length (Sebesta, p.205). Names are also case sensitive -- upper-
and lowercase letters are distinct in different names. For example, the names SUM, Sum, and
sum represent three distinct variables. Some people see this as a serious detriment to readability,
since similar-looking names can represent different entities (Sebesta, p.206). Also, since some
of the method names in Java include upper- and lowercase letters, the exact case of each letter
must be correct to be recognized by a compiler, which can be seen as an issue in writability
(Sebesta, p.206). However, since these method names have been written using the popular
“camel notation”, where each word in a multiple-word name is capitalized (except the first), the
problems of case sensitivity can often be avoided by using this notation.
Java can also support both static and dynamic binding, but its type binding is strictly
static. In other words, the variables declared in a program must be given a type before the
program’s compilation, because they will be bound with their type before execution, rather than
during execution. This design gives Java many advantages over languages that use dynamic type
binding. First, since every variable’s type is known before execution begins, statements that use
variables of different types (such as an integer and a string) will be detected by the compiler and
recognized as an error, which will prevent the program from using incorrect data (and producing
unpredictable output). Secondly, since each variable’s type will remain constant throughout the
entire execution, the size of the memory storage used for this value will also be constant, rather
than increasing or decreasing during execution. Finally, languages that use static type binding
are much more efficient, due to the fact that they can be easily translated into machine code by a
compiler, as opposed to pure interpretation, which typically takes at least 10 times as long
(Sebesta, p.214). For these reasons, cost is greatly reduced through static type binding.
Java is a block-structured language, meaning that its scopes are defined as “blocks”. A
block is a section of code that may include its own variable declarations; in Java, this can either
be a subprogram (a function or procedure), or a compound statement (a sequence of statements
surrounded by matching braces) (Sebesta, p.220). A block may be nested inside one or more
other blocks, and an internal block may use its own local variables as well as the variables
declared in any of the external blocks surrounding it (global variables). A strong advantage of
this powerful concept is that it allows a section’s scope to be minimized -- its variables are stack
dynamic, so their storage is allocated when the section is entered and deallocated when the
section is exited (Sebesta, p.220). However, the reuse of names in nested blocks is illegal,
because the designers of Java believed that this was too error prone to be allowed (Sebesta,
p.220). For example, if the integer sum were declared in a subprogram, a compound statement
would not be allowed to declare an integer with this name.
Data Types
Java supports eight primitive data types: byte, short, int, long, float, double, boolean,
and char. The first four data types represent different forms of signed integers: 8-bit, 16-bit, 32-
bit, and 64-bit, respectively. The next two types, float and double, represent different forms of
floating-point decimal values: single-precision and double-precision. A boolean can have one of
two possible values: true or false. Finally, a char is a single, 16-bit Unicode character
(docs.oracle.com). In addition to these, a generic array of objects may be declared by placing a
pair of square brackets after the object type (ex. int[] intArray), a string type (an array of
characters) may be declared by importing Java’s String class, and an enumeration type (a type
whose possible values are determined by the user), may be declared by using the keyword enum.
Pointers are not used in Java, but variables of reference types may be used to refer to an object in
order to invoke its methods through the use of a period (ex. method1.sort) (Deitel, p.87).
Java is strongly typed, which means that type errors (errors in which an inappropriate
data type is given as a parameter to a function) are illegal, and are always detected (Sebesta,
p.302). Explicit type conversions, also called casts, may be used to convert a variable of one
type to another; this is done by placing the type that a variable should be converted to in
parentheses before the name of the variable (ex. (double) sum). However, this conversion
must be used carefully -- a narrowing conversion, such as the conversion of a float value to an
int value, may result in a loss of precision (Sebesta, p.329).
Some data types may be combined in mixed-mode expressions, in which an operation
between operands of different types occurs (Sebesta, p.330). Since computers cannot perform
such expressions, an implicit type conversion takes place during the expression’s evaluation. For
example, if an int and a float are added, the value of the int variable will be converted to a
float variable and a floating-point addition will be performed (Sebesta, p. 302). While this may
lead to undetected errors, such as an unwanted type conversion, the number of possible type
conversions has been reduced to half as many as C++ has, which makes errors like this less
likely to occur (Sebesta, p.304).
Expressions and Assignment Statements
In Java, an expression is a construct made up of variables, operators, and method
invocations that evaluates to a single value (docs.oracle.com). An expression statement may be a
single value -- either a direct value or the name of the variable whose value is desired
(ex. X = 1 or X = Y) -- or a combination of values and mathematical operators and/or
function calls (ex. X = 1 + 1 or X = Y * 2 or X = Y + func(Z)). An assignment
statement must be typed with a variable name, followed by an equals sign (=) and an expression
statement that represents the variable’s intended value. Other statements that can be used to
manipulate variables include increment/decrement statements, which change a variable’s value
by one (ex. X++ or X--), method invocation statements, which invoke specific methods
(ex. System.out.print(X)), and object creation statements, which are used to create user-
defined objects (ex. Class myClass = new Class).
The use of the equals sign as an assignment operator may be confusing to some people.
This symbol is most often used as an equality operator in mathematics, but in Java, equality is
represented (and tested) with the == symbol. This creates the possibility of an undetected error;
for example, if the statement if (A == B) is mistakenly typed as if (A = B), then the
value of B will be assigned to A, rather than compared to A’s value, and the statement will be
evaluated as true. Suffice to say, this concept will need to be learned well in order to create
efficient programs in Java, especially for a programmer who is used to a different set of symbols.
There are, however, many other potential problems that have been eliminated by Java’s
designers. Functions, for example, can sometimes have unwanted side effects, which occur
when a function changes either one of its own parameters or a global variable (Sebesta, p.326).
If a statement such as b = a + func(a), were typed in a C program, the value of b would
depend on which operand of the plus sign was evaluated first -- a or func(a). Java solves this
problem by guaranteeing that operands be evaluated in left-to-right order.
Statement-Level Control Structures
The concepts of sequence, selection, and iteration can be explored thoroughly using
Java’s control statements. Each statement in a Java program is executed in order from top to
bottom. This can be changed by using the reserved word goto (an unconditional branch
statement), but this is no longer used (docs.oracle.com). Selection statements include the if
statement (for single selection), the if...else statement (for double selection), and the
switch statement (for multiple selection). Iteration can be used with the for statement, used
for counter-controlled repetition, the while statement, used with boolean values, the
do...while statement, a version of the while statement that executes its body statements at
least once, and the break and continue statements, which can be used to alter the flow of
control in a for, while, or do...while statement (Deitel, ch.4-5).
Perhaps the most useful utility provided by these control statements is the ability to insert
(or “nest”) any one control statement inside another. This allows programmers to add multiple
layers of functionality to a program, such as an if statement nested in a for loop, which can be
used to check the conditions of data in an array), or a for loop nested within another for loop
(useful for initializing or changing the values of a two-dimensional array). While this can lead to
a decrease in the readability of a program by adding more complexity, it gives the experienced
programmer the ability to create more powerful and flexible programs, and is therefore a large
contributor to writability.
Subprograms
Subprograms in Java are of one of two types: functions and procedures. A function
performs a calculation (either mathematical or logical) using data passed to it, and may be used
as an operand; a procedure simply performs a task by manipulating data, and may be called
simply by typing its name and list of parameters. A subprogram may be declared within a class,
before or after the main program (if the class contains one), or may be declared in an entirely
separate class. The header of every subprogram must have the same format, which is similar to
the format of a class: type name(parameters), where type is the data type that the
subprogram returns (void if it returns nothing), name is the subprogram’s name, and
parameters is a list of data types to be passed to and used by the subprogram. Each item in
the parameters list must be typed as its data type (int, boolean, etc.) followed by a space and a
temporary name for this value (it may have a different name in the program that calls the
subprogram). If nothing is passed to the subprogram, a set of empty parentheses must follow the
subprogram’s name. If the subprogram returns a data value, it must contain a return statement
that contains a value or variable of the type specified in the header.
An overloaded subprogram is a subprogram that has the same name as another
subprogram in the same referencing environment (Sebesta, p.421). Subprograms of the same
name must have a unique protocol; that is, they must differ in the number, order, or types of their
parameters. This technique may seem confusing, but if used wisely, it can improve a program’s
writability significantly. For example, suppose that a program included many arrays of different
numerical data types (such as int, float, and double) and used a subprogram named “avg”, with
a header such as avg(int[] array), to calculate the average of the numbers in a specific
array. This subprogram would only be usable on arrays of integers -- the arrays of other types
would need to be converted to this type, which could result in a loss of data precision. If,
however, there were three subprograms named “avg” -- one for int arrays, one for float arrays,
and one for double arrays -- then the average of any of the arrays could be calculated simply by
passing them to a subprogram named “avg” -- the compiler would know which of the avg’s to
use based on the data type of the given array. This sort of subprogram demonstrates Java’s
handy use of polymorphism.
Abstract Data Types/Encapsulation Constructs
An even more valuable asset to Java’s writability is its use of user-defined abstract data
types (ADTs). This sort of data type is unique in that it never truly exists in a program that uses
it until an instance of this type, or object, is created. In other words, its representation is hidden
from the program units that use it, so the only direction operations possible on it are those
provided in its definition (Sebesta, p.476). In Java, a variable may be declared private, which
means that it can only be accessed directly by other members in its class; in this way, the
variable becomes abstract. A class may also have its own constructor (typed as a method with
the class’s name), which can be used to initialize some or all of the class’s variables (thus
“constructing” the class) every time it is called -- this not only improves the reuse of the class,
but it also hides the details of initialization from the class (or classes) that call it. In addition to
this, Java 5.0 supports generic types, such as the LinkedList and the ArrayList, which can
be used to store data of any type, but only after being instantiating by the keyword new.
Larger forms of encapsulation can also be implemented in Java. An entire class may be
declared abstract, in which case the class’s body need only contain the headers of the
methods it uses; the methods’ bodies can then be constructed in every class that implements the
abstract class. Similarly, an interface may be used as a sort of “placeholder” for a class -- it must
contain a heading with the keyword interface and a name, and its body may include any
number of variables and methods, but it cannot be used in a main program until it is implemented
by a class that contains the same variables and methods.
The most resourceful of Java’s uses of encapsulation, however, is the package. Packages
can contain more than one type (a class or interface) definition, and the entities defined in a type
in a package that either are public or protected or have no access specifier are visible to all
other types in the package (Sebesta, p.515). A package may be used in any program through the
import declaration (import followed by the package name), which allows the program to
call any of the package’s types without any knowledge of the types’ details. A familiar example
is the java.Math package, which contains a large number of mathematical functions and operands
that are normally unavailable in Java.
Support for Object-Oriented Programming
What makes Java object-oriented? In Java, the unit of programming is the class from
which objects are instantiated (created), and a Java programmer can use existing classes as the
building blocks for constructing new classes (Deitel, p.21). At the center of object-oriented
programming and the languages that support it is inheritance -- the ability to factor out (or
abstract) the commonality of similar abstract data types and put it in a new type (Sebesta, p.524).
In Java, this can be implemented in many ways, such as through interfaces and abstract data
types, as discussed earlier. Another common method of using of inheritance is the keyword
extends, which allows a subclass to “inherit” every variable and method of its superclass (the
class it extends) and add any new variables and methods it needs to be used for a more specific
purpose. Java supports only single inheritance, but some of the benefits of multiple inheritance
can be gained by using the interface construct (Sebesta, p.93).
Another import aspect in object-oriented programming is dynamic binding. Using this
technique, a class may call a method another class, or from a subclass of this class, depending on
which it currently references (Sebesta, p.528). This sort of reference is polymorphic, and can
change between superclass and subclass during execution, which is very useful when referencing
a class that has many subclasses. In Java, all method calls are dynamically bound unless the
called method has been defined as final, in which case it cannot be overridden and all
bindings are static; static binding is also used if a method is static or private, both of
which disallow overriding (Sebesta, p.555). Java’s thorough implementation of inheritance and
dynamic binding explains why it is used by many textbooks to describe these concepts.
Concurrency
Concurrency, also known as multithreading, is a very useful tool that most programming
languages, especially imperative ones, do not provide. Java includes multithreading primitives
as part of the language itself and as part of its libraries, which makes it easier to manipulate
threads across platforms in a portable way (Deitel, p.1060). A thread in Java is the process in
which a method named run executes (Sebesta, p.603). A class with a run method may be
defined using one of two methods: one is to define a subclass of the predefined class Thread
and override its run method; the other is to define a subclass that implements the Runnable
interface, which provides the run method protocol (which means that the subclass must define a
run method) -- this method must be used if this subclass already has a necessary parent class
(Sebesta, p.604). The Thread class includes a useful collection of methods for concurrency:
the run method describes the thread’s actions, the start method starts the thread (as a
concurrent unit) by calling its run method, the yield method sends a request to the scheduler
to surrender the processor (which it may or may not do), the sleep method blocks the thread
for a specified amount of time, and the join method forces a method to delay its execution until
the run method of another thread completes its execution (Sebesta, p.604-605).
Java’s threads are lightweight, which means that they all run in the same address space,
as opposed to Ada’s tasks, which are heavyweight -- each task runs in its own address space; an
important result of this is that threads require far less overhead than Ada’s tasks (Sebesta, p.603-
604). This, however, eliminates the possibility of running Java threads on different computers in
different places, which is possible with heavyweight tasks (Sebesta, p.613). Java’s run methods
are able to communicate with each other in only two ways: through the join method and
through shared data (Sebesta, p.604). While Java threads are often complex, the simplest parts
can be used by an amateur programmer to effectively create concurrent programs.
Exception and Event Handling
As with any programming language, a large number of possible events can occur in Java,
some of which are less desirable than others. Unexpected or unusual events (called exceptions)
are handled by different languages in different ways, and Java’s exception handling is based on
that of C++, but is more closely based on the object-oriented language paradigm (Sebesta,
p.647). Java also includes a collection of predefined exceptions that will be implicitly pointed
out (or raised). All Java exceptions are objects of classes that are descendants of the predefined
Throwable class; the Java system includes two subclasses of Throwable: Error, which
deals with errors that are thrown into the Java run-time system, and Exception, which has two
direct descendants: RuntimeException and IOException. RuntimeException is
thrown when a user program causes an error; IOException is thrown when an error occurs in
an input or output operation. User programs can also define their own exception classes (usually
subclasses of Exception) (Sebesta, p.648). While some exceptions, of course, can be missed
or, even worse, misinterpreted by a Java compiler, a good programmer will be able to use Java’s
exception handling to his or her advantage.
Another important and useful detail in a programming language is its capability to react
to real-life events, such as mouse movement, scrolling, and key presses, also known as event
handling. Apart from Web applications, Java handles events such as these through a very
common tool: Graphical User Interfaces (GUIs). These are provided by Java’s Swing collection
of classes and interfaces (defined in javax.swing), which includes GUI components, called
widgets, such as text fields, radio buttons, checkboxes, and so on (Sebesta, p.656). These
components are able to “know” when a user interacts with them by using event listeners, which
must be registered for a specific event in order to be notified when that event occurs (Sebesta,
p.657). For example, to provide an action that is triggered by a radio button click, the interface
ItemListener must be implemented, which requires a definition of the method
itemStateChanged (Sebesta, p.658). Within this method, a compound statement (if, while,
etc.) may be used to assign an action to the event in which the radio button is clicked.
Other Issues in Java
What has made Java so popular in comparison to other languages? Other than the fact
that its original purpose was to provide improvements and additions to the languages that came
before it, Java has been upgraded in many ways since its creation. One example is the Java
applet - a program that is typically embedded in an XHTML (Extensible HyperText Markup
Language) document, better known as a web page (Deitel, p.955). This can be used to add a vast
array of different applications to a website, including games, image editors, and tools for
uploading and downloading files. Another practical application that Java offers is a database
management system (DBMS), which provides mechanisms for storing, organizing, retrieving,
and modifying data for many users. Java Database Connectivity™ JDBC can be used with a
JDBC driver to connect to a database and allow users to manipulate its data (Deitel, p.1185).
A large part of Java’s wide-spread use can be attributed to the Java Virtual Machine
(JVM), an application that simulates a computer but hides the underlying operating system and
hardware from the programs that interact with it. Any computer platform that implements a
JVM can execute Java applications, regardless of what system the applications were written and
compiled on. This is because a Java compiler translates an application’s source code into
bytecodes -- representations of the tasks to be executed -- which are then executed by the JVM
(Deitel, p.13). While different versions of Java will most likely be incompatible, the universal
portability of bytecodes has given the language an incredible way to expand its usage.
Evaluation of Java
Readability
A programming language’s readability depends largely on its reader’s previous
experience with it, or with a similar language. Fortunately, Java’s many predecessors make it
fairly simple for programmers familiar with other object-oriented languages (especially the C-
based languages) to adapt to Java’s unique programming environment. The use of brackets ( {} )
to represent the blocks in a program can greatly improve the organization of even the most
complex ones if used carefully. The names of compound statements (if, while, etc.) are self-
explanatory, making it easier to explain what a program does in plain English. Finally, since the
language is case sensitive, variable names must have the same capitalization, and all reserved
words and keywords must be lowercase. This prevents the reader from having to check to make
sure that a certain variable or class has been typed correctly each time -- the compiler will catch
any misspellings or improper capitalization that occur in the program.
Some arguments could made against the format of Java’s programs, such as its use of
square brackets ( [ ] ) to represent arrays, rather than the keyword “array”. While this does take
more time to remember and recognize in a program, it creates a way to initialize an array’s
length more easily when it is instantiated. For example, a single statement can create a new
array of 15 doubles:
double[] scores = new double[15];
In this case, some readability is traded for an increase in writability. Another example of this is
the ability to replace the keywords true and false with an entire Boolean expression -- these
two statements have the same meaning in Java:
return bool;
if (bool == true) return true;else return false;
Here, as well, the slight reduction in readability creates a much easier way to write a statement
that checks the value of a variable.
Writability
Learning to program in Java is a task that requires time and experience, but the concepts
involved in creating a useful and robust program are easily memorized. Every block has a
similar format, which can become natural to any programmer who continually creates new
blocks. A number of methods exist for shortening and encapsulating data, such as the ability to
define many data of a single type at once (ex. int a, b, c) and the interface and
abstract data type constructs. The many powerful constructs and techniques that have been
described give the language a powerful potential that any programmer can make use of.
Java does, of course, have its faults in regards to writability. Possibly the toughest and
most unhelpful thing to remember in Java is that the indexing of the array construct starts with 0,
rather than 1. Because of this, an attempt to perform an operation on every member of an array
named grades[], beginning with the index 1, will not compile:
for (int x = 1; int <= grades.length; x++)
This will result in a stack overflow error (since the last index will be one less than the actual
length of the array). Programmers who are not used to this convention may have trouble
adjusting to it, which may be difficult in complex problems, but most compilers will be able to
remind the programmer when he or she makes this mistake. Also, those who are not in favor of
case sensitivity may be easily annoyed by a Java compiler’s intolerance of type errors. This
intolerance, however, can be used to improve one’s “programming etiquette” over time.
Reliability
How likely is a Java program to contain errors that were missed by both the programmer
and the compiler? Considering how long Java has been around, it seems very unlikely that any
fatal errors have discouraged many programmers from using it. The language’s variety of
applications, from mobile apps to the design of automobile software, demonstrates how safe and
trustworthy its programs can be when typed by an experienced programmer. Keeping in mind
that part of the goal of Java’s development was to provide more reliability than C++, it makes
sense that its structure was so carefully planned out.
Cost
The cost of creating reliable software that uses Java, (time, money, etc.), is often very
low. This is largely a result of Java’s unique balance of readability, writability, and reliability.
Its fairly simple, object-oriented semantics provide a user-friendly environment for programmers
of nearly any level of experience -- this, along with its outstanding fame, makes the task of
finding and hiring a new programmer to work with software of many different type much less of
a struggle. Its flexible and easily-maintained style makes software construction, compilation,
and error-checking much faster. Most importantly, in the hands of an experienced and careful
programmer (or team of programmers), a finished Java program has very small chance of having
enough bugs to cause it to end up costing more time and money than it saves. When compared
against other languages, Java’s performance level is high when it comes to overall cost.
Overall
Taking all of these facts and observations into account, James Gosling’s Java is clearly
one of the most efficient languages to be imagined, planned out, and created. Its goal of being a
simpler, more reliable, and more useful language than its predecessors has undoubtedly been
fulfilled and surpassed. The Java language now has worldwide use and fame, among amateur
and professional programmers alike. In Sebesta’s words, one of the reasons for Java’s rapid rise
to prominence is simply that programmers like its design (Sebesta, p.93). Its popularity may be
attributed to its reliability, its portability, its potential, its low cost, and many other things, or it
may simply be attributed to the genius of James Gosling and the workers at Sun Microsystems.
In either case, we have object-oriented programming to thank for the new possibilities brought
about by the richness of the Java language.
Bibliography
Deitel, Paul and Harvey Deitel. Java: How to Program, 8th Edition. Upper Saddle River: Pearson Education, Inc., 2010.
Oracle. Lesson: Language Basics (The Java Tutorials > Learning the Java Language). 14 June 2012. 9 November 2012. <http://docs.oracle.com/javase/tutorial/java/nutsandbolts/index.html>.
Sebesta, Robert W. Concepts of Programming Languages, 10th Edition. Upper Saddle River: Pearson Education, 2012.