Académique Documents
Professionnel Documents
Culture Documents
Java I/O streaming filter and pipe streams Byte Code interpretation - Threading Swing.
Bytecode Verification
I/O Stream
A program uses an output stream/writer to write data to a destination, one item at time:
Java's basic output class is java.io.OutputStream. Its signature is
public abstract class OutputStream
This class provides the fundamental methods needed to write data. These are:
public abstract void write(int b) throws IOException
public void write(byte[] data) throws IOException
public void write(byte[] data, int offset, int length)
throws IOException
public void flush( ) throws IOException
public void close( ) throws IOException
To read and write from files on a disk, use the following classes:
FileInputStream
FileOutputStream
FileReader
FileWriter
Each of these has a few constructors, where class is the name of one of the above
classes:
class(File) - create an input or output file based on the abstract path name
passed in
class(String)- create an input or output file based on the String path name
class(FileDescriptor)- create an input or output file based on a
FileDescriptor
class(String, boolean)- [for output classes only] create an input or
output file based on the path name passed in, and if the boolean parameter is
true, append to the file rather than overwrite it.
For example, we could copy one file to another by using:
import java.io.*;
public class FileCopy
{
public static void main(String args[])
{
try
{
// Create an input file
FileInputStream inFile = new FileInputStream("c:\\source.txt");
// Create an output file
FileOutputStream outFile = new FileOutputStream("c:\\target.txt");
// Copy each byte from the input to output
int c;
while((c = inFile.read()) != -1)
{
outFile.write(c);
}
// Close the files!!!
inFile.close();
outFile.close();
}catch(IOException e) { }
}
}
For example,
import java.io.*;
public class ReadByteArray
{
public static void main(String[] args)
{
// create byte array
byte[] bytes = {1,2,3,4,5,6,7,8,9};
try
{
// wrap an input stream around the byte array
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
// read each byte and echo to the console
int b;
while((b = in.read()) != -1)
{
System.out.println(b);
}
InputStream and OutputStream are fairly raw classes. They allow you to read and write bytes,
either singly or in groups, but that's all. Deciding what those bytes meanwhether they're
integers or IEEE 754 floating point numbers or Unicode textis completely up to the
programmer and the code. However, there are certain data formats that are extremely
common and can benefit from a solid implementation in the class library. For example, many
integers passed as parts of network protocols are 32-bit big-endian integers. Much text sent
over the Web is either 7-bit ASCII or 8- bit Latin-1. Many files transferred by ftp are stored in
the zip format. Java provides a number of filter classes you can attach to raw streams to
translate the raw bytes to and from these and other formats.
The java.io package provides a set of abstract classes that define and partially
implement filter streams. A filter stream filters data as it's being read from or written to the
stream. The filter streams are FilterInputStream, and FilterOutputStream. A filter stream is
constructed on another stream (the underlying stream). The read method in a readable filter
stream reads input from the underlying stream, filters it, and passes on the filtered data to the
caller. The write method in a writable filter stream filters the data and then writes it to the
underlying stream. The filtering done by the streams depends on the stream.
What are filter input /output streams?
As you can see in the following hierarchies, there exist 4 subclasses for FilterInputStream and 3
subclasses for FilterOutputStream.
The hierarchy contains 12 input stream classes. All the classes can be divided into two
categories high-level streams and low-level streams. All the subclasses
of FilterInputStream are known as high-level streams (there exists 4) and all the remaining are
known as low-level streams (there are 8). Filter streams can be linked to another to have higher
functionality, but subject to some rules.
Similar to input stream hierarchy, there exists output stream hierarchy also dealing with all
output stream classes.
All the subclasses of FilterOutputStream are known as high-level streams (there exists 3) and all
the remaining are known as low-level streams (there are 6). One filter stream can be linked to
another to have higher functionality (output),
Java comes with low-level byte streams and high-level byte streams and similarly low-level and
high-level character streams. Sub classes of FilterInputStream and FilterOutputStream are
known as high-level streams. There are four high-level streams on input streams side
LineNumberInputStream, DataInputStream, BufferedInputStream and PushbackInputStream.
FilterInputStream
For example,
import java.io.BufferedOutputStream;
import java.io.*;
public class LineDemo {
public static void main(String sr[]) throws Exception{
String str="the\nonly\ntrue\nwidom\nis\nin\nknowing\nknow\nnothing";
StringBufferInputStream sbstream=new
StringBufferInputStream(str);
LineNumberInputStream lnis=new LineNumberInputStream(sbstream);
DataInputStream distream=new DataInputStream(lnis);
Output:
1.the
2.only
3.true
4.widom
5.is
6.in
7.knowing
8.know
8.nothing
Pipes
Pipes in Java IO provides the ability for two threads running in the same JVM to communicate.
As such pipes are a common source or destination of data.
Creating a pipe using Java IO is done via the PipedOutputStream and PipedInputStream classes.
A PipedInputStream should be connected to a PipedOutputStream. The data written to the
PipedOutputStream by one thread, can thus be read from the connected PipedInputStream by
another thread
You can set the number of bytes you should be able to unread in the constructor of
the PushbackInputStream. Here is how that is done:
PushbackInputStream input = new PushbackInputStream(
new FileInputStream("c:\\data\\input.txt"),
8);
This example sets an internal buffer of 8 bytes. That means you can unread at most 8 bytes at a
time, before readingthem again.
SequenceInputStream
The SequenceInputStream combines two or more other InputStream's into one. First all bytes
from the first input stream is iterated and returned, then the bytes from the second input
stream. Here is a simple example:
InputStream input1 = new FileInputStream("c:\\data\\file1.txt");
InputStream input2 = new FileInputStream("c:\\data\\file2.txt");
The combined input stream can now be used as if it was one coherent stream.
DataInputStream
The DataInputStream class enables you to read Java primitives from InputStream's instead of
only bytes. You wrap an InputStream in a DataInputStream and then you can read primitives
from it. Here is an example:
DataInputStream input = new DataInputStream(
new FileInputStream("binary.data"));
This is handy if the data you need to read consists of Java primitives larger than one byte each,
like int, long, float, double etc.
DataOutputStream
The DataOutputStream class enables you to write Java primitives to OutputStream's instead of
only bytes. You wrap an OutputStream in a DataOutputStream and then you can write
primitives to it. Here is an example:
DataOutputStream output = new DataOutputStream(
new FileOutputStream("binary.data"));
output.close();
This is handy if the data you need to write consists of Java primitives larger than one byte each,
like int, long, float, double etc.
BufferedInputStream
The BufferedInputStream class provides buffering to your input streams. Buffering can speed
up IO quite a bit. Rather than read one byte at a time from the network or disk, you read a
larger block at a time. This is typically much faster, especially for disk access and larger data
amounts.
To add buffering to your InputStream's simply wrap them in a BufferedInputStream. Here is
how that looks:
You can set the buffer size to use internally by the BufferedInputStream. You provide the size as
a constructor parameter, like this:
InputStream input = new BufferedInputStream(
new FileInputStream("c:\\data\\input-file.txt"),
8 * 1024);
This example sets the internal buffer to 8 KB. It is best to use buffer sizes that are multiples of
1024 bytes. That works best with most built-in buffering in hard disks etc.
Except for adding buffering to your input streams, BufferedInputStream behaves exactly like
an InputStream.
BufferedOutputStream
The BufferedOutputStream class provides buffering to your output streams. Buffering can
speed up IO quite a bit. Rather than write one byte at a time to the network or disk, you write a
larger block at a time. This is typically much faster, especially for disk access and larger data
amounts.
You can set the buffer size to use internally by the BufferedOutputStream. You provide the size
as a constructor parameter, like this:
OutputStream output = new BufferedOutputStream(
new FileOutputStream("c:\\data\\output-file.txt"),
8 * 1024);
This example sets the internal buffer to 8 KB. It is best to use buffer sizes that are multiples of
1024 bytes.
Byte code interpretation
Java compiler compiles source programs into bytecodes, and a trustworthy compiler ensures
that Java source code does not violate the safety rules. At runtime, a compiled code fragment
can come from anywhere on the net, and it is unknown if the code fragment comes from a
trustworthy compiler or not. So, practically the Java runtime simply does not trust the incoming
code, and instead subjects it to a series of tests by bytecode verifier.
When a class loader presents the bytecodes of a newly loaded Java platform class to the virtual
machine, these bytecodes are first inspected a verifier. All classes except for system classes are
verified.
Here are some of the checks that the verifier carries out:
Variables are initialized before they are used.
Method calls match the types of object references.
Rules for accessing private data and methods are not violated.
Local variable accesses fall within the runtime stack.
The runtime stack does not overflow.
If any of these checks fails, then the class is considered corrupted and will not be loaded.
You can, however, deactivate verification with the undocumented -noverify option.
For example,
java -noverify Hello
The bytecode verifier traverses the bytecodes, constructs the type state information, and
verifies the types of the parameters to all the bytecode instructions.
The illustration shows the flow of data and control from Java language source code through the
Java compiler, to the class loader and bytecode verifier and hence on to the Java virtual
machine, which contains the interpreter and runtime system.
The important issue is that the Java class loader and the bytecode verifier make no assumptions
about the primary source of the bytecode stream--the code may have come from the local
system, or it may have travelled halfway around the planet. The bytecode verifier acts as a sort
of gatekeeper: it ensures that code passed to the Java interpreter is in a fit state to be executed
and can run without fear of breaking the Java interpreter. Imported code is not allowed to
execute by any means until after it has passed the verifier's tests. Once the verifier is done, a
number of important properties are known:
There are no operand stack overflows or underflows
The types of the parameters of all bytecode instructions are known to always be correct
Object field accesses are known to be legal--private, public, or protected
While all this checking appears excruciatingly detailed, by the time the bytecode verifier has
done its work, the Java interpreter can proceed, knowing that the code will run securely.
Knowing these properties makes the Java interpreter much faster, because it doesn't have to
check anything. There are no operand type checks and no stack overflow checks. The
interpreter can thus function at full speed without compromising reliability.
Here's an example of how to construct such an altered class file. Assume that a program called
VerifiesTest.java has the following code fragment which is a method and displays the method
result. The program can be run both as a console program and as an applet. The fun method
itself just computes 1 + 2.
static int fun()
{
int m;
int n;
m = 1;
n = 2;
int r = m + n;
return r;
}
As an experiment, after the compilation, in the class file, the compiled codes of the above
method if it gets modification as
static int fun()
{
int m=1;
int n;
m = 1;
m = 2;
int r = m + n;
return r;
}
After this changes if it is recompiled the compiler detects that problem and refuses to compile
the program. In this case, n is not initialized, and it could have any random value.
To make changes in class file, we have to work a little harder. First, run the javap program to
find out how the compiler translates the fun method. The command
javap -c VerifierTest
shows the bytecodes in the class file in mnemonic form.
Method int fun()
0 iconst_1
1 istore_0
2 iconst_2
3 istore_1
4 iload_0
5 iload_1
6 iadd
7 istore_2
8 iload_2
9 ireturn
We use a hex editor to change instruction 3 from istore_1 to istore_0. That is, local
variable 0 (which is m) is initialized twice, and local variable 1 (which is n) is not initialized at
all. We need to know the hexadecimal values for these instructions. These values are readily
available from The Java Virtual Machine Specification.
0 iconst_1 04
1 istore_0 3B
2 iconst_2 05
3 istore_1 3C
4 iload_0 1A
5 iload_1 1B
6 iadd 60
7 istore_2 3D
8 iload_2 1C
9 ireturn AC
You can use any hex editor such as Gnome hex editor shown below to carry out the
modification.
Change 3C to 3B and save the class file. Then try running the VerifierTest program. You get an
error message:
Exception in thread "main" java.lang.VerifyError: (class: VerifierTest, method:fun
signature:() Accessing value from uninitialized register 1
Threads
Multitasking: the ability to have more than one program working at what seems like the same
time. For example, you can print while editing or sending a fax
Multithreaded programs extend the idea of multitasking by taking it one level lower: individual
programs will appear to do multiple tasks at the same time. Each task is usually called a thread
which is short for thread of control. Programs that can run more than one thread at once are said
to be multithreaded. For example, a browser should be able to simultaneously download multiple
images. An email program should let you read your email while it is downloading new messages.
What is the difference between multiple processes and multiple threads?
The essential difference is that while each process has a complete set of its own variables,
threads share the same data.
Multithreading has several advantages over Multiprocessing such as;
Threads are lightweight compared to processes
Threads share the same address space and therefore can share both data and code
Context switching between threads is usually less expensive than between processes
Cost of thread intercommunication is relatively low that that of process
intercommunication
Threads allow different tasks to be performed concurrently.
Creating threads: java.lang.Thread
Two ways of creating threads: implementing an interface and extending a class.
import java.lang.*;
public class Counter extends Thread
{
public void run()
{
....
}
}
Runnable threads
Once you invoke the start method, the thread is runnable. A runnable thread may not yet be
running.
1. If a thread has been put to sleep, the specified number of milliseconds must expire.
2. If a thread is waiting for the completion of an input or output operation, then the operation
must have finished.
3. If a thread called wait, then another thread must call notifyAll or notify.
4. If a thread is waiting for an object lock that was owned by another thread, then the other
thread must relinquish (surrender) ownership of the lock.
5. If a thread has been suspended, then someone must call its resume method. However, since the
suspend method has been deprecated, the resume method has been deprecated as well, and you
should not call it in your own code.
Dead Threads
A thread enters the blocked state when one of the following actions occurs:
1. Someone calls the sleep() method of the thread.
2. The thread calls an operation that is blocking on input/output, that is, an operation that will not
return to its caller until input and output operations are complete.
3. The thread calls the wait() method.
4. The thread tries to lock an object that is currently locked by another thread.
5. Someone calls the suspend() method of the thread
Daemon Threads
A daemon is simply a thread that has no other role in life than to serve others. Examples are
timer
threads that send regular "timer ticks" to other threads.
Thread Groups
Using a thread group you can simultaneously work with a group of threads.
The string argument of the ThreadGroup constructor identifies the group and must be unique.
You then add threads to the thread group by specifying the thread group in the thread
constructor.
Thread Methods
boolean isAlive() returns true if the thread has started and has not yet terminated.
void suspend() suspends this thread's execution. This method is deprecated.
void setDaemon(boolean on) marks this thread as a daemon thread or a user thread.
java.lang.ThreadGroup Methods
ThreadGroup(String name) creates a new ThreadGroup. Its parent will be the thread group of
the current thread.
int activeCount() returns an upper bound for the number of active threads in the thread
group.
void interrupt() interrupts all threads in this thread group and all of its child groups.
Thread Priorities
In Java, thread scheduler can use the thread priorities in the form of integer value to each of its
thread to determine the execution schedule of threads . Thread gets the ready-to-run state
according to their priorities. The thread scheduler provides the CPU time to thread of highest
priority during ready-to-run state.
Priorities are integer values from 1 (lowest priority given by the constant
Thread.MIN_PRIORITY) to 10 (highest priority given by the constant
Thread.MAX_PRIORITY). The default priority is 5(Thread.NORM_PRIORITY).
Constant Description
The maximum priority of any thread (an int value
Thread.MIN_PRIORITY
of 10)
The minimum priority of any thread (an int value
Thread.MAX_PRIORITY
of 1)
The normal priority of any thread (an int value of
Thread.NORM_PRIORITY
5)
The methods that are used to set the priority of thread shown as:
Method Description
setPriority() This is method is used to set the priority of thread.
thrun(String st,int p)
{
t = new Thread(this,st);
t.setPriority(p);
t.start();
}
publicvoid run()
{
System.out.println("Thread name : " + t.getName());
System.out.println("Thread Priority : " + t.getPriority());
}
class priority
{
publicstaticvoid main(String args[])
{
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
thrun t1 = new thrun("Thread1",Thread.NORM_PRIORITY + 2);
thrun t2 = new thrun("Thread2",Thread.NORM_PRIORITY - 2);
System.out.println("Main Thread : " + Thread.currentThread());
System.out.println("Main Thread Priority : " + Thread.currentThread().getPriority());
}
}
/*
Output
*/
Methods for inter process communication
wait( ) tells the calling thread to give up the monitor and go to sleep until some other thread enters the same
monitor and calls notify( ).
notify( ) wakes up the first thread that called wait( ) on the same object.
notifyAll( ) wakes up all the threads that called wait( ) on the same object.
The producer-consumer problem (also known as the bounded-buffer problem ) is another classical example of a
multithread synchronization problem. The problem describes two threads, the producer and the consumer, who
share a common, fi xed-size buffer. The producers job is to generate a piece of data and put it into the buffer. The
consumer is consuming the data from the same buffer simultaneously. The problem is to make sure that the
producer will not try to add data into the buffer if it is full and that the consumer will not try to remove data from
an empty buffer.
The solution for this problem involves two parts. The producer should wait when it tries to put the newly created
product into the buffer until there is at least one free slot in the buffer. The consumer, on the other hand, should
stop consuming if the buffer is empty. Take a message queue as an example, the following code shows a simple
implementation of such a message queue.
The synchronized keyword is essential as both get/put methods are accessing the shared data which is the
message queue buffer list.
class Q {
int n;
booleanvalueSet = false;
synchronizedint get() {
if(!valueSet)
try {
wait();
} catch(InterruptedException e) { }
class Test
{
public static void main(String args[]) {
Q q = new Q();
new Producer(q);
new Consumer(q);
System.out.println("Press Control-C to stop.");
}
}
Output
Inside get( ), wait( ) is called. This causes its execution to suspend until the Producernotifies you that some data is
ready. When this happens, execution inside get( )resumes. After the data has been obtained, get( ) calls notify( ).
This tells Producer thatit is okay to put more data in the queue. Inside put( ), wait( ) suspends execution until
theConsumer has removed the item from the queue. When execution resumes, thenext item of data is put in the
queue, and notify( ) is called. This tells the Consumerthat it should now remove it.
Progress Bars
Methods
Example
A JSlider component is intended to let the user easily enter a numeric value bounded by a minimum and
maximum value.
With the JTabbedPane class, you can have several components, such as panels, share the same space. The user
chooses which component to view by selecting the tab corresponding to the desired component.
With the JTable class you can display tables of data, optionally allowing the user to edit the data. JTable does not
contain or cache data; it is simply a view of your data. Here is a picture of a typical table displayed within a scroll
pane:
With the JTree class, you can display hierarchical data. A JTree object does not actually contain your data; it simply
provides a view of the data. Like any non-trivial Swing component, the tree gets data by querying its data model.
Here is a picture of a tree:
JSpinner: Spinners are similar to combo boxes and lists in that they let the user choose from a range of values. Like
editable combo boxes, spinners allow the user to type in a value. Unlike combo boxes, spinners do not have a
drop-down list that can cover up other components. Because spinners do not display possible values only the
current value is visible they are often used instead of combo boxes or lists when the set of possible values is
extremely large. However, spinners should only be used when the possible values and their sequence are obvious.
With the JInternalFrame class you can display a JFrame-like window within another window.
JToolTip: When the user of the program pauses with the cursor over any of the program's buttons, the tool tip for
the button comes up.
A JToolBar is a container that groups several components usually buttons with icons into a row or column.
ServerSocket
The java.net.ServerSocket class is used by server applications to obtain a port and listen for
client requests
ServerSocket constructors:
public ServerSocket(int port) throws IOException
Attempts to create a server socket bound to the specified port. An exception occurs if the port
is already bound by another application.
public ServerSocket(int port, int backlog) throws IOException
Similar to the previous constructor, the backlog parameter specifies how many incoming
clients to store in a wait queue.
public ServerSocket() throws IOException
Creates an unbound server socket. When using this constructor, use the bind() method when
you are ready to bind the server socket
ServerSocket Methods:
Socket
The java.net.Socket class represents the socket that both the client and server use to
communicate with each other.
Socket constructors:
1. c:\jdk\bin>java server
2. c:\jdk\bin>java Client
In Client window
Hi there!
DateServer program
Client program
public class Client {
public static void main(String[] args) throws Exception {
int port = 678;
InetAddress serverAddress = InetAddress.getLocalHost();
Socket client = new Socket(serverAddress, port);
System.out.println("Connecting on port " + port);
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from "+client.getInetAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("Server says " + in.readUTF());
client.close();
}
}
Output
In Server window
In Client window