Java基础(三)—— Java常用类

本文最后更新于:2021年6月26日 下午

概览:Java常用类:Object、包装类、String类、File类、IO流。

预警!仅用于本人快速自学,不过欢迎指正。

Scanner

java.util.Scanner

  • 创建对象:Scanner s = new Scanner(System.in);
  • 获取输入:next()方法以及nextLine()方法,同时与hasNext()以及hasNextLine()进行配合。
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class TestScanner {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String s = null;
while(scan.hasNextLine()){
s = scan.nextLine();
System.out.println(s);
}
}
}

next与nextLine的区别

next():

1、一定要读取到有效字符后才可以结束输入。

2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉

3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。

4、next() 不能得到带有空格的字符串。

nextLine():

1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。

2、可以获得空白。

读取数字

  • 读取int型:hasNextInt(),然后nextInt()
  • 读取double型:hasNextDouble()然后nextDouble()

Object类

理论上所有的类都直接或者间接继承java.Lang.Object类。

主要方法:

  • toString()
  • getClass()
  • equals()
  • clone()
  • finalize()

toString()

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 类名 + @ + 哈希码的无符号16进制
  • 建议自定义类重写此方法

getClass()

1
public final native Class<?> getClass();
  • 返回运行时类型
  • final表示不能够重写,一般与后接getName()方法。

equals()

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}
  • 判断obj与本身对象是不是指向了同一个对象(同一块内存)。
  • 若希望不比较内存而比较内容的话,最好重写equals方法,例如String类。

clone()

1
protected native Object clone() throws CloneNotSupportedException;
  • 复制对象。
  • 即先分配一个和源对象大小相同的空间,在这个空间中创建出一个新的对象。
    • 使用new创建一个对象
    • 使用clone复制一个对象
  • 作用:用于拷贝出一个新的对象,对新对象的修改不会影响原来的对象!即普通的A a = b,这样两个引用指向了同一块内存,修改对相互之间都会产生影响。而clone可以满足这样的需求,提供精确拷贝,生成一个新的对象。

Shallow Clone与Deep Clone

浅拷贝:简单的执行域对域的拷贝,对于一个Reference变量,简单的拷贝之后会由两个Reference指向了同样的内存。

深拷贝:对Reference变量进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Salary implements Cloneable{
private double sal;
private double reword;

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

class Employee implements Cloneable{
private String name;
private Salary salary;

@Override
protected Object clone() throws CloneNotSupportedException {
Employee cloned = (Employee) super.clone();
cloned.salary = (Salary) salary.clone();
return cloned;
}
}
  • Object中clone()是被声明为protected
  • 调用Clone()方法的对象所属的类(Class)必须implements Clonable接口,否则在调用Clone方法的时候会抛出CloneNotSupportedException

finalize()

1
protected void finalize() throws Throwable { }
  • 用于释放资源。
  • 工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
  • 用途:无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。

hashCode()

1
public native int hashCode();
  • 返回对象的哈希值。
  • 用于哈希查找,可减少在查找中使用equals的次数,重写equals方法一般都要重写hashCode方法。

一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode() == obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

wait()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}

if (nanos > 0) {
timeout++;
}

wait(timeout);
}

public final void wait() throws InterruptedException {
wait(0);
}

wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。

wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常

notify()

1
2
public final native void notify();
public final native void notifyAll();//所有线程

唤醒在该对象上等待的某个线程。


包装类

虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性、没有方法可调用。 沿用它们只是为了迎合人类根深蒂固的习惯,并的确能简单、有效地进行常规数据处理。

这种借助于非面向对象技术的做法有时也会带来不便,比如引用类型数据均继承了 Object 类的特性,要转换为 String 类型(经常有这种需要)时只要简单调用 Object 类中定义的toString()即可,而基本数据类型转换为 String 类型则要麻烦得多。为解决此类问题 ,Java为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有教材称为外覆类或数据类型类。

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean
  • 所有的包装类都是抽象类Number的子类。
  • 包装类的对象可以封装对象基本类型的数据,并提供了一些有用的方法。
  • 包装类对象一经创建,其内容即所封装的基本类型数据值不可改变。

装箱与拆箱

  • 装箱:基本来向装箱包装类
  • 拆箱:包装类转向基本类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestWrapper {
public static void main(String[] args) {
int m = 10;
Integer inte = new Integer(m); //手动装箱
System.out.println(inte.intValue());//获取值
int n= inte.intValue(); //拆箱

Integer inte2 = new Integer(10);
System.out.println("inte == inte2 ? " + inte.equals(inte2));//true

//Java 1.5之后可以自动拆箱装箱
int a = 20;
Integer num1 = a; //自动装箱
System.out.println("num1 " + num1.toString());
int a1 = num1; //自动拆箱
System.out.println("a1 " + a1);

Integer num2 = 20;
System.out.println("num1 == num2 ? " + num1.equals(num2));//true
}
}
  • 自动装箱与拆箱其实是编译器的工作。

数字与字符串的转换

  • 字符串转数字:parseInt(String s,int radix),radix指明进制,默认十进制。
  • 数字转字符串:toString()或者"" + s
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
public class ParseStr {
public static void main(String[] args) {
String [] str = {"123","123a","a123","abc"};
for(String tmp :str){
try {
int m = Integer.parseInt(tmp);
System.out.println(tmp + "可以转为数字: " + m);
}catch (Exception e){
System.out.println(tmp + "不可转为数字!");
}
}

Integer inte = 100;
System.out.println("inte: " + inte.toString());
System.out.println("inte: " + inte);
}
}

/////////////
123可以转为数字: 123
123a不可转为数字!
a123不可转为数字!
abc不可转为数字!
inte: 100
inte: 100

Math类

1
2
3
4
5
6
7
8
9
10
11
12
Math.PI 圆周率
Math.E 常量E
Math.abs 绝对值
Math.ceil 获取不小于某数的最大整数
Math.floor 获取不大于某数的最大整数
Math.max 求两数中最大值
Math.min 求两数中最小值
Math.sqrt 求开方
Math.pow 求某次方
Math.exp 求e的某次方
Math.log 求自然对数
Math.random 返回0-1之间的一个double随机数

Random类

待续

时间日期类

待续

String类

  • 代表字符串,Java中的所有字符串字面量都是此类的实例实现。
  • 字符串是常量,它们的值创建之后不能更改。
  • 字符串缓冲区支持可变的字符串。
1
2
3
4
5
6
7
private final char value[];//属性值 字符数组
private int hash; // Default to 0
private final int offset;//数组被使用的开始位置,不一定从0开始
private final int count;//String元素的个数
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];

构造方法

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
String()
//初始化一个新创建的 String 对象,使其表示一个空字符序列。
String(byte[] bytes)
//通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, Charset charset)
//通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(byte[] bytes, int offset, int length)
//通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, Charset charset)
//通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, int offset, int length, String charsetName)
//通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。
String(byte[] bytes, String charsetName)
//通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
String(char[] value)
//分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。
String(char[] value, int offset, int count)
//分配一个新的 String,它包含取自字符数组参数一个子数组的字符。
String(int[] codePoints, int offset, int count)
//分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。
String(String original)
//初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建
的字符串是该参数字符串的副本。
String(StringBuffer buffer)
//分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。
String(StringBuilder builder)
//分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。

创建字符串对象

直接赋值创建,位于方法去的常量池中。

1
String str = "hello";//直接赋值

通过构造方法,位于堆内存之中。

1
String str = new String("hello");//new 实例化

比较

  • String类是引用类型,==直接比较的是地址。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class TestStr {
public static void main(String[] args) {
String str1 = "hello";
String str2 = str1;

System.out.println(str1 == str2); //true
System.out.println(str1.equals(str2)); //true

String str3 = new String("hello");
String str4 = str3;

System.out.println(str3 == str4); //true
System.out.println(str3.equals(str4)); //true

System.out.println(str1 == str3); //false
System.out.println(str1.equals(str3)); //true
}
}

字符串常量池

  • 如果采用直接赋值的方式进行对象的实例化String s1 = "hi",则会将匿名对象"hi"放入对象池之中,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象。

当然也可以手动入池

1
2
3
String s1 = new String("hi").intern();//手动入池
String s2 = "hi";
System.out.println(s1 == s2); //true

字符串两种实例化方法的区别

  • 直接赋值:只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。
  • 构造方法:会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要手动入池。

String类的常用方法

字符串比较判断

1
2
3
4
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String str): 比较字符串的内容是否相同,忽略大小写
boolean startsWith(String str): 判断字符串对象是否以指定的str开头
boolean endsWith(String str): 判断字符串对象是否以指定的str结尾

截取

1
2
3
4
5
6
int length():获取字符串的长度,其实也就是字符个数
char charAt(int index):获取指定索引处的字符
int indexOf(String str):获取str在字符串对象中第一次出现的索引
String substring(int start):从start开始截取字符串
String substring(int start,int end):从start开始,到end结束截取字符串。包括start,
不包括end

转换

1
2
3
char[] toCharArray():把字符串转换为字符数组
String toLowerCase():把字符串转换为小写字符串
String toUpperCase():把字符串转换为大写字符串

分割、去除空格

1
2
去除字符串两端空格:String trim()
按照指定符号分割字符串:String[] split(String str)

StringBuilder StringBuffer

StringBuilder 是一个可变的字符序列。它继承于AbstractStringBuilder,实现了CharSequence接口。StringBuffer 也是继承于AbstractStringBuilder的子类;但是,StringBuilder和StringBuffer不同,StringBuilder是非线程安全的,StringBuffer是线程安全的。

  • 两者的常用方法相似

insert

1
2
3
4
5
6
7
8
9
10
insert(int offset,Object o);
insert(int offset,Object []o,int begin,int len);
insert(CharSequence s,int start,int end);//[start,end)

StringBuilder sbuild = new StringBuilder();
sbuild.insert(0,123);
sbuild.insert(0,new char[]{'a','b','c'},0,3);
sbuild.insert(0,new StringBuilder("helloworld"),5,10);//start = 5,end = 10

System.out.println(sbuild.toString());//worldabc123

append

1
2
3
4
5
6
7
8
9
10
append(Object o);
append(char[],int offset,int len);
append(CharSequence s,int begin,int end);//[start,end)

StringBuilder sbulids = new StringBuilder();
sbulids.append(123);
sbulids.append(true);
sbulids.append(new char[]{'a','b','c'},0,3);
sbulids.append(new StringBuffer("helloworld"),5,10);
System.out.println(sbulids.toString());//123trueabcworld

其他API

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
StringBuilder sbuilder = new StringBuilder("helloworld");
sbuilder.replace(0,5,"well");
System.out.println(sbuilder.toString());//wellworld

sbuilder.reverse();
System.out.println(sbuilder.toString());//dlrowllew

sbuilder.setCharAt(0,'A');
System.out.println(sbuilder.toString());//Alrowllew

sbuilder.deleteCharAt(0);
System.out.println(sbuilder.toString());//lrowllew

sbuilder.delete(0,3);
System.out.println(sbuilder.toString());//wllew

String strs = sbuilder.substring(3);
System.out.println(strs); //ew
strs = sbuilder.substring(2,5);
System.out.println(strs); //lew

strs = (String)sbuilder.subSequence(2,5);//获取CharSequence对象,转型为String
System.out.println(strs); //lew

//某个字符出现位置
System.out.println(sbuilder.toString());//wllew
System.out.println("从前向后中 e第一次出现的位置" + sbuilder.indexOf("e")); //3
System.out.println("从索引2开始,w第一次出现的位置"+sbuilder.indexOf("w",2));//4
System.out.println("从后向前,e第一次出现的位置" + sbuilder.lastIndexOf("e")); //3

String StringBuilder StringBuffer

  • String 字符串常量
  • StringBuffer 字符串变量(线程安全)
  • StringBuilder 字符串变量(非线程安全)
  • 速度:StringBuilder > StringBuffer > String

使用:

如果要操作少量的数据用 = String

单线程操作字符串缓冲区 下操作大量数据 = StringBuilder

多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

File类

java.io.File类:文件和目录路径名的抽象表示形式。

构造方法:

1
public File(String pathname)
  • 如果pathname是相对路径,则默认的当前路径在系统属性user.dir 中存储。
  • File的静态属性String separator存储了当前系统的路径分隔符。

访问属性:

1
2
3
4
5
6
7
8
9
10
public boolean isFile();
public boolean isDiractory();
public boolean exists();
public long lastModified();
public String getName();
public boolean canRead();
public boolean canWrite();
public boolean isHidden();
public long length();
public String getPath();

创建空文件或者目录:(在File对象所指的文件或者目录不存在的情况下)

1
2
3
4
5
6
public boolean createNewFile() throws IOException;
public boolean delete();
public boolean mkdir(); //要求上级目录必须存在
public boolean mkdirs();//上级墓库不存在时同时创建

public boolean renameTo(File dest)

实例:

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
public class TestJava {
public static void main(String[] args) {
//Java中的默认路径在与/src 同级之下!
File f2 = new File("BN.java");
System.out.println("exist: " + f2.exists());
System.out.println("file: " + f2.isFile());
System.out.println("name: " + f2.getName());
System.out.println("len: " + f2.length());

try{
f2.createNewFile(); //创建这个新文件
}catch (IOException e){
e.printStackTrace();
}

//创建一个目录
File dir = new File("Test");
if ( dir.mkdirs()){
System.out.println("success");
}else{
System.out.println("failed");
}

//移动BN.java到Test目录下
File newfile = new File("Test" +"/" + f2.getName() );
if(newfile.exists()){
System.out.println("已经存在!");
}else{
if(f2.renameTo(newfile)){ //旧文件.renameTo(新文件)
System.out.println("移动成功!");
}else{
System.out.println("移动失败!");
}
}
}
}

IO流

字节流和字符流

  • **字节流(8bit)**:最原始的一个流,读出来的数据就是010101这种最底层的数据表示形式,只不过它是按照字节来读的,一个字节(Byte)是8位(bit)读的时候不是一个位一个位的来读,而是一个字节一个字节来读
  • **字符流(16bit)**:字符流是一个字符一个字符地往外读取数据。一个字符是2个字节。

输入流和输出流

java.io包中提供的流类型都分别继承自以下四种抽象流

输入流:InputStream(字节流),Reader(字符流)

输出流:OutPutStream(字节流),Writer(字符流)

  • 输入和输出是站在程序的角度,从文件中读数据,这叫做输入!

节点流和处理流

  • 节点流:可以从一个特定的数据源(节点)读取数据,例如文件、内存
  • 处理流:是建立在已经存在的流(节点流或者处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

节点流类型

类型 字符流 字节流
File FileReader、FileWriter FileInputStream、FileOutputStream
Memory Array CharArrayReader、CharArrayWriter ByteArrayInputStream、ByteArrayOutputStream
Memory String StringReader、StringWriter -
Pipe PipeReader、PipeWriter PipeInputStream、PipeOutputStream

处理流类型

处理类型 字符流 字节流
Buffering BufferedReader、BufferedWriter BufferedInputStream、BufferedOutputStream
Filtering FilterReader、FilterWriter FilterInputStream、FilterInputStream
Converting between bytes and chaacter InputStreamReader、 OutputStreamWriter -
Object Serialization - ObjectInputStream、 ObjectOutputStream
Data conversion - DataInputStream、 DataOutputStream
Counting LineNumberReader LineNumberInputStream
Peeking ahead PusbackReader PushbackInputStream
Printing PrintWriter PrintStream

InputStream 输入流

  • 字节流 8bit,向程序中输入数据。

基本方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//读取一个字节并以整数的形式返回(0~255)
//如果返回-1就说明已经到了输入流的末尾
int read() throws IOException

//读取一系列字节并存储到一个数组buffer
//返回实际读取的字节数,如果读取前已到输入流的末尾,则返回-1
int read(byte[] buffer) throws IOException

//读取length个字节
//并存储到一个字节数组buffer,从length位置开始
//返回实际读取的字节数,如果读取前以到输入流的末尾返回-1.
int read(byte[] buffer,int offset,int length) throws IOException

//关闭流释放内存资源
void close() throws IOException

//跳过n个字节不读,返回实际跳过的字节数
long skip(long n) throws IOException
  • read()一个一个字节读
  • read(byte[] buffer) 则是先使用缓冲区再读。

案例:

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 class TestInput {
public static void main(String[] args) {
//使用FileInputStream来读取文件内容
FileInputStream in = null;

try{
in = new FileInputStream("./src/General/ParseStr.java");

}catch(FileNotFoundException e){
System.out.println("file not found");
System.exit(-1);
}

int ret = 0;//记录read()的返回值
long num = 0;//记录读取到的字节数目

try {
while ((ret = in.read()) != -1) {
System.out.println((char) ret);
num++;
}

in.close();
System.out.println("all byte : " + num);

}catch(IOException e){
System.out.println("read error!");
}
}
}
  • 一个字节一个字节的读取,对于中文就会产生乱码,这是应当采用字符流。

OutputStream 输出流

常用方法:

1
2
3
4
5
6
7
8
9
10
//向输出流中写入一个字节数据,该字节数据为参数b的低8位
void write(int b) throws IOException
//将一个字节类型的数组中的数据写入输出流
void write(byte[] b) throws IOException
//将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流
void write(byte[] b,int off,int len) throws IOException
//关闭流释放内存资源
void close() throws IOException
//将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException

案例:文件拷贝

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
package JavaIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class TestOutput {
public static void main(String[] args) {
FileInputStream in = null;
FileOutputStream out = null;

try{
in = new FileInputStream("./src/General/ParseStr.java");
out = new FileOutputStream("./src/General/ParseStrNew.java");
//若文件不存在则会自动创建
}catch(FileNotFoundException e){
System.out.println("File not found");
System.exit(-1);
}

int b = 0;
try {
while( (b = in.read()) != -1 ){
out.write(b);
}

in.close();
out.close();
}catch(IOException e){
System.out.println("File copy failed");
System.exit(-1);
}
}
}

Reader

  • 一个字节一个字节的读取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//读取一个字节并以整数的形式返回(0~255)
//如果返回-1就说明已经到了输入流的末尾
int read() throws IOException
//读取一系列字节并存储到一个数组buffer
//返回实际读取的字节数,如果读取前已到输入流的末尾,则返回-1
int read(byte[] buffer) throws IOException
//读取length个字节
//并存储到一个字节数组buffer,从length位置开始
//返回实际读取的字节数,如果读取前以到输入流的末尾返回-1
int read(byte[] buffer,int offset,int length) throws IOException
//关闭流释放内存资源
void close() throws IOException
//跳过n个字节不读,返回实际跳过的字节数
long skip(long n) throws IOException

Writer流

1
2
3
4
5
6
7
8
9
10
//向输出流中写入一个字节数据,该字节数据为参数b的低16位
void write(int b) throws IOException
//将一个字节类型的数组中的数据写入输出流
void write(byte[] b) throws IOException
//将一个字节类型的数组中的从指定位置(off)开始的len个字节写入到输出流
void write(byte[] b,int off,int len) throws IOException
//关闭流释放内存资源
void close() throws IOException
//将输出流中缓冲的数据全部写出到目的地
void flush() throws IOException

缓冲流 Buffering

缓冲流要套在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率。

1
2
3
4
5
6
7
8
BufferedReader(Reader in);
BufferedReader(Reader in,int sz);//sz为自定义缓冲区大小
BufferedWriter(Writer out);
BufferedWriter(Writer out,int sz);
BufferedInputStream(InputStream in);
BufferedInputStream(InputStream in,int sz);
BufferedOutputStream(OutputStream in);
BufferedOutputStream(OutputStream in,int sz);
  • 缓冲输出流支持其父类的mark和reset方法。https://blog.csdn.net/dengjili/article/details/79415695
  • BufferedReader提供了readLine方法用于读取一行字符串
  • BufferedWriter提供了newLine方法用于写入一个行分隔符
  • 对于输出的缓冲流,写出的数据会先存在内存中的缓存,使用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
package JavaIO;

import java.io.*;

public class TestBufferRead {
public static void main(String[] args) {
try{
BufferedWriter bw = new BufferedWriter( new FileWriter("./Test/hi.txt"));
for(int i = 0;i<100;i++){
bw.write((char)i);
bw.newLine();
}
bw.flush();//清空缓冲区

BufferedReader br = new BufferedReader(new FileReader("./Test/hi.txt"));
String s = null;
while( (s = br.readLine()) != null){
System.out.println(s);
}

br.close();
bw.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

转换流

  • InputStreamReaderOutputStreamWriter用于字节数据到字符数据之间的转换
  • InputStreamReader需要InputStream套接
  • OutputStreamWriter需要OutputStream套接
  • 转换流在构造时可以指定其编码集合。
1
InputStreamReader isr = new InputStreamReader(System.in,"ISO8859-1");

System.in是一个标准的输入流,用来接收从键盘输入的数据。

OutputStream是字节流,Writer是字符流, OutputStreamWriter就是把OutputStream转换成Writer。把OutputStream转换成Writer之后就可以 一个字符一个字符地通过管道写入数据了。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestTransIO {
public static void main(String[] args) {
try{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./Test/he.txt"));
osw.write("ABCDEFGHIJKLMN");

System.out.println("当前编码" + osw.getEncoding());

osw = new OutputStreamWriter(new FileOutputStream("./Test/he.txt",true),"ISO8859_1");
osw.write("ABCDEFGHIJKLMN");

System.out.println("当前编码" + osw.getEncoding());
osw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
/////
当前编码UTF8
当前编码ISO8859_1
  • 使用了转换流之后就可以以字符串的形式写入文件中,提高了写入的速度,也减少了对硬盘的访问次数。
  • 而若是使用FileOutputStream来写入,就只能是一个字节一个字节的尽心写入了。

数据流

  • DataInputStream 和 DataOutputStream 分别继承自InputStream 和 OutputStream , 它属于处理流,需要分别“套接”在InputStream 和 OutputStream类型的节点流上。
  • DataInputStream 和 DataOutputStream 提供了可以存取与机器无关的Java原始类型数据(int,double等)的方法。
1
2
DataInputStream(InputStream in);
DataOutputStream(OutputStream out);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestDataIO {
public static void main(String[] args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();//会在内存中创建一个ByteArray字节数组
DataOutputStream dos = new DataOutputStream(baos);//套一层数据流,用来处理数据

try{
dos.writeDouble(Math.PI);//直接写入到ByteArray数组中
dos.writeInt(123);

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais);

System.out.println(dis.readDouble());//先写进去就先读出来
System.out.println(dis.readInt());//后写进去就后读出来
}catch(Exception e){
e.printStackTrace();
}
}
}
  • 通过bais这个流往外读取数据的时候,是一个字节一个字节地往外读取的,因此读出来的数据无法判断是字符串还是其他类型的值,因此要在它的外面再套一个流,通过dataInputStream把读出来的数据转换就可以判断了。

打印流

  • PrintWriter 和 PrintStream 都属于输出流,分别针对与字符和字节
  • PrintWriter 和 PrintStream 提供了重载的print
  • Println方法用于多种数据类型的输出
  • PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
  • PrintWriter 和 PrintStream有自动flush功能
1
2
3
4
5
6
PrintWriter(Writer out);
PrintWriter(Writer out,boolean autoFlush);
PrintWriter(OutputStream out);
PrintWriter(OutputStream out,boolean autoFlush);
PrintStream(OutputStream out);
PrintStream(OutputStream out,boolean autoFlush);

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TestPrintIO {
public static void main(String[] args) {
PrintStream ps = null;
try{
FileOutputStream fos = new FileOutputStream("./Test/log.txt");
ps = new PrintStream(fos);
//使用打印流套接输出流

if(ps != null){
System.setOut(ps);//改变输出窗口到ps的指定文件
//这样输出的内容都会到达指定文件中
}
for(int c = 0;c<100;c++){
System.out.print(c + " ");
}
}catch(Exception e){
e.printStackTrace();
}
}
}

对象流

  • 直接将Object写入或者读出
  • transient关键字,表示透明,用它来修饰的成员变量在序列化的时候不予考虑,也就是当成不存在。
  • 凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口
    • Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类作标记,
      只是起到一个标记作用。
    • 这个标记是给编译器看的,编译器看到这个标记之后就可以知道这个类可以被序列化 如果想把某个类的对象序列化,就必须得实现Serializable接口
  • 直接实现Serializable接口的类是JDK自动把这个类的对象序列化,而如果实现public interface Externalizable extends Serializable的类则可以自己控制对象的序列化,建议能让JDK自己控制序列化的就不要让自己去控制
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
class Cat implements Serializable {
int i = 10;
double j = 3.14;
char k = 'a';
transient int m = 1;
}

public class TestObjectIO {
public static void main(String[] args) {
Cat cat = new Cat();
cat.i = 100;

try{
FileOutputStream fos = new FileOutputStream("./Test/Cat.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(cat);
oos.flush();
oos.close();
fos.close();

FileInputStream fis = new FileInputStream("./Test/Cat.txt");
ObjectInputStream ois = new ObjectInputStream(fis);

Cat tmp = (Cat)ois.readObject();//读出并强制转型
System.out.println(tmp.i + " " + tmp.j + " " + tmp.k + " " + tmp.m);//100 3.14 a 0
ois.close();
fis.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

System.in System.out

  • System.in InputStream类型,字节流,程序使用它可读取键盘输入的数据;
  • System.outPrintStream类型(是OutputStream的子类),字节流,程序使用它可将数据输出到显示屏上。