搜索词>>Java序列化 耗时0.0630
  • Java序列化和反序列化

    ​Java序列化的作用Java序列化允许将Java对象写入文件系统以进行永久存储,也可以将其写入网络以传输到其他应用程序​Java序列化的作用Java序列化允许将Java对象写入文件系统以进行永久存储,也可以将其写入网络以传输到其他应用程序。JAVA实现序列化Java中的序列化是通过Serializable接口实现的。 Java Serializable接口保证了序列化对象的能力。 此接口也建议我们使用serialVersioUID。Java序列化中的疑惑现在,即使您在应用程序类中同时使用了序列化和反序列化,您是否知道也有可能已经破坏您的设计呢? 下面让我们确定类中将来的更改,这些更改将是兼容的更改,而其他更改将证明是不兼容的更改。Java序列化不兼容的更改对类的不兼容更改是指不能保持互操作性的那些更改。 下面给出了在演化类时可能发生的不兼容更改(考虑默认的序列化或反序列化):删除字段--->如果在类中删除了某个字段,则写入的流将不包含其值。 当较早的类读取流时,该字段的值将设置为默认值,因为流中没有可用的值。 但是,此默认值可能会不利地损害早期版本兼容性的能力。将类上移或下移---->这是不允许的,因为流中的数据以错误的顺序显示。将非静态字段更改为静态或将非持久化态字段更改为持久化态--->当依赖默认序列化时,此更改等效于从类中删除字段。 该版本的类不会将该数据写入流,因此该类的早期版本将无法读取该数据。 与删除字段时一样,早期版本的字段将被初始化为默认值,这可能导致类以意外方式失败。更改原始字段的声明类型--->每个版本的类都使用其声明类型写入数据。 尝试读取该字段的早期版本的类将失败,因为流中的数据类型与该字段的类型不匹配。更改writeObject或readObject方法,使其不再写入或读取默认字段数据,或者对其进行更改,以使其尝试写入或读取默认字段数据,而先前版本则不这样做。 默认字段数据必须一致地出现在流中或不出现在流中。将类从“可序列化”更改为“可外部化”,反之亦然,这是不兼容的更改,因为流将包含与可用类的实现不兼容的数据。将类从非枚举类型更改为枚举类型,反之亦然,因为流将包含与可用类的实现不兼容的数据。删除Serializable或Externalizable是一项不兼容的更改,因为在编写时它将不再提供该类的旧版本所需的字段。如果该行为会产生与该类的任何旧版本不兼容的对象,则将writeReplace或readResolve方法添加到类是不兼容的。Java序列化兼容更改添加字段–当要重构的类的字段在流中不存在时,该对象中的该字段将被初始化为其类型的默认值。 如果需要特定于类的初始化,则该类可以提供一个readObject方法,该方法可以将字段初始化为非默认值。添加类–流将包含流中每个对象的类型层次结构。 将流中的此层次结构与当前类进行比较可以检测到其他类。 由于流中没有用于初始化对象的信息,因此该类的字段将被初始化为默认值。删除类–将流中的类层次结构与当前类的层次结构进行比较可以检测到某个类已被删除。 在这种情况下,从该流中读取与该类相对应的字段和对象。 原始字段将被丢弃,但是会创建由删除的类引用的对象,因为它们可能稍后在流中被引用。 当流被垃圾回收或重置时,它们将被垃圾回收添加writeObject / readObject方法–如果读取流的版本具有这些方法,则通常希望readObject读取默认序列化写入流中的所需数据。 在读取任何可选数据之前,应先调用defaultReadObject。 通常,writeObject方法将调用defaultWriteObject写入所需的数据,然后再写入可选数据。删除writeObject / readObject方法–如果读取流的类没有这些方法,则默认情况下将序列化读取所需的数据,并将丢弃可选数据。添加java.io.Serializable –这等效于添加类型。 该类的流中将没有任何值,因此其字段将被初始化为默认值。 对子类化不可序列化类的支持要求该类的超级类型具有no-arg构造函数,并且该类本身将被初始化为默认值。 如果no-arg构造函数不可用,则抛出InvalidClassException。更改对字段的访问权限–访问修饰符public,package,protected和private对序列化为字段分配值的能力没有影响。将字段从静态更改为非静态或将瞬态更改为非瞬态–当依靠默认序列化来计算可序列化字段时,此更改等效于将字段添加到类中。 新字段将被写入流,但是较早的类将忽略该值,因为序列化不会将值分配给静态或瞬态字段。Java序列化中的serialVersionUIDserialVersionUID是Serializable类的通用版本标识符。 反序列化使用此数字来确保已加载的类与序列化的对象完全对应。 如果找不到匹配项,则抛出InvalidClassException。始终将其包含为字段,例如:“ private static final long serialVersionUID = 7526472295622776147L; ”,即使在课程的第一个版本中也要包含此字段,以提醒其重要性。除非您有意对类进行更改以使其与旧的序列化对象不兼容,否则请勿在以后的版本中更改此字段的值。 如果需要,请遵循上述给定的准则。readObject和writeObject方法介绍反序列化必须视为任何构造函数:在反序列化结束时验证对象状态-这意味着readObject几乎应始终在Serializable类中实现,以便执行此验证。如果构造函数为可变对象字段制作防御性副本,则必须为readObject。一些序列化最佳实践使用javadoc的@serial标记表示可序列化字段。.ser扩展名通常用于表示序列化对象的文件。没有静态或瞬态字段接受默认序列化。除非必要,否则可扩展类不应是可序列化的。内部类很少(如果有的话)实现Serializable。容器类通常应遵循Hashtable的样式,该样式通过存储键和值来实现Serializable,而不是大型哈希表数据结构。遵循序列化最佳做法的演示package staticTest; import java.io.Serializable; import java.text.StringCharacterIterator; import java.util.*; import java.io.*; public final class UserDetails implements Serializable { /** * 此构造函数需要所有字段 * * @param aFirstName * contains only letters, spaces, and apostrophes. * @param aLastName * contains only letters, spaces, and apostrophes. * @param aAccountNumber * is non-negative. * @param aDateOpened * has a non-negative number of milliseconds. */ public UserDetails(String aFirstName, String aLastName, int aAccountNumber, Date aDateOpened) { super(); setFirstName(aFirstName); setLastName(aLastName); setAccountNumber(aAccountNumber); setDateOpened(aDateOpened); // there is no need here to call verifyUserDetails. } // 默认构造函数 public UserDetails() { this("FirstName", "LastName", 0, new Date(System.currentTimeMillis())); } public final String getFirstName() { return fFirstName; } public final String getLastName() { return fLastName; } public final int getAccountNumber() { return fAccountNumber; } /** * 返回该字段的防御性副本,因此没有人可以更改此字段。 * */ public final Date getDateOpened() { return new Date(fDateOpened.getTime()); } /** * Names must contain only letters, spaces, and apostrophes. Validate before * setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setFirstName(String aNewFirstName) { verifyNameProperty(aNewFirstName); fFirstName = aNewFirstName; } /** * Names must contain only letters, spaces, and apostrophes. Validate before * setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setLastName(String aNewLastName) { verifyNameProperty(aNewLastName); fLastName = aNewLastName; } /** * Validate before setting field to new value. * * @throws IllegalArgumentException * if the new value is not acceptable. */ public final void setAccountNumber(int aNewAccountNumber) { validateAccountNumber(aNewAccountNumber); fAccountNumber = aNewAccountNumber; } public final void setDateOpened(Date aNewDate) { // make a defensive copy of the mutable date object Date newDate = new Date(aNewDate.getTime()); validateAccountOpenDate(newDate); fDateOpened = newDate; } /** * The client's first name. * * @serial */ private String fFirstName; /** * The client's last name. * * @serial */ private String fLastName; /** * The client's account number. * * @serial */ private int fAccountNumber; /** * The date the account was opened. * * @serial */ private Date fDateOpened; /** * Determines if a de-serialized file is compatible with this class. * Included here as a reminder of its importance. */ private static final long serialVersionUID = 7526471155622776147L; /** * Verify that all fields of this object take permissible values * * @throws IllegalArgumentException * if any field takes an unpermitted value. */ private void verifyUserDetails() { validateAccountNumber(fAccountNumber); verifyNameProperty(fFirstName); verifyNameProperty(fLastName); validateAccountOpenDate(fDateOpened); } /** * Ensure names contain only letters, spaces, and apostrophes. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void verifyNameProperty(String aName) { boolean nameHasContent = (aName != null) && (!aName.equals("")); if (!nameHasContent) { throw new IllegalArgumentException( "Names must be non-null and non-empty."); } StringCharacterIterator iterator = new StringCharacterIterator(aName); char character = iterator.current(); while (character != StringCharacterIterator.DONE) { boolean isValidChar = (Character.isLetter(character) || Character.isSpaceChar(character) || character == '''); if (isValidChar) { // do nothing } else { String message = "Names can contain only letters, spaces, and apostrophes."; throw new IllegalArgumentException(message); } character = iterator.next(); } } /** * AccountNumber must be non-negative. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void validateAccountNumber(int aAccountNumber) { if (aAccountNumber < 0) { String message = "Account Number must be greater than or equal to 0."; throw new IllegalArgumentException(message); } } /** * DateOpened must be after 1970. * * @throws IllegalArgumentException * if field takes an unpermitted value. */ private void validateAccountOpenDate(Date aDateOpened) { if (aDateOpened.getTime() < 0) { throw new IllegalArgumentException( "Date Opened must be after 1970."); } } /** * Always treat deserialization as a full-blown constructor, by validating * the final state of the de-serialized object. */ private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { // always perform the default deserialization first aInputStream.defaultReadObject(); // make defensive copy of the mutable Date field fDateOpened = new Date(fDateOpened.getTime()); // ensure that object state has not been corrupted or tampered with // malicious code verifyUserDetails(); } /** * This is the default implementation of writeObject. Customise if * necessary. */ private void writeObject(ObjectOutputStream aOutputStream) throws IOException { // perform the default serialization for all non-transient, non-static // fields aOutputStream.defaultWriteObject(); } } 现在让我们看看如何在Java中进行序列化和反序列化。Java序列化和反序列化示例package serializationTest; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Calendar; import java.util.Date; public class TestUserDetails { public static void main(String[] args) { // Create new UserDetails object UserDetails myDetails = new UserDetails("Lokesh", "Gupta", 102825, new Date(Calendar.getInstance().getTimeInMillis())); // Serialization code try { FileOutputStream fileOut = new FileOutputStream("userDetails.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(myDetails); out.close(); fileOut.close(); } catch (IOException i) { i.printStackTrace(); } // deserialization code @SuppressWarnings("unused") UserDetails deserializedUserDetails = null; try { FileInputStream fileIn = new FileInputStream("userDetails.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); deserializedUserDetails = (UserDetails) in.readObject(); in.close(); fileIn.close(); // verify the object state System.out.println(deserializedUserDetails.getFirstName()); System.out.println(deserializedUserDetails.getLastName()); System.out.println(deserializedUserDetails.getAccountNumber()); System.out.println(deserializedUserDetails.getDateOpened()); } catch (IOException ioe) { ioe.printStackTrace(); } catch (ClassNotFoundException cnfe) { cnfe.printStackTrace(); } } } 执行示例,得到结果如下:Lokesh Gupta 102825 Wed Nov 21 15:06:34 GMT+05:30 2012
  • java编程中使用XStream实现java bean与XML的序列化和反序列化(一)

    java编程中使用XStream实现java bean与XML的序列化和反序列化(一),本文主要讲解XStream的入门使用。编写一个简单的java bean通过XStream序列化为XML,将XML的字符串转反序列化成java bean。<h2>引言</h2>    java编程中使用XStream实现java bean与XML的序列化和反序列化(一),本文主要讲解XStream的入门使用。编写一个简单的java bean通过XStream序列化为XML,将XML的字符串转反序列化成java bean。 <h2>一.环境准备</h2> <ul> <li>JDK1.6+(我这里使用的1.8)</li> <li>eclipse(或者你自己喜欢的IDE)</li> <li>maven 3+</li> </ul> <h2>二.编码使用XStream</h2> <h3>2.1创建一个普通的maven项目</h3> <img alt="xstream java项目结构图" class="img-thumbnail" src="/assist/images/blog/3d319f8ecf1345068963e271518ebd4e.png" /> <h3>2.2编写两个简单的java POJO对象</h3> <strong>PhoneNumber.java</strong> <pre> <code class="language-java">package net.xqlee.project.demo.pojo; public class PhoneNumber { public PhoneNumber(int code, String number) { super(); this.code = code; this.number = number; } private int code; private String number; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } } </code></pre> <br /> <strong>Person.java</strong> <pre> <code class="language-java">package net.xqlee.project.demo.pojo; public class Person { public Person() { } public Person(String firstname, String lastname) { super(); this.firstname = firstname; this.lastname = lastname; } private String firstname; private String lastname; private PhoneNumber phone; private PhoneNumber fax; public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } public PhoneNumber getPhone() { return phone; } public void setPhone(PhoneNumber phone) { this.phone = phone; } public PhoneNumber getFax() { return fax; } public void setFax(PhoneNumber fax) { this.fax = fax; } } </code></pre> <h3>2.3编写一个测试类来通过XStream序列化java bean和XML反序列化</h3> <strong>XTest.java</strong> <pre> <code class="language-java">package net.xqlee.project.demo.xstream; import com.thoughtworks.xstream.XStream; import net.xqlee.project.demo.pojo.Person; import net.xqlee.project.demo.pojo.PhoneNumber; public class XTest { public static void main(String[] args) { try { // ============初始化================== // 这种初始化方法需要依赖XPP3,XPP3是一个转换XML的实现 XStream xstream = new XStream(); // 不需要XPP3依赖,使用DomDriver替换XPP3 // XStream xstream = new XStream(new DomDriver()); // 不需要XPP3依赖, JDK1.6以上 // XStream xstream = new XStream(new StaxDriver()); // =============对象序列化成XML============ // 创建测试数据 Person person = new Person("左", "搜"); person.setFax(new PhoneNumber(023, "45215778")); person.setPhone(new PhoneNumber(86, "1355469901")); String xml = xstream.toXML(person); System.out.println(xml); System.out.println("--------------华丽的分割线--------------"); // 设置别名 xstream.alias("person", Person.class); xstream.alias("phonenumber", PhoneNumber.class); xml = xstream.toXML(person); System.out.println(xml); // =============XML解码成java对象============ Person person2 = (Person) xstream.fromXML(xml); System.out.println("name:" + person2.getFirstname() + "," + person2.getLastname()); } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <h2>三.演示</h2> 该例子是一个简单的java application,直接eclipse右键运行java application即可,观察控制台输出:<br /> <img alt="xstream运行结果" class="img-thumbnail" src="/assist/images/blog/d1f1de4f2621489581a3987c810ccc2e.png" /><br /> 可以看到已经通过xstream将java bean转换为XML,并且通过别名将默认的XML标签改成简单的类名。<br /> <br /> 一个简单的XStream使用教程描述玩啦。
  • GSON 序列化与反序列化使用说明

    前言学习使用Google GSON库将Java对象序列化为JSON表示形式,并将JSON字符串反序列化为等效的Java对象前言学习使用Google GSON库将Java对象序列化为JSON表示形式,并将JSON字符串反序列化为等效的Java对象。GSON提供简单的toJson()和fromJson()方法将Java对象转换为JSON,反之亦然。​GSON使用GsonBuilder创建Gson具有自定义配置的对象,例如漂亮的打印。//Gson gson = new Gson(); Gson gson = new GsonBuilder().setPrettyPrinting().create(); Employee emp = new Employee(1001, "Lokesh", "Gupta", "howtodoinjava@gmail.com"); String jsonString = gson.toJson(emp); Employee empObject = gson.fromJson(jsonString, Employee.class);1.依赖性Maven依赖。访问maven存储库以获取最新版本。$title(pom.xml) <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.5</version> </dependency>Gradle依赖。<dependency> dependencies { implementation 'com.google.code.gson:gson:2.8.5' }2.序列化 - 使用Gson编写JSONGson上下文中的序列化意味着将Java对象转换为其JSON表示。为了进行序列化,我们需要一个处理转换的Gson对象。接下来,我们需要调用函数toJson()并传递该Employee对象。Employee emp = new Employee(1001, "Lokesh", "Gupta", "howtodoinjava@gmail.com"); Gson gson = new Gson(); String jsonString = gson.toJson(emp); System.out.println(jsonString);程序输出:{ "id":1001, "firstName":"Lokesh", "lastName":"Gupta", "email":"howtodoinjava@gmail.com" }2.反序列化 - 使用Gson读取JSONGson上下文中的反序列化意味着将JSON字符串转换为等效的Java对象。为了进行反序列化,我们需要一个Gson对象并从json()调用函数并在解析完成后传递两个参数,即JSON字符串和期望的java类型。String jsonString = "{'id':1001, 'firstName':'Lokesh', 'lastName':'Gupta', 'email':'howtodoinjava@gmail.com'}"; Gson gson = new Gson(); Employee empObject = gson.fromJson(jsonString, Employee.class); System.out.println(empObject);程序输出:Employee [id=1001, firstName=Lokesh, lastName=Gupta, email=howtodoinjava@gmail.com]代码段 小部件
  • Java 9 模块化编程

    Java 9 模块化编程Java9 模块化编程
  • Gson @SerializedName注解使用说明

    前言在这个Gson @SerializedName示例中,学习在序列化和反序列化过程中更改json和java对象之间的字段名称前言在这个Gson @SerializedName示例中,学习在序列化和反序列化过程中更改json和java对象之间的字段名称。​1. @SerializedName 默认情况下,我们假设Java模型类和JSON将完全相同的字段名称。但有时情况并非如此,某些名称也有所不同。现在我们必须将someNamejson someOtherName中的映射映射到Java类中。这是@SerializedName注释有用的地方。@SerializedName注释指示应将带注释的成员序列化为JSON,并将提供的名称值作为其字段名称。此批注将覆盖FieldNamingPolicy可能已使用GsonBuilder该类的任何内容,包括默认字段命名策略。请注意,您在此批注中指定的值必须是有效的JSON字段名称。1.1。注释属性 它接受两个属性:value - 序列化或反序列化时字段的所需名称。alternate - 反序列化时字段的替代名称。除了'value'属性之外,它还提供了更多可能的名称。如果有多个字段与一个属性匹配,Gson将使用最后处理的字段。请记住,alternate具有多个名称的选项仅限于反序列化。在序列化中,它不会产生任何影响。2.在序列化期间更改字段名称 让我们举一个Employee只有4个字段的类的例子。我们想创建JSON,其中"email"写为字段名称"emailId"。$title(Employee.java) public class Employee { private Integer id; private String firstName; private String lastName; @SerializedName(value = "emailId", alternate = "emailAddress") private String email; } 让我们序列化一个员工记录并查看JSON输出。$title(Main.java) Employee emp = new Employee(1001, "Lokesh", "Gupta", "howtodoinjava@gmail.com"); Gson gson = new GsonBuilder().setPrettyPrinting().create(); System.out.println(gson.toJson(emp)); 执行输出:{ "id": 1001, "firstName": "Lokesh", "lastName": "Gupta", "emailId": "howtodoinjava@gmail.com" }3.在反序列化期间更改字段名称 在将JSON反序列化到Java类期间映射不同字段名称的Java程序。{ "id": 1001, "firstName": "Lokesh", "lastName": "Gupta", "email": "howtodoinjava@gmail.com", "emailAddress": "admin@gmail.com" }$title(Main.java) String json = "{'id': 1001," + "'firstName': 'Lokesh'," + "'lastName': 'Gupta'," + "'email': 'howtodoinjava@gmail.com'," + "'emailAddress': 'admin@gmail.com'}"; Gson gson = new GsonBuilder().setPrettyPrinting().create(); Employee emp = gson.fromJson(json, Employee.class); System.out.println(emp); 执行输出:Employee [id=1001, firstName=Lokesh, lastName=Gupta, email=admin@gmail.com] 注意程序输出。我们有两个匹配的电子邮件领域即email和emailAddress。最后一次出现是for "emailAddress",所以它的值被填充到Employee对象中。GSON 图像 小部件
  • Gson 序列化NULL值

    前言在Gson中实现的默认行为是忽略空对象字段前言在Gson中实现的默认行为是忽略空对象字段。例如,如果在Employee对象中,我们不指定电子邮件(即电子邮件是null),那么电子邮件将不是序列化JSON输出的一部分。Gson忽略空字段,因为此行为允许更紧凑的JSON输出格式。​1.如何在序列化期间允许空值 要配置GSON实例来输出null,我们必须使用serializeNulls()的GsonBuilder对象。Gson gson = new GsonBuilder() .serializeNulls() .create();2.演示 让我们看一下Gson的行为,同时启用或禁用空字段序列化。这是员工类,有四个字段。我们将电子邮件字段设置为'null'。$title(Employee.java) public class Employee { private Integer id; private String firstName; private String lastName; private String email; }2.1。不要序列化空字段 JSON输出中没有空值的默认Gson序列化。$title(Main.java) Employee employeeObj = new Employee(1, "Lokesh", "Gupta", null); Gson gson = new GsonBuilder() .setPrettyPrinting() .create(); System.out.println(gson.toJson(employeeObj)); 运行输出结果为:{ "id": 1, "firstName": "Lokesh", "lastName": "Gupta" }2.2。序列化空字段 自定义Gson序列化,JSON输出中包含空值。$title(Main.java) Employee employeeObj = new Employee(1, "Lokesh", "Gupta", null); Gson gson = new GsonBuilder() .setPrettyPrinting() .serializeNulls() .create(); System.out.println(gson.toJson(employeeObj)); 执行输出结果:{ "id": 1, "firstName": "Lokesh", "lastName": "Gupta", "emailId": null } 显然,null字段在JSON输出中被序列化。
  • Java编程try-with-resources嵌套资实例化源无法关闭原因

    try-with-resources是Java 7 引入的一个新功能,自动资源管理。try-with-resources语句的吸引力在于其“确保在语句结束时关闭每个资源“的保证。此上下文中的“资源”是实现AutoCloseable及其close()方法的任何类。在try-with-resources语句的“try”子句中实例化。Java编程try-with-resources嵌套资实例化源无法关闭try-with-resources是Java 7 引入的一个新功能,自动资源管理。try-with-resources语句的吸引力在于其“确保在语句结束时关闭每个资源“的保证。此上下文中的“资源”是实现AutoCloseable及其close()方法的任何类。在try-with-resources语句的“try”子句中实例化。在Java语言规范 [JLS]介绍了尝试,与资源声明中详细第14.20.3(的Java SE的10个JLS在这种情况下)。JLS声明“ try-with-resources语句使用局部变量(称为资源)参数化,这些局部变量在执行try块之前初始化,并在执行块之后自动关闭,并按照与它们初始化相反的顺序try。JLS明确规定可以定义与单个try-with-resources语句相关的多个资源,并指定如何指定多个资源。具体而言,它表示try可以跟随“ ResourceSpecification ”,其由“ ResourceList ”组成,该“ ResourceList ”由一个或多个“ Resource ”组成。当存在多个声明的资源时,多个资源由semicolon(;)分隔。以分号分隔的列表中的多个资源的这种规范很重要,因为try-with-resources语句不支持以这种方式声明的任何候选资源(不会自动关闭)。在try-with-resources语句中指定多个资源时最可能的错误来源是“嵌套”“资源”的实例化,而不是在每个实例化之间用分号分别显式地实例化每个资源的局部变量。以下示例将说明不同之处。接下来将展示两个荒谬但有说明性的课程。每个类都实现AutoCloseable,因此可以与try-with-resources 一起使用,并且close()当与try-with-resources语句一起正确使用时,将自动调用其方法。它们被命名为反映OuterResource可以使用实例实例化InnerResource。下面举个例子$title(InnerResource.java)import static java.lang.System.out;public class InnerResource implements AutoCloseable{   public InnerResource()   {      out.println("InnerResource created.");   }   public InnerResource(      final RuntimeException exceptionToThrow)   {      throw  exceptionToThrow != null         ? exceptionToThrow         : new RuntimeException("InnerResource: No exception provided.");   }   @Override   public void close() throws Exception   {      out.println("InnerResource closed.");   }   @Override   public String toString()   {      return "InnerResource";   }}$title(OuterResource.java)import static java.lang.System.out;public class OuterResource implements AutoCloseable{   private final InnerResource wrappedInnerResource;   public OuterResource(final InnerResource newInnerResource)   {      out.println("OuterResource created.");      wrappedInnerResource = newInnerResource;   }   public OuterResource(      final InnerResource newInnerResource,      final RuntimeException exceptionToThrow)   {      wrappedInnerResource = newInnerResource;      throw  exceptionToThrow != null           ? exceptionToThrow           : new RuntimeException("OuterResource: No exception provided.");   }   @Override   public void close() throws Exception   {      out.println("OuterResource closed.");   }   @Override   public String toString()   {      return "OuterResource";   }}刚刚定义的两个类现在可用于演示try在以分号分隔的列表中的相同-with-resources语句中正确声明每个实例的区别与在外部资源的构造函数内错误地嵌套内部资源的实例化之间的区别。后一种方法不如预期的那样好,因为没有本地定义的变量的内部资源在调用其AutoCloseable.close()方法方面不被视为“资源” 。下一个代码清单演示了在-with-resources语句中实例化“资源” 的错误方法try。在try-with-resources语句中实例化资源的错误方法try (OuterResource outer = new OuterResource(        new InnerResource(), new RuntimeException("OUTER"))){   out.println(outer);}catch (Exception exception){   out.println("ERROR: " + exception);}当执行上面的代码时,可以看到输出“InnerResource created。”,但是没有输出与资源的闭包有关。这是因为实例InnerResource是在对OuterResource类的构造函数的调用中实例化的,并且从未在try-with-resource语句的资源列表中分配给它自己的单独变量。使用真实资源,其含义是资源未正确关闭。下一个代码清单演示了在-with-resources语句中实例化“资源” 的正确方法try。在try资源声明中实现资源的正确方法try(InnerResource inner = new InnerResource();    OuterResource outer = new OuterResource(inner, new RuntimeException("OUTER"))){   out.println(outer);}catch (Exception exception){   out.println("ERROR: " + exception);}当执行上面的代码时,输​​出包括“InnerResource created。”和“InnerResource closed。”因为InnerResource实例被正确地分配给try-with-resources语句中的变量,因此close()即使异常也正确调用其方法在实例化期间发生。Java教程的try-with-resources语句部分包括几个正确指定-with-resources中的资源为分号分隔的单个变量定义的示例。一个示例使用java.util.zip.ZipFile和java.io.BufferedWriter显示了这种正确的方法,另一个示例使用java.sql.Statement和java.sql.ResultSet的实例显示了这种正确的方法。trytry在JDK 7中引入-with-resources是该语言的一个受欢迎的补充,它使Java开发人员更容易编写不太可能泄漏或浪费资源的资源安全应用程序。但是,当在单个try-with-resources语句中声明多个资源时,确保每个资源单独实例化并分配给在try资源说明符列表中声明的自己的变量以确保每个资源都已正确关闭是很重要的。 。一个快速的方法来检查,这是确保为ñ AutoCloseable在指定的执行资源try,应该有n-1个分号分隔这些实例化资源。
  • Redis 禁用持久化配置

    Redis 禁用持久化配置Redis 禁用持久化配置
  • Java EE 8最受关注的新特性或者说最重要的新特性

    备受期待的Java Enterprise Edition 8发布了两个令人兴奋的新API(JSON-Binding 1.0和Java EE Security 1.0)并改进了当前的API(JAX-RS 2.1,Bean Validation 2.0,JSF 2.3,CDI 2.0,JSON-P 1.1,JPA 2.2和Servlet 4.0)。这是Oracle企业Java平台近四年来的第一个版本,它包含数百个新功能,更新的功能和错误修复。那么最好的新功能是什么?<h2>前言</h2> <blockquote> <p>    备受期待的Java Enterprise Edition 8发布了两个令人兴奋的新API(JSON-Binding 1.0和Java EE Security 1.0)并改进了当前的API(JAX-RS 2.1,Bean Validation 2.0,JSF 2.3,CDI 2.0,JSON-P 1.1,JPA 2.2和Servlet 4.0)。这是Oracle企业Java平台近四年来的第一个版本,它包含数百个新功能,更新的功能和错误修复。那么最好的新功能是什么?</p> </blockquote> <h2 style="margin-left:0px; margin-right:0px; text-align:start">前5个新功能</h2> <ul> <li><strong>新的安全API:注释驱动的身份验证机制</strong></li> </ul> 全新的安全API包含三项出色的新功能:身份库抽象,新的安全上下文以及新的注释驱动的身份验证机制,这些机制会使web.xml文件声明过时。最后一个是我今天要讨论的内容。 <ul> <li><strong>JAX-RS 2.1:新的被动式客户</strong></li> </ul> 端JAX-RS 2.1中的新型被动式客户端,采用反应式编程风格并允许组合端点结果。 <ul> <li><strong>新的JSON绑定API</strong></li> </ul> 新的JSON绑定API,为JSON序列化和反序列化提供本机Java EE解决方案。 <ul> <li><strong>CDI 2.0:在Java SE中使用CDI 2.0中</strong></li> </ul> 有趣的新功能允许在Java SE应用程序中引导CDI。 <ul> <li><strong>Servlet 4.0:服务器推送Servlet 4.0中</strong></li> </ul> 的服务器推送功能将servlet规范与HTTP / 2对齐。<br />   <h2 style="margin-left:0px; margin-right:0px; text-align:start">1.新的安全API</h2> 有可能添加到Java EE 8中的最重要的新功能是新的安全API。这个新API的主要动机是简化,标准化和现代化跨容器和实现处理安全问题的方式。 <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">Web认证<strong>的配置</strong>已经实现了现代化,这要归功于三个使web.xml文件声明成为冗余的新注释。稍后再说。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff"><strong>新的安全上下文</strong> API标准化了servlet和EJB容器执行身份验证的方式</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff"><strong>新的Identity</strong>存储抽象简化了身份存储的使用。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">所以我们来看看这些新增的第一个。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">注释驱动的认证机制</h3> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">此功能全部是关于配置网络安全。web.xml文件中的哪个传统所需的XML声明。</span></span></span><br />  </p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">由于<em>HttpAuthenticationMechanism</em>接口代表了HTTP身份验证,并且随附了三个内置的启用CDI的实现,这些实现代表了网络安全可以配置的三种方式之一,这已不再是必需的。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">他们触发使用这些注释之一。</span></span></span></p> <pre> <code class="language-java">@BasicAuthenticationMechanismDefinition @FormAuthenticationMechanismDefinition @CustomFormAuthenticationMechanismDefinition</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">它们替换了servlet容器中已有的传统HTTP基本认证,基于表单和自定义表单的认证功能已经在servlet容器中了。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">例如,要启用基本身份验证,所需做的一切就是将@<em>BasicAuthenticationMechanismDefinition</em>注释添加到您的servlet,就像下面这样。</span></span></span></p> <pre> <code class="language-java">@BasicAuthenticationMechanismDefinition(realmName="${'user-realm'}") @WebServlet("/user") @DeclareRoles({ "admin", "user", "demo" }) @ServletSecurity(@HttpConstraint(rolesAllowed = "user")) public class UserServlet extends HttpServlet { //其他代码 }</code></pre> <p style="text-align:start">现在,您可以舍弃XML配置,并使用其中一个新注释来启用网络安全。<br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">2. JAX-RS 2.1:新的反应客户端</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们来看看JAX-RS 2.1中新的被动客户端以及它如何采用反应式编程风格。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">被动方法的核心是<strong>数据流</strong>的概念,其中执行模型通过流传播更改。一个典型的例子是JAX-RS方法调用。当调用返回时,将对方法调用的结果执行下一个操作(可能是继续,完成或错误)。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">您可以将其视为<strong>异步流程</strong>的流程,下一个流程将根据前一个流程的结果进行操作,然后将流程结果传递给链中的下一个流程。数据流是<strong>可组合的,</strong>因此您可以将许多流合并并转换为一个结果。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">通过<em>调用</em>用于构造客户端实例的<em>Invocation.Builder</em>实例上的<em>rx()</em>方法来启用反应性功能。它的返回类型是<strong>具有参数化</strong><strong><em>响应</em></strong><strong>类型</strong>的<strong><em>CompletionStage</em></strong>。该<em>CompletionStage</em>接口是用Java 8中引入,并提出了一些有趣的可能性。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">例如,在这个代码片段中,两个调用是对不同的端点进行的,然后将结果合并:</span></span></span></p> <pre> <code class="language-java">CompletionStage<Response> cs1 = ClientBuilder.newClient() .target(".../books/history") .request() .rx() .get(); CompletionStage<Response> cs2 = ClientBuilder.newClient() .target(".../books/geology") .request() .rx() .get(); cs1.thenCombine(cs2, (r1, r2) -> r1.readEntity(String.class) + r2.readEntity(String.class)) .thenAccept(System.out::println);</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">3.新的JSON绑定API</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在我们来看看下一个伟大的功能。新的JSON绑定API,此API为<strong>JSON序列化和反序列化</strong>提供<strong>了原生Java EE解决方案</strong>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">以前,如果您想要将JSON序列化和反序列化,则必须依赖Jackson或GSON等第三方API。不再。使用新的JSON绑定API,您可以使用本地可用的所有功能。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">从Java对象生成JSON文档并不那么简单。只需调用<em>toJson()</em>方法并将它传递给想要序列化的实例即可。</span></span></span></p> <pre> <code class="language-java">String bookJson = JsonbBuilder.create().toJson(book); </code></pre> <h3 style="margin-left:0px; margin-right:0px; text-align:start">行为定制</h3> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">可以通过注释字段,JavaBean方法和类来自定义默认的序列化和反序列化行为。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">例如,您可以使用<em>@JsonbNillable</em>来自定义空处理和<em>@JsonbPropertyOrder</em>注释来自定义您在类级别指定的属性顺序。你可以指定与数字格式<em>@JsonbNumberFormat()</em>注解,并与更改字段的名称<em>@JsonbProperty()</em>注释。</span></span></span></p> <pre> <code class="language-java">@JsonbNillable @JsonbPropertyOrder(PropertyOrderStrategy.REVERSE) public class Booklet { @JsonbProperty("cost") @JsonbNumberFormat("#0.00") private Float price; }</code></pre> 或者,您可以选择使用运行时配置构建器<em>JsonbConfig</em>处理自定义: <pre> <code class="language-java">JsonbConfig jsonbConfig = new JsonbConfig() .withPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE_WITH_DASHES) .withNullValues(true) .withFormatting(true); Jsonb jsonb = JsonbBuilder.create(jsonbConfig);</code></pre> 无论哪种方式,JSON绑定API都为Java对象的序列化和反序列化提供了广泛的功能。 <h2 style="margin-left:0px; margin-right:0px; text-align:start">4. CDI 2.0:在Java SE中使用</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在让我们继续看下一个API。CDI 2.0 API。该版本拥有许多新功能,其中一个更有趣的功能是<strong>在Java SE应用程序中引导CDI</strong>的功能。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">要在Java SE中使用CDI,必须明确引导CDI容器。这是通过调用<strong><em>SeContainerInitializer</em></strong><strong>抽象类</strong>的<strong>静态方法  </strong><strong><em>newInstance()</em></strong><strong>来实现的</strong>。它返回一个<em>SeContainer</em>实例,该实例是CDI运行时的句柄,您可以使用该句柄执行CDI解析,如此代码片段中所示。它可以访问作为CDI核心入口点的BeanManager。</span></span></span><br />  </p> <pre> <code class="language-java">SeContainer seContainer = SeContainerInitializer.newInstance().initialize(); Greeting greeting = seContainer.select(Greeting.class).get(); greeting.printMessage("Hello World"); seContainer.close();</code></pre> 使用<em>select()</em>方法检索CDI bean,方法是传递要检索和使用的bean的类名称。 <h3 style="margin-left:0px; margin-right:0px; text-align:start">配置选项</h3> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">可以通过添加拦截器,扩展,替代方法,属性和装饰器来<strong>进一步配置</strong><em>SeContext</em>。</span></span></span></p> <pre> <code class="language-java">.enableInterceptors() .addExtensions() .selectAlternatives() .setProperties() .enableDecorators()</code></pre> 通过在<em>SeContainer</em>上调用<em>close()</em>方法手动关闭容器,或者在使用<em>Try  </em><em>-with-resources</em>结构时自动关闭容器,因为<em>SeContainer</em>  扩展了<em>AutoCloseable</em>  接口。 <h2 style="margin-left:0px; margin-right:0px; text-align:start">5. Servlet 4.0:服务器推送</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">最后但并非最不重要的一点是,Servlet 4.0中的服务器推送功能使servlet规范与HTTP / 2保持一致。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">要理解此功能,您首先需要知道服务器推送的内容。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">什么是服务器推送?</h3> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">服务器推送是HTTP / 2协议中的许多新功能之一,旨在通过将这些资源推送到浏览器的缓存中来预测客户端资源需求,以便当客户端发送网页请求并接收到响应时从服务器,它需要的资源已经在缓存中。这是一项提高网页加载速度的性能增强功能。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start">它如何在Servlet 4.0中公开?</h3> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在Servlet 4.0中,<strong>Server Push功能通过</strong>从<em>HttpServletRequest</em>实例获得<strong>的</strong><strong><em>PushBuilder</em></strong><strong>实例</strong><strong>公开</strong>。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">看看这段代码片段。您可以看到,通过<em>path()</em>方法在<em>PushBuilder</em>实例上设置了<em>header.png</em>的<em>路径</em>,并通过调用<em>push()</em>将其推送到客户端。当方法返回时,路径和条件头部将被清除,以便构建器重用。该<em>menu.css</em>文件推,然后<em>ajax.js</em> javascript文件推送到客户端。</span></span></span></p> <pre> <code class="language-java">protected void doGet(HttpServletRequest request, HttpServletResponse response) { PushBuilder pushBuilder = request.newPushBuilder(); pushBuilder.path("images/header.png").push(); pushBuilder.path("css/menu.css").push(); pushBuilder.path("js/ajax.js").push(); // Return JSP that requires these resources }</code></pre> 在Servlet <em>doGet()</em>方法完成执行时,资源将到达浏览器。从JSP生成的需要这些资源的HTML不需要从服务器请求它们,因为它们已经是浏览器缓存。<br />  
  • HttpSession持久化/集群共享实现

    HttpSession什么时候创建什么时候销毁,session生命周期,javax.servlet.http.HttpSession接口表示一个会话,我们可以把一个会话内需要共享的数据保存到HttSession对象中。HttpSession持久化/集群共享实现<h2>一.Java web中什么是HttpSession</h2> javax.servlet.http.HttpSession接口表示一个会话,我们可以把一个会话内需要共享的数据保存到HttSession对象中 <h2>二.HttpSession由谁创建</h2>     session在访问tomcat<strong>服务器</strong>HttpServletRequest的getSession(true)/getSession()的时候<strong>创建</strong>,tomcat的ManagerBase类提供创建sessionid的方法:<strong>随机数+时间+jvmid</strong>;,session默认存储在服务器的内存中。<br />     sessionid是一个会话的key,浏览器第一次访问服务器会在服务器端生成一个session,有一个sessionid和它对应。tomcat生成的sessionid叫做jsessionid。 <h2>三.HttpSession什么时候销毁</h2> <ul> <li>超时;</li> <li>程序调用HttpSession.invalidate();</li> <li>程序关闭;</li> </ul> <h2>四.HttpSession持久化问题</h2>     所有支持Servlet规范的容器都自带session管理,于是大多数人都使用<code>HttpSession</code>接口存放状态信息。事实上, servlet的session会使得应用服务器水平扩展变的非常困难,在应用编写时应该完全弃用<code>HttpSession原生</code>接口。<br /> <br /> session在web应用中主要有以下几个功能实现: <ul> <li>session ID生成</li> <li>session过期管理</li> <li>session更新</li> </ul> <h3>4.1redis替代<code>HttpSession</code>接口方案设计</h3> <p><strong>生成session id:</strong><br /> 当用户登陆时,服务器生成一个全局唯一的字符串<code>SESSION:日期:20位随机字符串</code>做为redis中hash数据结构的key名,然后将该标识做为cookie返回给客户端。 之后该用户的后续请求都会带上此cookie, 我们编写一个filter, 其作用为读取请求中的标识,从redis中取出该标识对应的数据,然后放到<code>HttpServletRequest</code>对象中以供后续使用。</p> <p><strong>session过期:</strong><br /> 使用redis自带的过期功能为session id设置过期时间,轻松实现session过期。<br /> <br /> <strong>session追踪:</strong><br /> 我们可以将每个用户的session id记录下来,如保存到数据库中,或者依然放在redis里,这样就可以查到某个注册用户所有session id, 轻松实现踢出登陆功能。<br /> <br /> <strong>session更新:</strong><br /> 通过AOP, 在每个请求完后之后,检查在请求处理过程中有没有更新session信息,如果有则将新数据刷新到Redis中。<br /> <br /> <strong>方案特点:</strong><br /> 将session转移到redis中后,只要做好redis的运维工作,我们的应用服务器已经是完全无状态的了,水平扩展时只需要添加机器而不需要改动任何一行代码。</p> <h3>4.2 spring session成熟方案替换<code>HttpSession</code></h3> <strong>spring session介绍:</strong><br /> Spring session为管理用户的会话信息提供了一个API和实现。它还提供了无缝的集成: <ul> <li>HttpSession——允许在应用程序容器中替换HttpSession(即Tomcat)中立的方式。附加功能,包括</li> <li>集群会话——Spring session使得支持集群会话而不被绑定到应用程序容器的特定解决方案变得微不足道。</li> <li>多个浏览器会话——Spring session支持在一个浏览器实例中管理多个用户会话(也就是说,多个经过身份验证的帐户类似于谷歌)。</li> <li>RESTful api——Spring session允许在头文件中提供与RESTful api一起工作的会话id</li> </ul> WebSocket——提供在接收WebSocket消息时保持HttpSession存活的能力<br /> <br /> <strong>spring session主要得实现方式有:</strong> <ul> <li>Redis实现(逻辑同4.1)</li> <li>JDBC实现</li> <li>Mongo实现</li> <li>Hazelcast实现</li> <li>Pivotal GemFire实现</li> </ul> <br />