java基础5:工厂模式、单例模式、File文件类、递归、IO流、Properties配置文件、网络编程、利用IO流模拟注册登录功能、关于反射、JDK动态代理

本文介绍了Java设计模式中的工厂方法模式,展示了如何通过接口创建抽象类的实例,以及单例模式的饿汉式和懒汉式的实现,包括线程安全问题的解决。同时,详细讲解了File和Runtime类的单例模式应用,以及IO流复制文件的多种方式和递归在文件操作中的运用。
摘要由CSDN通过智能技术生成

1.工厂模式

23种java设计模式之一

1)提供抽象类(基类)

2)提供一些子类,完成方法重写

3)提供一个接口:完成具体子类的实例化对象的创建,不能直接new子类,构造函数私有化。

优点:具体的子类创建,交给工程类来完成(面向接口编程),便于功能的维护(代码维护)

缺点:代码量非常大

​ 如果有新的类增加,那么除了必须增加类之外还得提供对应的具体工厂类用来创建当前类的实例。

以动物类为例

//抽象的动物类
public abstract class Animal {
    public abstract  void eat() ;
    public abstract  void sleep() ;
}

//工厂接口,具体的对象的创建工作----交给工厂接口(抽象类)的子实现类完成!
public interface Factory {
    public Animal createAnimal() ;
}

//猫类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void sleep() {
        System.out.println("猫趴着睡觉...");
    }
}

//猫的工厂类
public class CatFactory  implements  Factory{
    @Override
    public Animal createAnimal() {
        return new Cat();
    }
}

//狗类
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头...");
    }

    @Override
    public void sleep() {
        System.out.println("狗躺着睡觉...");
    }
}

//狗的工厂类
public class DogFactory  implements Factory{
    @Override
    public Animal createAnimal() { //接口多态中---->抽象类多态
        return new Dog();
    }
}


//测试类
/*
 * 工厂方法模式:
 *
 *      1)抽象类:Animal 动物类 (基类)
 *      2)提供一些子类,完成方法重写:eat(){} sleep(){}
 *      3)提供一个接口 :
 *                  Factory
 *                          public Animal createAnimal() ;
 *
 *                 DogFactory /CatFactory具体的子实现类实现对象的创建
 *
 *      优点:具体的动物创建,交给类工厂类来实现 (面向接口编程),便于功能维护(代码维护)
 *      弊端:代码量非常大
 *              如果有新的类中增加,那么还必须提供对应具体的工厂类创建当前类实例!
 *
 *
 */
public class FactoryPattern {
    public static void main(String[] args) {

        //没有工厂方法之前:
        //使用抽象类多态
        Animal a = new Dog() ;
        a.eat();
        a.sleep();
        System.out.println("-------------------------------");
        a = new Cat() ;
        a.eat();
        a.sleep();

        //使用工厂方式模式来操作
        System.out.println("-------------------------------------");
        Factory factory = new DogFactory() ;
        Animal animal = factory.createAnimal();
        animal.eat();
        animal.sleep();

        System.out.println("---------------------------------------");
        factory = new CatFactory() ;
        Animal a2 = factory.createAnimal();
        a2.eat();
        a2.sleep();
    }
}

2.单例模式

始终再内存种创建一个实例

单例模式的分类:

​ 饿汉式:永远不会出现问题的单例模式(最简单的一种)

​ 懒汉式:可能出现问题的一种单例模式

2.1.饿汉式

1)构造方法私有化:保证外界不能直接new对象

2)在当前类的成员位置创建当前类的实例

3)提供一个静态的功能,返回值就是当前类的实例

//学生类(单例模式)
public class Student {
    //成员变量
   // public  static Student s = new Student() ; //当前类实例
    private  static Student s = new Student() ; //当前类实例 (静态实例变量)
    private Student(){}

    //静态功能
    //返回是当前类本身
    public static Student getStudentInstance(){
        return s;
    }
}

//测试类
public class Single_Pattern_01 {
    public static void main(String[] args) {

       // Student s1 = new Student() ;
        //Student s2 = new Student() ;
       // System.out.println(s1==s2);
       // System.out.println(s1.getClass()==s2.getClass());
        //class  com.qf.single_pattern.Student

        //调用公共方法
        Student s1 = Student.getStudentInstance();
        Student s2 = Student.getStudentInstance();
        Student s3 = Student.getStudentInstance();
        Student s4 = Student.getStudentInstance();
        System.out.println(s1==s2);
        System.out.println(s1==s3);
        System.out.println(s1==s4);
       // Student.s = null ; // 赋值null,堆内存不开辟空间(外界可以更改当前类地址值)
        //当前这个成员变量的实例s应该被私有修饰(保证安全性)
    }
}

2.2.懒汉式

1)构造方法私有化

2)在当前成员变量的位置声明变量:数据类型就是当期类 (私有的,静态的)

3)3)提供静态功能,返回值还是当前类本身;判断如果当前没有给当前类型变量为null,直接new当期类的实例;如果不为null,就返回当前类的变量!

在延迟加载或者懒加载的时候就会出现安全问题

面试中,问的最多就是懒汉式----->如何解决线程安全问题: 想到同步方法解决

//工人类(单例模式)
public class Worker {

    //成员变量位置声明变量w
    private static Worker w;//默认值null
    //构造方法私有化
    private Worker() {} //外界不能直接new 对象

    //提供静态功能,返回值是当前类本身
    //w1,w2,w3 互相抢占getWorkerIntance的内容---会造成安全问题
   /*
    public static Worker getWorkerIntance(){
        //w1
        //先判断w变量为null
        if(w == null){ //当为null
            w = new Worker() ; //如果当前w变量为null,创建一个新的对象,返回
            return w;
        }
        return w ;
     }
    */

   //改进:同步代码块---->静态的同步方法
    public synchronized static Worker getWorkerIntance(){ //锁对象:Worker.class
       // synchronized (w){
            //w1
            //先判断w变量为null
            if(w == null){ //当为null
                w = new Worker() ; //如果当前w变量为null,创建一个新的对象,返回
                return w;
            }
            return w ;
        //}

    }
}

//测试类
public class Single_pattern_02 {
    public static void main(String[] args) {

        //使用Worker第一次调用getInstance方法
        Worker w1 = Worker.getWorkerIntance();   //第一个用户操作这个方法----属于一个线程
        //第二次
        Worker w2 = Worker.getWorkerIntance();      //第二个用户操作---->属于一个线程
        System.out.println(w1==w2);
        Worker w3 = Worker.getWorkerIntance();        //第三个用户操作--->属于一个线程
        System.out.println(w1==w3);
    }

2.3.Runtime类

Runtime类就是按照单例模式设计的类

功能:

public int availableProcessors():获取本地计算机的处理器的数量

public Process exec(String command):开启某个软件的进程(参数为字符串命令)

Runtime类的源码

Runtime类的原码

 public class Runtime {  单例模式之饿汉式(类一加载就在当前内存中创建一个实例)
    //静态实例变量
    private static Runtime currentRuntime = new Runtime();

//公共的静态功能:返回值就是当类本身
public static Runtime getRuntime() {
    return currentRuntime;
}

   //Don't let anyone else instantiate this class  构造方法私有:外界不能直接访问
  private Runtime() {}
                       
}

3.File文件类

File 文件和目录(文件夹)路径名的抽象表示。

构造方法:

File(String pathname) :参数就是指定的路径/如果没有指定路径(默认是在当前项目下)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例

File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。

File(String parent, String child):参数1:父目录地址 参数2:具体的子文件地址

成员方法:

创建/删除/重命名

  •  public boolean createNewFile()   throws IOException :表示创建文件 :如果不存在,则创建
    
  •  public boolean mkdir():创建文件夹,如果不存在,则创建;否则就返回false
    
  •  public boolean mkdirs():创建多个文件,如果父目录不存在,则创建
    
  •  public boolean delete():删除文件或者文件夹(如果删除文件夹,文件夹必须为空目录)
    
  •  public boolean renameTo(File dest):重命名
    
public class FileDemo {
    public static void main(String[] args) throws IOException {
        //表示:E盘下的demo文件夹中的a.txt文件
        //File(String pathname) 方式1 (推荐)
       // File file = new File("e://demo//a.txt") ;只是表示这个路径,如果创建a.txt文件,系统找不到指定路径

        File file = new File("D:\\EE_2106\\day25\\code\\a.txt"); //绝对路径
        File file2 = new File("aaa.txt");//没有带路径,就默认在当前项目下(相对路径)
        File file3 = new File("D:\\EE_2106\\day25\\code\\demo") ;
        File file4 = new File("aaa\\bbb\\ccc\\ddd") ;

        //File(File parent, String child) 方式2
      /*  File file2 = new File("E://demo") ;
        File file3 = new File(file2,"a.txt") ;

        //File(String parent, String child):方式3
        File file4 = new File("E://demo","a.txt") ;*/
       // public boolean createNewFile()
        System.out.println(file.createNewFile());
        System.out.println(file2.createNewFile());
        System.out.println(file3.mkdir());
        System.out.println(file4.mkdirs());
        System.out.println(file3.delete());
        System.out.println(file.delete());
        System.out.println("------------------------");
        //D:\EE_2106\day25\code路径下logo.jpg :描述下这个地址File
       // File srcFile  = new File("D:\\EE_2106\\day25\\code\\logo.jpg") ;
        File srcFile = new File("D:\\EE_2106\\day25\\code\\mv.jpg") ;
        File destFile = new File("高圆圆.jpg") ;//当前项目路径下了
        //public boolean renameTo(File dest)
        System.out.println(srcFile.renameTo(destFile)) ;

    }
}

判断功能:

  • public boolean canRead()是否可读
  • public boolean canWrite()是否可写
  • public boolean exists():是否存在
  • public boolean isFile():是否是文件
  • public boolean isDirectory():是否是文件夹
  • public boolean isHidden():是否隐藏

高级获取功能:

  •  public long length()
    
  •  public String getName():获取抽象路径 名所表示的文件或者目录的名称
    
  •  public File[] listFiles():获取某个目录下的所有的文件以及文件夹的File数组
    
  •  public String[] list():获取某个抽象路径名所表示的文件以及目录的字符串数组
    
public class FileDemo2 {
    public static void main(String[] args) {

        //创建File对象,描述当前项目下的aaa.txt文件
        File file = new File("aaa.txt") ;
        System.out.println(file.canRead());
        System.out.println(file.canWrite());
        System.out.println(file.exists());
        System.out.println(file.isDirectory());//false
        System.out.println(file.isFile());
        System.out.println(file.isHidden());
        System.out.println(file.length());
        System.out.println(file.getName());
        System.out.println("------------------------");

        // public File[] listFiles():获取某个目录下的所有的文件以及文件夹的File数组
        //描述D盘
        File file2 = new File("d://") ;
        File[] fileArray = file2.listFiles();
        //防止空指针异常
        if(fileArray!=null){
            for(File f :fileArray){
                System.out.println(f.getName());
            }
        }
        System.out.println("----------------------------------");
        //public String[] list():获取某个抽象路径名所表示的文件以及目录的字符串数组
        String[] strArray = file2.list();
        if(strArray!=null){
            for(String s:strArray){
                System.out.println(s);
            }
        }

    }
}

4.递归

递归指的是方法的递归,简而言之,就是在方法体中由于某种规律的出现的出现而出现了方法自己调用自己的情况。

前提条件:

  • 1必须有一个成员方法
  • 2)必须有方法递归的出口条件(结束条件),如果没有出口条件,就是死递归…
  • 3)还存在一定的规律

注意事项:构造方法不存在递归

求5的阶乘

public class DiGuiDemo {

   /* public DiGuiDemo(){
        //DiGuiDemo() ;//构造方法不能使用递归
    }
*/
   public static void main(String[] args) {

       System.out.println("5的阶乘是:"+jieCheng(5));
   }

    //递归:需要考虑(一定要有规律)
    private static int jieCheng(int i) {
       if(i==1){
           return 1 ;
       }else{
           return i * jieCheng(i-1) ;
       }
    }

}

利用递归的思想进行文件的遍历操作

如:需求:使用递归方式去删除D盘某个demo文件夹中有很多个目录,每个目录中可能还有目录,删除这些所有目录中的带.java文件的文件!

分析:1)描述下D盘下demo文件夹 File srcFloder = new File(“d://demo”)

​ 2)调用递归删除方法

​ //递归删除多级目录中的java文件

​ public void deleteSrcFile(File srcFile){

​ //获取srcFile的所有的文件夹以及文件的File数组
​ //非空判断
​ //判断如果是文件夹
​ 回到到2)继续调用递归删除
​ 否则,是文件
​ 判断是否以.java文件
​ 最终删除文件即可!
​ }

public class DiGuiTest {
    public static void main(String[] args) {

         //描述D盘的demo文件夹
         File srcFloder = new File("d://demo") ;
         //调用递归删除的方法
        deleteFloder(srcFloder) ;
    }
    public static void deleteFloder(File srcFloder) {
        //获取srcFloder下的所有的文件以及文件的File数组
        File[] fileArray = srcFloder.listFiles();
        if(fileArray!=null){
            for(File file:fileArray){
                //获取到每一个file对象
                //如果file是文件夹,继续回到deleteFloder(srcFloder) ; 删除文件夹
                if(file.isDirectory()){
                    deleteFloder(file) ;
                }else{
                    //判断是文件,必须以.java结尾 文件
                    if(file.getName().endsWith(".java")){
                        //删除的同时----获取名称
                        System.out.println(file.getName()+"------------"+file.delete());
                    }
                }

            }
            //删除文件夹
            System.out.println(srcFloder.getName()+"----"+srcFloder.delete());
        }
    }
}

5.IO流

5.1IO流的分类

按流的方法分:	输入流、输出流。
按流的类型分:	字节流和字符流		先有字节再有字符(字符和字节之间存在字符转换流					InputStreamReader和OutputStreamWriter)
			
			
			在此的基础上再按流的方向分为:
					字节输入流:InputStream		所有字节输入流的父类
					FileInputStream
					BufferedInputStream
					字节输出流:OutputStream	所有字节输出流的父类
					FileOutputputStream
					BufferedOutputStream
					
					字符转换流InputStreamReader和OutputStreamWriter
					字符转换流的快捷方式FileReader和FileWriter
					
					字符输入流:Reader	所有字符输入流的父类
					 BufferedReader
					字符输出流:Writer	所有字符输出流的父类
					 BufferedWriter

5.2关于利用IO流复制文件

一共有11种方式:注意在读取视频、音频、图片时只能用字节流

​ 基本的字节流:一次读取一个字节/一次读取一个字节数组

​ (FileInputStream/FileOutputStream)读写文件的原始字节流

​ 字节缓冲流:一次读取一个字节/一次读取一个字节数组**(使用居多!)**

​ (BufferedInputStream/BufferedOutputStream)

​ 字符转换流:一次读取一个字符/一次读取一个字符数组**(字节和字符的桥梁)**

​ (InputStreamReader/OutputStreamWriter)它不能直接操作文件,jdk提供了这两 种类型的便捷类**FileReader(File file)、FileReader(String pathname); FileWriter(File file)、FileWriter(String filename)**二者默认使用平台的编码和解码 方式(UTF-8)

​ 转换流的便捷类:一次读取一个字符/一次读取一个字符数组

​ 即FileReader和FileWriter

​ BufferedReader/BufferedWriter:一次读取一个字符/一次读取一个字符数组/特有功 能(一次读取一行内容)

​ 特有功能**(使用居多)**:public String readLine()和 public String newLineP()

用复制一个Mp4文件来说明,字节数组复制和字节复制的速度差异

基本的字节流

按字节一个个来

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

public class IOPaste {
    public static void main(String[] args) {
        //源文件路径
        long start = System.currentTimeMillis();
        String srcFile = "E:\\qianf\\Test.mp4" ;
        //复制文件路径
        String destFile = "paste.mp4" ;
        //复制方法
        copy(srcFile,destFile);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+(end-start)+"毫秒");

    }

    private static void copy(String srcFile, String destFile) {
        FileInputStream fis = null ;    //读文件   读入
        FileOutputStream fos = null ;   //写文件   写出

        try {
            fis = new FileInputStream(srcFile);     //实例化读
            fos = new FileOutputStream(destFile);      //实例化写

            int read = 0 ;      //读到的字节
            while ((read = fis.read())!=-1){
                fos.write(read);    //读一个写一个
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

按字节数组来看

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

public class IOPasteArray {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //源文件路径
        String srcFile = "E:\\qianf\\Test.mp4" ;
        //复制文件路径
        String destFile = "pasteArray.mp4" ;
        //复制方法
        copy(srcFile,destFile);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+(end-start)+"毫秒");

    }

    private static void copy(String srcFile, String destFile) {
        FileInputStream fis = null ;    //读文件   读入
        FileOutputStream fos = null ;   //写文件   写出

        try {
            fis = new FileInputStream(srcFile);     //实例化读
            fos = new FileOutputStream(destFile);      //实例化写

            byte[] buffer = new byte[1024];     //一次读取一个1024个字节
            int len = 0;        //真实的读取长度
            while ((len = fis.read(buffer))!=-1){
                fos.write(buffer);    //读一个数组写一个数组
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注:当前用字节数组和字节的差异大概是681倍,当然,随着字节数组的长度的增加这个差异会愈发明显。可以通过修改字节数组的长度自行测试。需要注意的是此时字节数组的长度都是1024的倍数!

字节缓冲流

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

/**
 * 字节缓冲输入流
 * 构造方法
 * BufferedInputStream(InputStream in) :默认缓冲区大写 (足够大了)
 * 提供一个指定的缓冲区大小
 * BufferedInputStream(InputStream in, int size)
 */
public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建一个字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt")) ;
        /**
         *  class BufferedInputStream{
         *
         *      private static int DEFAULT_BUFFER_SIZE = 8192; //8kb
         *
                 *  public BufferedInputStream(InputStream in) {
                 *         this(in, DEFAULT_BUFFER_SIZE);
                 *     }
         *
         *     public BufferedInputStream(InputStream in, int size) {
         *         super(in);
         *         if (size <= 0) {
         *             throw new IllegalArgumentException("Buffer size <= 0");
         *         }
         *         buf = new byte[size];  //底层还是一个字节数组
         *     }
         *
         *     }
         */

        //读内容
        //一次读取一个字节
      /*  int by = 0 ;
        while((by=bis.read())!=-1){
            //打印控制台上
            System.out.print((char)by);
        }*/

      //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0  ;
        while((len=bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

    }
}


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

/**
 * 缓冲流的特点:提供缓冲区大写:1024的8倍  还是通过底层流提高读速度(FileInputStream)
 * BufferedInputStream
 *
 *
 *
 * BufferedOutputStream(OutputStream out)       推荐使用这个:默认缓冲区大小 足够大了  8kb
 * 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
 * BufferedOutputStream(OutputStream out, int size)
 */
public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //BufferedOutputStream(OutputStream out)  //参数父类:抽象类
        //创建字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
        /**
         * 源码:
         * public BufferedOutputStream(OutputStream out) {
         *         this(out, 8192);
         *     }
         *
         *
         *  public BufferedOutputStream(OutputStream out, int size){
         *super(out);
         *if (size <= 0) {
         *throw new IllegalArgumentException("Buffer size <= 0");
         *}
         *buf = new byte[size]; //创建了字节数组  byte[] buf = new  byte[8192]
         *}
         */

        //写数据
        //写一个字节数组
        bos.write("hello.bufferedOutputStream".getBytes());

        //释放资源
        bos.close();

    }
}

//复制Mp4文件
/*
 * 读取D://a.mp4
 * 将这个文件内容复制到 当前项目下的copy.mp4中
 *  字节缓冲流一次读取一个字节
 *          共耗时348毫秒
 *  字节缓冲流一次读取一个字节数组
 *          共耗时47毫秒
 *
 *
 */
public class CopyMp4 {
    public static void main(String[] args) {
        //起始时间
        long start = System.currentTimeMillis() ;
        //copyMp4_3("D://a.mp4","copy.mp4") ;
        copyMp4_4("D://a.mp4","copy.mp4") ;

        //结束时间
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时"+(end-start)+"毫秒");
    }

    //缓冲流一次读取一个字节数组
    private static void copyMp4_4(String srcFile, String destFile) {
        BufferedInputStream bis  = null ;
        BufferedOutputStream bos = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
            bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
            //读写操作
            byte[] bytes = new byte[1024] ;
            int len = 0 ;
            while((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //缓冲流一次读取一个字节
    private static void copyMp4_3(String srcFile, String destFile) {
        BufferedInputStream bis  = null ;
        BufferedOutputStream bos = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
            bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
            //读写操作
            int by = 0 ;
            while((by=bis.read())!=-1){
                bos.write(by);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Reader和Writer的子类转换流InputStreamReader和OutputStreamWriter

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;

/**
 * Reader:抽象类
 *   具体的子类:字符转换输入流 InputStreamReader
 *   构造方法
 *    InputStreamReader(InputStream in) :使用平台默认解码集进行读
 *    InputStreamReader(InputStream in,String charset) :使用指定的解码集进行读
 *
 */
public class ReaderDemo {

    public static void main(String[] args) throws Exception {
        //创建字符转换输入流对象
        //InputStreamReader(InputStream in,String charset) :使用指定的解码集进行读
       // InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"UTF-8") ;

        //InputStreamReader(InputStream in)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt")) ;//平台默认的解码集进行读取


        //读一个字符数组
        char[] chs = new char[1024] ;
        //实际字符数
        int len = 0 ;
        while((len=isr.read(chs))!=-1){
            //打印控制台上:每次获取的实际长度
            System.out.println(new String(chs,0,len));
        }

        //释放资源
        isr.close();
    }
}

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

/**
 * 字符流的出现是在字节流的后面,可以解决中文乱码问题
 *
 * Writer:抽象类
 *      提供子类:字符转换输出流: 字节输出流通向字符输出流的桥梁!
 *      构造方法
 *              OutputStreamWriter(OutputStream out) :使用平台默认编码集写入数据
 *              OutputStreamWriter(OutputStream out, String charsetName) :使用指定的字符集进行编码 写入数据
 *
 */
public class WriterDemo {
    public static void main(String[] args) throws Exception {
        //创建字符缓冲输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt")) ;//使用平台的默认编码集(utf-8)

        //写入数据
        /**
         * write(String str)
         * write(int ch):写入字符: 字符--->对应的ASCII码表
         * write(char[] ch)
         * write(char[] ch,int off,int len)
         * write(String str,int off,int len)
         */
        osw.write("hello,字符流我来了");
        osw.write(97);
        char[] chs = {'A','B','C','D','E'} ;
        osw.write(chs);

        osw.write(chs,2,2);
        //使用flush():刷新字符输出流
        osw.flush();

        //关闭资源,释放相关的资源
        osw.close();  //关闭之前,flush

        //osw.write(98);

    }
}

Reader和Writer的便捷式FileReader和FileWriter

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;

/**
 * Reader/Writer子类:转换流
 *      InputStreamReader(InputStream in)
 *      OutputStreamWriter(OutputStream out)
 *
 *      他们不能直接去操作文件,jdk提供了这两种类型的便捷类,可以直接操作文件
 *      FileReader(File file)
 *      FileReader(String pathname)
 *
 *      FileWriter(File file)
 *      FileWriter(String filename)
 *
 *      这两个类:使用的平台的默认编码和解码 (utf-8)
 *
 *需求:
 *   当前项目下的ReaderDemo.java  复制到 D://Demo.java
 *   针对文本文件:优先采用字符流
 */
public class CopyFile {
    public static void main(String[] args) throws Exception {

        //1)封装源文件:
        FileReader fileReader = new FileReader("ReaderDemo.java") ;
        //2)封装目文件
        FileWriter fileWriter = new FileWriter("D://Demo.java") ;

        //3)一次读取一个字符
        /*int by = 0 ; //实际字符数
        while((by=fileReader.read())!=-1){
            fileWriter.write(by) ;
            fileWriter.flush() ;
        }*/
        //一次读取一个字符数组
        char[] charArray = new char[1024] ;
        int len = 0 ;
        while((len=fileReader.read(charArray))!=-1){
            //写入实际字符数
            fileWriter.write(charArray,0,len);
            fileWriter.flush();
        }

        //关闭
        fileWriter.close();
        fileReader.close();
    }
}

字符缓冲输入输出流BufferReader和BufferWriter

public class CopyFile {
    public static void main(String[] args) {


        BufferedReader br = null ;
        BufferedWriter bw = null ;
        try {
            //封装源文件:前项目下的    ReaderDemo.java
             br = new BufferedReader(new FileReader("ReaderDemo.java")) ;
             bw = new BufferedWriter(new FileWriter("copy.java")) ;

             //使用特有功能读取一行内容
            String line = null ;
            while((line=br.readLine())!=null){
                //读取一行,bw写一行并换行,然后刷新
                bw.write(line);
                bw.newLine();
                bw.flush();;
            }

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

5.3合并流

SequenceInputStream将两个以上的文件进行读取

利用vector集合将需要读取的多个文件收集在一起

/*
 *  BufferedWriterDemo.java
 * ReaderDemo.java
 * CopyMp4.java
 * 复制到D://hello.java文件中
 */
public class CopyFileDemo2 {
    public static void main(String[] args) throws Exception {
        //public SequenceInputStream(Enumeration<? extends InputStream> e)

        //需要三个字节输入流对象
        InputStream is1 = new FileInputStream("BufferedWriterDemo.java") ;
        InputStream is2 = new FileInputStream("ReaderDemo.java") ;
        InputStream is3 = new FileInputStream("CopyMp4.java") ;

        //创建一个Vector集合
        Vector<InputStream> vector = new Vector<>() ;

        //添加流对象
        vector.add(is1) ;
        vector.add(is2) ;
        vector.add(is3) ;

        //public Enumeration<E> elements() ---- >类似于Collection的Iterator迭代器
        Enumeration<InputStream> enumeration = vector.elements();

        //创建合并流对象  封装源文件
        SequenceInputStream sis = new SequenceInputStream(enumeration);

        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d://hello.java")) ;

        //一次读取一个字节
        int by = 0 ;
        while((by=sis.read())!=-1){
            bos.write(by);
            bos.flush();
        }

        bos.close();
        sis.close();


    }
}

/**
 * SequenceInputStream:
 *      字节流的逻辑串联!
 *      可以将两个或者是两个以上的文件进行读的操作 ,只能操作源文件
 *
 * 构造方法
 * public SequenceInputStream(InputStream s1,InputStream s2)
 * 参数1和参数2:分别要读取的字节输入流对象
 *
 *        之前:
 *              当前项目的a.txt---->当前项目b.txt
 *
 *        现在:合并流
 *              当前项目a.java+b.java---->当前项目下的c.java文件中
 *
 *              将当前项目下的BufferedWriterDemo.java+copy.java文件---->复制到		当前项目下b.java文件中
 *
 *
 */
public class CopyMulFile {
    public static void main(String[] args) throws Exception {

        //创建两个字节输入流对象
        InputStream is = new FileInputStream("BufferedWriterDemo.java") ;
        InputStream is2 = new FileInputStream("copy.java") ;

        //public SequenceInputStream(InputStream s1,InputStream s2)
        //创建字节合并流
        SequenceInputStream sis = new SequenceInputStream(is,is2) ; //SequenceInputStream extends InputStream

        //封装目的地文件:
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.java")) ;


        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len = sis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        //释放资源
        bos.close();
        sis.close();
    }
}

5.4序列化

何为序列化,为什么要存在序列化?

将一个对象变成数据流就是序列化,将一个数据流还原成对象称为反序列化。在分布式系统中,为了信息的互通,而传输数据的时候是以流的方式进行的,所以序列化和反序列化显得尤为重要。

序列化:ObjectOutputStream 将某个实体对象写入到流对象中---->将一个对象变成流数据

构造方法:

protected ObjectOutputStream()

protected ObjectOutputStream(OutputStream out) :将某个对象通过底层的基本字节输出进行写的动作public final void writeObject(Object obj) throws IOException

反序列化:ObjectInputStream 将流数据----还原成 “对象”

构造方法:

public ObjectInputStream(InputStream in)

public final Object readObject() throws IOException, ClassNotFoundException

需求:将Person p = new Person(“高圆圆”,42) ;---->变成流对象 进行传输

package com.qf.serailizable_05;

import java.io.*;

import static com.sun.deploy.perf.DeployPerfUtil.write;

/**
 * 序列化:ObjectOutputStream
 *      将某个实体对象写入到流对象中---->将一个对象变成 流数据
 *      构造方法
 *          protected ObjectOutputStream()
 *          protected ObjectOutputStream(OutputStream out) :将某个对象通过底层的基本字节输出进行写的动作
 *      public final void writeObject(Object obj)
 *                        throws IOException
 *
 *      需求:将Person p = new Person("高圆圆",42) ;---->变成流对象 进行传输
 * 反序列化:ObjectInputStream
 *      将流数据----还原成 "对象"
 *      构造方法
 *          public ObjectInputStream(InputStream in)
 *          public final Object readObject()
 *                         throws IOException,  ClassNotFoundException
 *
 */
public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

      //  myWrite() ;//写

        myRead() ;//读
    }

    //反序列化
    private static void myRead() throws IOException, ClassNotFoundException {
        //创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;

        //读
        //public final Object readObject()
        Object object = ois.readObject();
        //关闭流
        ois.close();
        System.out.println(object);//toString()


        //java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
        //当从对象流读取的控制信息违反内部一致性检查时抛出。


    }

    //序列化
    //将Person p = new Person("高圆圆",42) ;---->变成流对象 进行传输
    private static void myWrite() throws IOException {
        //创建Person对象
        Person p = new Person("高圆圆",42) ;

        //protected ObjectOutputStream(OutputStream out):创建一个序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;

        //将p对象写入到oos流对象中
        //public final void writeObject(Object obj)throws IOException
        oos.writeObject(p);

        //释放资源
        oos.close();
    }
}

6.Properties配置文件

Properties extends Hashtable<K,V> ,它没有泛型,Key和Value都是String

表示一组持久的属性。 Properties可以保存到流中或从流中加载。

属性列表中的每个键及其对应的值都是一个字符串。

1)可以使用Map的功能

put(K ,V)

遍历:keySet()通用

2)有自己特有功能添加元素和遍历

public Object setProperty(String key,String value):给属性列表中添加属性描述(key和value)

public Set stringPropertyNames():获取属性列表中的所有的键

public String getProperty(String key):在属性列表中通过键获取值

public class PropertiesDemo {
    public static void main(String[] args) {
        //Properties() :空参构造
        Properties properties = new Properties() ;

       // System.out.println(properties);
        //利用Map集合的功能去添加元素
        properties.put("高圆圆","赵又廷") ;
        properties.put("文章","姚笛") ;
        properties.put("文章","马伊琍") ;
        properties.put("王宝强","马蓉") ;
       // System.out.println(properties);

        //遍历
        Set<Object> keySet = properties.keySet();
        for(Object key :keySet){
            //通过key获取value
            Object value = properties.get(key);
            System.out.println(key+"---"+value);
        }

        System.out.println("---------------------------------------");

        /**
         * 推荐Properties作为集合类 的遍历方式
         *   public Object setProperty(String key,String value):给属性列表中添加属性描述(key和value)
         *    public Set<String> stringPropertyNames():获取属性列表中的所有的键
         *    public String getProperty(String key):在属性列表中通过键获取值
         */

        //创建一个空的属性列表
        Properties prop = new Properties() ;
        prop.setProperty("张三","30") ;
        prop.setProperty("李四","40") ;
        prop.setProperty("王五","35") ;
        prop.setProperty("赵六","45") ;


        //遍历:
        Set<String> set = prop.stringPropertyNames();//获取所有键
        for(String key:set){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);

        }

    }
}

Propeties:键和值都是字符串类型:可能需要在src(类所在的路径:类路径)作为一个配置文件

xxx.properties

package com.qf.properties_06;

import java.io.*;
import java.util.Properties;
import java.util.Set;

/**
 *
 * 需求:
 *  *      有一个文本文件
 *  *              username.txt
 *  *              key1=value1
 *  *              key2=value2
 *  *              ....
 *  *              ...
 *  *              ...
 *  *
 *  *              将文本文件中的内容读取到属性列表中Properties  加载
 *  *
 *              将字节输入流或者字符输入中所在的文件中的内容加载属性列表中
 *              void load(Reader reader)
 *              void load(InputSteram in)
 *  *
 *  *          将Properties里面存储的内容----->保存到某个目录的xxx配置文件中 保存
 *                  public void store(Writer writer,String comments)
 *                  public void store(OutputStream out,String comments)
 *                  将属性列表中的内容保存到指定字节输出流/字符输出流所指向那个文件中
 *
 *
 *                  举例:
 *                          打游戏
 *
 *                              进度的保存/加载
 *
 * 需求
 *      将一个文本文件中属性,加载到属性集合列表(Properties)中,并将这些数据 ---->
 *      保存到另一个文件中
 *
 *
 *     需求
 *     文本文件中
 * 成龙=65
 * 刘德华=70
 * 张三丰=56
 * 吴奇隆=60
 *      将文本文件中的属性加载到属性列表中,然后如果里面有"刘德华",将其年龄改成"60",
 *      将属性列表中内容再次保存到当前文件文本中
 */
public class PropertiesDemo2 {
    public static void main(String[] args) throws IOException {
//        myLoad() ;
         myStore() ;
    }

    //将属性集合类中的内容 ---保存到指定文件中
    private static void myStore() throws IOException {
        Properties prop = new Properties() ;

        //添加key-value
        prop.setProperty("张三丰","56") ;
        prop.setProperty("吴奇隆","60") ;
        prop.setProperty("成龙","65") ;
        prop.setProperty("刘德华","70") ;

        //public void store(Writer writer,String comments):
        //参数2:给当前属性列表中加入一个描述
        //保存指定的文件中
        prop.store(new FileWriter("user.txt"),"name's list'");

        System.out.println("保存完毕");

    }

    //加载:需要将names.txt文件加载到属性列表中,然后遍历
    private static void myLoad() throws IOException {
        //创建一个空的属性列表
        Properties prop = new Properties() ;
        System.out.println(prop);

        //void load(Reader reader)
      //  Reader r = new FileReader("names.txt") ;//names.txt是在当前项目下路径下
        // prop.load(r) ;



        //读取src路径下的names.properties  (类路径)
        //使用步骤
        //1)获取当前类所在的字节码文件对象
        Class clazz = PropertiesDemo2.class ;
        //2)获取当前类所在的类加载器Class:public ClassLoader getClassLoader()
        ClassLoader classLoader = clazz.getClassLoader();
        //3)在类加载器中:获取当前资源文件(配置文件names.proprites)所在的输入流对象
        //public InputStream getResourceAsStream(String name) {
        InputStream inputStream = classLoader.getResourceAsStream("names.properties");
        //将inputStream加载到属性集合类中
//        void load(InputSteram in)
        prop.load(inputStream);

        System.out.println(prop);

        Set<String> keySet = prop.stringPropertyNames();
        for(String key:keySet){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);
        }
    }
}

7.网络编程

网络编程------>Socket编程

特点:有发送端和客户端,或接受端与服务器端,两端都必须存在Socket(套接字)对象

网络编程的三要素:

ip地址:为了找到计算机,因为每个计算机的ip地址都是唯一的

port(端口号):端口号可以确定当前通信的软件是哪个,因为计算机本身无法通信,通信指的是计算机硬件基础上的软件完成的功能。

协议:每种软件的通信协议是不同的,通信的前提是互相通信的双方的协议一致,否则无法对发送和接受的消息进行解读。

常用的通信协议:

UDP(用户数据报协议):

1)不需要建立连接通道

2)属于一种不可靠连接

3)发送文件大小限制的

4)执行效率高 (不同步的),不检查发出消息是否到达,当网络拥挤时,效率极低,容易丢失信息。

TCP/IP协议:

1)建立连接通道

2)属于安全连接(可靠连接)

3)发送文件(使用基本字节流),相对udp协议来说没有限制

4)执行效率低(同步的),应答是,发送失败需要进行重传

InetAddress:互联网ip地址

获取InetAddress:ip地址对象

public static InetAddress getByName(String host):参数:主机名称(计算机电脑名称)

public String getHostAddress():获取ip地址字符串形式

public class NetDemo {
    public static void main(String[] args) throws UnknownHostException {
        //如何获取自己电脑上的ip地址---->String形式!
        //或者cmd窗口输出ipconfig查看
        //10.12.156.107
        InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH");
        String ip = inetAddress.getHostAddress();
        System.out.println(ip);
        //10.12.156.107
    }
}

7.1UDP协议:模拟发送端和接受端进行通信

package com.qf.net_07;

import java.io.IOException;
import java.net.*;

/**
 * Udp编程的发送端:
 *  1)创建Socket对象
 *  2)发送内容  :内容数据一种数据报文(报包)DatagramPacket
 *  3)释放资源
 */
public class UdpSend {
    public static void main(String[] args) throws IOException {
        //1)创建Socket对象
        //DatagramSocket:发送和接收数据报数据包的套接字。
        //public DatagramSocket() throws SocketException
        DatagramSocket ds = new DatagramSocket() ;

        //2)创建一个数据报包对象DatagramPacket
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //参数1:当前数据的字节数组
        //参数2:当前数据的长度
        //参数3:InetAddress:ip地址对象
        //参数4:绑定的端口号
        byte[] bytes = "hello,马三奇".getBytes() ;
        int length = bytes.length ;
               //public static InetAddress getByName(String host)
        InetAddress inetAddress = InetAddress.getByName("10.12.156.107");
        int port = 12306 ;
        DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;

        //3)发送数据报包
        //public void send(DatagramPacket p)
        ds.send(dp);

        //4)释放资源
        ds.close();

    }
}
package com.qf.net_07;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * Udp接收端
 * 1)创建Socket对象
 * 2)创建一个接收容器:数据报包:DatagramPacket
 * 3)接收
 * 4)解析容器的的实际数据大小
 * 5)展示发送端发送的数据
 */
public class UdpReceive {
    public static void main(String[] args) throws IOException {
        //)创建Socket对象
        //public DatagramSocket(int port)
        DatagramSocket ds = new DatagramSocket(12306) ;
        //2)创建一个接收容器:数据报包:DatagramPacket  实际数据没有这么大
        //public DatagramPacket(byte[] buf, int length)
        //自定义字节缓冲区
        byte[] bytes = new byte[1024] ;
        int lentgth = bytes.length ;
        DatagramPacket dp = new DatagramPacket(bytes,lentgth);

        //3)public void receive(DatagramPacket p)
        ds.receive(dp);

        //4)解析实际数据
        //byte[] getData()   获取实际字节数
        //返回数据缓冲区。
        //int getLength()   获取实际长度
        byte[]  bytes2 = dp.getData();
        int length2 = dp.getLength();
        String receiverStr = new String(bytes2,0,length2) ;
        //获取ip地址
        //InetAddress getAddress()
        //InetAddress
                //public String getHostAddress():
        String ip = dp.getAddress().getHostAddress();

        //展示数据
        System.out.println("data from "+ip+" ,content is :"+receiverStr);

    }
}

7.2TCP协议:模拟通信

//发送端
package com.qf.tcp;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * TCP特点:需要建立连接通道(就是以一种字节流的方式写入,读取)
 *          什么时候建立连接(服务器端如果监听到端口,客户端就立即和服务器端建立连接!)
 *
 * TCP客户端写数据
 *      1)创建TCP客户端的Socket对象
 *      2)写数据
 *      3)释放资源
 */
public class ScoketDemo {
    public static void main(String[] args) throws IOException {
        //1)创建TCP客户端的Socket对象
       // public Socket(String host, int port)throws UnknownHostException, IOException
        //参数1:主机名称/或者ip地址字符串形式
        //参数2:指定的端口(0-65535(不包含0-1024))
        Socket s = new Socket("10.12.156.107",8888) ;

        //2)写数据
        //public OutputStream getOutputStream()throws IOException
        OutputStream out = s.getOutputStream();//获取通道内的输出流对象
        out.write("hello,TCP".getBytes());



        //读取服务器端反馈的数据

        //3)释放资源
        s.close();


    }
}

//服务器端
package com.qf.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * TCP服务器端的实现步骤:
 *      1)创建服务器端的Socket对象
      public ServerSocket(int port)throws IOException
        2)监听客户端连接
        public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
        3)获取通道内的输入流
        public InputStream getInputStream() throws IOException
        4)读取数据:一次读取一个字节数组
        5)展示数据 而且获取ip地址
            //public InetAddress getInetAddress()
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {

        // 1)创建服务器端的Socket对象
        //public ServerSocket(int port)throws IOException
        ServerSocket ss = new ServerSocket(8888) ;
        System.out.println("服务器正在等待连接...");

        //2)监听客户端连接
//        public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
        Socket socket = ss.accept(); //阻塞式方法
        //3)取通道内的输入流
        //public InputStream getInputStream() throws IOException
        InputStream inputStream = socket.getInputStream();
        //4)读取数据:一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes);
        //获取内容
        String clientStr = new String(bytes,0,len) ;


        //再去反馈数据

        //5)获取ip
        String ip = socket.getInetAddress().getHostAddress();
        //展示数据
        System.out.println("data from "+ip+" content is--->"+clientStr);

        //关闭

        ss.close();
    }
}

7.3利用TCP完成文件的复制

客户端的一个文本文件,服务器端进行复制到指定的某个文件中 ,复制完毕了,服务器端需要给客户端反馈!(反馈的消息,客户端能不能获取到)。加入反馈操作:出现客户端和服务器端互相等待的情况—>但是文件已经复制完毕!针对服务器端:不知道客户端是否还需要从通道内的输出流对象中写入数据(此时文件读写复制结束条件:只是null)文件读完毕的条件是null,但是TCP通过流的方式 要进行结束; 服务器端不知道客户端是否还需要写入数据,客户端等待着服务器反馈的数据!

解决方案:

1)自定义结束条件

在客户端读完文件中,通知一下服务器端,写入一行内容(“886/over”),服务器端只要读取到886或者over。

2)可以使用客户端Socket的一个方法 标记(通知服务器端,客户端已经没有数据输出了)

public void shutdownOutput() throws IOException

import java.io.*;
import java.net.Socket;
public class ClientTest {
    public static void main(String[] args) throws IOException {

        //将当前项目下的Test.java 客户端将文件写入到 服务器端,服务器端进行复制
        //创建客户端Socket对象
        Socket socket = new Socket("10.12.156.107",10086) ;

        //创建BufferedReader流对象,去操作当前项目下的Test.java文件
        BufferedReader br = new BufferedReader(new FileReader("Test.java")) ;

        //获取通道内字节输出流,写数据----封装通道内的字节流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;

        //一次读取一行,然后给通道内的流对象中写入过去
        String line = null ;
        while((line=br.readLine())!=null){//阻塞式方法
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //方案1 :自定义结束条件:客户端通道内的流中已经没有数据了
        /*bw.write("over");
        bw.newLine();
        bw.flush();*/

        //方案2:使用socket对象的方法
        socket.shutdownOutput(); //禁用套接字流输出,通知服务器端 客户端已经不会在写入数据了

        //客户端读取反馈
        //获取通道内字节输入流对象
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes);//阻塞方法
        String fkMsg = new String(bytes,0,len) ;
        System.out.println("客户端到读取的反馈数据是:"+fkMsg);

        //关闭
        br.close();
        socket.close();
    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
    public static void main(String[] args) throws IOException {

        //创建服务器端Socket
        ServerSocket ss = new ServerSocket(10086) ;

        Socket socket = ss.accept();
        //创建BufferedReader去读取通道内写入过来的数据,封装通道内的流对象
        BufferedReader br  = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
        //创建字符缓冲输出流,写入数据,输出到当前项目下的Copy.java文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;

        //一次读取一行
        String line  = null ;
        while((line=br.readLine())!=null){//阻塞方法  :可能客户端的文件已经null了,但是服务器端不知道!
           /* if("over".equals(line)){
                break ;
            }*/

            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //加入反馈操作
        //继续通道内输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("数据已经收到了,已经复制完毕".getBytes());
        outputStream.flush();

        //释放资源
        bw.close();
        ss.close();
    }
}

客户端的图片文件,服务器端将图片进行复制,并反馈给客户端

import java.io.*;
import java.net.Socket;
public class ClientImgDemo {
    public static void main(String[] args) throws IOException {
        //创建客户端的Socket
         Socket socket = new Socket("10.12.156.107",12306) ;

         //创建BuferedInputStream 读取图片文件  :d://mm.jpg
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d://mm.jpg")) ;
        //写入到通道内流中同时 封装通道内的流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;

        //一次读取一个字节数组
        byte[] buffer  = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(buffer))!=-1){
            //写
            bos.write(buffer,0,len);
            bos.flush(); //强制刷新,将缓冲的字节数全部写出
        }


        //通知服务器端,通道内输出流对象没有数据了
        socket.shutdownOutput();


        //读取反馈
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024] ;
        int length = inputStream.read(bytes);
        System.out.println("反馈内容为:"+new String(bytes,0,length));

        //关闭资源
        bis.close();
        socket.close();

    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务器端将图片进行复制,并反馈给客户端
 * 将图片文件写入到当前项目下的高圆圆.jpg
 */
public class ServlerImgDemo {

    public static void main(String[] args)  throws IOException {
        ServerSocket ss  = new ServerSocket(12306) ;

        //监听
        Socket socket = ss.accept() ;
        //读取:封装通道内的字节输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
        //输出
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("高圆圆.jpg")) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(bytes))!=-1){ //阻塞式方法
            bos.write(bytes,0,len);
            bos.flush();
        }

        //加入反馈
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("图片文件复制完毕".getBytes());
        outputStream.flush();


        //释放资源
        bos.close();
        ss.close();
    }
}

8.利用IO流模拟注册登录功能

//功能接口
package dao;

import entity.User;
public interface UserDao {
    /**
     *
     * @param user  注册用户实例
     * @return  返回注册结果
     */
    boolean isRegister(User user);

    /**
     *
     * @param username  登录用户名
     * @param password  登录密码
     * @return  返回登录结果
     */
    boolean isLogin(String username,String password);
}

//功能接口实现
package dao.impldao;

import dao.UserDao;
import entity.User;

import java.io.*;

public class UserDaoImpl implements UserDao {

    //静态实例变量,表示当前已注册的用户信息
    private static File file = new File("user.txt") ;

    //对当前用户信息提供一个静态代码块
    static {
        if (!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public boolean isRegister(User user) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        //判断是否注册成功
        boolean flag = true;
        try {
            br = new BufferedReader(new FileReader("user.txt"));
            bw = new BufferedWriter(new FileWriter("user.txt",true));
            String currentUser = null;
            String currentRegMeg = user.getUsername().concat(":").concat(user.getPassword());
            while ((currentUser = br.readLine()) != null){
                //获得用户名和密码
                String[] userMsg = currentUser.split(":");
                //如果检测到用户名相同,则注册失败
                if (userMsg[0].equals(user.getUsername())){
                    flag = false;
                    break;
                }
            }

            //判断flag是否发送变化,无变化则不重复,注册
            if (flag){
                bw.write(currentRegMeg);
                bw.newLine();
                bw.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null && bw != null){
                try {
                    br.close();
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return flag;
    }

    @Override
    public boolean isLogin(String username, String password) {
        BufferedReader br = null;
        //判断是否登录成功
        boolean flag = false;
        try {
            br = new BufferedReader(new FileReader("user.txt"));
            String currentUser = null;
            String currentRegMeg = username.concat(password);
            while ((currentUser = br.readLine()) != null){
                if (currentRegMeg.equals(currentUser)){
                    flag = true;
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return flag;
    }
}

//注册登录用户实体
package entity;

public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

//登录注册模拟
package test;

import dao.UserDao;
import dao.impldao.UserDaoImpl;
import entity.User;
import setting_ip_port.SettingIpPort;

import java.util.Random;
import java.util.Scanner;

public class TestRegisterLogin {
    public static void main(String[] args) {
        while(true){
            System.out.println("------------------------------欢迎访问LipenggJava聊天室登录界面---------------------------------");
            System.out.println("1 注册");
            System.out.println("2 登录");
            System.out.println("3 退出");

            //创建键盘录入对象
            Scanner sc = new Scanner(System.in) ;
            System.out.println("请输入您的选择:");
            String choice = sc.nextLine() ;

            //创建当前UserDao子类对象
            UserDao ud = new UserDaoImpl() ;

            switch(choice){
                case "1" ://注册
                    System.out.println("----------------------进入注册界面-----------------------------");
                    System.out.println("请输入用户名:");
                    String username = sc.nextLine() ;
                    System.out.println("请输入密码:");
                    String password = sc.nextLine() ;

                    //封装User对象
                    User user = new User(username,password) ;
                    //需要调用register(User)功能
                    boolean reFlag = ud.isRegister(user);
                    if (reFlag){
                        System.out.println("注册成功...");
                    }else {
                        System.out.println("注册失败...");
                    }
                    break ;
                case "2":  //登录
                    System.out.println("----------------------进入登录界面-----------------------------");
                    System.out.println("请输入用户名:");
                    String inputUsername = sc.nextLine() ;
                    System.out.println("请输入密码:");
                    String inputPassword = sc.nextLine() ;

                    //调用功能
                    boolean flag = ud.isLogin(inputUsername, inputPassword);
                    if(flag){
                        System.out.println("登录成功");
                        System.exit(0);
                          
                    }else{
                        System.out.println("用户名或者密码输入错误");
                    }


                    break;
                case "3"://退出
                default:
                    System.out.println("感谢您访问用户管理系统,欢迎下次访问,再见!");
                    System.exit(0);
            }
        }
    }

    private static void weChatRoom(String username) {

    }
}

9.关于反射

什么是反射?

能够获取当前某个类的字节码文件对象Class,那么就可以获取当前类的构造器并且创建当前类实例,还可以获取当前类的成员变量并去赋值,或者获取当前类的成员方法并去调用!

如何获取一个类的字节码文件对象?

1)Object类的getClass()获取 获取当前某个类的实例(正在运行的类)

2)任意Java类型的class属性

3)Class类的静态功能 public static Class<?> forName(String className)推荐使用,因为在后面可以通过修改配置文件不修改代码的情况下完成功能。

9.1示例

package com.qf.reflects;

public class Person {
    private String name;
    public int age;
    String address;

    public Person(){}

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

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

    private void show(){
        System.out.println("山海自有归期,万事终将如意");
    }

    String study(){
        return "good good study ,and day day up";
    }

    public void method(String name){
        System.out.println(name+"要高薪就业");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
package com.qf.reflects;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.qf.reflects.Person");
        Object o = clazz.newInstance();
        System.out.println(o);

        System.out.println("--------------");
        //获得构造器
        Constructor constructor = clazz.getConstructor();
        Object o1 = constructor.newInstance();
        System.out.println(o1);

        System.out.println("--------------------------------");
        //获得公共的属性
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //获得指定的属性
        Field ageField = clazz.getField("age");
        //获得指定的非公共的属性
        Field nameFiled = clazz.getDeclaredField("name");
        Field addressField = clazz.getDeclaredField("address");
        ageField.set(o1,22);
        //给私有的赋值,需要屏蔽java语言检测机制,并赋值
        nameFiled.setAccessible(true);
        nameFiled.set(o1,"彤彤");

        addressField.setAccessible(true);
        addressField.set(o1,"辽宁");

        System.out.println(o1);

        //获得公共方法
        Method method = clazz.getMethod("method", String.class);
        method.invoke(o1,"刚刚");

        //获得私有和默认方法
        Method show = clazz.getDeclaredMethod("show");
        Method study = clazz.getDeclaredMethod("study");
        show.setAccessible(true);
        show.invoke(o1);
        study.setAccessible(true);
        Object invoke = study.invoke(o1);
        System.out.println(invoke);

    }
}

9.2通过修改配置文件达到目的

需求:现在有一个学生类以及工人类,两类都有一个love方法

1)起初,创建的一个学生对象,调用love方法

2)创建一个工人类对象,通过修改配置文件调用love方法

//配置文件
myClass.properties(文件名)需要放在src目录下
className=com.qf.reflect_06.Worker
methodName=love
//工人类
public class Worker {
    public void love(){
        System.out.println("爱生活,爱drink...,爱足球");
    }
}

//学生类
package com.qf.reflect_06;

public class Student {
    public void love(){
        System.out.println("爱学习,爱Java,爱高圆圆...");
    }
}

//测试类
public class ReflectTest2 {
    public static void main(String[] args) throws Exception {

        //1)现在学生类对象
       /* Student s1 = new Student() ;
        s1.love();*/

        //需求改变:创建工人类对象
        /*Worker worker  = new Worker() ;
        worker.love();*/

        //优化:提供src(类路径下提供配置文件)myClass.properties
        //1)读取src下面的myClass.properties
        //获取资源文件所在的输入流对象
        InputStream inputStream = ReflectTest2.class
                .getClassLoader().
                getResourceAsStream("myClass.properties");

        //创建属性集合列表:空的
        Properties prop = new Properties() ;
        //将指定的配置文件所在的输入流加载属性列表中
        prop.load(inputStream);
        System.out.println(prop);

        //可以通过可以key获取value
        String className = prop.getProperty("className") ; //当前类的全限定名称
        String methodName = prop.getProperty("methodName") ;

        //通过反射创建当前类的字节码文件对象
        Class clazz = Class.forName(className) ;
        //创建当前类的实例 (解耦)
        Object obj = clazz.newInstance() ;
        //通过clazz字节码文件对象获取当前成员方法所在的Method类对象
        Method method = clazz.getMethod(methodName) ;
        method.invoke(obj) ;
    }
}

10.JDK动态代理

核心思想:代理角色和真实角色,代理角色完成真实角色的一些额外功能,同样,真实角色也只需专注于自己的功能的即可。

比如:在登录当中,实际场景中,在使用增删改查功能时,我们需要进行权限检查和日志记录。

而假设在没有使用动态代理之前,需要在接口的实现中完成这三个特定功能,所以将会产生在实现接口功能过程中,除了完成指定功能外还需完成权限检查和日志记录。

此时,通过动态代理,我们的特定功能只需完成自己的功能即可,而其余功能将作为代理的扩展功能书写。

未使用动态代理

//功能接口
package com.qf.jdk_reflect;

/**
 * 针对用户数据库访问接口层
 */
public interface UserDao {
    /**
     * 添加用户
     */
    void add() ;

    /**
     * 修改用户
     */
    void update() ;

    /**
     * 删除用户的功能
     */
    void delete() ;

    /**
     * 查询用户
     */
    void findAll() ;

}

//接口实现
package com.qf.jdk_reflect.impl;

import com.qf.jdk_reflect.UserDao;

/**
 *  针对用户的数据库访问接口实现层
 */
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("添加用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void findAll() {
        System.out.println("查询所有用户");
    }
}

//扩展功能实现
package com.qf.jdk_reflect.impl;

import com.qf.jdk_reflect.UserDao;

/**
 * 在操作业务功能之前,需要进行系统监控!
 */
public class UserDaoImpl2 implements UserDao {
    @Override
    public void add() {
        System.out.println("权限校验");
        System.out.println("添加用户");
        System.out.println("产生日志记录");

    }

    @Override
    public void update() {
        System.out.println("权限校验");
        System.out.println("修改用户");
        System.out.println("产生日志记录");
    }

    @Override
    public void delete() {
        System.out.println("权限校验");
        System.out.println("删除用户");
        System.out.println("产生日志记录");
    }

    @Override
    public void findAll() {
        System.out.println("权限校验");
        System.out.println("查询用户");
        System.out.println("产生日志记录");
    }
}

public class UserTest {
    public static void main(String[] args) {

        //直接:通过接口多态:创建接口对象
        UserDao ud = new UserDaoImpl() ;  //ud产生代理

        ud.add();
        ud.delete();
        ud.update();
        ud.findAll();

        //需求: 不仅实现自己的主要功能:add,update,delete,findAll() ...
        //而且还需要在每一个功能使用之前,先进行权限校验,然后使用功能,需要产生日志记录
        System.out.println("-------------------------------------");

        UserDao ud2 = new UserDaoImpl2() ;
        ud2.add();
        ud2.update();
        ud2.delete();
        ud2.findAll();      
    }
}


使用动态代理

package com.qf.jdk_reflect;
/**
 * 针对用户数据库访问接口层
 */
public interface UserDao {
    /**
     * 添加用户
     */
    void add() ;

    /**
     * 修改用户
     */
    void update() ;

    /**
     * 删除用户的功能
     */
    void delete() ;

    /**
     * 查询用户
     */
    void findAll() ;

}

//动态代理
package com.qf.jdk_reflect.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 自定义一类来实现 代理角色的产生 (InvocationHandler:基于代理的处理接口)
 */
public class MyInvocationHandler  implements InvocationHandler {

        //成员变量位置定义Object类型:对任意Java类型产生代理角色 (当前某个具体的实例)
    private Object target ;
    public MyInvocationHandler(Object target){
        this.target = target ;
    }
    /**
     *
     * @param proxy  代理实例
     * @param method  调用基于接口的代理方法
     * @param args   当前传递的实际参数
     * @return   返回代理角色
     * @throws Throwable  代理过程中可能产生代理异常
     *
     *
     */

    //method底层调用方法 invoke(当前类的实例,实际参数 args)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("权限校验");//此处实现权限校验
        
        Object returnObj = method.invoke(target, args);

        System.out.println("日志记录");//此处实现日志记录

        return returnObj; //代理角色对象
    }
}
package com.qf.jdk_reflect.test;

import com.qf.jdk_reflect.UserDao;
import com.qf.jdk_reflect.impl.MyInvocationHandler;
import com.qf.jdk_reflect.impl.UserDaoImpl;
import com.qf.jdk_reflect.impl.UserDaoImpl2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * 测试类
 *
 *
 * 动态代理
 *      jdk动态代理
 *      cglib动态代理-----导入cglib第三方jar包
 *
 *  动态代理核心思想:
 *                  再程序执行过程中通过一些特殊的方式产生代理
 *
 *
 *                   代理角色和真实角色
 *                   代理角色完成真实角色的一些额外功能,
 *                   真实角色专注于完成自己的事情!
 *
 *
 *                jdk动态代理:
 *                      前提必须有一个接口    比如 :UserDao
 *
 *                jdk提供的
 *
 *
 *                      java.lang.reflect.Proxy:提供了创建动态代理类和实例的静态方法
 *
 *                      public static Object newProxyInstance(ClassLoader loader,
 *                                     Class<?>[] interfaces,
 *                                       InvocationHandler h) throws IllegalArgumentException
 *
 *
 *                        参数1:当前针对该接口的类加载器
 *                        参数2:代理类实现的接口列表的字节码文件对象
 *                        参数3:基于代理的处理程序  接口
 *
 *   目前接口:
 *          UserDao接口 -------> 产生代理角色完成 "权限校验"  "日志记录"
 *
 *
 *
 *
 *
 */
public class UserTest {
    public static void main(String[] args) {

        //直接:通过接口多态:创建接口对象
        UserDao ud = new UserDaoImpl() ;  //ud产生代理
        //通过jdk动态代理反射来完成代理角色的产生
        //Class --->public Class<?>[] getInterfaces()
        //创建基于代理处理接口的子实现类对象
      MyInvocationHandler invocationHandler = new MyInvocationHandler(ud) ;
      UserDao proxyClass = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
                new Class[]{UserDao.class}, invocationHandler);

        proxyClass.add();
        proxyClass.delete();
        proxyClass.update();
        proxyClass.findAll();

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值