1. 幾種java序列化 對比
在java中socket傳輸數據時,數據類型往往比較難選擇。可能要考慮帶寬、跨語言、版本的兼容等問題。比較常見的做法有兩種:一是把對象包裝成JSON字元串傳輸,二是採用java對象的序列化和反序列化。隨著Google工具protoBuf的開源,protobuf也是個不錯的選擇。對JSON,Object Serialize,ProtoBuf 做個對比。
定義一個待傳輸的對象UserVo:
Java代碼
public class User{
private String name;
private int age;
private long phone;
private List<User> friends;
}
初始化User的實例src:
Java代碼
User user1 = new UserVo();
user1 .setName("user1 ");
user1 .setAge(30);
user1 .setPhone(13789126278L);
UserVo f1 = new UserVo();
f1.setName("tmac");
f1.setAge(32);
f1.setPhone(123L);
User user2 = new User();
user2 .setName("user2 ");
user2 .setAge(29);
user2 .setPhone(123L);
List<User> friends = new ArrayList<User>();
friends.add(user1 );
friends.add(user2 );
user1 .setFriends(friends);
JSON格式
採用Google的gson-2.2.2.jar 進行轉義
Java代碼
Gson gson = new Gson();
String json = gson.toJson(src);
得到的字元串:
Js代碼
{"name":"user1 ","age":30,"phone":123,"friends":[{"name":"user1 ","age":32,"phone":123},{"name":"user2 ","age":29,"phone":123}]}
位元組數為153
Json的優點:明文結構一目瞭然,可以跨語言,屬性的增加減少對解析端影響較小。缺點:位元組數過多,依賴於不同的第三方類庫。
Object Serialize
UserVo實現Serializalbe介面,提供唯一的版本號:
Java代碼
public class User implements Serializable{
private static final long serialVersionUID = -5726374138698742258L;
private String name;
private int age;
private long phone;
private List<User> friends;
序列化方法:
Java代碼
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();
位元組數是238
反序列化:
Java代碼
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (UserVo) ois.readObject();
ois.close();
fis.close();
Object Serializalbe 優點:java原生支持,不需要提供第三方的類庫,使用比較簡單。缺點:無法跨語言,位元組數佔用比較大,某些情況下對於對象屬性的變化比較敏感。
對象在進行序列化和反序列化的時候,必須實現Serializable介面,但並不強制聲明唯一的serialVersionUID
是否聲明serialVersionUID對於對象序列化的向上向下的兼容性有很大的影響。我們來做個測試:
思路一
把User中的serialVersionUID去掉,序列化保存。反序列化的時候,增加或減少個欄位,看是否成功。
Java代碼
public class User implements Serializable{
private String name;
private int age;
private long phone;
private List<UserVo> friends;
保存到文件中:
Java代碼
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(src);
os.flush();
os.close();
byte[] b = bos.toByteArray();
bos.close();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(b);
fos.close();
增加或者減少欄位後,從文件中讀出來,反序列化:
Java代碼
FileInputStream fis = new FileInputStream(dataFile);
ObjectInputStream ois = new ObjectInputStream(fis);
vo = (User) ois.readObject();
ois.close();
fis.close();
結果:拋出異常信息
Java代碼
Exception in thread "main" java.io.InvalidClassException: serialize.obj.UserVo; local class incompatible: stream classdesc serialVersionUID = 3305402508581390189, local class serialVersionUID = 7174371419787432394
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:560)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
at serialize.obj.ObjectSerialize.read(ObjectSerialize.java:74)
at serialize.obj.ObjectSerialize.main(ObjectSerialize.java:27)
思路二
eclipse指定生成一個serialVersionUID,序列化保存,修改欄位後反序列化
略去代碼
結果:反序列化成功
結論
如果沒有明確指定serialVersionUID,序列化的時候會根據欄位和特定的演算法生成一個serialVersionUID,當屬性有變化時這個id發生了變化,所以反序列化的時候就會失敗。拋出「本地classd的唯一id和流中class的唯一id不匹配」。
jdk文檔關於serialVersionUID的描述:
寫道
如果可序列化類未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的默認 serialVersionUID 值,如「Java(TM) 對象序列化規范」中所述。不過,強烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因是計算默認的 serialVersionUID 對類的詳細信息具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在反序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於直接聲明類 -- serialVersionUID 欄位作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有默認的計算值,但是數組類沒有匹配 serialVersionUID 值的要求。
Google ProtoBuf
protocol buffers 是google內部得一種傳輸協議,目前項目已經開源(http://code.google.com/p/protobuf/)。它定義了一種緊湊得可擴展得二進制協議格式,適合網路傳輸,並且針對多個語言有不同得版本可供選擇。
以protobuf-2.5.0rc1為例,准備工作:
下載源碼,解壓,編譯,安裝
Shell代碼
tar zxvf protobuf-2.5.0rc1.tar.gz
./configure
./make
./make install
測試:
Shell代碼
MacBook-Air:~ ming$ protoc --version
libprotoc 2.5.0
安裝成功!進入源碼得java目錄,用mvn工具編譯生成所需得jar包,protobuf-java-2.5.0rc1.jar
1、編寫.proto文件,命名UserVo.proto
Text代碼
package serialize;
option java_package = "serialize";
option java_outer_classname="UserVoProtos";
message User{
optional string name = 1;
optional int32 age = 2;
optional int64 phone = 3;
repeated serialize.UserVo friends = 4;
}
2、在命令行利用protoc 工具生成builder類
Shell代碼
protoc -IPATH=.proto文件所在得目錄 --java_out=java文件的輸出路徑 .proto的名稱
得到UserProtos類
3、編寫序列化代碼
Java代碼
UserVoProtos.User.Builder builder = UserVoProtos.User.newBuilder();
builder.setName("Yaoming");
builder.setAge(30);
builder.setPhone(13789878978L);
UserVoProtos.UserVo.Builder builder1 = UserVoProtos.UserVo.newBuilder();
builder1.setName("tmac");
builder1.setAge(32);
builder1.setPhone(138999898989L);
UserVoProtos.UserVo.Builder builder2 = UserVoProtos.UserVo.newBuilder();
builder2.setName("liuwei");
builder2.setAge(29);
builder2.setPhone(138999899989L);
builder.addFriends(builder1);
builder.addFriends(builder2);
UserVoProtos.UserVo vo = builder.build();
byte[] v = vo.toByteArray();
位元組數53
4、反序列化
Java代碼
UserVoProtos.UserVo uvo = UserVoProtos.UserVo.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
結果:tmac,反序列化成功
google protobuf 優點:位元組數很小,適合網路傳輸節省io,跨語言 。缺點:需要依賴於工具生成代碼。
工作機制
proto文件是對數據的一個描述,包括欄位名稱,類型,位元組中的位置。protoc工具讀取proto文件生成對應builder代碼的類庫。protoc xxxxx --java_out=xxxxxx 生成java類庫。builder類根據自己的演算法把數據序列化成位元組流,或者把位元組流根據反射的原理反序列化成對象。官方的示例:https://developers.google.com/protocol-buffers/docs/javatutorial。
proto文件中的欄位類型和java中的對應關系:
詳見:https://developers.google.com/protocol-buffers/docs/proto
.proto Type java Type c++ Type
double double double
float float float
int32 int int32
int64 long int64
uint32 int uint32
unint64 long uint64
sint32 int int32
sint64 long int64
fixed32 int uint32
fixed64 long uint64
sfixed32 int int32
sfixed64 long int64
bool boolean bool
string String string
bytes byte string
欄位屬性的描述:
寫道
required: a well-formed message must have exactly one of this field.
optional: a well-formed message can have zero or one of this field (but not more than one).
repeated: this field can be repeated any number of times (including zero) in a well-formed message. The order of the repeated values will be preserved.
protobuf 在序列化和反序列化的時候,是依賴於.proto文件生成的builder類完成,欄位的變化如果不表現在.proto文件中就不會影響反序列化,比較適合欄位變化的情況。做個測試:
把UserVo序列化到文件中:
Java代碼
UserProtos.User vo = builder.build();
byte[] v = vo.toByteArray();
FileOutputStream fos = new FileOutputStream(dataFile);
fos.write(vo.toByteArray());
fos.close();
為User增加欄位,對應的.proto文件:
Text代碼
package serialize;
option java_package = "serialize";
option java_outer_classname="UserVoProtos";
message User{
optional string name = 1;
optional int32 age = 2;
optional int64 phone = 3;
repeated serialize.UserVo friends = 4;
optional string address = 5;
}
從文件中反序列化回來:
Java代碼
FileInputStream fis = new FileInputStream(dataFile);
byte[] dstb = new byte[fis.available()];
for(int i=0;i<dstb.length;i++){
dstb[i] = (byte)fis.read();
}
fis.close();
UserProtos.User uvo = UserProtos.User.parseFrom(dstb);
System.out.println(uvo.getFriends(0).getName());
成功得到結果。
三種方式對比傳輸同樣的數據,google protobuf只有53個位元組是最少的。結論:
方式 優點 缺點
JSON 跨語言、格式清晰一目瞭然
位元組數比較大,需要第三方類庫
Object Serialize java原生方法不依賴外部類庫 位元組數比較大,不能跨語言
Google protobuf 跨語言、位元組數比較少
編寫.proto配置用protoc工具生成對應的代碼
2. 怎麼將java中list中的數據序列化到資料庫中,方便存取
請注意看如下代碼:
public List getObject(String sql, Object[] object) { //sql執行語句,object是你sql語句裡面的參數
List list = new ArrayList();
Connection con = null;
PreparedStatement pre = null;
ResultSet rs = null;
try{
con = C3P0Util.getInstance().getConnection(); //這是你獲得資料庫連接,你把這里改成調用你自己寫的jdbc方法
pre = con.prepareStatement(sql); //執行sql語句
if(object!=null){
for(int i=0;i<object.length;i++){
pre.setObject(i+1, object[i]); //給sql裡面的參數進行賦值
}
}
rs = pre.executeQuery();
while(rs.next()){
Users u = new User();
3. 什麼是java序列化,如何實現java序列化
序 列 化: 指把堆內存中的Java對象數據,通過某種方式把對象存儲到磁碟文件中或者傳遞給其他網路的節點(在網路上傳輸).我們把這個過程稱之為序列化.
反序列化:把磁碟文件中的對象數據或者把網路節點上的對象數據,恢復成Java對象的過程.
為什麼要做序列化:
1):在分布式系統中,需要共享的數據的JavaBean對象,都得做序列化,此時需要把對象再網路上傳輸,此時就得把對象數據轉換為二進制形式.以後存儲在HttpSession中的對象,都應該實現序列化介面(只有實現序列化介面的類,才能做序列化操作).
2):服務鈍化:如果服務發現某些對象好久都沒有活動了,此時伺服器就會把這些內存中的對象,持久化在本地磁碟文件中(Java對象-->二進制文件).如果某些對象需要活動的時候,現在內存中去尋找,找到就使用,找不到再去磁碟文件中,反序列化我們得對象數據,恢復成Java對象.
需要做序列化的對象的類,必須實現序列化介面:java.io.Serializable介面(標志介面[沒有抽象方法]).
底層會判斷,如果當前對象是Serializable的實例,才允許做序列化. boolean ret = Java對象 instanceof Serializable;
在Java中大多數類都已經實現Serializable介面.
4. 什麼是序列化,在java中如何實現序列化
一、什麼是序列化:
序列化理解成「打碎」是可以的,不過在書本上的名詞就是將對象轉換成二進制。
二、在java中如何實現序列化:
首先我們要把准備要序列化類,實現 Serializabel介面
例如:我們要Person類里的name和age都序列化
import java.io.Serializable;
public class Person implements Serializable { //本類可以序列化
private String name ;
private int age ;
public Person(String name,int age){
this.name = name ;
this.age = age ;
}
public String toString(){
return "姓名:" + this.name + ",年齡" + this.age ;
}
}
然後:我們將name和age序列化(也就是把這2個對象轉為二進制,統族理解為「打碎」)
package org.lxh.SerDemo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream ;
public class ObjectOutputStreamDemo { //序列化
public static void main(String[] args) throws Exception {
//序列化後生成指定文件路徑
File file = new File("D:" + File.separator + "person.ser") ; ObjectOutputStream oos = null ;
//裝飾流(流)
oos = new ObjectOutputStream(new FileOutputStream(file)) ;
//實例化類
Person per = new Person("張三",30) ; oos.writeObject(per) ;//把類對象序列化
oos.close() ;
}
}
5. java 中的序列化是什麼意思
1、序列化是干什麼的?
簡單說就是為了保存在內存中的各種對象的狀態,並且可以把保存的對象狀態再讀出來。雖然你可以 用自己的各種方法來保存Object states,
但是Java給你提供一種應該比你自己好的保存對象狀態的機制、那就是序列化。
2、什麼情況下需要序列化?
a)當你想把的內存中的對象保存到一個文件或者資料庫中時候。
b)當你想用套接字在網路上傳送對象的時候
c)當你想通過RMI傳輸對象的時候(RMI->Remote Method Invocation 遠程方法調用)
3、當對一個對象實現序列化時,究竟發生了什麼?
在沒有序列化前,每個保存在堆(Heap)中的對象都有相應的狀態(state),即實體變數(instance ariable)
例如:Foo myFoo=new Foo(); myFoo.setWidth(20); myFoo.setHeight(40);
6. java中的序列化是什麼意思
序列化是將對象狀態轉換為可保持或傳輸的格式的過程。說白點就是你可以用對象輸出流輸出到文件。如果不序列化輸出的話,很可能會亂。
java中的序列化機制能夠將一個實例對象(只序列化對象的屬性值,而不會去序列化什麼所謂的方法。)的狀態信息寫入到一個位元組流中使其可以通過socket進行傳輸、或者持久化到存儲資料庫或文件系統中;然後在需要的時候通過位元組流中的信息來重構一個相同的對象。
一般而言,要使得一個類可以序列化,只需簡單實現java.io.Serializable介面即可。
7. Java中我有一個List的類對象要輸出,類已序列化。打算用ObjetOutputStream,但是怎麼用
java.util
類 ArrayList<E>
java.lang.Object
java.util.AbstractCollection<E>
java.util.AbstractList<E>
java.util.ArrayList<E>
所有已實現的介面:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
直接已知子類:
AttributeList, RoleList, RoleUnresolvedList
確實 這個類是可以序列化的 直接做IO操作啊
8. java list能被序列化嗎
實現了java.io.Serializable介面才能被序列化,而list本身是一個介面,也沒繼承java.io.Serializable,不存在序列化一說,但是它的子類ArrayList是可以被序列化的:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
9. java中ArrayList的序列化問題
ArrayList 中的對象,要實現了java.io.Serializable Interface