Please send questions to
st10@humboldt.edu .
CIS 480 - Advanced Java - Random Notes, 4-9-01
--------
finally note:
* as someone noted last time, if a try-catch block includes
a "finally" clause, then the statements of that clause are
ALWAYS performed;
* this was indeed the desire in example FileCopy.java,
where no matter WHAT happened, exceptions-wise, we
wanted the open files streams closed;
* from Barnes, "Object Oriented Programming with Java - An
Introduction", Prentice-Hall, 2000, p. 326, there is a nice
example that lets you "test" if you "get" how the finally
clause works:
public void method()
{
try
{
// T2 might throw an exception
statement_T1;
statement_T2;
statement_T3;
}
catch (Exception e)
{
Statement_C;
}
finally
{
Statement_F;
}
}
* now, consider: no exception is thrown. What statements
are done?
T1, T2, T3, F
* T2 throws an exception; What statements are done?
T1, T2, C, F
* so, "finally" is lovely for "housekeeping" tasks that MUST
be done, no matter what.
* closing of files is one such classic housekeeping
task!
* but, CARE must be taken to write them so that they
"work" properly in all cases, whether exceptions have
occurred or not.
* thus the care, in FileCopy.java, to check
if the File objects are non-null before
attempting to close them!
* one more very-cool nuance: finally is NOT the same as simply
writing statements *after* the try-catch block. Why?
* [Barnes, p. 326] "...[the finally clause's] statements
are executed even if one of the other clauses causes the
method to terminate --- via a return statement or a propagated
exception." (!!)
* "In the case of a return statement, the return is
delayed until the finally clause has completed."
END of finally note
--------------------
--------------------
File I/O continued
* (see Parrot.java for simple example of terminal input...such input
also appears in FileCopy.java;)
* last time: we got to discuss FileCopy.java a tiny bit, but not the
examples TryWriteToFile.java, TryReadFromFile.java
* A few more file I/O nuances before proceeding to JDBC:
* A complication in Java (that we haven't mentioned yet) is
that Java represents characters using something called Unicode ---
the Unicode character set.
* not ASCII! (or EBCDIC), two other prominent character
sets;
* So, for some of Java's i/o methods provided in the package
java.io:
* some input methods will automatically convert data from
however the host operating system stores it into Unicode for
use within Java,
* some output methods will automatically convert Unicode data
into the format expected by the host operating system;
* (and some Stream classes do NO such conversion, as in
byte-oriented I/O where you do not want that)
* in GENERAL:
* binary files are processed by subclasses of
InputStream
OutputStream
* no Unicode conversion is done --- [Barnes, p. 339]
"capture the external representation of data"
* text files are processed by subclasses of
Reader
Writer
* Unicode conversion IS done --- [Barnes, p. 339]
"designed to convert a wide range of character-based
data to and from the internally-used Unicode
representation"
* hopefully, you can see why instances of stream classes
FileInputStream and FileOutputStream thus made more sense
in FileCopy.java than reader/writer subclasses would have;
hmmm... what next?
basic File I/O steps:
* create your File object
File myFile;
myFile = new File(myFileName); // myFileName is a String
* NOT the only File constructor, remember; another takes
two arguments, a directory File instance and a file File
instance (as seen in FileCopy.java), etc.
* (you can check on a File's status in many wonderful and interesting
ways, only a few of which were demo'd in Delete.java and FileCopy.java;
see the Java API, package java.io, package File)
* (you can also make directories, make a file readable,
etc.)
* it looks accepted to throw an IOException if an input
file name is not suitable --- makes sense, that info is
thus unsuitable input...!
* now, you need to pick which of those lovely 40 or so stream
classes in java.io you wish to use to perform input or output;
* For reading and writing of text files, FileReader and FileWriter
are convenient and often used (although they are often also
wrapped in BufferedReader instances and BufferedWriter instances,
as we shall see).
* note that there are constructors that create FileReader
and FileWriter instances both using a file's String name
as a parameter, and using a File instance as a parameter;
* one of FileWriter's constructors also lets you have
TWO arguments, a filename given as a String and a boolean;
if the boolean is set to true, then the file is opened
for APPENDING (its current contents are not nuked, as
they are otherwise).
* (yes, otherwise the creation of a FileWriter stream
for a file DOES cause the current contents to be destroyed;)
* you can use BufferedReader and BufferedWriter classes (subclasses
of Reader, Writer) if you want to read or write a whole LINE at
a time;
* and, when you do, you get to deal with that line as
a String, if you'd like, instead of as an array of
characters --- convenient!
* however, perhaps oddly, (Barnes, p. 347) "the buffered
classes simply act as wrappers around the objects they were
constructed with, which they retain as an attribute" ---
* so, one creates a BufferedReader or BufferedWriter
instance by constructing from an existing Reader
Writer object (or appropriate subclass of Reader or
Writer).
* you never even use the name of the Reader or Writer
used for this purpose --- closing the BufferedReader or
BufferedWriter closes both --- so it is very common for
it to be declared anonymously.
* and so, in FileCopy.java, you have:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
* ...around an instance of InputStreamReader, which
is fine because InputStreamReader is a Reader subclass;
* (InputStreamReader is fine here for reading in from
System.in; putting the BufferedReader around it then lets
you read from System input one line at a time...)
* and in new-revised-and-improved TryReadFromFile.java,
you have:
BufferedReader fromStream;
...
fromStream = new BufferedReader(new FileReader(fileToRead));
* ...around an instance of FileReader, which
is the appropriate class for reading text from
a File instance;
* but, by wrapping the FileReader instance in
a BufferedReader instance, you can read the file
in one whole line at a time;
* now, this is not done in any of the examples, but you
can also have something like:
BufferedWriter myBWriter;
...
myBWriter = new BufferedWriter(new FileWriter(outputFilename));
* ...and now you can write a String line
at a time into the file.
* notice, then --- instantiating the stream here
IS opening the file;
* and, it CAN throw a FileNotFoundException
(if the file cannot be found OR if it cannot be
opened for ANY reason...)
* I've added a catch block checking for
this to revised/improved versions of
TryWriteToFile2, TryReadFromFile
* when done, it is ESPECIALLY important to close the
stream ---
in.close() (in FileCopy.java)
fromStream.close() (in TryReadFromFile.java)
myBWriter.close() (somewhere after the above ex.)
* do NOT assume the end of a program automatically
closes files and flushes buffers --- NOT SO!
* a close() CAN throw an IOException?! (although it
is unlikely) --- but you might want a catch to
deal with it, thus;
* fortunately, since problems with reads and writes
can also throw this exception, your try-catch block
for this CAN catch it for the close(), too;
* that is what is done in TryReadFromFile,
TryWriteToFile2.
* you CAN make a specific request to force out stuff in the
internal buffer being held until there's "enough" with
method flush()
* typical ways of reading/writing using these streams,
once they are set up:
* BufferedReader:
* readLine() examples:
// from FileCopy
String response = in.readLine();
// latestLine is a String variable; from TryReadFromFile
latestLine = fromStream.readLine();
* skip(long n)
...can be used to skip a certain number of characters
* read() // with NO arguments
...can be used to read a single character, but it returns
the character read as an INTEGER, int!
(the complementary write() assumes that its int
argument is a Unicode character cast as an int,
and casts it back to a char before writing it
out...!)
* read(char[] cbuf, int offset, int length)
...can be used to read up to length characters
into cbuf, starting a cbuf[offset].
* returns the int number of characters read, unless
the first attempt to read fails --- in that case,
it returns -1
* BufferedWriter:
* write(String line)
// if myLine is a String variable...
// interesting, the method is NOT writeLine!
myBWriter.write(myLine);
* write(int c)
...writes an integer that it assumes is
a Unicode character cast as an int (as mentioned
above)
* write(String s, int offset, int length)
* write(char[] buf, int offset, int length)
...write a portion of a String or an array of
characters
* newLine()
...writes a line terminator;
* now that we have talked about the BufferedReader and BufferedWriter,
it isn't that hard to talk about a few of the i/o methods for some
of the other kinds of streams;
* for FileReader: (when you haven't wrapped a BufferedReader
around it...)
* it inherits two read()'s from InputStreamReader
that are very similar to two of BufferedReader's read()'s:
* read()
...can be used to read a single character, but it returns
the character read as an INTEGER, int!
(the complementary write() assumes that its int
argument is a Unicode character cast as an int,
and casts it back to a char before writing it
out...!)
* read(char[] cbuf, int offset, int length)
...can be used to read up to length characters
into cbuf, starting a cbuf[offset].
* returns the int number of characters read, unless
the first attempt to read fails --- in that case,
it returns -1
* for FileWriter:
* it inherits three write()'s from OutputStreamWriter,
which, again, are similar to three of BufferedWriter's
write()'s; it also inherits two from Writer superclass.
* from OutputStreamWriter, similar to those in
BufferedWriter:
* write(int c)
...writes an integer that it assumes is
a Unicode character cast as an int (as mentioned
above)
* write(String s, int offset, int length)
* write(char[] buf, int offset, int length)
...write a portion of a String or an array of
characters
* from Writer:
* write(char[] buf)
...writes an array of characters; behaves, in effect,
like a call write(buf, 0, buf.length)
* write(String str)
...ah! will simply write a String;
* and that's what we are doing in
TryWriteToFile2.java, putting the String
of text from the textarea...
* now, for the byte-oriented subclasses of InputStream and OutputStream:
* read and write bytes, as we've already mentioned;
* (so, they'll work for binary format files, note;)
* and please note: we're focusing on file-oriented material
in here today. Many of those 40 stream classes we mentioned
are not file-oriented at all --- they may [Barnes, p. 351]
"exist to deliver a stream of bytes to and from other sources
and sinks, such as an internal array of bytes".
* but, FileInputStream and FileOutputStream are indeed
oriented to deliver such bytes from and to files;
* FileInputStream: includes three read()'s
* read():
[Java 2 API] "reads a byte of data from this input
stream" expressed as an int
* read(byte[] b)
[Java 2 API] "Reads up to b.length bytes of data
from this input stream into an array of bytes."
"Returns: the total number of bytes read into the
buffer, or -1 if if there is no more data because
the end of the file has been reached."
* read(byte[] b, int offset, int length)
[Java 2 API] "Reads up to length bytes of data from this
input stream into an array of bytes" (starting at
offset)? Also returns the total number of bytes
read or -1 if there is no more data because the
end of the file has been reached.
* note that read(byte[] b) is the version used in
FileCopy.java;
* FileOutputStream: includes three write()'s
* write(int b)
[Java 2 API] "Writes the specified byte to this file
output stream" (presumably a byte in the form of an
int...)
* write(byte[] b)
[Java 2 API] "Writes b.length bytes from the specified
byte array to this file output stream."
* write(byte[] b, int offset, int length)
[Java 2 API] "Writes length bytes from the specified
byte array starting at offset offset to this file output
stream."
* note that write(byte[] b, int offset, int length)
is the version used in FileCopy.java; HAS to be,
because cannot be sure actually READ buffer.length
bytes in the read() preceding this write();
* now, as you have probably guessed, there is a LOT more about file
input and output, and even more about input and output streams in
general, in java.io.
* as a small for-example: filter streams, streams to and
from pipes, stream tokenizers, etc.!
* But, this mostly-file-oriented start can take you far,
and help you be able to investigate more on your own from here...