JAVA-SE核心高级篇

十一,IO流

1.File类

java.io.File

1)File类的使用

1.file类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
2.File类声明在java.io包下
*相对路径:hello.txt   相较于某个路径下,指明的路径。
 绝对路径:D://temp.txt      包含盘符在内的文件或文件目录的路径。
*路径分隔符:window用\,url用/。

2)如何创建File类的实例:

public static void main(String[] args) {
    File file=new File("D:\\input\\input.txt.txt");
    System.out.println(file);//输出文件路径  D:\input\input.txt.txt
    File file1 = new File("jdbc.properties");
    System.out.println(file1);jdbc.properties
}

3)常用方法:

public static void main(String[] args) {
    File file=new File("D:\\input\\input.txt.txt");
    File file1 = new File("jdbc.properties");
    String path = file.getAbsolutePath();//获取绝对路径
    System.out.println(path);//D:\input\input.txt.txt
    System.out.println(file1.getAbsolutePath());//E:\IEDADEMO\jdbc.properties
    String filePath = file.getPath();//获取路径
    System.out.println(filePath);//D:\input\input.txt.txt
    System.out.println(file1.getPath());//jdbc.properties
    String name = file.getName();//获取名字
    System.out.println(name);//input.txt.txt
    String parent = file.getParent();//获取上一层目录
    System.out.println(parent);//D:\input
    long length = file.length();//长度,字节
    System.out.println(length);//292
    long modified = file.lastModified();//最近修改时间
    System.out.println(modified);//1572326293899
    /**
     * 仅适用于目录
     */
    File file2 = new File("D:\\wokespace");
    String[] list = file2.list();//列出其下一级文件目录
    for (String s:list){
        System.out.println(s);
    }
    System.out.println("***********************************************");
    File[] files = file2.listFiles();//列出其下一级文件目录的绝对路径
    for (File f: files){
        System.out.println(f);
    }
    System.out.println("**************************");
    boolean b = file1.renameTo(file);//把file1重命名为file的名字
    //要想保证成功,file1在硬盘中是真实存在的,file在硬盘中是不存在的
    System.out.println(b);
}
public static void main(String[] args) {
    File file = new File("jdbc.properties");
    //判断功能
    System.out.println(file.isDirectory());//判断是不是一个目录  false
    System.out.println(file.isFile());//判断是不是一个文件true
    System.out.println(file.exists());//判断是否真实存在true
    System.out.println(file.canRead());//判断是否可读true
    System.out.println(file.canWrite());//判断是否可写true
    System.out.println(file.isHidden());//判读是否隐藏  false
}
public static void main(String[] args) {
    //File的创建功能和删除功能
    File file = new File("jdbc1.properties");
    if (!file.exists()){
        try {
            file.createNewFile();//创建文件
            System.out.println("文件创建成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }else{
        file.delete();//删除文件,不走回收站,直接删除
        System.out.println("文件删除成功!");
    }
   file.mkdir();//创建单层目录
    file.mkdirs();//创建多层目录

}

file.delete()删除目录时,要想删除成功,该目录下不能有文件或目录。

4)面试题:

/**
 * 创建一个与File同目录下的另外一个文件,文件名为:haha.txt
 * @param args
 */
public static void main(String[] args) {
    File file = new File("D:\\temp\\temp1.txt");
    File file1 = new File(file.getParent() , "haha.txt");
    System.out.println(file.getParent());
    try {
        boolean newFile = file1.createNewFile();
        if (newFile){
            System.out.println("创建成功!");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

}
/**
 * 遍历指定目录下所文件名称,包括子文件目录中的文件
 * 1.并计算指定目录占用空间的大小
 * 2.删除指定文件下的目录及其下的文件夹
 */
public static void main(String[] args) {
    File file = new File("D:\\temp");
    int sum=print(file);
    System.out.println(sum);
}
//遍历指定目录下所文件名称,包括子文件目录中的文件,并计算指定目录占用空间的大小
public static int print(File file){
    File []files=file.listFiles();
    int sum=0;
    for (File f:files){
        if (f.isDirectory()){
            print(f);
            f.delete();
        }else{
            System.out.println(f.getAbsolutePath());
            sum+=f.length();
            f.delete();
        }
    }
    return sum;
}

public static void main(String[] args) {
    /**
     * 判断指定目录下是否存在后缀名为.jpg的文件,如果就输出该文件名;
     */
    File file = new File("D:\\QQ");
   Find(file);
}
public static void Find(File file){
   File []files=file.listFiles();
   for (File f: files){
       if (f.isDirectory()){
           Find(f);
       }else{
           if (f.getName().endsWith(".jpg")){
               System.out.println(f.getAbsolutePath());
           }
       }
   }

2.IO流

1)概述

IO流原理及流的分类
input/output的缩写,处理设备之间的数据传输。
input:读取外部数据
output:将程序中的数据输出
按照操作数据单位的不同,分为字节流(8bit)和字符流(6bit)适合于文本。
                                       输入流:inputStream,reader
                                       输出流:outputStream,Writer
按照流的角色不同:作用在文件上的:节点流,作用在已经有的流上的:处理流。
按照数据流向:输入流,输出流。

IO流的体系结构
抽象基类                              节点流                                  缓冲流
InputStream                         FileInputStream                     BufferedInputStream
OutputStream                        FileOutputStream                    BufferedOutputStream
Reader                              FileReader                          BufferedReader
Writer                              FileWriter                          BufferedWriter

2)节点流-FileReader和FileWriter

FileReader读入数据的基本操作

说明点:
①read()地理解:返回读入的一个字符,如果达到文件末尾,返回-1
②异常的处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally处理。
③读入的文件一定要存在,否则就会报FileNotFoundException

/**
 * FileReader读取硬盘文件
 * @param args
 */
public static void main(String[] args){
    FileReader reader=null;
    BufferedReader reader1=null;
    try {
         reader = new FileReader(new File("jdbc.properties"));//相较于当前工程
         reader1 = new BufferedReader(reader);
        int data ;//返回读入的一个字符,如果达到文件末尾,返回-1.
        while ((data=reader1.read())!=-1){//循环读取数据
            System.out.print((char)data);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {//关闭流
        if (reader!=null){
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(reader1!=null){
            try {
                reader1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
public static void main(String[] args) {
        //对read(的升级,使用read的重载方法
        FileReader reader=null;
        BufferedReader br=null;
        try {
            reader = new FileReader(new File("jdbc.properties"));
            br=new BufferedReader(reader);
            char []buf=new char[1024];
            int len=0;
            while ((len=br.read(buf))!=-1){//返回每次读入buf[]数组中的字符个数,如果达到文件末尾,返回-1
//                for(int i=0;i<len;i++){
//                    System.out.print(buf[i]);
//                }
                String str=new String(buf,0,len);
                System.out.print(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            if (reader!=null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
FileWriter写出数据的操作

说明:
①输出操作:对应的File可以不存在,如果不存在,在输出过程中,会自动创建此文件。
②如果存在:如果流使用的构造器是FileWriter(file,false)/FileWriter(file):对原有文件覆盖。
如果流使用FileWriter(file,true):不会对原有文件覆盖。

/**
 * 从内存写出数据到硬盘文件
 * @param args
 */
public static void main(String[] args) {
    FileWriter writer = null;
    try {
        writer= new FileWriter(new File("hello.txt"),true);
        //true时表示对原文件的追加,false或不写表示对原文件的覆盖
        writer.write("I have a dream !");//写入文件
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        if (writer!=null){
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
使用FileReader和FileWriter实现文本文件的复制

字符流不能处理图片文件

/**
 * 将一个文件写入另一个文件
 * @param args
 */
public static void main(String[] args) {
    FileReader fr=null;
    FileWriter fw=null;
    try {
        fr=new FileReader(new File("E:\\IEDADEMO\\day07\\src\\com\\atguigu\\IO\\IO.txt"));
        fw=new FileWriter(new File("hello.txt"));
        //数据的读入和写出操作
        char []buf=new char[1024];
        int len=0;//记录每次读入到数组的数据多少个
        while ((len=fr.read(buf))!=-1){
            fw.write(buf,0,len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        if (fw!=null){
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fr!=null){
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

3)节点流-FileInputStream和FileOutputStream

使用FileInputStream不能读取文本文件的测试:
结论:对于文本文件,使用字符流处理
对于非文本文件,使用字节流处理

/**
 * 使用FileInputStream不能读取文本文件的测试
 */
public static void main(String[] args) {
    FileInputStream fis=null;
    FileOutputStream fos=null;
    try {
        fis=new FileInputStream(new File("E:\\IEDADEMO\\day07\\src\\com\\atguigu\\IO\\IO.txt"));
        fos=new FileOutputStream(new File("hello.txt"));
        byte [] bytes=new byte[1024];
        int len=0;
        while ((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        try {
            if (fos!=null){
                fos.close();
            }
            if (fis!=null){
                fis.close();
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }


}
FileInputStream和FileOutputStream读写非文本文件
/**
 * FileInputStream和FileOutputStream读写非文本文件
 * @param args
 */
public static void main(String[] args) {
    FileInputStream fis=null;
    FileOutputStream fos=null;
    try {
        fis=new FileInputStream(new File("E:\\IEDADEMO\\day07\\src\\下载.png"));
        fos=new FileOutputStream(new File("D:\\test.png"));
        byte[] bytes=new byte[1024*1024];
        int len=0;
        while ((len=fis.read(bytes))!=-1) {
            fos.write(bytes,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos!=null){
                fos.close();
            }
            if (fis!=null){
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

写一个复制文件的方法,可以用来直接调用,
public static void main(String[] args) {
    File file = new File("hello.txt");
    File file1 = new File("D://hello.txt");
  Copy(file,file1);
}
public static void Copy(File file,File file1){
    FileInputStream fis=null;
    FileOutputStream fos=null;
    try {
        fis=new FileInputStream(file);
        fos=new FileOutputStream(file1);
        byte [] bytes=new byte [1024];
        int len;
        while((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        System.out.println("复制完成!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos!=null){
                fos.close();
            }
            if (fis!=null){
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

4)处理流之一:缓冲流

缓冲流作用:提高流的读取写入速度
提高读写速度的原因:内部提供了一个缓冲区:1024*8=8192
缓冲流(字节型)实现非文本文件的复制

public static void main(String[] args) {
    FileInputStream fis=null;
    FileOutputStream fos=null;
    BufferedInputStream bis=null;
    BufferedOutputStream bos=null;
    try {
        fis=new FileInputStream(new File("hello.txt"));
        fos=new FileOutputStream(new File("D://hello.txt"));
        bis=new BufferedInputStream(fis);
        bos=new BufferedOutputStream(fos);
        byte [] bytes=new byte[1024];
        int len;
        while ((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        System.out.println("复制完成!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bos!=null){
                bos.close();
            }
            if (bis!=null){
                bis.close();
            }
            if (fos!=null){
                fos.close();
            }
            if (fis!=null){
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

缓冲流(字符型)实现文本文件的复制
处理流:就是套接在已有流的基础上

public static void main(String[] args) {
        FileReader fr=null;
        FileWriter fw=null;
        BufferedReader br=null;
        BufferedWriter bw=null;
        try {
            fr=new FileReader(new File("hello.txt"));
            fw=new FileWriter(new File("D://hello.txt"));
            br=new BufferedReader(fr);
            bw=new BufferedWriter(fw);
            //方式一:
//            char [] chars=new char[1024];
//            int len=0;
//            while ((len=br.read(chars))!=-1){
//                bw.write(chars,0,len);
//            }
            //方式二:
            String data;
            while ((data=br.readLine())!=null){
                bw.write(data);//一次读取一行
                bw.newLine();//换行
            }
            System.out.println("文本复制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw!=null){
                    bw.close();
                }
                if (br!=null){
                    br.close();
                }
                if (fw!=null){
                    fw.close();
                }
                if (fr!=null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

5)面试题

1.图片加密和图片解密
/***
 * 图片加密
 * @param args
 */
public static void main(String[] args) {
    BufferedInputStream bis=null;
    BufferedOutputStream bos=null;
    try {
        bis=new BufferedInputStream(new FileInputStream(new File("D:\\尹会东.jpg")));
        bos=new BufferedOutputStream(new FileOutputStream(new File("D:\\copy.jpg")));
        int len=0;
        byte [] bytes=new byte[1024];
        while ((len=bis.read(bytes))!=-1){
            for (int i = 0; i <len ; i++) {
                bytes[i]= (byte) (bytes[i]^5);//加密操作
            }
            bos.write(bytes,0,len);
        }
        System.out.println("加密完成!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bis!=null){
                bis.close();
            }
            if (bos!=null){
                bos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/**
 * 图片解密
 */
public static void main(String[] args) {
    BufferedInputStream bis=null;
    BufferedOutputStream bos=null;
    try {
        bis=new BufferedInputStream(new FileInputStream(new File("D:\\copy.jpg")));
        bos=new BufferedOutputStream(new FileOutputStream(new File("D:\\copy1.jpg")));
        int len=0;
        byte [] bytes=new byte[1024];
        while ((len=bis.read(bytes))!=-1){
            for (int i = 0; i <len ; i++) {
                bytes[i]= (byte) (bytes[i]^5);//解密操作
            }
            bos.write(bytes,0,len);
        }
        System.out.println("解密完成!");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bis!=null){
                bis.close();
            }
            if (bos!=null){
                bos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
2.获取文本上每个字符出现的次数
/**
 * 获取文本上每个字符出现的次数
 */
public static void main(String[] args) {
    HashMap<Character, Integer> map = new HashMap<>();
    BufferedReader br=null;
    BufferedWriter bw=null;
    try {
        br=new BufferedReader(new FileReader(new File("hello.txt")));
        bw=new BufferedWriter(new FileWriter(new File("D:\\WorldCount.txt")));
        int c=0;
        while ((c=br.read())!=-1){
            char ch= (char) c;
            if (map.get(ch)==null){
                map.put(ch,1);
            }else{
                map.put(ch,map.get(ch)+1);
            }
        }
        Set<Map.Entry<Character, Integer>> set = map.entrySet();
        Iterator<Map.Entry<Character, Integer>> iterator = set.iterator();
        while (iterator.hasNext()){
            bw.write(iterator.next().getKey()+"--------------"+iterator.next().getValue());
            bw.newLine();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bw!=null){
                bw.close();
            }
            if (br!=null){
                br.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.将一个文件写入另一个文件
/**
 * 将一个文件写入另一个文件
 * @param args
 */
public static void main(String[] args) {
    FileReader fr=null;
    FileWriter fw=null;
    try {
        fr=new FileReader(new File("E:\\IEDADEMO\\day07\\src\\com\\atguigu\\IO\\IO.txt"));
        fw=new FileWriter(new File("hello.txt"));
        //数据的读入和写出操作
        char []buf=new char[1024];
        int len=0;//记录每次读入到数组的数据多少个
        while ((len=fr.read(buf))!=-1){
            fw.write(buf,0,len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        if (fw!=null){
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fr!=null){
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

6)处理流之二:转换流

1.作用:提供了字节流和字符流的转换
2.转换流属于字符流
InputStreamReader:将一个字节的输入流转换为字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
3.解码:字节,字节数组----->字符数组,字符串
编码:字符数组,字符串----->字节,字节数组
4.字符集

public static void main(String[] args) {
        FileInputStream fis=null;
        InputStreamReader isr=null;
        FileOutputStream fos=null;
        OutputStreamWriter osw=null;
        try {
             fis = new FileInputStream(new File("hello.txt"));
//            InputStreamReader isr = new InputStreamReader(fis);//使用系统默认字符集
            isr = new InputStreamReader(fis, StandardCharsets.UTF_8);//指定字符集
            fos=new FileOutputStream(new File("D:\\Temps.txt"));
            osw=new OutputStreamWriter(fos,"utf-8");
            char [] chars=new char[1024];
            int len;
            while ((len=isr.read(chars))!=-1){
                osw.write(chars,0,len);
            }
//            while ((len=isr.read(chars))!=-1){
//                String str=new String(chars,0,len);
//                System.out.println(str);
//            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (isr!=null){
                    isr.close();
                }
                if (fis!=null){
                    fis.close();
                }
                if (osw!=null){
                    osw.close();
                }
                if (fos!=null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

7)其他流(了解)

其他流(了解)
1.标准的输入输出流
1.1
System.in标准的输入流
System.out标准的输出流
1.2我们可以通过System类的setIn(InputStream)/setOut(OutputStream)方式重新指定输入和输出的流。
1.3练习:IO5

 public static void main(String[] args) {
/**
 * 键盘输入字符,如果是e或exit程序结束,否则转化为大写输出
 */
        InputStreamReader isr=new InputStreamReader(System.in);
        BufferedReader br=new BufferedReader(isr);

        while (true){
            String data= null;
            try {
                data = br.readLine();
                if (data.equalsIgnoreCase("e")||data.equalsIgnoreCase("exit")){
                    System.out.println("程序结束!");
                    break;
                }else{
                    System.out.println(data.toUpperCase());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if (isr!=null){
                        isr.close();
                    }
                    if (br!=null){
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

2.打印流
PrintStream
PrintWriter
3.数据流
DataInputStream
DataOutputStream
4.随机存取文件流
RandomAccessFile
既可以读,也可以写。
实现了DataInput和DataOutput接口
java.io下,但是直接继承Object类
如果RandomAccessFile作为输出流时,写出到的文件不存在,会自动创建,
如果写出到的文件存在,会对原有文件进行覆盖,(默认情况下,从头覆盖)

public static void main(String[] args) {
    RandomAccessFile file = null;
    RandomAccessFile file1 = null;
    try {
        file=new RandomAccessFile(new File("hello.txt"),"r");
        file1=new RandomAccessFile(new File("D:\\object.txt"),"rw");
        byte []bytes=new byte[1024];
        int len=0;
        while ((len=file.read(bytes))!=-1){
            file1.write(bytes,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (file!=null){
                file.close();
            }
            if (file1!=null){
                file1.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

RandomAccessFile类实现数据插入

public static void main(String[] args) {
    RandomAccessFile raf=null;
    try {
        raf=new RandomAccessFile("hello.txt","rw");
        //raf.seek(3);//指定开始覆盖的位置
        raf.seek(new File("hello.txt").length());//最后面插入
        /**
         * 如果想要在中间插入,可以先将后面的内容读取到内存的某个变量,
         * 然后写入要插入的数据,再将变量中的内容插入末尾。
         */
        raf.write("xyz".getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (raf!=null){
                raf.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

8)对象流

ObjectInputStream用于存储和读取基本数据类型或对象的流处理。
序列化:用ObjectOutputStream将数据从内存写入硬盘
反序列化:ObjectInputStream将数据从硬盘读入内存。

要求对象所属的类是可序列化的—>实现了Seralizable接口

面试题:如何理解对象序列化机制?
把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存在硬盘上,或者通过网络,
将这种二进制流传输到另一个网络节点,当其他程序获取了这种二进制流,就可以恢复成原来的Java对象。
序列化的好处在于将任何实现了Seralizable接口的对象转化为字节数据,使其在保存和传输中可被还原。
序列化
public static void main(String[] args) {
    //序列化过程:
    ObjectOutputStream oos=null;
    try {
        oos=new ObjectOutputStream(new FileOutputStream(new File("D:\\object.txt")));
        oos.writeObject(new String("我爱北京天安门"));
        oos.flush();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(oos!=null){
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}
反序列化

//反序列化
public static void main(String[] args) {
    ObjectInputStream ois =null;
    try {
        ois=new ObjectInputStream(new FileInputStream(new File("D://object.txt")));
        Object object = ois.readObject();
        String str= (String) object;
        System.out.println(str);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (ois!=null){
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
自定义类实现序列化与反序列化操作
1.需要实现java.io.Serializable接口
2. public static final long serialVersionUID= 6080347956336285349L;
3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。
(默认情况下基本数据类型是可序列化的)
4.不能序列化static和transient的
public class Person implements java.io.Serializable{
 public static final long serialVersionUID= 6080347956336285349L;
    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 +
            '}';
    }
}

public static void main(String[] args) {
    //序列化
    ObjectOutputStream oos =null;
    ObjectInputStream ois=null;
    try {
        oos=new ObjectOutputStream(new FileOutputStream(new File("D://object.txt")));
        Person person = new Person("张贝贝", 25);
        oos.writeObject(person);
        //反序列化
        ois=new ObjectInputStream(new FileInputStream(new File("D:\\object.txt")));
        Object obj=ois.readObject();
        Person p1= (Person) obj;
        System.out.println(p1.toString());
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        try {
            if (ois!=null){
                ois.close();
            }
            if (oos!=null){
                oos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
serialVersionUID的理解

如果不手动写,Java会自动生成一个。
如果序列化以后修改了类,serialVersionUID也会被Java修改,
反序列化会失败!

十二,网络编程

网络编程中有两个主要的问题:

1.如何准确的定位到网络上一台或者多台主机:定位主机上的特定应用
2.找到主机后如何可靠高效的进行数据传输

网络编程中的两个要素

1.对应的问题:IP和端口号
2.对应问题二:提供网络通信协议:TCP/IP参考模型

通信要素一:IP和端口号

1.IP:唯一标识Internet上的计算机(通信实体)
2.在Java中使用InetAddress类代表IP
3.IP分类:IPV4和IPV6;万维网和局域网的区别
4.域名:www.baidu.com
5.域名--》NDS--》网络服务器
6.本地回路地址:127.0.0.1 对应着localhost
7.如何实例化InetAddress:两个方法;getByName(String host),getLocalHost();

端口号:表示计算机上运行的程序

不同进程有不同的端口号
Tomcat:8080,mysql:3306,oracle:1521
端口号与IP地址组合得出一个网络套接字,Socket
范围:0-65535
public static void main(String[] args) {
    try {
        InetAddress name = InetAddress.getByName("127.0.0.1");
        System.out.println(name);
        InetAddress name1 = InetAddress.getByName("www.baidu.com");
        System.out.println(name1);
        System.out.println(name.getHostName());
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}

TCP:3次握手,UDP:封装数据包

Tcp网络编程

/**
 * @author yinhuidong
 * @createTime 2020-04-10-21:24
 * TCP网络编程1:客户端向服务端发送消息,服务端将消息显示在控制台上
 */
public class Test1 {

    //客户端
    @Test
    public void client()throws Exception{
        InetAddress address = InetAddress.getByName("127.0.0.1");
        Socket socket = new Socket(address, 3307);
        OutputStream os = socket.getOutputStream();
        os.write("你好,我是客户端".getBytes());
        os.close();
        socket.close();
    }

    //服务端
    @Test
    public void server()throws Exception{
        ServerSocket serverSocket = new ServerSocket(3307);
        Socket socket = serverSocket.accept();
        InputStream is = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int len;
        byte[] buffer = new byte[10];
        while ((len=is.read(buffer))!=-1){
            baos.write(buffer,0,len);
        }
        System.out.println(baos.toString());
        baos.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

/**
 * @author yinhuidong
 * @createTime 2020-04-10-21:40
 * 客户端给服务端发送文件,服务端保存文件并告诉客户端自己接收成功
 * //如果不关闭资源,会抛出:java.net.SocketException: Connection reset异常
 */
public class Test2 {
    //客户端
    @Test
    public void test1() throws Exception {
        //获取IP地址
        InetAddress address = InetAddress.getByName("127.0.0.1");
        //获取socket对象
        Socket socket = new Socket(address, 3308);
        //写入本地图片
        FileInputStream is = new FileInputStream(new File("E:\\9.jpg"));

        byte[] buffer = new byte[1024 * 8];
        int len;
        //将本地图片写出到服务端
        OutputStream os = socket.getOutputStream();
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        socket.shutdownOutput();
        //获取输入流准备接受服务端的消息
        InputStream stream = socket.getInputStream();
        byte[] buffer2 = new byte[10];
        int len2;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while ((len2 = stream.read(buffer2)) != -1) {
            baos.write(buffer2, 0, len2);
        }
        //输出消息到控制台
        System.out.println(baos.toString());
        baos.close();
        stream.close();
        os.close();
        is.close();
        socket.close();
    }

    @Test
    public void test2() throws Exception {
        //创建一个服务端ServerSocket
        ServerSocket serverSocket = new ServerSocket(3308);
        //获取socket
        Socket socket = serverSocket.accept();
        //获取输入流
        InputStream is = socket.getInputStream();
        //指定输出流输出文件位置
        FileOutputStream fos = new FileOutputStream(new File("E:\\10.jpg"));
        int len;
        byte[] buffer = new byte[1024 * 8];
        while ((len = is.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }
        //获取输出流
        OutputStream stream = socket.getOutputStream();
        //输出内容
        stream.write("接收文件成功".getBytes());
        stream.close();
        fos.close();
        is.close();
        socket.close();
        serverSocket.close();
    }
}

UDP网络编程

/**
 * @author yinhuidong
 * @createTime 2020-04-10-23:03
 * UDP网络编程
 */
public class Test4 {

    //发送端
    @Test
    public void send()throws Exception{
        DatagramSocket socket = new DatagramSocket();
        byte []data="我是UDP方式的发送端".getBytes();
        InetAddress inet = InetAddress.getLocalHost();
        DatagramPacket packet = new DatagramPacket(data, 0, data.length,inet, 8081);
        socket.send(packet);
    }

    //接收端
    @Test
    public void receiver()throws Exception{
        DatagramSocket socket = new DatagramSocket(8081);
        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
        socket.receive(packet);
        System.out.println(new String(packet.getData(), 0, buffer.length));
    }
}

URL类的理解与实例化

URL:统一资源定位符,他表示internet上某一资源的地址。
格式:协议,主机名,端口号,片段名,参数列表

public static void main(String[] args) throws IOException {
    URL url = new URL("www.baidu.com");
    HttpURLConnection connection= (HttpURLConnection) url.openConnection();//获取连接
    connection.connect();//连接
    connection.getInputStream();//获取流
}

十三,反射

1.概述

反射

反射机制允许程序在执行时借助于反射API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
反射相关的主要API
java.lang.Class
java.lang.reflect.

关于java.lang.Class类的理解:*

1.类的加载过程:程序在经过javac.exe命令后,会生成一个或多个字节码文件(.class结尾),接着我们
使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行的类,就作为Class的一个实例。
2.换句话说:Class的实例就对应着一个运行时类。

类的加载过程(了解)

①类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类的加载器完成。
②类的链接:将类的二进制数据合并到JRE中,赋默认值。
③类的初始化:JVM负责对类进行初始化。将静态代码块和类中赋值操作的语句读取,给变量/常量赋值。

类的加载器ClassLoader的理解:(了解)

作用:把类加载进内存。

2.获取Class的实例的方式(重点)

加载到内存中的运行时类,会缓存一定的时间,在此时间内,我们可以通过不同的方式来获取此运行时的类。
Class实例对应的结构说明:
①claass,②interface,③数组④枚举类⑤注解⑥基本数据类型⑦void
/**
 * @author yinhuidong
 * @createTime 2020-04-11-9:57
 * 获取Class实例的四种方式
 */
public class Test1 {
    //通过对象.getClass()的方式
    @Test
    public void test1() {
        Class<? extends Person> clazz = new Person().getClass();
    }

    //通过类.class
    @Test
    public void test2() {
        Class<Person> clazz = Person.class;
    }

    //通过Class的静态方法
    @Test
    public void test3() throws Exception {
        Class<?> clazz = Class.forName("com.atguigui.java1.Person");
    }

    //通过类加载器
    @Test
    public void test4() throws Exception{
        ClassLoader classLoader = Person.class.getClassLoader();
        classLoader.loadClass("com.atguigui.java1.Person");
    }
}

class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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

3.使用ClassLoader加载配置文件

@Test
    public void test4() throws Exception {
        Properties prop=new Properties();
        //读取配置文件的方式一:
        //此时配置文件默认在当前module下
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        prop.load(fis);
        //读取配置文件的方式二:使用ClassLoader
        //配置文件默认识别为:当前module的src下
        ClassLoader classLoader=Test1.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("driud.properties");
        prop.load(is);
        String user=prop.getProperty("user");
        String password=prop.getProperty("password");
        System.out.println(user+" "+password);
    }

4.创建运行时类对象与反射的动态性

通过反射,创建运行时类对象

newInstance()调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器。
要想此方法正常运行:
①运行时类必须提供空参的构造器
②空参构造器访问权限得够。
在JavaBean中要求提供一个空参构造器的原因:
①便于通过反射,创建运行时类的对象。
②便于子类继承此运行类时,默认调用super方法时,保证父类有此构造器。
@Test
public void test5() throws IllegalAccessException, InstantiationException {
    Class<Person>clazz=Person.class;
    Person obj=clazz.newInstance();
    System.out.println(obj);
}

反射的动态性举例

@Test
public void test6() throws Exception {
    Object instance = getInstance("java.util.Date");
    System.out.println(instance);
}
//创建一个指定类的对象。classPath:指定类的全类名。
public Object getInstance(String classPath)throws Exception{
    Class clazz=Class.forName(classPath);
    return clazz.newInstance();
}

5.调用运行时类的指定结构

属性,

@Test
public void test() throws Exception {
    Class clazz=Person.class;
    //创建运行时类的对象
    Object o = clazz.newInstance();
    //获取指定的属性:public
   Field name = clazz.getField("name");
    name.set(o,"yinhuidong");
    String o1 = (String) name.get(o);
    System.out.println(o1);
}
@Test//掌握
public void test2() throws Exception {
    Class clazz=Person.class;
    //创建运行时类的对象
    Object o = clazz.newInstance();
    //获取指定的属性:无关权限修饰符,不包含父类
    Field age = clazz.getDeclaredField("age");
    age.setAccessible(true);//给与修改权限
    age.set(o,20);
    Object obj = age.get(o);
    System.out.println(obj);
}

方法,

@Test//掌握
public void test3()throws Exception{
 //如何操作运行时类中的指定非静态方法
           Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        Method show = clazz.getDeclaredMethod("show");
        show.setAccessible(true);
        Object obj = show.invoke(person);
        Method add = clazz.getDeclaredMethod("add", int.class, int.class);
        add.setAccessible(true);
        Integer invoke = (Integer) add.invoke(person, 1, 1);
        System.out.println(invoke);
}
@Test
public void test4() throws Exception{
    //如何操作运行时类中的指定静态方法
    Class clazz=Person.class;
    Method eat = clazz.getDeclaredMethod("eat");
    eat.setAccessible(true);
    eat.invoke(Person.class);
}

构造器

@Test//不是很常用,了解
public void test5() throws Exception{
    //调用运行时类中的指定构造器
    Class clazz=Person.class;
    Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class);
    constructor.setAccessible(true);
    Object obj = constructor.newInstance("Tom",22);
    System.out.println(obj);
}

6.代理

代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。
任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理举例

特点:编译期间,代理类和被代理类就被确定下来了。

interface  ClothFactory{
    void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
    private ClothFactory factory;//就拿被代理对象进行实例化

    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂进行准备工作");
        factory.produceCloth();
        System.out.println("代理工厂做后续工作!");
    }
}
//被代理类
class Nike  implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("耐克工厂生产一批运动服!");
    }
}
@Test
public void test(){
    Nike nike=new Nike();
    ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
    proxyClothFactory.produceCloth();
}

动态代理示例

动态代理:可以通过一个代理类完成全部的代理功能。
要求:理解过程。

/**
 * @author yinhuidong
 * @createTime 2020-04-11-11:17
 * 基于接口的动态代理
 */
public class Test1 {

    private PersonDao dao=new PersonDaoImpl();
    @Test
    public void test(){
        //动态代理
        PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
                //被代理类的类加载器
                dao.getClass().getClassLoader(),
                //被代理类实现的接口
                dao.getClass().getInterfaces(),
                //InvocationHandler接口的实现类
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object o=null;
                        if (!method.getName().equals("show")){
                            Integer a= (Integer) args[0];
                            Integer b= (Integer) args[1];
                            //对方法进行增强
                            o=method.invoke(dao,a*2,b*2);
                            return o;
                        }
                        o=method.invoke(dao);
                        return o;
                    }
                }
        );
        System.out.println(personDao.add(1, 1));
        personDao.del(2,1);
        personDao.show();
    }
}
//被代理类实现的接口
interface PersonDao{
    int add(int a,int b);
    void del(int a,int b);
    void show();
}
//被代理类
class PersonDaoImpl implements PersonDao{

    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public void del(int a, int b) {
        System.out.println(a-b);
    }

    @Override
    public void show() {
        System.out.println("show()....");
    }
}

7.总结

import java.util.Properties;

/**
 * @author yinhuidong
 * @createTime 2020-02-25-23:36
 */
public class Test3 {
    /**
     * 第二轮复习
     * 获取运行时类的Class实例
     */
    @Test
    public void test1() throws ClassNotFoundException {
        //方式一:
        Class<Person> clazz = Person.class;
        System.out.println(clazz);
        //方式二:
        Person person = new Person();
        Class<? extends Person> clazz1 = person.getClass();
        System.out.println(clazz1);
        //方式:
        Class<?> clazz2 = Class.forName("com.atguigu.java3.Person");
        System.out.println(clazz2);
        //方式四:
        ClassLoader loader = Test3.class.getClassLoader();
        Class<?> clazz3 = loader.loadClass("com.atguigu.java3.Person");
        System.out.println(clazz3);
    }

    /**
     * 使用ClassLoader加载配置文件
     */
    @Test
    public void test2() throws IOException {
        InputStream is = Test3.class.getClassLoader().getResourceAsStream("xxx.properties");
        Properties prop = new Properties();
        prop.load(is);
    }

    /**
     * 通过反射,创建运行时类的对象
     */
    @Test
    public void test3() throws IllegalAccessException, InstantiationException {
        Class<Person> clazz = Person.class;
        Person person = clazz.newInstance();
        System.out.println(person.toString());
    }

    /**
     * 反射的动态性举例
     */
    @Test
    public void test4() {
        try {
            Object p = GetInstance("com.atguigu.java3.Person");
            System.out.println(p.toString());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public Object GetInstance(String path) throws ClassNotFoundException {
        Class<?> clazz = Class.forName(path);
        return clazz;
    }
    /**
     * 调用运行时类的指定结构
     * 1.属性
     */
    @Test//只能获取公的属性
    public void test5() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<Person> clazz = Person.class;
        Person p = clazz.newInstance();
        Field name = clazz.getField("name");
        name.set(p,"张");
        System.out.println(name.get(p));
    }
    @Test//可以获取私的属性
    public void test6() throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<Person> clazz = Person.class;
        Person p = clazz.newInstance();
        //可以获取所属性
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p,"张");
        System.out.println(name.getName());
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        age.set(p,12);
        System.out.println(age.get(p));
        System.out.println(p.toString());
    }
    @Test//调用非静态方法
    public void test7() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<Person> clazz = Person.class;
        Person p = clazz.newInstance();
        Method show = clazz.getDeclaredMethod("show", String.class);//方法名和形参类型
        show.setAccessible(true);
        Object args = show.invoke(p, "args");//对象,实参
        System.out.println(args);//方法的返回值
    }
    @Test//调用静态方法
    public void test8() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<Person> clazz = Person.class;
        Person p = clazz.newInstance();
        Method eat = clazz.getDeclaredMethod("eat", String.class);
        eat.setAccessible(true);
        Object o = eat.invoke(Person.class, "可比克");
        System.out.println(o);


    }
}

class Person {
    private String name;
    private int age;

    public Person() {
    }
    private int show(String args){
        System.out.println(args);
        return 1;
    }
    private static void eat(String food){
        System.out.println("正在吃。。。"+food);
    }
    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 +
                '}';
    }
}

十四,jdk8新特性

1.Lambda表达式

Lambda表达式的使用
1.举例:Comparator<Integer> c= Comparator.comparingInt(o -> o);
2.格式:->:Lambda操作符
左边叫做形参列表,其实就是接口中的抽象方法的形参列表
右边叫做Lambda体(重写的抽象方法的方法体)
3.关于Lambda表达式的使用
总结:
-> 左边:lambda形参列表的参数类型可以省略(类型推断),如果形参列表只有一个参数,()可以省略。
-> 右边:Lambda体应该使用一对{}包裹,如果Lambda体只有一条执行语句(可能是return语句),{}和return也可以省略。
要求接口只有一个抽象方法。
4.Lambda表达式的本质:作为函数式接口的实例。
5.如果一个接口中只声明了一个抽象方法,则此接口称为函数式接口。
可以使用注解@FunctionalInterface检查是否是一个函数式接口。
用匿名实现类表示的现在都可以用Lambda表达式表示。
public class LambdaTest1 {
    //语法格式一:无参数,无返回值
    @Test
    public void test() {
        Runnable r = () -> System.out.println("语法格式一:无参数,无返回值");
        r.run();
    }

    //语法格式二:一个参数,无返回值
    @Test
    public void test2() {
        Consumer<String> c = (String s) -> {
            System.out.println(s);
        };
        c.accept("语法格式二:一个参数,无返回值");
    }

    //语法格式:类型推断
    @Test
    public void test3() {
        Consumer<String> c = (s) -> {
            System.out.println(s);
        };
        c.accept("语法格式:类型推断");
    }

    //语法格式四:只一个参数时,省略小括号
    @Test
    public void test4() {
        Consumer<String> c = s -> {
            System.out.println(s);
        };
        c.accept("语法格式四:只一个参数时,省略小括号");
    }

    //语法格式五:Lambda 需要两个以上参数,多条执行语句,并且有返回值。
    @Test
    public void test5() {
        Comparator<Integer> c = (o1, o2) -> {
            System.out.println("语法格式五:Lambda 需要两个以上参数,多条执行语句,并且有返回值。");
            return o1.compareTo(o2);
        };
        int compare = c.compare(32, 21);
        System.out.println(compare);
    }

    //语法格式六:当Lambda体只一条语句时,reurn与大括号若,都可以省略。
    @Test
    public void test6() {
        Comparator<Integer> c = (o1, o2) -> o1.compareTo(o2);
        int compare = c.compare(32, 21);
        System.out.println(compare);
    }
}
Java内置四大核心函数式接口(要求能看懂)
消费性接口 Consumer<T>  void accept(T t)
供给型接口 Supplier<T>   T get()
函数型接口 Function<T,R> R apply(T t)
断定型接口 Predicate<T> boolean test(T t)
test()
使用情景:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用:本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例,
所以方法引用,也是函数式接口的实例。
使用格式: 类(对象)::方法名
具体分为如下三种情况:
对象::非静态方法
类::静态方法
类::非静态方法
方法引用的使用要求,要求接口中的抽象方法的形参列表和返回值类型与方法引用
的方法的形参列表和返回值类型相同!

2.Stream API

1.Stream关注的是数据的运算,与CPU打交道。
集合关注的是是数据的存储,与内存打交道。
2.
①Stream自己不会存储元素。
②Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream。
③Stream操作是延迟执行的,这意味着它们会等到需要结果的时候才执行。
3.Stream的执行流程
①Stream的实例化
②一系列的中间操作(过滤,映射,。。。)
③终止操作
4.说明:
①一个中间链操作,对数据源的数据进行处理。
②一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。

Stream的实例化:

//创建Stream的方式一:通过集合创建
@Test
public void test() {
    List<Employee> employees = EmployeeData.getEmployees();
    //返回一个顺序流
    Stream<Employee> stream = employees.stream();
    //返回一个并行流
    Stream<Employee> employeeStream = employees.parallelStream();
}

//创建Stream的方式二:通过数组
@Test
public void test2() {
    int[] arr = new int[]{1, 2, 3, 4, 5, 6};
    IntStream stream = Arrays.stream(arr);
}

//创建方式:通过Stream的of()
@Test
public void test3() {
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}

//创建Stream的方式四,无限流
@Test
public void test4() {
    //迭代
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
    //生成
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

Stream的中间操作:

1.筛选与切片
@Test//filter()过滤出需要的元素
public void test(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.filter(e->e.getAge()>25).forEach(System.out::println);
}
@Test//limit()截断取前面
public void test2(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.limit(5).forEach(System.out::println);
}
@Test//skip()截断取后面
public void test3(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.skip(5).forEach(System.out::println);
}
@Test//
public  void test4(){//distinct()去除重复元素
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.distinct().forEach(System.out::println);
}

2.映射
@Test//map(str -> str + str)
public void test() {
    int a[] = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
    IntStream stream = Arrays.stream(a);
    stream.map(str -> str + str).forEach(System.out::println);
}

@Testmap(Employee::getAge)
public void test2() {
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.map(Employee::getAge).filter(age -> age > 25).forEach(System.out::println);
}
3.排序
@Test//自然排序
public void test() {
    int a[] = new int[]{1, 2, 3, 5, 9, 7, 6, 45, 65};
    IntStream stream = Arrays.stream(a);
    stream.sorted().forEach(System.out::print);
}

@Test//定制排序
public void test2() {
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.sorted((o1, o2) -> {
        return -o1.getAge().compareTo(o2.getAge());
    }).forEach(System.out::println);
}

@Test//定制排序
public void test3() {
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    stream.sorted((o1, o2) -> {
        if (o1.getAge() != o2.getAge()) {
            return o1.getAge().compareTo(o2.getAge());
        } else {
            return o1.getId().compareTo(o2.getId());
        }
    }).forEach(System.out::println);
}

Stream的终止操作

1.匹配与查找
@Test
public void test(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    boolean b = stream.allMatch(e -> e.getAge() > 23);
    System.out.println(b);
}
@Test
public void test2(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    boolean b = stream.anyMatch(e -> e.getAge() > 23);
    System.out.println(b);
}
@Test
public void test3(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    boolean b = stream.noneMatch(e -> e.getName().contains("java"));
    System.out.println(b);
}
@Test
public void test4(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    Optional<Employee> first = stream.findFirst();
    System.out.println(first);
}
@Test
public void test5(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    Optional<Employee> any = stream.findAny();
    System.out.println(any);
}
@Test
public void test6(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    long count = stream.count();
    System.out.println(count);
}
@Test
public void test7(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    Optional<Employee> max = stream.max((e1, e2) -> e1.getAge().compareTo(e2.getAge()));
    System.out.println(max);
}
@Test
public void test8(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    Optional<Employee> min = stream.max((e1, e2) -> -(e1.getAge().compareTo(e2.getAge())));
    System.out.println(min);
}
2.归约
@Test
public void test(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    Optional<Integer> reduce = stream.map(e -> e.getAge()).reduce((d1, d2) -> d1 + d2);
    System.out.println(reduce);
}
@Test
public void test2(){
    Integer a[]=new Integer []{1,2,3,4,5,6,7,8,9};
    Stream<Integer> stream = Arrays.stream(a);
    Integer reduce = stream.reduce(0, Integer::sum);
    System.out.println(reduce);
}
3.收集
@Test
public void test(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Employee> stream = employees.stream();
    List<Employee> collect = stream.collect(Collectors.toList());
    for (Employee employee : collect) {
        System.out.print(employee+" ");
    }
}

3.Optional类

Optional类:为了在程序中避免出现空指针异常而创建的。
常用的方法:ofNullable(T t)//允许存null
            ofElse(T t)//如果是null,使用形参中的对象。
            get()://如果调用对象包含值,返回调用对象,否则抛出异常。
public class OptionalTest {
    @Test
    public void test() {
        Person p=new Person();
        String boyName = getBoyName(p);
        System.out.println(boyName);//java.lang.NullPointerException原因:男孩为null
    }
    @Test
    public void test2(){
        Person p=null;
        String boyName = getBoyName(p);
        System.out.println(boyName);//java.lang.NullPointerException原因:Person为null
    }
    @Test
    public void test3(){
        Person p=null;
        System.out.println(getName(p));
    }
    public String getBoyName(Person person){
        return person.getBoy().getName();
    }
    public  String getName(Person person){
        Optional<Person> person1 = Optional.ofNullable(person);
        Person person2 = person1.orElse(new Person());
        Optional<Boy> boy = Optional.ofNullable(person2.getBoy());
        Boy boy1 = boy.orElse(new Boy("Tom"));
        return boy1.getName();
    }

}

class Person {
    private Boy boy;

    public Person() {
    }

    public Person(Boy boy) {
        this.boy = boy;
    }

    public Boy getBoy() {
        return boy;
    }

    public void setBoy(Boy boy) {
        this.boy = boy;
    }
}

class Boy {
    private String name;

    public Boy() {
    }

    public Boy(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

十五,JDBC核心技术

1.概述

软件架构方式:
B/S:浏览器
C/S:客户端

数据的持久化

把数据保存到可掉电式存储设备中以供之后使用

JDBC的理解:

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)

简单理解为:JDBC,是SUN提供的一套 API,使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL)

图示理解:

在这里插入图片描述

2.获取连接

驱动和url

Driver driver=new com.mysql.jdbc.Driver();//MySQL具体driver的实现类
String url="jdbc:mysql://localhost:3306/test";
jdbc:mysql :协议
localhost :ip地址
3306 :端口号
test:数据库

方式一

@Test
public void test() throws SQLException {
    Driver driver = new com.mysql.jdbc.Driver();
    String url = "jdbc:mysql://localhost:3306/test";
    Properties prop = new Properties();
    prop.setProperty("user", "root");
    prop.setProperty("password", "yhd666");
    Connection connect = driver.connect(url, prop);
}

上述代码中显式出现了第三方数据库的API

方拾二

@Test//方式二:对方式一的迭代,不出现第方api,使程序具更好的可移植性
public void test2() throws Exception {
    //1.利用反射获取Driver的具体实现类对象
    Class clazz = Class.forName("com.mysql.jdbc.Driver");
    Driver driver = (Driver) clazz.newInstance();
    //2.提供要链接的数据库
    String url = "jdbc:mysql://localhost:3306/test";
    Properties prop = new Properties();//将用户名和密码封装在prop中。
    prop.setProperty("user", "root");
    prop.setProperty("password", "yhd666");
    //3..获取连接
    Connection connect = driver.connect(url, prop);
    System.out.println(connect);
}

方式三

@Test //方式:使用DriverManager替换driver
public void test3() throws Exception {
    //注册驱动
    DriverManager.registerDriver((Driver) Class.forName("com.mysql.jdbc.Driver").newInstance());
    //获取连接
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "yhd666");
    System.out.println(connection);
}

使用DriverManager实现数据库的连接。体会获取连接必要的4个基本要素。

方式四

@Test
public void testConnection4() throws Exception {
    //1.数据库连接的4个基本要素:
    String url = "jdbc:mysql://localhost:3306/test";
    String user = "root";
    String password = "yhd666";
    String driverName = "com.mysql.jdbc.Driver";
    //2.加载驱动 (①实例化Driver ②注册驱动
    Class.forName(driverName);
    //Driver driver = (Driver) clazz.newInstance();
    //3.注册驱动
    //DriverManager.registerDriver(driver);
        /*
        可以注释掉上述代码的原因,是因为在mysql的Driver类中声明:
        static {
            try {
                DriverManager.registerDriver(new Driver());
            } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
            }
        }
         */
    //3.获取连接
    Connection conn = DriverManager.getConnection(url, user, password);
    System.out.println(conn);
}

方式五

@Test//最终版,配置文件版
//1.实现了数据与代码的分离,解耦
//2.如果需要修改配置文件信息,就可以避免程序重新打包
public void test5() throws Exception {
    InputStream is = Test1.class.getClassLoader().getResourceAsStream("jdbc.properties");
    Properties prop = new Properties();
    prop.load(is);
    String user = prop.getProperty("user");
    String password = prop.getProperty("password");
    String url = prop.getProperty("url");
    String driverClass = prop.getProperty("driverClass");
    Class.forName(driverClass);
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);
}

其中,配置文件声明在工程的src目录下:【jdbc.properties】

```properties
user=root
password=yhd666
url=jdbc:mysql://localhost:3306/test
driverClass=com.mysql.jdbc.Driver

3.PreparedStatement

Statement

数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。

- 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
  - Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。 
  - PrepatedStatement:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
  - CallableStatement:用于执行 SQL 存储过程

 

使用Statement操作数据表的弊端

- 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。

- Statement 接口中定义了下列方法用于执行 SQL 语句:

  ```sql
  int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
  ResultSet executeQuery(String sql):执行查询操作SELECT
  ```

- 但是使用Statement操作数据表存在弊端:

  - **问题一:存在拼串操作,繁琐**
  - **问题二:存在SQL注入问题**
其他问题:
Statement没办法操作Blob类型变量
Statement实现批量插入时,效率较低
- SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1') ,从而利用系统的 SQL 引擎完成恶意行为的做法。

- 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement(从Statement扩展而来) 取代 Statement 就可以了。

JDBCUtils封装获取连接和关闭资源

/**
 * @author yinhuidong
 * @createTime 2020-02-04-19:26
 * JDBC工具类
 */
public class JDBCUtils {
    //获取连接
    public static Connection getConnection()throws  Exception{
        InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
        Properties prop = new Properties();
        prop.load(is);
        String driverClass=prop.getProperty("driverClass");
        String url=prop.getProperty("url");
        String user = prop.getProperty("user");
        String password = prop.getProperty("password");
        Class.forName(driverClass);
        Connection connection = DriverManager.getConnection(url, user, password);
        return connection;
    }
    //关闭资源
    public static void closeResource(Connection co, PreparedStatement ps, ResultSet rs){
        try {
            if (co!=null){
                co.close();
            }
            if (ps!=null){
                ps.close();
            }
            if (rs!=null){
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

增删改操作

添加操作
@Test//添加操作
public void add(){
    Connection co=null;
    PreparedStatement ps=null;
    try {
        co=JDBCUtils.getConnection();
        String sql="insert into customers(name,email,birth) values(?,?,?);";
        ps = co.prepareStatement(sql);
        ps.setObject(1,"yinhuidong");
        ps.setObject(2,"1972039773@qq.com");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        java.util.Date date = format.parse("1998-11-16");
        ps.setObject(3,new Date(date.getTime()));
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(co,ps,null);
    }
}
删除操作
@Test//删除操作
public void delete(){
    Connection co=null;
    PreparedStatement ps=null;
    try {
        co=JDBCUtils.getConnection();
        String sql="delete from customers where id = ?;";
        ps=co.prepareStatement(sql);
        ps.setObject(1,20);
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(co,ps,null);
    }
}
修改操作:
@Test//修改
public void update(){
    Connection co=null;
    PreparedStatement ps=null;
    try {
        co=JDBCUtils.getConnection();
        String sql="update customers set name=? where id =?;";
        ps=co.prepareStatement(sql);
        ps.setObject(1,"尹会东");
        ps.setObject(2,1);
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(co,ps,null);
    }
}

通用增删改:
//通用增删改
public static void update(String sql,Object...args){
    Connection conn=null;
    PreparedStatement ps=null;
    try {
        conn=getConnection();
        ps=conn.prepareStatement(sql);
        for (int i = 0; i <args.length ; i++) {
            ps.setObject(i+1,args[i]);
        }
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        closeResource(conn,ps,null);
    }
}

通用查询操作

public static <T>List<T> find1(Class<T>clazz,String sql,Object...args)  {
    Connection co=null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    try {
        co=getConnection();
        ps=co.prepareStatement(sql);
        for (int i = 0; i <args.length ; i++) {
            ps.setObject(i+1,args[i]);
        }
        rs=ps.executeQuery();
        ResultSetMetaData data = rs.getMetaData();//获取数据源
        int count = data.getColumnCount();//获取一共有多少列
        ArrayList<T> list = new ArrayList<>();
        while (rs.next()){
            T t = clazz.newInstance();
            for (int i = 0; i <count ; i++) {
                Object value=rs.getObject(i+1);
                String name = data.getColumnLabel(i + 1);//获取别名,如果没别名就获取列名
                Field field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                field.set(t,value);
            }
            list.add(t);
        }
        return list;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        closeResource(co,ps,rs);
    }
    return null;
}

表的列名和对象的属性名不一致的解决办法

Java与SQL对应数据类型转换表

| Java类型           | SQL类型                  |
| ------------------ | ------------------------ |
| boolean            | BIT                      |
| byte               | TINYINT                  |
| short              | SMALLINT                 |
| int                | INTEGER                  |
| long               | BIGINT                   |
| String             | CHAR,VARCHAR,LONGVARCHAR |
| byte   array       | BINARY  ,    VAR BINARY  |
| java.sql.Date      | DATE                     |
| java.sql.Time      | TIME                     |
| java.sql.Timestamp | TIMESTAMP                |


1. **如何获取 ResultSetMetaData**: 调用 ResultSet 的 getMetaData() 方法即可
2. **获取 ResultSet 中有多少列**:调用 ResultSetMetaData 的 getColumnCount() 方法
3. **获取 ResultSet 每一列的列的别名是什么**:调用 ResultSetMetaData 的getColumnLabel() 方法

JDBC API小结

- 两种思想
  - 面向接口编程的思想

  - ORM思想(object relational mapping)
    - 一个数据表对应一个java类
    - 表中的一条记录对应java类的一个对象
    - 表中的一个字段对应java类的一个属性

  > sql是需要结合列名和表的属性名来写。注意起别名。

- 两种技术
  - JDBC结果集的元数据:ResultSetMetaData
    - 获取列数:getColumnCount()
    - 获取列的别名:getColumnLabel()
  - 通过反射,创建指定类的对象,获取指定的属性并赋值


面试题:statement和preparedStatement的区别

操作Blob类型的变量

@Test//像数据表中插入一个图片
public void test(){
    Connection co=null;
    PreparedStatement ps=null;
    try {
        co=JDBCUtils.getConnection();
        String sql="insert into customers(name,email,birth,photo)values(?,?,?,?);";
        ps=co.prepareStatement(sql);
        ps.setObject(1,"宋红康");
        ps.setObject(2,"shk@126.com");
        ps.setObject(3,"1988-11-13");
        FileInputStream fis = new FileInputStream("D:\\copy1.jpg");
        ps.setObject(4,fis);
        ps.execute();
    } catch (SQLException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(co,ps,null);
    }
}
@Test//查询blob字段
public void test2(){
    Connection co=null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    InputStream bs=null;
    FileOutputStream fos=null;
    try {
        co=JDBCUtils.getConnection();
        String sql="select * from customers where id=?;";
        ps=co.prepareStatement(sql);
        ps.setObject(1,22);
        rs=ps.executeQuery();
        while (rs.next()) {
            Integer id = (Integer) rs.getObject(1);
            String name = (String) rs.getObject(2);
            String email = (String) rs.getObject(3);
            Date birth = (Date) rs.getObject(4);
            Customer customer = new Customer(id,name,email,birth);
            System.out.println(customer);
            Blob photo=rs.getBlob(5);
             bs = photo.getBinaryStream();
             fos = new FileOutputStream("2.jpg");
            byte[]bytes=new byte[1024];
            int len;
            while ((len=bs.read(bytes))!=-1) {
                fos.write(bytes,0,len);
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(co,ps,rs);
        try {
            if (bs!=null){
                bs.close();
            }
            if (fos!=null){
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
插入中遇到的问题:

在这里插入图片描述

批处理

@Test//像数据库一张表插入两万条数据
public void test() {
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn = JDBCUtils.getConnection();
        String sql = "insert into goods(name)values(?)";
        ps = conn.prepareStatement(sql);
        for (int i = 1; i <= 20000; i++) {
            ps.setString(1, "name_" + i);
            ps.executeUpdate();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn, ps, null);
    }
}
@Test//插入优化
public  void test3(){
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        conn = JDBCUtils.getConnection();
        //设置不允许自动提交数据
        conn.setAutoCommit(false);
        String sql = "insert into goods(name)values(?)";
        ps = conn.prepareStatement(sql);
        for (int i = 0; i <=20000 ; i++) {
            ps.setString(1, "name_" + i);
            ps.addBatch();
            if (i%500==0){
                ps.executeBatch();
                ps.clearBatch();
            }

        }
        conn.commit();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(conn,ps,null);
    }
}

4.事务

1.事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

 * > 一组逻辑操作单元:一个或多个DML操作。
   > 2.事务处理的原则:
   > 保证所事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。
   > 当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存
   > 下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。

说明:
1.数据一旦提交,就不可回滚

 * 2.哪些操作会导致数据的自动提交?

 * >DDL操作一旦执行,都会自动提交。

 * >set autocommit = false 对DDL操作失效

 * >DML默认情况下,一旦执行,就会自动提交。

 * >我们可以通过set autocommit = false的方式取消DML操作的自动提交。

 * >默认在关闭连接时,会自动的提交数据
考虑事务以后的转账问题
@Test
public void test() {
    Connection co = null;
    try {
        co = JDBCUtils.getConnection();
        co.setAutoCommit(false);
        String sql1 = "update user_table set balance=balance-? where user=?";
        String sql2 = "update user_table set balance=balance+? where user=?";
        update(co,sql1,100,"AA");
        //System.out.println(10/0);
        update(co,sql2,100,"BB");
        co.commit();
        System.out.println("转账成功!");
    } catch (SQLException e) {
        try {
            co.rollback();
            System.out.println("转账失败!");
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    } finally {
        JDBCUtils.closeResource(co,null,null);
    }

}
public static int update(Connection co, String sql, Object... args) {
    PreparedStatement ps = null;
    try {
        ps = co.prepareStatement(sql);
        for (int i = 0; i < args.length; i++) {
            ps.setObject(i + 1, args[i]);
        }
        return ps.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        JDBCUtils.closeResource(null, ps, null);
    }
    return 0;
}
事务的属性

ACID

在这里插入图片描述

数据操作过程中可能出现的问题:(针对隔离性)

在这里插入图片描述

数据库的四种隔离级别:(一致性和并发性:一致性越好,并发性越差)

在这里插入图片描述

如何查看并设置隔离级别:

在这里插入图片描述

5.连接池技术

传统连接存在的问题:

在这里插入图片描述

使用数据库连接池大的好处

1.提高程序的响应速度(减少了创建连接相应的时间)
2.降低资源的消耗(可以重复使用已经提供好的连接)
3.便于连接的管理

c3p0连接池

//使用c3p0数据库连接池的配置文件方式,获取数据库的连接:推荐
private static DataSource cpds = new ComboPooledDataSource("helloc3p0");

public static Connection getConnection() throws SQLException {
    Connection conn = cpds.getConnection();
    return conn;
}

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<named-config name="helloc3p0">
		<!-- 提供获取连接的4个基本信息 -->
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///test</property>
		<property name="user">root</property>
		<property name="password">yhd666</property>
		
		<!-- 进行数据库连接池管理的基本信息 -->
		<!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
		<property name="acquireIncrement">5</property>
		<!-- c3p0数据库连接池中初始化时的连接数 -->
		<property name="initialPoolSize">10</property>
		<!-- c3p0数据库连接池维护的最少连接数 -->
		<property name="minPoolSize">10</property>
		<!-- c3p0数据库连接池维护的最多的连接数 -->
		<property name="maxPoolSize">100</property>
		<!-- c3p0数据库连接池最多维护的Statement的个数 -->
		<property name="maxStatements">50</property>
		<!-- 每个连接中可以最多使用的Statement的个数 -->
		<property name="maxStatementsPerConnection">2</property>

	</named-config>
</c3p0-config>

dbcp连接池

//使用dbocp数据库连接池的配置文件方式,获取数据库的连接:推荐
private static DataSource source = null;
  static{
      try {
          Properties pros = new Properties();
          InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
          pros.load(is);
          //根据提供的BasicDataSourceFactory创建对应的DataSource对象
          source = BasicDataSourceFactory.createDataSource(pros);
      } catch (Exception e) {
          e.printStackTrace();
      }
  }

public static Connection getConnection2() throws SQLException {
    Connection conn = source.getConnection();
    return conn;
}

dbcp.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=yhd666

initialSize=10

druid连接池

//使用druid数据库连接池的配置文件方式,获取数据库的连接:推荐
    private static DataSource ds = null;
    static {
        try {
            Properties pro = new Properties();
//        pro.load(PoolTest.class.getClassLoader().getResourceAsStream("druid.properties"));//加载配置文件
            pro.load(ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"));
            ds = DruidDataSourceFactory.createDataSource(pro);//通过工厂创建数据源
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection3() {
        return ds.getConnection();//通过数据源获取连接
    }

druid.properties

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=yhd666
driverClassName=com.mysql.jdbc.Driver
#初始化时的连接数
initialSize=10
#最大连接池数量
maxActive=20
#获取连接时最大等待时间,单位毫秒。
maxWait=1000
#防御sql注入的filter:wall
filters=wall
#mysql下建议关闭
poolPreparedStatements=false
#建议配置为true,不影响性能,并且保证安全性。
testWhileIdle=true

6.DBUtils工具类

通用增删改

使用现成的jar中的QueryRunner测试增、删、改的操作:
//测试插入
	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "insert into customers(name,email,birth)values(?,?,?)";
			int insertCount = runner.update(conn, sql, "蔡徐坤","caixukun@126.com","1997-09-08");
			System.out.println("添加了" + insertCount + "条记录");
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			
			JDBCUtils.closeResource(conn, null);
		}
		
	}

查询操作1

使用现成的jar中的QueryRunner测试查询的操作:

//测试查询
	/*
	 * BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录。
	 */
	@Test
	public void testQuery1(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
			Customer customer = runner.query(conn, sql, handler, 23);
			System.out.println(customer);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}

查询操作2

/*
	 * BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合。
	 */
	@Test
	public void testQuery2() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id < ?";
			
			BeanListHandler<Customer>  handler = new BeanListHandler<>(Customer.class);

			List<Customer> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			
			JDBCUtils.closeResource(conn, null);
		}
		
	}

查询操作3

/*
	 * MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录。
	 * 将字段及相应字段的值作为map中的key和value
	 */
	@Test
	public void testQuery3(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			MapHandler handler = new MapHandler();
			Map<String, Object> map = runner.query(conn, sql, handler, 23);
			System.out.println(map);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}

查询操作4

/*
	 * MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
	 * 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
	 */
	@Test
	public void testQuery4(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id < ?";
		
			MapListHandler handler = new MapListHandler();
			List<Map<String, Object>> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}

查询操作5

/*
	 * ScalarHandler:用于查询特殊值
	 */
	@Test
	public void testQuery5(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select count(*) from customers";
			
			ScalarHandler handler = new ScalarHandler();
			
			Long count = (Long) runner.query(conn, sql, handler);
			System.out.println(count);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}
	@Test
	public void testQuery6(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select max(birth) from customers";
			
			ScalarHandler handler = new ScalarHandler();
			Date maxBirth = (Date) runner.query(conn, sql, handler);
			System.out.println(maxBirth);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}
	

自定义ResultSetHandler的实现类

/*
	 * 自定义ResultSetHandler的实现类
	 */
	@Test
	public void testQuery7(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			
			String sql = "select id,name,email,birth from customers where id = ?";
			ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){

				@Override
				public Customer handle(ResultSet rs) throws SQLException {
//					System.out.println("handle");
//					return null;
					
//					return new Customer(12, "成龙", "Jacky@126.com", new Date(234324234324L));
					
					if(rs.next()){
						int id = rs.getInt("id");
						String name = rs.getString("name");
						String email = rs.getString("email");
						Date birth = rs.getDate("birth");
						Customer customer = new Customer(id, name, email, birth);
						return customer;
					}
					return null;
					
				}
				
			};
			Customer customer = runner.query(conn, sql, handler,23);
			System.out.println(customer);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
			
		}
		
	}

资源关闭

使用dbutils.jar包中的DbUtils工具类实现连接等资源的关闭:
/**
	 * 
	 * @Description 使用dbutils.jar中提供的DbUtils工具类,实现资源的关闭
	 * @author shkstart
	 * @date 下午4:53:09
	 * @param conn
	 * @param ps
	 * @param rs
	 */
	public static void closeResource1(Connection conn,Statement ps,ResultSet rs){

		
		DbUtils.closeQuietly(conn);
		DbUtils.closeQuietly(ps);
		DbUtils.closeQuietly(rs);
	}

7.DAO设计模式

BaseDao类

package com.atguigu.utils;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.*;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @author yinhuidong
 * @createTime 2020-02-12-21:07
 */
public class BaseDao<T> {
    private Class<T> type;// 定义一个变量来接收泛型的类型

    public BaseDao() {
        //当子类调用父类方法时获取当前子类的父类类型
        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        //获取具体的泛型的类型
        Type[] types = parameterizedType.getActualTypeArguments();
        this.type = (Class<T>) types[0];
    }

    private QueryRunner runner = new QueryRunner();

    /**
     * 考虑到事务以后的通用增删改
     */
    public int update(Connection co, String sql, Object... args) throws SQLException {
        return runner.update(co, sql, args);
    }

    /**
     * 考虑到事务以后的通用查询一
     */
    public <T> T select1(Connection co, String sql, Object... args) throws SQLException {
        BeanHandler<T> handler = new BeanHandler<>((Class<T>) type);
        return runner.query(co, sql, handler, args);
    }

    /**
     * 考虑到事务以后的通用查询二
     */
    public <T> List<T> select2(Connection co, String sql, Object... args) throws SQLException {
        BeanListHandler<T> handler = new BeanListHandler<>((Class<T>) type);
        return runner.query(co, sql, handler, args);
    }

    /**
     * MapHandler
     */
    public Map<String, Object> select3(Connection co, String sql, Object... args) throws SQLException {
        MapHandler handler = new MapHandler();
        return runner.query(co, sql, handler, args);
    }

    /**
     * MapListHandler
     */
    public List<Map<String, Object>> select4(Connection co, String sql, Object... args) throws SQLException {
        MapListHandler handler = new MapListHandler();
        return runner.query(co, sql, handler, args);
    }
    /**
     * 利用ScalarHandler获取单一值
     * 考虑事务问题
     */
    public  Object select5(Connection co, String sql, Object... args) throws SQLException {
        ScalarHandler handler = new ScalarHandler();
        return runner.query(co, sql, handler, args);
    }
}

Dao接口

/**
 * @author yinhuidong
 * @createTime 2020-02-06-14:49
 */
public interface CustomerDao {
    int add(Connection co,Object...args) throws SQLException;
    int delete(Connection co,Object...args) throws SQLException;
    int updates(Connection co,Object...args) throws SQLException;
    Customer FindByValue(Connection co,Object...args) throws SQLException;
    List<Customer> FindMore(Connection co,  Object...args) throws SQLException;
    Long count(Connection co,Object...args) throws SQLException;
}

DaoImpl实现类

/**
 * @author yinhuidong
 * @createTime 2020-02-06-14:56
 */
public class CustomerDaoImpl extends BaseDao<Customer> implements CustomerDao {


    @Override
    public int add(Connection co, Object... args) throws SQLException {
        String sql="insert into customers (name,email,birth)values(?,?,?);";
        return update(co,sql,args);

    }

    @Override
    public int delete(Connection co, Object... args) throws SQLException {
        String sql="delete from customers where id=?;";
        return update(co,sql,args);
    }

    @Override
    public int updates(Connection co, Object... args) throws SQLException {
        String sql="update customers set name=?,email=?,birth=? where id=?;";
        return update(co,sql,args);
    }

    @Override
    public Customer FindByValue(Connection co, Object... args) throws SQLException {
        String sql="select id,name,email,birth from customers where id=?;";
        return selectById(co,sql,args);
    }

    @Override
    public List<Customer> FindMore(Connection co, Object... args) throws SQLException {
        String sql="select id,name,email,birth from customers ;";
        return selectMore(co,sql,args);
    }

    @Override
    public Long count(Connection co,Object...args) throws SQLException {
        String sql="select count(*) from customers ;";
        return (Long) selectOne(co,sql,args);
    }
}

测试类

/**
 * @author yinhuidong
 * @createTime 2020-02-06-15:10
 */
public class TestDao {
    private CustomerDao dao=new CustomerDaoImpl();
    @Test
    public void test(){
        Connection co=null;
        try {
            co=JDBCUtils.getConnection();
            co.setAutoCommit(false);
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            Date date = format.parse("2017-02-21");
            int add = dao.add(co, "name", "email", new java.sql.Date(date.getTime()));
            co.commit();
            if (add>0){
                System.out.println("添加成功!");
            }else{
                System.out.println("添加失败!");
            }
        } catch (SQLException e) {
            try {
                co.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } catch (ParseException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(co);
        }
    }
    @Test
    public void test2(){
        Connection co=null;
        try {
            co=JDBCUtils.getConnection();
            co.setAutoCommit(false);
            Customer customer = dao.FindByValue(co, 21);
            co.commit();
            System.out.println(customer);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResource(co);
        }

    }

}

JDBCUtils工具类

package com.atguigu.java6;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.util.JdbcUtils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

/**
 * @author yinhuidong
 * @createTime 2020-02-27-21:52
 */
public class JDBCUtils {
    /**
     * c3p0
     */
    private static DataSource source1=new ComboPooledDataSource("helloc3p0");
    public static Connection getConnection() throws SQLException {
        return source1.getConnection();
    }
    /**
     * dbcp
     */
    private static DataSource source2=null;
    static{
        try {
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("dbcp.properties");
            Properties prop = new Properties();
            prop.load(is);
            source2=BasicDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection2() throws SQLException {
        return source2.getConnection();
    }
    private static DataSource source3=null;
    static {
        try {
            Properties prop = new Properties();
            InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
            prop.load(is);
            source3=DruidDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection3() throws SQLException {
        return source3.getConnection();
    }
    /**
     * closeResource
     */
    public static void closeResource(Connection co){
        JdbcUtils.close(co);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值