java io系列14之 DataInputStream(数据输入流)的认知、源码和示例

简介: 本章介绍DataInputStream。我们先对DataInputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解。 转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_14.html DataInputStream 介绍 DataInputStream 是数据输入流。

本章介绍DataInputStream。我们先对DataInputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解。

转载请注明出处:http://www.cnblogs.com/skywang12345/p/io_14.html

DataInputStream 介绍

DataInputStream 是数据输入流。它继承于FilterInputStream。
DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。

DataInputStream 函数列表

复制代码
DataInputStream(InputStream in)
final int     read(byte[] buffer, int offset, int length) final int read(byte[] buffer) final boolean readBoolean() final byte readByte() final char readChar() final double readDouble() final float readFloat() final void readFully(byte[] dst) final void readFully(byte[] dst, int offset, int byteCount) final int readInt() final String readLine() final long readLong() final short readShort() final static String readUTF(DataInput in) final String readUTF() final int readUnsignedByte() final int readUnsignedShort() final int skipBytes(int count)
复制代码

 

DataInputStream.java源码分析(基于jdk1.7.40)

View Code

说明
DataInputStream 的作用就是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。”
DataInputStream 中比较难以理解的函数就只有 readUTF(DataInput in);下面,对这个函数进行详细的介绍,其它的函数请参考源码中的注释。

readUTF(DataInput in)源码如下:

复制代码
  1 public final static String readUTF(DataInput in) throws IOException {  2 // 从“数据输入流”中读取“无符号的short类型”的值:  3 // 注意:UTF-8输入流的前2个字节是数据的长度  4 int utflen = in.readUnsignedShort();  5 byte[] bytearr = null;  6 char[] chararr = null;  7  8 // 如果in本身是“数据输入流”,  9 // 则,设置字节数组bytearr = "数据输入流"的成员bytearr  10 // 设置字符数组chararr = "数据输入流"的成员chararr  11 // 否则的话,新建数组bytearr和chararr  12 if (in instanceof DataInputStream) {  13 DataInputStream dis = (DataInputStream)in;  14 if (dis.bytearr.length < utflen){  15 dis.bytearr = new byte[utflen*2];  16 dis.chararr = new char[utflen*2];  17  }  18 chararr = dis.chararr;  19 bytearr = dis.bytearr;  20 } else {  21 bytearr = new byte[utflen];  22 chararr = new char[utflen];  23  }  24  25 int c, char2, char3;  26 int count = 0;  27 int chararr_count=0;  28  29 // 从“数据输入流”中读取数据并存储到字节数组bytearr中;从bytearr的位置0开始存储,存储长度为utflen。  30 // 注意,这里是存储到字节数组!而且读取的是全部的数据。  31 in.readFully(bytearr, 0, utflen);  32  33 // 将“字节数组bytearr”中的数据 拷贝到 “字符数组chararr”中  34 // 注意:这里相当于“预处理的输入流中单字节的符号”,因为UTF-8是1-4个字节可变的。  35 while (count < utflen) {  36 // 将每个字节转换成int值  37 c = (int) bytearr[count] & 0xff;  38 // UTF-8的每个字节的值都不会超过127;所以,超过127,则退出。  39 if (c > 127) break;  40 count++;  41 // 将c保存到“字符数组chararr”中  42 chararr[chararr_count++]=(char)c;  43  }  44  45 // 处理完输入流中单字节的符号之后,接下来我们继续处理。  46 while (count < utflen) {  47 // 下面语句执行了2步操作。  48 // (01) 将字节由 “byte类型” 转换成 “int类型”。  49 // 例如, “11001010” 转换成int之后,是 “00000000 00000000 00000000 11001010”  50 // (02) 将 “int类型” 的数据左移4位  51 // 例如, “00000000 00000000 00000000 11001010” 左移4位之后,变成 “00000000 00000000 00000000 00001100”  52 c = (int) bytearr[count] & 0xff;  53 switch (c >> 4) {  54 // 若 UTF-8 是单字节,即 bytearr[count] 对应是 “0xxxxxxx” 形式;  55 // 则 bytearr[count] 对应的int类型的c的取值范围是 0-7。  56 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:  57 /* 0xxxxxxx*/  58 count++;  59 chararr[chararr_count++]=(char)c;  60 break;  61  62 // 若 UTF-8 是双字节,即 bytearr[count] 对应是 “110xxxxx 10xxxxxx” 形式中的第一个,即“110xxxxx”  63 // 则 bytearr[count] 对应的int类型的c的取值范围是 12-13。  64 case 12: case 13:  65 /* 110x xxxx 10xx xxxx*/  66 count += 2;  67 if (count > utflen) 68 throw new UTFDataFormatException( 69 "malformed input: partial character at end"); 70 char2 = (int) bytearr[count-1]; 71 if ((char2 & 0xC0) != 0x80) 72 throw new UTFDataFormatException( 73 "malformed input around byte " + count); 74 chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | 75 (char2 & 0x3F)); 76 break; 77 78 // 若 UTF-8 是三字节,即 bytearr[count] 对应是 “1110xxxx 10xxxxxx 10xxxxxx” 形式中的第一个,即“1110xxxx” 79 // 则 bytearr[count] 对应的int类型的c的取值是14 。 80 case 14: 81 /* 1110 xxxx 10xx xxxx 10xx xxxx */ 82 count += 3; 83 if (count > utflen) 84 throw new UTFDataFormatException( 85 "malformed input: partial character at end"); 86 char2 = (int) bytearr[count-2]; 87 char3 = (int) bytearr[count-1]; 88 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) 89 throw new UTFDataFormatException( 90 "malformed input around byte " + (count-1)); 91 chararr[chararr_count++]=(char)(((c & 0x0F) << 12) | 92 ((char2 & 0x3F) << 6) | 93 ((char3 & 0x3F) << 0)); 94 break; 95 96 // 若 UTF-8 是四字节,即 bytearr[count] 对应是 “11110xxx 10xxxxxx 10xxxxxx 10xxxxxx” 形式中的第一个,即“11110xxx” 97 // 则 bytearr[count] 对应的int类型的c的取值是15 98 default: 99 /* 10xx xxxx, 1111 xxxx */ 100 throw new UTFDataFormatException( 101 "malformed input around byte " + count); 102 } 103 } 104 // The number of chars produced may be less than utflen 105 return new String(chararr, 0, chararr_count); 106 }
复制代码

说明:

(01) readUTF()的作用,是从输入流中读取UTF-8编码的数据,并以String字符串的形式返回。
(02) 知道了readUTF()的作用之后,下面开始介绍readUTF()的流程:

第1步,读取出输入流中的UTF-8数据的长度。代码如下:

int utflen = in.readUnsignedShort();

UTF-8数据的长度包含在它的前两个字节当中;我们通过readUnsignedShort()读取出前两个字节对应的正整数就是UTF-8数据的长度。

第2步,创建2个数组:字节数组bytearr 和 字符数组chararr。代码如下:

复制代码
 1 if (in instanceof DataInputStream) {
 2     DataInputStream dis = (DataInputStream)in;  3 if (dis.bytearr.length < utflen){  4 dis.bytearr = new byte[utflen*2];  5 dis.chararr = new char[utflen*2];  6  }  7 chararr = dis.chararr;  8 bytearr = dis.bytearr;  9 } else { 10 bytearr = new byte[utflen]; 11 chararr = new char[utflen]; 12 }
复制代码

首先,判断该输入流本身是不是DataInputStream,即数据输入流;若是的话,
则,设置字节数组bytearr = "数据输入流"的成员bytearr
      设置字符数组chararr = "数据输入流"的成员chararr
否则的话,新建数组bytearr和chararr。

第3步,将UTF-8数据全部读取到“字节数组bytearr”中。代码如下:

in.readFully(bytearr, 0, utflen);

注意: 这里是存储到字节数组,而不是字符数组!而且读取的是全部的数据。

第4步,对UTF-8中的单字节数据进行预处理。代码如下:

复制代码
1 while (count < utflen) {
2     // 将每个字节转换成int值 3 c = (int) bytearr[count] & 0xff; 4 // UTF-8的单字节数据的值都不会超过127;所以,超过127,则退出。 5 if (c > 127) break; 6 count++; 7 // 将c保存到“字符数组chararr”中 8 chararr[chararr_count++]=(char)c; 9 }
复制代码

UTF-8的数据是变长的,可以是1-4个字节;在readUTF()中,我们最终是将全部的UTF-8数据保存到“字符数组(而不是字节数组)”中,再将其转换为String字符串。
由于UTF-8的单字节和ASCII相同,所以这里就将它们进行预处理,直接保存到“字符数组chararr”中。对于其它的UTF-8数据,则在后面进行处理。

第5步,对“第4步 预处理”之后的数据,接着进行处理。代码如下:

复制代码
// 处理完输入流中单字节的符号之后,接下来我们继续处理。
while (count < utflen) {
    // 下面语句执行了2步操作。 // (01) 将字节由 “byte类型” 转换成 “int类型”。 // 例如, “11001010” 转换成int之后,是 “00000000 00000000 00000000 11001010” // (02) 将 “int类型” 的数据左移4位 // 例如, “00000000 00000000 00000000 11001010” 左移4位之后,变成 “00000000 00000000 00000000 00001100” c = (int) bytearr[count] & 0xff; switch (c >> 4) { // 若 UTF-8 是单字节,即 bytearr[count] 对应是 “0xxxxxxx” 形式; // 则 bytearr[count] 对应的int类型的c的取值范围是 0-7。 case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: /* 0xxxxxxx*/ count++; chararr[chararr_count++]=(char)c; break; // 若 UTF-8 是双字节,即 bytearr[count] 对应是 “110xxxxx 10xxxxxx” 形式中的第一个,即“110xxxxx” // 则 bytearr[count] 对应的int类型的c的取值范围是 12-13。 case 12: case 13: /* 110x xxxx 10xx xxxx*/ count += 2; if (count > utflen) throw new UTFDataFormatException( "malformed input: partial character at end"); char2 = (int) bytearr[count-1]; if ((char2 & 0xC0) != 0x80) throw new UTFDataFormatException( "malformed input around byte " + count); chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | (char2 & 0x3F)); break; // 若 UTF-8 是三字节,即 bytearr[count] 对应是 “1110xxxx 10xxxxxx 10xxxxxx” 形式中的第一个,即“1110xxxx” // 则 bytearr[count] 对应的int类型的c的取值是14 。 case 14: /* 1110 xxxx 10xx xxxx 10xx xxxx */ count += 3; if (count > utflen) throw new UTFDataFormatException( "malformed input: partial character at end"); char2 = (int) bytearr[count-2]; char3 = (int) bytearr[count-1]; if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) throw new UTFDataFormatException( "malformed input around byte " + (count-1)); chararr[chararr_count++]=(char)(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); break; // 若 UTF-8 是四字节,即 bytearr[count] 对应是 “11110xxx 10xxxxxx 10xxxxxx 10xxxxxx” 形式中的第一个,即“11110xxx” // 则 bytearr[count] 对应的int类型的c的取值是15 default: /* 10xx xxxx, 1111 xxxx */ throw new UTFDataFormatException( "malformed input around byte " + count); } }
复制代码

(a) 我们将下面的两条语句一起进行说明

c = (int) bytearr[count] & 0xff;
switch (c >> 4) { ... }

首先,我们必须要理解 为什么要这么做(执行上面2条语句)呢?
原因很简单,这么做的目的就是为了区分UTF-8数据是几位的;因为UTF-8的数据是1~4字节不等。

我们先看看UTF-8在1~4位情况下的格式。

--------------------+---------------------------------------------
1字节 UTF-8的通用格式  | 0xxxxxxx
2字节 UTF-8的通用格式  | 110xxxxx 10xxxxxx
3字节 UTF-8的通用格式  | 1110xxxx 10xxxxxx 10xxxxxx
4字节 UTF-8的通用格式  | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

执行 c = (int) bytearr[count] & 0xff; 和 c>>4 这2项操作之后,上面的数据变成

--------------------+---------------------------------------------
1字节 UTF-8的变换后对应的int类型值  | 00000000 00000000 00000000 00000xxx    (范围是0~7) 
2字节 UTF-8的变换后对应的int类型值  | 00000000 00000000 00000000 0000110x    (范围是12~13) 
3字节 UTF-8的变换后对应的int类型值  | 00000000 00000000 00000000 00001110    (范围是14) 
4字节 UTF-8的变换后对应的int类型值  | 00000000 00000000 00000000 00001111    (范围是15) 

为什么会是这样呢?
我们以“2字节 UTF-8的通用格式”来说明。
它的通用格式是 “110xxxxx 10xxxxxx”,我们在操作时,只会操作第1个字节,即只会操作“110xxxxx”
(a.1) 在执行 c = (int) bytearr[count] & 0xff; 时,首先将 bytearr[count] 转换成int。

“110xxxxx”

转成int类型之后,变成

“11111111 11111111 11111111 110xxxxx”

因为“110xxxxx”是负数(第1为是1),所以转换成int类型时多出来的位补1。

(a.2) 接着 c = (int) bytearr[count] & 0xff; 中,会将 “转换成int类型后的bytearr[count]” 与 “0xff”进行 逻辑与(即&) 操作。结果如下:

“00000000 00000000 00000000 110xxxxx”

(a.3) 执行 c>>4 时,会将上面的结果左移4位。得到的结果如下:

“00000000 00000000 00000000 0000110x”

(b) 上面的理解之后,swicth (c>>4) { ... } 其中的省略号部分就相当容易理解了。
我们还是以“2字节 UTF-8的通用格式”来说明。
它会执行 case 12 和 case 13;源码如下:

复制代码
count += 2;
if (count > utflen)
    throw new UTFDataFormatException( "malformed input: partial character at end"); char2 = (int) bytearr[count-1]; if ((char2 & 0xC0) != 0x80) throw new UTFDataFormatException( "malformed input around byte " + count); chararr[chararr_count++]=(char)(((c & 0x1F) << 6) | (char2 & 0x3F));
复制代码

(b.1) 由于这种情况对应的UTF-8数据是“2字节”的,因此,执行count+2;直接跳过2个字节。
(b.2) 由于chararr的元素是字符类型,而一个字符正好占2个字节;因为正好将(((c & 0x1F) << 6) | (char2 & 0x3F)); 的结果转换成char,然后保存在chararr数组中。


第6步,将字符数组转换成String字符串,并返回。代码如下:

return new String(chararr, 0, chararr_count);

示例代码

关于DataInputStream中API的详细用法,参考示例代码(DataInputStreamTest.java)

复制代码
  1 import java.io.DataInputStream;
  2 import java.io.DataOutputStream;  3 import java.io.ByteArrayInputStream;  4 import java.io.File;  5 import java.io.InputStream;  6 import java.io.FileInputStream;  7 import java.io.FileOutputStream;  8 import java.io.IOException;  9 import java.io.FileNotFoundException;  10 import java.lang.SecurityException;  11  12 /**  13  * DataInputStream 和 DataOutputStream测试程序  14  *  15  * @author skywang  16 */  17 public class DataInputStreamTest {  18  19 private static final int LEN = 5;  20  21 public static void main(String[] args) {  22 // 测试DataOutputStream,将数据写入到输出流中。  23  testDataOutputStream() ;  24 // 测试DataInputStream,从上面的输出流结果中读取数据。  25  testDataInputStream() ;  26  }  27  28 /**  29  * DataOutputStream的API测试函数  30 */  31 private static void testDataOutputStream() {  32  33 try {  34 File file = new File("file.txt");  35 DataOutputStream out =  36 new DataOutputStream(  37 new FileOutputStream(file));  38  39 out.writeBoolean(true);  40 out.writeByte((byte)0x41);  41 out.writeChar((char)0x4243);  42 out.writeShort((short)0x4445);  43 out.writeInt(0x12345678);  44 out.writeLong(0x0FEDCBA987654321L);  45  46 out.writeUTF("abcdefghijklmnopqrstuvwxyz严12");  47  48  out.close();  49 } catch (FileNotFoundException e) {  50  e.printStackTrace();  51 } catch (SecurityException e) {  52  e.printStackTrace();  53 } catch (IOException e) {  54  e.printStackTrace();  55  }  56  }  57 /**  58  * DataInputStream的API测试函数  59 */  60 private static void testDataInputStream() {  61  62 try {  63 File file = new File("file.txt");  64 DataInputStream in =  65 new DataInputStream(  66 new FileInputStream(file));  67  68 System.out.printf("byteToHexString(0x8F):0x%s\n", byteToHexString((byte)0x8F));  69 System.out.printf("charToHexString(0x8FCF):0x%s\n", charToHexString((char)0x8FCF));  70  71 System.out.printf("readBoolean():%s\n", in.readBoolean());  72 System.out.printf("readByte():0x%s\n", byteToHexString(in.readByte()));  73 System.out.printf("readChar():0x%s\n", charToHexString(in.readChar()));  74 System.out.printf("readShort():0x%s\n", shortToHexString(in.readShort()));  75 System.out.printf("readInt():0x%s\n", Integer.toHexString(in.readInt()));  76 System.out.printf("readLong():0x%s\n", Long.toHexString(in.readLong()));  77 System.out.printf("readUTF():%s\n", in.readUTF());  78  79  in.close();  80 } catch (FileNotFoundException e) {  81  e.printStackTrace();  82 } catch (SecurityException e) { 83 e.printStackTrace(); 84 } catch (IOException e) { 85 e.printStackTrace(); 86 } 87 } 88 89 // 打印byte对应的16进制的字符串 90 private static String byteToHexString(byte val) { 91 return Integer.toHexString(val & 0xff); 92 } 93 94 // 打印char对应的16进制的字符串 95 private static String charToHexString(char val) { 96 return Integer.toHexString(val); 97 } 98 99 // 打印short对应的16进制的字符串 100 private static String shortToHexString(short val) { 101 return Integer.toHexString(val & 0xffff); 102 } 103 }
复制代码

运行结果

byteToHexString(0x8F):0x8f
charToHexString(0x8FCF):0x8fcf
readBoolean():true
readByte():0x41
readChar():0x4243
readShort():0x4445
readInt():0x12345678
readLong():0xfedcba987654321
readUTF():abcdefghijklmnopqrstuvwxyz严12

结果说明
(01) 查看file.txt文本。16进制的数据显示如下:


001f 对应的int值是31。它表示的含义是后面的UTF-8数据的长度。字符串“abcdefghijklmnopqrstuvwxyz严12”中字母 “ab...xyz”的长度是26,“严”对应的UTF-8数据长度是3;“12”长度是2。总的长度=26+3+2=31。

(02) 返回byte对应的16进制的字符串
源码如下:

private static String byteToHexString(byte val) {
    return Integer.toHexString(val & 0xff); }

想想为什么代码是:

return Integer.toHexString(val & 0xff);

而不是

return Integer.toHexString(val);

我们先看看 byteToHexString((byte)0x8F); 在上面两种情况下的输出结果。
return Integer.toHexString(val & 0xff); 对应的输出是“0xffffff8f
return Integer.toHexString(val); 对应的输出是“0x8f
为什么会这样呢?
原因其实很简单,就是“byte类型转换成int类型”导致的问题。
byte 类型的0x8F是一个负数,它对应的2进制是10001111;将一个负数的byte转换成int类型时,执行的是有符号转型(新增位都填充符号位的数 字)。0x8F的符号位是1,因为将它转换成int时,填充“1”;转型后的结果(2进制)是11111111 11111111 11111111 10001111,对应的16进制为0xffffff8f。
因为当我们执行Integer.toHexString(val);时,返回的就是0xffffff8f。
在Integer.toHexString(val & 0xff)中,相当于0xffffff8f & 0xff,得到的结果是0x8f。

(03) 返回char和short对应的16进制的字符串
“返回char对应的16进制的字符串”对应的源码如下:

private static String charToHexString(char val) {
    return Integer.toHexString(val); }

“返回short对应的16进制的字符串”对应源码如下:

private static String shortToHexString(short val) {
    return Integer.toHexString(val & 0xffff); }

比较上面的两个函数,为什么一个是 “val” ,而另一个是 “val & 0xffff”?
通过(02)的分析,我们类似的推出为什么 “返回short对应的16进制的字符串” 要执行“val & 0xffff”。
但是,为什么 “返回char对应的16进制的字符串” 要执行 “val” 即可。原因也很简单,java中char是无符号类型,占两个字节。将char转换为int类型,执行的是无符号转型,新增为都填充0。

 

相关文章
|
12天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
46 7
|
12天前
|
前端开发 JavaScript Java
java常用数据判空、比较和类型转换
本文介绍了Java开发中常见的数据处理技巧,包括数据判空、数据比较和类型转换。详细讲解了字符串、Integer、对象、List、Map、Set及数组的判空方法,推荐使用工具类如StringUtils、Objects等。同时,讨论了基本数据类型与引用数据类型的比较方法,以及自动类型转换和强制类型转换的规则。最后,提供了数值类型与字符串互相转换的具体示例。
|
23天前
|
数据采集 人工智能 Java
Java产科专科电子病历系统源码
产科专科电子病历系统,全结构化设计,实现产科专科电子病历与院内HIS、LIS、PACS信息系统、区域妇幼信息平台的三级互联互通,系统由门诊系统、住院系统、数据统计模块三部分组成,它管理了孕妇从怀孕开始到生产结束42天一系列医院保健服务信息。
28 4
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
68 2
|
4天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
42 13
|
18天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
47 12
|
12天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
19天前
|
JSON Java 程序员
Java|如何用一个统一结构接收成员名称不固定的数据
本文介绍了一种 Java 中如何用一个统一结构接收成员名称不固定的数据的方法。
23 3
|
1月前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
1月前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
61 2