java基础(6) IO[下] 线程(补充) XML Servlet

news2025/1/13 7:47:50
输出流 OutputStream

OutputStream是Java标准库提供的最基本的输出流。

和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)
public abstract void write(int b) throws IOException;
这个方法会写入一个字节到输出流。要注意的是,虽然传入的是int参数,但只会写入一个字节,即只写入int最低8位表示字节的部分

复制一个文件
通过InputStream读取数据,通过OutputStream写入数据

 public static void copy(String from, String to) throws IOException {
        File f = new File(from);
        File t = new File(to);
        if (!f.exists()) {
            throw new FileNotFoundException();
        }
        if (!t.exists()) {
            try {
                t.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try (InputStream input = new FileInputStream(f);
                OutputStream output = new FileOutputStream(t)) {
            int fLenght = (int) f.length();

            byte[] data = new byte[fLenght];
            System.out.println("文件长度为" + fLenght);
            int n;
            while ((n = input.read(data)) != -1) {
                System.out.println("读取数据中...");
            }
            System.out.println("读取数据成功,cc写入数据中...");
            output.write(data);
            System.out.println("写入成功");
        }

    }

Filter模式(或者装饰器模式:Decorator)

当我们需要给一个“基础”InputStream附加各种功能时,我们先确定这个能提供数据源的InputStream,因为我们需要的数据总得来自某个地方,例如,FileInputStream,数据来源自文件:

InputStream file = new FileInputStream("test.gz")

我们希望FileInputStream能提供缓冲的功能来提高读取的效率,因此我们用BufferedInputStream包装这个InputStream,得到的包装类型是BufferedInputStream,但它仍然被视为一个InputStream:

InputStream buffered = new BufferedInputStream(file);

最后,如果这个文件已经被gzip压缩,我们可以封装一个GZIPInputStream,

InputStream gzip = new GZIPInputStream(buffered);

无论我们封装多少次,得到的对象始终是InputStream,我们直接用InputStream来引用它,就可以正常读取
在这里插入图片描述
上述这种通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式(或者装饰器模式:Decorator)在这里插入图片描述
在这里插入图片描述

操作Zip

ZipInputStream是一种FilterInputStream,他直接读取zip包的内容。
在这里插入图片描述

读取
创建一个 ZipInputStream,循环调用getNextEntry(),直到返回null,表示zip流结束
getNextEntry返回一个zipEntry,一个ZipEntry表示一个压缩文件或目录,如果是压缩文件,我们就用read()方法不断读取,直到返回-1

try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
    ZipEntry entry = null;
    while ((entry = zip.getNextEntry()) != null) {
        String name = entry.getName();
        if (!entry.isDirectory()) {
            int n;
            while ((n = zip.read()) != -1) {
                ...
            }
        }
    }
}
写入zip包
try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) {
    File[] files = ...
    for (File file : files) {
        zip.putNextEntry(new ZipEntry(file.getName()));
        zip.write(Files.readAllBytes(file.toPath()));
        zip.closeEntry();
    }
}
序列化

序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。
为什么需要序列化,因为通过byte[]可以保存到文件,也可以通过网络发送出去。
顾名思义反序列化就是将byte[]数组转位java对象。

java对象序列化

要实现序列化,就要实现java.io.Serializable接口,而Serializable接口没有定义任何方法和属性,

public interface Serializable {
}

它是一个空接口。我们把这样的空接口称为“标记接口”(Marker Interface),实现了标记接口的类仅仅是给自身贴了个“标记”,并没有增加任何方法。

借助ObjectOutputStream,他负责将数据对象写入字节流

public class Main {
    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
            // 写入int:
            output.writeInt(12345);
            // 写入String:
            output.writeUTF("Hello");
            // 写入Object:
            output.writeObject(Double.valueOf(123.456));
        }
        System.out.println(Arrays.toString(buffer.toByteArray()));
    }
}

打印的buffer是一个byte数组

反序列化
try (ObjectInputStream input = new ObjectInputStream(...)) {
    int n = input.readInt();
    String s = input.readUTF();
    Double d = (Double) input.readObject();
}

反序列化时,由JVM直接构造出Java对象,不调用构造方法,构造方法内部的代码,在反序列化时根本不可能执行。

字符流 Reader

Reader是IO提供的另一个输入流接口,跟InputStream区别是,InputStream是以字节流为准,单位是byte,而Reader是一个字符流,单位是char。
在这里插入图片描述
这个方法读取字符流的下一个字符,并返回字符表示的int(也就是可以用char来转换。),范围是0~65535(char表示范围,像InputStream的byte只能是0-255)。如果已读到末尾,返回-1。

FileReader

FileReader是Reader的子类,类似于FileInputStream于InputStream的关系。

// Java8不支持UTF_8
try (Reader reader = new FileReader("./test.txt", StandardCharsets.UTF_8)) {
            int n;
            while ((n = reader.read()) != -1) {
                System.out.println((char) n);
            }
        }

也支持缓冲区读取

 try (Reader reader = new FileReader("./test.txt")) {
            char[] buffer = new char[5];
            int n;
            // n返回读取的char数
            while ((n = reader.read(buffer)) != -1) {
                System.out.println(buffer);
            }
        }

打印结果

hello
 worl
d.中文l
CharArrayReader

CharArrayReader可以在内存中模拟一个Reader,它的作用实际上是把一个char[]数组变成一个Reader,这和ByteArrayInputStream非常类似:

try (Reader reader = new CharArrayReader("Hello".toCharArray())) {
}
StringReader

直接把String作为数据源。

try (Reader reader = new StringReader("Hello")) {
}
Reader和InputStream

普通的Reader实际上是基于InputStream构造的,因为Reader需要从InputStream中读入字节流(byte),然后,根据编码设置,再转换为char就可以实现字符流。如果我们查看FileReader的源码,它在内部实际上持有一个FileInputStream;

InputStreamReader可以把任务InputStream转位Reader

// 持有InputStream:
InputStream input = new FileInputStream("src/readme.txt");
// 变换为Reader:
Reader reader = new InputStreamReader(input, "UTF-8");
Writer

Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出(把char转位byte然后写进去)。
在这里插入图片描述
Writer是所有字符输出流的超类,它提供的方法主要有:

  • 写入一个字符(0~65535):void write(int c);
  • 写入字符数组的所有字符:void write(char[] c);
  • 写入String表示的所有字符:void write(String s)。
FileWirter

跟FileReader类似


        try (Reader reader = new FileReader("./test.txt"); Writer writer = new FileWriter("./test2.txt")) {
            char[] buffer = new char[5];
            int n;
            while ((n = reader.read(buffer)) != -1) {
                System.out.println("正在写入数据" + buffer);
                writer.write(buffer);
            }
            System.out.println("写入成功");
        }

CharArrayWriter

CharArrayWriter可以在内存中创建一个Writer,它的作用实际上是构造一个缓冲区,可以写入char,最后得到写入的char[]数组,这和ByteArrayOutputStream非常类似:

try (CharArrayWriter writer = new CharArrayWriter()) {
    writer.write(65);
    writer.write(66);
    writer.write(67);
    char[] data = writer.toCharArray(); // { 'A', 'B', 'C' }
}

内存中创建一个Writer,模拟写入。

StringWriter

StringWriter也是一个基于内存的Writer,它和CharArrayWriter类似。实际上,StringWriter在内部维护了一个StringBuffer,并对外提供了Writer接口。

OutputStreamWriter

与InputStreamReader相似。OutputStreamWriter可以讲OutputStream转位Writer

try (Writer writer = new OutputStreamWriter(new FileOutputStream("readme.txt"), "UTF-8")) {
    // TODO:
}
小结
  • inputStream用户读取文件的字节流,单位是byte,read方法返回字节的int表示(0-255)
  • Reader基于InputStream封装,他是以char为单位的字符流,read方法返回char的int表示,可以用(char) n来转换返回的数据。
  • outputStream用户用来写入的字节流,单位是byte。
  • Writer是基于outputStream封装的字符流,单位是char。
  • InputStream: FileInputStream. ByteArrayInputStream
  • outStream: FIleOutputStream byteArrayOutputStream
  • Reader: FIleReader CharByteReader StringReader InputStreamReader(InputStream->Reader)
  • Writer: FileWriter charByteWriter StringWriter OutputStreamWriter(OutputStream->Writer)
PrintStream和PrintWriter

PrintStream是一种FilterOutputStream,它在OutputStream的接口上,额外提供了一些写入各种数据类型的方法:

  • 写入int:print(int)
  • 写入boolean:print(boolean)
  • 写入String:print(String)
  • 写入Object:print(Object),实际上相当于print(object.toString())


以及对应的一组println()方法,它会自动加上换行符。
看着很像System.out.xxx

事实上System.out.println()实际上就是使用PrintStream打印各种数据。其中,System.out是系统默认提供的PrintStream

PrintStream和OutputStream相比,除了添加了一组print()/println()方法,可以打印各种数据类型,比较方便外,它还有一个额外的优点,就是不会抛出IOException,这样我们在编写代码的时候,就不必捕获IOException。

有PirntStream就有PrintWriter,

PrintStream最终输出的总是byte数据,而PrintWriter则是扩展了Writer接口,它的print()/println()方法最终输出的是char数据。

public class Main {
    public static void main(String[] args)     {
        StringWriter buffer = new StringWriter();
        try (PrintWriter pw = new PrintWriter(buffer)) {
            pw.println("Hello");
            pw.println(12345);
            pw.println(true);
        }
        System.out.println(buffer.toString());
    }
}

小结
PrintStream是一种能接收各种数据类型的输出,打印数据时比较方便:

  • System.out是标准输出;
  • System.err是标准错误输出。

PrintWriter是基于Writer的输出。

Files

虽然Files是java.nio包里面的类,但他俩封装了很多读写文件的简单方法,极大的方便了我们读写文件。

byte[] data = Files.readAllBytes(Path.of("/path/to/file.txt"));
// 默认使用UTF-8编码读取:
String content1 = Files.readString(Path.of("/path/to/file.txt"));
// 可指定编码:
String content2 = Files.readString(Path.of("/path", "to", "file.txt"), StandardCharsets.ISO_8859_1);
// 按行读取并返回每行内容:
List<String> lines = Files.readAllLines(Path.of("/path/to/file.txt"));

Files.readString是java11后支持的

写入文件
// 写入二进制文件:
byte[] data = ...
Files.write(Path.of("/path/to/file.txt"), data);
// 写入文本并指定编码:
Files.writeString(Path.of("/path/to/file.txt"), "文本内容...", StandardCharsets.ISO_8859_1);
// 按行写入文本:
List<String> lines = ...
Files.write(Path.of("/path/to/file.txt"), lines);

Files工具类还有copy()、delete()、exists()、move()等快捷方法操作文件和目录。

Files提供的读写方法,受内存限制,只能读写小文件,例如配置文件等,不可一次读入几个G的大文件。读写大型文件仍然要使用文件流,每次只读写一部分文件内容。

对于简单的小文件读写操作,可以使用Files工具类简化代码。

线程(补充)

死锁
public void add(int m) {
    synchronized(lockA) { // 获得lockA的锁
        this.value += m;
        synchronized(lockB) { // 获得lockB的锁
            this.another += m;
        } // 释放lockB的锁
    } // 释放lockA的锁
}

public void dec(int m) {
    synchronized(lockB) { // 获得lockB的锁
        this.another -= m;
        synchronized(lockA) { // 获得lockA的锁
            this.value -= m;
        } // 释放lockA的锁
    } // 释放lockB的锁
}

对于上述代码,线程1和线程2如果分别执行add()和dec()方法时:

线程1:进入add(),获得lockA;
线程2:进入dec(),获得lockB。
随后:

线程1:准备获得lockB,失败,等待中;
线程2:准备获得lockA,失败,等待中。

两个线程持有不同的所,然后各自试图获取对方手里的所,造成双方无限等待,这就是死锁。

避免死锁的方法是多线程获取锁的顺序要一致。

使用wait和notify

synchronied解决了多线程竞争的问题,但没解决多线程协调问题。

class TaskQueue {
    Queue<String> queue = new LinkedList<>();

    public synchronized void addTask(String s) {
        this.queue.add(s);
    }

    public synchronized String getTask() {
        while (queue.isEmpty()) {
        }
        return queue.remove();
    }
}

如上,看似getTask通过while循环调用queu.isEmpty判断是否有数据,没有就一直等待,等其他线程调用addTask加入数据后,就能取出数据。
但是,getTask这个方法已经锁了当前的实例,导致其他线程,根本无法调用addTask。解决这个方法就是调用wait方法

public synchronized String getTask() {
    while (queue.isEmpty()) {
        this.wait();
    }
    return queue.remove();
}

调用getTask时候已经获得所,然后调用到wait之后,线程会进入等待状态,直到未来某个时刻,被其他线程唤醒。wait才会返回。

必须在synchronized块中才能调用wait()方法,因为wait()方法调用时,会释放线程获得的锁,wait()方法返回后,线程又会重新试图获得锁。

public synchronized String getTask() {
    while (queue.isEmpty()) {
        // 释放this锁:
        this.wait();
        // 重新获取this锁
    }
    return queue.remove();
}

如何唤醒呢?通过notify/notifyAll

public synchronized void addTask(String s) {
    this.queue.add(s);
    this.notifyAll(); // 唤醒在this锁等待的线程
}

注意到在往队列中添加了任务后,线程立刻对this锁对象调用notify()方法,这个方法会唤醒一个正在this锁等待的线程(就是在getTask()中位于this.wait()的线程),从而使得等待线程从this.wait()方法返回。

ReentrantLock

XML

XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。

<?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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1523995.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

html--宠物

文章目录 htmljscss html <!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>CodePen - Spaceworm</title><script> window.requestAnimFrame (function() {return (window.requestAnimat…

Javascript抓取京东、淘宝商品数据(商品采集商品详情图片抓取)

之前用的方法&#xff1a; let temp []var lists $(#J_goodsList li.gl-item)$.each(lists,function(idx,item){ temp.push({ id:$(item).data(sku), goods_img:$(item).find(img).attr(src), goods_name:$(item).find(.p-name em).text(), market_price:$(item).fi…

2024 年(第 12 届)“泰迪杯”数据挖掘挑战赛——B 题:基于多模态特征融合的图像文本检索完整思路与源代码分享

一、问题背景 随着近年来智能终端设备和多媒体社交网络平台的飞速发展&#xff0c;多媒体数据呈现海量增长 的趋势&#xff0c;使当今主流的社交网络平台充斥着海量的文本、图像等多模态媒体数据&#xff0c;也使得人 们对不同模态数据之间互相检索的需求不断增加。有效的信…

洛谷P8972 『GROI-R1』 一切都已过去(树上前缀和+运算符重载)

『GROI-R1』 一切都已过去 题目背景 悦关上窗&#xff0c;拉上帘布。 果然还是想不起来啊。 隐约记得曾和什么人一起做过这样的事。 仰面躺下&#xff0c;手执一只木笺。 「究竟如何&#xff0c;才能拥有“过去”啊……」 她闭上双眼。 「6 岁前的记忆……究竟如何才能…

ConsiStory:Training-Free的主体一致性生成

Overview 一、总览二、PPT详解 ConsiStory 一、总览 题目&#xff1a; Training-Free Consistent Text-to-Image Generation 机构&#xff1a;NVIDIA, Tel-Aviv University 论文&#xff1a;https://arxiv.org/pdf/2402.03286.pdf 代码&#xff1a;https://consistory-paper.g…

Python自动获取指定上市公司的所有财务数据(资产负债表,利润表,现金流量表)

案例背景 很多经管类同学找财务数据都很困难&#xff0c;去找一个个查找特定的公司&#xff0c;然后又要去同花顺或者东方财富网一年一年的去查看报表&#xff0c;一年一年的数据一个个填入...太慢了。 tushare能获取金融数据的接口&#xff0c;他有资产负债表&#xff0c;利…

idea warning:java源值已过时将在未来所有发行版中删除

在idea中运行maven项目 如果出现idea warning:java源值已过时将在未来所有发行版中删除&#xff0c;详见如下截图所示&#xff1a; 注意&#xff1a;jdk8 要解决这个警告需要设置3个地方 首先打开File->Project Structure中的Project&#xff0c;将SDK和language level都设…

Java学习笔记(15)

JDK7前时间相关类 Date时间类 Simpledateformat Format 格式化 Parse 解析 默认格式 指定格式 EE&#xff1a;表示周几 Parse&#xff1a;把字符串时间转成date对象 注意&#xff1a;创建对象的格式要和字符串的格式一样 Calendar日历类 不能创建对象 Getinstance 获取当…

Python笔记|字符串合并、切片、索引

一、合并 字符串可以用 合并&#xff08;粘到一起&#xff09;&#xff0c;也可以用 * 重复&#xff1a; >>> 3 * un ium unununium 相邻的两个或多个字符串字面值&#xff08;引号标注的字符&#xff09;会自动合并&#xff1a; >>> Py thon Python …

蓝桥杯每日一题 走迷宫bfs 超超详细解释!!!

昨天学习了bfs的基本概念&#xff0c;今天来做一道经典习题练练手吧&#xff01; bfs常用的两类题型 1.从A出发是否存在到达B的路径(dfs也可) 2.从A出发到B的最短路径&#xff08;数小:<20才能用dfs&#xff09; 遗留的那个问题的答案- 题目&#xff1a;走迷宫 答案&…

【Java刷题篇】串联所有单词的子串

这里写目录标题 &#x1f4c3;1.题目&#x1f4dc;2.分析题目&#x1f4dc;3.算法原理&#x1f9e0;4.思路叙述✍1.进窗口✍2.判断有效个数✍3.维护窗口✍4.出窗口 &#x1f4a5;5.完整代码 &#x1f4c3;1.题目 力扣链接: 串联所有单词的子串 &#x1f4dc;2.分析题目 阅…

力扣L14--- 415.字符串相加(JAVA版)-2024年3月17日

1.题目 2.知识点 注1&#xff1a;你可以使用Integer.parseInt()或Double.parseDouble()等方法将字符串转换为整数或浮点数。 public class Main {public static void main(String[] args) {String str "123";int num Integer.parseInt(str);System.out.println(…

如何在Mac中删除照片?这里有详细步骤

前言 本文介绍如何从Mac中删除照片,以释放硬盘空间或更好地组织文件和文件夹。 如何使用废纸篓删除Mac上的图片 在Mac上删除图片的最简单方法之一是使用废纸篓功能。学习只需几秒钟。下面是如何删除单个图片以及如何在Mac上删除多个图片,以及一些关键和有用的提示,以使该…

2023全球国际专利申请数量公布:华为遥遥领先三星、高通、苹果~

华为、三星和高通在2023年的国际专利申请数量上位居前三甲&#xff0c;其中来自中国的公司大幅度超越韩国和美国的高科技竞争对手。世界知识产权组织&#xff08;WIPO&#xff09;最新发布的数据显示&#xff0c;即使面临美国严厉制裁严重影响其在全球市场的运作能力&#xff0…

HTML静态网页成品作业(HTML+CSS)——家乡广州介绍设计制作(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有5个页面。 二、作品演示 三、代…

AI泳池溺水识别摄像机

AI泳池溺水识别摄像机是一种利用人工智能技术来监测和识别游泳池中溺水行为的智能监控设备。通过深度学习算法和图像识别技术&#xff0c;该摄像机能够实时捕捉游泳池的画面&#xff0c;自动分析水面动态和人员行为&#xff0c;判断是否有溺水事件发生&#xff0c;并及时发出警…

浅谈虚拟机下部分内网穿透功能实现方法

新钛云服已累计为您分享789篇技术干货 最近个人搭建服务器的情况有所增长&#xff0c;简单介绍一下一些可以使得服务器能被公网ip访问的方法。内网穿透一般用于将位于内部私有网络&#xff08;如家庭网络&#xff09;的服务暴露到公共网络&#xff08;如互联网&#xff09;上&a…

Java解决完全二叉树的节点个数

Java解决完全二叉树的节点个数 01 题目 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的…

java集合框架——Map集合概述

前言&#xff1a; 之前接触了单列合集&#xff0c;现在又接触了双列合集。整理下心得&#xff0c;打好基础&#xff0c;daydayup&#xff01;&#xff01; Map集合 Map集合称为双列集合&#xff0c;也被称为“键值对集合”。格式&#xff1a;{key1value1,key2value2...}&#…

4-如何进行细分市场分析- 04 案例分析 健身房行业投资项目

现在运用前面学过的如何进行细分市场分析的1、2 、3 &#xff0c;以健身房行业投资项目为例来进行细分市场的分析&#xff1a;首先是画行业的产业链&#xff0c;如下图&#xff0c;注意其中的关键节点&#xff0c; 接着对市场竞争者进行进行细分和特征分析&#xff0c;确定主要…