Bootstrap

JavaSE中阶

目录

1.泛型

1泛型概述

1.1使用场景

1.2泛型概述小结

2.泛型类

2.1泛型类的定义

2.2泛型类的使用

3.泛型接口

2.注解

2.1内置注解

2.2元注解

2.3 自定义注解

3.异常处理

1.异常概述,体系

1.1什么是异常

1.2为什么要学习异常

1.3如何处理异常

2.常见运行时异常

3.常见编译时异常

4.异常的默认处理流程

5.Throwable类常用的方法

6.Throws和throw的区别

4.IO流

1.初识Java IO

1.1IO流分类

1.输入流和输出流

2.字节流和字符流

3.节点流和处理流

1.2案例操作

2.IO流对象

2.1File类

2.2字节流

2.3字符流

3.IO流

3.1字节流方法

3.2字符流方法

3.3比较速度快慢


1.泛型

1泛型概述

泛型的本质是为了将类型参数化, 也就是说在泛型使用过程中,数据类型被设置为一个参数,在使用时再从外部传入一个数据类型;而一旦传入了具体的数据类型后,传入变量(实参)的数据类型如果不匹配,编译器就会直接报错。这种参数化类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

1.1使用场景

public class Main {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add(111);// 在编译阶段,编译器会报错 因为在创建list集合的时候明确了传入类型为string类
        for (int i = 0; i < list.size(); i++) {
            System.out.println((String) list.get(i));
        }
    }
}

1.2泛型概述小结

  • 与使用 Object 对象代替一切引用数据类型对象这样简单粗暴方式相比,泛型使得数据类型的类别可以像参数一样由外部传递进来。它提供了一种扩展能力,更符合面向对象开发的软件编程宗旨。

  • 当具体的数据类型确定后,泛型又提供了一种类型安全检测机制,只有数据类型相匹配的变量才能正常的赋值,否则编译器就不通过。所以说,泛型一定程度上提高了软件的安全性,防止出现低级的失误。

  • 泛型提高了程序代码的可读性。在定义泛型阶段(类、接口、方法)或者对象实例化阶段,由于 < 类型参数 > 需要在代码中显式地编写,所以程序员能够快速猜测出代码所要操作的数据类型,提高了代码可读性。

2.泛型类

2.1泛型类的定义

public class Test <T> {
    //key 这个成员变量的数据类型为T,T的类型由外部传入
    private T key;
    //泛型构造方法形参 key类型也为T,T由外部传入
    public Test(T key) {
        this.key = key;
    }
    //泛型方法 getKey的返回值类型为T,T的类型由外部指定
    public T getKey() {
        return key;
    }
    public void setKey(T key) {
        this.key = key;
    }
}

在泛型类中,类型参数定义的位置有三处,分别为:

 1.非静态的成员属性类型
  2.非静态方法的形参类型(包括非静态成员方法和构造器)
  3.非静态的成员方法的返回值类型

  • 类型参数用于类的定义中,则该类被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map等

  • 泛型类中的静态方法和静态变量不可以使用泛型类所声明的类型参数

而静态变量和静态方法在类加载时已经初始化,直接使用类名调用;在泛型类的类型参数未确定时,静态成员有可能被调用,因此泛型类的类型参数是不能在静态成员中使用的

  • 静态泛型方法中可以使用自身的方法签名中新定义的类型参数(即泛型方法,后面会说到),而不能使用泛型类中定义的类型参数。

public class Test2<T> {   
	// 泛型类定义的类型参数 T 不能在静态方法中使用  
    public static <E> E show(E one){ // 这是正确的,因为 E 是在静态方法签名中新定义的类型参数    
        return null;    
    }    
}  

泛型类不只接受一个类型参数,它还可以接受多个类型参数

public class MultiType <E,T>{}

2.2泛型类的使用

在创建泛型类的对象时,必须指定类型参数 T 的具体数据类型,即尖括号 <> 中传入的什么数据类型,T 便会被替换成对应的类型。如果 <> 中什么都不传入,则默认是 < Object >

使用泛型的上述特性便可以在集合中限制添加对象的数据类型,若集合中添加的对象与指定的泛型数据类型不一致,则编译器会直接报错,这也是泛型的类型安全检测机制的实现原理

3.泛型接口

定义一个泛型接口如下:

interface IUsb<U, R> {
    int n = 10;
    U name;// 报错! 接口中的属性默认是静态的,因此不能使用类型参数声明
    R get(U u);// 普通方法中,可以使用类型参数
    void hi(R r);// 抽象方法中,可以使用类型参数
    // 在jdk8 中,可以在接口中使用默认方法, 默认方法可以使用泛型接口的类型参数
    default R method(U u) {
        return null;
    }
}
  • 注意:在泛型接口中,静态成员也不能使用泛型接口定义的类型参数。

  • 定义一个接口 IA 继承了 泛型接口 IUsb,在 接口 IA 定义时必须确定泛型接口 IUsb 中的类型参数

  • 定义一个类 BB 实现了 泛型接口 IUsb,在 类 BB 定义时需要确定泛型接口 IUsb 中的类型参数

  • 定义一个类 CC 实现了 泛型接口 IUsb 时,若是没有确定泛型接口 IUsb 中的类型参数,则默认为 Object。

  • 定义一个类 DD 实现了 泛型接口 IUsb 时,若是没有确定泛型接口 IUsb 中的类型参数,也可以将 DD 类也定义为泛型类,其声明的类型参数必须要和接口 IUsb 中的类型参数相同

// DD 类定义为 泛型类,则不需要确定 接口的类型参数
// 但 DD 类定义的类型参数要和接口中类型参数的一致
class DD<U, R> implements IUsb<U, R> { 
	...
}

2.注解

2.1内置注解

Annotation是从JDK5.0开始引入的新技术 它的作用不是程序本身,可以对程序做出解释(这一点和注释comment没什么区别)可以被其他程序(比如编译器读取)

格式@...

Annotation可以在package,class,method,field上面使用,相当于给他们添加额外的辅助信息,我们通过反射机制编程实现对这些元数据的访问

@override 重写的注解

@Deprecated 不推荐程序员使用,但是可以使用,过时更好方式

@SuppressWarnings() 用来抑制编译时的警告信息需要添加参数

2.2元注解

  • 元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来操作对其他annotation类型作说明

  • 这些类型和他们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)

  1. @Target用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

  2. @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)

  3. @Document 说明该注解被包含在javadoc中

  4. @Inherited 说明子类可以继承父类中的该注解

import java.lang.annotation.*;
public class Test01 {
    public static void main(String[] args) {
    }
}
//target表示注解可以用在什么地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention表示我们注解在什么地方有效
    @Retention(value = RetentionPolicy.RUNTIME)//runtime>class>source
//document 表示是否将我们的注解生成在javadoc中
    @Documented
@Inherited //表示子类继承父类 
@interface MyAnnotation{
}

2.3 自定义注解

使用@interface自定义注解时,自动继承java.lang.annotation.Annotation接口

  • @interface用来声明一个注解

  • 其中每一个方法实际上声明了一个配置参数,方法的名称就是参数的名称

  • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum),可以通过default来声明参数的默认值

  • 如果只有一个参数成员,一般参数名为value

  • 注解元素必须要有值,我们定义注解元素时,经常用空字符串,0作为默认值

import java.lang.annotation.*;
public class Test01 {
    //注解可以显示赋值,如果没有默认值,我们必须给注解赋值
    @MyAnnotation1(id = 1)
   public void test1(){}
    @MyAnnotation2("将jixuan")
    public void test2(){}
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
    //注解的参数:参数类型+参数名();有点类似方法,所以上面叫方法名称
    int id();
    String name() default "姜霁轩";
    int[]arr() default {1,2,3};
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    String value();
    int id ()default -1;//如果默认值为-1,代表不存在
}

3.异常处理

1.异常概述,体系

1.1什么是异常

异常是程序在编译或执行过程中出现的问题或错误,而不是语法错误,语法错误由自己导致 比如:数组索引越界异常,空指针异常,日期格式化异常等

1.2为什么要学习异常

异常一旦出现没有提前处理,程序就会退出JVM而终止

学习异常不是为了让我们以后不出现异常,而是程序出现异常之后,该如何处理 体现的是程序的安全,健壮性

Error错误,是一种特殊的类(java.long.Error),与普通的Exception异常类不同,Error是程序无法处理的错误,Error不应该由应用程序捕获和处理,而是由虚拟机自己处理,通常代表虚拟机和系统级别问题

一些常见的Error比如:

  1. Virtual MachineError:Java虚拟机运行错误=>内存溢出(OutOfmemory ---堆内存溢出,StackOverflowError---栈内存溢出)

  2. java.long.NoClassDefFoundError:当虚拟机无法找到要加载的类时抛出的错误

  3. LinkageError:类加载过程出现

  • Java中所有异常都来自顶级父类Throwable,Throwable下有两个子类Exception和Error Throwable是所有异常类的根类,Java异常的顶级父类,所有的异常类都是由它继承 Java中异常继承的根类是Throwable Throwable是根类,不是异常类

  • Exception异常,程序本身可以处理的异常 Exception才是异常类 当程序出现Exception时,是可以靠程序自己来解决的

  • 异常体系最上层父类是Exception

  • 异常分为两类:①编译时异常(受检异常,必须处理)②运行时异常(非受检异常)

  • 受检异常就是程序必须手法处理的异常,如果不手动处理,则编译失败 非受检异常不强制程序处理的异常,如果不手动处理,则编译可以通过,运行报错

  • Error属于非受检异常

  • 编译时异常,没有继承RuntimeException的异常或直接继承与Exception 编译阶段就会报错/错误提示,必须手动处理(如果受检异常没有被catch或者throws关键字处理的话,就没法通过编译)编译异常就是为了提醒程序员

  • 比如:IOException,ClassNotFoundException,ParseException,SQLException,java.io.FileNotFoundException

  • 运行时异常 非受检异常 继承RuntimeException,RuntimeException本身和子类 编译时不会报错,运行时才会报错

1.3如何处理异常

在java中有两个方式,一个是try...catch,一个是throws

  • try..catch(捕获异常):将可能发生异常的代码放在try代码中,然后使用catch来捕获对应的异常

  • 如果try代码块正常运行,那么catch就不会生效;如果发生了指定异常,则会执行catch代码块

  • 捕获异常时还可以接上finally代码块,无论有没有发生异常,finally代码块都会执行

public static void main(String[] args) throws Exception {
        try {
            throw  new Exception();
        }catch (IOException e){
            System.out.println(e);
        }catch (Exception e){
            System.out.println("111"+e.toString());
        }finally {
            System.out.println("hahaha");
        }
    }

throws

在方法上使用throws关键字可以声明该方法可能会抛出的异常

当我们调用一个方法时,如果这个方法用throws关键字声明了受检异常,此时我们就必须手动处理它声明的异常,否则就会编译失败(要么使用讨try...catch,要么在当前方法中使用throws声明同样的或其父类异常)

如果一个问题或者一个异常发生了后就一直往上抛,到最顶层的main方法,都没有人去try...catch处理,那么程序就会终止运行

如果碰到了会影响正常逻辑的情况,基本就这三种处理方式

2.常见运行时异常

  1. 数组索引越界异常ArrayIndexOutOfBoundsException

  2. 空指针异常NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错

  3. 数字操作异常ArithmeticException

  4. 类型转换异常ClassCastException

  5. 数字转换异常NumberFormatException

运行时异常一般是程序员业务没有考虑好或者是编程逻辑不够严谨引起的程序错误,自己水平有问题

public static void main(String[] args)  {
     String a=null;
        System.out.println(a);
        System.out.println(a.length());//调用空指针变量的功能会出错	
 }

3.常见编译时异常

当调用一个抛出异常的方法的时候,调用者必须处理这个异常(try...catch或者throws),如果不处理,编译失败

4.异常的默认处理流程

  1. 默认会在出现异常的代码那里自动创建一个异常对象ArithmeticException

  2. 异常会从方法中出现的点抛出给调用者,调用者最终抛出给JVM虚拟机

  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据

  4. 直接从当前执行的异常点干掉当前程序

  5. 后续代码没有机会执行了,因为程序已经死亡了

5.Throwable类常用的方法

  • String getMessage()返回异常发生时的简要描述

  • String toString()返回异常发生时的写详信息

  • void printStackTrace()在控制台打印Throwable对象封装的异常信息

  • public synchronized throwable getCause()获取实际的异常原因

6.Throws和throw的区别

throw

  • 用在方法体内,根的是异常对象名=>new

  • 只能抛出一个异常对象名

  • throw是用来主动制造异常并抛出异常,由方法体内的语句处理

  • throw是抛出了异常,执行throw则一定抛出了某种异常

  • 关键字throw后面,写异常类的对象=>new

throws

  • 关键字声明不是方法内部

  • throws后面跟的是异常类名,不是对象

  • throws是声明方法可能出现的异常,throw表示抛出异常,由方法调用者处理

  • throws表示出现异常的一种可能性,并不一定会发生这些异常

4.IO流

1.初识Java IO

IO即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递

Java就是通过处理IO的

流,是一个抽象的概念,是指一连串的数据(字符和字节),是以先进先出的方式发送信息通道

当程序需要读取数据的时候,就会开启个通向数据源的流,这个数据源可以是文件,内存或者是网路连接 类似当程序需要写入数据的时候,就会开启一个通向目的地的流

流的几个特性:

  1. 先进先出 最先写入输出流的数据最先被输入流读取到

  2. 顺序读取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据(RandomAccessFile除外)

  3. 只读或只写:每一个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读取操作,对输出流只能进行写操作 在一个数据传输通道中,如果既要写入数据,又要读取数据,则分别提供两个流

1.1IO流分类

IO流主要有以下三种:

  1. 按数据流方向:输入流,输出流

  2. 按处理数据单位:字节流,字符流

  3. 按功能:节点流,处理流

1.输入流和输出流

输入和输出是相对于程序而言

2.字节流和字符流

字节流和字符流用法几乎一样,区别于所操作的数据单元不同,字节流操作的单位是数据单元是8位的一个字节,字符流操作的是数据单元为16的字符,2个字节(不同编码形式可能不同)

为什么要有字符流?

Java中字符采用Unicode标准,Unicode编码中,一个英文字母或一个汉字为两个字节

在UTF-8中,一个中文字符是三个字节,如果用字节流处理中文,如果一次读写一个对应的字节数就不会出问题,一旦将一个字符对应的字节分裂开就会出现乱码 为了方便出现了字符流

Java中字符采用Unicode标准,Unicode编码中,一个英文字母或一个汉字为两个字节

在UTF-8中,一个中文字符是三个字节,如果用字节流处理中文,如果一次读写一个对应的字节数就不会出问题,一旦将一个字符对应的字节分裂开就会出现乱码 为了方便出现了字符流

字节流和字符流的区别:

  • 字节流一般用来处理图像 字符流一般处理纯文本,但是不能处理视频 字节流可以处理一切文件,而字符流只能处理纯文本文件

  • 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率高 而字符流本身带有缓冲区,缓冲字符流相对于字符流效率提升就不大

3.节点流和处理流

节点流:直接操作数据读写的流类,比如FileInputStream

处理流:对一个已经存在的流的链接和封装,通过对数据进行处理为程序提供功能强大,灵活的读写功能,比如BufferedInputStream(缓冲字节流)

程序与磁盘的交互相对于内存来说是很慢的,容易成为程序的性能瓶颈 减少程序与磁盘的交互是提升效率的一种手段 缓冲流,就应用这种思路 普通流每次读写一个字节,而缓冲流在内存中设置一个缓冲区,缓冲区先存储好足够的操作数,程序再与内存或磁盘进行交互

但是有些情况缓冲流效率并不高

1.2案例操作

1.FileInputStream,FIleOutStream(字节流)字节流效率低不建议使用

public class IOTest {
	public static void main(String[] args) throws IOException {
		File file = new File("D:/test.txt");
		Write(file);
		System.out.println(Read(file));
	}
	public static void Write(File file) throws IOException {
		OutputStream os = new FileOutputStream(file, true);
		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		// 写入文件
		os.write(string.getBytes());
		// 关闭流
		os.close();
	}
	public static String Read(File file) throws IOException {
		InputStream in = new FileInputStream(file);

		// 一次性取多少个字节
		byte[] bytes = new byte[1024];
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 读取到的字节数组长度,为-1时表示没有数据
		int length = 0;
		// 循环取数据
		while ((length = in.read(bytes)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(new String(bytes, 0, length));
		}
		// 关闭流
		in.close();
		return sb.toString();
	}
}

2.BufferedInputStream,BufferedOutputStream(缓冲字节流)

缓冲字节流是为了高效率设计的,真正的读写操作还是靠FileOutputStream和FileInputStream,所以其构造方法入参是这两个对象也不奇怪

public class IOTest {
	public static void Write(File file) throws IOException {
		// 缓冲字节流,提高了效率
		BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream(file, true));
		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		// 写入文件
		bis.write(string.getBytes());
		// 关闭流
		bis.close();
	}
	public static String Read(File file) throws IOException {
		BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
		// 一次性取多少个字节
		byte[] bytes = new byte[1024];
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 读取到的字节数组长度,为-1时表示没有数据
		int length = 0;
		// 循环取数据
		while ((length = fis.read(bytes)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(new String(bytes, 0, length));
		}
		// 关闭流
		fis.close();
		return sb.toString();
	}
}

3.InputStreamReader,OuterStreamWriter(字符流)

字符流适用于文本文件的读写,OuterputStreamWriter类其实也是借助FileOutputStream类实现的,故其构造方法也是FileOutputStream的对象

public class IOTest {
	public static void Write(File file) throws IOException {
		// OutputStreamWriter可以显示指定字符集,否则使用默认字符集
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8");
		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		osw.write(string);
		osw.close();
	}
	public static String Read(File file) throws IOException {
		InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "UTF-8");
		// 字符数组:一次读取多少个字符
		char[] chars = new char[1024];
		// 每次读取的字符数组先append到StringBuilder中
		StringBuilder sb = new StringBuilder();
		// 读取到的字符数组长度,为-1时表示没有数据
		int length;
		// 循环取数据
		while ((length = isr.read(chars)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(chars, 0, length);
		}
		// 关闭流
		isr.close();
		return sb.toString()
	}
}

4.字符流便捷类

Java提供了FileWriter和FileReader简化字符流的读写,new FileWriter等等与new OuterStreamWriter(new FileOutputStream(file,true))

public class IOTest {
	public static void Write(File file) throws IOException {
		FileWriter fw = new FileWriter(file, true);
		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		fw.Write(string);
		fw.close();
	}
	public static String Read(File file) throws IOException {
		FileReader fr = new FileReader(file);
		// 一次性取多少个字节
		char[] chars = new char[1024];
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 读取到的字节数组长度,为-1时表示没有数据
		int length;
		// 循环取数据
		while ((length = fr.read(chars)) != -1) {
			// 将读取的内容转换成字符串
			sb.append(chars, 0, length);
		}
		// 关闭流
		fr.close();
		return sb.toString();
	}
}

5.BufferedReader和BufferedWriter(字符缓冲流)

public class IOTest {
	public static void Write(File file) throws IOException {
		// BufferedWriter fw = new BufferedWriter(new OutputStreamWriter(new
		// FileOutputStream(file, true), "UTF-8"));
		// FileWriter可以大幅度简化代码
		BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
		// 要写入的字符串
		String string = "松下问童子,言师采药去。只在此山中,云深不知处。";
		bw.write(string);
		bw.close();
	}
	public static String Read(File file) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(file));
		// 用来接收读取的字节数组
		StringBuilder sb = new StringBuilder();
		// 按行读数据
		String line;
		// 循环取数据
		while ((line = br.readLine()) != null) {
			// 将读取的内容转换成字符串
			sb.append(line);
		}
		// 关闭流
		br.close();
		return sb.toString();
	}
}

2.IO流对象

2.1File类

file是用来操作文件的类,,但它不能操作文件中的数据

File中的构造方法就在这里不赘述了,有兴趣的小伙伴去api查看

2.2字节流

InputStream与OutputStream是两个抽象类,是字节流的基类,所有具体的字节流实现类分别继承了这两个类

以InputStream为例,它继承了Object,实现了Closeable

InputStream有很多的实现子类,下面列举一些比较常用的

  1. InputStream是所有字节流输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模版存在的,为所有实现类定义了处理输入流的方法

  2. FileInputStream文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作

  3. PipedInputStream管道字节输入流,能实现多线程间的管道通信

  4. ByteArrayInputStream字节数组输入流,从字节数组(byte[])中进行以字节为单位i的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去

  5. DateInputStream数据输入流,它用来修饰其他输入流,作用是"允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型"

  6. BufferedInputStream缓冲流,对字节流进行修饰,内部会有一个缓冲区,用来存放字节,每次都是将缓冲区存满然后发送,而不是一个字节或2个字节这样发送效率更高

  7. ObjectInputStream对象输入流,用来提供对基本数据或对象的持久存储 通俗来说,就是能直接传输对象,通常应用在反序列化中 他也是一种处理流,构造器的入参是一个InputStream的实例对象

OutputStream类继承关系图

OutputStream类继承与InputStream类似,需要注意PrintStream

2.3字符流

与字节流类似,字符流也有两个抽象基类,分别是Reader和Writer 其他的字符流实现类都是继承了这两个类

以Reader为了,它的主要实现子类如下图

各个类详细说明

  1. InputStreamReader从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符 它使用的字符集可以通过名称指定,也可以显式给定,或接受平台的默认字符集

  2. BufferedReader从字符缓冲流中读取文本,设置一个缓冲区来提高效率 BufferedReader是对InputStreamReader的封装,前者构造器的入参是后者的一个实例对象

  3. FileReader用于读取字符文件的便利类,new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file,true),"UTF-8"),但FileReader不能指定字符编码和默认缓冲字符区

  4. PipedReader管道字符输入流 实现多线程间的管道通信

  5. CharArrayReader从char数组中读取数据的介流值

  6. StringReader从String中读取数据的介流值

Writer和Reader结构类似,方向相反 唯一区别Writer的子类PrintWriter

3.IO流

3.1字节流方法

字节输入流InputStream主要方法:

  • read从此输入流中读取一个数据字节

  • read(byte[]b)从此输入流中将最多b.length个字节数据读入一个byte数组中

  • read(byte[]b,int off,int len)从此输入流中将最多len个字节的数据读入一个byte数组中,off表示从哪个位置开始开始

  • close()关闭此输入流并释放与该流相关联的所有系统资源

字节输出流OutputStream主要方法:

  • write(byte[]b)将b.length个字节从指定byte数组写入此文件输出流中

  • write(byte[]b,int off,int len)将指定byte数组中从偏移量off开始的len个字节写入此文件输出流

  • write(int b)将指定字节写入此文件输入流

  • close关闭此输入流并释放与该流相关联的所有系统资源

3.2字符流方法

字符流输入Reader主要方法

  • read读取单个字符

  • read(char[]cbuf)将字符读入数组

  • read(char[]cbuf,int off,int len)将字符读入数组的某一部分

  • read(charbuffer target)试图将字符读入指定的字符缓冲区

  • flus(刷新该流的缓冲区)

  • close关闭此流,但是要先刷新它

字符流输出writer方法:

  • write(char []cbuf)写入字符数组

  • write(char[] cbuf,int off,int len)写入字符数组某一部分

  • write(int c)写入单个字符

  • write(String str)写入字符串

  • write(String str,int off,int len)写入字符串某一部分

  • flush()刷新该缓冲区

  • close

另外字符缓冲流还有两个独特的方法

bufferedWriter类newLine()写入一个行分隔符 这个方法会自动适配所在系统的行分隔符

bufferedReader类newLine()读取一个文本行

3.3比较速度快慢

public class IOTest {
	public static void main(String[] args) throws IOException {
		// 数据准备
		dataReady();
		File data = new File("C:/Mu/data.txt");
		File a = new File("C:/Mu/a.txt");
		File b = new File("C:/Mu/b.txt");
		File c = new File("C:/Mu/c.txt");
		long start = System.currentTimeMillis();
		copy(data, a);
		long end = System.currentTimeMillis();
		long start2 = System.currentTimeMillis();
		copyChars(data, b);
		long end2 = System.currentTimeMillis();
		long start3 = System.currentTimeMillis();
		bufferedCopy(data, c);
		long end3 = System.currentTimeMillis();
		System.out.println("普通字节流1耗时:" + (end - start) + " ms,文件大小:" + a.length() / 1024 + " kb");
		System.out.println("普通字节流2耗时:" + (end2 - start2) + " ms,文件大小:" + b.length() / 1024 + " kb");
		System.out.println("缓冲字节流耗时:" + (end3 - start3) + " ms,文件大小:" + c.length() / 1024 + " kb");
	}

	// 普通字符流不使用数组
	public static void copy(File in, File out) throws IOException {
		Reader reader = new FileReader(in);
		Writer writer = new FileWriter(out);

		int ch = 0;
		while ((ch = reader.read()) != -1) {
			writer.write((char) ch);
		}
		reader.close();
		writer.close();
	}
	// 普通字符流使用字符流
	public static void copyChars(File in, File out) throws IOException {
		Reader reader = new FileReader(in);
		Writer writer = new FileWriter(out);
		char[] chs = new char[1024];
		while ((reader.read(chs)) != -1) {
			writer.write(chs);
		}
		reader.close();
		writer.close();
	}
	// 缓冲字符流
	public static void bufferedCopy(File in, File out) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(in));
		BufferedWriter bw = new BufferedWriter(new FileWriter(out));
		String line = null;
		while ((line = br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		// 释放资源
		bw.close();
		br.close();
	}
	// 数据准备
	public static void dataReady() throws IOException {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < 600000; i++) {
			sb.append("abcdefghijklmnopqrstuvwxyz");
		}
		OutputStream os = new FileOutputStream(new File("C:/Mu/data.txt"));
		os.write(sb.toString().getBytes());
		os.close();
		System.out.println("完毕");
	}
}

;