首页 > 编程语言 >Java 序列化详解

Java 序列化详解

时间:2023-04-02 23:02:26浏览次数:52  
标签:XML Java name JSON 详解 new 序列化 public


XML和JSON是两种经常在网络使用的数据表示格式,这里我们介绍如何使用Java读写XML和JSON。 

一、XML 概述

1、XML简介

我们都知道对象是不能在网络中直接传输的,不过还有补救的办法。

XML(Extensible Markup Language)可扩展标记语言,本身就被设计用来存储数据,任何一个对象都可以用XML来描述。XML是可以作为对象信息的载体在网络中传输,因为它是文本形式的。

例如,一个描述书籍的XML文档可能如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE note SYSTEM "book.dtd">
<book id="1">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>

XML有几个特点:一是纯文本,默认使用UTF-8编码,二是可嵌套,适合表示结构化数据。如果把XML内容存为文件,那么它就是一个XML文件,例如book.xml。此外,XML内容经常通过网络作为消息传输。

2、XML的结构

XML有固定的结构,首行必定是<?xml version="1.0"?>,可以加上可选的编码。紧接着,如果以类似<!DOCTYPE note SYSTEM "book.dtd">声明的是文档定义类型(DTD:Document Type Definition),DTD是可选的。接下来是XML的文档内容,一个XML文档有且仅有一个根元素,根元素可以包含任意个子元素,元素可以包含属性,例如,<isbn lang="CN">1234567</isbn>包含一个属性lang="CN",且元素必须正确嵌套。如果是空元素,可以用<tag/>表示。

由于使用了<、>以及引号等标识符,如果内容出现了特殊符号,需要使用&???;表示转义。

例如,Java<tm>必须写成:

<name>Java<tm></name>

常见的特殊字符如下:

字符

表示

<

&lt;

>

&gt;

&

&amp;

"

&quot;

'

&apos;

格式正确的XML(Well Formed)是指XML的格式是正确的,可以被解析器正常读取。而合法的XML是指,不但XML格式正确,而且它的数据结构可以被DTD或者XSD验证。

DTD文档可以指定一系列规则,例如:

  • 根元素必须是book
  • book元素必须包含nameauthor等指定元素
  • isbn元素必须包含属性lang
  • ...

如何验证XML文件的正确性呢?最简单的方式是通过浏览器验证。可以直接把XML文件拖拽到浏览器窗口,如果格式错误,浏览器会报错。

和结构类似的HTML不同,浏览器对HTML有一定的“容错性”,缺少关闭标签也可以被解析,但XML要求严格的格式,任何没有正确嵌套的标签都会导致错误。

XML是一个技术体系,除了我们经常用到的XML文档本身外,XML还支持:

  • DTD和XSD:验证XML结构和数据是否有效;
  • Namespace:XML节点和属性的名字空间;
  • XSLT:把XML转化为另一种文本;
  • XPath:一种XML节点查询语言;
  • ...

实际上,XML的这些相关技术实现起来非常复杂,在实际应用中很少用到,通常了解一下就可以了。

3、DOM

因为XML是一种树形结构的文档,它有两种标准的解析API:

  • DOM:一次性读取XML,并在内存中表示为树形结构。
  • SAX:以流的形式读取XML,使用事件回调。

我们先来看如何使用DOM来读取XML。

DOM是Document Object Model的缩写,DOM模型就是把XML结构作为一个树形结构处理,从根节点开始,每个节点都可以包含任意个子节点。

我们以下面的XML为例:

<?xml version="1.0" encoding="UTF-8" ?>
<book id="1">
    <name>Java核心技术</name>
    <author>Cay S. Horstmann</author>
    <isbn lang="CN">1234567</isbn>
    <tags>
        <tag>Java</tag>
        <tag>Network</tag>
    </tags>
    <pubDate/>
</book>

如果解析为DOM结构,它大概长这样:

Java 序列化详解_序列化

注意到最顶层的document代表XML文档,它是真正的“根”,而<book>虽然是根元素,但它是document的一个子节点。

Java提供了DOM API来解析XML,它使用下面的对象来表示XML的内容:

  • Document:代表整个XML文档;
  • Element:代表一个XML元素;
  • Attribute:代表一个元素的某个属性。

使用DOM API解析一个XML文档的代码如下:

InputStream input = Main.class.getResourceAsStream("/book.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(input);

DocumentBuilder.parse()用于解析一个XML,它可以接收InputStream,File或者URL,如果解析无误,我们将获得一个Document对象,这个对象代表了整个XML文档的树形结构,需要遍历以便读取指定元素的值:

void printNode(Node n, int indent) {
    for (int i = 0; i < indent; i++) {
        System.out.print(' ');
    }
    switch (n.getNodeType()) {
    case Node.DOCUMENT_NODE: // Document节点
        System.out.println("Document: " + n.getNodeName());
        break;
    case Node.ELEMENT_NODE: // 元素节点
        System.out.println("Element: " + n.getNodeName());
        break;
    case Node.TEXT_NODE: // 文本
        System.out.println("Text: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    case Node.ATTRIBUTE_NODE: // 属性
        System.out.println("Attr: " + n.getNodeName() + " = " + n.getNodeValue());
        break;
    default: // 其他
        System.out.println("NodeType: " + n.getNodeType() + ", NodeName: " + n.getNodeName());
    }
    for (Node child = n.getFirstChild(); child != null; child = child.getNextSibling()) {
        printNode(child, indent + 1);
    }
}

解析结构如下:

Document: #document
 Element: book
  Text: #text = 
    
  Element: name
   Text: #text = Java核心技术
  Text: #text = 
    
  Element: author
   Text: #text = Cay S. Horstmann
  Text: #text = 
  ...

对于DOM API解析出来的结构,我们从根节点Document出发,可以遍历所有子节点,获取所有元素、属性、文本数据,还可以包括注释,这些节点被统称为Node,每个Node都有自己的Type,根据Type来区分一个Node到底是元素,还是属性,还是文本,等等。

使用DOM API时,如果要读取某个元素的文本,需要访问它的Text类型的子节点,所以使用起来还是比较繁琐的。

4、SAX

使用DOM解析XML的优点是用起来省事,但它的主要缺点是内存占用太大。

另一种解析XML的方式是SAX。SAX是Simple API for XML的缩写,它是一种基于流的解析方式,边读取XML边解析,并以事件回调的方式让调用者获取数据。因为是一边读一边解析,所以无论XML有多大,占用的内存都很小。

SAX解析会触发一系列事件:

  • startDocument:开始读取XML文档;
  • startElement:读取到了一个元素,例如<book>
  • characters:读取到了字符;
  • endElement:读取到了一个结束的元素,例如</book>
  • endDocument:读取XML文档结束。

如果我们用SAX API解析XML,Java代码如下:

InputStream input = Main.class.getResourceAsStream("/book.xml");
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser saxParser = spf.newSAXParser();
saxParser.parse(input, new MyHandler());

关键代码SAXParser.parse()除了需要传入一个InputStream外,还需要传入一个回调对象,这个对象要继承自DefaultHandler:

class MyHandler extends DefaultHandler {
    public void startDocument() throws SAXException {
        print("start document");
    }

    public void endDocument() throws SAXException {
        print("end document");
    }

    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        print("start element:", localName, qName);
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {
        print("end element:", localName, qName);
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        print("characters:", new String(ch, start, length));
    }

    public void error(SAXParseException e) throws SAXException {
        print("error:", e);
    }

    void print(Object... objs) {
        for (Object obj : objs) {
            System.out.print(obj);
            System.out.print(" ");
        }
        System.out.println();
    }
}

运行SAX解析代码,可以打印出下面的结果:

start document
start element:  book
characters:
     
start element:  name
characters: Java核心技术
end element:  name
characters:
     
start element:  author
...

如果要读取<name>节点的文本,我们就必须在解析过程中根据startElement()和endElement()定位当前正在读取的节点,可以使用栈结构保存,每遇到一个startElement()入栈,每遇到一个endElement()出栈,这样,读到characters()时我们才知道当前读取的文本是哪个节点的。可见,使用SAX API仍然比较麻烦。

总结:

  • XML使用嵌套结构的数据表示方式,支持格式验证;
  • XML常用于配置文件、网络消息传输等;
  • Java提供的DOM API可以将XML解析为DOM结构,以Document对象表示;
  • DOM可在内存中完整表示XML数据结构;
  • DOM解析速度慢,内存占用大;
  • SAX是一种流式解析XML的API;
  • SAX通过事件触发,读取速度快,消耗内存少;
  • 调用方必须通过回调方法获得解析过程中的数据;

二、XML 序列化和反序列化

序列化指把对象通过流的方式存储到文件中,反序列化则是指把文件中的字节内容读出来并还原成 Java 对象。

1、XmlSerializer类

XmlSerializer类:将对象序列化到 XML 文档中和从 XML 文档中反序列化对象。XmlSerializer 使您得以控制如何将对象编码到 XML 中。

XML 序列化是将对象的公共属性 (Property)(如Student的Name属性)和字段转换为序列格式(这里是指 XML)以便存储或传输的过程。反序列化则是从 XML 输出中重新创建原始状态的对象。因此,可以将序列化视为将对象的状态保存到流或缓冲区的方法。

例如,ASP.NET 使用 XmlSerializer 类对 XML Web services 消息进行编码。

看一段最简单的Xml序列化代码:

class Program
{
    static void Main(string[] args)
    {
        int i = 10;
        //声明Xml序列化对象实例serializer
        XmlSerializer serializer = new XmlSerializer(typeof(int));
        //执行序列化并将序列化结果输出到控制台
        serializer.Serialize(Console.Out, i);
        Console.Read();
    }
}

上面代码对int i进行了序列化,并将序列化的结果输出到了控制台,输出结果如下:

<?xml version="1.0" encoding="gb2312"?>
<int>10</int>

可以将上述序列化的xml进行反序列化,如下代码:

static void Main(string[] args)
{
    using (StringReader rdr = new StringReader(@"<?xml version=""1.0"" encoding=""gb2312""?>
<int>10</int>"))
    {
        //声明序列化对象实例serializer 
        XmlSerializer serializer = new XmlSerializer(typeof(int));
        //反序列化,并将反序列化结果值赋给变量i
        int i = (int)serializer.Deserialize(rdr);
        //输出反序列化结果
        Console.WriteLine("i = " + i);
        Console.Read();
    }
}

以上代码用最简单的方式说明了xml序列化和反序列化的过程,.Net系统类库为我们做了大量的工作,序列化和反序列化都非常简单。但是在现实中业务需求往往比较复杂,不可能只简单的序列化一个int变量,显示中我们需要对复杂类型进行可控制的序列化。

[XmlRoot("cat")]
//要求不序列化Speed属性
[XmlIgnore]
[XmlAttribute]
[XmlElement]

可以使用XmlElement指定属性序列化为子节点(默认情况会序列化为子节点);或者使用XmlAttribute特性制定属性序列化为Xml节点的属性;还可以通过XmlIgnore特性修饰要求序列化程序不序列化修饰属性。

2、对象序列化和反序列化

首先我们先定义实体类:

public class People
    {
        //XmlAttribute:指定XmlSerializer将该类成员序列化为XML属性
        [XmlAttribute]
        public string Name { get; set; }
        [XmlAttribute]
        public int Age { get; set; }
    }

    [XmlRoot]
    public class Student : People
    {
  
        
//定义SClass属性的序列化为Student节点的属性

        [XmlElement]
        public string SClass { get; set; }
        [XmlElement]
        public int Number { get; set; }
    }

第一步:将实体类序列化为XML文档,代码如下:

Student stu = new Student() { Age = 12, Number = 23, Name = "张三", SClass = "高一(2)班" };
    XmlSerializer ser = new XmlSerializer(typeof(Student));
    StringWriter writer = new StringWriter();
    ser.Serialize(writer,stu);
    MessageBox.Show(writer.ToString());

在弹出框,出现的结果是:

Java 序列化详解_序列化_02

这样,我们就序列化成功了。

第二步:现在我们来进行反序列化测试: 

//将Xml反序列为Student对象
    StringReader reader = new StringReader(writer.ToString());
    //Deserialize反序列化指定TextReader包含的Xml文档,当然,不仅仅可以是TextReader,还可以是Stream等等,具体看起构造函数参数即可知道
    Student stu2= (Student)ser.Deserialize(reader);

我们用上面得到XML数据进行反序列化测试。查看运行结果,ok!

3、列表序列化和反序列化

和上面一样,序列化学生列表(People类和Student类和上面代码一样)。

List<Student> stuList = new List<Student>();
    stuList.Add(new Student() { Age = 10, Number = 1, Name = "Tom", SClass = "Class One" });
    stuList.Add(new Student() { Age = 11, Number = 2, Name = "Jay", SClass = "Class Two" });
    stuList.Add(new Student() { Age = 12, Number = 3, Name = "Pet", SClass = "Class One" });
    stuList.Add(new Student() { Age = 13, Number = 4, Name = "May", SClass = "Class Three" });
    stuList.Add(new Student() { Age = 14, Number = 5, Name = "Soy", SClass = "Class Two" });


     //序列化
    XmlSerializer ser = new XmlSerializer(typeof(List<Student>));
    StringWriter writer = new StringWriter();
    //将学生列表序列化为Xml数据
    ser.Serialize(writer, stuList);

    //反序列化
    //要先将构造StringReader,作为Deserialize()的初始化参数
    StringReader reader = new StringReader(writer.ToString());
    //别忘了从Object到List<Student>,否则会报错。。
    List<Student> stuList2 = (List<Student>)ser.Deserialize(reader);

运行结果是(注意:根是ArrayOfStudent不是Student了):

Java 序列化详解_XML_03

4、字典序列化和反序列化 

在XmlSerializer中,不支持Dirctionary<>类型的对象,所以在序列化这种最常见类型的时候,只能按照它的格式先创建一个可以序列化的类型,然后,将数据存储在该可序列化的类型中,然后再进行序列化即可。

Dictionary<string, int> dic = new Dictionary<string, int>();
    dic.Add("第一",1);
    dic.Add("第二", 2);
    dic.Add("第三", 3);
    dic.Add("第四", 4);

    List<DictionaryList> dicList = new List<DictionaryList>();
    foreach (var a in dic)
    {
      DictionaryList dicl = new DictionaryList() {  Name=a.Key, Value=a.Value};
      dicList.Add(dicl);
       
    }
    //序列化
    XmlSerializer ser = new XmlSerializer(typeof(List<DictionaryList>));
    StringWriter writer = new StringWriter();
    //序列化为Xml数据
    ser.Serialize(writer, dicList);
    MessageBox.Show(writer.ToString());

    //反序列化
    StringReader reader = new StringReader(writer.ToString());            
    List<DictionaryList> stuList2 = (List<DictionaryList>)ser.Deserialize(reader);

运行结果是:

Java 序列化详解_序列化_04

5、图片序列化和反序列化 

 补充:XmlArray和XmlArrayItem的使用,用在数组中。

 先构造实体类:

[XmlRoot("cats")]
    public class CatCollection
    {
        [XmlArray("items"), XmlArrayItem("item")]
        public Cat[] Cats { get; set; }
    }

    //[XmlRoot("cat")] 加不加都无所谓的。
    public class Cat
    {
        //定义Color属性的序列化为cat节点的属性
        [XmlAttribute("color")]
        public string Color { get; set; }

        //要求不序列化Speed属性
        [XmlIgnore]
        public int Speed { get; set; }

        //设置Saying属性序列化为Xml子元素
        [XmlElement("saying")]
        public string Saying { get; set; }
    }

 现在,进行序列化:

//声明一个猫咪对象
   var cWhite = new Cat { Color = "White", Speed = 10, Saying = "White or black,  so long as the cat can catch mice,  it is a good cat" };
   var cBlack = new Cat { Color = "Black", Speed = 10, Saying = "White or black,  so long as the cat can catch mice,  it is a good cat" };

   CatCollection cc = new CatCollection { Cats = new Cat[] { cWhite, cBlack } };

   //序列化这个对象
   XmlSerializer serializer = new XmlSerializer(typeof(CatCollection));
   StringWriter writer = new StringWriter();
   serializer.Serialize(writer,cc);
   MessageBox.Show(writer.ToString());

运行结果是:

Java 序列化详解_XML_05

6、XmlSerializer内存泄漏问题

为了提高性能,XML 序列化基础结构将动态生成程序集,以序列化和反序列化指定类型。此基础结构将查找并重复使用这些程序集。此行为仅在使用以下构造函数时发生:

XmlSerializer(Type)   XmlSerializer.XmlSerializer(Type, String)

如果使用任何其他构造函数,则会生成同一程序集的多个版本,且绝不会被卸载,这将导致内存泄漏和性能降低。最简单的解决方案是使用先前提到的两个构造函数的其中一个。

否则,必须在 Hashtable 中缓存程序集,如以下示例中所示。

/// <summary>
    /// 提供xml文档序列化 反序列化
    /// </summary>
    public sealed class EncodeHelper
    {
        /// <summary>
        /// 反序列化XML字符串为指定类型
        /// </summary>
        public static object Deserialize(string Xml, Type ThisType)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(ThisType);
            object result;
            try
            {
                using (StringReader stringReader = new StringReader(Xml))
                {
                    result = xmlSerializer.Deserialize(stringReader);
                }
            }
            catch (Exception innerException)
            {
                bool flag = false;
                if (Xml != null)
                {
                    if (Xml.StartsWith(Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble())))
                    {
                        flag = true;
                    }
                }
                throw new ApplicationException(string.Format("Couldn't parse XML: '{0}'; Contains BOM: {1}; Type: {2}.", 
                Xml, flag, ThisType.FullName), innerException);
            }
            return result;
        }

        /// <summary>
        /// 序列化object对象为XML字符串
        /// </summary>
        public static string Serialize(object ObjectToSerialize)
        {
            string result = null ;
            try
            {
            XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());
            
            using (MemoryStream memoryStream = new MemoryStream())
            {
                XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding(false));
                xmlTextWriter.Formatting = Formatting.Indented;
                xmlSerializer.Serialize(xmlTextWriter, ObjectToSerialize);
                xmlTextWriter.Flush();
                xmlTextWriter.Close();
                UTF8Encoding uTF8Encoding = new UTF8Encoding(false, true);
                result= uTF8Encoding.GetString(memoryStream.ToArray());
            }
            }
            catch (Exception innerException)
            {
                throw new ApplicationException("Couldn't Serialize Object:" + ObjectToSerialize.GetType().Name, innerException);
            }
            return result;
        }
    }

也就是说我们在使用XmlSerializer序列化,初始化XmlSerializer对象时最好使用下面两个构造函数否则会引起内存泄漏。

XmlSerializer(Type)  XmlSerializer.XmlSerializer(Type, String)

三、JSON 序列化和反序列化

1、JSON 简介

前面我们讨论了XML这种数据格式。XML的特点是功能全面,但标签繁琐,格式复杂。在Web上使用XML现在越来越少,取而代之的是JSON这种数据结构。

JSON是JavaScript Object Notation的缩写,它去除了所有JavaScript执行代码,只保留JavaScript的对象格式。

一个典型的JSON如下:

{
    "id": 1,
    "name": "Java核心技术",
    "author": {
        "firstName": "Abc",
        "lastName": "Xyz"
    },
    "isbn": "1234567",
    "tags": ["Java", "Network"]
}

JSON作为数据传输的格式,有几个显著的优点:

  • JSON只允许使用UTF-8编码,不存在编码问题;
  • JSON只允许使用双引号作为key,特殊字符用\转义,格式简单;
  • 浏览器内置JSON支持,如果把数据用JSON发送给浏览器,可以用JavaScript直接处理。

因此,JSON适合表示层次结构,因为它格式简单,仅支持以下几种数据类型:

  • 键值对:{"key": value}
  • 数组:[1, 2, 3]
  • 字符串:"abc"
  • 数值(整数和浮点数):12.34
  • 布尔值:true或false
  • 空值:null

浏览器直接支持使用JavaScript对JSON进行读写:

// JSON string to JavaScript object:
jsObj = JSON.parse(jsonStr);

// JavaScript object to JSON string:
jsonStr = JSON.stringify(jsObj);

所以,开发Web应用的时候,使用JSON作为数据传输,在浏览器端非常方便。因为JSON天生适合JavaScript处理,所以,绝大多数REST API都选择JSON作为数据传输格式。

现在问题来了:使用Java如何对JSON进行读写?

2、Jackson 序列化与反序列化

1. Jackson简介

在Java中,针对JSON也有标准的JSR 353 API,但是我们在前面讲XML的时候发现,如果能直接在XML和JavaBean之间互相转换是最好的。类似的,如果能直接在JSON和JavaBean之间转换,那么用起来就简单多了。

常用的用于解析JSON的第三方库有:

  • Jackson
  • Gson
  • Fastjson
  • ...

注意到上一节提到的那个可以解析XML的浓眉大眼的Jackson也可以解析JSON,因此我们只需要引入以下Maven依赖:

  • com.fasterxml.jackson.core:jackson-databind:2.12.0

就可以使用下面的代码解析一个JSON文件:

InputStream input = Main.class.getResourceAsStream("/book.json");
ObjectMapper mapper = new ObjectMapper();
// 反序列化时忽略不存在的JavaBean属性:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Book book = mapper.readValue(input, Book.class);

核心代码是创建一个ObjectMapper对象。

关闭DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES功能使得解析时如果JavaBean不存在该属性时解析不会报错。

2. 序列化

把JavaBean变为JSON,那就是序列化,如果把JSON解析为JavaBean的过程称为反序列化。

要实现JavaBean到JSON的序列化,只需要一行代码:

String json = mapper.writeValueAsString(book);

要把JSON的某些值解析为特定的Java对象,例如LocalDate,也是完全可以的。

例如:

{
    "name": "Java核心技术",
    "pubDate": "2016-09-01"
}

要解析为:

public class Book {
    public String name;
    public LocalDate pubDate;
}

只需要引入标准的JSR 310关于JavaTime的数据格式定义至Maven:

  • com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.0

然后,在创建ObjectMapper时,注册一个新的JavaTimeModule:

ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());

有些时候,内置的解析规则和扩展的解析规则如果都不满足我们的需求,还可以自定义解析。

举个例子,假设Book类的isbn是一个BigInteger:

public class Book {
	public String name;
	public BigInteger isbn;
}

但JSON数据并不是标准的整形格式:

{
    "name": "Java核心技术",
    "isbn": "978-7-111-54742-6"
}

直接解析,肯定报错。这时,我们需要自定义一个IsbnDeserializer,用于解析含有非数字的字符串:

public class IsbnDeserializer extends JsonDeserializer<BigInteger> {
    public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // 读取原始的JSON字符串内容:
        String s = p.getValueAsString();
        if (s != null) {
            try {
                return new BigInteger(s.replace("-", ""));
            } catch (NumberFormatException e) {
                throw new JsonParseException(p, s, e);
            }
        }
        return null;
    }
}

然后,在Book类中使用注解标注:

public class Book {
    public String name;
    // 表示反序列化isbn时使用自定义的IsbnDeserializer:
    @JsonDeserialize(using = IsbnDeserializer.class)
    public BigInteger isbn;
}

类似的,自定义序列化时我们需要自定义一个IsbnSerializer,然后在Book类中标注@JsonSerialize(using = ...)即可。

3. 反序列化

在反序列化时,Jackson要求Java类需要一个默认的无参数构造方法,否则,无法直接实例化此类。存在带参数构造方法的类,如果要反序列化,注意再提供一个无参数构造方法。

对于enum字段,Jackson按String类型处理,即:

class Book {
    public DayOfWeek start = MONDAY;
}

序列化为:

{
    "start": "MONDAY"
}

对于record类型,Jackson会自动找出它的带参数构造方法,并根据JSON的key进行匹配,可直接反序列化。对record类型的支持需要版本2.12.0以上。

总结:

JSON是轻量级的数据表示方式,常用于Web应用;

Jackson可以实现JavaBean和JSON之间的转换;

可以通过Module扩展Jackson能处理的数据类型;

可以自定义JsonSerializer和JsonDeserializer来定制序列化和反序列化。

3、Fastjson 序列化与反序列化

1. Fastjson 简介

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。

Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。

Fastjson 源码地址Fastjson 中文 Wiki

Fastjson 特性:

  • 提供服务器端、安卓客户端两种解析工具,性能表现较好。
  • 提供了 toJSONString() 和 parseObject() 方法来将 Java 对象与 JSON 相互转换。调用toJSONString方 法即可将对象转换成 JSON 字符串,parseObject 方法则反过来将 JSON 字符串转换成对象。
  • 允许转换预先存在的无法修改的对象(只有class、无源代码)。
  • Java泛型的广泛支持。
  • 允许对象的自定义表示、允许自定义序列化类。
  • 支持任意复杂对象(具有深厚的继承层次和广泛使用的泛型类型)。

下载和使用:

你可以在 maven 中央仓库中直接下载,或者配置 Maven 依赖:


<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>  <!-- 根据需要使用特定版本,建议使用最新版本 -->
</dependency>


2. 序列化:toJSONString()

1)序列化对象


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class User {

    /**
     * @JSONField 作用:自定义对象属性所对应的 JSON 键名
     * @JSONField 的作用对象:
     * 1. Field
     * 2. Setter 和 Getter 方法
     * 注意:
     * 1. 若属性是私有的,必须要有 set 方法,否则反序列化会失败。
     * 2. 若没有 @JSONField 注解,则直接使用属性名。
     */
    @JSONField(name="NAME")
    private String name;
    @JSONField(name="AGE")
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class ObjectTest {

    private static List<User> userList = new ArrayList<User>();

    @BeforeAll
    public static void setUp() {
        userList.add(new User("xiaoming", 18));
        userList.add(new User("xiaodan", 19));
    }

    @DisplayName("序列化对象")
    @Test
    public void testObjectToJson() {
        String userJson = JSON.toJSONString(userList.get(0));
        System.out.println(userJson);  // {"AGE":18,"NAME":"xiaoming"}
    }

    @DisplayName("序列化集合")
    @Test
    public void testListToJson() {
        String userListJson = JSON.toJSONString(userList);
        System.out.println(userListJson);  // [{"AGE":18,"NAME":"xiaoming"},{"AGE":19,"NAME":"xiaodan"}]
    }

    @DisplayName("序列化数组")
    @Test
    public void testArrayToJson() {
        User[] userArray = new User[5];
        userArray[0] = new User("zhangsan", 20);
        userArray[1] = new User("lisi", 21);
        String userArrayJson = JSON.toJSONString(userArray);
        System.out.println(userArrayJson);  // [{"AGE":20,"NAME":"zhangsan"},{"AGE":21,"NAME":"lisi"},null,null,null]
    }

    @DisplayName("序列化映射")
    @Test
    public void testMapToJson() {
        Map<Integer, User> userMap = new HashMap<Integer, User>();
        userMap.put(1, new User("xiaotie", 10));
        userMap.put(2, new User("xiaoliu", 11));
        String userMapJson = JSON.toJSONString(userMap);
        System.out.println(userMapJson);  // {1:{"AGE":10,"NAME":"xiaotie"},2:{"AGE":11,"NAME":"xiaoliu"}}
    }

}


2)序列化指定属性字段

利用 JSON.toJSONString 方法序列化指定属性字段,主要通过设置属性预过滤器(SimplePropertyPreFilter)的包含属性字段列表(includes)实现。

主要应用于只想验证某些字段的情况,比如只验证跟测试用例有关的字段。


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;

class User {

    /**
     * @JSONField 作用:自定义对象属性所对应的 JSON 键名
     * @JSONField 的作用对象:
     * 1. Field
     * 2. Setter 和 Getter 方法
     * 注意:
     * 1. 若属性是私有的,必须要有 set 方法,否则反序列化会失败。
     * 2. 若没有 @JSONField 注解,则直接使用属性名。
     */
    @JSONField(name="NAME")
    private String name;
    @JSONField(name="AGE")
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class ObjectTest {

    @DisplayName("指定所有类的属性字段")
    @Test
    public void testAllClassField() {
        User user = new User("xiaoming", 18);
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();  // 默认所有类型的类均可转换
        filter.getIncludes().addAll(Arrays.asList("NAME", "AGE"));  // 需存在于 @JSONField
        String text = JSON.toJSONString(user, filter);
        System.out.println(text);  // {"AGE":18,"NAME":"xiaoming"}
    }

    @DisplayName("指定单个类的个别属性字段")
    @Test
    public void testOneClassField() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("xiaodan", 18));
        users.add(new User("xiaoxue", 19));
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class);  // 指定User类
        filter.getIncludes().addAll(Arrays.asList("NAME"));
        String text = JSON.toJSONString(users, filter);
        System.out.println(text);  // [{"NAME":"xiaodan"},{"NAME":"xiaoxue"}]
    }

}


3)序列化排除属性字段

利用 JSON.toJSONString 方法序列化过滤属性字段,主要通过设置属性预过滤器(SimplePropertyPreFilter)的排除属性字段列表(excludes)实现。

主要应用于不想验证某些字段的情况,比如排除无法验证的随机属性字段。


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;

class User {

    /**
     * @JSONField 作用:自定义对象属性所对应的 JSON 键名
     * @JSONField 的作用对象:
     * 1. Field
     * 2. Setter 和 Getter 方法
     * 注意:
     * 1. 若属性是私有的,必须要有 set 方法,否则反序列化会失败。
     * 2. 若没有 @JSONField 注解,则直接使用属性名。
     */
    @JSONField(name="NAME")
    private String name;
    @JSONField(name="AGE")
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

public class ObjectTest {

    @DisplayName("排除所有类的属性字段")
    @Test
    public void testAllClassField() {
        User user = new User("xiaoming", 18);
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter();  // 默认所有类型的类均可转换
        filter.getExcludes().addAll(Arrays.asList("NAME"));  // 排除 NAME 字段(需存在于 @JSONField)
        String text = JSON.toJSONString(user, filter);
        System.out.println(text);  // {"AGE":18}
    }

    @DisplayName("排除指定类的属性字段")
    @Test
    public void testOneClassField() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("xiaodan", 18));
        users.add(new User("xiaoxue", 19));
        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(User.class);  // 指定User类
        filter.getExcludes().addAll(Arrays.asList("AGE"));
        String text = JSON.toJSONString(users, filter);
        System.out.println(text);  // [{"AGE":18},{"AGE":19}]
    }

}


3.  反序列化:parseObject() / parseArray()

1)反序列化对象


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;

class User {

    /**
     * @JSONField 作用:自定义对象属性所对应的 JSON 键名
     * @JSONField 的作用对象:
     * 1. Field
     * 2. Setter 和 Getter 方法
     * 注意:
     * 1. 若属性是私有的,必须要有 set 方法,否则反序列化会失败。
     * 2. 若没有 @JSONField 注解,则直接使用属性名。
     */
    // @JSONField(name="name")
    private String name;
    // @JSONField(name="age")
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class ObjectTest {

    @DisplayName("反序列化对象")
    @Test
    public void testJsonToObject() {
        String text = "{\"age\":18,\"name\":\"xiaoming\"}";
        User user = JSON.parseObject(text, User.class);
        System.out.println(user);  // User{name='xiaoming', age=18}
    }

    @DisplayName("反序列化数组")
    @Test
    public void testJsonToArray() {
        String text = "[{\"age\":18,\"name\":\"xiaoming\"}, {\"age\":19,\"name\":\"xiaowa\"}]";
        User[] users = JSON.parseObject(text, User[].class);
        System.out.println(Arrays.toString(users));  // [User{name='xiaoming', age=18}, User{name='xiaowa', age=19}]
    }

    @DisplayName("反序列化集合")
    @Test
    public void testJsonToCollection() {
        String text = "[{\"age\":18,\"name\":\"xiaoming\"}, {\"age\":19,\"name\":\"xiaowa\"}]";
        // List 集合
        List<User> userList = JSON.parseArray(text, User.class);
        System.out.println(Arrays.toString(userList.toArray()));  // [User{name='xiaoming', age=18}, User{name='xiaowa', age=19}]
        // Set 集合
        Set<User> userSet = JSON.parseObject(text, new TypeReference<Set<User>>() {});
        System.out.println(Arrays.toString(userSet.toArray()));  // [User{name='xiaowa', age=19}, User{name='xiaoming', age=18}]
    }

    @DisplayName("反序列化映射")
    @Test
    public void testJsonToMap() {
        String text = "{1:{\"age\":18,\"name\":\"xiaoming\"}, 2:{\"age\":19,\"name\":\"xiaowa\"}}";
        Map<Integer, User> userList = JSON.parseObject(text, new TypeReference<Map<Integer, User>>() {});
        for (Integer i : userList.keySet()) {
            System.out.println(userList.get(i));
        }
        /*
            User{name='xiaoming', age=18}
            User{name='xiaowa', age=19}
         */
    }

}


2)反序列化非公有字段

由于某些属性字段没有公有设置方法,或者没有以字段名称作为公有设置方法,那么当需要反序列化这些属性字段时,需要指定 SupportNonPublicField(支持非公有字段)反序列化参数。


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;

class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }

    private int getAge() {
        return age;
    }

    private void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class PrivateTest {

    @DisplayName("反序列化非公有字段")
    @Test
    public void testJsonToObject() {
        String text = "{\"age\":18,\"name\":\"xiaoming\"}";
        Person person = JSON.parseObject(text, Person.class, Feature.SupportNonPublicField);
        System.out.println(person.toString());  // Person{name='xiaoming', age=18}
    }

}


四、YAML 序列化和反序列化

1、YAML简介

  • YAML(YAML Ain't Markup Language,即 YAML 不是一种标记语言),也可以叫做 YML 。YAML 是一种直观的、能够被电脑识别的数据序列化格式,容易被人类阅读,容易和脚本语言交互,可以被支持 YAML 库的不同编程语言程序所导入(如 C/C++、Ruby、Python、Java、Perl、C#、PHP 等)。
  • YML 文件是以数据为核心的,相比 JSON、XML 等方式更加简洁。
  • YAML 文件的扩展名可以使用 .yml 或者 .yaml 。

YAML 官网

Java 序列化详解_java_06

YAML 语法:

  • 大小写敏感。
  • 数据值前边必须要有空格(大于等于 1 个)作为分隔符。
  • 使用缩进表示层级关系。
  • 缩进时不允许使用 Tab 键,只允许使用空格(各个系统 Tab对应的 空格数目可能不同,导致层次混乱)。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
  • # 表示注释,从这个字符一直到行尾,都会被解析器忽略。

YAML 数据格式:

  • 对象(map):键值对的集合
# 行内写法
address: {province: 山东, city: 济南}

# 多行写法
address:
  province: 山东
  city: 济南
  • 数组:一组按次序排列的值
# 行内写法
hobbyList: [游泳, 跑步]

# 多行写法
hobbyList:
  - 游泳
  - 跑步
  • 纯量:单个的、不可再分的值
# 字符串默认不用加引号,但包含空格或特殊字符必须加引号,单引号或双引号都可以
# 单引号:不识别转移字符,即原样输出
# 双引号:识别转移字符,如 \r、\n 等
userId: S123
username: "lisi"
password: '123456'
province: 山东
city: "济南 : ss"

# 布尔值
success: true

# 整数
age: 13

# 浮点数
weight: 75.5

# Null
gender: ~

# 时间:使用 ISO8601 标准
createDate: 2001-12-14T21:59:43.10+05
  • 参数引用
name: lisi

person:
  name: ${name}  # 引用上边定义的name值

2、YAML 序列化和反序列化

1. yaml文件与Bean类

示例:yaml 文件。

userId: 1
username: lisi
password: 123456
address: {province: 山东, city: 济南}
hobbyList: [游泳, 跑步]

或:

userId: 1
username: "lisi"
password: '123456'
address:
  province: 山东
  city: "济南 : ss"
hobbyList:
  - 游泳
  - 跑步

示例:Bean 实体类。

  • Maven 依赖:
<!-- Bean类的GETTER、SETTER注解 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>
  • User 实体类:
import lombok.*;

import java.security.Timestamp;
import java.util.List;

@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String userId;
    private String username;
    private String password;
    private Timestamp createDate;
    private Address address;
    private List<String> hobbyList;
}
  • Address 实体类:
import lombok.*;

@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Address {

    private String province;
    private String city;
}

2. snakeyaml 库

Maven 依赖:

<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.21</version>
</dependency>

1)yaml、map 互转

使用 yaml 对象中的 load 方法会返回一个 map 对象,然后遍历这个 map 即可得到自己想要的数据。

import org.yaml.snakeyaml.Yaml;

import java.io.InputStream;
import java.util.Map;

public class YamlDemo {

    public static void main(String[] args) {
        // yaml 读取
        InputStream in = YamlDemo.class.getClassLoader().getResourceAsStream("test.yaml");
        Yaml yaml = new Yaml();
        Map<String, Object> map = yaml.loadAs(in, Map.class);
        map.forEach(
                (String key, Object value) -> {
                    System.out.println("key: "+key+" value: "+value);
                }
        );
        /* 执行结果:
            key: userId value: 1
            key: username value: lisi
            key: password value: 123456
            key: address value: {province=山东, city=济南}
            key: hobbyList value: [游泳, 跑步]
         */

        // yaml 写入
        map.put("username", "zhangsan");  // 修改读取的yaml内容
        try {
            // 将修改后的内容写入new_user.yaml
            yaml.dump(map, new OutputStreamWriter(new FileOutputStream(new File("new_user.yaml"))));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

执行结果:

key: userId value: 1
key: username value: lisi
key: password value: 123456
key: address value: {province=山东, city=济南}
key: hobbyList value: [游泳, 跑步]

2)yaml 转 Bean

import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.Objects;

public class YamlDemo {

    public static void main(String[] args) {
        InputStream resource = YamlDemo.class.getClassLoader().getResourceAsStream("test.yaml");
        if (Objects.nonNull(resource)) {
            Yaml yaml = new Yaml();
            User user = yaml.loadAs(resource, User.class);
            System.out.println(user.getClass());  // class User
            System.out.println(user);  // User(userId=1, username=lisi, password=123456, createDate=null, address=Address(province=山东, city=济南), hobbyList=[游泳, 跑步])
        }
    }
}

3)Bean 转 yaml

import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.Arrays;

public class YamlDemo {

    public static void main(String[] args) {
        User user = new User();
        user.setUserId("1");
        user.setUsername("lisi");
        user.setPassword("123456");
        user.setAddress(new Address("山东", "济南"));
        user.setHobbyList(Arrays.asList("游泳", "跑步"));
        Yaml yaml = new Yaml();
        String userString = yaml.dump(user);  // 输出字符串
        try {
            yaml.dump(user, new FileWriter("Bean.yaml"));  // 输出文件
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(userString);
        System.out.println(yaml.loadAs(userString, User.class));
    }
}

输出结果:

!!User  # 首行为:!!+全类名
address: {city: 济南, province: 山东}
createDate: null
hobbyList: [游泳, 跑步]
password: '123456'
userId: '1'
username: lisi

上面的对象和数组是显示在一行的,我们也可以通过自定义序列化显示为多行。

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import java.io.*;
import java.util.Arrays;

public class YamlDemo {

    public static void main(String[] args) {
        User user = new User();
        user.setUserId("1");
        user.setUsername("lisi");
        user.setPassword("123456");
        user.setAddress(new Address("山东", "济南"));
        user.setHobbyList(Arrays.asList("游泳", "跑步"));
        DumperOptions dumperOptions = new DumperOptions();
        dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        Yaml yaml = new Yaml(dumperOptions);
        String userString = yaml.dump(user);  // 输出字符串
        try {
            yaml.dump(user, new FileWriter("Bean.yaml"));  // 输出文件
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(userString);
        System.out.println(yaml.loadAs(userString, User.class));
    }
}

执行结果:

!!User
address:
  city: 济南
  province: 山东
createDate: null
hobbyList:
- 游泳
- 跑步
password: '123456'
userId: '1'
username: lisi

3. jackson 库

jackson-dataformat-yaml 是在 snakeyaml 的基础上又封装了一层。

Maven 依赖:

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-yaml</artifactId>
  <version>2.12.0</version>
</dependency>

1)yaml 转 Bean

import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;

import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;

public class YamlDemo {

    public static void main(String[] args) {
        InputStream resource = YamlDemo.class.getClassLoader().getResourceAsStream("test.yaml");
        if (Objects.nonNull(resource)) {
            YAMLMapper yamlMapper = new YAMLMapper();
            User user = null;
            try {
                user = yamlMapper.readValue(resource, User.class);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println(user.getClass());  // class User
            System.out.println(user);  // User(userId=1, username=lisi, password=123456, createDate=null, address=Address(province=山东, city=济南), hobbyList=[游泳, 跑步])
        }
    }
}

2)Bean 转 yaml

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;

import java.io.FileWriter;
import java.util.Arrays;

public class YamlDemo {

    public static void main(String[] args) {
        User user = new User();
        user.setUserId("1");
        user.setUsername("lisi");
        user.setPassword("123456");
        user.setAddress(new Address("山东", "济南"));
        user.setHobbyList(Arrays.asList("游泳", "跑步"));
        YAMLMapper yamlMapper = new YAMLMapper();
        try {
            System.out.println(yamlMapper.writeValueAsString(user));
            yamlMapper.writeValue(new FileWriter("Bean.yaml"), user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 执行结果:

---
userId: "1"
username: "lisi"
password: "123456"
createDate: null
address:
  province: "山东"
  city: "济南"
hobbyList:
- "游泳"
- "跑步"

标签:XML,Java,name,JSON,详解,new,序列化,public
From: https://blog.51cto.com/u_11837698/6165247

相关文章

  • Python 数据结构与算法详解
    一、数据结构与算法1、算法提出1.算法概念算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机按照确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后再调用。算法是独立......
  • 【Java 并发】【synchronized】【一】synchronized底层是怎么通过monitor进行加锁的
    1 前言之前我们说过对象头的信息,这节我们就来看看synchronized是怎么通过monitor进行重量级加锁。2 内容回顾我们先来回顾下MarkWord的内容:当MarkWord的最后两位的锁标志位是10的时候,MarkWord这哥们说自己处于重量级锁的模式,重量级加锁不是它的责任,是monitor的责任。......
  • Java Checked Exception 的是与非
    结论JavaCheckedException是一个设计错误,初衷很美好,现实很糟糕。设计的初衷把方法可能抛出的异常,显示地声明在方法定义中,比如FileInputStream的构造函数可能会抛出FileNotFoundException:publicFileInputStream(Stringname)throwsFileNotFoundException{this(name......
  • JAVA - IO 流
    FileInputStreamimportjava.io.FileOutputStream;importjava.io.IOException;publicclassFileOutPutStreamDemo{/*FileOutputStream使用细节:1.write方法写出的数字:是ASCCI表中字符对应的数字,因此a=>972.创建FileOutputStream对象时“E:\java......
  • Java学习笔记14
    1.Arrays类​ Arrays类包含用于操作数组的各种方法(如排序和搜索)。该类没有构造函数,直接使用类名.方法名()的方法调用需要的方法。常用方法方法作用publicstaticStringtoString(数组)把数组拼接成一个字符串publicstaticintbinarySearch(数组,查找的元素)二......
  • 从 JDK 9 到 19,认识一个新的 Java 形态(内存篇)
    前言在JDK9之前,Java基本上平均每三年出一个版本。但是自从2017年9月份推出JDK9到现在,Java开始了疯狂更新的模式,基本上保持了每年两个大版本的节奏。从2017年至今,已经发布了一个版本到了JDK19。其中包括了两个LTS版本(JDK11与JDK17)。除了版本更新节奏明显加快之......
  • 【UNCTF逆向】pytrade详解
    前段时间有点别的东西在忙,最近会加大力度。题目pytrade解法这道题的内容是一些opcode也就是python编译的字节码。网上搜的一些教程是叫手扒,就简单学习了一下。变量constfast(有形参和局部变量之分)global(全局)数据结构listdictionaryslice循环whileforinif函数函数范......
  • JNDI(Java Naming and Directory Interface–Java命名和目录接口)
    JNDI(JavaNamingandDirectoryInterface,Java命名和目录接口)为应用程序提供了一种通过网络访问远程服务的方式。本节我们学习如何通过JNDIAPI注册和访问JDBC数据源对象。读者如果需要了解更多JNDI相关细节,则可参考JNDI规范文档。JNDIAPI的命名服务可以把一个逻辑名称和一个具......
  • java使用模块后,用maven打包时,需要保护的maven插件
    <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version><configuration><release>11</release><......
  • java流
    流运算map和flatMapmap函数接收一个函数作为参数,将该函数应用于流中的每个元素,并返回一个新的流。例如,我们可以通过map函数将流中的每个元素都加上1:List<Integer>numbers=Arrays.asList(1,2,3,4);List<Integer>incrementedNumbers=numbers.stream()......