FilterInputStream, FilterOutputStream – абстрактные классы для создания собственных потоков или добавления функционала к существующим.
В примере ниже создается класс расширяющий FilterInputStream, в котором мы реализуем свой поток байтового ввода. Этот созданный нами поток изменяет консольный байтовый ввод System.in так, чтобы он возвращал номер в таблице ASCII введенного пользователем в консоль символа, но сдвинутого на 13 символов в этой таблице.
Пример программы:
import java.io.*;
import java.util.*;
class Rot13InputStream extends FilterInputStream {
// Переданный в конструктор поток i
public Rot13InputStream(InputStream i) {
// Передаем в конструктор класса родителя через super.
super(i);
// Там этот поток сохраняется.
// И будет доступен здесь в наследнике через in,
// которым мы пользуемся ниже.
}
// Переопределим read, чтобы он возвращал сдвинутое значение
public int read() throws IOException {
// С помощью read считываем символ потоком in,
// переданный сюда через конструктор,
// и передаем его в созданный нами метод rot13.
return rot13(in.read());
}
// Сдвиг на 13 символов
public int rot13(int c) {
// Сильно не разбирайтесь в коде ниже. Просто знайте,
// что он сдвигает номер символа в ASCII на 13 символов.
if ((c >= ‘A’) && (c <= 'Z'))
c = (((c - 'A') + 13) % 26) + 'A';
if ((c >= ‘a’) && (c <= 'z'))
c = (((c - 'a') + 13) % 26) + 'a';
return c; // Этот сдвинутый номер возвращается
}
}
class FilterStreamLesson {
public static void main(String[] args) {
try {
Rot13InputStream b13is = new Rot13InputStream(System.in);
// Воспользуемся переопределенным нами read()
// в Rot13InputStream, который будет сдвигать номер
// введенного в поток символа на 13 значений
// в таблице ASCII и возвращать сдвинутый номер.
System.out.println(b13is.read());
} catch (Exception e) {
// Обработаем возможную ошибку
e.printStackTrace();
}
}
}
Вывод:
Номер символа а в таблице ASCII – 97, но поскольку мы сдвинули его на 13 символов выводиться 110.
ObjectInputStream, ObjectOutputStream – для сериалиации/десериализации. Для сохранения копии объекта в поток (в файл, например) для восстановления его потом (десереализации).
Пример программы:
import java.io.*;
import java.util.*;
class ObjectStreamLesson{
public static void main(String[] args) {
// сериализация
try(ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream(“person.dat”)))
{
Person p = new Person(“Mike”, 25, 178, false);
// сериализуем объект p в файл person.dat
oos.writeObject(p);
}
catch(Exception ex){
System.out.println(ex.getMessage());
}
// десериализация
try(ObjectInputStream ois =
new ObjectInputStream(new FileInputStream(“person.dat”)))
{
// десериализуем объект p из файла person.dat
Person p = (Person) ois.readObject();
// выведем его данные на консоль с помощью printf
System.out.printf(“Name: %s \t Age: %d \n”,
p.getName(), p.getAge());
}
catch(Exception ex){
System.out.println(ex.getMessage());
}
}
}
// чтобы сериализовать, класс должен быть Serializable
class Person implements Serializable{
private String name;
private int age;
private double height;
private boolean married;
Person(String n, int a, double h, boolean m){
name = n; age = a; height = h; married = m;
}
String getName() { return name; }
int getAge() { return age; }
double getHeight() { return height; }
boolean getMarried() { return married; }
}
Вывод:
То есть мы сохраняем в файл копию какого-либо объекта и когда она нам будет нужна в нашей программе достаем ее.
OutputStreamWriter – мост между символьными и байтовыми потоками.
Пример программы:
import java.io.*;
import java.util.*;
class OutputStreamWriterLesson {
public static void main(String[] args) {
try {
FileOutputStream outputStream =
new FileOutputStream(“person.txt”);
OutputStreamWriter outputStreamWriter =
new OutputStreamWriter(outputStream);
// OutputStreamWriter конвертирует записываемые в него
// символы в байты и эти байты записываются в файл байтовым
// потоком, который был обернут в OutputStreamWriter.
// В нашем случае FileOutputStream
String str = “My Some Text!”;
// Как видим записываем мы символы. То есть String,
// OutputStreamWriter конвертирует String в байты
// и передает байтовому потоку FileOutputStream
// для вывода в файл.
outputStreamWriter.write(str);
// Также с помощью flush достаем FileOutputStream
// из обертки OutputStreamWriter чтобы записать в него байты.
outputStreamWriter.flush();
} catch (Exception e) {}
}
}
PushbackInputStream – прочитав несколько байтов входного потока с помощью inpsrream.read()бывает необходимо вернуться и прочитать эти же уже прочитанные ранее байты еще раз.
Делается с помощью inpsrteam.unread().
Пример программы:
import java.io.*;
import java.util.*;
class PushbackStream {
public static void main(String[] args) {
try {
String str = “Hello world!!!”;
byte b[] = str.getBytes();
ByteArrayInputStream bin = new ByteArrayInputStream(b);
PushbackInputStream push = new PushbackInputStream(bin);
// читаем первый символ
System.out.print((char)push.read());
// Возвращаем его обратно.
// То есть сейчас мы извлекли первый элемент ‘H’ из str
// и если мы вызовем System.out.print((char)push.read());
// еще раз, то извлечется уже второй ‘e’. А мы хотим
// опять прочитать первый. Для этого мы можем
// вставить его перед ‘e’ и прочитать его еще
// раз вместо ‘e’. На самом деле нам не обязательно
// вставлять именно ‘H’ можно вставить любой символ.
// Важная ремарка, что сама строка str
// при всех этих манипуляциях не изменяется.
push.unread(‘H’);
// читаем снова первый возвращенный символ
System.out.print((char)push.read());
// дальше будет читать со второго символа
System.out.print((char)push.read());
System.out.print((char)push.read());
System.out.print((char)push.read());
} catch (Exception e) {}
}
}
Вывод:
Как видим, мы прочитали первый символ два раза. То есть мы прочитали его, потом вернули и прочитали его еще раз.
PipedInputStream, PipedOutputStream – применяется в многопоточной среде. Потоки в слове многопоточной, это не те потоки, которые мы сейчас изучаем (сейчас мы изучаем потоки ввода вывода). Эти же потоки понимайте как куски кода, которые выполняются параллельно. То есть в одном потоке один кусок кода, в другом другой и т.д, но главное все они выполняются параллельно друг другу.
PipedInputStream, PipedOutputStream связывают два потока I/O для для того чтобы они могли обмениваться информацией друг с другом.
Для передачи каких-либо данных между разными потоками лучше всего использовать этот класс.
Хотя потоки мы пока не разбирали, всё равно приведем пример.
Пример программы:
import java.io.*;
import java.util.*;
class PipedStreamLesson {
// Например, есть 2 независимых потока: ThreadA и ThreadB.
// Их классы создаются снизу после класса PipedStreamLesson.
// Это не те потоки, которые мы сейчас изучаем
// (сейчас мы изучаем потоки ввода-вывода).
// Эти же потоки понимайте как два куска кода,
// которые выполняются параллельно.
// То есть содержимое метода run() в ThreadA выполняется
// параллельно содержимому run() в ThreadB.
public static void main(String[] args) {
// Создаем концы канала: выходной и входной.
PipedInputStream pis;
PipedOutputStream pos;
try {
// Связываем в единый канал
// для передачи данных между ними.
pis = new PipedInputStream();
pos = new PipedOutputStream(pis);
// Запускаем потоки.
// После этого содержимое методов run
// в этих классах начнет выполняться параллельно.
new ThreadB(pis).start();
new ThreadA(pos).start();
// Также в эти потоки передаем связанные
// каналы pis и pos, которые будут
// использоваться для пересылки и считывания данных.
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
}
class ThreadA extends Thread {
PipedOutputStream pos;
ThreadA(PipedOutputStream pos) {
this.pos = pos;
}
@Override
public void run() {
try {
byte[] bytes = new byte[] {‘a’, ‘g’, ‘b’, ‘c’, ‘6’};
for (byte b : bytes) {
// Используем переданный в конструктор pos
// для передачи массива bytes в ThreadB.
pos.write(b);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread {
PipedInputStream pis;
ThreadB(PipedInputStream pis) {
this.pis = pis;
}
@Override
public void run() {
try {
int b = 0;
// pis считывает данные, передаваемые pos
// из ThreadA.
while ((b = pis.read()) != -1) {
System.out.println((char) b);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Вывод:
Как видим данные успешно пересланы в другой поток и выведены им на консоль.
SequenceInputStream – сливает потоки в один поток.
Он считывает от первого byte до последнего byte первого InputStream, затем делает то же самое со следующим InputStream и т.д. до последнего InputStream и объединяет их всех в один InputStream.
Допустим, есть два файла person1 и person2 по пути C:/HTML/somedata/:
Теперь давайте объединим два файловых потока, которые читают из этих файлов в один, и, таким образом, сможем читать данные из обоих файлов через один SequenceInputStream.
import java.io.*;
import java.util.*;
class SequenceStream {
public static void main(String[] args) {
try {
FileInputStream is1 =
new FileInputStream(“C:/HTML/somedata/person1.txt”);
FileInputStream is2 =
new FileInputStream(“C:/HTML/somedata/person2.txt”);
// объединяем
SequenceInputStream is = new SequenceInputStream(is2, is1);
// выведем содержимое обоих файлов через буферизованные потоки
BufferedInputStream in = new BufferedInputStream(is);
BufferedOutputStream out = new BufferedOutputStream(System.out);
int ch;
while ((ch = in.read()) != -1) {
out.write((char) ch);
out.flush();
}
} catch (Exception e) {}
}
}
StringReader, StringWriter – потоки, которые хранят строку в себес которой мы работаем через эти потоки как с любыми другими источниками дынных (фалами например).
Как раньше например мы записывали или выводили из консоли теперь будем записывать в строку или выводить из строки.
ByteArrayInputStream, ByteArrayOutputStream – аналог только работаем с массивом байтов вместо строки.
Пример программы:
import java.io.*;
import java.util.*;
class StringWRLesson {
public static void main(String[] args) {
try {
String s = “data”;
// Строчка ниже – ключевая: мы «превратили» строку в Reader.
// И теперь можем работать со строкой как с объектом Reader.
Reader reader = new StringReader(s);
// можем сделать строку (поток) буферезированной.
BufferedReader br = new BufferedReader(reader);
String line = br.readLine(); // считать строку
System.out.println(line);
Writer writer = new StringWriter();
// Пишем какую-то строку в Writer
// для дальнейшей записи ее в String переменную.
writer.write(line + “some text”);
// получаем текст, который был записан во Writer
String result = writer.toString();
System.out.println(result);
} catch (Exception e) {}
}
}
BufferedInputStream, BufferedOutputStream – чтение байтового потока и запись в байтовый поток с добавлением буфера для улучшения производительности.
Это класс обертка для InputStream с буферизацией.
Благодаря буферизации потоки, которые переданы в конструктор BufferedInputStream будут читать данные из буфера маленькими порциями, а буфер, чтобы сэкономить время и силы, читает их из потока источника большими порциями.
Пример программы:
import java.io.*;
import java.util.*;
class BufferedStreamLesson{
public static void main(String[] args) {
String text = “Hello world!”;
byte[] buffer = text.getBytes();
//ByteArrayInputStream, ByteArrayInputStream очевидно
//для работы с массивами байтов
//(читать из массива байтов/писать в массив байтов).
ByteArrayInputStream in = new ByteArrayInputStream(buffer);
//теперь in и System.out буферизованные что улучшает
//производительность.
BufferedInputStream bis = new BufferedInputStream(in);
BufferedOutputStream bos=new BufferedOutputStream(System.out);
try{
int ch;
while((ch=bis.read()) != -1)
{
bos.write((char)ch);
//только что мы записали байт в
//буферизованный вывод BufferedOutputStream
//а для записи байта в поток, который
//обернут в BufferedOutputStream
//необходимо сделать flush
bos.flush();
}
}
catch(Exception e){
System.out.println(“gewgwg”);
}
}
}
DataInputStreamDataOutputStream – чтение байтового потока и запись в байтовый потокв формате примитивных типов данных.
Пример программы:
import java.io.*;
import java.util.*;
class DataStreamLesson{
public static void main(String[] args) {
try {
FileOutputStream fileOutputStream =
new FileOutputStream(“double.txt”);
FileInputStream fileInputStream =
new FileInputStream(“double.txt”);
// В данном примере файловый ввод/вывод.
// Запись в формате примитивных типов
// через файловый ввод.
DataInputStream dis =
new DataInputStream(fileInputStream);
// Чтение в формате примитивных типов
// через файловый вывод.
DataOutputStream dos =
new DataOutputStream(fileOutputStream);
// Запись double числа в файл.
dos.writeDouble(56.45);
// Ниже можно увидеть, что мы считали только
// что записанное в файл double число из файла
// в переменную double благодаря методу readDouble().
// То есть это тот поток, который нужен, когда
// необходимо считать откуда-то какой-то тип данных
// и, например, записать его в переменную этого типа.
// Есть методы и для других типов данных,
// например readChar, writeChar и тому подобное.
// Чтение в double переменную из файла только
// что записанного double числа.
double inpStrDouble = dis.readDouble();
System.out.println(inpStrDouble);
} catch (Exception e) {}
}
}
После dos.writeDouble(56.45); в файл double.txt записывается число 56.45 (в формате double, поэтому оно так выглядит в файле).
С помощью double inpStrDouble = dis.readDouble(); считываем из файла число, которое в формате double, в переменную double.
FileOutputStream, FileInputStream – для чтения файла и записи в файл.
В конструкторе должен быть указываем путь к файлу с которым будем совершать ввод/вывод.
Пример программы:
import java.io.*;
import java.util.*;
class FileStreamLesson {
public static void main(String[] args) {
try {
// FileOutputStream – для записи в файл. В конструктор
// FileOutputStream передаем в виде строки путь к файлу
// в который будем записывать.
// Если полноценный путь не указан, а только файл, значит
// файл, который мы собираемся читать/записывать находится
// в той же папке где и текущий файл java в который вы
// сейчас смотрите.
FileOutputStream fileOutputStream =
new FileOutputStream(“person.txt”);
// FileInputStream – для чтения файла. Также передаем путь
FileInputStream fileInputStream =
new FileInputStream(“person.txt”);
// Поскольку это OutputStream и InputStream значит, что
// читать из файла или записывать в файл будем байты.
// Поэтому чтобы записать в файл строку String
// ее сначала нужно преобразовать в массив байтов,
// и а его уже записывать в файл. Ниже это делаем.
// Строка которую будем записывать
String str = “Some Text”;
// преобразовываем строку в байты
byte[] buff = str.getBytes();
// через поток записи в файл записываем туда массив байт
fileOutputStream.write(buff, 0, buff.length);
// Теперь прочитаем из файла person.txt
// только что нами записанную строку.
// available возвращает текущее количество байтов в файле
// и пока оно больше нуля цикл продолжается.
int i;
while (fileInputStream.available() > 0) {
// с помощью read на каждой итерации цикла
// считываем один байт и записываем в переменную int.
// и после read возвращаемое количество байтов
// через метод available уменьшается на 1
i = fileInputStream.read();
// также необходимо привести к символу
// полученный из файла байт
System.out.println((char) i);
}
} catch (Exception e) {
}
}
}
После fileOutputStream.write(buff,0,buff.length); в файл person.txt записывается “Some Text” и файл выглядит так:
Циклом побайтно считается файл с помощью FileInputStream: