I am currently preparing for an exam and am working on the following task:
How can I pass an ArrayList to a "save" method that stores the list's data and another "load" method that passes the data back?
class Person {
private String firstname;
private String lastname;
private String sortname;
public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
updateSortname();
//getter and setter..
According to the task I should use these methods:
public static List<Person> load(String filename) throws IOException {
return ??;
}
public static Person load(DataInputStream in) throws IOException {
return ??;
}
public static void save(String filename, List<Person> list) throws IOException {
}
public static void save(DataOutputStream out, Person person) throws IOException {
}
public static List<Person> unserialize(String filename) throws IOException, ClassNotFoundException {
return ??;
}
public static void serialize(String filename, List<Person> persons) throws IOException {
}
This is the main method that should produce the following output:
[Willy Wonka (WonkaWilly), Charlie Bucket (BucketCharlie), Grandpa Joe
(JoeGrandpa)]
[Willy Wonka (WonkaWilly), Charlie Bucket (BucketCharlie), Grandpa Joe
(JoeGrandpa)]
[Willy Wonka (WonkaWilly), Charlie Bucket (BucketCharlie), Grandpa Joe
(JoeGrandpa)]
public class PersonTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
List<Person> persons = new ArrayList<>();
persons.add(new Person("Willy", "Wonka"));
persons.add(new Person("Charlie", "Bucket"));
persons.add(new Person("Grandpa", "Joe"));
System.out.println(persons);
Person.save("persons.sav", persons);
persons = Person.load("persons.sav");
System.out.println(persons);
Person.serialize("persons.ser", persons);
persons = Person.unserialize("persons.ser");
System.out.println(persons);
}
}
It should look something like this. But I don't know how I do it for ArrayLists.
public static void save(String filename , Graph graph ) throws IOException{
try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream (new FileOutputStream (filename)))) {
out.writeObject (graph);
}
}
public static Graph load (String filename) throws IOException, ClassNotFoundException {
Graph graph = null;
try (ObjectInputStream in = new ObjectInputStream (new BufferedInputStream ( new FileInputStream (filename)))) {
graph = (Graph) in.readObject();
}
return graph;
}
As you need output of the Person Object to be as, we need to override toString() of Person class.
[Willy Wonka (WonkaWilly), Charlie Bucket (BucketCharlie), Grandpa Joe (JoeGrandpa)]
class Person {
//Respective Constructor, Getter & Setter methods
/* Returns the string representation of Person Class.
* The format of string is firstName lastName (lastNameFirstName)*/
#Override
public String toString() {
return String.format(firstName + " " + lastName + "("+ lastName + firstName + ")");
}
}
There are many approaches to writing objects to file. Here is with PrintWriter
Save Objects to File
public static void save(String filename, List<Person> list) throws IOException {
PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));
for (Person person : list) {
pw.println(person.toString());
}
pw.close();
}
Or Use serialization
// You could use serialization mechanism.
To use it, you need to do the following:
Declare the Person class as implementing Serializable:
public class Person implements Serializable {
...
#Override
public String toString() {
return String.format(firstName + " " + lastName + "("+ lastName + firstName + ")");
}
}
Write your list to file:
public static void save(String filename, List<Person> list) throws IOException {
FileOutputStream fos = new FileOutputStream(filename);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(list);
oos.close();
}
Read List from File:
public static List<Person> load(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream ois = new ObjectInputStream(fis);
List<Person> list = (List<Person>) ois.readObject();
ois.close();
return list;
}
You can try something like this:
public static void save(String filename , ArrayList<Person> persons) throws IOException{
try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream (new FileOutputStream (filename)))) {
for(int i = 0; i < persons.size; i++){
out.writeObject(persons.get(i));
}
}
}
Related
I really don't understand the use of overriding the clone().In case of both shallow and deep cloning we can write any method name and it can serve the purpose.Also we are not using the parent(Object) reference to call the clone method .Then whats the use of overriding please explain.
Shallow Copy
class Person implements Cloneable {
private String name;
protected Object copy() {//method name is copy
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public void setName(String name) {
this.name = name;
}
}
public class TestClone {
public static void main(String[] args) {
Person ob1 = new Person();
ob1.setName("Bibhu");
Person ob2 = (Person) ob1.copy();
System.out.println(ob1.getClass() == ob2.getClass());//true
}
}
Deep Copy
import java.util.ArrayList;
import java.util.List;
class Company {
private String name;
private List<String> empList = new ArrayList<>();
public Company(String name, List<String> empList) {
this.name = name;
this.empList = empList;
}
public String getName() {
return name;
}
public List<String> getEmpList() {
return empList;
}
public Object copy() {
List<String> tempList = new ArrayList<>();
for (String s : this.empList) {
tempList.add(s);
}
String cName = this.name;
return new Company(cName, tempList);
}
}
public class TestDeepClone {
public static void main(String[] args) {
List<String> empList = new ArrayList<>();
empList.add("Bibhu");
empList.add("Raj");
empList.add("John");
Company c1 = new Company("ABC Company", empList);
Company c2 = (Company) c1.copy();
System.out.println(c1.getClass() == c2.getClass());//true
System.out.println(c1.getEmpList() == c2.getEmpList());//false
}
}
It's just for convention. Even the JavaDoc sais that:
By convention, classes that implement this interface should override
Object.clone (which is protected) with a public method. See
Object.clone() for details on overriding this method.
public interface FileReader {
void readFile();
void writeFile();
}
public class XMLReader implements FileReader {
#Override
public void readFile() {
System.out.println("Hellp i am read");
}
#Override
public void writeFile() {
System.out.println("Hello i am write");
}
}
public class ExcelReader implements FileReader {
#Override
public void readFile() {
System.out.println("Hellp i am read");
}
#Override
public void writeFile() {
System.out.println("Hello i am write");
}
}
public class Context {
FileReader reader ;
public Context(FileReader reader) {
super();
this.reader = reader;
}
public void executeRead(){
reader.readFile();
}
}
public class TestStrategy {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(args[0]);
String s=args[0];
String[] a=s.split("\\.");
if(a[1].equals("csv")){
new Context(new XMLReader()).executeRead();
}else{
new Context(new ExcelReader()).executeRead();
}
}
}
I have a concern more file format are introduced we will create separate class for them but i have to change the if else code in TestStrategy class to create new object for the file pattern introduced.
Can we remove this if else code .Any suggestions.
You could use a registry that maps a files extension to the implementation.
public class Registry {
static Map<String,FileReader> reg = new HashMap<String,FileReader>();
public static void register(String ext, FileReader fr ) {
reg.put( ext, fr );
}
}
and let newly added implementation register themself e.g.
public class XMLReader implements FileReader {
static {
Registry.register( "xml", new XMLReader() );
}
....
public class ExcelReader implements FileReader {
static {
Registry.register( "xls", new ExcelReader() );
}
...
then you could simply lookup the registry for a suitable implementation with no if or switch required.
You can get a class by name. Build a Map to configure the FileReader to use for each extension.
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Map<String, String> extensionToClass = new HashMap<>();
extensionToClass.put("xml", "de.lhorn.XMLReader");
extensionToClass.put("xls", "de.lhorn.ExcelReader");
String s = "foo.xml";
String[] a = s.split("\\.");
String extension = a[1];
// Get the class that is configured for the extension.
String className = extensionToClass.get(extension);
Class clazz = Class.forName(className);
// Create a new instance of this class.
FileReader reader = (FileReader) clazz.newInstance();
// Use the FileReader.
new Context(reader).executeRead();
}
You can read extensionToClass from an external source, of course.
I have a problem while serializing an object in Java(8). I saw many examples, but none of them work for me. The thing is that while serializing it doesn't serialize the object with its complete data. When I try to deserialize, it reads all the variables as null. I do this with Employee class. The code for Serialize.java :
public class Serialize {
private static ArrayList<Employee> emp = new ArrayList<Employee>();
public static void main(String[] args){
try{
emp.add(new Employee("Areg Hovhannisyan",5));
emp.add(new Employee("Tigran Hakobyan",15));
emp.add(new Employee("Shivanshu Ojha",11));
FileOutputStream fos = new FileOutputStream("emps.emp");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(emp);
out.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Employee.java:
import java.io.Serializable;
public class Employee implements Serializable {
private static int age;
private static String name;
public static int getAge() {
return age;
}
public static void setAge(int age) {
Employee.age = age;
}
public static String getName() {
return name;
}
public static void setName(String name) {
Employee.name = name;
}
public Employee(String name,int i) {
this.name = name;
this.age = i;
}
#Override
public String toString() {
return "Name : " + getName() + ", Age : " + getAge();
}
}
Please give an example to do this,how to deserialize and please give it with a explanation because I also want to understand how it works.
This is because your fields in class are static. Statics are implicitly transient and we can not serialize transient fields.
The only problem in your code is that age and name fields should not be static for what you intend to do...
Your code should work just by removing the two static modifiers.
Then you maybe should read about static modifier to understand why your code couldn't work.
Default serializable fields of a class are defined to be the non-transient and non-static fields.
Oracles Link
Here is stack OverFlow Link for it
As mentioned in the above comments, Statics are implicitly transient. Also as per your code if the variables are static, you'll have one vaule(which is finally added) in your Arraylist. This is the behaviour of static variable.
Please give an example to do this,how to deserialize
Code with Deserialization:
public static void main(String[] args) throws IOException,
ClassNotFoundException {
FileOutputStream fos = null;
ObjectOutputStream out = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try {
emp.add(new Employee("Areg Hovhannisyan", 5));
emp.add(new Employee("Tigran Hakobyan", 15));
emp.add(new Employee("Shivanshu Ojha", 11));
fos = new FileOutputStream("emps.emp");
out = new ObjectOutputStream(fos);
out.writeObject(emp);
fis = new FileInputStream("emps.emp");
in = new ObjectInputStream(fis);
ArrayList<Employee> empRead = (ArrayList) in.readObject();
System.out.println(empRead.get(0));
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
fos.close();
}
}
I have read a little bit about 3rd party serialization methods such as JSON, but was wondering if there is any other way to serialize objects such as an area that do not implement serializable. In other word would JSON be the best way to serialize such an object?
EDIT : Throwing A NotSerializable Exception
public class Test {
public static void main(String[] args) throws Exception {
Pojo pojo = new Pojo(new Area()); // The original object, NOT SERIALIZABLE !
byte[] pojoBytes = Serializer.serialize(pojo); // Serialize
pojo = (Pojo) Serializer.deserialize(pojoBytes); // Deserialize
System.out.println(pojo); // Good as new !
}
}
public class Pojo implements Serializable {
private final Area msg;
public Pojo(Area msg) {
this.msg = msg;
}
public Area getMsg() {
return msg;
}
public String toString() {
return "Pojo says : " + msg;
}
}
public class Serializer {
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileOutputStream fileOut = new FileOutputStream("Test.ser");
ObjectOutputStream oos = new SurrogateObjectOutputStream(fileOut); // Magically handle Pojos !
oos.writeObject(o);
oos.flush();
oos.close();
return baos.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
FileInputStream fileIn = new FileInputStream("Test.ser");
ObjectInputStream ois = new ObjectInputStream(fileIn);
Object o = ois.readObject();
bais.close();
return o;
}
}
public class SurrogateObjectOutputStream extends ObjectOutputStream {
public SurrogateObjectOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateObjectOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Pojo) {
return new PojoSurrogate((Pojo) obj);
} else return super.replaceObject(obj);
}
}
public class PojoSurrogate implements Serializable {
private Area foo;
public PojoSurrogate(Pojo pojo) {
this.foo = pojo.getMsg();
}
private Object readResolve() throws ObjectStreamException {
return new Pojo(foo);
}
}
It depends, if you want to use that Object in another program or another language for that matter, then yes JSON is the way to go (or XML).
But if you want to reuse that Object in another JAVA program then I guess it would be more convenient to look for a way to make possible non-serializable Objects serializable.
I haven't tested it yet, but I found a promising solution in this blog (which is in French, sorry). I'll try my best to summarize it:
what you have
say you have a class names Pojo and you want to serialize it although you don't know if it is serializable or not.
public final class Pojo {
private final String msg;
public Pojo(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public String toString() {
return "Pojo says : " + msg;
}
}
what you need
what you need is a new class that acts as a surrogate, which will take the member variables of the real Pojo and simply replace it.
public class PojoSurrogate implements Serializable {
private String foo;
public PojoSurrogate(Pojo pojo) {
this.foo = pojo.getMsg();
}
private Object readResolve() throws ObjectStreamException {
return new Pojo(foo);
}
}
the last method ( readResolve() ) is the one that will in the end give you back your new Pojo later.
The other thing you need is your own surrogate version of the ObjectOutputStream:
public class SurrogateObjectOutputStream extends ObjectOutputStream {
public SurrogateObjectOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateObjectOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Pojo) {
return new PojoSurrogate((Pojo) obj);
} else return super.replaceObject(obj);
}
}
And again here the last method replaceObject() will do the magic and transform the Pojo into the serializable version PojoSurrogate to store all the information as bytes.
Serialize like this
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new SurrogateObjectOutputStream(baos);
oos.writeObject(o);
oos.flush();
oos.close();
byte[] serializedPojo = baos.toByteArray();
Deserialize normally
ObjectInputStream bais = new ObjectInputStream(new ByteArrayInputStream( serializedPojo ));
Pojo myNewPojo = (Pojo) bais.readObject();
bais.close();
Sorry, long answer.. and I hope I haven't missed anything supercool from that blog where it is easier to create a more scalable solution.. hope this helps anyway!
[EDIT:]
I tried your code with an Area object and here is how I got some thing to work (although I am not sure if this is actually working with all Areas, so you might have to test if your areas still have the same characteristics after deserialization)
AreaSurrogate
public class AreaSurrogate implements Serializable {
private final Rectangle bounds;
public AreaSurrogate(Area area) {
this.bounds = area.getBounds();
}
private Object readResolve() throws ObjectStreamException {
return new Area(bounds);
}
}
SurrogateOutputStream
public class SurrogateOutputStream extends ObjectOutputStream {
public SurrogateOutputStream(OutputStream out) throws IOException {
super(out);
enableReplaceObject(true);
}
protected SurrogateOutputStream() throws IOException, SecurityException {
super();
enableReplaceObject(true);
}
#Override
protected Object replaceObject(Object obj) throws IOException {
if (obj instanceof Area) {
return new AreaSurrogate((Area) obj);
} else {
return super.replaceObject(obj);
}
}
}
Serializer
public class Serializer {
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new SurrogateOutputStream(baos); // Magically handle Pojos !
oos.writeObject(o);
oos.flush();
oos.close();
return baos.toByteArray();
}
public static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
Object o = ois.readObject();
bais.close();
return o;
}
}
Main (to test)
public static void main(String[] args) throws Exception {
Area area = new Area(new Rectangle(0, 0, 100, 100)); // The original object, NOT SERIALIZABLE !
System.out.println(area.contains(new Rectangle(1, 1, 10, 10))); // Good as new !
System.out.print("serializing...");
byte[] pojoBytes = Serializer.serialize(area); // Serialize
System.out.println("done");
System.out.print("deserializing...");
area = (Area) Serializer.deserialize(pojoBytes); // Deserialize
System.out.println("done");
System.out.println(area.contains(new Rectangle(1, 1, 10, 10))); // Good as new !
}
In the main() I create an Area from a Rectangle which starts at Coordinate (0,0) and is 100 wide and 100 high. Then I test if the Rectangle from (1,1) with 10 width and 10 height is inside the area (which it is, forcibly). After serializing and deserializing I test if the same Rectangle is still inside the new Area.
This might be insufficient, since the new Area Object is created from a Rectangle (see AreaSurrogate). so this might not work with other Area shapes..
expected output
true
serializing...done
deserializing...done
true
I want to define my own serialize and deserialize methods for Student class, so I extended TypeAdapter and override its methods, but now deserialization does not work. Why this happens?
public class GSONFormat {
#Test
public void run()
{
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Student.class, new StudentAdapter());
Gson gson = builder.create();
Student s = new Student();
s.setAge(11);
s.setName("hiway");
System.out.println(gson.toJson(s));
String str = "{\"age\":11,\"name\":\"hiway\"}";
s = gson.fromJson(str, Student.class);
System.out.println(s);
}
}
class Student{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class StudentAdapter extends TypeAdapter<Student>
{
#Override
public void write(JsonWriter out, Student s) throws IOException {
out.beginObject();
out.name("age");
out.value(s.getAge());
out.name("name");
out.value(s.getName());
out.endObject();
}
#Override
public Student read(JsonReader in) throws IOException {
in.beginObject();
Student s = new Student();
s.setAge(in.nextInt());
s.setName(in.nextString());
in.endObject();
return s;
}
}
As Matt Ball commented is not a good idea at all writing your type adapter if you do not have a very good reason at all. If you remove this line:
builder.registerTypeAdapter(Student.class, new StudentAdapter());
your parsing will work without any effort. However you might be interested in understand a bit further what you can do with type adapter. So I give you a possible implementation. Keep in mind that if your class Student get more complex, writing your own type adapter can be hard. It's better to let Gson do its own work. Anycase, here is something that patches your code (compiles under Java7, if you use lower version, change switch with an if chain).
class StudentAdapter extends TypeAdapter<Student>
{
#Override
public void write(JsonWriter out, Student s) throws IOException {
out.beginObject();
out.name("age");
out.value(s.getAge());
out.name("name");
out.value(s.getName());
out.endObject();
}
#Override
public Student read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
in.beginObject();
Student s = new Student();
while (in.peek() == JsonToken.NAME){
String str = in.nextName();
fillField(in, s, str);
}
in.endObject();
return s;
}
private void fillField(JsonReader in, Student s, String str)
throws IOException {
switch(str){
case "age": s.setAge(in.nextInt());
break;
case "name": s.setName(in.nextString());
break;
}
}
}