Ръководство за Java OutputStream

1. Общ преглед

В този урок ще проучим подробности за Java класа OutputStream . O utputStream е абстрактен клас. Това служи като суперклас за всички класове, представляващи изходен поток от байтове.

Ще разгледаме какво означават тези думи като „изход“ и „поток“ по-подробно, докато вървим напред.

2. Кратко въведение в Java IO

OutputStream е част от Java IO API, който дефинира класове, необходими за извършване на I / O операции в Java. Всички те са пакетирани в пространството от имена на java.io. Това е един от основните пакети, налични в Java от версия 1.0.

Стартирайки Java 1.4, ние също имаме Java NIO, пакетиран в пространството от имена java.nio, което позволява неблокиращи операции по въвеждане и извеждане. Нашата фокусна област за тази статия обаче е ObjectStream като част от Java IO.

Подробности, свързани с Java IO и Java NIO, можете да намерите тук.

2.1. Вход и изход

Java IO основно предоставя механизъм за четене на данни от източник и запис на данни до дестинация . Входът представлява източника, докато изходът представлява дестинацията тук.

Тези източници и дестинации могат да бъдат всичко - от файлове, тръби до мрежови връзки.

2.2. Потоци

Java IO предоставя концепцията за потоци, която основно представлява непрекъснат поток от данни . Потоците могат да поддържат много различни видове данни като байтове, знаци, обекти и т.н.

Освен това връзката с източник или дестинация е това, което представлява потокът. Следователно те идват съответно като InputStream или OutputStream .

3. Интерфейси на OutputStream

OutputStream реализира куп интерфейси, които осигуряват различен характер на своите подкласове. Да ги разгледаме бързо.

3.1. Затваряне

Интерфейсът Closeable осигурява метод, наречен close (), който обработва затваряне на източник или дестинация на данни. Всяко внедряване на OutputStream трябва да осигури изпълнение на този метод. Тук те могат да извършват действия за освобождаване на ресурси.

3.2. Автоматично затваряне

Интерфейсът AutoCloseable също предоставя метод, наречен close () с подобно поведение на този в Closeable . В този случай обаче методът close () се извиква автоматично при излизане от блок try-with-resource.

Повече подробности относно опитайте с ресурс можете да намерите тук.

3.3. Изплаква се

Интерфейсът Flushable осигурява метод, наречен flush (), който обработва данни за измиване до дестинация.

Една конкретна реализация на OutputStream може да избере да буферира предварително написаните байтове за оптимизиране, но извикването на flush () го кара незабавно да записва до дестинацията .

4. Методи в OutputStream

OutputStream има няколко метода, които всеки изпълняващ клас трябва да внедри за съответните си типове данни.

Те са различни от методите close () и flush () , които той наследява от Closeable и Flushable интерфейси.

4.1. пиши (int b)

Можем да използваме този метод, за да напишем един конкретен байт в OutputStream . Тъй като аргументът „int“ се състои от четири байта, като по договора се записва само първият байт от нисък ред, а останалите три байта от висок ред се игнорират:

public static void fileOutputStreamByteSingle(String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes[6]); } }

Ако извикаме този метод с данни като „Hello World!“, Това, което получаваме като резултат, е файл със следния текст:

W

Както виждаме, това е седмият знак от низа, индексиран шести.

4.2. запис (byte [] b, int off, int length)

Тази претоварена версия на метода write () е там, за да напише подпоследователност на байтовия масив в OutputStream .

It can write “length” number of bytes from the byte array as specified by the argument starting at an offset determined by “off” to the OutputStream:

public static void fileOutputStreamByteSubSequence( String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes, 6, 5); } }

If we now call this method with the same data as before, we get the following text in our output file:

World

This is the substring of our data starting at index five and comprising five characters.

4.3. write(byte[] b)

This is yet another overloaded version of the write() method which can write an entire byte array as specified by the argument to the OutputStream.

This has the same effect as a call to write(b, 0, b.lengh):

public static void fileOutputStreamByteSequence(String file, String data) throws IOException { byte[] bytes = data.getBytes(); try (OutputStream out = new FileOutputStream(file)) { out.write(bytes); } }

When we call this method now with the same data, we have the entire String in our output file:

Hello World!

5. Direct Subclasses of OutputStream

Now we'll discuss some of the direct known subclasses of OutputStream which individually represent a specific data type of which the OutputStream they define.

They define their own methods apart from implementing those inherited from OutputStream.

We won't go into the details of these subclasses.

5.1. FileOutputStream

As the name suggests, a FileOutputStream is an OutputStream to write data to a File. FileOutputStream, like any other OutputStream, can write a stream of raw bytes.

We have already examined different methods in FileOutputStream as part of the last section.

5.2. ByteArrayOutputStream

ByteArrayOutputStream is an implementation of OutputStream that can write data into a byte array. The buffer keeps growing as ByteArrayOutputStream writes data to it.

We can keep the default initial size of the buffer as 32 bytes or set a specific size using one of the constructors available.

The important thing to note here is that the method close() has practically no effect. The other methods in ByteArrayOutputStream can be safely called even after close() has been called.

5.3. FilterOutputStream

OutputStream primarily writes a byte stream to a destination, but it can as well transform the data before doing so. FilterOutputStream represents superclass of all such classes which perform a specific data transformation. FilterOutputStream is always constructed with an existing OutputStream.

Some of the examples of FilterOutputStream are BufferedOutputStream, CheckedOutputStream, CipherOutputStream, DataOutputStream, DeflaterOutputStream, DigestOutputStream, InflaterOutputStream, PrintStream.

5.4. ObjectOutputStream

ObjectOutputStream can write primitive data types and graphs of Java objects to a destination. We can construct an ObjectOutputStream using an existing OutputStream to write to a specific destination like File.

Please note that it is necessary for objects to implement Serializable for ObjectOutputStream to write them to a destination. You can find more details on Java Serialization here.

5.5. PipedOutputStream

A PipedOutputStream is useful to create a communication pipe. PipedOutputStream can write data which a connected PipedInputStream can read.

PipedOutputStream features a constructor to connect it with a PipedInputStream. Alternatively, we can do this later by using a method provided in PipedOutputStream called connect().

6. OutputStream Buffering

Input and output operations typically involve relatively expensive operations like disk access, network activity, etc. Performing this often can make a program less efficient.

We have “buffered streams” of data in Java to handle these scenarios. BufferedOutputStreamwrites data to a buffer instead which is flushed to the destination less often, when the buffer gets full, or the method flush() is called.

BufferedOutputStream extends FilterOutputStream discussed earlier and wraps an existing OutputStream to write to a destination:

public static void bufferedOutputStream( String file, String ...data) throws IOException { try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { for(String s : data) { out.write(s.getBytes()); out.write(" ".getBytes()); } } }

The critical point to note is that every call to write() for each data argument only writes to the buffer and does not result in a potentially expensive call to the File.

In the case above, if we call this method with data as “Hello”, “World!”, this will only result in data being written to the File when the code exits from the try-with-resources block which calls the method close() on the BufferedOutputStream.

This results in an output file with the following text:

Hello World!

7. Writing Text with OutputStreamWriter

A byte stream, as discussed earlier, represents raw data which may be a bunch of text characters. Now we can get the character array and perform the conversion to the byte array ourselves:

byte[] bytes = data.getBytes();

Java provides convenient classes to bridge this gap. For the case of OutputStream, this class is OutputStreamWriter. OutputStreamWriter wraps an OutputStream and can directly write characters to the desired destination.

We can also optionally provide the OutputStreamWriter with a character set for encoding:

public static void outputStreamWriter(String file, String data) throws IOException { try (OutputStream out = new FileOutputStream(file); Writer writer = new OutputStreamWriter(out,"UTF-8")) { writer.write(data); } }

Сега, както виждаме, не е нужно да извършваме трансформацията на масива от знаци в масив от байтове, преди да използваме FileOutputStream. OutputStreamWriter прави това удобно за нас .

Не е изненадващо, когато извикаме горния метод с данни като „Hello World!“, Това води до файл с текст като:

Hello World!

8. Заключение

В тази статия обсъдихме абстрактния клас на Java OutputStream . Преминахме през интерфейсите, които той прилага и методите, които предоставя.

След това обсъдихме някои от подкласовете на OutputStream, налични в Java. Най-накрая говорихме за буфериране и потоци от символи.

Както винаги, кодът за примерите е достъпен в GitHub.