第十一章、IO流

89 阅读14分钟

1、常用的文件操作

1.1 基本概念

文件:存储数据的地方,比如图片、pdf、word文档等等。

输入流:文件存储于磁盘,从磁盘读入内存的路径(把内存想做是自己的大脑,磁盘相当于课本,本子的内容记录到脑子里就是输入)。

输出流:从内存到磁盘的路径。

image-20220313084951282

1.2 创建文件对象

相关方法:

new File(String pathname)	//根据路径构建一个File对象
new File(File parent,String child)	//根据父目录文件+子路径构建
new File(String parent,String child)	//根据父目录+子路径构建
    
//创建文件需要使用createNewFile方法
file.createNewFile();

题目:在磁盘下,以三种不同方式创建新文件

  • 方式一:new File(String pathname) //根据路径构建一个File对象
@Test
public void create01(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\news1.txt";
    File file = new File(filePath);
    try {
        file.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 方式二:new File(File parent,String child) //根据父目录文件+子路径构建
@Test
public void create02(){
    //这里是个目录路径
    File file = new File("D:\\笔记和课程\\code review\\javase-io\\");
    String filename = "new2.txt";
    File file1 = new File(file, filename);
    try {
        file1.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 方式三:new File(String parent,String child) //根据父目录+子路径构建
@Test
public void create03(){
    String pathname = "D:\\笔记和课程\\code review\\javase-io\\";
    String filename = "new3.txt";
    File file = new File(pathname,filename);
    try {
        file.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

书中的记录

1、File类不仅可以表示一个特定的文件名,还可以表示一个目录下一组文件名的集合,可以通过file.list()获取集合

File file = new File("D:\\笔记和课程\\code review\\javase-io\\news");
String[] list = file.list();
for(String fileName:list){
    System.out.println(fileName);
}

在这个文件下创建一个目录news,并在它下面创建一些文件,然后按照上面方法就能拿到这些文件名称。

1.3 获取文件相关的信息

File的方法结构:

image-20220313082929757

比较常用的方法是:getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory

@Test
public void info(){
    //创建文件对象
    File file = new File("D:\\笔记和课程\\code review\\javase-io\\news1.txt");
    File file1 = new File("D:\\笔记和课程\\code review\\javase-io\\");
    System.out.println("文件的名字是" + file.getName());
    System.out.println("文件的绝对路径是" + file.getAbsolutePath());
    System.out.println("文件的父路径是" + file.getParent());
    System.out.println("文件的字节长度是" + file.length());
    System.out.println("是否存在该文件" + file.exists());
    System.out.println("是否是文件" + file1.isFile());
    System.out.println("是否是目录" + file1.isDirectory());
}

1.4 目录的操作和文件删除

判断:D:\笔记和课程\code review\javase-io\news下是否有news1.txt,如果有就删除

@Test
public void m1(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\news\\news1.txt";
    File file = new File(filePath);
    if (file.exists()){
        file.delete();
    }else{
        System.out.println("文件不存在");
    }
}

判断:D:\笔记和课程\code review\javase-io\news目录是否存在,如果存在就提示已经存在,否则就创建

@Test
public void m2(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\news";
    File file = new File(filePath);
    if(file.exists()) {
        if(file.delete()){
            System.out.println("文件删除成功");
        }else{
            System.out.println("文件删除失败");
        }
    }else {
        System.out.println("文件不存在");
    }
}

判断:D:\笔记和课程\code review\javase-io\new目录是否存在,如果存在就提示存在,否则就创建

@Test
public void m3() throws IOException {
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\new";
    File file = new File(filePath);
    if (file.exists()){
        System.out.println(filePath + "已存在");
    }else{
        //mkdirs()是创建多级目录,注意不要用mkdir(),这个只能创建一级目录
        if (file.mkdirs()){
            System.out.println("创建成功");
        }else{
            System.out.println("创建失败");
        }
    }
}

2、IO流的分类

2.1 IO流的分类

数据的输入和输出是以Stream流的方式进行的。

java.io包下提供了很多接口和类。

  • 字节流(二进制文件:可以传输声音文件、视频文件)和字符流(文本文件)
  • 输入流和输出流
  • 节点流和处理流(包装流)

image-20220313211118589

具体的结构:

img

2.2 字节输入流和字节输出流

InputStream字节输入流,它是一个抽象类。

public abstract class InputStream implements Closeable {

三个比较重要的子类:

  1. FileInputStream:文件输入流
  2. BufferedInputStream:缓冲字节输入流
  3. ObjectInputStream:对象字节输入流

image-20220313213825814

(1)FileInputStream类

构造方法:

  • FileInputStream(File file)
  • FileInputStream(String name)

相当于通过获取到的流对象去操作磁盘上实际的文件,上面创建的就是流对象。

常用方法:

  • close():关闭流
  • read():从该输入流中读取一个数据字节
  • read(byte[] b):从该输入流中最多读取b.length个字节数据到byte[]数组中
  • read(byte[] b,int off,int len):从该输入流中将最多len个字节的数据读取到byte数组中
@Test
public void readFile01(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\hello.txt";
    int content = 0;
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(filePath);
        //如果read()得到的结果返回为-1,说明全部读取完毕
        while ((content = fis.read()) != -1){
            //这里读取到的值是int类型的,要转换成char类型
            System.out.print((char)content);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            if (fis != null) {
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

还可以改进,可以读取多个字节一起输出,提高效率,这就相当于先读取到一个数组中去,然后把这个数组传给String:

@Test
public void readFile02(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\hello.txt";
    FileInputStream fis = null;
    int content = 0;
    byte[] bytes = new byte[10];
    try {
        fis = new FileInputStream(filePath);
        while ((content = fis.read(bytes)) != -1) {
            System.out.print(new String(bytes,0,content));
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(2)FileOutputStream类

构造方法:

  • FileOutputStream(File file)
  • FileOutputStream(File file,boolean append):创建一个向file对象表示的文件中写入数据的文件输出流,以追加的方式写入
  • FileOutputStream(String name):创建一个向具有指定名称的文件写入数据的输出文件流
  • FileOutputStream(String name,boolean append):创建一个向具有指定name文件中写入数据的输出文件流

常用方法:

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

image-20220314083710745

把hello world写入电脑指定位置的文件中,并且要创建该文件

@Test
public void write01(){
    String filePath = "d:\\a.txt";
    FileOutputStream fos = null;
    String message = "hello world";
    try {
        fos = new FileOutputStream(filePath);
        fos.write(message.getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fos != null){
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如果不想覆盖原来的文件可以采用这种方式FileOutputStream(File file,boolean append),append为true表示在原文件追加

@Test
public void write01(){
    String filePath = "d:\\a.txt";
    FileOutputStream fos = null;
    String message = "hello world!!!";
    try {
        fos = new FileOutputStream(filePath,true);
        fos.write(message.getBytes());
        fos.write(message.getBytes(),0,4);
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fos != null){
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(3)综合-文件的拷贝

@Test
public static void copyFile(){
    //原文件路径
    String srcFilePath = "C:\\Users\\16140\\Pictures\\Saved Pictures\\godfather.jpg";
    String descFilePath = "D:\\godfather.jpg";
    FileInputStream fis = null;
    FileOutputStream fos = null;
    int content = 0;
    byte[] bytes = new byte[1024];
    try {
        fis = new FileInputStream(srcFilePath);
        fos = new FileOutputStream(descFilePath);
        while ((content = fis.read(bytes)) != -1){
            fos.write(bytes,0,content);
        }
        System.out.println("copy finished");
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fos != null){
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.3 字符输入流和字符输出流

(1)FileReader类

构造方法:

  • new FileReader(File/String)

常用方法:

  • read():每次读取单个字符返回该字符,读到文件末尾返回-1
  • read(char[] char):批量读取多个字符到数组,返回读取到的字符数
  • new String(char[] char):将char[]转换成String
  • new String(char[],off,len):将char的指定部分转换成字符串

image-20220314091812609

读取story.txt中的内容,并显示

@Test
public void test01(){
    String filePath= "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    FileReader fr = null;
    int data = 0;
    try {
        fr = new FileReader(filePath);
        while ((data = fr.read()) != -1){
            System.out.print((char)data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fr != null){
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

方式二:

@Test
public void test02(){
    String filePath= "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    FileReader fr = null;
    int readLength = 0;
    char[] chars = new char[10];
    try {
        fr = new FileReader(filePath);
        while ((readLength = fr.read(chars)) != -1){
            System.out.print(new String(chars,0,readLength);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fr != null){
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(2)FileWriter类

构造方法:

  • new FileWriter(File/String):覆盖的方式,相当于流的指针在前端
  • new FileWriter(File/String,true):追加模式,相当于流的指针在尾端

常用方法:

  • write(int):写入单个字符
  • write(char[]):写入指定数组
  • write(char[],off,len):写入指定数组的指定部分
  • write(string):写入整个字符串
  • write(string,off,len):写入字符串的指定部分
注意:FileWriter使用完毕需要关闭或者刷新流,否则不能写入指定文件

image-20220314112454560

将“填平水泊擒晁盖,踏破梁山捉宋江”写入到note.txt文件中

@Test
public void write1(){
    String filePath = "d:\\note.txt";
    FileWriter fw = null;
    String message = "填平水泊擒晁盖,踏破梁山捉宋江\r";
    try {
        fw = new FileWriter(filePath,true);
        //fw.write(message);
        fw.write(message,0,message.length());
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        if (fw != null) {
            try {
                fw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

看源码会发现close和flush方法最终走向是一样的。

2.4 节点流和处理流

(1)基本概念

节点流:就是从特定的数据源读写数据的,比如FileReader和FileWriter等。

  • 比如针对带中文的文件:可以使用Reader或Writer
  • 针对二进制文件:可以使用InputStream或OutputStream
  • 针对数组:可以使用ByteArrayInputStream或ByteArrayOutputStream

处理流:也叫包装流,它是在已经存在的流之上,给其提供更加强大的读写功能

  • 比如BufferedReader或BufferedWriter
public class BufferedReader extends Reader {
    private Reader in;
    ......
}

可见BufferedReader缓冲字符输入流包含了Reader,这也就是说,可以使用这个来包含不同类型的Reader和Writer,以达到自己需要的目的。

image-20220314210831306

看下面:BufferedWriter(Writer):这里就是说可以传一个Writer对象,也就包含了任意类型的Writer子类的对象。

image-20220314211226237

这也叫做装饰器模式

节点流和处理流:节点流偏底层,直接操作数据源;而处理流可以包装节点流,提供更加强大的功能。

装饰器代码

创建一个抽象类当作父类

public abstract class Reader_ {
    public void readFile(){};

    public void readString(){};
}

分别写两个不同类型的子类

public class FileReader extends Reader_{
    public void readFile(){
        System.out.println("读取文件...");
    }
}

public class StringReader extends Reader_{
    public void readString(){
        System.out.println("读取字符串...");
    }
}

写一个Buffered类

public class BufferedReader_ extends Reader_{
    private Reader_ reader;//Reader_属性

    public BufferedReader_(Reader_ reader) {
        this.reader = reader;
    }

    public void readFiles(int nums){
        while (nums > 0){
            reader.readFile();
            nums--;
        }
    }

    public void readString(int nums){
        while (nums > 0){
            reader.readString();
            nums--;
        }
    }
}

写一个测试类

public class Test01 {
    public static void main(String[] args) {
        BufferedReader_ bufferedReader_1 = new BufferedReader_(new FileReader());
        bufferedReader_1.readFiles(5);
        BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader());
        bufferedReader_2.readString(10);
    }
}

//输出
读取文件...
读取文件...
读取文件...
读取文件...
读取文件...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...
读取字符串...

(2)BufferedReader类

BufferedReader和BufferedWriter都是属于字符流,按照字符读取,因为是外层包内层,所以只需要关闭外层流,就可以关闭内层。

image-20220314222026531

使用BufferedReader读取文本文件,并显示在控制台

@Test
public void testBufferedReader(){
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    BufferedReader bufferedReader = null;
    String line;
    try {
        bufferedReader = new BufferedReader(new FileReader(filePath));
        //readLine就相当于是增加的功能,读取的为null的时候,说明读取完了
        while ((line = bufferedReader.readLine()) != null){
            System.out.println(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            bufferedReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

debug进bufferedReader.close()方法,可以看到调用了fileReader.close();也就是把内层的流给关闭了

image-20220314223607077

(3)BufferedWriter类

写入文件

@Test
public void testBufferedWriter() throws IOException {
    String filePath = "d:\\test.txt";
    BufferedWriter bufferedWriter = null;
    bufferedWriter = new BufferedWriter(new FileWriter(filePath));
    bufferedWriter.write("他时若遂凌云志");
    //添加一个换行符
    bufferedWriter.newLine();
    bufferedWriter.write("敢笑黄巢不丈夫");
    bufferedWriter.newLine();
    bufferedWriter.close();
}

(4)综合-文件的拷贝

使用BufferedReader和BufferedWriter完成文本文件拷贝

public static void main(String[] args){
    String srcFilePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    String descFilePath = "D:\\笔记和课程\\code review\\javase-io\\story1.txt";
    BufferedReader br = null;
    BufferedWriter bw = null;
    String line;
    try {
        br = new BufferedReader(new FileReader(srcFilePath));
        bw = new BufferedWriter(new FileWriter(descFilePath));
        while ((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null){
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bw != null){
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注意:BufferedReader和BufferedWriter不要去操作二进制文件。

(5)BufferedInputStream

和BufferedReader相似。

(6)BufferedOutputStream

和BufferedWriter相似。

完成图片或音乐的拷贝

public static void main(String[] args){
    String srcFilePath = "D:\\笔记和课程\\code review\\javase-io\\云彩.jpg";
    String descFilePath = "D:\\笔记和课程\\code review\\javase-io\\云彩1.jpg";
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;

    try {
        bis = new BufferedInputStream(new FileInputStream(srcFilePath));
        bos = new BufferedOutputStream(new FileOutputStream(descFilePath));

        byte[] buffer = new byte[1024];
        int readLine = 0;
        while ((readLine = bis.read(buffer)) != -1){
            bos.write(buffer,0,readLine);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意,这个可以使用于文本文件。

(7)ObjectInputStream类

如果我们想把一个对象保存到文件中时,直接保存它的值是不行的,需要保存它的值和数据类型,这种操作就是序列化,反之,把文件中的数据值和数据类型恢复到程序中叫做反序列化。

这里可以实现两个接口实现:

  • Serializable:这个很方便
  • Externalizable:这个需要实现方法,不建议使用

image-20220315210456267

使用ObjectInputStream来从文件中反序列化数据

public class ObjectInputStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String filePath = "D:\\笔记和课程\\code review\\javase-io\\dog.txt";
        ObjectInputStream ois = null;

        ois = new ObjectInputStream(new FileInputStream(filePath));
        //一定要安装顺序读取
        System.out.println(ois.read());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readDouble());
        //注意这里取出的是Object类型,如果想用需要向下转型
        Object dog = ois.readObject();
        //向下转型如果Dog类不是公共的,会找不到,所以建议把Dog类提出来变为public
        Dog dog1 = (Dog) dog;
        ((Dog) dog).eat();
        ois.close();
    }
}

(8)ObjectOutputStream类

image-20220315210625283

使用ObjectOutputStream保存基本数据类型和应用数据类型对象到文件中

public class ObjectOutputStream_ {
    public static void main(String[] args) throws IOException {
        String filePath = "D:\\笔记和课程\\code review\\javase-io\\dog.txt";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.write(100);
        oos.writeBoolean(true);
        oos.writeDouble(10.5);
        oos.writeObject(new Dog("小花",5));
        oos.close();
    }
}

Dog类公开

//注意这里要实现Serializable接口
public class Dog implements Serializable {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    public void eat(){
        System.out.println("eating...");
    }
}

注意事项:

image-20220315213932254

序列号:serialVersionUIO=1L,如果对需要序列化的类又添加新的属性,加了序列号的类就不会认为这是一个新的类。

static和transient修饰的测试:

public class Dog implements Serializable {
    private String name;
    private int age;
    //一个static修饰
    private static String type;
    private transient int price;

    public Dog(String name, int age, String type,int price) {
        this.name = name;
        this.age = age;
        this.type = type;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", price=" + price +
                ", type=" + type +
                '}';
    }

    public void eat(){
        System.out.println("eating...");
    }
}

然后修改另外两个的代码,测试结果为:

100
true
10.5
//price和type就没有值,因为没有被序列化过来
Dog{name='小花', age=5, price=0, type=null}
eating...

2.5 标准输入输出流

名称类型默认设备
System.in 标准输入InputStream键盘
System.out 标准输出PrintStream显示器

看代码:

System.in其实就等于
编译类型就是InputStream
public final static InputStream in = null;

运行类型就是BufferedInputSstream
    
System.out其实就等于
编译类型就是PrintStream
public final static PrintStream out = null;

运行时类型也是PrintStream

2.6 转换流

(1)InputStreamReader类

image-20220316214726706

主要用于解决文件字符集使用造成乱码。

先模拟编码字符集使用非utf-8造成读取文件出现的问题

@Test
public void test01() throws IOException {
    //读取文件
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    BufferedReader br = new BufferedReader(new FileReader(filePath));
    String line;
    while ((line = br.readLine()) != null){
        System.out.println(line);
    }
    br.close();
}

注意把文件的编码改成别的试一下。

如何改进

把字符流改成字节流,这就用到了转换流。

@Test
public void test02() throws IOException {
    //读取文件
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\story.txt";
    InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath),"GBK");
    BufferedReader bf = new BufferedReader(isr);
    String line;
    while ((line = bf.readLine()) != null){
        System.out.println(line);
    }
}

(2)OutputStreamWriter类

image-20220316214805771

写入一些文本到文件,编码不要utf-8,看看是否会出现乱码

@Test
public void test03() throws IOException {
    String filePath = "D:\\笔记和课程\\code review\\javase-io\\test.txt";
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
    osw.write("转换流可以用来转换数据,it's nice");
    osw.close();
}

2.7 打印流

只有输出流,没有输入流。

(1)PrintStream类

image-20220316215852288

public class PrintStream_ {
    public static void main(String[] args) {
        //默认情况下,PrintStream输出的位置是标准输出(显示器)
        PrintStream out = System.out;
        out.print("2 * 5 = 10");
        out.close();
    }
}

这里看一下源码:

public void print(String s) {
    if (s == null) {
        s = "null";
    }
    //调用了write方法
    write(s);
}

既然调用了write()方法,那么也可以直接使用write()方法:

public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        //默认情况下,PrintStream输出的位置是标准输出(显示器)
        PrintStream out = System.out;
        out.print("2 * 5 = 10");
        //等价于print()方法
        out.write("2 * 5 = 10".getBytes());
        out.close();
    }
}

当然也可以设置输出的位置:

System.setOut(new PrintStream("d:\\test.txt"));
System.out.print("aaaaaaa");

(2)PrintWriter类

image-20220316220050889

public class PrintWriter_ {
    public static void main(String[] args) throws FileNotFoundException {
        //输出到显示器上
        //PrintWriter printWriter = new PrintWriter(System.out);
        //输出到指定位置
        PrintWriter printWriter = new PrintWriter(new PrintWriter("D:\\笔记和课程\\code review\\javase-io\\test.txt"));
        printWriter.println("hi bob");
        //不close()是不会输出的
        printWriter.close();
    }
}

2.8 Properties类

mysql的配置文件mysql.properties,如何读取值:

ip = 192.168.200.130

user = root

password = 123456

第一种方式:

@Test
public void getProperties01() throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
    String line = "";
    while ((line = br.readLine()) != null){
        String[] strings = line.split("=");
        System.out.println(strings[0] + "的值是" + strings[1]);
    }
    br.close();
}

第二种方式:

@Test
public void getProperties02() throws IOException {
    Properties props = new Properties();
    props.load(new FileReader("src\\mysql.properties"));
    System.out.println(props.getProperty("username"));
    System.out.println(props.getProperty("password"));
    System.out.println(props.getProperty("address"));
}