-
2017-10-10 11:32:04
很多时候,工作流引擎就是系统的核心,可是很多工作流框架太过死板,配置项也比较麻烦,归根结底工作流引擎也不过是帮助我们完成事件的框架,其实可以根据自己的需求自己设计数据库,完成工作流 首先工作流必备的几张表有:历史表,步骤表,步骤历史表和事件当前状态表,这四张表是最基础的,如果工作里比较复杂也可以根据自己的工作流自行增加删减
历史表:历史表是对事件办理完成后的归档保存处理,所以历史表必须要包含事件的所有字段,这里就不给出具体表结构,根据自己的事件自己设计。
步骤表:步骤表的作用,首先定义每个工作流的步骤,当流程开始时流程的每一步以及下一步是什么大致表结构如下:CREATE TABLE "FLOW_STATE" ("CODE" NVARCHAR2(255), "STATE_CODE" NVARCHAR2(255), "BEROF_NUM" NVARCHAR2(255), "AFTER_NUM" NVARCHAR2(255), "STATE_NAME" NVARCHAR2(255), "EXPLAIN" NVARCHAR2(255) )
该表包括主键、步骤code、上一步code、下一步code,当前步骤名称,及步骤注释。
步骤历史表:记录每个事件,每一步的历史留痕,事件每走一步,这张表就插入一条数据,表结构如下:CREATE TABLE "FLOW_STEP" ("CODE" NVARCHAR2(255), "CREATE_DATE" NVARCHAR2(255), "PERSON_CODE" NVARCHAR2(255), "BEFOR_STEP" NVARCHAR2(255), "AFTER_STEP" NVARCHAR2(255), "OPINION" NVARCHAR2(255), "STEP_STATE" NVARCHAR2(255), "MAIN_CODE" NVARCHAR2(255) )
该表包括主键,操作时间按,操作人,上一步操作人,下一步操作人,意见,步骤code,以及事件当前状态表code。
事件当前状态表:主要记录事件分类,事件当前状态等信息,表结构如下:CREATE TABLE "ARES"."FLOW_MAIN" ( "CODE" NVARCHAR2(255), "CREATE_DATE" NVARCHAR2(255), "PERSON_CODE" NVARCHAR2(255), "EVENT_CODE" NVARCHAR2(255), "EVENT_STATE" NVARCHAR2(255) DEFAULT NULL, "EVENT_NAME" NVARCHAR2(255), "STEP_NUM" NVARCHAR2(255) )
此表关联所有表的相关信息,包括主键,创建时间,创建人,事件code,事件状态,事件名称code以及事件步骤code
更多相关内容 -
Java自己开发的一个工作流引擎例子
2015-05-09 00:15:20最近项目中需要使用到工作流,然后上网搜到一个大神用Javascript写的一个工作流,感觉写的很屌。然后我将Javasc代码用Java改写了,并且用mysql建了数据库表,进行了测试,可以处理一些简单的流程。我觉得代码很有... -
【Java基础-3】吃透Java IO:字节流、字符流、缓冲流
2020-09-23 20:12:33什么是Java-IO?字符流和字节流的区别与适用场景是什么?缓冲流到底实现了什么?如何高效地读写文件? 本文用大量的示例图和实例,带你吃透Java IO。Java IO流
前言
有人曾问fastjson的作者(阿里技术专家高铁):“你开发fastjson,没得到什么好处,反而挨了骂背了锅,这种事情你为什么要做呢?”
高铁答道:“因为热爱本身,就是奖励啊!”
这个回答顿时触动了我。想想自己,又何尝不是如此。写作是个痛苦的过程,用心写作就更加煎熬,需字字斟酌,反复删改才有所成。然而,当一篇篇精良文章出自己手而呈现眼前时,那些痛苦煎熬就都那么值得。如果这些博文能有幸得大家阅读和认可,就更加是莫大的鼓舞了。技术人的快乐就是可以这么纯粹和简单。
点波关注不迷路,一键三连好运连连!
IO流是Java中的一个重要构成部分,也是我们经常打交道的。这篇关于Java IO的博文干货满满,堪称全网前三(请轻喷!)
下面几个问题(问题还会继续补充),如果你能对答如流,那么恭喜你,IO知识掌握得很好,可以立即关闭文章。反之,你可以在后面得文章中寻找答案。
- Java IO流有什么特点?
- Java IO流分为几种类型?
- 字节流和字符流的关系与区别?
- 字符流是否使用了缓冲?
- 缓冲流的效率一定高吗?为什么?
- 缓冲流体现了Java中的哪种设计模式思想?
- 为什么要实现序列化?如何实现序列化?
- 序列化数据后,再次修改类文件,读取数据会出问题,如何解决呢?
1 初识Java IO
IO,即
in
和out
,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。Java 中是通过流处理IO 的,那么什么是流?
流(
Stream
),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
一般来说关于流的特性有下面几点:
- 先进先出:最先写入输出流的数据最先被输入流读取到。
- 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(
RandomAccessFile
除外) - 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
1.1 IO流分类
IO流主要的分类方式有以下3种:
- 按数据流的方向:输入流、输出流
- 按处理数据单位:字节流、字符流
- 按功能:节点流、处理流
1、输入流与输出流
输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反。
2、字节流与字符流字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。
为什么要有字符流?
Java中字符是采用Unicode标准,Unicode 编码中,一个英文字母或一个中文汉字为两个字节。
而在UTF-8编码中,一个中文字符是3个字节。例如下面图中,“云深不知处”5个中文对应的是15个字节:-28-70-111-26-73-79-28-72-115-25-97-91-27-92-124
那么问题来了,如果使用字节流处理中文,如果一次读写一个字符对应的字节数就不会有问题,一旦将一个字符对应的字节分裂开来,就会出现乱码了。为了更方便地处理中文这些字符,Java就推出了字符流。
字节流和字符流的其他区别:
- 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
- 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。详见文末效率对比。
以写文件为例,我们查看字符流的源码,发现确实有利用到缓冲区:
3、节点流和处理流
节点流:直接操作数据读写的流类,比如
FileInputStream
处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如
BufferedInputStream
(缓冲字节流)处理流和节点流应用了Java的装饰者设计模式。
下图就很形象地描绘了节点流和处理流,处理流是对节点流的封装,最终的数据处理还是由节点流完成的。
在诸多处理流中,有一个非常重要,那就是缓冲流。我们知道,程序与磁盘的交互相对于内存运算是很慢的,容易成为程序的性能瓶颈。减少程序与磁盘的交互,是提升程序效率一种有效手段。缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
联想一下生活中的例子,我们搬砖的时候,一块一块地往车上装肯定是很低效的。我们可以使用一个小推车,先把砖装到小推车上,再把这小推车推到车前,把砖装到车上。这个例子中,小推车可以视为缓冲区,小推车的存在,减少了我们装车次数,从而提高了效率。
需要注意的是,缓冲流效率一定高吗?不一定,某些情形下,缓冲流效率反而更低,具体请见IO流效率对比。完整的IO分类图如下:
1.2 案例实操
接下来,我们看看如何使用Java IO。
文本读写的例子,也就是文章开头所说的,将“松下问童子,言师采药去。只在此山中,云深不知处。”写入本地文本,然后再从文件读取内容并输出到控制台。
1、FileInputStream、FileOutputStream(字节流)
字节流的方式效率较低,不建议使用
public class IOTest { public static void main(String[] args) throws IOException { File file = new File("D:/test.txt"); write(file); System.out.println(read(file)); } public static void write(File file) throws IOException { OutputStream os = new FileOutputStream(file, true); // 要写入的字符串 String string = "松下问童子,言师采药去。只在此山中,云深不知处。"; // 写入文件 os.write(string.getBytes()); // 关闭流 os.close(); } public static String read(File file) throws IOException { InputStream in = new FileInputStream(file); // 一次性取多少个字节 byte[] bytes = new byte[1024]; // 用来接收读取的字节数组 StringBuilder sb = new StringBuilder(); // 读取到的字节数组长度,为-1时表示没有数据 int length = 0; // 循环取数据 while ((length = in.read(bytes)) != -1) { // 将读取的内容转换成字符串 sb.append(new String(bytes, 0, length)); } // 关闭流 in.close(); return sb.toString(); } }
2、BufferedInputStream、BufferedOutputStream(缓冲字节流)
缓冲字节流是为高效率而设计的,真正的读写操作还是靠
FileOutputStream
和FileInputStream
,所以其构造方法入参是这两个类的对象也就不奇怪了。public class IOTest { public static void write(File file) throws IOException { // 缓冲字节流,提高了效率 BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true)); // 要写入的字符串 String string = "松下问童子,言师采药去。只在此山中,云深不知处。"; // 写入文件 bis.write(string.getBytes()); // 关闭流 bis.close(); } public static String read(File file) throws IOException { BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file)); // 一次性取多少个字节 byte[] bytes = new byte[1024]; // 用来接收读取的字节数组 StringBuilder sb = new StringBuilder(); // 读取到的字节数组长度,为-1时表示没有数据 int length = 0; // 循环取数据 while ((length = fis.read(bytes)) != -1) { // 将读取的内容转换成字符串 sb.append(new String(bytes, 0, length)); } // 关闭流 fis.close(); return sb.toString(); } }
3、InputStreamReader、OutputStreamWriter(字符流)
字符流适用于文本文件的读写,
OutputStreamWriter
类其实也是借助FileOutputStream
类实现的,故其构造方法是FileOutputStream
的对象public class IOTest { public static void write(File file) throws IOException { // OutputStreamWriter可以显示指定字符集,否则使用默认字符集 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8"); // 要写入的字符串 String string = "松下问童子,言师采药去。只在此山中,云深不知处。"; osw.write(string); osw.close(); } public static String read(File file) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8"); // 字符数组:一次读取多少个字符 char[] chars = new char[1024]; // 每次读取的字符数组先append到StringBuilder中 StringBuilder sb = new StringBuilder(); // 读取到的字符数组长度,为-1时表示没有数据 int length; // 循环取数据 while ((length = isr.read(chars)) != -1) { // 将读取的内容转换成字符串 sb.append(chars, 0, length); } // 关闭流 isr.close(); return sb.toString() } }
4、字符流便捷类
Java提供了
FileWriter
和FileReader
简化字符流的读写,new FileWriter
等同于new OutputStreamWriter(new FileOutputStream(file, true))
public class IOTest { public static void write(File file) throws IOException { FileWriter fw = new FileWriter(file, true); // 要写入的字符串 String string = "松下问童子,言师采药去。只在此山中,云深不知处。"; fw.write(string); fw.close(); } public static String read(File file) throws IOException { FileReader fr = new FileReader(file); // 一次性取多少个字节 char[] chars = new char[1024]; // 用来接收读取的字节数组 StringBuilder sb = new StringBuilder(); // 读取到的字节数组长度,为-1时表示没有数据 int length; // 循环取数据 while ((length = fr.read(chars)) != -1) { // 将读取的内容转换成字符串 sb.append(chars, 0, length); } // 关闭流 fr.close(); return sb.toString(); } }
5、BufferedReader、BufferedWriter(字符缓冲流)
public class IOTest { public static void write(File file) throws IOException { // BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new // FileOutputStream(file, true), "UTF-8")); // FileWriter可以大幅度简化代码 BufferedWriter bw = new BufferedWriter(new FileWriter(file, true)); // 要写入的字符串 String string = "松下问童子,言师采药去。只在此山中,云深不知处。"; bw.write(string); bw.close(); } public static String read(File file) throws IOException { BufferedReader br = new BufferedReader(new FileReader(file)); // 用来接收读取的字节数组 StringBuilder sb = new StringBuilder(); // 按行读数据 String line; // 循环取数据 while ((line = br.readLine()) != null) { // 将读取的内容转换成字符串 sb.append(line); } // 关闭流 br.close(); return sb.toString(); } }
2 IO流对象
第一节中,我们大致了解了IO,并完成了几个案例,但对IO还缺乏更详细的认知,那么接下来我们就对Java IO细细分解,梳理出完整的知识体系来。
Java种提供了40多个类,我们只需要详细了解一下其中比较重要的就可以满足日常应用了。
2.1 File类
File
类是用来操作文件的类,但它不能操作文件中的数据。public class File extends Object implements Serializable, Comparable<File>
File
类实现了Serializable
、Comparable<File>
,说明它是支持序列化和排序的。File类的构造方法
方法名 说明 File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 File类的常用方法
方法 说明 createNewFile()
当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 delete()
删除此抽象路径名表示的文件或目录。 exists()
测试此抽象路径名表示的文件或目录是否存在。 getAbsoluteFile()
返回此抽象路径名的绝对路径名形式。 getAbsolutePath()
返回此抽象路径名的绝对路径名字符串。 length()
返回由此抽象路径名表示的文件的长度。 mkdir()
创建此抽象路径名指定的目录。 File类使用实例
public class FileTest { public static void main(String[] args) throws IOException { File file = new File("C:/Mu/fileTest.txt"); // 判断文件是否存在 if (!file.exists()) { // 不存在则创建 file.createNewFile(); } System.out.println("文件的绝对路径:" + file.getAbsolutePath()); System.out.println("文件的大小:" + file.length()); // 刪除文件 file.delete(); } }
2.2 字节流
InputStream
与OutputStream
是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。以
InputStream
为例,它继承了Object
,实现了Closeable
public abstract class InputStream extends Object implements Closeable
InputStream
类有很多的实现子类,下面列举了一些比较常用的:
详细说明一下上图中的类:InputStream
:InputStream
是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。FileInputSream
:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。PipedInputStream
:管道字节输入流,能实现多线程间的管道通信。ByteArrayInputStream
:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。FilterInputStream
:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。DataInputStream
:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。BufferedInputStream
:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。ObjectInputStream
:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream
的实例对象。
OutputStream
类继承关系图:
OutputStream
类继承关系与InputStream
类似,需要注意的是PrintStream
.2.3 字符流
与字节流类似,字符流也有两个抽象基类,分别是
Reader
和Writer
。其他的字符流实现类都是继承了这两个类。以
Reader
为例,它的主要实现子类如下图:
各个类的详细说明:InputStreamReader
:从字节流到字符流的桥梁(InputStreamReader
构造器入参是FileInputStream
的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。BufferedReader
:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader
是对InputStreamReader
的封装,前者构造器的入参就是后者的一个实例对象。FileReader
:用于读取字符文件的便利类,new FileReader(File file)
等同于new InputStreamReader(new FileInputStream(file, true),"UTF-8")
,但FileReader
不能指定字符编码和默认字节缓冲区大小。PipedReader
:管道字符输入流。实现多线程间的管道通信。CharArrayReader
:从Char
数组中读取数据的介质流。StringReader
:从String
中读取数据的介质流。
Writer
与Reader
结构类似,方向相反,不再赘述。唯一有区别的是,Writer
的子类PrintWriter
。2.4 序列化
待续…
3 IO流方法
3.1 字节流方法
字节输入流
InputStream
主要方法:read()
:从此输入流中读取一个数据字节。read(byte[] b)
:从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。read(byte[] b, int off, int len)
:从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。close()
:关闭此输入流并释放与该流关联的所有系统资源。
字节输出流
OutputStream
主要方法:write(byte[] b)
:将 b.length 个字节从指定 byte 数组写入此文件输出流中。write(byte[] b, int off, int len)
:将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。write(int b)
:将指定字节写入此文件输出流。close()
:关闭此输入流并释放与该流关联的所有系统资源。
3.2 字符流方法
字符输入流
Reader
主要方法:read()
:读取单个字符。read(char[] cbuf)
:将字符读入数组。read(char[] cbuf, int off, int len)
: 将字符读入数组的某一部分。read(CharBuffer target)
:试图将字符读入指定的字符缓冲区。flush()
:刷新该流的缓冲。close()
:关闭此流,但要先刷新它。
字符输出流
Writer
主要方法:write(char[] cbuf)
:写入字符数组。write(char[] cbuf, int off, int len)
:写入字符数组的某一部分。write(int c)
:写入单个字符。write(String str)
:写入字符串。write(String str, int off, int len)
:写入字符串的某一部分。flush()
:刷新该流的缓冲。close()
:关闭此流,但要先刷新它。
另外,字符缓冲流还有两个独特的方法:
BufferedWriter
类newLine()
:写入一个行分隔符。这个方法会自动适配所在系统的行分隔符。BufferedReader
类readLine()
:读取一个文本行。
4 附加内容
4.1 位、字节、字符
字节(Byte)是计量单位,表示数据量多少,是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于八位。
字符(Character)计算机中使用的字母、数字、字和符号,比如’A’、‘B’、’$’、’&'等。
一般在英文状态下一个字母或字符占用一个字节,一个汉字用两个字节表示。
字节与字符:
- ASCII 码中,一个英文字母(不分大小写)为一个字节,一个中文汉字为两个字节。
- UTF-8 编码中,一个英文字为一个字节,一个中文为三个字节。
- Unicode 编码中,一个英文为一个字节,一个中文为两个字节。
- 符号:英文标点为一个字节,中文标点为两个字节。例如:英文句号 . 占1个字节的大小,中文句号 。占2个字节的大小。
- UTF-16 编码中,一个英文字母字符或一个汉字字符存储都需要 2 个字节(Unicode 扩展区的一些汉字存储需要 4 个字节)。
- UTF-32 编码中,世界上任何字符的存储都需要 4 个字节。
4.2 IO流效率对比
首先,对比下普通字节流和缓冲字节流的效率:
public class MyTest { public static void main(String[] args) throws IOException { File file = new File("C:/Mu/test.txt"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 3000000; i++) { sb.append("abcdefghigklmnopqrstuvwsyz"); } byte[] bytes = sb.toString().getBytes(); long start = System.currentTimeMillis(); write(file, bytes); long end = System.currentTimeMillis(); long start2 = System.currentTimeMillis(); bufferedWrite(file, bytes); long end2 = System.currentTimeMillis(); System.out.println("普通字节流耗时:" + (end - start) + " ms"); System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms"); } // 普通字节流 public static void write(File file, byte[] bytes) throws IOException { OutputStream os = new FileOutputStream(file); os.write(bytes); os.close(); } // 缓冲字节流 public static void bufferedWrite(File file, byte[] bytes) throws IOException { BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(file)); bo.write(bytes); bo.close(); } }
运行结果:
普通字节流耗时:250 ms 缓冲字节流耗时:268 ms
这个结果让我大跌眼镜,不是说好缓冲流效率很高么?要知道为什么,只能去源码里找答案了。翻看字节缓冲流的
write
方法:public synchronized void write(byte b[], int off, int len) throws IOException { if (len >= buf.length) { /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ flushBuffer(); out.write(b, off, len); return; } if (len > buf.length - count) { flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; }
注释里说得很明白:如果请求长度超过输出缓冲区的大小,刷新输出缓冲区,然后直接写入数据。这样,缓冲流将无害地级联。
但是,至于为什么这么设计,我没有想明白,有哪位明白的大佬可以留言指点一下。
基于上面的情形,要想对比普通字节流和缓冲字节流的效率差距,就要避免直接读写较长的字符串,于是,设计了下面这个对比案例:用字节流和缓冲字节流分别复制文件。
public class MyTest { public static void main(String[] args) throws IOException { File data = new File("C:/Mu/data.zip"); File a = new File("C:/Mu/a.zip"); File b = new File("C:/Mu/b.zip"); StringBuilder sb = new StringBuilder(); long start = System.currentTimeMillis(); copy(data, a); long end = System.currentTimeMillis(); long start2 = System.currentTimeMillis(); bufferedCopy(data, b); long end2 = System.currentTimeMillis(); System.out.println("普通字节流耗时:" + (end - start) + " ms"); System.out.println("缓冲字节流耗时:" + (end2 - start2) + " ms"); } // 普通字节流 public static void copy(File in, File out) throws IOException { // 封装数据源 InputStream is = new FileInputStream(in); // 封装目的地 OutputStream os = new FileOutputStream(out); int by = 0; while ((by = is.read()) != -1) { os.write(by); } is.close(); os.close(); } // 缓冲字节流 public static void bufferedCopy(File in, File out) throws IOException { // 封装数据源 BufferedInputStream bi = new BufferedInputStream(new FileInputStream(in)); // 封装目的地 BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(out)); int by = 0; while ((by = bi.read()) != -1) { bo.write(by); } bo.close(); bi.close(); } }
运行结果:
普通字节流耗时:184867 ms 缓冲字节流耗时:752 ms
这次,普通字节流和缓冲字节流的效率差异就很明显了,达到了245倍。
再看看字符流和缓冲字符流的效率对比:
public class IOTest { public static void main(String[] args) throws IOException { // 数据准备 dataReady(); File data = new File("C:/Mu/data.txt"); File a = new File("C:/Mu/a.txt"); File b = new File("C:/Mu/b.txt"); File c = new File("C:/Mu/c.txt"); long start = System.currentTimeMillis(); copy(data, a); long end = System.currentTimeMillis(); long start2 = System.currentTimeMillis(); copyChars(data, b); long end2 = System.currentTimeMillis(); long start3 = System.currentTimeMillis(); bufferedCopy(data, c); long end3 = System.currentTimeMillis(); System.out.println("普通字节流1耗时:" + (end - start) + " ms,文件大小:" + a.length() / 1024 + " kb"); System.out.println("普通字节流2耗时:" + (end2 - start2) + " ms,文件大小:" + b.length() / 1024 + " kb"); System.out.println("缓冲字节流耗时:" + (end3 - start3) + " ms,文件大小:" + c.length() / 1024 + " kb"); } // 普通字符流不使用数组 public static void copy(File in, File out) throws IOException { Reader reader = new FileReader(in); Writer writer = new FileWriter(out); int ch = 0; while ((ch = reader.read()) != -1) { writer.write((char) ch); } reader.close(); writer.close(); } // 普通字符流使用字符流 public static void copyChars(File in, File out) throws IOException { Reader reader = new FileReader(in); Writer writer = new FileWriter(out); char[] chs = new char[1024]; while ((reader.read(chs)) != -1) { writer.write(chs); } reader.close(); writer.close(); } // 缓冲字符流 public static void bufferedCopy(File in, File out) throws IOException { BufferedReader br = new BufferedReader(new FileReader(in)); BufferedWriter bw = new BufferedWriter(new FileWriter(out)); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } // 释放资源 bw.close(); br.close(); } // 数据准备 public static void dataReady() throws IOException { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 600000; i++) { sb.append("abcdefghijklmnopqrstuvwxyz"); } OutputStream os = new FileOutputStream(new File("C:/Mu/data.txt")); os.write(sb.toString().getBytes()); os.close(); System.out.println("完毕"); } }
运行结果:
普通字符流1耗时:1337 ms,文件大小:15234 kb 普通字符流2耗时:82 ms,文件大小:15235 kb 缓冲字符流耗时:205 ms,文件大小:15234 kb
测试多次,结果差不多,可见字符缓冲流效率上并没有明显提高,我们更多的是要使用它的
readLine()
和newLine()
方法。4.3 NIO
待续…
-
流媒体开发中自己创建一个rtsp流地址用于测试
2018-12-03 17:31:24在流媒体项目中经常遇到需要自己搭建个实时视频流rtsp地址,虽然网上有现成的,但总是有这样那样的问题导致不是很方便或者效率不理想。 2. 正题: (1)下载一个MP4的文件,当然没有的话,随便什么格式都可以,...1. 前言
在流媒体项目中经常遇到需要自己搭建个实时视频流rtsp地址,虽然网上有现成的,但总是有这样那样的问题导致不是很方便或者效率不理想。
2. 正题:
(1)下载一个MP4的文件,当然没有的话,随便什么格式都可以,vlc创建rtsp流的时候是可以用来转码,输出mp4的
(2)打开vlc --媒体--流,点击添加,选择视频文件或者音频文件,点击串流
(3)下一步,可选择rtsp,点击添加
(4)路径 处输入streamname,这边为123,点击下一步
(5)勾选激活转码,格式选择如图,就会将不是mp4格式的文件进行转码了
(6)点击流 这个按钮
如图:说明已经在 转码输出rtsp流了
3. 验证输出的音视频流
新打开一个vlc, 媒体-- 打开网络串流 ,输入rtsp://127.0.0.1:8554/123,如图说明已经ok
个人联系方式: qq:527691055 微信:sanxizeng ,有问题可以随时咨询
-
表单式工作流功能模块设计方案
2021-08-26 13:48:41最近一个项目中需要独自设计一个表单式工作流功能模块,在此将整个功能模块的设计思路分享出来。最近一个项目中需要独自设计一个表单式工作流功能模块,在此将整个功能模块的设计思路分享出来。
文章目录
1. 需求及分析
此处所有的需求是建立在医院临床信息管理系统之上的。这里只分析关于表单式工作流功能模块的需求。
关于表单式工作流功能模块的需求全部整理如下:
- 1.一个患者在不同时期要填写的表单不同。
- 2.每个患者的手术类型不同,也就意味着,每个患者的各个时期可能都不相同。
- 3.每个时期需要单独填写多张表单,相同时期要填写表单的相同。
- 4.在每个时期要填写的其中一张表单中,可能一条患者信息对应一条记录,也可能一条患者信息对应多条记录。比如在手术期的一张检查表单中,一条患者信息只对应一条记录,但是术后期的一张检查表单中,可能会有手术后30天的情况,也会有手术后60天的情况,也就是一条患者信息对应多条记录。
- 5.对于某一个患者,要能单独的处理它的工作流(也就是不同时期的表单)。
- 6.对于某一个时期,要能单独处理在这个时期的所有患者信息。
- 7.对每张表单要有增删改查操作,在每个时期,都要能通过各种方式查询患者的信息。
针对这些需求,对整个工作流功能模块初步的想法大概是这个样子:
- 1.每位患者可以绑定一个工作流程,称为表单式的工作流。
- 2.每个流程由若干个节点组成。
- 3.医生在一个时期处理完所有的表单后,点击完成该流程节点即可进入下一流程节点。
- 4.医生还可以修改一个患者已经完成的流程节点的数据,但是无法查看还未达到的流程节点的数据。
2. 功能实现分析
上述要实现的表单式的工作流跟oa系统中的工作流不大一样,oa系统中的工作流一般是这样几个步骤:
- 1.设计流程。上级设计一种办事的流程,其中包含若干个节点,一个节点代表着一个审批人,所有节点审批都通过后才算完成,一个节点不通过,就会被打回。
- 2.发起流程。由普通员工发起一个流程申请,发起后,员工无法修改这张表单,随后这张表单会跟随流程一级一级审批。
- 3.流程审批。有若干上级审批流程,点击审批通过,该表单就会进入下以流程节点,点审批失败,流程会终止或者打回。
- 4.流程通过。所有流程通过之后,会得到流程通过的凭证,就可以拿着这个凭证去处理相关的事情。
整个oa系统的工作流功能的核心就是多级审批机制,但我们需求中的表单式的工作流,并不设计审批机制,整个工作流的功能可以简单理解为:一条患者信息选择流程后会在各个时期不停流动,在一个时期需要填写非常多的表单,这些表单填写完成后,医生点击进入下一流程,患者信息即可进入下一流程,但是这条患者信息已经留在了每个时期的表单中,医生仍然可以查看和修改这条信息。
oa式的工作流的实现非常复杂,市面上也有商业化和开源的工作流框架和引擎,但是针对我们这个需求来看,如果硬加使用,只会使整个业务更加复杂,因此,最好的办法就是自己设计一种简单工作流机制,实现上述全部的需求。
3. 工作流结构设计
根据上面的需求分析和功能实现的分析,设计的整个工作流结构如下:
- 每个工作流包含若干个节点。
- 每个节点包含基本信息和若干个表单。
- 每个患者绑定一个工作流,患者在每个工作流节点需要处理多张表单。
4. 数据库设计
4.1 总设计思路
根据上面的分析,总的设计思路如下:
- 每个不同的时期有一张主表,主要记载患者的id,每当有患者信息到达该节点时,记录患者的id,一共有两种方式记载。两种方式各有好处。综合考虑还是第1种方式最好。
- 1.只记载患者信息的id。
- 2.记载同步记载所有患者信息。
- 不管使用哪种方法,都需要将患者的id作为主表的主键,这样方便查询子表中的信息。
- 如果使用第1种方法,那么每张主表中其实主要就是吧包含主键。但在每个时期,都需要联合患者表一起查询数据。
- 如果使用第2种方法,那么每张主表需要同步患者的所有信息,且不管在哪修改了患者信息,都需要在所有主表同步患者的消息。
- 每个时期的若干小表都是主表的子表,包含该小表需要填写的所有数据字段,外键是主表的id。
- 流程节点表中绑定一张主表。
- 这样设计的好处就是,在每一个流程节点处,还能看到许多该流程节点的其它信息。
- 工作流表绑定若干个流程节点。
- 一个患者绑定一个工作流,并且存储当前节点,下一节点。
4.1 各时期主表设计
各时期的主表主要干的事情就是存储患者信息的id,代表着患者正处于当前流程节点或者已经完成该流程节点。
患者id模式
- 这种模式很简单,基本上不需要什么字段。
字段 说明 id 患者id … 权限管理需要的其它字段 同步所有患者信息模式
- 这种方式需要同步主表的所有字段。
字段 说明 id 患者id … 患者表所有字段 4.2 每个时期的若干小表
存储基本信息,关键是外键是主表的id。
字段 说明 id 唯一id … 所有数据字段 4.3 流程节点表
主要是绑定主表,还可以添加一个时期的额外数据,比如一个时期的表单填写提示。
字段 说明 id 唯一id master_table 主表名称 … 权限控制相关字段 … 该时期的其它数据字段 4.4 工作流表
在工作流表中绑定多个工作节点有两种方式:
- 1.根据最多节点数目设置若干个节点字段,存储流程节点的id。
- 2.设置一个字段,字符串拼接的形式,存储所有流程节点id。
同样,两种方式各有好处:
- 使用第1种方式,需要需求中最多节点个数完全确定,才方便设计合适的字段。
- 使用第2种方式,可以存储任意个节点,但在拿出处理的时候有些麻烦。
一个字段模式
字段 说明 id 唯一id process_name 工作流名称 process_des 工作流描述 process_ndoes 所有节点id … 权限控制相关的其它字段 多个字段模式
- 其中哈希值主要用来工作流判重。
字段 说明 id 唯一id process_name 工作流名称 process_des 工作流描述 node_num 节点数目 process_hash 工作流哈希值 node1 节点1 node2 节点2 node3 节点3 … 若干节点 … 权限控制相关的其它字段 4.5 患者表
患者表中和流程相关的一共三个字段:工作流id,当前节点id,下一节点id。其中存储下一节点id的做法类似单链表。
字段 说明 id 唯一id process_id 工作流id current_node_id 当前节点id next_node_id 下一节点id … 所有数据字段 5.接口设计
5.1 针对工作流节点的接口
-
1.新增工作流节点。
- 需要保证工作流节点所绑定的主表id和名称是唯一的。
-
2.修改工作流节点。
- 如果修改了工作流绑定的主表,先要判断这个节点所在工作流是否被患者绑定,如果绑定了,那么将无法修改主表。
-
3.删除工作流节点。
- 需要保证该节点未绑定任何工作流。
-
4.查询工作流节点。
- 查询该节点的所有数据。
5.2 针对工作流的接口
-
1.新增工作流。
- 需要保证工作流节点不重复,重复就会出错。
- 需要保证节点数大于0.
- 需要保证整条节点链表不重复。
- 如果是多字段模式的,还需要检验是否依次填写节点。
-
2.修改工作流。
- 如果已经有患者绑定了工作流,那么将不能修改工作流的节点。
-
3.删除工作流。
- 如果已经有患者绑定了工作流,那么将不能删除工作流的节点。
-
4.查询工作流。
- 查询工作流的所有字段。
5.3 针对患者的接口
-
CRUD操作略。
-
查询患者流程信息。
- 需要获取患者所处流程的所有节点信息。
- 需要判断哪些节点已经完成,正处于哪个节点,哪些节点还未到达。
-
患者流程通过。
- 患者的信息将会插入下一节点对应的主表。
- 修改患者当前节点,下一节点。
6. 前端页面设计
6.1 针对化患者信息处
- 在操作一栏中能够处理患者的流程。
6.2 患者流程处理处
6.3 具体时期处
7.其它
这种表单式工作流的设计,理论上是可以抽取出来成为一个独立的框架,待日后有时间精力再去尝试。
ATFWUS 2021-08-26
-
电商系统如何制作自己的数据流DFD图
2019-02-18 16:30:55电商系统如何制作自己的数据流DFD图 最近,要做一些系统分析,用数据的逻辑角度去理解系统,因此需要制作一个数据流图。 制作这个图时,主要的障碍是,在制作过程中,很容易,把一些控制都考虑在里面。 ... -
为什么使用工作流引擎,什么是工作流引擎,工作流引擎选型以及如何使用
2021-08-12 10:25:41不使用工作流存在以下问题工作流优缺点什么是工作流引擎尝试自己构建工作流引擎有哪些选型方案呢基于bpmn标准进行流程定义国产自定义如何使用SnakerFlow工作流以请假流程来看下数据库中数据流转情况初始状态员工发起... -
java ->IO流_转换流
2021-02-12 21:50:54转换流在学习字符流(FileReader、FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter,这又是什么意思呢?OutputStreamWriter类... -
【SRS】流媒体服务器(推流+拉流+转流)
2020-06-02 23:14:10文章目录前言安装推流拉流 前言 课程作业需要搭建一个视频流服务器,最初我采用的是nginx+rtmp发现,那延迟卡的我一愣一愣的。那行吧,那就换一个吧,一番周折后遇到了SRS似乎还不错的样子,就是环境有点难受,win... -
技术分享| 如何搭建直播场景下的推拉流媒体服务器
2022-03-16 11:43:23本文使用的流媒体服务器的搭建是基于rtmp(Real Time Message Protocol)协议的,rtmp协议是应用层的协议,要依靠底层的传输层协议,比如tcp协议来保证信息传输的可靠性。 相关服务: Nginx、srs、MediaServer等三种... -
TS流解析【PCR】自己的总结
2018-08-30 18:19:49有的时候PCR的PID跟音频或者视频的PID相同,说明PCR会融进音视频的包,注意解析,有的时候PCR是自己单独的包;CAT、NIT、SDT、EIT的PID分别为: 0x01、0x10、0x11、0x12 。 PSI被分为4个表结构,他们应被... -
第一章 java 基础 - 08.IO输入流与输出流
2018-09-26 16:48:12无意间都到一篇《走心的安卓工程师跳槽经验分享》,发现自己工作几年了,技术方面虽然有了飞跃的进步,可是不知道自己的技术到了什么地步,每个方面我都涉及到了,但都不深,这大概是初级工程师的诟病吧!... -
搭建自己的流媒体服务器-(1)服务器搭建篇
2016-02-19 15:09:47前言介绍如何搭建自己的流媒体服务器,并逐步实现客户端和服务器之间的通信。本文属于第一篇介绍如何搭建流媒体服务器。流媒体服务器开源的流媒体服务器有很多,本文使用开源流媒体解决方案live555。1. live555简介... -
控制流和数据流
2018-03-06 00:04:48数据流 数据流——描述程序运行过程中数据的流转方式及其行为状态 在MVC模型中,Model层的本质就是“数据”,数据在MVC的各个构成要素中流转并且在不同的层次扮演着不同的角色。当程序运行起来之后,我们会发现... -
【实例】百度信息流账户搭建步骤
2021-08-13 01:22:56预算,可以不限,也可以设一个自己的最大承受值(预算多比少好);推广日期:一般选择长期投放;推广时段:一般自定义,可以选择晚6到12点。当然,每个行业流量高峰不同,看行业而设;计划层级设置预算分配控制:通常... -
Java限流及常用解决方案总结
2020-09-20 11:43:42说到限流,想必大家都不陌生,一个很简单的例子就是,在12306上面买票的时候,遇到某时刻开始抢票的时候,经常页面会弹出一个类似请稍后重试的提示,从后端的技术层面来看,大概有2层解释,第一是服务器担心扛不住... -
Learun FrameWork 强大工作流引擎,让OA更智能
2018-10-25 11:50:54OA的选型关乎企业的生存发展,除了需要重视“OA技术、OA品牌、OA产品、OA服务”四大要素之外,更重要的其实是让OA变得智能化的工作流引擎。毫不夸张的说,工作流是OA协同办公的核心,起到协助提高企业运营效率、... -
OBS实现推流
2020-12-24 15:05:33这里的服务选择 自定义 就行了,服务器填自己的rtmp服务器的地址,串流密钥也填自己的。 4,开始推流 如下图所示: 这样整个推流就完成了,然后就可以在本地跑起来的flv播放器中访问rtmp定义的地址就可以了。 ... -
数据流图——从软考真题中学画数据流图DFD
2019-03-28 16:27:45我们先不看给出的图,凭借题目给出的信息自己画图,先是顶层图,画顶层图步骤有3步: 1.将软件系统看作加工, 2.确定外部实体, 3.画出数据流 找到题目中的软件系统,题目第一句就可以看到 “成绩管理系统” ... -
微服务系统架构设计系列 - RateLimiter - 1. 限流器简介与一般算法
2020-12-10 18:56:40限流器是一种防御性的编程实现方式,防止一个大型的分布式系统在不可预知的大流量到来的时候导致系统大规模故障。 限流器可以设置在服务端,主要为了限制资源的使用。放在客户端主要考虑调用压力更加均匀。 一般限流... -
文件和文件流
2019-08-08 20:16:26文件和文件流 1. File类 Java.io.File类可以获取文件以及文件夹的一些基本的属性 常用的方法 文件名称,路径,大小,判断是否存在,删除,创建 // 创建一个文件对象(可以是文件,可以是文件夹) File file = new ... -
百度信息流账户怎么搭建?百度信息流账户搭建教程
2021-08-13 01:23:08百度信息流广告账户该如何搭建?前面几篇介绍了广点通和快手的信息流账户搭建流程,这篇来重磅...预算,可以不限,也可以设一个自己的最大承受值(预算多比少好);推广日期:一般选择长期投放;推广时段:一般自定义... -
文件读取和写入(字节流和字符流)
2019-04-01 11:22:32什么是字节流? 字节流的类通常以stream结尾 字节流--传输过程中,传输数据的最基本单位是字节的流。 什么是字符流? 字符流的类通常以reader和writer结尾 字符流--传输过程中,传输数据的最基本单位是字符的流。... -
高并发处理之接口限流
2018-06-13 18:24:56最近开发的抢购活动上线后发现了两个比较明显的问题,其一:活动一开始,接口访问量剧增;其二:黑名单中增加了一大批黑名单用户...限流的方式也蛮多,本篇只讲几种我自己常用的,并且是后端的限流操作。 漏桶... -
Sentinel 集群限流设计原理
2020-05-04 14:23:23本节目录1、集群限流使用场景2、集群限流与单机限流的异同思考3、探究集群限流实现原理3.1 ClusterBuilderSlot 详解3.2 集群限流模式实现原理3.2.1 DefaultClusterTokenClient 详解3.2.2 DefaultTokenService 详解4... -
史上最骚最全最详细的IO流教程,小白都能看懂!
2019-10-31 08:54:47相信大家都能体会到,io流用到的地方很多,就比如上传下载,传输,设计模式等....基础打扎实了,才能玩更高端的。 在博主认为真正懂IO流的优秀程序员每次在使用IO流之前都会明确分析如下四点: > (1)明确要... -
分层数据流图(画法+例子)
2021-07-17 21:57:05分层数据流图(画法+例子) 1. 步骤 1.1 个人理解(说人话) 画数据流图的大概步骤就是: 我们根据题目分析出这个系统的外部系统,找出数据流的源和宿,搞清楚每个数据流的流向。画出顶层图。 根据刚刚我们画出的... -
今日头条是否限流 头条号限流是什么状态
2021-01-14 09:31:05头条号突然被限流了是什么原因?我才不管呢!有些人会对他们写的东西感兴趣。坚持不懈地努力。意思是你不能靠自己发财。这意味着你不能靠自己发财!(不管怎样,我不知道我是不是受了限制)只要快乐!如果你不高兴,就... -
在自己的电脑下搭建nginx+rtmp的流媒体服务器及用java对推流权限验证
2017-12-14 11:44:21因为公司任务需要让做一个直播的系统,经过一段时间的研究,和方便以后捡起来所以把这个写了下来 下载windows版本的nginx http://nginx-win.ecsds.eu/download/nginx 1.7.11.3 Gryphon.zip 解压到c盘,最好把...