Showing posts with label I/O. Show all posts
Showing posts with label I/O. Show all posts

Friday, April 24, 2009

Serializing non-serializable objects using readObject and writeObject

Basic serialization is pretty straightforward, just make the class implement the Serializable interface. Serializable is a marker interface, so pretty much it involves nothing i.e. implementing no methods. Also if one of the super classes of the class is serializable then the class is automatically serializable. Here the basic assumption is that all the member variables of the class are serializable. However that isn't always the case. Class sometimes contains one or more member variables that are not serializable. In such cases, one of the mechanisms to make the class serializable is to do the following:
- Mark the non-serializable fields transient. So these are not saved as part of the object state.
- Implement the following methods in the class:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

When the above methods are implemented first call the defaultReadObject/defaultWriteObject to save the state of the serializable member fields and then proceed to read/write the transient variables. Here is an example:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MySerializationTest implements Serializable{

private static final long serialVersionUID = 1L;
transient Color color;
int length;

public MySerializationTest(String color, int length) {
this.color = new Color(color);
this.length = length;
}

//Deserialization
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
color = new Color((String)ois.readObject());
}

//Serialization
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(color.getColor());
}

@Override
public String toString() {
StringBuffer str = new StringBuffer();
str.append("Color:" + color.getColor());
str.append(" length:" + length);
return str.toString();
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
String filename = "Ser.ser";
MySerializationTest mySer = new MySerializationTest("Green", 20), mySer2;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(filename));
//Write serialized object to file
oos.writeObject(mySer);
ois = new ObjectInputStream(new FileInputStream(filename));
//read the serialized object
mySer2 = (MySerializationTest)ois.readObject();
System.out.println(mySer2);
} finally {
oos.close();
ois.close();
}


}

}

//Non serialized class
class Color {

private String color;

public Color(String color) {
this.color = color;
}

public void setColor(String color) {
this.color = color;
}

public String getColor() {
return color;
}

}


Output:
Color:Green length:20

Tuesday, March 31, 2009

Using Java I/O API to read and write integers as bytes to a binary file

Java I/O API is very vast. I have shown how to use BufferedInputStream and BufferedOutput stream to read and write integers as bytes to a file.My test case only involves 5 integers but the program is meant for a large number of integers.

BufferedInputStream:
My input stream is binary and not character. So I have used the stream type BufferedInputStream. I have wrapped the plain FieInputStream with the BufferedInputStream so that the input can be buffered. Then I have used the following method int read(byte[] b,int off,int len) to read up to len bytes of data from the stream into an array of bytes, thereby, improving performance.

BufferedOutputStream:
I have used the BufferedOutputStream to write all the bytes at once instead of using the plain FileOutputStream.

Efficient way to read integers from a file of bytes

import java.io.*;

public class ReadFile {

private static final int numOfBytesInInt = 4;

/**
* This method reads bytes from a file and converts them to integers.
* @param name filename
* @param numOfBytes Number of bytes to read
* @return Integer array
*/
public static int[] getIntegersStoredAsBytesFromFile(String name, int numOfBytes) throws IOException {
byte[] b = readBytesFromFile(name,numOfBytes);
return convertBytesToInteger(b);
}

/**
* This method actually reads bytes from a file in one shot(hopefully).
* It is more efficient than reading 1 integer or byte at a time in a file.
* @param name filename
* @param numOfBytes Number of bytes to read
*/
public static byte[] readBytesFromFile(String name, int numOfBytes) throws IOException {
BufferedInputStream bis = null;
byte[] b = new byte[numOfBytes];
try {
bis = new BufferedInputStream(new FileInputStream(name));
bis.read(b);
return b;
} finally {
bis.close();
}
}

/**
* This helper method actually converts each 4 bytes into an integer.
* There is also another way of doing it through bit manipulation.
* @param b The byte array to convert to int
* @return The array of ints
*/
public static int[] convertBytesToInteger(byte[] b) throws IOException{
ByteArrayInputStream bai = new ByteArrayInputStream(b);
DataInputStream dis = new DataInputStream(bai);
int len = b.length;
int[] ints = new int[len/numOfBytesInInt]; //4 bytes = 1 int
for (int i = 0,j = 0; i < len; i += numOfBytesInInt, j++) {
ints[j] = dis.readInt();
}
return ints;
}

}


 

Efficient way to write integers as bytes to a file

import java.io.*;
import java.util.Random;

public class WriteFile {

private static final int numOfBytesInInt = 4;

/**
* This method generates integers using random number
* generator, converts each integer to 4 bytes and
* writes it to a file
* @param name filename
* @param numOfBytes Number of bytes to write
*/
public static void generateIntegersAndStoreInFileAsBytes(
String name, int numOfBytes) throws IOException {
//Generate a byte[] of integers
byte[] b = generateIntegersAsBytes(numOfBytes);
//Write them to the file
writeBytesToFile(name,b);
}

/**
* This helper method actually generates the integers
* using random number generator and converts
* each integer to 4 bytes
* @param numOfBytes Number of bytes to generate
* @return Array of bytes
*/
public static byte[] generateIntegersAsBytes(int numOfBytes) throws IOException {
byte[] b = new byte[numOfBytes];
int randomNumber = 0;
Random r = new Random();
for (int offset = 0; offset < b.length; offset += numOfBytesInInt) {
//Generate random int
randomNumber = r.nextInt();
//Just have it here to show what is written into the file
//is what we are reading later..
System.out.println(randomNumber);
//convert int to bytes
intToByte(randomNumber, b, offset);
}
return b;
}

/**
* This method writes all the bytes to a file in a one shot(hopefully without blocking).
* It is more efficient than writing 1 integer at a time in a file.
* @param name filename
* @param b Array of bytes to write
*/
public static void writeBytesToFile(String name, byte[] b) throws IOException{
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(name));
bos.write(b);
bos.flush();
} finally {
bos.close();
}
}

/**
* This helper method actually converts each integer to 4 bytes.
* See the next method for another way to do the same thing.
* @param integer Integer to be converted to bytes
* @param b The byte array into which the bytes have to be stored
* @param offset The start offset in b
*/
public static void intToByte(int integer, byte[] b, int offset) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(integer);
dos.flush();
System.arraycopy(bos.toByteArray(), 0, b, offset, numOfBytesInInt);
}


/* Instead of using the intToByte method, one could also manually convert
* the integer to an array of bytes using bit arithmetic.
* Here is a way to do it. To use this method, just replace the call
* to the method intToByte() with generateByte() above
*
* private static void generateByte(int integer, byte[] b, int offset) {
b[offset]=(byte)((integer & 0xff000000)>>>24);
b[offset+1]=(byte)((integer & 0x00ff0000)>>>16);
b[offset+2]=(byte)((integer & 0x0000ff00)>>>8);
b[offset+3]=(byte)((integer & 0x000000ff));
}*/

}

 

Test It

import java.io.*;
import static java.lang.System.out;

public class TestingFiles {

private static final String filename = "INPUTBYTES";

/*
* I am just testing with 5 integers but you can practically
* use any number which is a multiple of 4.
*/
private static final int numOfBytes = 20;

public static void main(String[] args) throws IOException{
out.println("Writing to File");
WriteFile.generateIntegersAndStoreInFileAsBytes(filename,numOfBytes);
out.println("Reading from File");
int[] ints = ReadFile.getIntegersStoredAsBytesFromFile(filename,numOfBytes);
for (int i: ints) {
out.println(i);
}
}


}


Here is the output:
Writing to File
931158145
189423544
641759623
-1731070039
1477642147
Reading from File
931158145
189423544
641759623
-1731070039
1477642147