Refresher: Java Core
Topics - Java Core Notes
At my core, I know that the American Dream is about the opportunity to work hard and make your future - Tammy Duckworth
Editor Note:
- The material below was cobbled together for personal notes use, from attributed sources, and endured some mild look/feel massage.
- Document Purpose: Conveniently scoped refresher on the listed Java material.
Sources:
- Pluralsight
- Photo source by Alexander Gilbertson on Unsplash
Input and Output with Streams and Files
Streams Overview
- Streams provide a common i/o model
- Abstracts details of underlying source or destination
- Streams are unidirectional, either read-to or write from
- Two categories of streams
- Byte streams - interact as binary data
- Text streams - interact as Unicode characters
- The interaction model is the same for both streams
Reading an Writing with Streams
- Base class for working with Binary data is InputStream
- working with Text data - Reader class
- Read individual byte - int read() method
# Reading one byte at a time
InputStream input =
int intVal;
while ((intVal = input.read()) >=0) { //indicates EOS with return val of -1
byte byteVal = (byte) intVal;
// do something
}
# Reading from a character stream/reading one character at a time
Reader reader =
int intVal;
while ((intVal = reader.read()) >=0) {
char charVal = (char) intVal;
// do something
}
# Reading array of bytes
InputStream input =
int length;
byte[] byteBuff = new byte[10];
while ((length = input.read(byteBuff)) >= 0) {
for(int i=0; i < length; i++) {
byte byteVal = byteBuff[i];
// do something
}
}
# Reading array of characters
Reader reader =
int length;
char[] charBuff = new char[10];
while ((length = input.read(byteBuff)) >= 0) {
for(int i=0; i < length; i++) {
byte byteVal = byteBuff[i];
// do something
}
}
# Writing bytes
OutputStream output =
byte byteVal = 100;
output.write(byteVal);
byte[] byteBuff = {0,63,127};
output.write(byteBuff);
# Writing Characters
Writer writer =
char charVal = 'a';
writer.write(charVal);
char[] charBuff = {'a','b','c'};
writer.write(charBuff);
String stringVal = "Hello World";
writer.write(stringVal);
Common Stream Classes
- Common Input/OutputStream derived classes
- InputStream
- ByteArrayInputStream
- PipedInputStream
- FileInputStream
- OutputStream
- ByteArrayOutputStream
- PipedOutputStream
- FileOutputStream
- Reader
- CharArrayReader
- StringReader
- PipedReader
- InputStreamReader - File Reader
- Writer
- CharArrayWriter
- StringWriter
- PipedWriter
- OutputStreamWriter - File Writer
Stream Errors and Cleanup
- Realities
- Error Handling
- Cleanup
- Streams are backed by physical storage
- Often exist outside Java runtime, may not reliably cleanup
- Streams implement Closable interface, method .close()
Reader reader;
try {
reader = //open reader
// do something
} catch (IOException e) {
// handle ex
} finally {
try {
if(reader != null)
reader.close();
} catch(IOException e2) {
// handle
}
}
- Provide support for try-with-resources, Closable interface implemented
- Streams implement Closable interface
- Try-with-resources
- Automates cleanup of 1 or more resources
- A resource is any type that implements AutoClosable
- Syntax similar to try, optionally includes catch block
Chaining Streams
- Streams are often chained together
- One stream instance leverages another
- Creates higher-level functionality
- Simplifies reusability
- Chain using constructor
- InputStreamReader leverages chaining
- Provides Reader behavior over InputStream
- Character behavior over binary
# Chaining Streams
void doChain(InputStream in) throws IOException {
int length;
char[] charBuff = new char[128];
try (InputStreamReader rdr = new InputStreamReader(in)) {
while((length = rdr.read(charBuff)) >= 0) {
//do stuff
}
} // Try-with-resources automatically closes InputStreamReader
// InputStreamReader then closes InputStream
}
- Can create your own high-level streams
- Most commonly chain similar streams
- Chain a reader over a reader, etc
- Pattern so popluar that classes are available to simplify customization
- FilterReader, FilterWriter, FilterInputStream, FilterOutputStream
- Abstract classes
- Methods call to contained stream methods
- Override only customized methods
File and Buffered Streams
- Often use streams for file-based i/o
- Class for each stream type in java.io package
- FileReader
- FileWriter
- FileInputStream
- FileOutputStream
- The java.io classes are now deprecated
- Still widely used
- Direct file access is inefficient
- Buffered streams can improve efficiency
- Buffers content in memory
- Performs reads/writes in large chunks
- Reduces underlying stream interaction
- Buffering available for all 4 stream types
- BufferedReader
- BufferedWriter
- BufferedInputStream
- BufferedOutputStream
- BufferedStreams handle line breaks (win/nix)
- BufferedWriter - newLine()
- BufferedReader - readLine()
# Buffered Streams
try (BufferedReader br =
new BufferedReader(new FileReader("file1.txt"))) {
int intVal;
while((intVal = br.read()) >= 0) {
char charVal = (char) intVal;
//stuff
}
}
# Writing with line breaks
void writeData(String[] data) throws IOException {
try (BufferedWriter bw =
new BufferedWriter(new FileWriter("data.txt"))) {
for(String d:data) {
bw.write(d);
bw.newLine();
}
}
}
Accessing Files with the java.nio.file package
- Want you want to use. File types have been deprecated, but reader/writers types are still around.
- Better exception handling, scalability, more file system support, simplify common tasks.
- Path
- Used to locate a file system item
- Can be a file or a directory
- Paths (type)
- Static Path factory methods
- Translate from common representation of file (string based file path, URI)
// Paths example
Path p1 = Paths.get("\\documents\\data\\foo.txt");
Path p2 = Paths.get("documents", "data", "foo.txt");
- Files (type)
- Static methods for interacting with files
- Create, copy, delete, etc.
- Open file streams
- newBufferedReader
- newBufferedWriter
- newInputStream
- newOutputStream
- Read/write file contents
- readAllLines
- write
- Different than java.io in that we create a files type and then layer a reader over it.
// Example, reading lines with BufferedReader
void readData() throws IOException {
try (BufferedReader br =
Files.newBufferedReader(Paths.get("data.txt"))) {
String inValue;
while((inValue = br.readLine()) != null {
System.out.println(inValue);
}
}
}
// Example of a convenience method, readAllLines
void readThemAll() throws IOException {
List<String> lines =
Files.readAllLines(Paths.get("data.txt"));
for(String line:lines)
System.out.println(line);
}
}
- Be careful to have enough memory for large file
Using Default File System and zip file system
- Has a default file system
- Specialized file systems are supported
- Ex: zip file system
- Path instances are tied to a file system
- Paths class works only for default
- File System Type
- Represents an individual file system
- Factory for Path instances
- FileSystems
- Static FileSystem factory methods
- Open or create a file system
- newFileSystem
- File systems identified by URIs
- Specifics vary greatly
- Zip file system uses “jar.file” scheme
- jar:file:/data/bar.zip
- File systems support custom properties
- Different for each file system type
- Examples - Whether to create if doesnt exist, String encoding
String Formatting and Regular Expressions
More Powerful Solutions to Create String Representations
- Need more powerful string creation
- Concatenating strings is often not enough
- Very focused on creation details
- Numeric conversions awkward
- StringBuilder has same issues
- StringJoiner
- Simplifies joining a sequence of values
- String formatting
- Use format specifiers to closely control string formating
- Can specify desired appearance without dealing with creation details
Joining Sequences of Values with StringJoiner
- Construct the StringJoiner
- Specify string to separate values
- Optionally specify start/end strings
- Add values
- Retrieve the resulting string
// StringJoiner ex
StringJointer sj = new StringJoiner(", ")
sj.add("alpha").add("theta").add("gamma");
String theResult = sj.toString();
// More involved separator
StringJoiner sj = new StringJoiner("], [", "[", "]");
sj.add("alpha").add("theta").add("gamma");
String theResult = sj.toString();
// [alpha], [theta], [gamma]
StringJoiner Edge Case Handling
- toString when only one value added
- When constructed with separator only - returns added value
- When constructed with start/end strings - returns added value within start/end
- toString when no values added
- When constructed with separator only - returns empty string
- When constructed with start/end strings - returns string with start/end only
- You can customize empty case handling
- Only considered empty string if add is never called
Constructing String with Format Specifiers
- Focus is on describing the desired result
- Not concerned with the how
- Can control many aspects of appearance
- Positioning
- Decimal places
- Representation
- Some methods supporting format specifiers
- String.format
- System.out.printf
- Formatter.format
# Concatenation vs Formatting
String s1 = String.format("The words are %d, %d, and %d", foo, bar, baz);
val=3.666665
String s2 = String.format("blah, blah %1f", val);
// blah, blah 3.7
- All format specifiers start with a %, and then have a conversion, ie “d”
- You can specify precision, ie %.1f
- You can specify width, space-padded, right-justified by default