Web Programming Unleashed
Java and the Internet
by Dan Joshi
- Java History
- Java's Place in the Object-Oriented Continuum
- Defining Java
- The Benefits of Java
- The Negatives of Java
This chapter gives a brief history of programming languages, from
their earlier incarnations to today's modern languages, and provides
a foundation of common understanding as to where Java fits in
the scheme of things. This chapter is not just about the benefits
of Java-it is about Java.
Structured programming was the 1960s' answer to the need for discipline in the computer programming industry, leaving spaghetti code for the history books. An excellent example of a structured programming language was Pascal, developed in 1971.
Object-oriented programming was invented to improve three things that structured programming languages lacked: the capability to "reduce, reuse, and recycle" code.
Since the birth of C++, object-oriented programming languages have become the standard by which modern programming languages are defined. Object-oriented (OO) programming languages such as C++ deal with user-defined types (UDT). UDTs are programmatic representations of objects. Programmers can reuse these objects throughout their program and in other programs.
The idea behind object-oriented programming is based on the logic of designing your code into objects and letting these objects communicate with each other through various object-based technologies. Before you can understand Java, you must understand objects; and before you can understand objects, you must understand the structured programming techniques upon which objects are founded. Object-oriented programming encompasses much more than described in this brief introduction. Many of these benefits won't be fully appreciated until you have had a chance to work with Java later in this book.
In real life we deal with objects every day. With objects we can have superobjects and subobjects. For example, a car is an object. A luxury sedan is a subobject to the object of a car, and a vehicle is a superobject to the object of a car. The concept that a car completely encapsulates the luxury sedan and that the vehicle completely encapsulates both of them helps to define what an object is. In another light, a car is a more specific representation than a vehicle, and a luxury sedan is a more specific object than a car. Thus, the car is a subobject to a vehicle and a luxury sedan is a subobject to the car. And if you can conclude that the luxury sedan is also a subobject to the vehicle, you would be correct. However, if the only vehicle you ever saw was a car, you would conclude that it would be redundant to add a subclass (that is, a more specific representation) of a car object because a car fully encompasses your definition of a vehicle.
This point brings out one of the most powerful and confusing attributes of object technology in the programming industry, which is that objects are defined. We must use our own perception of what is a subclass or superclass to another object. Even though this makes object-oriented programming difficult for most new people to understand, it is extremely powerful when you do understand it. In object-oriented programming, you get to make your own set of rules.
Before getting into more detail on the anatomy of objects, there is one other key feature of object-oriented programming to introduce. It is the capability of objects to contain both data and behavior that acts on that data. This grouping of both data (or attributes or properties) and behavior (which is represented in methods) helps to integrate your programs as opposed to conventional structured programming techniques, which separate the data-based code from the action-based code.
Objects usually contain properties. For example, the number of wheels a vehicle has is a property of the vehicle object. In order to better understand properties, let's design an acting model. The vehicle object, as defined in the preceding paragraph, is a superobject to the car object. The car object also can have properties; however, it would be inappropriate to give our car object the property of trunk size, for example, because not all cars have trunks (assuming your definition of a car includes hatchbacks). On the other hand, it would be appropriate to give the luxury sedan object a property of trunk size because all luxury sedans have some sort of trunk.
When you are assigning properties in your program, look for the highest class that would still be appropriate for the property. This follows the logic that the lower you go in a class hierarchy the more specific an object becomes. On the other hand, the higher you go in a class hierarchy the more abstract an object becomes.
Another important term used in object technology is method. Driving a car is an action; thus, driving could be considered, in "object jargon," a method to the car. A good way to understand a method is to think of it in terms of action. A method is always doing something. As mentioned earlier, methods can be appropriately and inappropriately placed depending on their location in the class hierarchy.
Object-oriented programming is simply another way of putting better logic into your program. Digressing into a more philosophical aspect of programming languages, here is a description of one of the most important components of the perfect programming language: Ideally, this language would be completely unrelated to computer science. The advantage of this is that anyone could sit down and use it without needing to know anything about computers and computer science. All a person would need to know to create a computer program would be his or her field of expertise. For example, an insurance broker would only need to know about his field to sit down in front of a computer and create a program that would make his job (hence his life) easier.
Unfortunately, such a language does not exist and probably won't be here any time soon. However, based on lessons learned in the history of programming languages discussed further in the next section, the object paradigm is probably one of the biggest steps toward this perfect language.
At its very core, object-oriented programming is simply a more powerful way to construct your code logically; using objects with properties and methods creates a more logical program.
Although most programming languages have many rules specific to each of them, the golden rule of all programming languages is this: Never write the same code twice. By having a more distributed program, you are more able to follow this golden rule of programming. Although most programmers would find it nearly impossible to reuse their code 100 percent of the time, object-oriented programming languages greatly improve the ability for anyone to reuse their code.
Now, imagine that you are a developer and you are writing a program called Alpha. You write this program in a structured programming language and compile it. Your program is successful, and your manager comes to you and says she wants an Alpha version 2.0.
At this point, you would usually need to modify the original program. Sometimes you can edit code that is already resident in the program. Other times you would need to write new code. Typically, you would need to do both: edit old code and write new code. However, it is also likely that, due to the nature of the changes for version 2.0, you will need to dissolve the old program and redesign it from the ground up.
Now imagine that you are an object-oriented programmer, and you are asked to write the same program called Alpha. You create the program by building objects. Your code is more logical, is easier to understand, and typically also might be smaller. Thus, you are reducing the amount of time and code you spend developing the application. Now, when your manager says she wants Alpha version 2.0, instead of going into the code of the original program, you can take your original Alpha classes and "inherit" them into new classes that have added functionality for use with version 2.0. This object technology is called inheritance.
With the advent of object-oriented programming like C++, Java, and SmallTalk, using inheritance you can recycle old code by taking the old objects and turning them into new ones. The key point is that in object-oriented programming you can more effectively "reduce, reuse, and recycle" your code.
Java is a new object-oriented programming language that has its
foundation in C++. In fact, part of the process of designing Java
was taking C++ and analyzing its strengths and weaknesses. Java
was the next step from C++, with a perfect fit in the Internet's
multiplatformed environment. Furthermore, Java roots come from
the portable electronic device industry, making it extremely portable
from one device to another device, or in the case of the Net,
from platform to platform.
This section includes notes on Java in terms of C/C++, helping those already familiar with C/C++ quickly learn Java.
Java was developed by Sun Microsystems and is a multithreaded object-oriented programming language. It is a new programming language with complete functionality like that of C++. At this point, Java's niche is on the World Wide Web. Probably the most important things that Java brings to the Web are the capacity for more interactivity on the Internet through multimedia and animation. In order to view Java applets on the Net, you must have a Java-compliant browser. The Internet Explorer (as of version 3.0) and Netscape Navigator (as of version 2.0) are two prominent browsers on the Net that support Java.
The next section defines Java applets and discusses the differences between Java and Java applets.
Java applets are a hybrid form of Java programs to run on a Java-compliant browser on the World Wide Web. The applet is inherently embedded in an HTML document. A Web site that has one or more Java applets embedded in it is said to be "Java-powered." Java applets are portable to just about any Web site. If you would like to check out some already existing applets, go to the Gamelan home page at the following URL:
On the same topic as Java applets is the abbreviation JARS, which
stands for Java Applet Rating Service. JARS is a rating service
for Java applets on the Internet. Anyone can create an applet
and submit it to the JARS judges for review. Your applet then
would be scored on four topics for official awards. See Table
5.1 for a breakdown of how applets are rated.
|Presentation (front end)|
|AppletPerfect (programming content)|
|Originality (concept, new idea)|
For unofficial awards, the second topic in Table 5.1 is removed. If you would like to see some of the best Java applets on the Internet, point your browser to the JARS home page at the following URL:
Because the Internet has enabled us to connect all kinds of computers from all over the world, there are major security issues that need to be addressed. Java applets have been designed with security as a top issue. Applets are specifically designed to make sure that the applet itself cannot harm the client's environment and cannot carry a downloadable virus that could infect the client's system. Unfortunately, this security benefit has a downside. On the one hand, you will have protection against potential viruses and rampaging Java applets. On the other hand, it limits Java applet potential significantly in several areas. The applet is pretty much restricted from gaining direct access to a client's system.
As you will see in the following sections, Java benefits from serious pre-planning and design. It is a completely object-oriented programming language from the ground up and is also multithreaded, letting your programs run more smoothly. Java is platform-independent, which means one compiled Java program can be accessed by several different environments. Finally, Java handles all memory management automatically, making it an extremely powerful, useful, and versatile programming language. Let's take a closer look at each of these.
Java is an object-oriented programming language, as mentioned
earlier. Instead of having to memorize a set of functions to program
in Java, you have a set of classes known as the Java class library.
A class library is simply a group of related classes.
The Java class library enables you to design the user interface for your Java program, access a file (if it is a Java application), or implement a thread. This helps to make Java a very functional language. Remember, though, that everything in Java deals with objects, starting from the ground up.
Java Compared to C++
Java is closely related to C++. But there are a few major discrepancies that should be noted.
Java does not use pointers. A pointer is a variable that contains the address of another variable's location. This form of indirectly accessing variables and UDTs has been one of the most powerful and most confusing features of C/C++. Pointers are also one of the easiest ways to crash a system. Pointers appear to be one of the major ways that seemingly non-removable bugs enter into a program. For example, simply by pointing to an area of memory where you don't belong, you can inadvertently start writing new information over the current information stored in that memory location. This is not uncommon. A pointer might point to an incorrect location. Sometimes you get lucky and write over only unused or unimportant information stored in that memory location, but at other times you could write over important information and send the system crashing down. Also, pointers have the capability to illicitly "look" at information that you should not be looking at. This, unfortunately, has given crackers a loophole to work with. The use of pointers was removed from Java for these reasons.
Java is based on single inheritance. C++ currently supports multiple inheritance. The major difference between multiple inheritance and single inheritance is that in C++ you can inherit more than one class into another class. So, a subclass could effectively have two or more superclasses, and each of the superclasses would completely become part of the subclass, making the subclass a more specific representation of both superclasses. In theory this was a great idea, but in practice it turned out to be much too complicated and hence more trouble than it was worth. So, Sun kept the object technology of inheritance but only allowed single inheritance; this means that a subclass can have only one superclass. This is really the best of both worlds because with single inheritance you can still inherit one class to another new one, but you can do so only once per class basis. I might add, though, that Java does have a way to support a kind of multiple inheritance; that's through the use of implementing interfaces.
Java's memory management system is greatly improved over C/C++. Java has automatic memory management and garbage collection. More information on garbage collection will be explained later in this chapter.
Some other important notes about Java are that it does support overloaded functions, but not overloaded operators; also, commands like struct and union are gone from Java's vocabulary. Another minor note on the differences between C/C++ and Java is that Java does not support the ability to change the nature of a variable implicitly. Everything must be explicitly cast in Java. In C/C++ jargon, Java does not support automatic coercion. Header files were replaced by interfaces. All functions must be wrapped with a particular class.
Finally, C/C++ does not have a native string object like Java does. Instead, you have to build it by creating an array of single-character data types or else define your own C++ based string UDT. Furthermore, character arrays in C/C++ do not do any bounds checking, so if your array of characters in C/C++ is larger than what was allocated, you are writing on memory locations that do not belong to your program, usually causing it to crash.
Modern operating systems are said to be multitasking. This
means that the operating system can process more than one thread
at a time (in truth, unless you have more than one CPU, the operating
system is merely fudging at handling more than one thread at any
one point in time, but this statement will suffice for now). Java,
on the other hand, enables the programmer to create a program
that directs how the CPU will handle the thread. Probably the
greatest advantage to this is shown in all of the animation- and
multimedia-intensive Java applets on the Internet where the programmer
has directly assigned a special thread for animation.
A thread is merely the basic entity to which the operating system allocates CPU time.
What Multithreaded Java Brings to a Multithreaded Operating System
Before you can understand how Java improves the multithread paradigm, you need to digress a little to understand a bit about the operating system. Because operating systems vary from one platform to another, this section discusses them in a generic light as they pertain to Java.
First, let's start with an understanding of a typical operating
system. When you start a program, a process is spawned.
A process is a program loaded into memory and prepared for execution.
The process holds the code. Each process is started with a single
thread, but additional independently executing threads can be
created. This makes for a smoother program and environment, but
it can also create conflicts such as when two or more threads
attempt to access the same resource.
Thread synchronization is the answer to avoiding thread conflicts.
On a single processor system, the operating system merely gives
the illusion that more than one thread is executing at one time.
What is really going on is that each of the active threads receives
a time slice, usually an extremely small amount of time.
Time slices usually are measured in milliseconds.
You could think of it as a string of lights, where each of the bulbs are separate threads, blinking one at a time, but blinking so fast that to the human eye the string looks as though all the lights are on at the same time. Now if multitasking were just that simple, it would be easy to understand it.
Preemptive multitasking is the capability of the operating system to interrupt a thread (most of the time) and assign the CPU to a waiting thread. So, for example, imagine that you have a word processor and spreadsheet loaded, and in the middle of a long CPU-intensive process in the spreadsheet, you switch over to the word processor. In this scenario, you can effectively preempt the thread currently running (the spreadsheet) and give it to a thread that belongs to the word processor. This keeps the modern desktop responsive. Typically, the only time that the environment might lose its responsiveness is when too many threads are running.
The last topic to mention is the idea of thread priorities. For example, the thread that handles mouse events should have a high priority in the operation system. That way, the mouse will get more attention from the CPU, and the user will have a more responsive mouse that does not "lock up" whenever he starts a program that hogs CPU time.
With this explanation, you can pretty much tell how the operating system handles all of the threads and what they are doing. Java enables the programmer to design an application with multiple threads. You, the programmer, have control over your thread, to some extent anyway. You can create a thread and develop code to respond to various events in which the operating system handles the thread. As a Java programmer you can designate the priority of the thread in your Java applet. Also, you can directly deal with things like the synchronization of threads. Java gives you much more control over how your program will run in the user's environment. A key point is that typically you are requesting all of these things. Multithreaded environments help us deal with things in the computer in a simultaneous manner.
Most of the memory management technologies Java uses are based on ideas borrowed from other languages and improved. First, as object-oriented Java programmers, let's ask the question: How does Java memory management handle objects? It is all done automatically. Let's understand how.
One of the major problems with programming languages-either object-oriented or structured languages-is the idea of memory leaks. Memory leaks have been around since the structured programming days. However, take a closer look at how they occur in an object-oriented programming environment. When a program starts, it takes free memory from the environment to construct instances of objects. Next, the program runs, typically creating and destroying these objects and therefore taking and freeing memory throughout the life of the program. Finally, the program exits and destroys any remaining objects, leaving the environment with the same amount of free memory as before it was started. This is a perfect program. All of the objects constructed are destroyed; thus, all of the memory taken for the program would be released back into the environment. However, in the real world this is not the case, and in many programs the user's environment is left with less available memory than when the program was started. The key here is that in C++ typically the programmer must keep track of the allocation of memory for objects being constructed and the release of memory for objects that are no longer needed. As programs become more complicated, there is more room for error. Memory leaks are a very unwanted, yet accepted part of the program.
An excellent example of a program with memory leaks is a major application developed in C/C++: the 16-bit version of Windows. This operating system has memory leaks; to crash it, all you have to do is just use it without ever turning it off at night. Although the crash might take a week or month, eventually the system will run out of resources. To avoid such crashes, in C/C++ you have to keep track of all the objects you create and make sure that they are all properly destroyed. This is not so in Java, however. Java will take care of all the memory operations (though you do have a manual override option).
Another important feature of Java is this: it has garbage collection. Garbage collection is the method Java uses to automatically handle allocating memory. Java uses two types of garbage collection tools. One is compaction, which is merely another word for defragmenting memory. Frankly, just as your hard disk becomes fragmented as you read and write information to it, memory also becomes fragmented. Compaction is the process of defragmenting memory.
Marking and sweeping is the other technology Java uses as part of its garbage collection tools. Marking and sweeping is a recursive process that marks objects in use; if at the end of this marking an object is not marked, it is swept or destroyed.
Garbage collection is not a new technology that Java just introduced; in fact, it is a fairly old one. Garbage collection has been around with languages like SmallTalk and Lisp. However, with these programming languages, particularly SmallTalk, the system grinds to a halt for a period of time whenever it is collecting garbage. Java, however, takes the idea and makes it better. Java, as you already know, has a fully functional garbage collection mechanism. And Java is a multithreaded programming environment. So, in coalescence of the two, Java runs its garbage collection on a (you guessed it) separate thread, making Java's garbage collection virtually unnoticeable. The thread is given a fairly low priority so that it will activate itself only when there is actual or anticipated idle time on the CPU. In summary, Java gives the programmer a hands-off approach to keeping track of how memory is being used and optimized.
When you write a Java program or applet, Java turns your written code into bytecodes. Bytecodes are like machine language instructions, though not quite at the level of machine language instructions. The advantage of this is that bytecodes are not processor-specific. When you compile a C/C++ program the compiler turns it directly into a set of machine codes or processor instructions. What this means is that, to take advantage of a different processor, you have to recompile the program each time. This will make a very efficient program, but it is a very tedious process every time you change processors. If you change environments, (for example, from the IBM clone to Mac, or to UNIX, and back again), you will again have to recompile your program. Not only do you have to recompile it, but also you often must rewrite the code for the specific environment you are heading toward. Java, on the other hand, takes care of "porting" the program and code to other environments.
Java is compiled and interpreted. It is compiled to bytecodes. When the program or applet is run on the client's system, the bytecodes are interpreted to the specific machine language and processor instructions of the client's system. By compiling one program in Java and turning it into bytecodes, you can use it on several environments.
Because Java uses bytecodes, it can also be verified. This makes the utilization of bytecodes a major part of Java's security model. Bytecodes can also be checked for security violations. Methods and variables are always accessed by name, instead of by numbers. This makes it easy to determine what is being used, and more important, it protects them from misuse. So, on the Internet when you are using an applet, the program is sent from the server to the client's system; then it is verified before it is interpreted.
Here is more on multiple environments: In Java you can write one program and compile it once, and it will be capable of running on multiple environments. This means that Java code is 100 percent portable to all environments Java supports. Furthermore, the original source code is completely portable to any of these Java supported environments. Thus, you will only need to write the code once, and compile it once to port it to other platforms. As a result, Java is ripe for the heterogeneous environment of the Internet. Applets are interpreted by a Java-compliant browser on the client's system. By creating one applet it can run on a browser in UNIX, Mac, and the PC. This is probably one of the biggest selling points of Java and the reason for its success on the Internet.
Java is slow. One of the newer issues that has arisen, particularly
on the Internet, is that Java is relatively slow. This slowness
can be explained by looking at what is going on when a Java applet
is loaded on your system. First, the applet must be downloaded.
Then the applet must be verified. Third, the applet is interpreted
instead of executed. However, something known as a Just In Time
(JIT) compiler has been released to help increase the performance
of Java programs.
Programs that are interpreted are slower than programs that are executed.
Finally, any pictures or graphics also must be downloaded. All of these steps contribute to the slowness of Java. Some things are out of the direct control of Java, such as the download time, which is pretty much up to the server, the Internet traffic at the time, and the client's connection to the Internet. Java is, however, very efficient in making the compiled classes relatively small. Pictures and graphics are up to the mercy of the programmer who decides what to use in the applet. The remaining area that causes decreased speed in Java is the verification process. Although Java has the best of both worlds by being interpreted and compiled, this still forces Java to be significantly slower than C/C++.
At this point, Java does not have a good indigenous development environment, but several development environments have been released by other software development companies. The major development environment for Java is the Java Developers Kit (JDK). Essentially, it is a command-line compiler, and if you are in the Windows environment, basically all you will have to use is an ASCII text editor. The compiler comes with the JDK. If you would like to pick up the JDK, go to the following site:
Here is the security model for Java applets: When being downloaded from the Internet, a completely restricted environment is created for the Java applet to be placed in. This removes the capability to access files; nor can applets typically communicate with any other server than the one from where the applet came. Applets also cannot start programs on the client's system. This is a very limiting factor for Java. Probably one of the goals in future versions of Java will be finding a way to overcome these limitations without compromising security.
In this chapter, you learned what Java is and how it is part of the object-oriented programming language field. You also took a high-level look at the pros and cons of Java, including what makes it powerful and what it needs to work on for the future.
Java is still new (as far as programming languages are concerned), but has definitely established a presence in the industry.