0%

参考:

Java IO 考点及资料整理

深入分析 Java I/O 的工作机制

java.io包主要涉及文件访问、网络数据流、内存缓冲访问、线程内部通信(管道)、缓冲、过滤、解析、读写文件(Readers/Writers)、读写基本类型数据(long,int etc)、读写对象等输入输出。

字节流

InputStream字节输入流

  • ByteArrayInputStream
  • FileInputStream
  • FilterInputStream
    • BufferedInputStream
    • DataInputStream
    • LineNumberInputStream
    • PushbackInputStream
  • ObjectInputStream
  • PipedInputStream
  • SequenceInputStream
  • StringBufferStream

InputStream

1
2
3
4
5
6
7
8
9
10
11
public abstract class InputStream implements Closeable {
int available();
void close();
void mark(int readlimit);
boolean markSupported();
int read(byte[] buffer);
abstract int read();
int read(byte[] buffer, int offset, int length);
synchronized void reset();
long skip(long byteCount);
}

ByteArrayInputStream

ByteArrayInputStream 是字节数组输入流。它继承于InputStream。
它包含一个内部缓冲区,该缓冲区包含从流中读取的字节;通俗点说,它的内部缓冲区就是一个字节数组,而ByteArrayInputStream本质就是通过字节数组来实现的。
我们都知道,InputStream通过read()向外提供接口,供它们来读取字节数据;而ByteArrayInputStream 的内部额外的定义了一个计数器,它被用来跟踪 read() 方法要读取的下一个字节。

1
2
3
4
5
6
7
8
9
10
11
12
// 构造函数;
ByteArrayInputStream(byte[] buf);
ByteArrayInputStream(byte[] buf, int offset, int length);

synchronized int available();
void close();
synchronized void mark(int readlimit);
boolean markSupported();
synchronized int read();
synchronized int read(byte[] buffer, int offset, int length);
synchronized void reset();
synchronized long skip(long byteCount);

ByteArrayInputStream实际上是通过“字节数组”去保存数据。

  • 通过ByteArrayInputStream(byte buf[]) 或 ByteArrayInputStream(byte buf[], int offset, int length) ,我们可以根据buf数组来创建字节流对象。
  • read()的作用是从字节流中“读取下一个字节”。
  • read(byte[] buffer, int offset, int length)的作用是从字节流读取字节数据,并写入到字节数组buffer中。offset是将字节写入到buffer的起始位置,length是写入的字节的长度。
  • markSupported()是判断字节流是否支持“标记功能”。它一直返回true。
  • mark(int readlimit)的作用是记录标记位置。记录标记位置之后,某一时刻调用reset()则将“字节流下一个被读取的位置”重置到“mark(int readlimit)所标记的位置”;也就是说,reset()之后再读取字节流时,是从mark(int readlimit)所标记的位置开始读取。

PipedInputStream

在java中,PipedOutputStreamPipedInputStream分别是管道输出流和管道输入流。
它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。

PipedInputStream类中属性有closedByWriterclosedByReaderconnectedreadSidewriteSidebufferinout,分别代码被写入线程关闭连接,被读取线程关闭连接,是否连接,读取线程,写入线程,内部缓冲byte数组,写入数,读取数。通过协调读取和写入线程,一方面从PipedOutputStream接收数据,一方面当调用read()read(byte[], int, int)方法是从内部缓存byte数组中读取数据。

ObjectInputStream

ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。

当我们需要读取ObjectOutputStream存储的“基本数据或对象”时,可以创建“文件输入流”对应的ObjectInputStream,进而读取出这些“基本数据或对象”。

注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!

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
// 构造函数;
ObjectInputStream(InputStream input);

//public函数;
int available();
void close();
void defaultReadObject();
int read(byte[] buffer, int offset, int length);
int read();
boolean readBoolean();
byte readByte();
char readChar();
double readDouble();
ObjectInputStream.GetField readFields();
float readFloat();
void readFully(byte[] dst);
void readFully(byte[] dst, int offset, int byteCount);
int readInt();
String readLine();
long readLong();
final Object readObject();
short readShort();
String readUTF();
Object readUnshared();
int readUnsignedByte();
int readUnsignedShort();
synchronized void registerValidation(ObjectInputValidation object, int priority);
int skipBytes(int length);

FileInputStream

FileInputStream 是文件输入流,它继承于InputStream。
通常,我们使用FileInputStream从某个文件中获得输入字节。

1
2
3
4
5
6
7
8
9
10
11
FileInputStream(File file); // 构造函数1:创建“File对象”对应的“文件输入流”
FileInputStream(FileDescriptor fd); // 构造函数2:创建“文件描述符”对应的“文件输入流”
FileInputStream(String path); // 构造函数3:创建“文件(路径为path)”对应的“文件输入流”

int available(); // 返回“剩余的可读取的字节数”或者“skip的字节数”
void close(); // 关闭“文件输入流”
FileChannel getChannel(); // 返回“FileChannel”
final FileDescriptor getFD(); // 返回“文件描述符”
int read(); // 返回“文件输入流”的下一个字节
int read(byte[] buffer, int byteOffset, int byteCount); // 读取“文件输入流”的数据并存在到buffer,从byteOffset开始存储,存储长度是byteCount。
long skip(long byteCount); // 跳过byteCount个字节

FilterInputStream

FilterInputStream 的作用是用来“封装其它的输入流,并为它们提供额外的功能”。它的常用的子类有BufferedInputStream和DataInputStream。

BufferedInputStream的作用就是为“输入流提供缓冲功能,以及mark()和reset()功能”。

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

FilterInputStream很明显使用了装饰器模式 ( Decorator )

BufferedInputStream

BufferedInputStream 是缓冲输入流。它继承于FilterInputStream

BufferedInputStream 的作用是为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持“mark()标记”和“reset()重置方法”。

BufferedInputStream 本质上是通过一个内部缓冲区数组实现的。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到我们读完输入流数据位置。

1
2
3
4
5
6
7
8
9
10
11
BufferedInputStream(InputStream in);
BufferedInputStream(InputStream in, int size);

synchronized int available();
void close();
synchronized void mark(int readlimit);
boolean markSupported();
synchronized int read();
synchronized int read(byte[] buffer, int offset, int byteCount);
synchronized void reset();
synchronized long skip(long byteCount);

DataInputStream

DataInputStream 是数据输入流。它继承于FilterInputStream。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);

OutputStream字节输出流

  • ByteArrayOutputStream
  • FileOutputStream
  • FilterOutputStream
    • BufferedOutputStream
    • DataOutputStream
    • PrintStream
  • ObjectOutputStream
  • PipedOutputStream

OutputStream

1
2
3
4
5
6
7
public abstract class OutputStream implements Closeable, Flushable {
void close();
void flush();
void write(byte[] buffer, int offset, int count);
void write(byte[] buffer);
abstract void write(int oneByte);
}

ByteArrayOutputStream

ByteArrayOutputStream 是字节数组输出流。它继承于OutputStream。
ByteArrayOutputStream 中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长,“新容量”的初始化 = “旧容量”x2。可使用 toByteArray() 和 toString() 获取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 构造函数;
ByteArrayOutputStream();
ByteArrayOutputStream(int size);

void close();
synchronized void reset();
int size();
synchronized byte[] toByteArray();
String toString(int hibyte);
String toString(String charsetName);
String toString();
synchronized void write(byte[] buffer, int offset, int len);
synchronized void write(int oneByte);
synchronized void writeTo(OutputStream out);

ByteArrayOutputStream实际上是将字节数据写入到“字节数组”中去。

  • 通过ByteArrayOutputStream()创建的“字节数组输出流”对应的字节数组大小是32。
  • 通过ByteArrayOutputStream(int size) 创建“字节数组输出流”,它对应的字节数组大小是size。
  • write(int oneByte)的作用将int类型的oneByte换成byte类型,然后写入到输出流中。
  • write(byte[] buffer, int offset, int len) 是将字节数组buffer写入到输出流中,offset是从buffer中读取数据的起始偏移位置,len是读取的长度。
  • writeTo(OutputStream out) 将该“字节数组输出流”的数据全部写入到“输出流out”中。

PipedOutputStream

在java中,PipedOutputStreamPipedInputStream分别是管道输出流和管道输入流。
它们的作用是让多线程可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用。
使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStream中写入数据,这些数据会自动的发送到与PipedOutputStream对应的PipedInputStream中,进而存储在PipedInputStream的缓冲中;此时,线程B通过读取PipedInputStream中的数据。就可以实现,线程A和线程B的通信。

PipedOutputStream类中持有PipedInputStream实例,调用write/flush等方法时均对PipedInputStream实例进行操作。

ObjectOutputStream

ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。

创建“文件输出流”对应的ObjectOutputStream对象,该ObjectOutputStream对象能提供对“基本数据或对象”的持久存储。

注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!

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
// 构造函数;
ObjectOutputStream(OutputStream output);

// public函数;
void close();
void defaultWriteObject();
void flush();
ObjectOutputStream.PutField putFields();
void reset();
void useProtocolVersion(int version);
void write(int value);
void write(byte[] buffer, int offset, int length);
void writeBoolean(boolean value);
void writeByte(int value);
void writeBytes(String value);
void writeChar(int value);
void writeChars(String value);
void writeDouble(double value);
void writeFields();
void writeFloat(float value);
void writeInt(int value);
void writeLong(long value);
final void writeObject(Object object);
void writeShort(int value);
void writeUTF(String value);
void writeUnshared(Object object);

FileOutputStream

FileOutputStream 是文件输出流,它继承于OutputStream。

通常,我们使用FileOutputStream 将数据写入 File 或 FileDescriptor 的输出流。

1
2
3
4
5
6
7
8
9
10
11
FileOutputStream(File file); // 构造函数1:创建“File对象”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(File file, boolean append); // 构造函数2:创建“File对象”对应的“文件输入流”;指定“追加模式”。
FileOutputStream(FileDescriptor fd); // 构造函数3:创建“文件描述符”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(String path); // 构造函数4:创建“文件(路径为path)”对应的“文件输入流”;默认“追加模式”是false,即“写到输出的流内容”不是以追加的方式添加到文件中。
FileOutputStream(String path, boolean append); // 构造函数5:创建“文件(路径为path)”对应的“文件输入流”;指定“追加模式”。

void close(); // 关闭“输出流”
FileChannel getChannel(); // 返回“FileChannel”
final FileDescriptor getFD(); // 返回“文件描述符”
void write(byte[] buffer, int byteOffset, int byteCount); // 将buffer写入到“文件输出流”中,从buffer的byteOffset开始写,写入长度是byteCount。
void write(int oneByte); // 写入字节oneByte到“文件输出流”中

FilterOutputStream

FilterOutputStream 的作用是用来“封装其它的输出流,并为它们提供额外的功能”。它主要包括BufferedOutputStream, DataOutputStream和PrintStream。

BufferedOutputStream的作用就是为“输出流提供缓冲功能”。

DataOutputStream 是用来装饰其它输出流,将DataOutputStream和DataInputStream输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。

PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

FilteroutputStream很明显使用了装饰器模式 ( Decorator )

BufferedOutputStream

BufferedOutputStream 是缓冲输出流。它继承于FilterOutputStream。

BufferedOutputStream 的作用是为另一个输出流提供“缓冲功能”。

1
2
3
4
5
6
7
BufferedOutputStream(OutputStream out); 
BufferedOutputStream(OutputStream out, int size);

synchronized void close();
synchronized void flush();
synchronized void write(byte[] buffer, int offset, int length);
synchronized void write(int oneByte);

BufferedOutputStream的源码非常简单,这里就BufferedOutputStream的思想进行简单说明:BufferedOutputStream通过字节数组来缓冲数据,当缓冲区满或者用户调用flush()函数时,它就会将缓冲区的数据写入到输出流中。

DataOutputStream

DataOutputStream 是数据输出流。它继承于FilterOutputStream。

DataOutputStream 是用来装饰其它输出流,将DataOutputStream和DataInputStream输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。

PrintStream

PrintStream 是打印输出流,它继承于FilterOutputStream。

PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

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

另外,PrintStream 提供了自动flush 和 字符集设置功能。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数。

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
/* 
* 构造函数
*/
// 将“输出流out”作为PrintStream的输出流,不会自动flush,并且采用默认字符集
// 所谓“自动flush”,就是每次执行print(); , println(); , write(); 函数,都会调用flush(); 函数;
// 而“不自动flush”,则需要我们手动调用flush(); 接口。
PrintStream(OutputStream out);
// 将“输出流out”作为PrintStream的输出流,自动flush,并且采用默认字符集。
PrintStream(OutputStream out, boolean autoFlush);
// 将“输出流out”作为PrintStream的输出流,自动flush,采用charsetName字符集。
PrintStream(OutputStream out, boolean autoFlush, String charsetName);
// 创建file对应的FileOutputStream,然后将该FileOutputStream作为PrintStream的输出流,不自动flush,采用默认字符集。
PrintStream(File file);
// 创建file对应的FileOutputStream,然后将该FileOutputStream作为PrintStream的输出流,不自动flush,采用charsetName字符集。
PrintStream(File file, String charsetName);
// 创建fileName对应的FileOutputStream,然后将该FileOutputStream作为PrintStream的输出流,不自动flush,采用默认字符集。
PrintStream(String fileName);
// 创建fileName对应的FileOutputStream,然后将该FileOutputStream作为PrintStream的输出流,不自动flush,采用charsetName字符集。
PrintStream(String fileName, String charsetName);

// 将“字符c”追加到“PrintStream输出流中”
PrintStream append(char c);
// 将“字符序列从start(包括); 到end(不包括); 的全部字符”追加到“PrintStream输出流中”
PrintStream append(CharSequence charSequence, int start, int end);
// 将“字符序列的全部字符”追加到“PrintStream输出流中”
PrintStream append(CharSequence charSequence);
// flush“PrintStream输出流缓冲中的数据”,并检查错误
boolean checkError();
// 关闭“PrintStream输出流”
synchronized void close();
// flush“PrintStream输出流缓冲中的数据”。
// 例如,PrintStream装饰的是FileOutputStream,则调用flush时会将数据写入到文件中
synchronized void flush();
// 根据“Locale值(区域属性); ”来格式化数据
PrintStream format(Locale l, String format, Object... args);
// 根据“默认的Locale值(区域属性); ”来格式化数据
PrintStream format(String format, Object... args);
// 将“float数据f对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(float f);
// 将“double数据d对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(double d);
// 将“字符串数据str”写入到“PrintStream输出流”中,print实际调用的是write函数
synchronized void print(String str);
// 将“对象o对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(Object o);
// 将“字符c对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(char c);
// 将“字符数组chars对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(char[] chars);
// 将“long型数据l对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(long l);
// 将“int数据i对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(int i);
// 将“boolean数据b对应的字符串”写入到“PrintStream输出流”中,print实际调用的是write函数
void print(boolean b);
// 将“数据args”根据“Locale值(区域属性); ”按照format格式化,并写入到“PrintStream输出流”中
PrintStream printf(Locale l, String format, Object... args);
// 将“数据args”根据“默认Locale值(区域属性); ”按照format格式化,并写入到“PrintStream输出流”中
PrintStream printf(String format, Object... args);
// 将“换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println();
// 将“float数据对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(float f);
// 将“int数据对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(int i);
// 将“long数据对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(long l);
// 将“对象o对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(Object o);
// 将“字符数组chars对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(char[] chars);
// 将“字符串str+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
synchronized void println(String str);
// 将“字符c对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(char c);
// 将“double数据对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(double d);
// 将“boolean数据对应的字符串+换行符”写入到“PrintStream输出流”中,println实际调用的是write函数
void println(boolean b);
// 将数据oneByte写入到“PrintStream输出流”中。oneByte虽然是int类型,但实际只会写入一个字节
synchronized void write(int oneByte);
// 将“buffer中从offset开始的length个字节”写入到“PrintStream输出流”中。
void write(byte[] buffer, int offset, int length);

System.out.Println

  • out的定义

    一个PrintStream实例

    1
    2
    3
    public final class System {
    public final static PrintStream out = null;
    }
  • out的初始化

    1
    2
    3
    4
    5
    6
    7
    pubilc final class System {
    private static void initializeSystemClass() {
    //省略其他代码...
    FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
    setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
    }
    }
  • FileDescriptor.out是啥

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public final class FileDescriptor {
    private int fd;
    /**
    * A handle to the standard output stream. Usually, this file
    * descriptor is not used directly, but rather via the output stream
    * known as {@code System.out}.
    * @see java.lang.System#out
    */
    public static final FileDescriptor out = new FileDescriptor(1);
    private FileDescriptor(int fd) {
    this.fd = fd;
    useCount = new AtomicInteger();
    }
    }

    FileDescriptor.out是一个FileDescriptor实例,其fd=1,正是标准输出的标识符。

  • setOut0方法

    setOut0()是一个native本地方法,其功能是设置out为传入的参数

    1
    2
    3
    public final class System {
    private static native void setOut0(PrintStream out);
    }
  • System.out.println

    所以System.out其实是标准输出的PrintStream实例,而println方法正是调用的PrintStream的println方法。这样PrintStream中的多个输出方法均可以通过System.out调用。

字符流

Reader字符输入流

  • BufferedReader
    • LineNumberReader
  • CharArrayReader
  • FilterReader
  • PushbackReader
  • InputStreamReader
    • FileReader
  • PipedReader
  • StringReader

Reader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Reader implements Readable, Closeable {
protected Reader();
protected Reader(Object lock);
public int read(Charbuffer target) throws IOException;
public int read() throws IOException;
public int read(char cbuf[]) throws IOException;
abstract public int read(char cbuf[], int off, int len) throws IOException;
public long skip(long n) throws IOException;
public boolean read() throws IOException;
public boolean markSupported();
public void mark(int readAheadLimit) throws IOException;
public void reset() throws IOException;
abstract public void close() throws IOException;
}

CharArrayReader

CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位!

1
2
3
4
5
6
7
8
9
10
11
CharArrayReader(char[] buf);
CharArrayReader(char[] buf, int offset, int length);

void close();
void mark(int readLimit);
boolean markSupported();
int read();
int read(char[] buffer, int offset, int len);
boolean ready();
void reset();
long skip(long charCount);

PipedReader

PipedReader 是字符管道输入流,它继承于Writer。

PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedWriter和PipedReader配套使用。

1
2
3
4
5
6
7
8
9
10
public PipedReader();
public PipedReader(int pipeSize);
public PipedReader(PipedWriter src) throws IOException;
public PipedReader(PipedWriter src, int pipeSize) throws IOException;

public void close() throws IOException;
public void connect(PipedWriter src) throws IOException;
public synchronized int read() throws IOException;
public synchronized int read(char cbuf[], int off, int len) throws IOException;
public synchronized boolean ready() throws IOException;

InpustreamReader

InputStreamReader和OutputStreamWriter 是字节流通向字符流的桥梁:它使用指定的 charset 读写字节并将其解码为字符。

InputStreamReader 的作用是将“字节输入流”转换成“字符输入流”。它继承于Reader。

1
2
3
4
5
6
7
8
9
10
public InputStreamReader(InputStream in);
public InputStreamReader(InputStream in, Charset cs);
public InputStreamReader(InputStream in, CharsetDecoder dec);
public InputStreamReader(InputStream in, String charsetName) throws UnsupportedEncodingException;

public void close() throws IOException;
public String getEncoding();
public int read(char cbuf[], int offset, int length) throws IOException;
public int read() throws IOException;
public boolean ready() throws IOException;

InputStreamReader内部持有一个StreamDecoder sd的私有属性,大部分的功能由StreamDecoder实现。

FileReader

FileReader 是用于读取字符流的类,它继承于InputStreamReader。要读取原始字节流,请考虑使用 FileInputStream。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package java.io;

public class FileReader extends InputStreamReader {

public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}

public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}

public FileReader(FileDescriptor fd) {
super(new FileInputStream(fd));
}
}

BufferedReader

BufferedReader 是缓冲字符输入流。它继承于Reader。

BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。

1
2
3
4
5
6
7
8
9
10
11
12
BufferedReader(Reader in);
BufferedReader(Reader in, int size);

void close();
void mark(int markLimit);
boolean markSupported();
int read();
int read(char[] buffer, int offset, int length);
String readLine();
boolean ready();
void reset();
long skip(long charCount);

Writer字符输出流

  • BufferedWriter
  • CharArrayWriter
  • FilterWriter
  • OutputStreamWriter
    • FileWriter
  • PipedWriter
  • PrintWriter
  • StringWriter

Writer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Writer implements Appendable, Closeable, Flushable {
protected Writer();
protected Writer(Object lock);
public void write(int c) throws IOException;
public void write(char cbuf[]) throws IOException;
abstract public void write(char cbuf[], int off, int len) throws IOException;
public void write(String str) throws IOException;
public void write(String str, int off, int len) throws IOException;
public Writer append(Chrequence csq) throws IOException;
public Writer apend(CharSequence csq, int start, int end) throws IOException;
public Write append(char c) throws IOException;
abstract public void flush() throws IOException;
abstract public void close() throws IOException;
}

CharArrayWriter

CharArrayReader 用于写入数据符,它继承于Writer。操作的数据是以字符为单位!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CharArrayWriter();
CharArrayWriter(int initialSize);

CharArrayWriter append(CharSequence csq, int start, int end);
CharArrayWriter append(char c);
CharArrayWriter append(CharSequence csq);
void close();
void flush();
void reset();
int size();
char[] toCharArray();
String toString();
void write(char[] buffer, int offset, int len);
void write(int oneChar);
void write(String str, int offset, int count);
void writeTo(Writer out);

PipedWrite

PipedWriter 是字符管道输出流,它继承于Writer。

PipedWriter和PipedReader的作用是可以通过管道进行线程间的通讯。在使用管道通信时,必须将PipedWriter和PipedReader配套使用。

1
2
3
4
5
6
7
8
public PipedWriter();
public PipedWriter(PipedReader snk) throws IOException;

public void close() throws IOException;
public synchronized void connect(PipedReader snk) throws IOException;
public synchronized void flush() throws IOException;
public void write(char cbuf[], int off, int len) throws IOException;
public void write(int c) throws IOException;

OutputStreamWriter

InputStreamReader和OutputStreamWriter 是字节流通向字符流的桥梁:它使用指定的 charset 读写字节并将其解码为字符。

OutputStreamWriter 的作用是将“字节输出流”转换成“字符输出流”。它继承于Writer。

1
2
3
4
5
6
7
8
9
10
11
public OutputStreamWriter(OutputStream out);
public OutputStreamWriter(OutputStream out, Charset cs);
public OutputStreamWriter(OutputStream out, CharsetEecoder enc);
public OutputStreamWriter(OutputStream out, String charsetName) throws UnsupportedEncodingException;

public void close() throws IOException;
public void flush() throws IOException;
public String getEncoding();
public int write(char cbuf[], int offset, int length) throws IOException;
public int write(int c) throws IOException;
public boolean write(String str, int off, int len) throws IOException;

OutputStreamWriter内部持有一个StreamEecoder se的私有属性,大部分的功能由StreamDecoder实现。

FileWriter

FileWriter 是用于写入字符流的类,它继承于OutputStreamWriter。要写入原始字节流,请考虑使用 FileOutputStream。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package java.io;

public class FileWriter extends OutputStreamWriter {

public FileWriter(String fileName) throws IOException {
super(new FileOutputStream(fileName));
}

public FileWriter(String fileName, boolean append) throws IOException {
super(new FileOutputStream(fileName, append));
}

public FileWriter(File file) throws IOException {
super(new FileOutputStream(file));
}

public FileWriter(File file, boolean append) throws IOException {
super(new FileOutputStream(file, append));
}

public FileWriter(FileDescriptor fd) {
super(new FileOutputStream(fd));
}
}

BufferedWriter

BufferedWriter 是缓冲字符输出流。它继承于Writer。
BufferedWriter 的作用是为其他字符输出流添加一些缓冲功能。

1
2
3
4
5
6
7
8
9
10
// 构造函数
BufferedWriter(Writer out);
BufferedWriter(Writer out, int sz);

void close(); // 关闭此流,但要先刷新它。
void flush(); // 刷新该流的缓冲。
void newLine(); // 写入一个行分隔符。
void write(char[] cbuf, int off, int len); // 写入字符数组的某一部分。
void write(int c); // 写入单个字符。
void write(String s, int off, int len); // 写入字符串的某一部分。

PrintWriter

PrintWriter 是字符类型的打印输出流,它继承于Writer。

PrintStream 用于向文本输出流打印对象的格式化表示形式。它实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。

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
PrintWriter(OutputStream out);
PrintWriter(OutputStream out, boolean autoFlush);
PrintWriter(Writer wr);
PrintWriter(Writer wr, boolean autoFlush);
PrintWriter(File file);
PrintWriter(File file, String csn);
PrintWriter(String fileName);
PrintWriter(String fileName, String csn);

PrintWriter append(char c);
PrintWriter append(CharSequence csq, int start, int end);
PrintWriter append(CharSequence csq);
boolean checkError();
void close();
void flush();
PrintWriter format(Locale l, String format, Object... args);
PrintWriter format(String format, Object... args);
void print(float fnum);
void print(double dnum);
void print(String str);
void print(Object obj);
void print(char ch);
void print(char[] charArray);
void print(long lnum);
void print(int inum);
void print(boolean bool);
PrintWriter printf(Locale l, String format, Object... args);
PrintWriter printf(String format, Object... args);
void println();
void println(float f);
void println(int i);
void println(long l);
void println(Object obj);
void println(char[] chars);
void println(String str);
void println(char c);
void println(double d);
void println(boolean b);
void write(char[] buf, int offset, int count);
void write(int oneChar);
void write(char[] buf);
void write(String str, int offset, int count);
void write(String str);

File

File

File 是“文件”和“目录路径名”的抽象表示形式。

File 直接继承于Object,实现了Serializable接口和Comparable接口。实现Serializable接口,意味着File对象支持序列化操作。而实现Comparable接口,意味着File对象之间可以比较大小;File能直接被存储在有序集合(如TreeSet、TreeMap中)。

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
// 静态成员
public static final String pathSeparator; // 路径分割符":"
public static final char pathSeparatorChar; // 路径分割符':'
public static final String separator; // 分隔符"/"
public static final char separatorChar; // 分隔符'/'

// 构造函数
File(File dir, String name);
File(String path);
File(String dirPath, String name);
File(URI uri);

// 成员函数
boolean canExecute(); // 测试应用程序是否可以执行此抽象路径名表示的文件。
boolean canRead(); // 测试应用程序是否可以读取此抽象路径名表示的文件。
boolean canWrite(); // 测试应用程序是否可以修改此抽象路径名表示的文件。
int compareTo(File pathname); // 按字母顺序比较两个抽象路径名。
boolean createNewFile(); // 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
static File createTempFile(String prefix, String suffix); // 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
static File createTempFile(String prefix, String suffix, File directory); // 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
boolean delete(); // 删除此抽象路径名表示的文件或目录。
void deleteOnExit(); // 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
boolean equals(Object obj); // 测试此抽象路径名与给定对象是否相等。
boolean exists(); // 测试此抽象路径名表示的文件或目录是否存在。
File getAbsoluteFile(); // 返回此抽象路径名的绝对路径名形式。
String getAbsolutePath(); // 返回此抽象路径名的绝对路径名字符串。
File getCanonicalFile(); // 返回此抽象路径名的规范形式。
String getCanonicalPath(); // 返回此抽象路径名的规范路径名字符串。
long getFreeSpace(); // 返回此抽象路径名指定的分区中未分配的字节数。
String getName(); // 返回由此抽象路径名表示的文件或目录的名称。
String getParent(); // 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
File getParentFile(); // 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。
String getPath(); // 将此抽象路径名转换为一个路径名字符串。
long getTotalSpace(); // 返回此抽象路径名指定的分区大小。
long getUsableSpace(); // 返回此抽象路径名指定的分区上可用于此虚拟机的字节数。
int hashCode(); // 计算此抽象路径名的哈希码。
boolean isAbsolute(); // 测试此抽象路径名是否为绝对路径名。
boolean isDirectory(); // 测试此抽象路径名表示的文件是否是一个目录。
boolean isFile(); // 测试此抽象路径名表示的文件是否是一个标准文件。
boolean isHidden(); // 测试此抽象路径名指定的文件是否是一个隐藏文件。
long lastModified(); // 返回此抽象路径名表示的文件最后一次被修改的时间。
long length(); // 返回由此抽象路径名表示的文件的长度。
String[] list(); // 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
String[] list(FilenameFilter filter); // 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。
File[] listFiles(); // 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
File[] listFiles(FileFilter filter); // 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
File[] listFiles(FilenameFilter filter); // 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
static File[] listRoots(); // 列出可用的文件系统根。
boolean mkdir(); // 创建此抽象路径名指定的目录。
boolean mkdirs(); // 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
boolean renameTo(File dest); // 重新命名此抽象路径名表示的文件。
boolean setExecutable(boolean executable); // 设置此抽象路径名所有者执行权限的一个便捷方法。
boolean setExecutable(boolean executable, boolean ownerOnly); // 设置此抽象路径名的所有者或所有用户的执行权限。
boolean setLastModified(long time); // 设置此抽象路径名指定的文件或目录的最后一次修改时间。
boolean setReadable(boolean readable); // 设置此抽象路径名所有者读权限的一个便捷方法。
boolean setReadable(boolean readable, boolean ownerOnly); // 设置此抽象路径名的所有者或所有用户的读权限。
boolean setReadOnly(); // 标记此抽象路径名指定的文件或目录,从而只能对其进行读操作。
boolean setWritable(boolean writable); // 设置此抽象路径名所有者写权限的一个便捷方法。
boolean setWritable(boolean writable, boolean ownerOnly); // 设置此抽象路径名的所有者或所有用户的写权限。
String toString(); // 返回此抽象路径名的路径名字符串。
URI toURI(); // 构造一个表示此抽象路径名的 file: URI。
URL toURL(); // 已过时。 此方法不会自动转义 URL 中的非法字符。建议新的代码使用以下方式将抽象路径名转换为 URL:首先通过 toURI 方法将其转换为 URI,然后通过 URI.toURL 方法将 URI 装换为 URL。

FileDescriptor

FileDescriptor 是“文件描述符”。
FileDescriptor 可以被用来表示开放文件、开放套接字等。
以FileDescriptor表示文件来说:当FileDescriptor表示某文件时,我们可以通俗的将FileDescriptor看成是该文件。但是,我们不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作。

FileDescriptor 类中三个类属性in/out/err,分别为标准输入/标准输出/标准错误,Java提供了 System.in/System.out/System.error对应这三个属性,以简化对其的使用。

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

/**
* A handle to the standard input stream. Usually, this file
* descriptor is not used directly, but rather via the input stream
* known as {@code System.in}.
*
* @see java.lang.System#in
*/
public static final FileDescriptor in = standardStream(0);

/**
* A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as {@code System.out}.
* @see java.lang.System#out
*/
public static final FileDescriptor out = standardStream(1);

/**
* A handle to the standard error stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as {@code System.err}.
*
* @see java.lang.System#err
*/
public static final FileDescriptor err = standardStream(2);

RandomAccessFile

RandomAccessFile 是随机访问文件(包括读/写)的类。它支持对文件随机访问的读取和写入,即我们可以从指定的位置读取/写入文件数据。
需要注意的是,RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类;它也不同于FileInputStream和FileOutputStream。 FileInputStream 只能对文件进行读操作,而FileOutputStream 只能对文件进行写操作;但是,RandomAccessFile 同时支持文件的读和写,并且它支持随机访问。

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
public class RandomAccessFile implements DataOutput, DataInput, Closeable {

RandomAccessFile(File file, String mode);
RandomAccessFile(String fileName, String mode);

void close();
synchronized final FileChannel getChannel();
final FileDescriptor getFD();
long getFilePointer();
long length();
int read(byte[] buffer, int byteOffset, int byteCount);
int read(byte[] buffer);
int read();
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 String readUTF();
final int readUnsignedByte();
final int readUnsignedShort();
void seek(long offset);
void setLength(long newLength);
int skipBytes(int count);
void write(int oneByte);
void write(byte[] buffer, int byteOffset, int byteCount);
void write(byte[] buffer);
final void writeBoolean(boolean val);
final void writeByte(int val);
final void writeBytes(String str);
final void writeChar(int val);
final void writeChars(String str);
final void writeDouble(double val);
final void writeFloat(float val);
final void writeInt(int val);
final void writeLong(long val);
final void writeShort(int val);
final void writeUTF(String str);
}

RandomAccessFile共有4种模式:”r”, “rw”, “rws”和”rwd”。

1
2
3
4
"r"    以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。
"rws" 打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。
"rwd" 打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。

“rw”, “rws”, “rwd” 的区别。
当操作的文件是存储在本地的基础存储设备上时(如硬盘, NandFlash等),”rws” 或 “rwd”, “rw” 才有区别。
当模式是 “rws” 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]” 或 “修改文件元数据(如文件的mtime)”时,都会将这些改变同步到基础存储设备上。
当模式是 “rwd” 并且 操作的是基础存储设备上的文件;那么,每次“更改文件内容[如write()写入数据]”时,都会将这些改变同步到基础存储设备上。
当模式是 “rw” 并且 操作的是基础存储设备上的文件;那么,关闭文件时,会将“文件内容的修改”同步到基础存储设备上。至于,“更改文件内容”时,是否会立即同步,取决于系统底层实现。

参考:红黑树(一)之 原理和算法详细介绍

R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

二叉查找树

二叉查找树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:

  • 若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 任意结点的左、右子树也分别为二叉查找树。
  • 没有键值相等的结点(no duplicate nodes)。

因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(lgn).(至于n个结点的二叉树高度为lgn的证明,可参考算法导论 第12章 二叉查找树 第12.4节)。

但二叉树若退化成了一棵具有n个结点的线性链后,则此些操作最坏情况运行时间为O(n)。后面我们会看到一种基于二叉查找树-红黑树,它通过一些性质使得树相对平衡,使得最终查找、插入、删除的时间复杂度最坏情况下依然为O(lgn)。

特性

  • 每个节点或者是黑色,或者是红色。
  • 根节点是黑色。
  • 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
  • 如果一个节点是红色的,则它的子节点必须是黑色的。
  • 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树

要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。

红黑树

红黑树的应用比较广泛,主要是用它来存储有序的数据,它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。

红黑树的旋转

当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。

为了继续保持红黑树的性质,我们可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后,继续保持它的性质或平衡。

树的旋转,分为左旋和右旋

左旋

红黑树左旋

对x进行左旋,意味着”将x变成一个左节点”。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 《算法导论》伪代码
LEFT-ROTATE(T, x)
y ← right[x] // 前提:这里假设x的右孩子为y。下面开始正式操作
right[x] ← left[y] // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子
p[left[y]] ← x // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x
p[y] ← p[x] // 将 “x的父亲” 设为 “y的父亲”
if p[x] = nil[T]
then root[T] ← y // 情况1:如果 “x的父亲” 是空节点,则将y设为根节点
else if x = left[p[x]]
then left[p[x]] ← y // 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
else right[p[x]] ← y // 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子”
left[y] ← x // 将 “x” 设为 “y的左孩子”
p[x] ← y // 将 “x的父节点” 设为 “y”

右旋

红黑树右旋

对x进行右旋,意味着”将x变成一个右节点”。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 《算法导论》伪代码
RIGHT-ROTATE(T, y)
x ← left[y] // 前提:这里假设y的左孩子为x。下面开始正式操作
left[y] ← right[x] // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为y的左孩子
p[right[x]] ← y // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y
p[x] ← p[y] // 将 “y的父亲” 设为 “x的父亲”
if p[y] = nil[T]
then root[T] ← x // 情况1:如果 “y的父亲” 是空节点,则将x设为根节点
else if y = right[p[y]]
then right[p[y]] ← x // 情况2:如果 y是它父节点的右孩子,则将x设为“y的父节点的左孩子”
else left[p[y]] ← x // 情况3:(y是它父节点的左孩子) 将x设为“y的父节点的左孩子”
right[x] ← y // 将 “y” 设为 “x的右孩子”
p[y] ← x // 将 “y的父节点” 设为 “x”

红黑树的插入

将一个节点插入到红黑树中,需要执行哪些步骤呢?首先,将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;最后,通过旋转和重新着色等方法来修正该树,使之重新成为一颗红黑树。

阅读全文 »

参考:在 windows10 安裝 bash& oh-my-zsh

Win7 without wsl

  • 下载hyper: windows版本

  • 中文显示问题

    修改.hyper.js 里面 TermCSS 改成:termCSS: '.wc-node{width: 16px !important}',或者termCSS: '.wc-node.unicode-node{width: 1em}',后面一种修改更推荐

  • hyper-material-theme

    修改.hyper.js以下内容:

    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
    plugins: ['hyper-material-theme'],

    colors: {..},

    MaterialTheme: {

    // Set the theme variant,

    // OPTIONS: 'Darker', 'Palenight', ''

    theme: 'Darker',

    // [Optional] Set the rgba() app background opacity, useful when enableVibrance is true

    // OPTIONS: From 0.1 to 1

    backgroundOpacity: '1',

    // [Optional] Set the accent color for the current active tab

    accentColor: '#64FFDA',

    // [Optional] Mac Only. Need restart. Enable the vibrance and blurred background

    // OPTIONS: 'dark', 'ultra-dark', 'bright'

    // NOTE: The backgroundOpacity should be between 0.1 and 0.9 to see the effect.

    vibrancy: 'dark'

    },

Win10 with wsl

  • 启用wsl
  • 程序和功能
  • 启用或关闭windows功能
  • 启用适用于Linux的windows子系统
  • 应用商店搜索wsl下载ubuntu并配置账号
  • 配置hyper启动后登录wsl
    1
    2
    3
    shell: 'C:\Windows\System32\cmd.exe', <---------------- shell: '',

    shellArgs: ['--login', '-i', '/c wsl'], <----------------- shellArgs: ['--login'],
阅读全文 »

数据库

数据库三范式

  1. 第一范式:表中不要重复意义的列,每列的值应当有原子性,不可再拆分。
  2. 第二范式 : 数据库表中的每一列都要和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
  3. 第三范式:数据库中的非键列必须相互之前没有关系,完全独立。如果改变一列中的值需要改变另外一列的值,那就是违反了第三范式。

Mysql

  • DECIMAL精度高于FLOATDOUBLE
  • FULLTEXT查找通常比LIKE查询要好一些。TEXT类型字段可以利用缓存,而LIKE不行。
  • 如果创建的主键没有任何其他意义和目的,就称其为代理主键

Java基础

多态

多态主要是通过继承来实现的。参数不同,编译时多态,称为重载。子类覆盖父类方法,称为覆写。

ClassLoader类加载器

  • 双亲委派机制

    java.lang.ClassLoaderloadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

Java内存模型

  • Heap堆

    对象,数组,及对象的实例变量。

    堆的优点是动态分配内存大小,生存期也不必事先告诉编译器。缺点是速度较慢。

  • Stack栈

    在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配,方法,局部变量等。

    栈的优点是存取速度比堆快,仅次于CPU中的寄存器,另外栈数据可以共享。缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

Java正则

1
2
3
4
5
6
7
String text = "gogo";
String patternStr = ".g.";
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(text);
boolean isExist = matcher.matches();
boolean isFound = matcher.find();
String str = matcher.group();

Java IO

字节流

  • 字节输入流InputStream
    • FileInputStream/ObjectInputStream/ByteArrayInputStream/BufferedInputStream/StringBufferStream
  • 字节输出流OutputStream
    • FileOutputStream/ObjectOutputStream/ByteArrayOutputStream/BufferedOutputStream
1
2
3
4
5
6
7
8
9
10
11
12
Writer w = new BufferedWriter(new FileWriter(filePath, false));
w.write("hello world");
w.flush();
w.close();

InputStream is = new BufferedInputStream(new FileInputStream(filePath));
byte[] b = new byte[2048];
int a = 0;
while ((a = is.read(b)) != -1) {
System.out.print(new String(b, 0, a));
}
is.close();

字符流

  • 字符输入流Reader
    • StringReader/BufferedRead/FileReader
  • 字符输出流Writer
    • StringWriter/BufferedWriter/FileWriter

Java异常处理

  • Throwable
    • Error
    • Exception
      • RuntimeException

      • IOException

Java集合

  • 接口:Collection(包括List,Set)/Map
  • 类:ArrayList,HashSet,LinkedList,HashMap,LinkedHashMap,WeakHashMap,ConcurrentHashMap
  • ArrayList,数组,允许Null
  • LinkedList,有序,双向链表,允许Null
  • HashSet,哈希,链表,允许Null
  • HashMap,哈希,数组,链表,允许Null键和Null
  • LinkedHashMap,有序,双向链表,允许Null键和Null值,可实现LRUcache,根据最后使用来排序
  • WeakHashMap
  • ConcurrentHashMap,同步,最多支持16并发写

进阶复习

参考:我的阿里之路+Java面经考点

阅读全文 »

Git工作流程

  • 分类

    • Git flow
    • Github flow
    • Gitlab flow
  • 共同点:

    功能驱动:需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch)。完成开发后,该分支就合并到主分支,然后被删除。

Git flow

项目存在2个长期分支:

  • 主分支master[存放对外发布的版本]
  • 开发分支develop[日常开发的分支]

项目存在3种短期分支:

  • 功能分支(feature branch)
  • 补丁分支(hotfix branch)
  • 预发分支(release branch)

一旦完成开发,它们就会被合并进developmaster,然后被删除。

优点:

  • 清晰可控

缺点:

  • 相对复杂,需要同时维护2个长期分支。大多数工具都将master当作默认分支,可开发在develop分支,导致要经常切换分支。
  • 基于版本发布,目标是一段时间的产出一个新版本。如果是持续发布的项目,代码一有变动,就部署一次。这时masterdevelop分支差别不大,没必要维护2个长期分支。

Github flow

Github flow是git flow的简化版,用于持续发布

只有1个长期分支master,用起来非常简单。

官方推荐流程:

第一步:根据需求,从master拉出新分支,不区分功能分支或补丁分支。

第二步:新分支开发完成后,或者需要讨论的时候,就向master发起一个pull request(简称PR)。

第三步:Pull Request既是一个通知,让别人注意到你的请求,又是一种对话机制,大家一起评审和讨论你的代码。对话过程中,你还可以不断提交代码。

第四步:一旦你的Pull Request通过评审,并通过测试,就可以部署在生产上,如果出现问题,可以用现有的master分支代码进行回退。

第四步:如果部署在生产上的代码没有问题,那么你的Pull Request将被接受,合并进master。一旦合并(merge),Pull Request就会生成一条commit记录,方便以后搜索与查找。

优点:

  • 简单
  • 适用于持续发布的产品

缺点:

  • master分支的更新必须与产品的发布一致。但有时候代码合并进master,并不代码它能立刻发布。比如苹果APP审核要时间,或者有些公司有发布窗口。导致线上版本落后于master分支。此时,需要新建一个production分支跟踪线上版本。

Gitlab flow

Gitlab flow 是 Git flow 与 Github flow 的综合。它吸取了两者的优点,既有适应不同开发环境的弹性,又有单一主分支的简单和便利。它是 Gitlab.com 推荐的做法。

上游优先upsteam first

  • 即存在一个主分支master,它是所有其他分支的上游。只有上游分支采纳的代码变化,才能应用到其他分支。

持续发布:

  • master分支:开发环境
  • pre-production分支:预发环境
  • production分支:生产环境

开发分支是预发分支的”上游”,预发分支又是生产分支的”上游”。代码的变化,必须由”上游”向”下游”发展。比如,生产环境出现了bug,这时就要新建一个功能分支,先把它合并到master,确认没有问题,再cherry-pickpre-production,这一步也没有问题,才进入production

只有紧急情况,才允许跳过上游,直接合并到下游分支。

版本发布:

  • master分支:主分支
  • 2-3-stable分支:稳定版本分支

只有修补bug,才允许将代码合并到这些分支,并且此时要更新小版本号(通过打tag方式)。

个人开发

建议Gitlab flow

持续集成(Continuuous integration,CI)

本文为这篇文章笔记:持续集成是什么? - 阮一峰的网络日志

  • 定义:频繁地(一天多次)将代码集成到主干。

  • 好处:

    • 快速发现错误。每完成一点更新,就集成到主干。
    • 防止分支大幅偏离主干。
  • 目的:让产品可以快速迭代,同时还能保持高质量。

  • 措施:代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

  • 流程:

    • 提交代码(commit)

    • 第一轮测试

      • 自动化,通过代码仓库的钩子(hook)
      • 至少跑一遍单元测试(针对函数或模块的测试)
    • 构建(将源码转换为可运行的实际代码)

      • 代码合并进主干,可以交付
      • 构建工具
        • Jenkins
        • Travis
    • 第二轮测试

      • 全面测试,单元测试和集成测试都要跑
      • 有条件,也要做端对端测试(从用户界面直达数据库的全链路测试)
      • 自动化为主,少数无法自动化的,人工跑。
    • 部署

    • 回滚(如果有问题)

持续交付(Continuous delivery,CD)

  • 定义:频繁地将软件的新版本,交付给质量团队或用户,以供评审。如果评审通过,就进入生产阶段。

持续部署(Continuous deployment CD)

  • 定义:代码通过评审以后,自动部署到生产环境。

AMQP

  • AMQP ( Advance Message Queuing Protocal ) ,高级消息队列协议。是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦。
  • AMQP的主要特征是面向消息、队列、路由( 包括点对点和发布/订阅 )、可靠性、安全。
  • RabbitMQ是一个开源的 AMQP 实现。服务端用 Erlang 语言编写,支持多种客户端,如PythonJavaC等。用于在分布式系统中存储转发消息。

RabbitMQ

  • 原理图

    rabbitmq

  • 概念

    • server ( broker ) 服务器端

      接受客户端连接,实现 AMQP 消息队列和路由功能的进程

    • producer 生产者

      用来创建消息,然后发消息到代理服务器 ( RabbitMQ )。消息包括有效载荷和标签。

    • consumer 消费者

    • Virtual Host

      这是一个虚拟概念,类似于权限控制组,一个Vitual Host里面可以有若干个Exchange和Queue,但是权限控制的最小粒度是Vitual Host。

    • Exchange 交换机/交易所

      所有消息都是发送给它的.由它调度给相应队列。

      接收生产者发送的消息,并根据Binding规则将消息路由给服务器中的队列。ExchangeType决定了Exchange路由消息的行为。在RabbitMQ中,ExchangeType有directFanoutTopic三种。

    • Queue 队列

      RabbitMQ内部的消息缓冲器,用于存储还未被消费者消费的消息。

    • Message 消息

      HeaderBody组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、由哪个Message Queue接受、优先级是多少等。Body是真正传输的数据。

    • Binding 绑定规则

      将Exchange和Queue绑定起来,通过Routing Key (路由键)。

    • Routing Key 路由关键字

      exchange根据这个关键字进行消息投递。

    • Connection 连接

      一个连接可以创建多个通道.多个通道共享该连接的通道线程池。

    • Channel 通道

      一个通道通常对应一个生产者或一个消费者,否则可能出现bug,通道可以创建若干消费者,所有被创建的消费者共享connection的消费者线程池。

    • 所以,对于消息的过滤,我们首先是指定交易所,然后指定队列和路由。如果需要进一步细分,就可以使用主题。

  • 使用过程

    • 消费端
      • 消费端连接到消息队列服务器,此时建立一个Connection (连接)
      • 打开一个Channel
      • 消费端声明一个Exchange,并设置相关属性
      • 消费端声明一个Queue,并设置相关属性
      • 消费端使用Routing Key,在Exchange和Queue之间建立绑定关系
    • 生产端
      • 生产端连接到消息队列服务器,此时建立一个Connection(连接)或使用消费端的Connection
      • 打开一个Channel
      • 生产端声明一个Exchange,并设置相关属性
      • 生产端使用Routing Key投递消息到Exchange
      • Exchange接收到消息后,根据消息的Routing key和已经设置的binding,进行消息路由(ExchangeType起作用),将消息投递到一个或多个队列中

设计模式

分类

创建型模式(5)
  • 工厂方法模式 ( Factory Method )
  • 抽象工厂模式 ( Abstract Factory )
  • 单例模式 ( Singleton )
  • 建造者模式 ( Builder )
  • 原型模式 ( Prototype )
结构型模式(7)
  • 适配器模式 ( Adapter )
  • 装饰器模式 ( Decorator )
  • 代理模式 ( Proxy )
  • 外观模式 ( Facade )
  • 桥接模式 ( Bridge )
  • 组合模式 ( Composite )
  • 享元模式 ( Flyweight )
行为型模式(11)
  • 策略模式 ( Strategy )
  • 模板方法模式 ( Template Method )
  • 观察者模式 ( Observer )
  • 迭代器模式 ( Iterator )
  • 责任链模式 ( Chain of Responsibility )
  • 命令模式 ( Command )
  • 备忘录模式 ( Memento )
  • 中介者模式 ( Mediator )
  • 解释器模式 ( Interpreter )
  • 访问者模式 ( Visitor )
  • 状态模式 ( State )

七大原则

  • 单一职责原则(Single Responsibility Principle)

    单一职责原则表示一个模块的组成元素之间的功能相关性。从软件变化的角度来看,就一个类而言,应该仅有一个让它变化的原因;通俗地说,即一个类只负责一项职责。

  • 开闭原则( Open Close principle )

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

  • 里氏替换原则 ( Liskov Substitution Principle )

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP 是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

  • 依赖倒转原则 ( Dependence Inversion Principle )

    这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

  • 接口隔离原则 ( Interface Segregation Principle )

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还有一个降低类之间的耦合度的意思。

  • 迪米特法则 (最少知道原则 ) ( Demeter Principle )

    一个实体应当尽量少的与其他实体之间发生相互作用,使系统功能模块相对独立。

  • 合成复用原则 ( Composite Reuse Principle )

    尽量使用合成/聚合的方式,而不是使用继承。

简单(4)

  • 工厂方法 factory类->静态方法
  • 抽象工厂 多个factory类
  • 单例 懒汉/饿汉/双重检查
  • 建造者 builder

有用的(12)

  • 适配器 adapter
  • 装饰器 decorator|wrapper
  • 代理 proxy 静态代理|动态代理
  • 外观 Facade
  • 桥接 bridge
  • 组合 composite
  • 策略 strategy
  • 模板方法 template
  • 观察者 observer
  • 责任链 chain of responsibility
  • 命令 command
  • 状态 state

很少使用(7)

  • 原型 prototype
  • 享元 flyweight
  • 迭代器 iterator
  • 备忘录 memento
  • 中介者 mediator
  • 解释器 interperter
  • 访问者 visitor

工厂方法模式 ( Factory Method )

建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

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
public interface Sender {
public void send();
}

public class MailSender implements Sender {
@Override
public void send() {
//...
}
}

public class SmsSender implements Sender {
@Override
public void send() {
//...
}
}

public class SendFactory {
public static Sender produceMail() {
return new MailSender();
}

public static Sender produceSms() {
return new SmsSender();
}
}

//test
SendFactory.produceMail().send();
Jdk中的工厂方法模式
  • java.lang.Proxy#newProxyInstance()
  • java.lang.Object#toString()
  • java.lang.Class#newInstance()
  • java.lang.reflect.Array#newInstance()
  • java.lang.reflect.Constructor#newInstance()
  • java.lang.Boolean#valueOf(String)
  • java.lang.Class#forName()
Mybatis中的工厂方法模式
  • org.apache.ibatis.exceptions.ExceptionFactory
  • org.apache.ibatis.logging.LogFactory

抽象工厂模式 ( Abstract Factory )

工厂方法模式的一个问题就是类的创建依赖工厂类,如果要拓展,则需要修改工厂类,违背了开闭原则。使用抽象工厂模式,创建多个工厂类,增加新功能,就直接增加新的工厂类就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface Provider {
public Sender produce();
}

public class SmsSendFactory implements Provider {
@Override
public Sender produce() {
return new SmsSender();
}
}

public class MailSendFactory implements Provider {
@Override
public Sender produce() {
return new MailSender();
}
}

//test
new MailSendFactory().produce().send();
new SmsSendFactory().produce().send();
Jdk中的抽象工厂模式
  • `java.util.Calendar#getInstance()
  • java.util.Arrays#asList()
  • java.util.ResourceBundle#getBundle()
  • java.sql.DriverManager#getConnection()
  • java.sql.Connection#createStatement()
  • java.sql.Statement#executeQuery()
  • java.text.NumberFormat#getInstance()
  • javax.xml.transform.TransformerFactory#newInstance()
Mybatis中的抽象工厂模式
  • org.apache.ibatis.datasource.DataSourceFactory

    org.apache.ibatis.datasource.jndi.JndiDataSourceFactory

    org.apache.ibatis.datasource.pooled.PooledDataSourceFactory

    org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory

  • org.apache.ibatis.session.SqlSessionFactory

    org.apache.ibatis.session.defaults.DefaultSqlSessionFactory

    1
    2
    3
    4
    5
    //DefaultSqlSessionFactory类
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    //...
    return new DefaultSqlSession(configuration, executor, autoCommit);
    }

单例模式 ( Singleton )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//静态内部类保证调用getInstance()方法时才初始化,同时由jvm保证仅一次初始化
public class SingleTon {
private SingleTon() {
//防止被实例化
}
private static class SingleTonFactory {
private static SingleTon instance = new SingleTon();
}

public static SingleTon getInstance() {
return SingleTonFactory.instance;
}

/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return getInstance();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//双重校验加volatile,保证jdk5以后不出现指令重排
public class SingleTon {
private volatile static SingleTon instance;
private SingleTon() {

}
public static SingleTon getInstance() {
if(instance == null) {
synchronized (SingleTon.class) {
if(instance == null) {
instance = new SingleTon();
}
}
}
return instance;
}
}
阅读全文 »

WebService

定义

W3C 组织对其的定义如下,它是一个软件系统,为了支持跨网络的机器间相互操作交互而设计。Web Service 服务通常被定义为一组模块化的 API,它们可以通过网络进行调用,来执行远程系统的请求服务。

Web Service = SOAP + HTTP + WSDL。

其中SOAP ( Simple Object Access Protocol )协议是 Web Service 主体,它通过 HTTP 或者 SMTP 等应用层协议进行通讯,自身使用 XML 文件来描述程序的函数方法和参数信息,从而完成不同主机的异构系统间的计算服务处理。这里的 WSDL ( Web Service Description Language )web服务描述语言也是一个 XML 文档,它通过 HTTP 向公众发布,公告客户端程序关于某个具体的 Web Service 服务的 URL 信息、方法的命名、 参数、 返回值等。

SOAP

SOAP 指简单对象访问协议,它是一种基于 XML 的消息通讯格式,用于网络上,不同平台,不同语言的应用程序间的通讯。可自定义,易于扩展。一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素:

  • Envelope 元素,标识 XML 文档一条 SOAP 消息
  • Header 元素,包含头部信息的 XML 标签
  • Body 元素,包含所有的调用和响应的主体信息的标签
  • Fault 元素,错误信息标签。

以上的元素都在 SOAP 的命名空间 http://www.w3.org/2001/12/soap-envelope 中声明。

  • SOAP 的语法规则

    • SOAP 消息必须用 XML 来编码
    • SOAP 消息必须使用 SOAP Envelope 命名空间
    • SOAP 消息必须使用 SOAP Encoding 命名空间
    • SOAP 消息不能包含 DTD 引用
    • SOAP 消息不能包含 XML 处理指令
  • SOAP 消息的基本结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <? xml version="1.0"?>
    <soap:Envelope
    xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
    soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
    <soap:Header>
    ...
    ...
    </soap:Header>
    <soap:Body>
    ...
    ...
    <soap:Fault>
    ...
    ...
    </soap:Fault>
    </soap:Body>
    </soap:Envelope>
  • SOAP Envelope元素
    Envelope元素是 SOAP 消息的根元素。它指明 XML 文档是一个 SOAP 消息。它的属性 xmlns:soap 的值必须是 http://www.w3.org/2001/12/soap-envelope
    encodingStyle 属性,语法:soap:encodingStyle=”URI”。
    encodingStyle 属性用于定义文档中使用的数据类型。此属性可出现在任何 SOAP 元素中,并会被应用到元素的内容及元素的所有子元素上。

  • SOAP Header 元素
    actor 属性,语法 soap:actor=”URI”
    通过沿着消息路径经过不同的端点, SOAP 消息可从某个发送者传播到某个接收者。并非 SOAP 消息的所有部分均打算传送到 SOAP 消息的最终端点。不过,另一个方面,也许打算传送给消息路径上的一个或多个端点。 SOAP 的 actor 属性可被用于将会Header 元素寻址到一个特定的端点。
    mustUnderstand 属性,语法 soap:mustUnderstand=”0|1”
    SOAP 的 mustUnderstand 属性可用标识标题项对于要对其进行处理的接收者来说是强制的还是可选的。假如您向Header 元素的某个子元素添加了“mustUnderstand=1“,则要求处理头部的接收者必须认可此元素。

  • SOAP Body 元素
    必需的 SOAP Body 元素可包含打算传送到消息最终端点的实际 SOAP 消息。Body 元素中既可以包含 SOAP 定义的命名空间中的元素,如 Fault,也可以是用户的应用程序自定义的元素。

  • SOAP Fault 元素
    Fault 元素表示 SOAP 的错误消息。它必须是 Body 元素的子元素,且在一条 SOAP 消息中, Fault 元素只能出现一次。Fault 元素拥有下列子元素:

    • <faultcode>供识别故障的代码
    • <faultstring>可供人阅读的有关故障的说明
    • <faultactor>有关是谁引发故障的信息
    • <detail>存留涉及 Body 元素的应用程序专用错误信息
      常用的 SOAP Fault Codes
    • VersionMismatch SOAP Envelope 元素的无效命名空间
    • MustUnderstand Header 元素的一个直接子元素(带有设置为”1“的mustUnderstand 属性)无法被理解
    • Client 消息被不正确的构成,或包含了不正确的信息
    • Server 服务器有问题,因此无法处理进行下去
  • HTTP 协议中的 SOAP 实例

    • SOAP 请求:(注意 HTTP 的 Head 属性)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      POST /InStock HTTP/1.1
      Host: www.jsoso.net
      Content-Type: application/soap+xml; charset=utf-8
      Content-Length: XXX

      <?xml version="1.0"?>
      <soap:Envelope
      xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
      soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
      <soap:Body xmlns:m="http://www.jsoso.net/stock">
      <m:GetStockPrice>
      <m:StockName>IBM</m:StockName>
      </m:GetStockPrice>
      </soap:Body>
      </soap:Envelope>
    • SOAP 响应:(注意 HTTP 的 Head 属性)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      HTTP/1.1 200 OK
      Content-Type: application/soap+xml; charset=utf-8
      Content-Length: XXX

      <? xml version="1.0"?>
      <soap:Envelope
      xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
      soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
      <soap:Body xmlns:m="http://www.jsoso.net/stock">
      <m:GetStockPriceResponse>
      <m:Price>34.5</m:Price>
      </m:GetStockPriceResponse>
      </soap:Body>
      </soap:Envelope>

WSDL

主要元素

  • <types> web service 使用的数据类型,它是独立与机器和语言的类型定义,这些数据类型定义被<message>标签所使用。
  • <message> web service 使用的消息,它定义了Web Service函数的参数。在WSDL中,输入参数与输出参数要分开定义,使用不同的<message>标签体标识。<message>标签定义的输出输入参数被<portType>标签使用。
  • <portType> web service 执行的操作。该标签引用<message>标签的定义来描述函数签名(操作名、输入参数、输出参数)。
  • <binding> web service 使用的通信协议。<portType>标签中定义的每一操作在此绑定实现。
  • <service> 该标签确定每一<binding>标签的端口地址。
    WSDL 文档可分为两个部分。
  • 顶部分由抽象定义组成。抽象部分以独立于平台和语言的方式定义 SOAP 消息,它们并不包含任何随机器或语言而变的元素。这就定义了一系列服务,截然不同的应用都可以实现。<types><message><portType>属于抽象定义层。所有的抽象可以是单独存在于别的文件中,也可以从主文档中导入。
  • 底部分由具体描述组成。如数据的序列化则可以归入底部分,因为它包含具体的定义。<binding><service>属于具体定义层。

JAVA NIO.2

参考资料:NIO.2 入门,第 1 部分: 异步通道 APINIO.2 入门,第 2 部分: 文件系统 API

  • Files
  • Paths
  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousDatagramChannel
  • AsynchronousFileChannel

Files、Paths

  • Java 7中三个新的文件系统包

    • java.nio.file
    • java.nio.file.attribute
    • java.nio.file.spi
  • 最有用的类

    • java.nio.file.Filesjava.nio.file.FileVisitor在特定目录按深度优先遍历算法来查询文件或目录,并可对每个查询结果执行用户实现的回调方法。

    • java.nio.file.Pathjava.nio.file.WatchService允许“注册”来监视特定目录。如果在目录中发生了文件创建、修改或者删除操作,监视目录的应用程序将收到通知。

    • java.nio.attribute.*AttributeView允许查询此前对于Java用户隐藏的文件和目录属性,这些属性包括文件所所有 者及组权限,访问控制列表(ACL),以及扩展文件属性。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      FileVisitor<Path> fileVisitor = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult preVisitDirectory(Path dir) {
      System.out.println("visit dir[" + dir + "]");
      return FileVisitResult.CONTINUE;
      }
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attribs) {
      System.out.println("visi file[" + file + "]'s size=" + attribs.size());
      return FileVisitResult.CONTINUE;
      }
      }
      Files.walkFileTree(path, fileVisitor);
  • 目录监视

    WatchService API。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    WatchService watchService = FileSystems.getDefault().newWatchService();
    File watchDirFile = new File("watchDir");
    Path watchDirPath = watchDirFile.toPath(); //FilesSystems.getDefault().getPath(watchDirFile.getPath())
    //Path watchDirPath = Paths.get("watchDir");
    WatchKey watchKey = watchDirPath.register(watchService, StandardWatchEventKind.ENTRY_CREATE, StandardWatchEventKind.ENTRY_MODIFY);
    //get watchkey
    WatchKey watchKey = watchService.take(); //blocking //watchService.poll()/watchService.poll(10,TimeUtil.SECONDS);
    for(WatchEvent<?> event : watchKey.pollEvents()) {
    System.out.println(event.kind() +" with " + event.context());
    }
  • 文件属性

    使用java.nio.file.attribute包中的类获取并设置文件属性。

    该包中有7个属性视图(AtrributeView),其中一些特定于操作系统。

    • AclFileAttributeViewAclEntry

      允许设置和获取特定文件的ACL(Access Control List)及文件所有者属性。这些属性视图仅可用于Windows系统。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      Path path = Paths.get("c:", "users", "download");
      UserPrincipal joe = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByName("joe");
      //get view
      AclFileAttributeView view = Files.getFileAttributeView(path, AclFileAttributeView.class);
      //create ACE to give "joe" read access
      AclEntry entry = AclEntry.newBuilder().setType(AclEntryType.ALLOW)
      .setPrincipal(joe)
      .setPermissions(AclEntryPermission.READ_DATA, AclEntryPermission.READ_ATTRIBUTES)
      .build();
      //read ACL, insert ACE, re-write ACL
      List<AclEntry> acl = view.getAcl();
      acl.add(0, entry); //insert before any DENY entries
      view.setAcl(acl);
    • BasicFileAttributeViewBasicFileAttributes

      允许获取一系列“平常的”基本文件属性。其中readAttributes()方法返回一 个BasicFileAttributes实例,该实例包含最后修改时间最后访问时间创建时间大小、以及文件属性等细节(常规文件、目录、符号链接、或者其他)。这一属性视图在所有平台上均可用。

      1
      2
      3
      4
      5
      Path path = Paths.get("attributeFile");
      BasicFileAttributeView basicView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
      BasicFileAttribute basicAttribute = basiceView.readAttributes();
      FileTime newModTime = FileTime.fromMillis(basicAttribute.lastModifiedTime().toMillis() + 60000);
      basicView.setTimes(newModTime, null, null); // only update modifyTime
    • DosFileAttributeViewDosFileAttributes

      这一视图类允许您获取指定给 DOS 的属性。(您可能会猜想,这一视图仅用于 Windows 系统。)其 readAttributes() 方法返回一个 DosFileAttributes 实例,该实例包含有问题的文件是否为只读、隐藏、系统文件、以及存档文件等细节信息。这一视图还包含针对每个属性的 set*(boolean) 方法。

    • FileOwnerAttributeViewUserPrincipal

      这一视图类允许您获取并设置特定文件的所有者。其getOwner()方法返回一个UserPrincipal,其getName()方法,返回所有者名字。该视图还提供setOwner(UserPrincipal)方法用于变更文件所有者。这一属性视图在所有平台上均可用。

    • PosixFileAttributeView PosixFileAttributes

      允许获取并设置指定给POSIX(Portable Operationg System Interface)的属性。有关特定文件存储的信息。其readAttributes()方法返回一个包含有关这一文件的所有者、组所有者、以及这一文件许可(这些细节通常用UNIX chmod命令设置)的PosixFileAttributes实例。这一视图还提供setOwner(UserPrincipal)setGroup(GroupPrincipal)、以及setPermissions(Set<PosixFilePermission>)来修改这些属性。这一属性视图仅在UNIX系统上可用。

    • FileStoreAttributeViewFileStore

      允许您获取有关特定文件存储的信息。其 readAttributes() 方法返回一个包含文件存储的整个空间、未分配空间、以及已使用空间细节的 FileStoreSpaceAttributes 实例。这一视图在所有平台上都可用。

      1
      2
      3
      4
      5
      6
      7
      8
      Path path = Paths.get("c:");
      FileStore fileStore = Files.getFileStore(path);
      fileStore.getTotalSpace();
      fileStore.getUnallocatedSpace();
      fileStore.getUsableSpace();
      fileStore.isReadOnly();
      fileStore.name();
      FileStoreAttributeView view = fileStore.getFileStoreAttributeView(FileStoreAttributeView.class);
    • UserDefinedFileAttributeView

      允许您获取并设置文件的扩展属性。这些属性跟其他的不同,它们只是名称值对,并可按需对其进行设置。如果想向文件增加一些隐藏的元数据,而不必修改文件内容,这就很有用了。通过list()方法,来返回相关扩展属性的名字列表。然后通过size(String name)返回属性值的大小,通过read(String name, ByteBuffer dst)来将属性值读取到ByteBuffer中,通过write(String name, ByteBuffer source)方法来创建或者修改属性,通过delete(String name)来移除现有属性。

      1
      2
      3
      4
      5
      6
      7
      8
      Path path = Paths.get("c:");
      UserDefinedFileAttributeView userView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
      List<String> attribList = userView.list();
      ByteBuffer attribValue = ByteBuffer.allocate(userView.size(attribName));
      userView.read(attribName, attribValue); //读取
      CharsetDecoder decoder = Charset.forName("utf-8").newDecoder();
      CharBuffer buffer = decoder.decode(attribValue);
      userView.write(attribName, attribValue);

异步通道

通道API提供2种对已启动异步操作的监测与控制机制。第一种是通过返回java.util.concurrent.Future对象来实现,它将会建模一个挂起操作,并可用于查询其状态以及获取结果。第二种通过传递给操作一个新类的对象java.nio.channels.CompletionHandler来完成,它会定义在操作完毕后所需要执行的内容。

  • 异步套接字通道及特性

    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
    AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open();
    server.bind(new InetSocketServer(5555));

    //Future
    Future<AsynchronousSocketChannel> acceptFuture = server.accept(); //立即返回
    //if(!future.isDone()) {
    // future.cancel(true);
    //}
    while(!future.isDone()); //waiting
    AsynchronousSocketChannel worker = future.get();
    //AsynchronousSocketChannel worker = future.get(10, TimeUnit.SECONDS); //等待10秒
    // read a message from the client
    worker.read(readBuffer).get(10, TimeUnit.SECONDS);
    System.out.println("Message: " + new String(readBuffer.array()));

    //CompletionHandler,第一个参数为accept返回值
    server.accept(null, new CompletionHandler(AsynchronousSocketChannel, Void) {
    public void completed(AsynchronousSocketChannel ch, Void att) {
    server.accept(null, this); //accept the next connection
    //handle this connection
    handle(ch);
    }
    public void failed(Throwable exc, Void att) {
    //...
    }
    });
    //client
    AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
    client.connect(server.getLocalAddress());
    ByteBuffer message = ByteBuffer.wrap("ping".getBytes());
    int writeLength = client.write(message).get();
  • 异步文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(Paths.get("afile"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);

    fileChannel.write(ByteBuffer.wrap(bytes), 0, "Write operation 1", new CompletionHandler<Integer, Object>() {
    @Override
    public void completed(Integer result, Object attachment) {
    System.out.println(attachement + " completed with " + result + " bytes Written");
    }

    @Override
    public void failed(Throwable e, Object attachement) {
    System.err.println(attachment +" fialed with:");
    e.printStackTrace();
    }
    });
  • 异步通道组

    每个异步通道都属于一个通道组,它们共享一个Java线程池,该线程池用于完成启动的异步I/O操作。
    默认情况下,具有 open() 方法的通道属于一个全局通道组,可利用如下系统变量对其进行配置:

    • java.nio.channels.DefaultThreadPool.threadFactory,其不采用默认设置,而是定义一个 java.util.concurrent.ThreadFactory
    • java.nio.channels.DefaultThreadPool.initialSize,指定线程池的初始规模

    java.nio.channels.AsynchronousChannelGroup 中的三个实用方法提供了创建新通道组的方法:

  • withCachedThreadPool()

  • withFixedThreadPool()

  • withThreadPool()

    1
    2
    3
    4
    5
    AsynchronousChannelGroup tenThreadGroup = AsynchronousChannelGroup.withFixedThreadPool(10, Executors.defaultThreadFactory());
    AysnchronousChannelGroup tenThreadGroup2 = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 10);
    //使用 tenThreadGroup 而不是默认通道组来获取线程
    AsynchronousServerSocketChannel channel =
    AsynchronousServerSocketChannel.open(tenThreadGroup);

    利用通道组来控制线程关闭

    1
    2
    3
    4
    5
    6
    7
    8
    channel.accept(null, completionHandler);
    if(!tenThreadGroup.isShutdown()) {
    tenThreadGroup.shutdown();
    }
    if(!tenThreadGroup.isTerminated()) {
    tenThreadGroup.shutdownNow();
    }
    tenThreadGroup.awaitTermination(10, TimeUnit.SECONDS);

    AsynchronousFileChannel的open()方法使用ExecutorService而不是AsynchronousChannelGroup来实现定制的线程池。

  • 异步数据报通道与多播