IO文件读写
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream |
字节输出流 OutputStream |
字符流 | 字符输入流 Reader |
字符输出流 Writer |
字节流
字节输出流
java.io.OutputStream
:这个抽象类是表示字节输出流的所有类的超类。
方法:
void close()
:关闭此输出流并释放与此流相关联的任何系统资源。void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。void write(byte[] b)
:将b.length
字节从指定的字节数组写入此输出流。void write(byte[] b, int off, int len)
:从指定的字节数组写入len
个字节,从偏移off
开始输出到此输出流。abstract void write(int b)
:将指定的字节写入此输出流。
FileOutputStream类
java.io.FileOutputStream extends OutputStream
把内存中的数据写入到硬盘的文件中
构造方法:
-
FileOutputStream(String name)
:创建文件输出流以指定的名称写入文件。 -
FileOutputStream(File file)
:创建文件输出流以写入由指定的File
对象表示的文件。
构造方法的作用:
- 创建一个FileOutputStream对象
- 根据构造方法中传递的文件/文件路径,创建一个空的文件
- 把FileOutputStream对象指向创建好的文件
写入数据的原理(内存 --> 硬盘):
java程序 --》 JVM(java虚拟机) --》OS(操作系统) --》OS调用写数据的方法 --》 把数据写入到文件中
案例:
package com.jz.demo;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节输出流的使用步骤:
1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地。
2.调用FileOutputStream对象中的方法write,把数据写入到文件中。
3.释放资源。
*/
public class OutputStreamTest01 {
public static void main(String[] args) throws IOException {
// 1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地。
FileOutputStream fos = new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\a.txt");
// 2.调用FileOutputStream对象中的方法write,把数据写入到文件中。
fos.write(98);
// 3.释放资源
fos.close();
}
}
package com.jz.demo;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
/*
一次写多个字节的方法:
public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
public void write(byte[] b, int off, int len):从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
*/
public class OutputStreamTest02 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\b.txt");
fos.write(49);
fos.write(48);
fos.write(48);
/*
public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
一次写多个字节:
如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
*/
byte[] bytes = {65, 66, 67, 68}; // ABCD
// byte[] bytes = {-65, -66, -67, 68, 69}; // 烤紻E
fos.write(bytes);
/*
public void write(byte[] b, int off, int len):从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
把字节数组的一部分写入文件中
int off:开始的索引
int length:写几个字节
*/
fos.write(bytes, 1, 2); // BC
/*
写入字符串方法:可以使用String类中的方法把字符串,转换为字节数组
byte[] getBytes() 把字符串转换为字节数组
*/
byte[] bytes1 = "你好".getBytes();
System.out.println(Arrays.toString(bytes1)); // [-28, -67, -96, -27, -91, -67]
fos.write(bytes1);
fos.close();
}
}
数据追加续写:
package com.jz.demo;
import java.io.FileOutputStream;
import java.io.IOException;
/*
FileOutputStream(File file, boolean append):创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name, boolean append):创建文件输出流以指定的名称写入文件。
*/
public class OutputStreamTest03 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\c.txt", true);
for (int i = 0; i < 10; i++) {
fos.write("你好".getBytes());
fos.write("\r\n".getBytes());
}
fos.close();
}
}
字节输入流
java.io.InputStream
方法:
-
public void close()
:关闭此输入流并释放与流相关联的任何系统资源。 -
public abstract int read()
:从该输入流读取下一个数据字节。 -
public int read(byte[] b)
:从该输入流读取最多byte.length
个字节的数据到字节数组。
FileInputStream类
java.io.FileInputStream extends InputStream
FileInputStream:文件字节输入流
作用:把硬盘文件中的数据,读取到内存中使用
构造方法:
-
FileInputStream(String name)
String name:文件的路径
-
FileInputStream(File file)
File file:文件
构造方法的作用:
- 创建一个FileInputStream对象
- 把FileInputStream对象指定构造方法中要读取的文件
读取数据原理(硬盘-->内存)
java程序 --> JVM --> OS --> OS读取数据的方法 -->读取文件
package com.jz.demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFile {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("c:\\1.png");
FileOutputStream fos = new FileOutputStream("d:\\1.png");
int len = 0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes))!= -1){
fos.write(bytes, 0 , len);
}
// while((len = fis.read()) != -1){
// fos.write(len);
// }
fos.close();
fis.close();
}
}
字符流
使用字节流在读取中文字符时,可能出现显示乱码问题。
字符输入流
java.io.Reader
抽象类是表示用于读取字符流的所有类的超类。
共性方法:
-
public void close()
释放资源 -
public int read()
从输入流读取一个字符 -
public int read(char[] cbuf)
从输入流中读取一些字符,并将它们存储到字符数组cbuf中。
public class FileReader extends InputStreamReader
package com.jz.reader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
public class ReaderDemo01 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\c.txt");
int len = 0;
char[] cs = new char[1024];
while((len = fr.read(cs)) != -1){
System.out.println(len);
System.out.println(new String(cs, 0, len));
}
fr.close();
}
}
字符输出流
java.io.Writer
字符输出流,所有字符输出流的最顶层的父类,是一个抽象类。
public class FileWriter extends OutputStreamWriter extends Writer
package com.jz.writer;
import java.io.FileWriter;
import java.io.IOException;
public class WriterDemo01 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\d.txt");
fw.write(97); // 把数据写入到内存缓冲区(字符转换为字节的过程)
fw.flush(); // 把内存缓冲区中的数据,刷新到文件中
fw.close(); // 释放资源之前先把内存缓冲区中的数据刷新到文件中
}
}
由于关闭(close) 和 刷新(flush)都可以将内存缓冲区中的数据,刷新到文件中
flush方法和close方法的区别:
- flush 刷新缓冲区,流对象可以继续使用
- close 先刷新缓冲区,然后通知系统释放资源。流对象不能再被使用
package com.jz.writer;
import java.io.FileWriter;
import java.io.IOException;
/*
字符输出流写数据的其他方法
*/
public class WriterDemo01 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\d.txt");
char[] cs = {'a', 'b', 'c', 'd'};
fw.write(cs);// void write(char[] cbuf) 写入字符数组
fw.write(cs, 1, 2); // 写入数组的一部分数据
fw.write("嘿嘿嘿"); // 直接写入字符串
fw.write("嘿嘿嘿呼呼呼", 3, 3); // 写入一部分字符串
fw.flush();
fw.close();
}
}
续写和换行:
package com.jz.writer;
import java.io.FileWriter;
import java.io.IOException;
/*
字符输出流写数据的其他方法
*/
public class WriterDemo01 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("D:\\CODE\\JAVA\\IDEA\\javabase\\io\\d.txt", true);
fw.write("\r\n"); // 回车换行
char[] cs = {'a', 'b', 'c', 'd'};
fw.write(cs);// void write(char[] cbuf) 写入字符数组
fw.write(cs, 1, 2); // 写入数组的一部分数据
fw.write("嘿嘿嘿"); // 直接写入字符串
fw.write("嘿嘿嘿呼呼呼", 3, 3); // 写入一部分字符串
fw.flush();
fw.close();
}
}
缓冲流
字节缓冲流
字节缓冲输出流
java.io.BufferedOutputStream extends OutputStream
package com.jz.io;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedOutputStreamDemo01 {
public static void main(String[] args) throws IOException {
// 1.创建FileOutputStream对象
FileOutputStream fos = new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\a.txt");
// 2.创建BufferedOutputStream对象
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 3.使用BufferedOutputStream对象中的write方法,把数据写入内存缓冲区
bos.write("我把数据写入缓冲qv".getBytes());
bos.flush();
bos.close();
}
}
字节缓冲输入流
java.io.BufferedInputStream extends InputStream
package com.jz.io;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedInputStreamDemo01 {
public static void main(String[] args) throws IOException {
//1.创建FileInputStream对象
FileInputStream fis = new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\a.txt");
//2.创建BufferedInputStream对象
BufferedInputStream bis = new BufferedInputStream(fis);
//3.使用BufferedInputStream对象中的read,读取文件
// int read()从输入流中读取数据的下一个字节
/*int len = 0; // 记录每次读取到的字节
while((len = bis.read()) != -1){
System.out.println((char)len);
}*/
// int read(byte[] b)从输入流中读取一定数量的字节,并将其存储到缓冲区数组b中
byte[] bytes = new byte[1024];
int len = 0; //记录每次读取的有效字节个数
while((len = bis.read(bytes)) != -1){
System.out.println(new String(bytes, 0, len));
}
bis.close();
}
}
文件复制:
package com.jz.io;
import java.io.*;
public class CopyFile {
public static void main(String[] args) throws IOException {
long s = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:\\1.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\1.png"));
/*int len = 0;
while((len = bis.read()) != -1){
bos.write(len);
}*/
byte[] bytes = new byte[1024];
int len = 0;
while((len = bis.read(bytes)) != -1){
bos.write(bytes);
}
bos.close();
bis.close();
long e = System.currentTimeMillis();
System.out.println("共用了: "+(e-s)+"毫秒");
}
}
字符缓冲流
字符缓冲输出流
java.io.BufferedWriter extends Writer
package com.jz.io;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo01 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\CODE\\JAVA\\IDEA\\IO\\b.txt"));
for (int i = 0; i < 10; i++) {
bw.write("嘿嘿嘿");
//bw.write("\r\n");
bw.newLine();
}
bw.flush();
bw.close();
System.out.println();
}
}
字符缓冲输入流
java.io.BufferedReader extends Reader
package com.jz.io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo01 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\CODE\\JAVA\\IDEA\\IO\\b.txt"));
/*String line = br.readLine();// 一次读取一行数据
System.out.println(line);*/
String line = "";
while((line = br.readLine()) != null){ // 读取到null结束
System.out.println(line);
}
br.close();
}
}
文件内容排序:
package com.jz.io;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) throws IOException {
Map map = new HashMap<>();
BufferedReader br = new BufferedReader(new FileReader("D:\\CODE\\JAVA\\IDEA\\IO\\b.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\CODE\\JAVA\\IDEA\\IO\\c.txt"));
String line = "";
while((line = br.readLine()) != null){
String[] split = line.split("\\.");
map.put(split[0], split[1]);
}
Set> set = map.entrySet();
for (Map.Entry entry : set) {
bw.write(entry.getKey()+"."+entry.getValue());
bw.newLine();
}
bw.close();
br.close();
}
}
转换流
指定编码表
转换流中的输出流
java.io.OutputStreamWriter extends Writer
OutputStreamWriter:字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码称字节。
构造方法:
-
OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter -
OutputStreamWriter(OutputStream out, String charsetName)
创建使用指定字符集的 OutputStreamWriter。
package com.jz.io;
import java.io.*;
public class OutputStreamWriterDemo01 {
public static void main(String[] args) throws IOException {
// write_utf_8();
write_gbk();
}
private static void write_gbk() throws IOException {
// 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\gbk.txt"), "gbk");
// 使用OutputStreamWriter对象中的方法writer,把字符转换为字节存储缓冲区中(编码)
osw.write("你好");
// 使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷洗到文件中(使用字节流写字节的过程)
osw.flush();
osw.close();
}
/*
使用转换流OutputStreamWriter写UTF-8格式的文件
*/
private static void write_utf_8() throws IOException {
// 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\utf_8.txt"), "utf-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\utf_8.txt")); // 不指定编码默认utf-8
// 使用OutputStreamWriter对象中的方法writer,把字符转换为字节存储缓冲区中(编码)
osw.write("你好");
// 使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷洗到文件中(使用字节流写字节的过程)
osw.flush();
osw.close();
}
}
转换流中的输入流
java.io.InputStreamReader extends Reader
InputStreamReader:字节流通向字符流的桥梁,它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)
构造方法:
-
InputStreamReader(InputStream in)
创建一个使用默认字符集的 InputStreamReader -
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader
注意事项:
构造方法中指定的编码表名称要和文件的编码相同,否则会出现乱码
package com.jz.io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws IOException {
// read_utf_8();
read_gbk();
}
/*
使用InputStreamReader读取gbk格式的文件
*/
private static void read_gbk() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\gbk.txt"), "gbk");
int len = 0;
while((len = isr.read()) != -1){
System.out.println((char)len);
}
isr.close();
}
/*
使用InputStreamReader读取UTF-8格式的文件
*/
private static void read_utf_8() throws IOException {
// InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\utf_8.txt"), "utf-8");
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\utf_8.txt"));
// 使用InputStreamReader对象中的方法read读取文件
int len = 0;
while((len = isr.read()) != -1){
System.out.println((char)len);
}
isr.close();
}
}
案例:转换文件编码
package com.jz.io;
import java.io.*;
public class Test2 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\gbk.txt"), "gbk");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\utf8.txt"), "utf-8");
int len = 0;
while((len = isr.read()) != -1){
osw.write(len);
}
osw.flush();
osw.close();
isr.close();
}
}
序列化
对象序列化流
java.io.ObjectOutputStream extends OutputStream
作用:把对象以流的方式写入到文件中保存
构造方法:
-
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream.
特有的成员方法:
void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream
序列化异常:
Exception in thread "main" java.io.NotSerializableException
没有序列化异常
package com.jz.objectStream;
import java.io.Serializable;
/*
序列化和反序列化的时候,会抛出NotSerializableException没后序列化异常
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
*/
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.jz.objectStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamDemo01 {
public static void main(String[] args) throws IOException {
// 创建ObjectOutputStream对象,构造方法中传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\person.txt"));
// 使用ObjectOutputStream对象中的方法writeObject,把对象写入文件中
Person p = new Person("小明", 19);
oos.writeObject(p);
oos.flush();
oos.close();
}
}
对象反序列化流
java.io.ObjectInputStream extends InputStream
作用:把文件中保存的对象,以流的方式读取出来使用
构造方法:
-
ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream
特有的成员方法:
-
Object readObject()
从ObjectInputStream
读取对象声明抛出了ClassNotFoundException异常
反序列化前提:
- 类必须实现Serializable
- 必须存在类对应的class文件
package com.jz.objectStream;
import java.io.Serializable;
/*
序列化和反序列化的时候,会抛出NotSerializableException没后序列化异常
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
*/
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.jz.objectStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStreamDemo01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\person.txt"));
Object o = ois.readObject();
ois.close();
System.out.println(o);
}
}
static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中),被static修饰的成员变量不能被初始化,序列化的都是对象
transient关键字:瞬态关键字
被transient修饰的成员变量,不能被序列化 private transient int age;
反序列化操作:
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。
练习:序列化集合
package com.jz.objectStream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/*
把多个对象存储到集合中,对集合进行序列化和反序列化
*/
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 定义存储Person对象的ArrayList集合
List list = new ArrayList<>();
// 向list集合中添加Person对象
list.add(new Person("小明",18));
list.add(new Person("小红",20));
list.add(new Person("小聪",10));
// 创建序列化流ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\CODE\\JAVA\\IDEA\\IO\\list.txt"));
//使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
oos.writeObject(list);
// 创建反序列化ObjectInputStream对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\CODE\\JAVA\\IDEA\\IO\\list.txt"));
// 使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
Object o = ois.readObject();
// 把Object类型的集合转换为ArrayList类型
ArrayList list2 = (ArrayList) o;
for (Person person : list2) {
System.out.println(person);
}
ois.close();
oos.close();
}
}
打印流
java.io.PrintStream
打印流
PrintStream:为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
PrintStream特点:
只负责数据的输出,不负责数据的读取
于其他输出流不同,PrintStream 永远不会抛出 IOException
-
有特有的方法,print,println
void print(任意类型的值)
void println(任意类型的值并换行)
构造方法:
-
PrintStream(File file)
输出的目的地是一个文件 -
PrintStream(OutputStream out)
输出的目的地是一个字节输出流 -
PrintStream(String fileName)
输出的目的地是一个文件路径
PrintStream extends OutputStream
所以,PrintStream继承自父类的成员方法
注意:
- 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
- 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
package com.jz.printStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class PrintStreamDemo01 {
public static void main(String[] args) throws FileNotFoundException {
// 创建PrintStream对象,构造方法中绑定要输出的目的地
PrintStream ps = new PrintStream("D:\\CODE\\JAVA\\IDEA\\IO\\print.txt");
// 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
ps.write(97);
// 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
ps.println(97);
// System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
System.setOut(ps);
System.out.println("我在打印流的目的地输出");
ps.close();
}
}