Wednesday, February 22, 2012

Choosing good abstractions

Coupling and cohesion
The efficiency of the programs you design depends on how well you choose the classes they use.

Well-abstracted classes lead to programs that are easy to maintain and extend. However, if you choose bad abstractions, you could end up with an error-prone application that doesn't model the problem domain effectively.
Simplicity is the key to choosing good classes.

The classes you choose should capture only one key abstraction. If a class performs two functions, then you should split it into two separate classes, each with its own function.
There are five key concepts you should consider when choosing classes:
  • coupling
  • cohesion
  • sufficiency
  • completeness
  • primitiveness
These allow you to measure how well-designed classes are.
Coupling refers to how tightly connected classes are. If a class is very dependent on other classes to carry out its functions, then it is strongly coupled.
Strongly coupled classes can lead to problems in program design. Because such classes are interdependent, they are difficult to extend, and bugs in one can affect others.

This interdependency can also make it difficult for other programmers to understand programs that contain such classes.
To perform adequately, classes must be connected to other classes. But you should choose a level of connection that doesn't make one class too dependent on another.

This is particularly important when you design for inheritance.
Though inheritance is a form of coupling, it can make programs easier to design and extend.

So when choosing classes, you need to balance the requirements of inheritance against the need for weak coupling.
Cohesion is a measure of how closely related the elements in a class are. These elements are the states, behaviors, and functionalities of the class.
A class whose members are simply grouped together and have little in common is only coincidentally cohesive. Such a class might have two unlinked functions, or it may group object states with unrelated object behavior.

Because its elements do not cohere to realize a single purpose, such a class is confusing, difficult to use, and often too complex to implement.
Let's say you used a class named BankEmployee to model how bank staff are paid and how they interact with customers.

Since the elements of these separate functionalities are unrelated, this class would be a bad abstraction.
Classes with good cohesion are well defined and contain elements that properly belong together.

Such classes are easy to understand and use because they serve a definite purpose – they have functional cohesion.

Sufficiency, completeness, and primitiveness
Sufficiency, completeness, and primitiveness are related principles.

A good class needs to balance the requirements of each of these principles before it can function well.
  • Sufficiency
  • Completeness
  • Primitiveness
Sufficiency
Sufficiency is the characteristic that ensures classes are able to function effectively.

You must create classes that contain all the attributes and operations necessary to perform the tasks you require of them. So if you create a Car class, you must include an accelerate operation – otherwise no car object will be able to change speed.

Sufficiency means that you should include only those attributes and operations that are needed for the system you are modeling.

If you want to create a class that models a typical car, you need to specify things like its speed and model.

But you don't need to specify what type of suspension the car uses – this would complicate the class without adding any important functionality to it.

Whether a class is sufficient or not depends on its purpose and on the level of detail it is intended to capture.

If you need to represent something in great detail, then you might have to include specifications that would not be needed in a more abstract model.
Completeness
Completeness determines whether the interface of the class captures all the behavior of the class.

The attributes and operations you give to a class must be general enough to allow other classes to communicate with it.
Primitiveness
Primitiveness means that you shouldn't make a class unnecessarily complicated. It is a principle that lets you control a large spectrum of behavior with just a few operations.

For any class, you should choose operations and attributes that are very basic.

To create more complex class states, you can simply change the values of the attributes you have chosen.

Or you can combine simple operations to create complex ones.

Let's say that in the Car class, you want to model the way gear changes work.

A vertical rectangle with three compartments depicts the Car class with the class name, Car, in the first compartment. The second compartment contains the attributes which are: engineSize, color, model, maxSpeed, currentSpeed. The third compartment contains the operations which are: accelerate(), brake(), turn(), getSpeed(), shiftUp(), shiftDown(), shiftUpTwoGears().

You will need to create a shiftUp operation, but you won't need a shiftUpTwoGears operation. You can simply use the more primitive shiftUp operation twice.

Primitiveness is very closely related to completeness.

To make a class complete, you need to account for all its behavior. But by defining this with primitive functions, you ensure that you don't have to list every possible permutation of a class's behavior.

The level of primitiveness you choose depends on how you want your program to perform.

If you need a program to run very fast, it may be better to use a complex function once rather than using a primitive one several times.

Summary

Classes should capture only one key abstraction. Classes that depend on other classes to function are strongly coupled. You should choose classes with weak coupling, because this makes programs easier to extend, maintain, and debug. Classes should also be functionally cohesive.

To be sufficient, a class must have enough attributes and operations to capture all the relevant states and behavior of its objects. You should make the set of these properties sufficiently complete to allow the purpose of the class to be easily understood, but you should also try to keep the class as simple as possible. You can do this by observing the principle of primitiveness.

Objects, classes, and UML

Classes in UML
A class is represented in UML by a rectangle with three compartments. These are
  • Class
  • attributes
  • operations()
Class
The first compartment in the UML diagram contains the class name, which UML recommends is centered and in bold. It also contains general properties like package owner or stereotype.
attributes
The second compartment contains the class's attributes. Attributes are features within a classifier that describe a range of values that instances of the classifier may hold.
operations()
The third compartment contains the class's operations. Operations are services that can be requested from an object that affect its behavior.
UML recommends that the names of both attributes and operations are left-justified and in plain type.
When representing a class, you can choose to show
  • attributes but not operations
  • operations but not attributes
  • attributes and operations
Or if you don't want to show any of a class's attributes or operations, you can simply represent it with a plain rectangle.
The names you choose for classes should come directly from the vocabulary of the problem domain. This helps you to understand what each class should do, and how it should relate to other classes.

If you have difficulty in naming a class, this can mean that it is poorly defined, or serves no useful purpose and should be discarded.
The following is a standard UML convention for naming classes.
Class names should
  • be singular nouns
  • start with an uppercase letter
Although not a UML recommendation, if you need to give a class a name that uses more than one word, you should
  • push the words together
  • capitalize the initial letter of each word
There are no strict UML conventions for naming attributes and operations. But it is common practice to push together words in any attribute or operation names that use more than one word.

The initial letter of the first word in such names is not usually capitalized, but the initial letter of each word after the first is.


Objects in UML
The UML notation for objects is very similar to the notation used for classes. Just like a class, an object is represented with a rectangle – with or without compartments.
But in contrast to a class name, an object name is always
  • underlined
  • in plain type
An object name can include the name of the class to which it belongs. In this case the class name follows the object name, and the two are separated by a colon.

For example, suppose the object BankTeller belongs to the Employee class. So its object name is bankTeller:Employee.
You can represent an object whose name or particular identity is irrelevant or not specified.
You do this by
  • underlining the class name
  • putting a colon before it
In a payroll system for a bank, it wouldn't be necessary to specify whether objects involved with the system were tellers or managers.

Such objects could be represented anonymously, with just their class name. So you would name each of them :Employee.


Class and object diagrams
Class diagrams are one of the most important types of diagram in UML.

This is because they provide a clear view of the relationship between a proposed system and its problem domain. Also, they can be used in conjunction with many development tools to generate skeleton code (the definition of the classes).
Class diagrams show only the static structure of systems.
They can be used to show
  • single classes
  • large groups of classes
  • relationships between classes
But they do not show the dynamic behavior of classes or the sequence of steps needed for a class to complete a use case.
Class diagrams can depict various levels of abstraction in a system.

They can give a high-level view by showing packages, which are groups of related classes handling specific parts of system functionality.
Or they can focus on the precise details of a single class's attributes and operations.
Class diagrams show a general overview of a system.
Object diagrams are very similar to class diagrams – but they provide a view of what a system looks like at a particular point in time, when classes are instantiated. They are useful for showing concrete examples of how a system functions.

Summary

Objects and classes are both represented in UML by a rectangle. A class has three compartments in the rectangle to show its name, attributes, and operations. An object has two compartments: one for the name and one for the attributes and their values.

Class diagrams show the static structure of classes, while object diagrams depict how classes look when instances are created. Class diagrams can show single classes, relationships between classes, and packages.

In UML, it is recommended, but not mandatory, that class names should be in bold, be without spaces, and have the initial letters of each word capitalized. Object names should be underlined and in plain type.

State, behavior, and identity

Objects
Much of the power and flexibility of modern software analysis and design derives from its use of objects.

The use of objects allows designers to create programs that are more easily maintained and extended. And it makes it easier to design very complex or large-scale programs.
The advantage of using objects in programming is that they allow us to model the real world more accurately.

Procedural programming represents the world as a series of processes acting on data.
However, object-oriented programming represents it the way we really see it – as a set of objects interacting with each other.
Use cases and problem statements allow you to define the problem domain you're analyzing.

After use cases and problem statements have been created, you need to model how the problem domain actually behaves.
By using objects, you can translate features of the problem domain into conceptual models that can be used to create software.

So creating objects is usually the next stage in a design project after use cases or problem statements have been drawn up.
An object is a distinct entity that represents something significant in the problem domain.

It has clear boundaries. And it has a well-defined behavior and a definite purpose.


Classes
Classes are the moulds or templates for the creation of objects. They set out rules for how the objects that they contain may behave.
Classes are part of the abstract structure of a program, and are created when the program is created.

But objects are created only when a program runs. In contrast to classes, they exist at a definite point in time.
Grouping objects into a class allows you to simplify the problem you're modeling.

Classes create an abstract representation of the world, letting you discard unnecessary details.
A class includes objects with similar
  • properties
  • behavior
  • relationships to other objects
The way you group objects into classes depends on the problem you're modeling.

Different problem domains have different requirements, even when they contain the same objects. You will need to create specific classes that suit the needs of each problem domain.
For example, the following are objects relevant to a bank application domain:
  • bank teller
  • manager
  • customer
  • ATM
  • Computer
  • mainframe
To model different aspects of the bank, you need to create classes that will capture the essential features of the problem domain.

So a class named ComputerSystem will capture the essential features of ATMs, computers, and the bank mainframe.
Alternatively, you could create separate classes for each of these objects if you wanted to analyze the problem domain in more detail.
Let's say you want to model the way users interact with the bank computer system.

You can create a class to model the behavior of bank tellers, customers, and managers. And all of these things will be instances of the ComputerSystemUser class.
You may need to rearrange the classes you've chosen if you want to model other aspects of the bank. For example, you might create a class named Employee to model the roles of staff in the bank.
But you would need to split this into an Employee class and a class named Manager if you also wanted to model the bank's chain of command.


State
All objects have three essential features:
  • state
  • behavior
  • identity
An object's state is defined by the attributes of the object and by the values these have.
An attribute is a feature of an object, which distinguishes it from other kinds of objects. For example, one of the attributes of any car object is that it is capable of movement – it has a speed.
An attribute is usually something static. This means that it cannot be removed or altered without changing the essential nature of the object to which it belongs.

For example, a car that could never be moved would not be a typical car.
Though attributes themselves are usually static, the value an attribute can have is usually dynamic.

So you can alter the state of an object by changing the value of its attributes.

Behavior and identity
You can model the states of a system by defining the states of the objects that represent it. But to capture the complexity of real world problems, you also need to consider how these objects behave.
Objects always exist in some kind of relation to other objects.

Sometimes they act on other objects, or even on themselves. And sometimes they are acted on by other objects.
The ways in which an object behaves are determined by its operations. So when an object needs to act on another object – to retrieve data, for instance – it uses an operation.
An operation occurs when a message is passed between two objects, allowing some function to be performed.
For structural and security reasons, some operations and attributes in a class or object are not visible to other classes.

This protects a program against malicious damage. And it prevents data being accidentally deleted or overwritten by other parts of the computer program.
The hidden parts of a class can be accessed only indirectly, through the visible parts of the class.

Together, the visible operations and attributes of a class make up its interface. This is the part of a class that is acted on by operations from other classes or system users.

The three most common types of operations that a class can have are
  • modifiers
  • selectors
  • iterators
modifiers
A modifier operation alters the state of an object.
selectors
A selector operation lets you access the state of an object without changing it.
iterators
An iterator operation allows all the parts of an object to be accessed in a definite order. Object-oriented programmers often create a separate class that is responsible for using the iterator function on an object.
Modifiers, selectors, and iterators are not part of any programming language, but are used to characterize the effects operations have.
Two types of operation are needed to create and destroy instances of a class. These are:
  • Constructor
  • Destructor
Constructor
The constructor operation creates an object and fixes its initial state.
Destructor
The destructor operation destroys an object.
Together, state and behavior define the roles that an object may play. And an object may play many roles during its lifetime.

For example, an object in the bank's Employee class could be involved with the payroll system, with the customer database, or with the command hierarchy.
The functions you require an object to perform are its responsibilities. And an object's responsibilities are fulfilled by its roles – by its state and behavior combined.

So you can capture all the meaningful functionality of an object by specifying its state and behavior.
Objects are characterized by a third feature in addition to state and behavior – identity.

Even objects with the same properties and behavior have their own individual identity. For instance, two blue station wagons that were built in the same year by the same manufacturer are still separate and unique cars.
The identity of an object is independent of its attributes or operations. So an object will retain its identity no matter what values its properties have.

You can, for example, respray your car or fit another engine, and it will still be the same car.

Summary

Objects are representations of abstract or real things. Their use in object-oriented programming is to allow designers to model the world accurately.

Objects represent particular instances of things, and classes represent types of object. Different classes are used for different problem domains.

States are the conditions in which objects exist. An object's state is defined by its attributes. An object's attributes are usually static, and the values of the attributes are usually dynamic.

The term "behavior" refers to how objects interact with each other, and it is defined by the operations an object can perform. There are five kinds of operations: modifiers, selectors, iterators, constructors, and destructors. No matter what its attributes and operations are, an object is always uniquely itself. It retains its identity regardless of changes to its state or behavior.

Thursday, February 16, 2012

Runtime and checked exceptions

Java packages contain Exception subclasses that describe exceptions that are specific to each package. Each subclass of Exception represents a particular exception type.
The RuntimeException class - a subclass of Exception - and its subclasses describe exception objects that are thrown automatically by the Java Virtual Machine (JVM) at runtime.

Runtime exceptions are generally caused by bugs in the source code. For instance, you need to ensure that a divisor is not equal to zero before dividing by it.
The RuntimeException subclass contains various subclasses. These include
  • ArithmeticException
  • IndexOutOfBoundsException
  • IllegalStateException
  • NegativeArraySizeException
ArithmeticException
When a program tries to do something that breaks the rules of arithmetic, an ArithmeticException is thrown. For example, if a method tries to divide an integer by zero, the method throws an instance of this class.
IndexOutOfBoundsException
When an index to a string or an array is out of range, an IndexOutOfBoundsException is thrown. For example, trying to access the twelfth element of a ten element array will throw this exception.
IllegalStateException
When a method has been invoked illegally an IllegalStateException is thrown. In other words it was not in the correct state to be called at the time.
NegativeArraySizeException
If an application tries to create an array that has negative size, a NegativeArraySizeException is thrown. An array must have zero or more elements.
You can catch RuntimeException types by using try-catch blocks.
Code that might trigger a runtime exception, but that does not contain try-catch blocks, will still compile. All exceptions deriving from RuntimeException are known as unchecked exceptions. This means that the compiler does not check whether they are handled or declared.
All the classes that inherit from Exception and are not RuntimeException subclasses are known as checked exception classes.
When checked exceptions might occur in a method, they must either be declared in the method declaration using a throws clause, or explicitly handled in the method body. Otherwise, the code will not compile.

An example of a situation that can cause a checked exception is when a method attempts to open a file that cannot be opened.

You use a try-catch statement to handle checked exceptions that are raised.
Checked exception classes include
  • ClassNotFoundException
  • InterruptedException
  • IllegalAccessException
  • FileNotFoundException
ClassNotFoundException
ClassNotFoundException is thrown when a program tries to load a class using its name, but the definition for the class with the specified name is not found. These exceptions occur when a program uses the forName method in the Class class or the findSystemClass or loadClass method of the ClassLoader class.
InterruptedException
When a thread has been inactive for a long time and another thread uses the interrupt method in the Thread class to interrupt it, an InterruptedException is thrown.
IllegalAccessException
When the currently executing method does not have access to the definition of a field the application is trying to get or set, an IllegalAccessException is thrown. This also applies if the method doesn't have access to the definition of a method the application is trying to invoke.
FileNotFoundException
FileNotFoundException, which belongs to the java.io package, is thrown when an application fails to open a file specified by a pathname. This can happen if the file is inaccessible, or if it doesn't exist.

The Throwable class

All Java exceptions and errors are subclasses of a class in the java.lang package called Throwable. Only an object of type Throwable can be thrown in code, including exceptions and system errors.
Methods of the Throwable class include
  • getMessage
  • toString
  • initCause
  • printStackTrace
getMessage
The getMessage method returns an appropriate String error message from a Throwable object.

The getMessage method returns null if the object was not created with an error message. You should provide descriptive messages for all exceptions handled in code.
toString
The toString method returns a description of an exception object, which includes the exception type.
initCause
The initCause method sets the cause of the exception, which is always another Throwable object. This method enables exception chaining.
printStackTrace
You use the printStackTrace method to find out which method has thrown a particular exception.
Suppose you want to print out the call stack at the point that e is caught.
public class TestExceptions2 {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ ) 
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    catch (ArrayIndexOutOfBoundsException e) {
      System.out.println ("Array bounds exceeded" ) ;
      MISSING CODE();
      wasError = true ;
    }
  }
}
To print out the call stack, you type e.printStackTrace.
The call stack records the series of method calls leading to the exception. When debugging, you can use printStackTrace to help determine where the exception originated in your code.
public class TestExceptions2 {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ ) 
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    catch (ArrayIndexOutOfBoundsException e) {
      System.out.println ("Array bounds exceeded" ) ;
      e.printStackTrace();
      wasError = true ;
    }
  }
}
The two immediate subclasses of Throwable are
  • Error
  • Exception
Error
Error and its subclasses are used for serious system or compile-time errors that cannot or should not be handled by an application. For instance these could include the following errors - ExceptionInInitializerError, StackOverflowError, and NoClassDefFoundError.
Exception
Exception and its subclasses are used for implementation-specific exceptions that an application might be expected to handle - for example, if a printer is switched off when the user attempts to print a document.

Exception is the superclass of all the exceptions you can handle in your code.

Wednesday, February 15, 2012

Using try, catch, and finally blocks

Java's exception-handling code is specified within a try...catch...finally block.


The try block encloses the code that might cause an exception to occur. The code in the try block is called protected code.
// basic try statement syntax
try {
  // protected code
}
catch (ExceptionType1 Identifier1) {
  // exception-handling code
}
catch (ExceptionType2 Identifier2) {
  // exception-handling code
}

finally {
  // 0 or 1 finally clause
}
You use zero or more catch blocks. When present, catch blocks specify exception handlers for the types of exceptions thrown. If no exception is thrown, then the code in the catch block doesn't run because it isn't needed.
The finally block is optional if a try block already has an associated catch block. If you have a finally block, the code it contains always executes, regardless of whether exceptions are thrown or not. The only exception to this rule is if a System.exit occurs, in which case the application terminates without executing a finally block.
If an exception occurs in a try block and is not caught in a catch block, a finally block will execute, provided it is present. The application then terminates.

If required, you must explicitly throw the exception up the call stack. If no method handles the exception, the program terminates when the exception object reaches the top of the call stack.
Consider the code in which the tryValues method contains appropriate exception-handling code.

The method takes two parameters – x and y. It declares and initializes an array and then prints the value of each array element divided by y, within a loop.
public class TestExceptions {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ )
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    //...
You enclose any lines of code that could cause exceptions in a try block.

For example, an exception could be caused if x is larger than the size of the array, or y is equal to zero.
public class TestExceptions {

  // Catch the exceptions within the method itself
  void tryValues (int x, int y) {

    boolean wasError = false ;
    int[] intArray = new int[5] ;
    for (int i=0; i < intArray.length - 1; i++ )
      { intArray[i] = i + 8 ; }
    try {
      for (int i = 0; i <= x - 1; i++) {
        System.out.println (intArray[i] / y) ;
      }
    }
    //...
If an exception occurs in a try block, execution is immediately directed to a series of catch blocks following the try block, which include the error-handling code.

Generally, you include a catch block for each type of exception that might be thrown in the try block, although you can write a single catch block for all exceptions if you like. You should aim to catch and handle specific exceptions, rather than general ones.
try {
  for (int i = 0; i <= x - 1; i++) {
    System.out.println (intArray[i] / y) ;
  }
}
catch (ArrayIndexOutOfBoundsException e) {
  System.out.println ("Array bounds exceeded" ) ;
  wasError = true ;
}
catch (ArithmeticException e) {
  System.out.println ("Attempt to divide by zero" ) ;
  wasError = true ;
}
catch (Exception e) {
  System.out.println ("Unknown exception occurred: " + e) ;
  wasError = true ;
}
finally {
  if (wasError)
    System.out.println ("Ending tryValues with error" ) ;
  else
    System.out.println ("Ending tryValues without an error" ) ;
}
If an exception occurs, the catch blocks are checked in order, from top to bottom.

If the exception is of the appropriate type for one of the catch blocks, the statements within the catch block are executed and no further catch blocks are checked. So the order in which you position catch blocks is important.

Introducing exception handling

In programming terms, an exception is an event that stops the normal execution of a program.

For example, a program might try to access an element outside the bounds of an array, or some file input and output operation might fail.

You need to ensure that your Java programs can deal with exceptions without simply crashing.
When an exception occurs at run time, the method in which it occurs creates an exception object. Execution is aborted unless the exception is handled somewhere along the call stack.

The method can throw the exception back to the calling method, which may be better able to handle it. The exception object includes information about the exception, such as the method in which it occurred and the cause of the exception.
The calling method can, in turn, throw the exception back to its caller. It's up to the developer to provide exception-handling code within the appropriate methods.
If no method handles the exception, the program terminates when the exception object reaches the top of the visible call stack - the main method.
When an exception occurs, you can prevent it being passed up the call stack – and potentially terminating the application – by providing handling code for the particular exception type, enabling the application to continue execution along a different path.

You can direct an application to use an exception handler in the method in which the exception occurs, or in one of that method's calling methods.
Once the exception is handled, the program continues to run, although this depends on the specific implementation.

Monday, February 13, 2012

New methods of the File class

In Java SE 6.0, the File class has been enhanced to include new methods that provide information about disk usage:

  • getTotalSpace()
  • getFreeSpace()
  • getUsableSpace()
getTotalSpace()
The getTotalSpace method provides information on a particular File's partition size in bytes.
getFreeSpace()
The getFreeSpace method provides information about the available space still unused in a particular File partition in bytes.
getUsableSpace()
Similar to getFreeSpace, the getUsableSpace method further checks for OS restrictions and available write permissions associated with the File.
This code is an example of how to use the getTotalSpace, getFreeSpace, and getUsableSpace methods.
import java.io.*;

public class SampleFileMethod {
  public static void main(String args[]) {

       if(args.length>0) {
    File f=new File(args[0]);
    System.out.println(" Total size of " + f + " is=:"+ f.getTotalSpace());
    System.out.println(" Available space of " + f + " is=:"+ f.getFreeSpace());
    System.out.println(" Usable space of " + f + " is=:"+ f.getUsableSpace());
      } else {
    System.out.println(" Enter a Filename");
    }
  }
}
Additional methods used to specify restrictions or permissions in reading, writing, or executing a File object are
  • setWritable()
  • setReadable()
  • setExecutable():
setWritable()
The setWritable method specifies the permissions on write operations to a File object. This method is overloaded as follows:
  setWritable(boolean writable)
  setWritable(boolean writable, boolean ownerOnly)

The first method is used to specify if the file is writable or not. The second method enhances the first by specifying if the write permission is applicable only to the owner.
setReadable()
The setReadable method specifies the permissions on read operations to a File object. This method is overloaded as follows:
  setReadable(boolean readable)
  setReadable(boolean readable, boolean ownerOnly)
The first method specifies if the file can be accessed or not. The second method enhances the first by specifying if the read permission is applicable only to the owner.

If the underlying file system cannot distinguish the owner's read permission from that of others, the permission will apply to everybody, regardless of this value.
setExecutable():
The setExecutable method specifies the execute permissions on a File object being executed. This method is overloaded as follows:
  setExecutable(boolean executable)
  setExecutable(boolean executable, boolean ownerOnly)


The method used to test if the file can be executed or not is the canExecute() method.
This code is an example of how setWritable() can be used.
import java.io.*;

public class SampleFileWrite {

  public static void main(String args[]) {
       if(args.length>0) {
    File f=new File(args[0]);
    f.setWritable(false);
    System.out.println("File writable? " + f.canWrite());
    //checks if the File is editable
    f.setWritable(true, true);
    System.out.println("File writable? " + f.canWrite());
    //checks if the File is editable
      } else {
    System.out.println(" Enter a Filename");
    }
  }
}
The canWrite method is used to check whether the file can be edited or is read only (where setWritable() is set to false).

Console Class

The Console class is a new feature of Java 6.0. It provides an alternative to the standard streams currently being used.



The Console is commonly used as a support in securing password entries. It contains methods that can suppress the characters being displayed on the user's screen, and remove them from memory when they are no longer needed.
To use the Console, you must retrieve the Console object using the System.console method. If the Console object is available, the Console object is returned. Otherwise, it returns null, and use of the Console is not permitted. This happens when the Console is not supported by the underlying OS or the application is launched in an environment that is not interactive.
Consider this sample code that uses the Console class to verify a password.

The first statement executed within the static main method attempts to retrieve an instance of the Console object using System.console().
import java.util.Arrays ;
import java.io.* ;
public class SampleConsole {
    
    public static void main (String args[]) throws IOException {

        Console c = System.console() ;
        if (c == null) {
            System.err.println("No console is available.") ;
            System.exit(1) ;
        }

        String login = c.readLine("Please enter your login information: ") ;
        char [] oldPassword = c.readPassword("Enter your old password: ") ;
      boolean test ;
        
            do {
                char [] newPassword1 =
                    c.readPassword("Input your new password: ") ;
                char [] newPassword2 =
                    c.readPassword("Input the new password again: ") ;
                test = ! Arrays.equals(newPassword1, newPassword2) ;
                if (test) {
                    c.format("Your passwords do not match. Try again.%n") ;
                } else {
                    c.format("The password for %s changed.%n", login) ;
                }
                
            } while (test) ;        
  }  
}
If the Console object is not available, the application is aborted using the System.exit(1) method.

If a Console object is available, the readLine method is invoked to prompt for and read the user's login name.
The readPassword method is invoked to prompt and read the password. Values typed are not echoed on screen. This provides a secure entry of values being typed for a password.
import java.util.Arrays ;
import java.io.* ;
public class SampleConsole {
    
    public static void main (String args[]) throws IOException {

        Console c = System.console() ;
        if (c == null) {
            System.err.println("No console is available.") ;
            System.exit(1) ;
        }

        String login = c.readLine("Please enter your login information: ") ;
        char [] oldPassword = c.readPassword("Enter your old password: ") ;
      boolean test ;
        
            do {
                char [] newPassword1 =
                    c.readPassword("Input your new password: ") ;
                char [] newPassword2 =
                    c.readPassword("Input the new password again: ") ;
                test = ! Arrays.equals(newPassword1, newPassword2) ;
                if (test) {
                    c.format("Your passwords do not match. Try again.%n") ;
                } else {
                    c.format("The password for %s changed.%n", login) ;
                }
                
            } while (test) ;        
  }  
}
The Arrays.equals method is used to test the two character arrays. If they contain the same values, the Arrays.equals method will return a boolean value.
The format method writes output to the console's outputstream using the specified format. It is similar to a printf method because it enables more control over how the output should display.
import java.util.Arrays ;
import java.io.* ;
public class SampleConsole {
    
    public static void main (String args[]) throws IOException {

        Console c = System.console() ;
        if (c == null) {
            System.err.println("No console is available.") ;
            System.exit(1) ;
        }

        String login = c.readLine("Please enter your login information: ") ;
        char [] oldPassword = c.readPassword("Enter your old password: ") ;
      boolean test ;
        
            do {
                char [] newPassword1 =
                    c.readPassword("Input your new password: ") ;
                char [] newPassword2 =
                    c.readPassword("Input the new password again: ") ;
                test = ! Arrays.equals(newPassword1, newPassword2) ;
                if (test) {
                    c.format("Your passwords do not match. Try again.%n") ;
                } else {
                    c.format("The password for %s changed.%n", login) ;
                }
                
            } while (test) ;        
  }  
}
The %s coversion type format is used to represent a value as a String. The login varaible represents the value that is being formatted by %s and the %n conversion type format is equivalent to a carriage return.

Reader and Writer classes

Reader classes are similar to input streams, and writer classes are similar to output streams. Reader classes descend from the abstract Reader class, whereas the Writer classes descend from the abstract Writer class.


Both readers and writers are divided into low-level and high-level classes. Low-level classes communicate with I/O devices, and high-level classes communicate with the low-level ones.
Readers and writers are designed specifically for Unicode characters. Low-level readers and writers deal with chars.
The java.io package provides the following low-level Reader classes:
  • FileReader
  • CharArrayReader
  • PipedReader
  • StringReader
FileReader
The FileReader class is used to read streams of characters from a file.

This class is useful to read text files.
CharArrayReader
The CharArrayReader class reads arrays of characters by implementing a character buffer. The character array acts like a character input stream.
PipedReader
The PipedReader class provides a piped character-input stream.

It should be used with a piped character-output stream so that data written to the PipedWriter will be available from this reader.
StringReader
The StringReader class uses strings as its source of a character stream. Individual characters can be marked and read from the string.
The high-level reader classes include
  • BufferedReader
  • FilterReader
  • InputStreamReader
BufferedReader
The BufferedReader class is used to read text from a character-input stream.

You can use the class to improve the efficiency of your code. Buffers enable you to write and read data in bulk. It is recommended to always use buffered I/O.
FilterReader
The FilterReader class is an abstract class that is used to filter character streams. By overriding the appropriate methods of FilterReader, a subclass can decide what gets read and how it is handled. For example, you can filter lines from a file, based on a regular expression.
InputStreamReader
The InputStreamReader is a class that is used to convert a byte stream into a set of characters, using a specified Charset. You can use InputStreamReader to accept input from System.In, up to a designated escape character or sequence.
Consider the code for the InnerActionListener class. The FileReader class, which the application uses to read data, has two constructors. One constructor takes a File object as a parameter.
class InnerActionListener
  implements ActionListener {
  public void actionPerformed (ActionEvent e) {
    String s ;
    long len ;
    contents.setText(null) ;
    File f = new File (tb.getText().trim()) ;
    if (f.exists() && f.isFile()
    && f.canRead()) {
      try {
        FileReader buff = new FileReader (f) ;
        BufferedReader theFile = 
        new BufferedReader(buff) ;
        while ((s = theFile.readLine()) != null) {
          contents.append (s + "\n") 
        }
        target.setText(tb.getText().trim()+"2") ;
        theFile.close() ;
FileReader(String pathname)
FileReader(File file)
The application first creates a File object. The File object allows you to interrogate the file system.
The file object is passed into the constructor for a FileReader called buff. The FileReader is a low-level object that allows you to read from a file.
BufferedReader - one of the high-level readers in the java.io package - has an internal buffer that enables data to be read in large blocks. This reduces I/O overhead.
class InnerActionListener
  implements ActionListener {
  public void actionPerformed (ActionEvent e) {
    String s ;
    long len ;
    contents.setText(null);
    File f = new File (tb.getText().trim()) ;
    if (f.exists() && f.isFile()
    && f.canRead()) {
      try {
        FileReader buff = new FileReader (f) ;
        BufferedReader theFile = 
        new BufferedReader(buff);
        while ((s = theFile.readLine()) != null) {
          contents.append (s + "\n") ;
        }
        target.setText(tb.getText().trim()+"2") ;
        theFile.close();
Its readLine method can read the next line of text sent to it by a low-level reader.
String readLine() throws IOException
It is a good idea to wrap buffered readers around unbuffered readers to make I/O more efficient.
A BufferedReader object can accept any type of low-level reader as an input source. For example, you can specify that the buff FileReader object is used as an input source by passing it into the constructor of the BufferedReader class as a parameter.
class InnerActionListener
  implements ActionListener {
  public void actionPerformed (ActionEvent e) {
    String s ;
    long len ;
    contents.setText(null);
    File f = new File (tb.getText().trim()) ;
    if (f.exists() && f.isFile()
    && f.canRead()) {
      try {
        FileReader buff = new FileReader (f) ;
        BufferedReader theFile = 
        new BufferedReader(buff) ;
        while ((s = theFile.readLine()) != null) {
          contents.append (s + "\n") ;
        }
        target.setText(tb.getText().trim()+"2") ;
        theFile.close() ;
You can use a while loop to read the next line from the specified file and display it in the application's contents area.
As with input and output streams, most reader classes have a corresponding writer class. In this example, an application uses a FileReader and a BufferedReader to read files. It uses a FileWriter and a BufferedWriter to write them.
class innerButtonListener extends MouseAdapter{
  public void mouseClicked(MouseEvent evt) {
  File f = new File(target.getText().trim());
    Button b2;
    if(f.exists()) {
      enter.setText("This file already exists");
      return;
    }
  try{
    FileWriter output = new FileWriter(f);
    BufferedWriter out =
    new BufferedWriter(output);
    String s = contents.getText();
    //write out contents of TextArea
    out.write(s,0,s.length());
    //send output from write to file
    out.flush();
    out.close();
    //close files
    output.close();
  }
The write method of BufferedWriter writes the data from the application's contents area.
But it does not write data to its destination if the amount of data is smaller than the BufferedWriter object's buffer. If that is the case, the object stores the data instead of writing it to the file. When the buffer's size limit is reached, the object writes the contents of the object's buffer to the file.
To prevent this data being lost when you close the file, you use the flush method to send all the remaining data from the BufferedWriter object's buffer to the FileWriter object.
Exception errors often occur when executing an application that uses the input and output classes. These can be thrown by the JVM. Some important errors include
  • FileNotFoundException
  • EOFException
  • InterruptedIOException
  • ObjectStreamException
FileNotFoundException
A FileNotFoundException occurs when an attempt to locate a file at a specified path is unsuccessful.
EOFException
An EOFException occurs when the end of a file is reached unexpectedly.
InterruptedIOException
An InterruptedIOException occurs when the input or output operation is interrupted unexpectedly.
ObjectStreamException
The ObjectStreamException class is the base class for errors thrown by the ObjectStream classes.

High-level streams

High-level input and output streams communicate with low-level streams rather than with I/O devices. You can use them for high-level input and output.

Most of Java's high-level input and output classes inherit attributes from the FilterInputStream and FilterOutputStream superclasses. In turn, these classes inherit from the abstract InputStream and OutputStream classes.
Suppose you are using a DataInputStream constructor for one of these classes. You need to pass an InputStream to the constructor as a parameter.
DataInputStream(InputStream objectName)
You can use any class that inherits from the InputStream class as an input source for a high-level stream. For example, you can use a FileInputStream object that you have already created, or use input from a socket or pipe.
When a high-level stream object, such as an instance of the DataInputStream class, receives byte input from a low-level stream, it processes the bytes and converts them into the appropriate datatype.

The DataInputStream class contains read methods that convert bytes into all the primitive datatypes, as well as into UTF strings. For example, the readInt method reads the next four bytes and converts them into an int. For the methods to work correctly, these four bytes must represent an int. You need to make sure that the data is read in the same order in which it is written to a stream.
To close a DataInputStream object, you use the class's close method.

If you need to close a chain of stream objects, you do so in reverse order so that the object that was created first is the last one to close.

This prevents you from closing an InputStream before you close the high-level stream that uses it as an input source.
For example, the code sample that reads sales data from a file uses the close method to close an instance of the DataInputStream object.
    for (int i = 0; i < descs.length; i ++) {
      myData = new SalesData (descs[i], amounts[i], values[i]);
      writeSalesData ( myData ) ;
    }
    out.close() ;


    // Prepare to read it in
    in = new DataInputStream(new FileInputStream(fruitfile)) ;

    for (int i=0; i<6; i++) {
      myData = readSalesData () ;
      System.out.println("You sold " +
      myData.desc + " at $" +
      myData.value + " each. Amount was " + myData.amount) ;
    }
    in.close() ;
  }

The subclasses of the FilterOutputStream class include
  • DataOutputStream
  • BufferedOutputStream
  • PrintStream
DataOutputStream
You use the DataOutputStream to write data to a stream by passing an OutputStream to a DataOutputStream object as a parameter when you create the object.
BufferedOutputStream
You use the BufferedOutputStream to write data to a buffer. This in turn writes it to the underlying stream.
PrintStream
A PrintStream allows other output streams to conveniently print data of various formats. This class never throws an IOException, unlike other output streams.
The methods of the DataOutputStream class process data, such as characters, integers, and UTF strings, convert it to bytes, and write it to the stream.
File f = new File (myFileName);
if(f.exists() && f.isFile() && f.canWrite()) {
  try {
    FileOutputStream fostream = 
    new FileOutputStream(f);
    DataOutputStream dostream =
    new DataOutputStream(fostream);
    
    dostream.writeUTF("Some UTF data");
    dostream.close();
    
    fostream.close();
  }
  catch (IOException e) {
  }
}

Supplement

Selecting the link title opens the resource in a new browser window.
View the DataInputStream and DataOutputStream methods.
Consider the code that creates a FileOutputStream object named fostream, and a DataOutputStream object named dostream.
File f = new File (myFileName);
if(f.exists() && f.isFile() && f.canWrite()) {
  try {
    FileOutputStream fostream = 
    new FileOutputStream(f);
    DataOutputStream dostream =
    new DataOutputStream(fostream);
    
    dostream.writeUTF("Some UTF data");
    dostream.close();
    
    fostream.close();
  }
  catch (IOException e) {
  }
}
In doing so, the code writes the DataOutputStream to the FileOutputStream. - fostream.
It passes a string as a parameter to the writeUTF method, which writes it to the output stream.
Finally, it closes the two streams in the correct order. This way, the one that was created last is closed first.
File f = new File (myFileName);
if(f.exists() && f.isFile() && f.canWrite()) {
  try {
    FileOutputStream fostream = 
    new FileOutputStream(f);
    DataOutputStream dostream =
    new DataOutputStream(fostream);
    
    dostream.writeUTF("Some UTF data");
    dostream.close();
    
    fostream.close();
  }
  catch (IOException e) {
  }
}

Low-level streams

When Java reads or writes data, it opens a data stream, reads or writes the information, and closes the stream.



Java uses the stream, reader, and writer classes for streamed data.
Stream classes deal with general data input and output, whereas the reader and writer classes deal specifically with Unicode and Unicode Transformation Format (UTF) string input and output.
Data received from or sent to general I/O devices consists of bytes. However, Java can support higher-level I/O by piecing together bytes to represent other types of data, such as integers, characters, or strings.
For example, a sequence of four bytes can make up an int.
Java uses a hierarchy of classes to deal with different types of data. The InputStream and OutputStream are abstract classes that use low-level I/O streams to read bytes from or send them to I/O devices such as files, network sockets, and pipes.
Low-level streams provide access directly to underlying bytes. High-level streams build upon low-level streams for additional capabilities.

The FilterInputStream and FilterOutputStream classes extend InputStream and OutputStream respectively. They use high-level filter streams to read or write data, such as strings and ints, from byte streams.
All low-level stream classes inherit from either the InputStream or OutputStream abstract classes.

Many stream input classes have a corresponding output class with similar methods. For example, the FileInputStream class, which is the input class for files, has a corresponding output class, FileOutputStream.
The low-level streams that are direct descendants of the InputStream or OutputStream include
  • ByteArrayInputStream and ByteArrayOutputStream
  • FileInputStream and FileOutputStream
  • ObjectInputStream and ObjectOutputStream
  • PipedInputStream and PipedOutputStream
  • SequenceInputStream
ByteArrayInputStream and ByteArrayOutputStream
The ByteArrayInputStream and ByteArrayOutputStream classes read and write arrays of bytes with buffering.
FileInputStream and FileOutputStream
The FileInputStream receives data from a file in byte form. The FileOutputStream outputs the data to a file.
ObjectInputStream and ObjectOutputStream
The ObjectInputStream deserializes primitive data and objects that have been previously serialized using an ObjectOutputStream object.
PipedInputStream and PipedOutputStream
The PipedInputStream and PipedOutputStream classes work with thread communication. They enable you to create and connect two sides of a stream.
SequenceInputStream
The SequenceInputStream class enables you to concatenate other input streams and to read from each of them, in turn.
The corresponding InputStream and OutputStream subclasses have complementary structures and functions, including
  • constructors
  • read and write methods
  • reading and writing arrays
constructors
The FileInputStream and FileOutputStream classes have similar constructors.

The syntax for the constructors is

FileInputStream(String pathname)
FileInputStream(File file)
FileOutputStream(String pathname)
FileOutputStream(File file)
read and write methods
The FileInputStream class uses the read method to read the next byte from a file. The FileOutputStream class uses the write method to write a byte to a file.

The syntax for these methods is

int read () throws IOException
void write (int <b>) throws IOException
reading and writing arrays
The FileInputStream and FileOutputStream classes have complementary read and write methods for reading and writing arrays of bytes.

The syntax for these methods is

int read(byte[] b)
int read(byte[] b, int off, int len)

void write(byte[] b)
void write(byte[] b, int off, int len)
Suppose you have code that reads information on sales from a file and prints it to the standard output. To do this, the code creates a DataInputStream. This class has an InputStream data member, which is inherited from FilterInputStream.
    for (int i = 0; i < descs.length; i ++) {
      myData = new SalesData (descs[i], amounts[i], values[i]) ;
      writeSalesData ( myData ) ;
    }
    out.close() ;


    // Prepare to read it in
    in = new DataInputStream(new FileInputStream(fruitfile)) ;

    for (int i=0; i<6; i++) {
      myData = readSalesData () ;
      System.out.println("You sold " +
      myData.desc + " at $" +
      myData.value + " each. Amount was " + myData.amount);
    }
    in.close() ;
  }
The DataInputStream object wraps a FileInputStream instance. This enables native datatypes to be read in a machine-independent fashion.
You can layer stream objects together to create streams capable of performing very specific I/O functions.

For example, you can extend FilteredInputStream to create custom filters that discriminately read formatted data. Your custom filter can, in turn, be chained to other streams.

The java.io.File class

Programs are required to write and read data to and from external sources - such as files, other programs, or network resources.

The Java Input/Output (I/O) package - java.io - enables Java programs to read and write data in various formats. Text, sound, graphics, and video files can be processed by appropriate classes from the java.io package.
The java.io package contains classes that enable you to access data both sequentially and at random.

Note

In sequential data access, data is read or written in sequence, from the first record to the last. Nonsequential or random - data access involves reading or writing data in a random order.
The Reader and Writer classes, and their various subclasses, are used for sequential data access. These classes input or output data sequentially as ordered streams of bytes.

The RandomAccessFile class and its subclasses are used to input or output data in a file. The bytes do not need to be in ordered sequences, as opposed to a stream.
The java.io.File class represents either a directory or a single file within the file system.It allows you to navigate, describe, and access those files or directories.
The Java security manager allows only specified operations to be performed within a given security context. Because most browsers don't allow any kind of file access, the File class and related I/O classes are usually used in applications instead of applets.
Creating a File object does not necessarily mean that you create a real file or directory. While representing the name of a file, the File object does not represent, or enable you to access, the data in the file.

Similarly, when a File object is deleted by the garbage collector, no physical files are deleted.
Creating a File object does not actually create a file on the local system. It merely encapsulates the specified string in one of a number of constructors:
  • File (File directoryObj, String fileName)
  • File (String pathName)
  • File (String pathName, String fileName)
File (File directoryObj, String fileName)
This constructor creates a new File instance using the pathname from an existing File instance and a string containing the filename.
File (String pathName)
This constructor creates a new File instance using the given pathname string.
File (String pathName, String fileName)
This constructor creates a new File instance using the given pathname string and filename string.
Consider the code that creates a simple file access application in Java. To access a file, you should first import the relevant Java classes – in this case File and RandomAccessFile, which are both part of the java.io package.
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class SampleWriteFile implements ActionListener {"
The java.awt , java.awt.event, and javax.swing packages are imported to be able to utilize GUI components.
Once you have imported the relevant java.io classes, you need to instantiate the GUI components to be able to input names. These names are written to a RandomAccessFile named sampletext.txt.
public class SampleWriteFile  implements ActionListener {

  private Frame f;
  private Button write, clear, exit;
  private TextField txt;
  public SampleWriteFile(String s) {
    f=new Frame(s);
    write=new Button("Write to file");  write.addActionListener(this);
    clear=new Button("Clear entries");  clear.addActionListener(this);
    exit=new Button("Exit");  exit.addActionListener(this);
    txt=new TextField(20);  
    Label l=new Label("Enter name");
    Panel p=new Panel();
    Panel p2=new Panel();
    p.add(l);p.add(txt);
    p2.add(write);p2.add(clear);p2.add(exit);
    f.add(p, "North");
    f.add(p2);
    f.pack();
    f.setResizable(false); f.setVisible(true);  
  } 
In the RandomAccessFile instantiation, the rw option is used to be able to read and write data from the file.
public void writeFile() {
       try {
  RandomAccessFile file=new RandomAccessFile("sampletext.txt", "rw");
  file.seek(file.length());
  file.writeBytes(txt.getText()+ "\n");
      }
      catch(IOException e) {
  JOptionPane.showMessageDialog(null,"Cannot write to File","Exception",JOptionPane.ERROR_MESSAGE);
}
The seek method is used to get to a specific location in the file. The value returned by the length method is passed to the seek method, so that we can get to the end of the specified file.
The writeBytes method is used to write data coming from the text field into the file named SampleText.txt.

Supplement

Selecting the link title opens the resource in a new browser window.
View the code for the SampleWriteFile application.
There are 12 methods you can use on a File object. These include
  • exists
  • getName
  • getAbsolutePath
  • isDirectory
  • isFile
  • canRead
exists
You use the exists method to confirm whether a specified file exists. The method returns a value of true if the file exists.
getName
You use the getName method to return the name of a file or directory - the last element of a full pathname.
getAbsolutePath
The getAbsolutePath method returns a String value, which is the full absolute path to a file or directory, whether the file was initially constructed using a relative pathname or not.
isDirectory
The isDirectory method returns a value of true if the string specified in the object's constructor represents a directory instead of a file.
isFile
The isFile method determines whether a File object represents a file or a directory. The method returns a boolean value of true if an abstract filename exists, and is a normal file - that is, the File object does not represent a directory. Otherwise it returns false. The MS Windows implementation of the isFile method has been reworked in JDK1.6. It is now always set to return false for devices such as CON, NUL, AUX, LPT, which makes it consistent with the UNIX implementation of isFile.
canRead
The canRead method determines whether data can be read from a file. It returns a boolean value of true only if the file exists and can be read by the application. Otherwise it returns false.
The remaining six methods are
  • getTotalspace
  • getFreespace
  • getUsablespace
  • setWritable
  • setExecutable
  • canExecute
getTotalspace
The getTotalspace method returns a long value representing the size in bytes of the partition named by the abstract path.
getFreespace
The getFreespace method returns a long value, which is the number of bytes available on the partition named by the abstract path. The value returned is a hint, and not a guarantee that all the bytes are usable. The number of unallocated bytes are most likely accurate after the call to gerFreespace. This method does not guarantee that write operations to the file system are successful.
getUsablespace
The getUsablespace method returns a long value, which is the number of bytes available on the partition named by the abstract path name.
setWritable
The setWritable method allows a particular file to be writeable and takes two parameters. The first parameter is a boolean value. If set to true, the file becomes writable, but if set to false, the file becomes read only. The second parameter is also a boolean value. If set to true, the write permission applies only to the owner or the creator of the file, but if set to false, the write permission applies to everybody.
setExecutable
The setExecutable method sets the execute permission of a file. It also takes two parameters. If the first parameter is set to true, the file is set to allow execute operations. If the second parameter is set to true, the execute permissions apply only to the owner or the creator of the file. If the second parameter is set to false, the execute permission is applied to everybody.
canExecute
The canExecute method returns a boolean value. If true is returned, the file can be executed.
Consider the code that uses the isDirectory method to determine whether an array element - a File instance - is a directory. If it is, the application searches it for a specified file.
class InnerActionListener implements
  ActionListener {
    public void actionPerformed (ActionEvent e) {
      ReportAction r = new ReportAction() ;                  
      r.setPriority(Math.min(r.getPriority() + 1,
            Thread.MAX_PRIORITY));
      r.start() ;
      
      File[] allDrives = File.listRoots();
      for (int i=1 ; i<allDrives.length-1; i++) {
              if (allDrives[i].isDirectory())
        answer.append (search(allDrives[i], "test.txt"));
      }
      r.interrupt() ;
    }
}
Suppose you want to create an instance of a File and use the most common methods associated with a File class. This application searches for files using a method that takes a File object and a search string as parameters. The File object represents the directory being searched.
String search (File f, String fileName) {
  String contents[] = f.list() ;
  int i = 0;
  String found = "Not found" ;

  for (i=0; i<contents.length; i++) {
    if (fileName.equals (contents[i]))
      return (new File(f, contents[i]).getAbsolutePath()) ;
  }
  i = 0 ;
  while (i < contents.length) & (found.equals ("Not found"))) {
    File child = new File (f, contents[i]) ;
    if (child.isDirectory())
      found = search (child, fileName);
    i++ ;
  }    
  return found ;
}
Java uses the File object's list method to return an array of Strings, listing the contents of a directory. If the File does not represent a directory, the list method returns a null value. This will cause the application to throw a NUllPointerException, which can be handled by using a try-catch block.
If one of the files in the directory matches the filename specified by the user, the method returns a string, representing the absolute path to the target file.

Painting Swing components

Painting GUI components

In Java, components are rendered on screen in a process known as painting. Although this is usually handled automatically, there are occasions when you need to trigger repainting, or modify the default painting for a component.



The core painting mechanism is based on the Abstract Windowing Toolkit (AWT). In newer versions of Java, Swing painting mechanisms are based on and extend the functionality of AWT mechanisms.
Components can be heavyweight or lightweight. A heavyweight component has its own native screen peer. For a lightweight component to exist there must be a heavyweight further up the containment hierarchy. So lightweight components are more efficient. Swing components tend to be lightweight.

Painting for lightweight and heavyweight components differs slightly.
Painting is triggered when a GUI element is launched or altered in any way. This can be caused by
  • a system event
  • an application event
a system event
System-triggered painting operations are caused when a system requests a component to be rendered onscreen for the first time, resized, or repaired.

If you open the Save As window in an application, then a system request is triggered.
an application event
Application-triggered painting events occur when the internal state of an application changes and requires components to be updated.

For example, if an item is selected, the application might send a request for the item to be highlighted.
Painting in AWT
When a paint request is triggered, AWT uses a callback mechanism to paint the lightweight and heavyweight components of a GUI.

You should place the code for rendering the component in the relevant overriding paint method of the java.awt.Component class. The method is invoked whenever a system or application request is triggered.
public void paint(Graphics g)
The paint method is not invoked directly. Instead, you call the repaint method to schedule a call to paint correctly.

The Graphics object parameter is pre-configured with the state required to draw and render a component. The Graphic parameter can be reconfigured to customize painting.

public void repaint()
public void repaint(long tm)
public void repaint(int x, int y, int width, int height)
public void repaint(long tm, int x, int y,
  int width, int height)

For complex components, you should specify the region to be rendered using the arguments of the repaint method. The updated region is referred to as the clip rectangle.

The whole component is repainted if no arguments are given.

public void repaint()
public void repaint(long tm)
public void repaint(int x, int y, int width, int height)
public void repaint(long tm, int x, int y,
  int width, int height)

You can use an overridden update method to handle application-triggered painting differently from system-triggered painting.

For application-triggered paint operations, AWT calls the update method. If the component doesn't override the update() method, the default update method paints heavyweight components by clearing the background and calling the paint method.

The process of updating specified areas of a component is known as incremental painting. Incremental painting is not supported by lightweight components.
Consider the code that is used to refresh the applet when a user types text. You first create the ke instance to represent the event of typing a key.

public void keyTyped(keyEvent ke) {
  msg += ke.getKeyChar();
  repaint();
}

You call the ke instance's getKeyChar method to determine the value of the typed key. You assign the value to a variable - msg in this case - using the += overloaded operator.

You then call the repaint method, which in turn calls the overridden paint method. The value of the key typed - msg - is passed to the paint method and the key character is painted onscreen.


Painting in Swing
Painting Swing components is based on the AWT callback method, so it supports the paint and repaint methods. Swing painting also extends the functionality of paint operations with a number of additional features.

The Swing API, RepaintManager, can be used to customize the painting of Swing components. In addition, Swing painting supports Swing structures, such as borders and double-buffering.

Double-buffering is supported by default. Removing double-buffering is not recommended.
Because lightweight components are contained within heavyweight components, the painting of lightweight components is triggered by the paint method of the heavyweight component.
When the paint method is called, it is translated to all lightweight components using the java.awt.Container class's paint method. This causes all defined areas to be repainted.
There are three customized callbacks for Swing components, which factor out a single paint method into three subparts. These are
  • paintComponent()
  • paintBorder()
  • paintChildren()
paintComponent()
You use the paintComponent method to call the UI delegate object's paint method. The paintComponent method passes a copy of the Graphics object to the UI delegate object's paint method. This protects the rest of the paint code from irrevocable changes.

You cannot call the paintComponent method if UI delegate is set to null.
paintBorder()
You use the paintBorder method to paint a component's border.
paintChildren()
You use the paintChildren method to paint a component's child components.
You need to bear several factors in mind when designing a Swing paint operation.

Firstly, application-triggered and system-triggered requests call the paint or repaint method of a Swing component, but never the update method. Also, the paint method is never called directly. You can trigger a future call to it by invoking the repaint method.

As with AWT components, it is good practice to define the clip rectangle using the arguments of the repaint method. Using the clip rectangle to narrow down the area to be painted makes the code more efficient.

You can customize the painting of Swing components using two properties, namely
  • opaque
  • optimizedDrawingEnabled
opaque
You use the opaque property to clear the background of a component and repaint everything contained within the paintComponent method. To do this, opaque must be set to true, which is the default. Setting opaque to true reduces the amount of screen clutter caused by repainting a component's elements.
optimizedDrawingEnabled
The optimizedDrawingEnabled property controls whether components can overlap. The default value of the optimizedDrawingEnabled property is true.
Setting either the opaque or the optimizedDrawingEnabled property to false is not advised unless absolutely necessary, as it causes a large amount of processing.

You use the paintComponent method for Swing component extensions that implement their own paint code. The scope of these component extensions need to be set in the paintComponent method.

The paintComponent method is structured in the same way as the paint method.

// Custom JPanel with overridden paintComponent method
class MyPanel extends JPanel {
 
public MyPanel(LayoutManager layout) {
  super (layout) ;
}
 
public void paintComponent(Graphics g) {
  // Start by clearing the current screen
  g.clearRect( getX(), getY(), getWidth(), getHeight()) ;
  g.setColor (Color.red) ;
  int[] x = {30, 127, 56, 355, 240, 315 } ;
  int[] y = {253, 15, 35, 347, 290, 265} ;
  //Draw complex shape on-screen and fill it
  g.drawPolygon (x, y, 5) ;
  g.fillPolygon (x, y, 5) ;
  }
}

Summary

Painting is the process of rendering components onscreen. Heavyweight components have matching native peers, whereas lightweight components rely on a heavyweight container to do their painting. Painting is triggered by requests. System-triggered requests occur when windows are launched, resized, or repaired. Application-triggered requests occur when the internal state of an application changes.

AWT uses a callback method to invoke an overridden version of the paint method of the java.awt.Component class. You define the area that requires painting, the clip rectangle, using the arguments of the repaint method. You can also override the update method for application-triggered paint operations.

Swing painting is based on AWT painting and then adds further functionality. Heavyweight containers translate the paint method to all the lightweight components contained within them. Swing painting has three customized callbacks and two properties that you use to customize the Swing painting operation.