Java

Warm up Questions

What is Java

[Albert]: Depends on who you are and what you are talking, Java can be a nice cup of coffee or an object oriented programming language invented by Sun Corp. or a virtual machine invented by Sun Corp.

What is Java final method

[Albert]: All Java methods by default can be overrided by subclasses. This is quite different from C++ where all methods by default can't be overrided by subclasses.

To make a Java method not overrideable by its subclasses, a keyword final should be used when declaring the method. In comparison, to make a C++ method overrideable by its subclasses, a keyword virtual should be used.

However, the difference here is not entirely syntactical. When Java makes a method non-overrideable using keyword final, its subclasses can't have any methods of the same name, otherwise Java compiler will generate a syntax error. On the other hand, when C++ makes a method non-overrideable by not specifying it as virtual, its subclasses can still have their own methods of the same name. The result is that different methods of the same name will be called at different time. For instance:

class level1 {
public void dummyMethod () {
cout << "level1 object";
}
};

class level2 : public level1 {
public void dummyMethod () {
cout << "level2 object";
}
};

void main () {
level2 *poTest2 = new level2;
level1 *poTest1 = poTest2;

poTest1->dummyMethod();
poTest2->dummyMethod();
}

In this C++ code, both class level1 and level2 have a non-virtual method of the same name and it is perfect legal. Note that the object oTest1 and Test2 are the same object viewed at different class level. The result from this arrangement is that different dummyMethod() is used when the method of the same object is called at different level. This feature may be useful or confusing depending on who you are and your programming taste.

Why Java "main()" function has to be static

[Albert]: Different from C++ or C where the "main()" function is not a method and declared outside a class declaration, Java requires that the "main()" function is declared as a method within a class:

class Foo {
public static void main (String argv[])
{
System.out.println ("Hello, World");
}
}

However, when method "main()" is called, the class "Foo" has not been created yet. Java allows program to use a method of a class without creating the object for that class, however, the method must be declared as "static". In fact, in this example, the class "Foo" is not created at all. In other words, Java does not need to have a "Foo" object to run program "Foo".

Does Java has DLLs

[Albert]: Yes (are you a Visual C++ programmer?), but they are not referred to as Dlls (dynamic linked libraries) like in C++. A Java class file is the equivalence of the C++ style DLLs. It has following characteristics:

  1. it is loaded and linked on the fly
  2. it can be changed independently without affecting the Java programs or Java classes that are using it
  3. it will not be recompiled if the Java programs or classes that use it are changed
  4. it reduces the program size but increases the program loading time
  5. it makes the program version control harder.

One thing about Java is that everything in Java is a class file: a class, a program, or an applet. On the other hand, in C++, you can have executables, static libraries, and DLLs.

How to start a Java Virtual Machine

[Albert]: To start a Java virtual machine, run a program called "Java".

Java virtual machine does not support sessions, you can only start the Java virtual machine when you run a Java program. The Java virtual machine will shut itself down automatically when the application exits. For example, if you want to run a Java program test.class, you do:

java test

This will start a Java virtual machine and execute the program test.class. Once the program ends, the virtual machine shuts itself down and returns the control to the host machine.

What it would be like if Java Virtual Machine supports sessions

[Albert]: The following is an imagined session based scenario:

java

>test1

>test2

>shutdown

What we have here is: the first statement lets us enter a virtual machine session, then we can run as many Java programs as we want and run them as many times as we want. After we finish all, we explicitly shut down the virtual machine and come back to the host machine.

This actually seems very logical and make Java virtual machine more like a physical machine.

Why Java Virtual Machine is not session based

[Albert]: The problem with the session based virtual machine is that we have to develop a set of session commands to support a Java virtual machine session, such as dir, cd, mkdir, rmdir, copy, delete, and etc. The good news is that these commands will become platform independent as well, the bad news is that nobody wants to spend money and time to think it through and actually implement it. 

 

Basic Questions

Five ways of using "super" and "this"

[Albert]: Java supports super and this notations. Super is a convenient way to indicate the super class of a class and this is a convenient way to self-identify a class itself.

Super is used mainly for cascaded initialization during a hierarchical object construction (case 1), overriding super class methods (case 2), and naming conflict resolution between super class and subclass (case 3). This is used mainly for self-manipulation (case 4) and the naming conflict resolution between class variables and method-bound local variables (case 5). Following is an example of these four scenarios:

class DummySuper
{
int w;
int x;
int y; // assigned by sub-class
int z; // assigned by sub-class
Registray subList; // some registration class

DummySuper (int x)
{
int w = 15;

this.x = x; // case 5: naming conflict
this.w = w; // case 5: naming conflict
}

public void dummyMethod ()
{
return;
}
}

class DummySub extends DummySuper
{
int z;

DummySub (int x, int y)
{
super (x); // case 1: cascaded init
super.y = y; // case 3: naming conflict
super.z = z; // case 3: naming conflict
subList.add (this);
// case 4: self manipulation
}

public void dummyMethod // overriding
{
System.out.println("dummyTask");
super.dummyMethod (); // case 2: overriding
return;
}
}

Properly use "super" and "this"

[Albert]: The only way in Java we can initialize the super class through a subclass construction is to call "super". Therefore, the "case 1" (shown above) is a legitimized and necessary use of super.

To extend the functionality of a super class method, we have to override the method first through conflicting naming mechanism, then put the new tasks in, and finally call the original method to complete the task using super. Therefore, the "case 2 " is a legitimized and necessary use of super.

It is always bad idea to hide the super class variables by using conflicting names. It accomplishes nothing but creates confusions. The confusion is due to the fact that the hidden variables are not "hidden" in a consistent manner. They are hidden if subclass methods are used or if super class methods are overridden during a polymorphic process. On the other hand, the hidden variables will be used (most of the time unintentionally) if the super class non-overridden or final methods are used during a polymorphic process. Therefore, the "case 3" is a bad coding practice.

The most legitimized use of this is self-manipulation ("case 4"). For instance, if an object want to add/remove itself into/from a registry, post itself onto a message queue, or link/delete itself into/from a link list.

There is really no reason why one should use conflicting names between a class and its methods. Like the "case 3", it accomplishes nothing but creates confusions. Therefore, the "case 5" (above) should always be avoided by simply using different names.

Why Class Based Hiding is Problematic

[Albert]: When there is a naming conflict between the supper and subclass, the super class variable or method is hidden by the subclass variable or method of the same name. For instance:

class superCls {
public int x = 6;
}

class subCls extends superCls {
public int x = 9; // hide the superCls x
}

class test {
public Foo {
subCls oMe = new subCls();
superCls oMeAgain = oMe; // same object

int x1 = oMe.x; // x1 = 9
int x2 = oMeAgain.x; // x2 = 6
}
}

This is just like a name scope issue associated with C++ program blocks: the variables defined in the outer block will be hidden by the variables of the same names defined in the inner block:

{
int x = 6;
if (x == 6)
{
// outer x is hidden and new x is declared
int x = 9;

// outer x is hidden and will print x = 9
printf ("x = %d", x);
}

// inner x is expired and outer x is restored
printf ("%d", x); // will print 6
}

Hidden variables are different from private variables, it is not a protection mechanism but a side effect of a bad programming style. You can use the hidden variables (if they are not private themselves), you just do not have the straightforward way to use them because you have hidden them through naming conflict.

The block based hiding provides no benefit but confusion and generally avoidable (by simply using different names) so that Java does not even support it (that is why we only have a C++ example above). The same statement is true for class based hiding except that it is difficult to avoid the naming conflict especially when we derive our own classes from other people's classes. In this case, we do not want mind other people's variables at all. This being ignorant attitude is fine as long as we are consistent, that is, we will not later snoop and manipulate other people's variables directly.

super.super

[Albert]: Java does not support super.super, in other words, you have no direct way to access super super class variables if you have hidden them using naming conflict or super super class methods if you have overridden them. Namely:

super.super.x // access hidden variables
super.super.a() // access overridden method

are both invalid.

The rational here is that never manipulating the grand-dady directly. If this has to be done, then talking to him through dady. Following are the "tricks":

// access super.super by creaing corresponding super
// methods
super.getYourSuper_x() // trick 1
super.callYourSuper_a() // trick 2

In fact, you should never manipulate the hidden super super components at all. First of all, it is bad idea to have hidden super variables at first place (case 3 in previous question), therefore, you should have no hidden super super variables. Secondly, as we discussed in "case 2" previoud question, the only time that the super's methods should be used is when extending the functionality through overriding for polymorphism. However, with the "trick 2" you can access the hidden method, but it does not support the polymorphism. More specifically, with "trick 2", you can "call" the hidden super super methods but you cannot "override" them.

Load a Library into Runtime Dynamically and Statically

[Albert]: You can load a library dynamically anywhere in your Java program by calling:

System.loadLibrary("LibraryName");

The library name can be a package name or a class name. The default is "java.*", so it is the same to write "java.net" or simply just "net". Since all Java source codes are compiled into class files, everything in Java can be dynamically loaded.

To load a library statically when a class is created, you can simply declare a static block and load the library in the block.

static {
System.loadLibrary("LibraryName");
}

Sometimes, the statically loaded libraries are actually loaded dynamically, it is because a dynamically loaded library may contain static blocks and Java virtual machine only see this static blocks after the library is loaded.

Create a Thread through Subclassing or Containment

[Albert]: Some people believe the class inheritance (through subclassing) and others believe object inheritance (through containment). When creating a thread, Java gives people the choice, that is, you can either create a thread through subclassing or through containment.

Following demonstrate two different methods using the same simple example:

class Foo1 implement Runnable
{
run ()
{
System.out.println
("this is an anonymous thread");
}
}

or

class Foo2 extends Thread
{
run ()
{
System.out.println
("this is an anonymous thread");
}
}

These two classes are the same except the prototypes. Where Foo1 is a class that implements Runnable interface and Foo2 is a subclass of the Java class Thread. Since the Java class Thread also implements the Runnable interface, Foo2 supports Runnable interface by inheritance.

Because although class Foo1 supports Runnable interface, it is not a thread, therefore, to start a thread for an object oFoo1, we have to create a host Thread object to host oFoo1:

Foo1 oFoo1 = new Foo1;
Thread oThrd = new Thread(oFoo1);

After creating the host object oThrd, we can run it by calling the Thread object's run() method through Runnable interface:

oThrd.start();

Underneath, oThrd's run() method will delegate the task to Foo1's run() method, in other words, under the hood, it is Foo1's run method does the real work. Similarly, to stop, suspend, and resume the Foo1, we stop, suspend, and resume oThrd and under the hood, oThrd delegates the real work to Foo1.

Above gives the containment scenario where a host object contains a real working object. The keyword here is the delegation, that is, the host object delegates the tasks to the contained object.

On the other hand, Foo2 is a Thread object in its own right and can be started, stopped, suspended and resumed directly through its Runnable interface.

Foo2.start()
Foo2.stop()
Foo2.suspend()
Foo2.resume()

 

 

 

What is Java ServerSocket

[Albert]: Java ServerSocket is a class for Java programmer to create a TCP anchor socket.

A socket is a TCP/IP network terminator. There are two types of sockets: TCP socket and datagram socket. The former is the terminator of a reliable session based connection and the latter is the terminator of a temporal message path (you may imagine it as a virtual connection existing only for one message).

In order to establish a TCP connection, we need two kinds of sockets, the anchor socket and the regular socket. The anchor socket can not be used to transmit the data, therefore it never been a part of TCP connection. The anchor socket has a fixed public IP address (like a hotel address) and port (like a room number) so that a regular socket can send connection requests to the anchor. Once the anchor socket receives a connection request from a remote socket, it creates a local regular socket to establish the reliable session based connection.

The process for anchor socket to claim its public IP address and port on one machine is called binding. Each machine is like a hotel, there are limited number of ports (like hotel rooms) and if the port is occupied by other anchor socket, the new anchor socket has to find a different port and let all its potential contacts (regular sockets) know it.

For a regular socket to connect to an anchor socket, all it needs to know is the machine's IP address and the anchor's port number. Usually, the anchor socket can change to different port number but it has to let all its regular sockets know it.

In two situations, you should make the anchor socket on the same port (by kicking out of the other anchor socket if necessary). First, if the application that uses this anchor socket is on multiple machines, you should make sure the same anchor socket on all machines are using the same port and stay the same, so that you will not confuse yourself or other people. Secondly, if the application that uses the anchor socket is used by lots of people or client applications, you should make sure the anchor socket uses the same port and stay the same. Otherwise, you will have a (or several) very long day to inform everyone and to adjust every client application.

The process that an anchor socket is waiting for remote socket connection request is called listening.

Once the anchor socket is in listening mode, we can either ignore it (kind of contradicting to all we have done) or monitor it. The de facto function name for this monitoring is called accept(). The accept() will block and wait until the anchor socket receives a connection request. Then the accept() will create a new local regular socket and establish the connection. At this time, the only thing we need to do is to pick up the local regular socket to talk (to the remote socket).

What is Java Socket

[Albert]: Java Socket is a class for Java programmer to create a regular TCP socket (see What is Java ServerSocket).

There are two kinds of regular socket, the client socket which is created explicitly by a client application and the server socket (we have a name clashing with Java definition) which is created by the anchor socket's Accept(). The client socket is always responsible to go out to the network to seek the server end of the connection and the server socket is only responsible to complete the connection when reached by the client. However, after the connection is established, these two sockets will behave the same.

If the socket server (proxy server) presents, client socket will first make a TCP connection to the proxy and then send a proxy specific command to the proxy. This proxy specific command is to ask proxy to carry out the actual connection task to the server end on behalf of the client socket. Upon receiving the command, the proxy will try to establish a TCP connection to the server machine.

In this proxy approach, there are two segments of TCP connections. The first one is from local socket to the proxy and the second is from the proxy to the remote machine. The proxy is not a dummy middleman, it can monitors the traffic and even manipulate the data.

How Java Socket Knows if there is a Socket Server

[Albert]: When Java virtual machine establish the Runtime environment, it will collect the Socket Server information from the operating system and save the data into Java.lang.System.properties.

What the Java Socket does is to check the system properties to see if the entry for Socket Server is empty. If it is not empty, it means there is a Socket Server and the Java Socket will then get the machine's IP address and the Socket Server's port number from the system properties. Once IP address and port number are know, Socket can connect to the Socket Server just like any kind of regular TCP anchor socket.

 

 

 Advanced Questions

Why ThreadDeath Has to be Rethrown

[Albert]: Assume that we have thread A and thread B. When A calls B's "stop()" method, B is forced to stop what it is doing immediately and throws a ThreadDeath exception. Throwing ThreadDeath will cause the B's finally clause of the try block to be executed.

It is important that application should not try to catch the ThreadDeath exception, otherwise thread B cannot die properly.

The ThreadDeath will be eventually catched by the top-level error handler where the ThreadDeath is recognized and handled properly.

 

 Philosophical Questions

 

Is Java InputStream for Object Inheritance

[Albert]: Yes, the abstract class InputStream is designed both for class inheritance and for object inheritance. The same statement is true for other Java classes such as OutputStream, Reader, and Writer.

The purpose for class inheritance (by not specifying the class as final) is to create a standard framework and interface for a specific implementation.

The purpose for object inheritance is to add generic enhancement to the class without knowing any specific implementation.

The class inheritance is archived by subclassing and method overriding. On the other hand, object inheritance is archived by containment and task delegation.

The abstract Java class InputStream is such a class that needs both implementation and enhancement. The examples of specific implementations include socket input stream, file input stream, byte array input stream, and string input stream. The examples of the enhancements provided by Java development kits include buffered input stream, data input stream, and object input stream.

It is easy to see that all specific implementation classes above are the subclasses of InputStream. How about those enhanced classes? Since all InputStream enhanced classes are also InputStream in their own right (just like all enhanced dog foods are dog foods), these enhanced classes are also the subclasses of the InputStream. Here things become rather convoluted. To clarify this little bit (intentionally or unintentionally we do not know), Java development kids (up until version 1.1.7) provides a filter class called FilterInputStream. What this class does is to externalize the relationship between class inheritance and containment/delegation (the object inheritance).

Class FilterInputStream is an abstract subclass of class InputStream. It takes an object of type InputStream as input, keep that object internally (containment) during the full course of its life span, and delegates its core task (i.e. read() ) to that object. Class FilterInputStream is not necessary for object inheritance but it serves as an example that shows how to make a class "object-inherit" from another class.

The key feature of the enhanced InputStream classes is that they all take an object of type InputStream as input, keep that object internally, and delegates the method read() to that object.

Under this framework, to apply an enhancement to a specific implementation, what we need to do is first creating the implementation object then using that object as the input to construct the enhancement object. Finally, the object, that we are actually going to use for real work, is the enhancement object not the implementation object.

For instance:

// 1) create the implementation object

ByteArrayInputStream oBAIS =
new ByteArrayInputStream();

// 2) create the enhancement object using
// implementation object

BufferedInputStream oBIS =
new BufferedInputStream(oBAIS);

// 3) use enhancement object for real work

iNum = oBIS.read (cBuf, MAX_READ_LENGTH);

Where oBIS is an InputStream as well as contains an InputStream. It delegates the task read() to oBAIS, does some enhancement on the return result from oBAIS's read(), then return the enhanced result through its own read() to the caller.

The reason we call this is the object inheritance at work is that it is very logic to think that oBIS is a sub-object of oBAIS (in a similar logic as subclassing). Through object inheritance we have the sub object enhance the super object and through class inheritance we have the sub-class enhance the super class. In the former the sub-object is the super object (e.g. my Christmas decorated house is my house) and in the latter the sub-class is the super class (e.g. dogs are animals).

Above oBIS and oBAIS are classic example of the object inheritance where oBIS is oBAIS. We can either use oBIS or oBAIS, we will get the same result. The oBIS is easier to use because it is a enhanced oBAIS, that's all.

Is Java Programming Language Platform Independent

[Albert]: No (surprise), Java programming language is specifically for Java virtual machine. The Java program, regardless compiled or not, will not run on any machine with any operating system except the Java virtual machine. It is the Java virtual machine's independence makes the Java program run anywhere.

Is Java Virtual Machine Platform Independent

[Albert]: No (another surprise), Java virtual machine is platform dependent. We have to develop different Java virtual machines for different platforms. The virtual machine for Unix is a Unix program and will not run on Windows NT.

How Java Programming Language claims Platform Independence

[Albert]: The foundation of the platform independence of Java programming is based on the promise that Java virtual machine will be developed for every existing platform as well as for any future new platform. However, this promise, like any other promises, can be broken.

In reality (at least for the current computer architecture), there is no such thing called write once run everywhere. To run something everywhere, we have to install a virtual machine everywhere first. It is possible and relatively easy to support this kind of pseudo platform independence for interpreted languages (including scripting languages, such as tcl and perl) because their interpreters are their "virtual machines". However, it is harder if not impossible to support such pseudo platform independence for compiled high level languages, such as C++, because the result of the compilation is the machine code.

The trick used by Sun Corp is to make Java programming language a half- compiled hybrid. Java programming language has the usual look and feel of the normal high level languages but the result of its half-compilation is not machine code but bytecode stream. The bytecode stream is in essence a very efficient low level interpreted language. Because the bytecode is an interpreted language, it has an interpreter called, you can guess, the Java virtual machine.

By porting Java virtual machine to every platform, we have created pseudo platform independence for Java programs and classes (they are all bytecode streams).

What is the best performance Java can get

[Albert]: Since bytecode is very low level, the Java interpreter (virtual machine) can perform almost as efficient as a good compiler-generated machine code.

However, since the bytecode is not machine code and can not be executed by the machine directly, theoretically, a good Java compiler along with a good virtual machine can never beat a good compiler generated machine code. However, in practical, since the overhead of the interpretation of bytecode can be marginal (especially with JIT technology), the Java program can perform so well that you won't see the difference.

Or from other perspective, the difference between a Java compiler and a C compiler may be smaller than the difference between two C compilers. In the sense, Java performance may be a non-issue after all.

What is a Java Machine

[Albert]: A Java machine is a machine that can execute Java bytecode stream. It has nothing (or little thing if you insist) to do with Java programming language.

Currently, we have many off-the-shelf or internet downloadable Java compilers (a program called "javac") to convert a Java program into a Java bytecode stream.

Currently, we do NOT have any C++ Java compiler (a program called "cppjavac", interesting to no real life form so far) to convert a C++ program into a Java bytecode stream.

It is possible to convert a Java program to a non-Java machine executable. On the other hand, it is possible to convert programs of any other languages (including scripting languages) into Java bytecodes.

What is a Java Virtual Machine

[Albert]: A Java machine can be a hardware based machine or a software based machine. A software based Java machine is a non-Java computer with a software layer that makes the computer look like a Java machine. The software based Java machine is called Java virtual machine because it is not a physical Java machine in a traditional sense but it acts exactly like a physical Java machine to the end user.

What is a Sun's Java Virtual Machine

[Albert]: The Sun Java virtual machine is developed first by Sun Corp.

It turns out that the Sun's Java virtual machine is the only game in the town. People can develop a totally different Java virtual machine to execute the Java bytecode streams in totally different fashion. But so far, everyone is enhancing Sun's Java virtual machine, no one want to invent something drastically different, mainly because there is no business case for it.

Today, when people talk about Java virtual machine, they really mean Sun's Java virtual machine. This is fair, since after all, Sun Corp. is the inventor of the concept of the Java virtual machine.

Which one is better, the Real Java Machine or the Virtual One

[Albert]: The answer is "the virtual one is better", at least for the time being. Because the massive production of the non-Java computers, it is actually much cheaper to build a software based Java virtual machine than build a physical one. Also, since so much investment has been pouring into the super-enhanced non-Java computers and software development, a Java virtual machine will likely be faster than a cheap physical (real) Java machine.

In addition, it is much simpler to integrate Java based applications with legacy systems if we are using virtual machines since, in this case, both Java applications and the legacy systems are on non-Java computers. For instance, it is harder to integrate a real Java toaster (physical machine) than a Java toaster program (virtual machine) with an Oracle database.

Besides, because the non-Java computers have already become so cheap to build today, there will probably never be an economic incentive for people to build a general-purposed physical Java machine (missed the opportunity). It is likely that if there are ever physical Java machines to be built, they will be task-specific ones, like Java toaster, Java TV, or a Java bank machines.

 

Related Page 1 | Related Page 2 | Related Page 3