文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java编程思想学习笔记2 - 字节流和字符流

2023-06-02 23:25

关注

《Thinking in Java》第18章的内容是相当丰富精彩的,也在网络学习参考了很多前辈们的笔记,个人由于能力有限(毕竟和大神Bruce Eckel的能力相差甚远),将这一章的内容分三个部分来写,希望能够慢慢品味和领悟Java IO的精粹:1. 起步:File类

令我很吊胃口的一件事情是,当我翻开圣经,想拜读Java IO的精髓时,Eckel告诉我,在学习真正用于流读写数据的类之前,让我们先学习如何处理文件目录问题(潜台词仿佛在说,对不起,菜鸟,你得从基础班开始!)

File类:Java中File既能代表一个特定文件的名称,又能代表一个目录下的一组文件(相当于Files)

我们要学会的是,如何从一个目录下筛选出我们想要的文件?可以通过目录过滤器FilenameFilter+正则表达式实现对文件的筛选:

import java.io.File; import java.io.FilenameFilter; import java.util.Arrays; import java.util.regex.Pattern;  class DirFilter implements FilenameFilter { private Pattern pattern; public DirFilter (String regex) {        pattern = pattern.compile(regex);    } public boolean accept(File dir, String name) { return pattern.matcher(name).matches();    }} public class DirList { public static void main(String[] args) {        File path = new File(".");        String[] list; if(args.length == 0)            list = path.list(); else list = path.list(new DirFilter(args[0]));        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); for(String dirItem: list)            System.out.println(dirItem);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

这个用匿名内部类的方式来实现是更合适的,DirFilter在内部实现,使程序变的更小巧灵活:

* 通过内部类方式实现的目录列表器 */ package c18; import java.io.File; import java.io.FilenameFilter; import java.util.Arrays; import java.util.regex.Pattern; public class DirList2 { public static FilenameFilter filter(final String regex) { //内部类 return new FilenameFilter() { private Pattern pattern = Pattern.compile(regex); @Override public boolean accept(File dir, String name) { return pattern.matcher(name).matches();            }        };          } public static void main(String[] args) {        File path = new File(".");        String[] list; if(args.length == 0)            list = path.list(); else list = path.list(new DirFilter(args[0]));        Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); for(String dirItem: list)            System.out.println(dirItem);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

针对一些文件集上的常用操作我们可以封装成一个工具类,比如:

 package c18; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; public final class Directory {  public static File[] local(File dir, final String regex) { return dir.listFiles(new FilenameFilter() { private Pattern pattern = Pattern.compile(regex); @Override public boolean accept(File dir, String name) { return pattern.matcher(name).matches();            }        });    } // 方法重载 public static File[] local(String path, final String regex) { return local(new File(path), regex);    }  public static class TreeInfo implements Iterable<File> { public List<File> files = new ArrayList<File>(); public List<File> dirs = new ArrayList<File>(); // public Iterator<File> iterator() { return files.iterator();        } void addAll(TreeInfo other) {            files.addAll(other.files);            dirs.addAll(other.dirs);        } public String toString() { return "dirs:" + PPrint.pformat(dirs) + "\n\nfiles: " + PPrint.pformat(files);        }    }  public static TreeInfo walk(String start, String regex) { return recurseDirs(new File(start), regex);    } public static TreeInfo walk(File start, String regex) { return recurseDirs(start, regex);    } public static TreeInfo walk(File start) { return recurseDirs(start, ".*");    } public static TreeInfo walk(String start) { return recurseDirs(new File(start), ".*");    }  static TreeInfo recurseDirs(File startDir, String regex) {        TreeInfo result = new TreeInfo(); for(File item : startDir.listFiles()) { if (item.isDirectory()) { //持有目录 result.dirs.add(item);            } else if(item.getName().matches(regex)) //持有普通文件 result.files.add(item);        } return result;    } public static void main(String[] args) { // PPrint.pprint(Directory.walk(".").dirs); // for(File file : Directory.local(".", "T.*"))            System.out.println(file);           }}  class PPrint { public static String pformat(Collection<?> c) { if(c.size() == 0) return "[]";        StringBuilder result = new StringBuilder("["); for(Object elem : c) { if(c.size()!=1)                result.append("\n ");            result.append(elem);        } if(c.size()!=1)            result.append("\n ");        result.append("]"); return result.toString();    } public static void pprint(Collection<?> c) {        System.out.println(pformat(c));    } public static void pprint(Object c) {        System.out.println(Arrays.asList(c));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

2. 字节流和字符流概述

Java IO中处理字节流和字符流输入输出的基类和派生类繁多,本节主要做两件事情:(1)给出类的关系图;(2)回答一个问题,为什么字节流和字符流要分开处理,为什么既要有Reader/Writer,又保留InputStream/OutputStream? 
Java 1.0中是只存在InputStream/OutputStream的,设计Reader/Writer3. 装饰者模式和适配器模式

Java IO中分别用到了装饰者模式和适配者模式。

装饰者模式(Decorator)

装饰模式(Decorator)又称为包装模式(Wrapper),通过创建一个装饰(包装)对象,来装饰真实的对象。

Java I/O类库需要多种不同功能的组合,这正是使用装饰器模式的理由所在。为什么不使用继承而采用装饰器模式呢?如果说Java IO的各种组合是通过继承方式来实现的话,那么每一种组合都需要一个类,这样就会出现大量重复性的问题。而通过装饰器来实现组合,恰恰避免了这个问题。

对于字节流而言,FilterInputStream和FilterOutputStream是用来提供装饰器接口以控制特定输入\输出的两个类。需要注意的是,对于字符流而言,同样用到的是装饰者模式,但是有一点不同,Reader体系中的FilterRead类和InputStream体系中的FilterInputStream的功能不同,它不再是装饰者。class BufferedReader extends Reader BufferedInputStream:public class BufferedInputStream extends FilterInputStream

举一个例子如下所示,其中BufferedReader是装饰对象,FileReader是被装饰对象。

package c18; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class BufferedInputFile { public static String read(String filename) throws IOException{ //BufferedReader是装饰对象,FileReader是被装饰对象 BufferedReader in = new BufferedReader(new FileReader(filename));        String s;        StringBuilder sb = new StringBuilder(); while ((s = in.readLine())!=null) {            sb.append(s + "\n");                    }        in.close(); return sb.toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

适配者模式(Adapter)

适配者模式又可以分为类适配方式(类似多继承)和对象适配方式。而Java IO中使用的是对象适配方式。我们以FileOutputStream为例,可以看到源码中,FileOutputStream继承了OutputStream类型,同时持有一个对FileDiscriptor对象的引用。这是一个将FileDiscriptor接口适配成OutputStream接口形式的对象形适配器模式。

public class FileOutputStream extends OutputStream { private FileDescriptor fd; public FileOutputStream(FileDescriptor fdObj) {        SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException();        } if (security != null) {            security.checkWrite(fdObj);        }        fd = fdObj;        fd.incrementAndGetUseCount();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4. 基于字节流的IO操作

本节主要介绍基于字节流的输入InputStream和输出OutputStream,以及用于实现装饰模式的FilterInputStream和FilterOutputStream。

字节流输入InputStream

InputStream的作用是表示从不同数据源产生输入的类:

下面列表展现了InputStream的派生类,所有的派生类都要联合装饰类FilterInputStreamByteArrayInputStream

  public ByteArrayInputStream(byte buf[]) { this.buf = buf; this.pos = 0; this.count = buf.length;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

举个例子:

 package c18; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; public class FormattedMemoryInput { public static void main(String[] args) throws IOException{ try { //DataInputStream面向字节的装饰类 DataInputStream in = new DataInputStream( new ByteArrayInputStream( //BufferedInputFile.read上例中已经实现了 BufferedInputFile.read("D:/workspace/java_learning/" + "Java_Learning/src/c18/DirList2.java").getBytes())); while (true) { //readByte按字节输出 System.out.println((char)in.readByte());            }        } catch (EOFException e) {            System.err.println("End of stream");        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

StringBufferInputStream

需要注意的是StringBufferInputStream已经过时了,JDK给出的过时原因如下:

This class does not properly convert characters into bytes. As of JDK 1.1, the preferred way to create a stream from a string is via theFileInputStream

  • 作用:用于从文件中读取信息
  • 构造器参数:表示文件路径的字符串、File对象、FileDescriptor对象
//String public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null);} //File public FileInputStream(File file) throws FileNotFoundException {    String name = (file != null ? file.getPath() : null);    SecurityManager security = System.getSecurityManager(); if (security != null) {        security.checkRead(name);    } if (name == null) { throw new NullPointerException();        }    fd = new FileDescriptor();        fd.incrementAndGetUseCount();    open(name);} //FileDescriptor public FileInputStream(FileDescriptor fdObj) {    SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException();    } if (security != null) {        security.checkRead(fdObj);    }    fd = fdObj;    fd.incrementAndGetUseCount();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

PipedInputStream

  • 作用:产生写入PipedOutputStream中的数据,实现“管道化”
  • 构造器参数:PipedOutputStream
public PipedInputStream(PipedOutputStream src, int pipeSize) throws IOException {     initPipe(pipeSize);     connect(src);}
  • 1
  • 2
  • 3
  • 4
  • 5

管道流可以实现两个线程之间,二进制数据的传输。管道流就像一条管道,一端输入数据,别一端则输出数据。通常要分别用两个不同的线程来控制它们。(这里埋个伏笔,目前笔者对多线程掌握还不够成熟,等到后面学习Java并发中会继续提到PipedIntputStream/PipedOutputStream)

SequenceInputStream

  • 作用:将两个或多个InputStream对象转换成单一InputStream
  • 构造器参数:两个InputStream或一个容器Enumeration
public SequenceInputStream(Enumeration<? extends InputStream> e) { this.e = e; try {        nextStream();    } catch (IOException ex) { // This should never happen throw new Error("panic");    }} public SequenceInputStream(InputStream s1, InputStream s2) {    Vector  v = new Vector(2);    v.addElement(s1);    v.addElement(s2);    e = v.elements(); try {        nextStream();    } catch (IOException ex) { // This should never happen throw new Error("panic");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

举两个例子(源于网络):

import java.io.*; import java.util.*;  class SequenceDemo1  { public static void main(String[] args)throws IOException      {          Vector<FileInputStream> v = new Vector<FileInputStream>();          v.add(new FileInputStream("c:\\1.txt"));          v.add(new FileInputStream("c:\\2.txt"));          v.add(new FileInputStream("c:\\3.txt"));          Enumeration<FileInputStream> en = v.elements();          SequenceInputStream sis = new SequenceInputStream(en);          FileOutputStream fos = new FileOutputStream("c:\\4.txt"); byte[] buf = new byte[1024]; int len = 0; while((len=sis.read(buf))!=-1)          {              fos.write(buf,0,len);          }          fos.close();          sis.close();      }  } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
import java.io.*; import java.util.*;  class SequenceDemo2  { public static void main(String[] args)throws IOException      {          InputStream is1 = null;          InputStream is2 = null;          OutputStream os = null;          SequenceInputStream sis = new null;          is1 = new FileInputStream("d:"+File.separator+"a.txt");          is2 = new FileInputStream("d:"+File.separator+"b.txt");          os = new FileOutputStream("d:"+File.separator+"ab.txt");          sis = new SequenceInputStream(is1,is2); int temp = 0; while((temp)=sis.read()!=-1)          {              os.write(temp);          }          sis.close();          is1.close();          is2.close();          os.close();      }  } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

另外,还有FilterInputStream,它是抽象类,作为“装饰器”的接口,笔者将单独用一节来详述。


字节流输出OutputStream

OutputStream的作用是表示从不同数据源产生输入的类:

  • 字节数组
  • 文件
  • 管道

下面是OutputStream的派生类。同样,所有的派生类都要联合装饰类FilterOutputStreamByteArrayOutputStream

与ByteArrayInputStream相对应,public ByteArrayOutputStream() { this(32);} public ByteArrayOutputStream(int size) { if (size < 0) { throw new IllegalArgumentException("Negative initial size: " + size); } buf = new byte[size];}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

FileOutputStream

与FileInputStream相对应,PipedOutputStream

  • 作用:任何写入其中的信息都会自动作为相关PipedInputStream输出,实现“管道化”
  • 构造器参数:PipedInputStream

装饰器FilterInputStream/FilterOutputStream

FilterInputStream/FilterOutputStream同样也是InputStream/OutputStreamDataInputStream/DataOutputStream

  • 作用:搭配使用,可以按照可移植的方式从流读取基本数据类型(int、char、long等)
  • 构造器参数:InputStream/OutputStream
public DataInputStream(InputStream in) { super(in);}
  • 1
  • 2
  • 3
public DataOutputStream(OutputStream out) { super(out);}
  • 1
  • 2
  • 3

BufferedInputStream/BufferedOutputStream

  • 作用:使用缓冲器输入输出
  • 构造器参数:InputStream/OutputStream

举个例子:

 package c18; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class BinaryFile { public static byte[] read(File bFile) throws IOException {        BufferedInputStream bf = new BufferedInputStream( new FileInputStream(bFile)); try { byte [] data = new byte[bf.available()];            bf.read(data); return data;        } finally {            bf.close();        }    } public static byte[] read(String bFile) throws IOException { return read(new File(bFile).getAbsoluteFile());    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

LineNumberInputStream

  • 作用:跟踪输入流中的行号,可调用getLineNumber()和setLineNumber(int)
  • 构造器参数:InputStream

该类已经被废弃了,推荐使用字符流的类来操作。

PushbackInputStream

  • 作用:回推字节,读取字节,然后再将它们返回到流中。回推操作有unread()方法实现
  • 构造器参数:InputStream

PushbackInputStream类实现了这一思想,提供了一种机制,可以“偷窥”来自输入流的内容而不对它们进行破坏。

//允许将size大小的字节回推回流 public PushbackInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("size <= 0");        } this.buf = new byte[size]; this.pos = size;} //每次允许回推一个字节 public PushbackInputStream(InputStream in) { this(in, 1);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
package c18; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.PushbackInputStream; public class PushbackInputStreamDemo { public static void main(String[] args) { byte[] arrByte = new byte[1024]; byte[] byteArray = new byte[]{'H', 'e', 'l', 'l', 'o',};  InputStream is = new ByteArrayInputStream(byteArray);          PushbackInputStream pis = new PushbackInputStream(is, 10); try { for (int i = 0; i < byteArray.length; i++) {                arrByte[i] = (byte) pis.read();                System.out.print((char) arrByte[i]);             } //换行 System.out.println(); byte[] b = {'W', 'o', 'r', 'l', 'd'};  pis.unread(b); for (int i = 0; i < byteArray.length; i++) {arrByte[i] = (byte) pis.read();                System.out.print((char) arrByte[i]);             }          } catch (Exception ex) {             ex.printStackTrace();          }       }}输出结果:HelloWorld
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

PrintStream

  • 作用:PrintStream可以方便地输出各种类型的数据(而不仅限于byte型)的格式化表示形式。
  • 构造器参数:InputStream

需要注意的是,与其他输出流不同, PrintStream 永远不会抛出 IOException ;它产生的IOException会被自身的函数所捕获并设置错误标记, 用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。5. 基于字符流的IO操作

设计Reader和Writer继承层次结构主要是为了国际化的16位Unicode字符编码。下表展示了Reader/Writer package c18; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; public class TextFile { public static String read(String filename) { StringBuilder sb = new StringBuilder(); try { BufferedReader in = new BufferedReader(new FileReader( new File(filename).getAbsoluteFile())); try { String s; while ((s = in.readLine())!= null) { sb.append(s); sb.append("\n"); } } finally { in.close(); } } catch (IOException e) { throw new RuntimeException(e); } return sb.toString(); } public static void write(String filename, String text) { try { PrintWriter out = new PrintWriter( new File(filename).getAbsoluteFile()); try { out.print(text); } finally { out.close(); } } catch (IOException e) { throw new RuntimeException(e); } } }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

需要注意以下几点:

参考文献

感谢并致敬以下前辈的文章:

  • 《Thinking in Java》 作者:Bruce Eckel 
  • Java之美[从菜鸟到高手演变]之Java中的IO 
    http://blog.csdn.net/zhangerqing/article/details/8466532 
    作者:终点 
  • Java IO最详解 
    http://blog.csdn.net/yczz/article/details/38761237 
    作者:yczz
  • java中的IO操作总结(一) 
    http://www.cnblogs.com/nerxious/archive/2012/12/15/2818848.html 
    作者:Nerxious
  • java.io包中的字符流(下) 适配器模式和InputStreamReader/OutputStreamWriter  
    http://www.molotang.com/articles/782.html 
    作者: 三石·道
  • SequenceInputStream合并流 
    http://blog.csdn.net/xuefeng1009/article/details/6955707 
    作者:xuefeng1009
  • 探究java IO之PushbackInputStream类 
    http://my.oschina.net/fhd/blog/345011 
    作者:柳哥
  • JAVA中常用IO流类: PrintStream和PrintWriter 
    http://blog.csdn.net/caixiexin/article/details/6719627 
    作者:caixiexin
  • java io系列16之 PrintStream(打印输出流)详解 
    http://www.cnblogs.com/skywang12345/p/io_16.html 
    作者:skywang12345
阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯