




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、序列化是干什么的?简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。2、什么情况下需要序列化a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;b)当你想用套接字在网络上传送对象的时候;c)当你想通过RMI传输对象的时候;3、当对一个对象实现序列化时,究竟发生了什么?在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instanceariable)比如:java 代码Foo? myFoo = new Foo();myFoo .setWidth(37);myFoo.setHeight(70);当通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。java 代码FileOutputStream fs = new FileOutputStream(foo.ser);ObjectOutputStream os = new ObjectOutputStream(fs);os.writeObject(myFoo);4、实现序列化(保存到一个文件)的步骤a、Make a FileOutputStreamjava 代码FileOutputStream fs = new FileOutputStream(“foo.ser”);b、Make a ObjectOutputStreamjava 代码ObjectOutputStream os =? new ObjectOutputStream(fs);c、write the objectjava 代码os.writeObject(myObject1);os.writeObject(myObject2);os.writeObject(myObject3);d、 close the ObjectOutputStreamjava 代码os.close();5、举例说明Java代码packagecom.hotye.dchaoxiong.serializabletest;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;publicclassBox?implementsSerializable? privatestatic finallong serialVersionUID?=?1L; privateintwidth; privateintheight; privateString?name; publicstatic void main(String?args)? try Box?myBox?=?newBox(); myBox.setWidth(50); myBox.setHeight(30); myBox.setName(“雕戈”); FileOutputStream?fs?=?newFileOutputStream(“serializableObject.txt”); ObjectOutputStream?os?=?newObjectOutputStream(fs); os.writeObject(myBox); os.close(); fs.close(); ?catch(Exception?ex)? ex.printStackTrace(); try FileInputStream?fis?=?newFileInputStream(“serializableObject.txt”); ObjectInputStream?ois?=?newObjectInputStream(fis); Box?box?=?(Box)?ois.readObject(); System.out.println(box.getWidth(); System.out.println(box.getHeight(); System.out.println(box.getName(); ois.close(); fis.close(); ?catch(Exception?e)? e.printStackTrace(); publicvoid setHeight(intheight)? this.height?=?height; publicint getHeight()? returnheight; publicvoid setWidth(intwidth)? this.width?=?width; publicint getWidth()? returnwidth; publicvoid setName(String?name)? ?=?name; publicString?getName()? returnname; 6、相关注意事项a)序列化时,只对对象的状态进行保存,而不管对象的方法;b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;d)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输?等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。2.资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分?配,而且,也是没有必要这样实现。详细描述:序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其保存到文件中,管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大,在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题,但却相当重要,具有许多实用意义。一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。从上面的叙述中,我们知道了对象序列化是java编程中的必备武器,那么让我们从基础开始,好好学习一下它的机制和用法。java序列化比较简单,通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复,不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意:不是每个类都可序列化,有些类是不能序列化的,例如涉及线程的类与特定JVM有非常复杂的关系。序列化机制:序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例。ObjectOutputStream中的序列化过程与字节流连接,包括对象类型和版本信息。反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中。下面我们分两大部分来阐述:处理对象流:(序列化过程和反序列化过程)java.io包有两个序列化对象的类。ObjectOutputStream负责将对象写入字节流,ObjectInputStream从字节流重构对象。我们先了解ObjectOutputStream类吧。ObjectOutputStream类扩展DataOutput接口。writeObject()方法是最重要的方法,用于对象序列化。如果对象包含其他对象的引用,则writeObject()方法递归序列化这些对象。每个ObjectOutputStream维护序列化的对象引用表,防止发送同一对象的多个拷贝。(这点很重要)由于writeObject()可以序列化整组交叉引用的对象,因此同一ObjectOutputStream实例可能不小心被请求序列化同一对象。这时,进行反引用序列化,而不是再次写入对象字节流。下面,让我们从例子中来了解ObjectOutputStream这个类吧。/ 序列化 todays date 到一个文件中.FileOutputStream f = newFileOutputStream(“tmp”);?/创建一个包含恢复对象(即对象进行反序列化信息)的”tmp”数据文件ObjectOutputStream s = new ObjectOutputStream(f);s.writeObject(“Today”);?/写入字符串对象;s.writeObject(newDate();?/写入瞬态对象;s.flush();现在,让我们来了解ObjectInputStream这个类。它与ObjectOutputStream相似。它扩展DataInput接口。ObjectInputStream中的方法镜像DataInputStream中读取Java基本数据类型的公开方法。readObject()方法从字节流中反序列化对象。每次调用readObject()方法都返回流中下一个Object。对象字节流并不传输类的字节码,而是包括类名及其签名。readObject()收到对象时,JVM装入头中指定的类。如果找不到这个类,则readObject()抛出ClassNotFoundException,如果需要传输对象数据和字节码,则可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化过程。/从文件中反序列化 string 对象和 date 对象FileInputStream in = new FileInputStream(“tmp”);ObjectInputStream s = new ObjectInputStream(in);String today =(String)s.readObject();?/恢复对象;Date date = (Date)s.readObject();定制序列化过程:序列化通常可以自动完成,但有时可能要对这个过程进行控制。java可以将类声明为serializable,但仍可手工控制声明为static或transient的数据成员。public class simpleSerializableClass implements SerializableString sToday=”Today:”;transient Date dtToday=new Date();序列化时,类的所有数据成员应可序列化除了声明为transient或static的成员。将变量声明为transient告诉JVM我们会负责将变元序列化。将数据成员声明为transient后,序列化过程就无法将其加进对象字节流中,没有从transient数据成员发送的数据。后面数据反序列化时,要重建数据成员(因为它是类定义的一部分),但不包含任何数据,因为这个数据成员不向流中写入任何数据。记住,对象流不序列化static或transient。我们的类要用writeObject()与readObject()方法以处理这些数据成员。使用writeObject()与readObject()方法时,还要注意按写入的顺序读取这些数据成员。/重写writeObject()方法以便处理transient的成员。public void writeObject(ObjectOutputStream outputStream) throwsIOExceptionoutputStream.defaultWriteObject();/使定制的writeObject()方法可以outputStream.writeObject(oSocket.getInetAddress();outputStream.writeInt(oSocket.getPort();/重写readObject()方法以便接收transient的成员。private void readObject(ObjectInputStream inputStream) throwsIOException,ClassNotFoundExceptioninputStream.defaultReadObject();/defaultReadObject()补充自动序列化InetAddress oAddress=(InetAddress)inputStream.readObject();int iPort =inputStream.readInt();oSocket = new Socket(oAddress,iPort);iID=getID();dtToday =new Date();完全定制序列化过程:如果一个类要完全负责自己的序列化,则实现Externalizable接口而不是Serializable接口。Externalizable接口定义包括两个方法writeExternal()与readExternal()。利用这些方法可以控制对象数据成员如何写入字节流.类实现Externalizable时,头写入对象流中,然后类完全负责序列化和恢复数据成员,除了头以外,根本没有自动序列化。这里要注意了。声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。这包括使用安全套接或加密整个字节流。到此为至,我们学习了序列化的基础部分知识。=以下来源于J2EE API:对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形还原为最初写入它们时的形状。例如,要写入可通过 ObjectInputStream 中的示例读取的对象,请执行以下操作:FileOutputStream fos = new FileOutputStream(“t.tmp”);ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeInt(12345);oos.writeObject(“Today”);oos.writeObject(new Date();oos.close();在序列化和反序列化过程中需要特殊处理的类必须实现具有下列准确签名的特殊方法:private void readObject(java.io.ObjectInputStream stream)throws IOException, ClassNotFoundException;private void writeObject(java.io.ObjectOutputStream stream)throws IOExceptionwriteObject 方法负责写入特定类的对象状态,以便相应的 readObject方法可以还原它。该方法本身不必与属于对象的超类或子类的状态有关。状态是通过使用 writeObject 方法或使用DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。序列化操作不写出没有实现 java.io.Serializable 接口的任何对象的字段。不可序列化的 Object的子类可以是可序列化的。在此情况下,不可序列化的类必须有一个无参数构造方法,以便允许初始化其字段。在此情况下,子类负责保存和还原不可序列化的类的状态。经常出现的情况是,该类的字段是可访问的(public、package 或 protected),或者存在可用来还原状态的 get和 set 方法。实现 writeObject 和 readObject 方法可以阻止对象的序列化,这时抛出NotSerializableException。ObjectOutputStream 导致发生异常并中止序列化进程。实 现 Externalizable 接口允许对象假定可以完全控制对象的序列化形式的内容和格式。调用Externalizable 接口的方法(writeExternal 和readExternal)来保存和恢复对象的状态。通过类实现时,它们可以使用 ObjectOutput 和 ObjectInput的所有方法读写它们自己的状态。对象负责处理出现的任何版本控制。Enum 常量的序列化不同于普通的 serializable 或 externalizable 对象。enum常量的序列化形式只包含其名称;常量的字段值不被传送。为了序列化 enum 常量,ObjectOutputStream需要写入由常量的名称方法返回的字符串。与其他 serializable 或 externalizable 对象一样,enum常量可以作为序列化流中后续出现的 back 引用的目标。用于序列化 enum 常量的进程不可定制;在序列化期间,由 enum类型定义的所有类特定的 writeObject 和 writeReplace 方法都将被忽略。类似地,任何serialPersistentFields 或 serialVersionUID 字段声明也将被忽略,所有 enum 类型都有一个0L 的固定的 serialVersionUID。基本数据(不包括 serializable 字段和 externalizable 数据)以块数据记录的形式写入ObjectOutputStream中。块数据记录由头部和数据组成。块数据部分包括标记和跟在部分后面的字节数。连续的基本写入数据被合并在一个块数据记录中。块数据记录的分块因子为1024 字节。每个块数据记录都将填满 1024 字节,或者在终止块数据模式时被写入。调用 ObjectOutputStream 方法writeObject、defaultWriteObject 和 writeFields 最初只是终止所有现有块数据记录。java.io.Serializable引发的问题什么是序列化?在什么情况下将类序列化?序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。序列化:序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。是对象永久化的一种机制。 确切的说应该是对象的序列化,一般程序在运行时,产生对象,这些对象随着程序的停止运行而消失,但如果我们想把某些对象(因为是对象,所以有各自不同的特性)保存下来,在程序终止运行后,这些对象仍然存在,可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存下来的对象。这种情况下就要用到对象的序列化。 只有序列化的对象才可以存储在存储设备上。为了对象的序列化而需要继承的接口也只是一个象征性的接口而已,也就是说继承这个接口说明这个对象可以被序列化了,没有其他的目的。之所以需要对象序列化,是因为有时候对象需要在网络上传输,传输的时候需要这种序列化处理,从服务器硬盘上把序列化的对象取出,然后通过网络传到客户端,再由客户端把序列化的对象读入内存,执行相应的处理。 对象序列化是java的一个特征,通过该特征可以将对象写作一组字节码,当在其他位置读到这些字节码时,可以依此创建一个新的对象,而且新对象的状态与原对象完全相同。为了实现对象序列化,要求必须能够访问类的私有变量,从而保证对象状态能够正确的得以保存和恢复。相应的,对象序列化API能够在对象重建时,将这些值还原给私有的数据成员。这是对java语言访问权限的挑战。通常用在服务器客户端的对象交换上面,另外就是在本机的存储。 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整性和可传递性。譬如通过网络传输,或者把一个对象保存成一个文件的时候,要实现序列化接口 。*Quote:比较java.io.Externalizable和java.io.SerializableURL/developer/code/story/0,3800066897,39304080,00.htm/URL即使你没有用过对象序列化(serialization),你可能也知道它。但你是否知道 Java 还支持另外一种形式的对象持久化,外部化(externalization)?下面是序列化和外部化在代码级的关联方式:public interface Serializable public interface Externalizable extends Serializable void readExternal(ObjectInput in);void writeExternal(ObjectOutput out);序列化和外部化的主要区别外部化和序列化是实现同一目标的两种不同方法。下面让我们分析一下序列化和外部化之间的主要区别。通过Serializable接口对对象序列化的支持是内建于核心 API 的,但是java.io.Externalizable的所有实现者必须提供读取和写出的实现。Java 已经具有了对序列化的内建支持,也就是说只要制作自己的类java.io.Serializable,Java 就会试图存储和重组你的对象。如果使用外部化,你就可以选择完全由自己完成读取和写出的工作,Java 对外部化所提供的唯一支持是接口:voidreadExternal(ObjectInput in)void writeExternal(ObjectOutput out)现在如何实现readExternal() 和writeExternal() 就完全看你自己了。序列化会自动存储必要的信息,用以反序列化被存储的实例,而外部化则只保存被存储的类的标识。当你通过java.io.Serializable接口序列化一个对象时,有关类的信息,比如它的属性和这些属性的类型,都与实例数据一起被存储起来。在选择走Externalizable这条路时,Java 只存储有关每个被存储类型的非常少的信息。每个接口的优点和缺点Serializable接口 优点:内建支持 优点:易于实现 缺点:占用空间过大 缺点:由于额外的开销导致速度变比较慢Externalizable接口 优点:开销较少(程序员决定存储什么) 优点:可能的速度提升 缺点:虚拟机不提供任何帮助,也就是说所有的工作都落到了开发人员的肩上。在两者之间如何选择要根据应用程序的需求来定。Serializable通常是最简单的解决方案,但是它可能会导致出现不可接受的性能问题或空间问题;在出现这些问题的情况下,Externalizable可能是一条可行之路。要记住一点,如果一个类是可外部化的(Externalizable),那么Externalizable方法将被用于序列化类的实例,即使这个类型提供了Serializable方法:private void writeObject()private void readObject()Bean Serializable Interface 的接口让BEAN可以串行化,将其变成一个可保存为以后使用的二进制流。当一个BEAN被系列化到磁盘上或者其他任何地方,其状态被保存起来,其中的属性值也不会改变。在BEAN的规范中,JSP并没有要求BEAN实现Serializable接口。但是,如果您希望自己控制您所创建的组件的serialization进程,或者您想serialize并不是标准组件扩展的组件,您必须了解serialization and deserialization的细节。有几个原因你会把BEAN冷藏起来以备后用。有些服务器通过将所有的SESSION 数据(包括BEAN)写入磁盘来支持任意长的SESSION生命期,即使服务器停机也不会丢失。当服务器重新启动后,串行化的数据被恢复。同样的理由,在重负载的站点上支持服务器分簇的环境中,许多服务器通过串行化来复制SESSION。如果你的BEAN不支持串行化,服务器就不能正确地保存和传输类。通过同样的策略,你可以选择将BEAN保存在磁盘上或者数据库中,以备后用。例如,也许可以将客户的购物车实现为一个BEAN,在访问期间将其保存在数据库中。如果BEAN需要特殊的复杂的初始设置,可以将BEAN设置好后串行化保存在磁盘上。这个BEAN的“快照”可以用在任何需要的地方,包括在$#60;jsp:useBean$#62;中用beanName属性的调用。$#60;jsp:useBean$#62;标签中的beanName属性,用来实例化一个串行化的BEAN,而不是用来从一个类创建一个全新的实例。如果BEAN还没有创建,beanName属性传给java.beans.Bean.instantiate()方法,由类装载器对类进行实例化。它首先假定存在一个串行化的BEAN(带有扩展名.ser),然后会将其激活。如果这个操作失败,它就会实例化一个新的实例。下面简单介绍一下这个接口:对象能包含其它的对象,而这其它的对象又可以包含另外的对象。JAVA serialization能够自动的处理嵌套的对象。对于一个对象的简单的域,writeObject()直接将值写入流。而,当遇到一个对象域时,writeObject()被再次调用,如果这个对象内嵌另一个对象,那么,writeObject() 又被调用,直到对象能被直接写入流为止。程序员所需要做的是将对象传入ObjectOutputStream 的writeObject() 方法,剩下的将又系统自动完成。下面的例子创建了一个调用mine对象的PersonalData对象。代码实现的是将一个串和mine 对象输出到一个流,并存入一个文件:public class PersonalData implements Serializable public int idpublic int yearOfBirth;public float yearlySalary;PersonalData mine = new PersonalData(101, 1956, 46500.00);FileOutputStream outstream = new FileOutputStream(PersonalData.ser);ObjectOutputStream out = new ObjectOutputStream(outstream);out.writeObject(My personal data); /将一个串写入流out.writeObject(mine); /将这个对象写入流out.close(); / 清空并关闭流.一个FileOutputStream对象被创建且传到一个ObjectOutputStream。当out.writeObject() 被调用,这个串和mine 对象被objects are serializ顺序加入一个存入文件PersonalData.ser的字节对列。您应该注意上述类是实现的java.io.Serializable接口。因为它并未指定要实现的方法,所以Serializable被称为tagging interface ,但是它仅仅tags它自己的对象是一个特殊的类型。任一个您希望serialize的对象都应该实现这个接口。这是必须的。否则,用到流技术时将根本不工作。例如,如果您试着去serialize 一个没有实现这个接口的对象,一个 NotSerializableException将产生。类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。Java的对象序列化能让你将一个实现了Serializable接口的对象转换成一组byte,这样日后要用这个对象时候,你就能把这些byte数据恢复出来,并据此重新构建那个对象了。要想序列化对象,你必须先创建一个OutputStream,然后把它嵌进ObjectOutputStream。这时,你就能用writeObject( )方法把对象写入OutputStream了。writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它。通过调用 out.defaultWriteObject 可以调用保存 Object 的字段的默认机制。该方法本身不需要涉及属于其超类或子类的状态。状态是通过使用 writeObject 方法或使用 DataOutput 支持的用于基本数据类型的方法将各个字段写入 ObjectOutputStream 来保存的。读的时候,你得把InputStream嵌到ObjectInputStream里面,然后再调用readObject( )方法。不过这样读出来的,只是一个Object的reference,因此在用之前,还得先下
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 元旦营销策划方案(3篇)
- 高端人群保险营销方案(3篇)
- 内网安全培训课件
- 化学品上岗安全培训课件
- 5G无线资源动态分配-洞察及研究
- 创伤的救治流程
- 化学仪器室安全守则培训课件
- 七年级上册(2024) 第二单元 写作 学会记事 公开课一等奖创新教案+(共18张)
- 2025年秋部编版语文四上 语文园地七(公开课一等奖创新教案+)
- 12 在牛肚子里旅行(+公开课一等奖创新教案+备课素材)
- 4.2《遵守规则》教学设计 -2025-2026学年八年级道德与法治上册
- 人工智能+高质量发展文化旅游产业智能化升级研究报告
- 2025年自考专业(计算机网络)考试综合练习附参考答案详解(A卷)
- 冷链技术对水果品质保持的数值预测模型研究
- 集输工应急处置考核试卷及答案
- 2025年全国保密教育线上培训考试试题库附完整答案(必刷)
- 珠江医院护理面试题库及答案
- 流程管理某省市场营销MPR+LTC流程规划方案
- 化工厂实习安全培训课件
- 疏浚清淤工程合同协议书
- 电子电子技术试题及答案
评论
0/150
提交评论