java获取文件编码-java如何获取xml文件
二
序列化和反序列化
序列化:
对象序列化的主要目的是在传递和保存对象时保证对象的完整性和可传递性。 序列化是将对象转换为有序的字节流,以便通过网络传输或存储在本地文件中。 序列化后的字节流保存了Java对象的状态和相关的描述信息。 序列化机制的核心功能是对象状态的保存和重构。
反序列化:
客户端从文件或网络中获取序列化后的对象字节流后,根据字节流中存储的对象状态和描述信息,通过反序列化重构对象。
序列化是将实体对象的状态按照一定格式写入有序字节流中,反序列化是从有序字节流中重建对象,恢复对象状态。 简单来说,进程间通信可以以二进制的形式传输图片、视频、音频等信息。 但是进程之间的对象不能这样做。
例如,我创建了一个 User u1 = new User(1,"a",100);
我要把它传递给另一个软件(进程),
进程之间的对象如果要传递就需要序列化和反序列化。
序列化为二进制数据,可以永久保存在硬盘中,也可以通过网络传输。
三
实现java序列化和反序列化
如果下面太长,可以直接看例子。
JDK类库中的序列化和反序列化API
java.io.ObjectOutputStream:
代表一个对象输出流;
它的writeObject(Object obj)方法可以序列化参数指定的obj对象,并将得到的字节序列写入一个目标输出流;
java.io.ObjectInputStream:
表示对象输入流; 它的 readObject() 方法从源输入流中读取字节序列,将它们反序列化为一个对象,并返回它;
实现序列化要求
只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则会抛出异常!
一种实现Java对象序列化和反序列化的方法
如果User类只实现了Serializable接口,可以通过以下方式进行序列化和反序列化:
ObjectOutputStream 使用默认的序列化方法序列化 User 对象的非瞬态实例变量。
ObjcetInputStream 使用默认的反序列化方法反序列化User 对象的非瞬态实例变量。
如果User类只实现了Serializable接口,同时定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),序列化和反序列化方式如下:
ObjectOutputStream调用User对象的writeObject(ObjectOutputStream out)方法进行序列化。
ObjectInputStream会调用User对象的readObject(ObjectInputStream in)方法进行反序列化。
如果User类实现了Externalizable接口,并且User类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,序列化和反序列化方式如下: ObjectOutputStream调用User对象方法的writeExternal(ObjectOutput out)用于序列化。 ObjectInputStream会调用User对象的readExternal(ObjectInput in)方法进行反序列化。
例子
user对象使用了上面的第一种方法,所以User必须实现Serializable。
import java.io.Serializable; public class User implements Serializable { int id; String name; String phone; #一些get set 构造参数,这里就不列举了}
序列化和反序列化
import java.io.*; public class userDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { //创建对象 User u1 = new User(1,"AAAAAAA","110"); //被序列化的对象 User u2; //反序列化的对象 //序列化 getSerial(u1); //反序列化 u2 = backSerial(); System.out.println(u2.getName()); } //序列化 static void getSerial(User u1) throws IOException { FileOutputStream fos = new FileOutputStream("obj.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(u1); oos.flush(); oos.close(); } //反序列化 static User backSerial() throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream("obj.out"); ObjectInputStream ois = new ObjectInputStream(fis); User u1 = (User) ois.readObject(); return u1; }}
四
序列化底层分析
ObjdectOutputStream 对象的初始化
bout是数据输出流的底层
writeStreamHeader 将文件头写入文件
根据这里的序列化文件分析
所以这里是写入文件头,说明使用了序列化协议,序列化版本初始化完成,文件存在,写入文件头。
开始序列化写入文件
写对象(u1);
向下调用write0ject0(); 这个方法的内容比较长。 重点是根据不同类型的方法写入序列化数据。 您可以在上面看到用于序列化和反序列化 Java 对象的方法。
我们的例子中实现了Serializable,所以执行了writeOrdinaryObject方法。
bout.writeByte(TC_OBJECT);
0x73 被写入
调用 writeClassDesc(desc, false); 跟进:
这里isProxy是判断类是否为动态代理模式。 具体的你可以自己去了解,我也不知道。 因为我们实例的类不是动态代理,所以跟进writeNonProxyDesc();
先写描述符0x72
接下来判断跟进两个参数,一个是1java获取文件编码,一个是2。
执行与true相同的方法:
在开发中,我们经常会遇到for循环判断循环体中是否包含某个元素。 这时候我们也经常会使用一个布尔值来介入判断。 而“|=”可以很容易的让我们完成执行。
布尔标志=假; 在循环体中,标志 |= (c==e); 如果不相等,则标志始终为假,一旦相等,则为真;
out.writeUTF(名字);
写类名
out.writeLong(getSerialVersionUID());
写入序列化后的uid然后去下一堆if判断接口的实现,写入标志位
out.writeByte(标志);
我们使用serializable,所以应该写0x02
所以从 0x000B - 0x0013 都是序列化的 uid
然后调用writeShort写入两个字节的字段长度(比如有3个变量就写入00 03)。
实例中有三个参数
下一步是在循环中写入变量名和变量类型。
每个周期:
writeByte写入一个byte变量类型,writeUTF()写入变量名判断是否为原始类型,即是否为对象。 如果不是原始类型(基本类型),则调用 writeTypeString()。
这个writeTypeString(),如果是字符串,就会调用writeString()。 而这个writeString()往往是这样写的,如果字符串长度(不是size)小于两个字节,先写一个字节的TC_STRING(十六进制74),然后调用writeUTF()写签名,貌似与jvm有关。
最后一般写成这样 74 00 12 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b "translated" 字符串类型,占18字节长度,变量名是Ljava/lang/string;
红色id参数int类型
绿名参数string 因为String是引用数据类型,调用writeTypeString()写Ljava/lang/string;
黄色电话参数字符串
这里第一次出题,phone参数也是字符串,但是他没有Ljava/lang/string; 这个字符串,后面跟一个字符串参数,保证同一个引用数据类型只写一次。
循环执行完毕,返回writeNonProxyDesc方法,写入结束标志位0x78
bout.writeByte(TC_ENDBLOCKDATA);
准备开始写入序列化数据,返回writeOrdinaryObject()方法,writeSerialData(obj, desc); 写入序列化数据的方法
这里根据使用方法判断,所以调用了defaultWriteFields();
第二个if是判断是否是基本数据类型,如果是就直接写入序列化后的数据,如果不是就往下到for循环附近。获取变量个数,然后调用writeObject0() 循环; 写
循环结束,直到所有运行完成,返回到 main 函数。
反序列化就不写了,反着来做。
五
Java反射机制
反射机制允许程序在运行时通过Reflection API获取任何类的内部信息,可以直接操作任何类和对象的所有属性和方法。 要使用一个类,必须先将它加载到虚拟机中。 类加载完成后java获取文件编码,会在堆内存的方法区生成一个Class类型的对象(一个类只有一个类对象),而这个对象包含了完整的类结构信息,我们可以通过这个看到类结构object,这个对象就像一面镜子,通过镜子我们可以看到类的结构,所以这个形象叫做:反射。
例子:
import java.lang.reflect.Method; public class test { public static void main(String[] args) throws Exception { a1Class a1 = new a1Class(); //通过运行时的对象调用getClass(); Class c = a1.getClass(); try { //getMethod(方法名,参数类型) //getMethod第一个参数是方法名,第二个参数是该方法的参数类型 //因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法 Method m1 = c.getMethod("print", int.class, int.class); //相当于r1.print(1, 2);方法的反射操作是用m1对象来进行方法调用 和r1.print调用的效果完全相同 //使用r1调用m1获得的对象所声明的公开方法即print,并将int类型的1,2作为参数传入 Object i = m1.invoke(a1,1,1); }catch (Exception e){ e.printStackTrace(); } } static class a1Class { public void print(int a, int b) { System.out.println(a + b); } }}
尝试简化上面的代码
创建另一个文件
public class testMiao { public static void maio(){ System.out.println("miao!"); }}
使用反射执行miao();
public class test { public static void main(String[] args) throws Exception { try { Object s = Class.forName("testMiao").getMethod("maio").invoke(null); }catch (Exception e){ e.printStackTrace(); } }}
尝试添加参数简化
public class testMiao { public static void maio(String s){ System.out.println("miao!"+s); }}
反射
public class test { public static void main(String[] args) throws Exception { try { Class.forName("testMiao").getMethod("maio", String.class).invoke(Class.forName("testMiao"),"aaa"); }catch (Exception e){ e.printStackTrace(); } }}
六
java执行命令
java中可以使用runtime.getRuntime.exec()来执行系统命令
喜欢:
尝试使用反射来执行
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke("open /System/Applications/Calculator.app\n");
这样会报错,错误信息:对象不是声明类的实例
说明exec只能通过getRuntime执行
import java.lang.reflect.Method; public class test { public static void main(String[] args) throws Exception { Object o = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null); Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(o,"open /System/Applications/Calculator.app\n"); }}
这样就成功了,原理按照反射实例的第一个实例来理解。
现在你可以打开计算器,了解什么是序列化和反序列化。 关于cc1链,以后再写。 可以看看bilibili白日梦组长的思路分析。 我个人觉得他的想法真的很棒。
看雪ID:p1yang
*本文由p1yang原创于看雪论坛,转载请注明来自看雪社区
#之前的推荐
1.
2.
3.
4.
5.
6.