IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 单例模式Filed类递归IO流序列化与反序列化TCP与UDP反射 -> 正文阅读

[网络协议]单例模式Filed类递归IO流序列化与反序列化TCP与UDP反射

1.单例模式

单例模式:始终在内存中创建一个实例;
分为两种:
	饿汉式:永远不会出现问题的单例模式;
	懒汉式:可能出现问题的一种单例模式;
饿汉式:
	1)构造方法私有:保证外界不能直接创建当前类对象;
	2)在当前类的成员位置创建当前类实例;
	3)提供一个静态的功能,返回值就是当前类本身(需要当前类的实例)
懒汉式:
	1)构造方法私有化;
	2)在当前成员变量的位置声明变量:数据类型就是当前类(私有的,静态的);
	3)提供静态功能,返回值是当前类本身
		判断如果当前没有给当前类型变量为null,直接new当前类的实例;如果不为null,就返回当前类的变量
			可能出现延时加载或懒加载---出现安全问题
Runtime:当前获取的当前本地运行环境的实例;
public int availableProcessors():获取本地计算机的处理器数量
public Process exec(String command):开启某个软件的进程(参数为字符串命令)
public class RuntimeDemo {
    public static void main(String[] args) throws IOException {

        //获取当前类的实例:Runtime  利用单例模式
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime.availableProcessors());
        Process notepad = runtime.exec("notepad");
        System.out.println(runtime.exec("QQ"));

        //利用关机
        //exec("shutdown -s"):立即关机
        //exec("shutdown -s -t 300"):立即关机
        //exec("shutdown -a"):取消关机

        System.out.println(runtime.exec("shutdown -s -t 300"));
        System.out.println(runtime.exec("shutdown -a"));

    }
}

2.File类

File文件和目录(文件夹)路径名的抽象表示
构造方法:
	File(String pathname) : 参数就是指定的路径/如果没有指定路径(默认是在当前项目下)
	通过将给定的路径名字符串转换为抽象路径来创建新的FIle实例
	File(File parent , String child):从父抽象路径名和子路径名字符串创建新的File实例;
	File(String parent, String child): 参数1:父目录地址  参数2; 具体的子文件地址;
成员方法:
	public boolean createNewFile()  throws IOExcepption:表示创建文件:如果不存在则创建;
	public boolean mkdir():创建文件夹,如果不存在,则创建;否则就返回false;
	public boolean mkdirs():创建多个文件,如果父目录不存在,则创建;
	public boolean delete():删除文件或者文件夹(如果删除文件夹,文件夹必须为空目录);
	public boolean renameTo(File dest):重命名,参数传递的修改的File对象;
判断方法:
	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():获取某个抽象路径名所表示的文件以及目录的字符串数组;

获取D盘下的所有文件夹及文件的名称

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);
            }
        }
    }
}
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") ;
        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());
        File srcFile = new File("D:\\EE_2106\\day25\\code\\mv.jpg") ;
        File destFile = new File("高圆圆.jpg") ;//当前项目路径下了
        System.out.println(srcFile.renameTo(destFile)) ;



    }
}

获取D盘下所有的以.jpg结尾的文件

 /*      提供了另一个重载功能:
 *              public File[] listFiles(FilenameFilter filter)
 *              String[] list(FilenameFilter filter)
 *          参数为:文件名称过滤器FilenameFilter:接口
 *                  成员方法:
 *                  boolean accept(File dir,String name):测试指定文件是否包含在文件列表中
 *                          返回如果true,将文件添加到文件列表中
 *                     1)描述下D盘
 *                   2)  public  File[]listFiles(FilenameFilterfilenamefilter):
 *                              获取D盘下的File数组的时候,就已经指定文件进行过滤...
 */
public class FileTest {
    public static void main(String[] args) {
        //1)描述D盘
        File file = new File("D://") ;

        //2)获取盘符下的所有的文件以及文件夹的File数组
        File[] fileArray = file.listFiles();//这一步并没有直接获取到要的.jpg文件
        //后面一些判断
        if(fileArray!=null){
            for(File f:fileArray){

                //判断f是文件
                if(f.isFile()){
                    //是文件
                    //判断它的名称是否以.jpg结尾
                    if(f.getName().endsWith(".jpg")){
                        System.out.println(f.getName());
                    }
                }
            }
        }

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

        //描述下D盘
        File srcFile = new File("D://") ;

        //获取当前D盘下的所有文件以及文件夹File数组,并进行文件名过滤
        //public File[] listFiles(FilenameFilter filter)
        File[] files = srcFile.listFiles(new FilenameFilter() {//接口的匿名内部类
            @Override
            public boolean accept(File dir, String name) {
                //返回true:表示将指定文件添加列表文件中
                //描述文件所表示抽象路径File
                File file = new File(dir, name);

                //两个条件:file是文件并且file的文件名称以".jpg结尾"
                return file.isFile() && file.getName().endsWith(".jpg");
            }
        });
        if(files!=null){
            for(File f :files){
                System.out.println(f.getName());
            }
        }
    }
}

3.递归

方法递归:方法调用方法本身的一种现象,并非是方法嵌套方法;
前提条件:
	1)必须有一个成员方法;
	2)必须有方法递归的出口条件(结束条件),如果没有出口条件,就是死递归;
	3)还存在一定规律
注意事项:构造方法不存在递归
//使用递归方式删除D盘某个demo文件夹中有很多个目录,每个目录中还可能有目录,删除所有的以.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());
        }
    }
}

4.IO流

IO流的分类

按流的方向:
	输入流和输出流;
按类型分:
	字节流和字符流:字节流先出现,后面才有字符流;
	再按流的方向划分:
		字节输入流:InputStream:表示输入字节流的所有类的超类;
		字节输出流:OutputStream:表示字节输出流的所有类的超类
		字符输入流:Reader:表示输入字符流的抽象类;
		字符输出流:Writer:表示输出字符流的抽象类;
字节输出流:OutputStream抽象类子类进行实例化FileOutputStream:将指定的内容写到文件中
实现步骤:
	1)创建文件输出流对象:FileOutputStream(String name):可以指定参数地址,FileOutputStream(File file);
	2)写数据:
		public void write(int b) throws IOException 写一个字节;
		public void write(byte[] bytes) throws IOException 写一个字节数组;
		public void write(byte[] bytes,int off,int len) throws IOException:写一部分字节数组;
	3)关闭资源;
public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {

        //创建文件输出流对象
        //指向某个盘符下的文件中(或者当前项目下)
        FileOutputStream fos = new FileOutputStream("my.txt") ;//文件需要被创建当前项目下

        //2)写数据
       /* fos.write(97);
        fos.write(98);
        fos.write(99);*/

       //写一个字节数组
      /*  byte[] bytes = {97,98,99,100,101,102} ;
       // fos.write(bytes);
        fos.write(bytes,2,2);*/
      for(int x = 0 ; x < 10 ; x ++){
          fos.write(("hello"+x).getBytes());

          //windows操作系统  "\r\n"代表换换行
          fos.write("\r\n".getBytes());
      }
        //3)关闭资源
        fos.close(); //释放fos流对象所指向的my.txt的系统资源
    }
}

在字节输出流中加入异常处理(捕获异常)

public class FileOutputStreamDemo2 {

    public static void main(String[] args) {

       // method1() ;
        method2() ;//
    }

    //标准方式:try...catch...finally
    //统一处理
    private static void method2() {
        FileOutputStream fos = null ;
        //alt+ctrl+t--->
        try {
           fos = new FileOutputStream("fos2.txt") ;
            //写数据
            fos.write("hello,我来了".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源

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

字节输入流:InputStream
子类:FileInputStream
需要读取当前项目下的fis.txt文件
实现步骤:
	1)创建文件字节输入流对象,指向fish.txt
	2)读内容
	3)释放资源
public class FileInputStreamDemo {
    public static void main(String[] args) {
        //创建一个字节输入流对象
        FileInputStream fis = null ;
        try {
          // fis  = new FileInputStream("fis.txt") ;
           fis  = new FileInputStream("DiGuiTest.java") ; //读取当前项目下的DiGuiTest.java
           int by = 0 ; //字节数为0
           while((by=fis.read())!=-1) {
               System.out.print((char)by);
           }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
//需求:将fish.txt读取出来,并展示在控制台上
public class FileInputStreamDemo2 {
    public static void main(String[] args) {
        //创建字节输入流对象
        FileInputStream fis = null ;
        try {
            fis = new FileInputStream("fis.txt") ;
          	byte[] buffer = new byte[1024] ;   //长度虽然1024个长度
          	int len = 0 ;
          	while((len=fis.read(buffer))!=-1){
              //每次获取的从0开始获取实际字节长度
              	System.out.println(new String(buffer,0,len)) ;
          }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

读写复制操作

一次读取一个字节的方式
一次读取一个字节数组
需求:将当前项目下的DiGuiTest.java 的内容 复制到D盘下的Copy.java文件中
public class CopyFileDemo {
    public static void main(String[] args) {

        long start = System.currentTimeMillis() ;//时间毫秒值

       // method("DiGuiTest.java","D://Copy.java") ;
        method2("DiGuiTest.java","D://Copy.java") ;

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

    private static void method2(String srcFile, String destFile) {
        FileInputStream fis  = null ;
        FileOutputStream fos = null ;
        try {
            //  封装源文件:FileInputStraem(String pathname)
            //字节输入流
            fis = new FileInputStream(srcFile) ;
            //字节输出流
            // 封装目的地文件:FileOutputStream(String pathname)
            fos = new FileOutputStream(destFile) ;

            //读写复制操作
            //一次读取一个字节数组
            byte[] bytes = new byte[1024] ;
            int len = 0 ;
            while((len=fis.read(bytes))!=-1){
                //赋值
                //fos流对象中写
                //带上len的使用
                fos.write(bytes,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 一次读取一个字节:读写复制
     * @param srcFile  源文件
     * @param destFile 目的地文件
     */
    private static void method(String srcFile, String destFile) {
        FileInputStream fis  = null ;
        FileOutputStream fos = null ;
        try {
            //  封装源文件:FileInputStraem(String pathname)
            //字节输入流
            fis = new FileInputStream(srcFile) ;
            //字节输出流
            // 封装目的地文件:FileOutputStream(String pathname)
            fos = new FileOutputStream(destFile) ;

            //读写复制操作
            //一次读取一个字节
            int by = 0 ;
            while((by=fis.read())!=-1){
                //没有读完,继续复制 :写一个字节
                fos.write(by);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

缓冲流BufferedInputStream

缓冲流的特点:提供缓冲区大小:1024的8倍,通过底层流提高读速度(FileInputStream);
BufferedOutputStream(OutputStream out):默认缓冲区大小,足够大;
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")) ;
        //写数据
        //写一个字节数组
        bos.write("hello.bufferedOutputStream".getBytes());

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

    }
}
public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建一个字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bos.txt")) ;
        //读内容
        //一次读取一个字节
      /*  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));
        }

    }
}

读取一个MP4文件,并将文件内容复制到当前项目下的copy.mp4中

1)缓冲流一次读取一个字节数组

public class CopyMp4 {
    public static void main(String[] args) {
        //起始时间
        long start = System.currentTimeMillis() ;
        copyMp4("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();
            }
        }
    }
}

2)缓冲流一次读取一个字节

public class CopyMp4 {
    public static void main(String[] args) {
        //起始时间
        long start = System.currentTimeMillis() ;
        copyMp4_2("D://a.mp4","copy.mp4") ;
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时"+(end-start)+"毫秒");
    }
        private static void copyMp4_2(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();
            }
        }
    }
}

3)基本的字节流一次读取一个字节数组

public class CopyMp4 {
    public static void main(String[] args) {
        //起始时间
        long start = System.currentTimeMillis() ;
        copyMp4_3("D://a.mp4","copy.mp4") ;
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时"+(end-start)+"毫秒");
    }
        public static void copyMp4_3(String srcFile,String destFile){
        //封装源文件和目的地文件
        FileInputStream fis = null ;
        FileOutputStream fos = null ;
        try {
            fis = new FileInputStream(srcFile) ;
            fos = new FileOutputStream(destFile) ;
            //读写复制
            byte[] bytes = new byte[1024] ;
            int len = 0 ;//实际字节数
            while((len=fis.read(bytes))!=-1){
                fos.write(bytes,0,len);
                //强制输出流将缓冲的这字节数写出来
                fos.flush();
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4)基本的字节流一次读取一个字节

public class CopyMp4 {
    public static void main(String[] args) {
        //起始时间
        long start = System.currentTimeMillis() ;
        copyMp4_4("D://a.mp4","copy.mp4") ;
        long end = System.currentTimeMillis() ;
        System.out.println("共耗时"+(end-start)+"毫秒");
    }
    public static void copyMp4(String srcFile, String destFile) {
        //封装源文件和目的地文件
        FileInputStream fis = null ;
        FileOutputStream fos = null ;
        try {
             fis = new FileInputStream(srcFile) ;
             fos = new FileOutputStream(destFile) ;

             //读写复制
            int by = 0 ;
            while((by=fis.read())!=-1){
                fos.write(by);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

阻塞IO针对文本文件的读写复制

基本字节流一次读取一个字节
基本字节流一次读取一个字节数组
高效字节流(字节缓冲流BufferedInputStream)一次读取一个字节/一次读取一个字节数组
基本字符流:Reader-->转换输入流InputStreamReader(InputStream in):一次读取一个字符/一次读取一个字符数组
高效字符流(字符缓冲流BufferedReader):一次读取一个字符/一次读取一个字符数组
特有功能:一次读取一行内容 readLine()

字符缓冲输入流

BufferedReadr ----- 键盘录入数据
BufferedReader(Reader in)
	Reader
		InputStreamReader(InputStream in):转换流
		如果直接进行读的操作(直接操作文件)FileReader(String pathName)
创建使用默认大小的输入缓冲区的缓冲字符输入流
	BufferedReader(Reader in , int size):指定缓冲区的大小;
特有功能:
	public String readLine():一次读取一行内容;
public class BufferedReadDemo{
    public static void main(String[] args) throws IOException{
        //创建字符缓冲输入流对象  :使用键盘录入数据!(流的方式)
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;
        System.out.println("请您输入一个字符串数据:");
        String line = br.readLine(); //阻塞式方法
        System.out.println("您输入的数据是:"+line);
    }
}

使用BufferedReader读取bw.txt文件的内容

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br2 = new BufferedReader(new FileReader("bw.txt")) ;
         String line2 = null ;
        while((line2=br2.readLine())!=null){
            System.out.println(line2);
        }
    }
}

字符缓冲输出流

BufferedWriter(Writer out):创建默认的缓冲区的大小:默认大小:defaultcharbuffersize:8192
BufferedWriter(Writer out, int sz)  :指定的大小
public void newLine() throws IOException:写入行的分隔符号
特有功能:
	利用BufferedReader的readLine()读取一行
	利用BufferedWriter写一行,然后换行
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {

        //输出bw.txt文件,并给里面写内容,而且去实现换行效果   write("\r\n")----现在可以使用newLine
        //创建字符缓冲输出流对象
        //BufferedWriter(Writer out) :
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

        //写入数据
        //写字符串/写字符
        bw.write("hello");
        //public void newLine()
        bw.newLine();
        bw.write("world");
        bw.newLine();
        bw.write("javaEE");
        bw.newLine();

        //刷新流
        bw.flush();

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

读写复制操作

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();
            }
        }
    }
}

字符流

字符流的出现是在字节流的后面,可以解决中文乱码问题
Writer:抽象类
	提供子类:字符转换输出流:字节输出流通向字符输出流的桥梁;
构造方法:
	OutputStreamWritter(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")) ;
        //写入数据
        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
    }
}

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

    public static void main(String[] args) throws Exception {
        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();
    }
}

Reader/Writer子类:转换流

InputStreamReader(InputStream in) OutputStreamWriter(OutputStream out)
他们不能直接去操作文件,jdk提供了这两种类型的便捷类,可以直接操作文件
    FileReader(File file)
    FileReader(String pathname)
    FileWriter(File file)
    FileWriter(String filename)

当前项目下的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();
    }
}

字符流的逻辑串联

可以将两个或者两个以上的文件进行读的操作,只能操作源文件;
构造方法:
	public SequenceInputStream(InputStream s1 ,InputStream s2)
	参数s1和s2分别是要读取的字节输入流对象
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) ; 

        //封装目的地文件:
        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();
    }
}

public SequenceInputStream(Enumeration<? extends InputStream> e) :将两个以上的文件进行读取;
Vector<InputStream>
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();


    }
}

属性列表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<String> stringPropertyNames():获取属性列表中的所有的键;
	public String getProperty(String key):在属性列表中通过键获取值
public class PropertiesDemo {
    public static void main(String[] args) {
        //Properties() :空参构造
        Properties properties = new Properties() ;
        /**
         * 推荐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);

        }

    }
}

5.序列化与反序列化

序列化

将某个实体对象写入到流对象中----->讲一个对象变成流数据
构造方法:
	protected ObjectOutputStream();
	protected ObjectOutputStream(OutputStream out) :将某个对象通过底层的基本字节输出进行写的动作;
	public final void writeObject(Object obj) throws IOException

反序列化

将流数据还原成对象;
构造方法:
	public ObjectInputStream(InputStream in);
	public final Object readObject() throws IOException,  ClassNotFoundException;

异常

NotSerializableException:未实现序列化接口的异常;
一个类在进行序列化的时候,(将类的对象写入序列化流中),标记接口Serializable 它有个特点:为当前这个Person进行编码(为类以及类的成员----->序列化化版本ID(签名):SerialversonUID);
InvalidClassException:序列化版本id号无效

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
        //当从对象流读取的控制信息违反内部一致性检查时抛出。


    }

    //序列化
    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();
    }
}
public class Person implements Serializable {

    //产生一个固定的序列化版本Id
    private static final long serialVersionUID = 8988325735017562383L; //常量值
    String name ; //姓名
    private transient int age  ; //年龄  //transient :想让某个字段不参与序列化:这个字段(成员变量就使用transient)
    public Person(){}

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

        this.age = age;
    }

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

6.网络编程

网络编程----->Socket编程
特点:
	客户端与服务端必须存在Socket对象
网络编程的三要素:
	1)规定的协议 UDP/TCP;
	2)互联网ip地址;
	3)port 端口号;
InetAddress:互联网ip地址:
获取ip地址的方法:
	public static InetAddress getByName(String host):参数:主机名称(计算机电脑名称);
	public String getHostAddress():获取ip地址字符串形式

UDP与TCP协议的区别

UDP协议:
	1)不需要建立连接通道;
	2)属于一种不可靠连接;
	3)发送文件大小有限制;
	4)执行效率高(不同步的);
TCP协议:
	1)建立连接通道;
	2)属于可靠连接(安全连接);
	3)相对UDP来说,发送文件大小无限制;
	4)执行效率底(同步的)
public class NetDemo {
    public static void main(String[] args) throws UnknownHostException {
        //如何获取自己电脑上的ip地址----》String形式!
        InetAddress inetAddress = InetAddress.getByName("计算机主机名称");
        String ip = inetAddress.getHostAddress();
        System.out.println(ip);
    }
}

UDP发送端和接收端的实现

UDP发送端

//1)创建Socket对象
	DatagramSocket ds  = new DatagramSocket() ;
//2)创建数据报包对象
	byte[] bytes = "hello,upd".getBytes() ;
	int length = bytes.length ;
	InetAddress inetAddress = InetAddress.getByName("主机ip地址") ;
	int port = 8888 ;//端口号
	DatagramPacket dp  = new DatagramPacket(bytes,length,inetAddress,port) ;
//3)发送数据报包
	ds.send(dp) ;
//4)释放资源
	ds.close() ;

Upd接收端

//1)创建接收端的Socket对象
	DatagramSocket ds = new DatagramSocket(8888) ;
//2)创建一个接收容器:数据报包 
	byte[] bytes = new byte[1024] ; //创建一个字节数组:缓冲区(实际数据发送到缓冲区中)
	int length = bytes.length ;
	DatagramPacket dp = new DatagramPacket(bytes,length) ;
//3)接收数据
	ds.receive(dp) ;
//4)解析接收容器中的实际数据
	byte[] bytes2 = dp.getData() ;
	int length2 = dp.getLength() ;
	String str = new String(bytes2,0,length2) ;
//获取ip地址
	InetAddress inetAddress = dp.getInetAddress() ;
	String ip = inetAddress.getHostAddress() ;
//展示数据
	System.out.println("data from "+ip+"data is ---->"+str) ; 

TCP

TCP的特点:需要建立连接通道(以一种字节流的方式写入.读取);
什么时候建立连接?
	服务器端如果监听到端口,客户端就立即和服务器端建立连接
TCP客户端写数据:
	1)创建TCP客户端的Socket对象;
	2)写数据;
	3)释放资源;

TCP客户端

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();
    }
}

TCP服务端的实现步骤

1)创建服务器端的Socket对象
2)监听客户端连接
3)获取通道内的输入流
4)读取数据:一次读取一个字节数组
5)展示数据 而且获取ip地址
public class ServerDemo {
    public static void main(String[] args) throws IOException {

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

        //2)监听客户端连接
        Socket socket = ss.accept(); //阻塞式方法
        //3)取通道内的输入流
        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();
    }
}

TCP客户端不断键盘录入数据,

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

        //创建客户端的Socket
        Socket socket = null ;
        try {
           socket  = new Socket("10.12.156.107",10010) ;
           //创建BufferedReader
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

            //录入一行
            //读取一行
            String line = null ;
            while((line=br.readLine())!=null){//null只是作为 一个文件是否读完

                if("over".equals(line)){
                    break ;
                }


                //获取通道内的输出流(字节流OutputStream)
                //封装通道内字节输出流
                //分步走
                //OutputStream outputStream = socket.getOutputStream();  
                //创建字符缓冲输出流
               // BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;

                //一步走
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
                 //给封装的通道内的流对象bw写一行
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            //释放资源
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
        }
        //需要获取服务器端的反馈数据 (已经读完完毕了)
    }
}

服务端不断读取数据并显示

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

        //创建服务端的Socket,一直开启
        ServerSocket ss = null ;
        try {
           ss = new ServerSocket(10010) ;
            //ArrayList<Socket>:ArrayList存储一个列表:都是多个客户端 对象

           while(true){
               System.out.println("服务器正在等待连接");
               Socket socket = ss.accept();
               //不断监听,不断展示数据

               //获取通道内的输入流(InputStream)
               //封装通道内的输入流对象
               BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
               //一次读取一行
               String line = null ;
               while((line=br.readLine())!=null){ //阻塞式方法
                   // //展示数据
                   System.out.println(line);
               }
               //复制完毕之后,给客户端反馈

           }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //服务器端不关闭
    }
}

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

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();

    }
}

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();
    }
}

7.反射reflect

什么是反射?

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

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

1)通过Object类的getClass()获取,获取当前某个正在运行的类的实例;
2)任意java类型的class属性;
3)Class类的静态功能 --class.forName();

方法

获取构造器对象:
	public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象;
	public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组  包含私有化构造/默认的;
获取公共的成员变量Filed:
	public Field[] getDeclaredFields():所有的字段所在的Field对象;这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。;
	Field[] fields = clazz.getFields() ;
	Field[] fields = clazz.getDeclaredFields() ;
	//遍历
	for (Field field : fields) {
		System.out.println(field);
	获取指定的单个的字段所在的Field对象;
	public Field getDeclaredField(String name):参数为当前字段名称;
	给绑定在当前对象上的字段进行赋值:
	public void set(Object obj,Object value);参数1:创建当前类的实例,参数2:value赋值的实际参数;
取消java语言访问检查:
	字段名称+Filed.setAccessible(true);
获取成员方法:
	当前字节码文件对象.getMethods();
	当前字节码文件对象.getDeclaredMethods():通过此表示类对象,包括公共,默认访问,私有,保护方法,但不包括继承的方法;

使用反射创建对象

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //使用反射的方式创建当前类实例
        //1)获取Person类的字节码文件对象
        Class personClass = Class.forName("com.qf.reflect_03.Person") ;
        //2)获取构造器对象
        Constructor constructor = personClass.getConstructor();
        //通过构造器对象创建当前类的实例
        Object obj = constructor.newInstance();
        System.out.println(obj); //p
    }
}

通过反射获取当前类实例,并且去获取成员变量所在对象Field,然后去给成员变量赋值

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //1)获取Person类的字节码文件
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;
        //方式1:直接获取当前类的实例
        Object obj = clazz.newInstance();
        System.out.println(obj);

        //方式2:通过获取构造函数对象,创建当前类实例
      /*  Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj);*/

        //2)Class类

        Field nameField = clazz.getDeclaredField("name"); //默认:null

        //Field提供方法
        //public void set(Object obj,Object value):给绑定在当前对象上的字段进行赋值
        //参数1:创建当前类的实例
        //参数2:value 赋值的实际参数
        //取消Java语言访问检查
        nameField.setAccessible(true);
        //赋值
        nameField.set(obj,"高圆圆");
        System.out.println(obj);

        //public int age ;
        //获取当前age所在Field对象
        Field ageField = clazz.getField("age");
        //直接赋值
        ageField.set(obj,20);
        System.out.println(obj);

        //获取Field对象
        Field addressField = clazz.getDeclaredField("address");
        //取消Java语言访问检查
        addressField.setAccessible(true);
        //赋值
        addressField.set(obj,"西安市");
        System.out.println(obj);
    }
}

通过反射获取成员方法所在的Method,并能去调用

public class ReflectDemo {
    public static void main(String[] args)  throws  Exception{
        //直接反射方式获取
        //1)获取当前Person字节码文件对象
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;
        Object obj =clazz.newInstance() ;//当前类实例

        //2)获取所有的成员方法:公共的(包含他父类的所有的公共的方法)

        Method showMethod = clazz.getMethod("show");

        showMethod.invoke(obj) ;

        //调用Person类中function
        //获取function方法的Method对象
        Method functonMethod = clazz.getDeclaredMethod("functioin", String.class);
        //取消Java语言访问检查:私有方法
        functonMethod.setAccessible(true);
        Object returnObj = functonMethod.invoke(obj, "hello");
        System.out.println(returnObj);
    }
}

反射的应用:

给ArrayList集合中添加String类型的数据

public class ReflectTest {
    public static void main(String[] rgs) throws Exception {
        //有一个ArrayList集合对象
        ArrayList<Integer> arrayList  = new ArrayList<>() ;
        arrayList.add(100) ;
        arrayList.add(50) ;
        arrayList.add(200) ;

        //1)获取当前集合实例所在的字节码文件对象
        //通过Object的getClass()获取当前实例所在的类的字节码对象
        Class clazz = arrayList.getClass();

        //获取当前类中 add方法所在的Method
        Method addMethod = clazz.getMethod("add", Object.class);
        //调用方法
        addMethod.invoke(arrayList, "hello");
        addMethod.invoke(arrayList, "world");
        addMethod.invoke(arrayList, "javaEE");

        System.out.println(arrayList);
    }
}

通过配置文件调用不同类的方法

public class ReflectTest2 {
    public static void main(String[] args) throws Exception {
        //1)读取src下面的myClass.properties
        //获取资源文件所在的输入流对象
        InputStream inputStream = ReflectTest2.class.getClassLoader().getResourceAsStream("myClass.properties");

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

        //可以通过可以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) ;
    }
}

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-16 12:04:52  更:2021-08-16 12:07:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 16:17:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码