Bufferlist类的实现原理(java缓冲流BufferWriter的原理跟提高效率原因)

2024-09-06 03:00:12 :20

bufferlist类的实现原理(java缓冲流BufferWriter的原理跟提高效率原因)

大家好,关于bufferlist类的实现原理很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于java缓冲流BufferWriter的原理跟提高效率原因的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!

本文目录

java缓冲流BufferWriter的原理跟提高效率原因

不带缓冲的流的工作原理:它读取到一个字节/字符,就向用户指定的路径写出去 读一个写一个 所以就慢了带缓冲的流的工作原理:读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写出去,从而提高了工作效率举个现实生活中的例子:假设你是一个快递员,不带缓冲的流就像快递公司给你一份快递你就跑一次,而带缓冲的流就像快递公司给你一份快递,你先不去,等攒攒多一起送,试想一下,是不是效率提高了?

c#socket在使用TCP或UDP异步通信的时候SocketAsyncEventArgs和一般的Begin有何区别

SocketAsyncEventArgs(SAEA)在.net 2.0 sp1所提供开发人员主要使用它的场景分别是Accept,Send和Receive.在传统的Begin End模式中一般都通过调用Begin方法然后在回调方法中调用End来处理,其实SAEA原理差不多,只是由原来的指定的回调过程变成了完成事件,更重要的一个改变就是SAEA是可以复用的.详解一下SAEA的以上几种用法:1、在使用SocketAsyncEventArgs进行TCP或UDP通讯的时候最常用的几个成员分别是:Buffer,BufferList,BytesTransferred,SocketError,UserToken,BytesTransferred属性,SetBuffer方法和Completed事件。2、SocketAsyncEventArgs接收和发送数据都需要设置buffer,一般用SetBuffer方法或设置BufferList。通过Completed来查看完成情况,而在Completed通过SocketError和BytesTransferred结合来判断当前操作是否成功能,如在receive状态当BytesTransferred为零的时候,说明对方已经把连接断开了。3、由于SocketAsyncEventArgs是异步操作,在很多情况需要的一些其他信息传递到Completed事件中,这个时候可以通过UserToken属性来解决异步信息传递的问题。使用注意:SocketAsyncEventArgs同一时间只能进行一个操作,通过Completed来确认当前操作是否完成,如果同步完成是不会触该事件需要自己手动调用处理。

stringbuffer和stringbuilder的区别,底层实现原理

1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。 3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

请教关于java的泛型方法

Java泛型详解

  1. 概述在引入范型之前,Java类型分为原始类型、复杂类型,其中复杂类型分为数组和类。引入范型后,一个复杂类型就可以在细分成更多的类型。例如原先的类型List,现在在细分成List《Object》, List《String》等更多的类型。注意,现在List《Object》, List《String》是两种不同的类型,他们之间没有继承关系,即使String继承了Object。下面的代码是非法的    List《String》 ls = new ArrayList《String》();    List《Object》 lo = ls;这样设计的原因在于,根据lo的声明,编译器允许你向lo中添加任意对象(例如Integer),但是此对象是List《String》,破坏了数据类型的完整性。在引入范型之前,要在类中的方法支持多个数据类型,就需要对方法进行重载,在引入范型后,可以解决此问题(多态),更进一步可以定义多个参数以及返回值之间的关系。例如public void write(Integer i, Integer ia);public void write(Double  d, Double da);的范型版本为public 《T》 void write(T t, T ta);2. 定义&使用 类型参数的命名风格为: 推荐你用简练的名字作为形式类型参数的名字(如果可能,单个字符)。最好避免小写字母,这使它和其他的普通 的形式参数很容易被区分开来。 使用T代表类型,无论何时都没有比这更具体的类型来区分它。这经常见于泛型方法。如果有多个类型参数,我们 可能使用字母表中T的临近的字母,比如S。 如果一个泛型函数在一个泛型类里面出现,最好避免在方法的类型参数和类的类型参数中使用同样的名字来避免混 淆。对内部类也是同样。  2.1 定义带类型参数的类 在定义带类型参数的类时,在紧跟类命之后的《》内,指定一个或多个类型参数的名字,同时也可以对类型参数的取 值范围进行限定,多个类型参数之间用,号分隔。 定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数, 就像使用普通的类型一样。 注意,父类定义的类型参数不能被子类继承。 public class TestClassDefine《T, S extends T》 {     ....   }  2.2 定义待类型参数方法 在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的《》内,指定一个或多个类型参数的名字, 同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,号分隔。 定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。 例如: public 《T, S extends T》 T testGenericMethodDefine(T t, S s){     ... } 注意:定义带类型参数的方法,骑主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继 承关系, 返回值的类型和第一个类型参数的值相同。 如果仅仅是想实现多态,请优先使用通配符解决。通配符的内容见下面章节。 public 《T》 void testGenericMethodDefine2(List《T》 s){     ... } 应改为 public void testGenericMethodDefine2(List《?》 s){     ... } 3. 类型参数赋值 当对类或方法的类型参数进行赋值时,要求对所有的类型参数进行赋值。否则,将得到一个编译错误。  3.1 对带类型参数的类进行类型参数赋值 对带类型参数的类进行类型参数赋值有两种方式 第一声明类变量或者实例化时。例如 List《String》 list; list = new ArrayList《String》; 第二继承类或者实现接口时。例如 public class MyList《E》 extends ArrayList《E》 implements List《E》 {...}   3.2 对带类型参数方法进行赋值 当调用范型方法时,编译器自动对类型参数进行赋值,当不能成功赋值时报编译错误。例如 public 《T》 T testGenericMethodDefine3(T t, List《T》 list){     ... } public 《T》 T testGenericMethodDefine4(List《T》 list1, List《T》 list2){     ... }  Number n = null; Integer i = null; Object o = null; testGenericMethodDefine(n, i);//此时T为Number, S为Integer testGenericMethodDefine(o, i);//T为Object, S为Integer  List《Number》 list1 = null; testGenericMethodDefine3(i, list1)//此时T为Number  List《Integer》 list2 = null; testGenericMethodDefine4(list1, list2)//编译报错  3.3 通配符 在上面两小节中,对是类型参数赋予具体的值,除此,还可以对类型参数赋予不确定值。例如 List《?》 unknownList; List《? extends Number》 unknownNumberList; List《? super Integer》 unknownBaseLineIntgerList;  注意: 在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能像其中添加元素, 因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL List《String》 listString; List《?》 unknownList2 = listString; unknownList = unknownList2; listString = unknownList;//编译错误 4. 数组范型 可以使用带范型参数值的类声明数组,却不可有创建数组 List《Integer》 iListArray; new ArrayList《Integer》;//编译时错误 5. 实现原理5.1. Java范型时编译时技术,在运行时不包含范型信息,仅仅Class的实例中包含了类型参数的定义信息。泛型是通过java编译器的称为擦除(erasure)的前端处理来实现的。你可以(基本上就是)把它认为是一个从源码到源码的转换,它把泛型版本转换成非泛型版本。基本上,擦除去掉了所有的泛型类型信息。所有在尖括号之间的类型信息都被扔掉了,因此,比如说一个List《String》类型被转换为List。所有对类型变量的引用被替换成类型变量的上限(通常是Object)。而且,无论何时结果代码类型不正确,会插入一个到合适类型的转换。       《T》 T badCast(T t, Object o) {         return (T) o; // unchecked warning       }类型参数在运行时并不存在。这意味着它们不会添加任何的时间或者空间上的负担,这很好。不幸的是,这也意味着你不能依靠他们进行类型转换。5.2.一个泛型类被其所有调用共享下面的代码打印的结果是什么?       List《String》 l1 = new ArrayList《String》();       List《Integer》 l2 = new ArrayList《Integer》();       System.out.println(l1.getClass() == l2.getClass());或许你会说false,但是你想错了。它打印出true。因为一个泛型类的所有实例在运行时具有相同的运行时类(class),而不管他们的实际类型参数。事实上,泛型之所以叫泛型,就是因为它对所有其可能的类型参数,有同样的行为;同样的类可以被当作许多不同的类型。作为一个结果,类的静态变量和方法也在所有的实例间共享。这就是为什么在静态方法或静态初始化代码中或者在静态变量的声明和初始化时使用类型参数(类型参数是属于具体实例的)是不合法的原因。5.3. 转型和instanceof泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。       Collection cs = new ArrayList《String》();       if (cs instanceof Collection《String》) { ...} // 非法类似的,如下的类型转换Collection《String》 cstr = (Collection《String》) cs;得到一个unchecked warning,因为运行时环境不会为你作这样的检查。6. Class的范型处理Java 5之后,Class变成范型化了。JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的例子。现在,Class有一个类型参数T, 你很可能会问,T 代表什么?它代表Class对象代表的类型。比如说,String.class类型代表 Class《String》,Serializable.class代表 Class《Serializable》。这可以被用来提高你的反射代码的类型安全。特别的,因为 Class的 newInstance() 方法现在返回一个T, 你可以在使用反射创建对象时得到更精确的类型。比如说,假定你要写一个工具方法来进行一个数据库查询,给定一个SQL语句,并返回一个数据库中符合查询条件的对象集合(collection)。一个方法是显式的传递一个工厂对象,像下面的代码:interface Factory《T》 {      public T make();}public 《T》 Collection《T》 select(Factory《T》 factory, String statement) {        Collection《T》 result = new ArrayList《T》();       /* run sql query using jdbc */       for ( int i=0; i《10; i++ ) { /* iterate over jdbc results */            T item = factory.make();            /* use reflection and set all of item’s fields from sql results */            result.add( item );       }       return result;}你可以这样调用:select(new Factory《EmpInfo》(){     public EmpInfo make() {         return new EmpInfo();        }       } , ”selection string”);也可以声明一个类 EmpInfoFactory 来支持接口 Factory:class EmpInfoFactory implements Factory《EmpInfo》 { ...    public EmpInfo make() { return new EmpInfo();}}然后调用:select(getMyEmpInfoFactory(), "selection string");这个解决方案的缺点是它需要下面的二者之一:调用处那冗长的匿名工厂类,或为每个要使用的类型声明一个工厂类并传递其对象给调用的地方,这很不自然。使用class类型参数值是非常自然的,它可以被反射使用。没有泛型的代码可能是:Collection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static Collection select(Class c, String sqlStatement) {     Collection result = new ArrayList();    /* run sql query using jdbc */    for ( /* iterate over jdbc results */ ) {         Object item = c.newInstance();        /* use reflection and set all of item’s fields from sql results */        result.add(item);    }        return result;}但是这不能给我们返回一个我们要的精确类型的集合。现在Class是泛型的,我们可以写:Collection《EmpInfo》 emps=sqlUtility.select(EmpInfo.class, ”select * from emps”); ...public static 《T》 Collection《T》 select(Class《T》c, String sqlStatement) {     Collection《T》 result = new ArrayList《T》();    /* run sql query using jdbc */    for ( /* iterate over jdbc results */ ) {         T item = c.newInstance();        /* use reflection and set all of item’s fields from sql results */        result.add(item);    }     return result;}来通过一种类型安全的方式得到我们要的集合。这项技术是一个非常有用的技巧,它已成为一个在处理注释(annotations)的新API中被广泛使用的习惯用法。7. 新老代码兼容7.1. 为了保证代码的兼容性,下面的代码编译器(javac)允许,类型安全有你自己保证List l = new ArrayList《String》();List《String》 l = new ArrayList();7.2. 在将你的类库升级为范型版本时,慎用协变式返回值。例如,将代码public class Foo {     public Foo create(){        return new Foo();    }}public class Bar extends Foo {     public Foo create(){        return new Bar();    } }采用协变式返回值风格,将Bar修改为public class Bar extends Foo {     public Bar create(){        return new Bar();    } }要小心你类库的客户端。

js模板引擎事件怎么解决

前端深入node.js 3 模板引擎原理 事件 文件操作 可读流的实现原模板引擎是基于new Function + with 实现的。ejs使用实现:思路:借助fs的readFile先读取文件内容,然后使用正则表达式替换掉即可。打印的结果是一样的。复杂的情况呢?拼接字符串,拼成想要的代码主要难点就是在字符串的拼接,第二部分,将全文分为三部分,然后拼接对应的,如let str = ""; with(obj){str+= `《!DOCTYPE html》《html lang="en"》《head》 《meta charset="UTF-8"》***隐藏网址*** 《meta name="viewport" content="width=device-width, initial-scale=1.0"》 《title》Document《/title》《/head》《body》 `arr.forEach(item=》{str+=` 《li》 ${item} 《/li》 `})str+=`《/body》《/html》`} return str登录后复制这就是拼出来的字符串,然后再new Function,包裹一层函数,将with的obj传入,返回str。大概长这样。效果就是:所以本质就是将获取到的内容,使用正则表达式匹配,拼接字符串成我们想要的内容,用with包裹,改变内部作用域,再通过new Function将str包装成一个函数,传入对应的值给obj。然后运行之后str就能正常通过作用域获取值赋值。buffer在服务端,需要一个东西来标识内存,但不能是字符串,因为字符串无法标识图片。node中使用buffer来标识内存的数据。他把内存转换成了16进制来显示(16进制比较短)buffer每个字节的取值范围就是0-0xff(十进制的255).node中buffer可以和字符串任意的转换(可能出现乱码)编码规范:ASCII -》 GB8030/GBK -》 unicode -》 UTF8Buffer代表的是内存,内存是一段固定空间,产生的内存是固定大小,不能随意增加。扩容:需要动态创建一个新的内存,把内容迁移过去。创建一个长度为5的buffer,有点像数组,但是数组可以扩展,而buffer不可以扩展。还有一种声明buffer。Buffer.form。一般使用alloc来声明一个buffer,或者把字符串转换成Buffer使用。文件操作也是采用Buffer形式。buffer使用无论是二进制还是16进制,表现得东西是一样的。base64编码:base64可以放在任何路劲的链接里,可以减少请求次数。但是文件大小会变大。比如webpack中的asset/type,把一些小的文件转换成了Base64编码内嵌到了文件当中,虽然可以减少请求次数,但也增大了文件的大小。base64的来源就是将每个字节都转化为小于64的值。没有加密功能,因为规则很简单。如第一步:将buffer中每个位置的值转为二进制。如上。一个字节有八位,八位的最大值是255,有可能超过64。而base64编码是要将每个字节转化为小于64的值。所以就取每个字节的6位。6位的最大值就是2*6 - 1 = 63。也就是:第二步:将38的形式转成64的,保证每个字节小于64。将其转为十进制。第三步,通过特定的编码规则转换即完成。将我们获取到的十进制传入,因为每个字节都是小于64的,所以不超过。完成。buffer的常用方法除了form和alloc还有slice// sliceconst a = Buffer.from()const d = a.slice(0,2)d = 4console.log(d);console.log(a); 登录后复制与数组的用法相同,但是他并不是浅复制,而是直接关联在一起。改变d也会改变a。而数组的slice是浅复制。改变原始数据的值不会改变。copy将Buffer的数据拷贝到另一个数据上。const a = Buffer.from();const d = Buffer.alloc(5);a.copy(d, 1, 2, 3); //四个参数,拷贝到哪里?从d的第一个开始拷贝 a的aconsole.log(d);登录后复制copy四个参数,分别是拷贝的目标d。从d的第几个长度开始。拷贝a的第2到第3位。所以应该是 《Buffer 00 03 00 00 00 》concat用于拼接bufferBuffer.concat(arr, index)第二个参数是拼接出来的Buffer的长度,如果大于原本的长度,用00填写。Buffer.myConcat = function ( bufferList, length = bufferList.reduce((a, b) =》 a + b.length, 0)) { let bigBuffer = Buffer.alloc(length); let offset = 0; bufferList.forEach((item) =》 { //使用copy每次拷贝一份然后offset向下走。 item.copy(bigBuffer, offset); offset += item.length; }); return bigBuffer};登录后复制借助copy,逐个拷贝一份即可。文件操作fs模块有两种基本api,同步,异步。io操作,input,output,输入输出。读取时默认是buffer类型。写入的时候,默认会将内容以utf8格式写入,如果文件不存在则创建。读取的data是Buffer类型,写入的是utf8格式。这种读写适合小文件读取文件某段内容的办法fs.open用于打开一个文件。fs.read用来读取内容并且写入到buffer中。fs.write用于将内容写入某个文件之中。如上,打开了b.txt,然后用fs.wirte。五个参数,分别是fd,buffer,从buffer的第0个位置,到buffer的第0个位置,从b.txt的第0位开始写,回调函数。写入成功。这种写法也不太美观,每次都需要fs.open然后fs.read或者fs.wirte,容易造成回调地狱。流 Stream的出现源码的实现步骤:fs的CreateReadStrem是new了一个ReadStream,他是基于Stream的Readable类的,然后自己实现了_read方法。供Stream.prototype.read调用。1 内部会先open文件,然后直接直接继续读取操作,默认是调用了pause暂停。2 监听用户是否绑定了data事件,resume,是的话开始读取事件3 调用fs.read 将数据读取出来。4 调用this.push去emit data事件,然后判断是否可以读取更多再去读取。第一种: fs.readFile(需要将文件读取到磁盘中,占用内容)=》fs.wirteFile第二种: fs.open =》 fs.read =》 fs.write 回调地狱。实现读取三个字节写入三个字节。采用fs.open fs.read fs.write的方法。实现copy方法。看实现:首先创建一个三字节的Buffer。然后使用fs.open打开要读取和要写入的文件。因为我们是每三个每三个读取,所以需要采用递归方式,一直读取文件。直到读取完毕,调用回调函数。fs.read和fs.write的参数都是类似的,即fd,buffer,buffer的start,buffer的end,读取文件/写入文件的start、回调函数(err,真正读取到的个数/真正写入的个数)现在基本实现了读一部分,写一部分,但是读写的逻辑写在一起了,需要把他拆开。流 Stream模块可读流不是一下子把文件都读取完毕,而是可以控制读取的个数和读取的速率。流的概念跟fs没有关系,fs基于stream模块底层扩展了一个文件读写方法。所以fs.open,fs.read等需要的参数,createReadStream也需要、返回一个对象,获取需要监听data事件。close事件在end事件之后触发。由此可以看出:流的内部基于 fs.open fs.close fs.read fs.write以及事件机制。暂停是不再触发data事件rs.resume()是恢复。实现readStream从vscode调试源码得知实现思路:createReadStream内部new了一个ReadStream的实例,ReadStream是来自于Stream模块。做一系列参数默认后, 调用this.open方法,这个方法会去调用fs.open去打开文件,打开之后触发事件,从回调的形式发布订阅模式,然后监听事件,当发现有用户注册了data事件之后,调用fs.read,j监听open事件,在open之后再去读取文件等等。这样我们的读写逻辑就分离开了,从回调的形式变成了发布订阅模式,有利于解耦。第一步:第一步:参数初始化,并调用fs.openopen打开之后会触发open事件,注意,这里是异步的第二步: 监听用户注册的data事件,当用户注册了data事件才去调用fs.read。调用this.read的时候open还没完成。所以第一次read的时候需要判断,然后只监听一次open事件,重复打开read事件。这个end和start配合,表示读取文件从哪到哪的位置,但是end是包后的,比如上面的end为4,实际上读取到的是5。创建buffer存放读取的内容,再判断应该读取多少内容,以哪个小为准。然后打开fs.read。将读取到的buffer发布出去。再次调用this.read去继续读取。start=1,end=4,读取到2345的内容,正确。不给end,每次3个3个的读取。接着实现暂停,需要一个开关。这样就基本完成了。总结:一开始实现的的copy方法,也是利用fs.open, fs.read, fs.write等,通过回调的形式完成的,这样虽能完成,但是内聚度较高,容易形成回调地狱。而基于fs模块,和events模块,实现的可读流,可以有效的解耦刚才的代码,通过发布订阅模式,在一开始订阅事件,在每个时间点对应发布事件,然后代码执行,各司其职。open和close是文件流独有的,可读流具备:on (‘data’ | ‘end’ | ‘error’), resume, pause这些方法。相关代码:// copyconst fs = require("fs");const path = require("path");// let buf = Buffer.alloc(3);// //open打开一个文件,第一个参数是路劲,第二个参数是打开文件用途,第三个是回调函数。// fs.open(path.resolve(__dirname, "a.txt"), "r", function (err, fd) {// // fd 是file descriptor文件描述// console.log("fd", fd);// //读取a.txt的内容,并且将内容写入buf中的第0个位置到3第三个位置,从a.txt的第六个位置开始// fs.read(fd, buf, 0, 3, 3, function (err, data) {// fs.open(path.resolve(__dirname, "./b.txt"), "w", function (err, fd2) {// fs.write(fd2, buf, 0, 3, 0, function (err, data2) {// console.log("buf", buf);// console.log("data2", data2);// });// });// });// });function copy(source, target, cb) { const BUFFER_SIZE = 3; const buffer = Buffer.alloc(BUFFER_SIZE); //每次读入文件的位置 let r_offset = 0; //每次写入新文件的位置 let w_offset = 0; //读取一部分数据,写入一部分数据 //第三个参数可以是权限 权限有三个组合 rwx 可读可写可执行 r的权限是4,w的权限是2,x的权限是1 421 = 777 可写可读可执行 // 0o666表示最大的权限,默认不用写。 //读取的文件必须要存在。写入文件不存在会创建,如果文件有内容会清空。 fs.open(source, "r", function (err, fd1) { //打开写的文件 fs.open(target, "w", function (err, fd2) { //每次读取三个写入三个,回调的方式实现的功能,需要用递归 // 同步代码则可以采用while循环 function next() { fs.read( fd1, buffer, 0, BUFFER_SIZE, r_offset, function (err, bytesRed) { // bytesRed真正读取到的个数 if (err) { cb("读取失败"); return; } if (bytesRed) { //将读取到的内容写入target目标 fs.write( fd2, buffer, 0, bytesRed, w_offset, function (err, written) { if (err) retrun; // written 真正写入的个数 r_offset += bytesRed; //每次写入之后,下一次读取的内容就该往前进 w_offset += written; next(); } ); } else { //读取内容为空,结束 fs.close(fd1, () =》 {}); fs.close(fd2, () =》 {}); cb(); } } ); } next(); }); });}copy("./a.txt", "b.txt", (err, data) =》 { console.log("copy success");});登录后复制createStream的实现const EventMitter = require("events");const fs = require("fs");class ReadStream extends EventMitter { constructor(path, options = {}) { super(); this.path = path; //操作 this.flags = options.flags || "r"; this.encoding = options.encoding || null; this.autoClose = options.autoClose || true; this.start = options.start || 0; this.end = options.end || Infinity; //读取的个数,包后的,如果是1 ,就可能读取到0,1,2 this.highWaterMark = options.highWaterMark || 64 * 1024; this.emitClose = options.emitClose || false; this.offset = this.start; // 每次读取文件的位移数 this.flowing = true; //暂停继续开关 this.open(); // 文件操作,注意这个方法是异步的。 // events可以监听newListener,可以获取注册的所有事件 this.on("newListener", function (type) { if (type === "data") { //用户订阅了data事件,才去读取。 this.read(); //这时候文件还没open,fd为undefined。 } }); } pause() { //暂停 this.flowing = false; } resume() { //继续 if (!this.flowing) { this.flowing = true; this.read(); } } destory(err) { if (err) { this.emit("error", err); } if (this.autoClose) { fs.close(this.fd, () =》 { this.emit("close"); }); } } read() { //希望在open之后打开 if (typeof this.fd !== "number") { this.once("open", (fd) =》 { //之前实现的copy这段逻辑是写在了fs.open里面,换成发布订阅模式之后就可以分离出来。 this.read(); //第二次read的时候,fd有值了 }); } else { //判断每次读取多少。因为this.end是包后的,比如start = 0, end = 1, 那么读取的就是 0 , 1, 2所以需要+1 const howMutchToRead = Math.min( this.end - this.offset + 1, this.highWaterMark ); const buffer = Buffer.alloc(howMutchToRead); fs.read( this.fd, buffer, 0, howMutchToRead, this.offset, (err, byteRead) =》 { if (err) { this.destory(err); } else { if (byteRead) { //读取到文件,发布data事件,发送真正读取到的内容 this.offset += byteRead; this.emit("data", buffer.slice(0, byteRead)); this.flowing && this.read(); } else { this.emit("end"); if (this.autoClose) { this.destory(); } } } } ); } } open() { fs.open(this.path, this.flags, (err, fd) =》 { if (err) { //报错: this.destory(err); } //从回调的形式变成了发布订阅模式 this.fd = fd; this.emit("open", fd); }); }}const rs = new ReadStream("./a.txt", { flags: "r", //创建可读流。 encoding: null, //默认Buffer autoClose: true, //自动关闭,相当于读取完毕调用fs.close emitClose: true, //触发close事件 start: 0, //从文件哪里开始读取 highWaterMark: 3, //每次读取的数据个数,默认是64*1025字节。 //end: 4, // 比如这个就会读取 1到5的内容});rs.on("open", () =》 { console.log("文件打开了");});// rs.on("data", (data) =》 {// console.log("监听Data事件", data);// });// //底层还是 fs.open fs.read fs.close// const rs = fs.createReadStream("./a.txt", {// flags: "r", //创建可读流。// encoding: null, //默认Buffer// autoClose: true, //自动关闭,相当于读取完毕调用fs.close// emitClose: true, //触发close事件// start: 0, //从文件哪里开始读取// highWaterMark: 3, //每次读取的数据个数,默认是64*1025字节。// }); //返回一个对象//没监听前,是非流动模式,监听后,是流动模式。//监听data事件,并且不停触发rs.on("data", function (chunk) { console.log(chunk); //暂停 rs.pause();});rs.on("end", function () { console.log("读取完毕");});rs.on("close", function () { console.log("文件关闭");});setInterval(() =》 { console.log("一秒后"); rs.resume();}, 1000)

buffer rcl裂解红细胞原理

细胞裂解液一般都用的是含酶的,在血红细胞表面上有红细胞专属的表面抗原,当裂解液中含可以攻击特定红细胞表面抗原的酶的时候就只会造成红细胞的变形,生物通道扩大,膨胀,裂解,或者引起红细胞的变性.而不会攻击其他细胞.另外红细胞有自己的电负性,渗透脆性(通常是一些非酶细胞裂解液的突破点),悬浮稳定性,这都是区别于其他种类细胞的区别点.红细胞裂解液就是利用这些特性裂解红细胞的.另外红细胞裂解液不能达到100%准确的裂解红细胞,总会或多或少导致其他种类细胞的裂解.

关于bufferlist类的实现原理到此分享完毕,希望能帮助到您。

bufferlist类的实现原理(java缓冲流BufferWriter的原理跟提高效率原因)

本文编辑:admin
Copyright © 2022 All Rights Reserved 威海上格软件有限公司 版权所有

鲁ICP备20007704号

Thanks for visiting my site.