Ajuda com serialização (Resolvido)

11 respostas
java
WilliamLycan

Olá, sou novo no fórum, gostaria que vocês me ajudassem com a serialização de um objeto. Tenho uma classe Char que tem atributos de um jogador de RPG, porém quando modifico o nome e carrego o objeto salvo com outro “nome” ele não carrega o atributo “nome” do objeto des-serializado, mas o que eu setei entre o salvamento e o carregamento.

public class Main {
    public static void main(String[] args) {
	    Char player = new Char("Will", 1, 100, 100, 40, 40);
		
	        player.save();
		
		player.setName("Bob");
		
		player.load(player);
		
		System.out.println(player.getName());
	    }
}

E a saída sempre dá “Bob” ao invés de Will. O que estou fazendo de errado?

E aqui está a classe Char:

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

public class Char implements Serializable {
	private static final long serialVersionUID = 1L;
	private String name;
	private int level;
	private int hp;
	private int mp;
	private int atk;
	private int def;

public static long getSerialVersionID(){
	return serialVersionUID;
}

public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
public int getLevel() {
	return level;
}
public void setLevel(int level) {
	this.level = level;
}
public int getHp() {
	return hp;
}
public void setHp(int hp) {
	this.hp = hp;
}
public int getMp() {
	return mp;
}
public void setMp(int mp) {
	this.mp = mp;
}
public int getAtk() {
	return atk;
}
public void setAtk(int atk) {
	this.atk = atk;
}
public int getDef() {
	return def;
}
public void setDef(int def) {
	this.def = def;
}

public void save() {
	try {
		FileOutputStream fileOut = new FileOutputStream("C:\\JavaIO\\player.save");
		ObjectOutputStream objOut = new ObjectOutputStream(fileOut);
		objOut.writeObject(this);
		objOut.close();
		fileOut.close();
	} catch(IOException e) {
		System.out.println("Ocorreu um erro: " + e.getMessage());
	}
}

public void load(Char c) {
	try {
		FileInputStream fileIn = new FileInputStream("C:\\JavaIO\\player.save");
		ObjectInputStream objIn = new ObjectInputStream(fileIn);
		c = (Char)objIn.readObject();
		objIn.close();
		fileIn.close();
	} catch(IOException e) {
		System.out.println("Ocorreu um erro: " + e.getMessage());
	} catch(ClassNotFoundException e) {
		System.out.println("Ocorreu um erro: " + e.getMessage());
	}
}

public Char(String name, int level, int hp, int mp, int atk, int def) {
	this.name = name;
	this.level = level;
	this.hp = hp;
	this.mp = mp;
	this.atk = atk;
	this.def = def;
}

}

E mais uma coisa: devo criar uma classe separada para os métodos save() e load() e assim poder salvar qualquer coisa, o estado do jogador, o inventário etc…?

11 Respostas

ThiagoA

Aparentimente você esta salvando os dados antes de altera-los. O que esta acontecendo, é que vc salva com o nome “Will”, altera para “Bob” mas não salva a alteração. Dai quando vc faz o carregamento dos dados, na verdade vc esta buscando os dados salvos do “Will”.

E sim! Para a pergunta dos metodos para salvar estados do jogo.
É uma boa pratica separa as classes por responsabilidade.

WilliamLycan

Obrigado Thiago, mas o problema é justamente esse: eu salvei o nome “Will”, carreguei e ele me mostra “Bob”! Por que ele me mostra “Bob” depois do carregamento do estado salvo em que o nome é “Will”?

ThiagoA

O seu metodo de load() não retorna nada, é um metodo sem retorno.
Não esta buscando o player salvo, esta simplismente impriminido o mesmo
objeto player modificado.

TalonNoxus

Bom... Você esta salvando de maneira correta.

Já na leitura, você esta passando um objeto por parâmetro, e nesse objeto você atribui o conteúdo do arquivo, só que em java sempre é passado uma cópia no parâmetro, e nunca o endereço da variável em si para ela ser modificada, ou seja, o seu objeto c está com o arquivo carregado direitinho, só que você não esta atribuindo nada ao seu objeto player apesar dele ter sido o parâmetro do método load.

Você pode mudar o tipo de retorno para Char, e em um bloco finally retornar c, então fazer
player = player.load(player)
Isso deve funcionar. Um exemplo pratico sobre copia é esse, ai você entenderá melhor
public class Main
{
	
	int a = 2;
	public void modificarValor(int numero)//Essa variável "numero" pode ser chamado de "a" que não irá mudar nada também
	{
		numero = numero + 5;
		System.out.println("Valor dentro do método -> " + numero);
		
	}
	public static void main(String[] args)
	{
		Main main = new Main();
		main.modificarValor(main.a);
		System.out.println("Valor fora do método -> " + main.a);
	}
}
WilliamLycan

Obrigado pessoal, entendi, é melhor usar um retorno e atribuir ao objeto. Quando passei o objeto como argumento na verdade eu estava passando somente uma cópia do valor e não o objeto em si (a referência). Obrigado mesmo!

TalonNoxus

Exato, testa lá e nos conta se funcionou

WilliamLycan

Deu certo, porém o Eclipse ainda me dá um warning no bloco finally (finally block does not complete normally):

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

public abstract class Game {
	public static boolean save(Object object){
		try {
			String path = "C:\\JavaIO\\";
			String extension = ".save";
			path = path.concat(object.toString());
			path = path.concat(extension);
			
			FileOutputStream fileOut = new FileOutputStream(path);
			ObjectOutputStream objOut = new ObjectOutputStream(fileOut);			
			objOut.writeObject(object);
			objOut.close();
			fileOut.close();
			return true;
		} catch(IOException e) {
			System.out.println(e.getMessage());
			return false;
		}
	}
	
	public static Object load(Object object) {
		try {
			String path = "C:\\JavaIO\\";
			String extension = ".save";
			path = path.concat(object.toString());
			path = path.concat(extension);
			
			FileInputStream fileIn = new FileInputStream(path);
			ObjectInputStream objIn = new ObjectInputStream(fileIn);
			object = objIn.readObject();
			objIn.close();
			fileIn.close();
		} catch(IOException e) {
			System.out.println(e.getMessage());
		} catch(ClassNotFoundException e) {
			System.out.println(e.getMessage());
		} finally {
			return object;
		}
	}
}

public class Main {
	public static void main(String args[]) {
		
		Char player = new Char("Will", 2, 100, 100, 40, 40);
		
		Game.save(player);
		
		player.setName("Bob");
		
		player = (Char)Game.load(player);
		
		System.out.println(player.getName());
	}
}

E quanto ao caminho (path) do arquivo, estou fazendo de maneira correta? Existe alguma desvantagem ou algum problema em usar o object.toString() para montar o nome do arquivo?

TalonNoxus

Eu acho que não tem problema algum utilizar o toString()

Eu falei para por o return em um bloco finally, mas eu acho que não é a maneira correta de se fazer isso, se você tirar o finally e por o return fora do try deverá funcionar também

WilliamLycan

Retirei o bloco finally, tem como montar um caminho relativo para o arquivo de save?

TalonNoxus

Criará um arquivo chamado arquivo.txt no projeto ao invés de criar no C;/JavaIO como você estava fazendo

esmiralha

Como regra geral, nunca use return em um bloco finally.

Criado 23 de setembro de 2016
Ultima resposta 24 de set. de 2016
Respostas 11
Participantes 4