IO流

IO流

File

File:可以使用该类的对象描述一个文件夹的路径,将来调用File类中的方法,可以 操作 改路径的文件夹或者文件。

路径:

- 绝对路径【完整路径】:带有盘符的路径
- 相对路径:相对一个项目来说
分隔符:
    a.路径名称分隔符:
      windows: \
      linux: /    
    b.路径分隔符:一个路径和其他路径之间的分隔符
      ;
构造方法:

public File(String pathname)

public File(String parent,Stirng child )

public File(File parent,String child)
1.概述:文件和目录(文件夹)路径名的抽象表示   
2.简单理解:
  我们在创建File对象的时候,需要传递一个路径,这个路径定为到哪个文件或者文件夹上,我们的File就代表哪个对象
  File file = new File("E:\Idea\io\1.jpg")    
public class FileDemo1 {
    public static void main(String[] args) {

        //完整路径
//        File file = new File("F:\\projects\\IDEAProjects\\bigdata33-java\\bigdata33-java\\src\\shujia\\day12\\ketang\\b.txt");
//        //相对路径
//        File file1 = new File("src/shujia/day12/a.txt");

        //public File(String parent,String child)
//        File file1 = new File("src/shujia/day12","a.txt");

        //public File(File parent,String child)
        File file = new File(getPathname());
        File file1 = new File(file, "a.txt");


    }
    private static String getPathname() {
        return "src/shujia/day12";
    }
}

创建、删除、重命名功能

创建功能

public boolean createNewFile()
public boolean mkdir()
public boolean mkdirs()

File(String parent, String child) 根据所填写的路径创建File对象
     parent:父路径
     child:子路径
File(File parent, String child)  根据所填写的路径创建File对象
     parent:父路径,是一个File对象
     child:子路径
File(String pathname)  根据所填写的路径创建File对象
     pathname:直接指定路径   

删除功能
public boolean delete()

重命名功能
public boolean renameTo(File dest)

public class FileDemo2 {
    public static void main(String[] args) throws Exception{
        File file1 = new File("src/shujia/day12/ketang/a.txt");

        // public boolean createNewFile() 创建一个新的文件
//        System.out.println(file1.createNewFile());

        // public boolean mkdir() 创建单极文件夹
//        System.out.println(file1.mkdir());
        File file2 = new File("src/shujia/day12/ketang/aaa/bbb/ccc");
//        System.out.println(file2.mkdir());


        //public boolean mkdirs() 创建多极文件夹
//        System.out.println(file2.mkdirs());

        //public boolean delete() 删除一个文件夹或者文件
//        file1.delete();
//        file2.delete();

        File file3 = new File("src/shujia/day12/ketang/aaa");
//        file3.delete(); // 无法直接删除一个非空文件夹


        // public boolean renameTo(File dest) 重命名一个文件夹或者文件
        File file4 = new File("src/shujia/day12/ketang/张成阳.txt"); // 需要先将目标封装成File对象
        file1.renameTo(file4);

    }
}

判断功能

​ public boolean isDirectory()
​ public boolean isFile()
​ public boolean exists()
​ public boolean canRead()
​ public boolean canWrite()
​ public boolean isHidden()

public class FileDemo3 {
    public static void main(String[] args) {
        File file = new File("src/shujia/day12/ketang/张成阳.txt");

        //public boolean isDirectory() 判断目标是否是一个文件夹
        System.out.println(file.isDirectory());

        // public boolean isFile() 判断目标是否是一个文件
        System.out.println(file.isFile());

        // public boolean exists() 判断目标是否存在
        System.out.println(file.exists());

        // public boolean canRead() 判断目标是否可读
        System.out.println(file.canRead());
        System.out.println(file.canWrite());
        // public boolean isHidden() 判断目标是否被隐藏
        System.out.println(file.isHidden());

    }
}

获取功能

基本获取功能
public String getAbsolutePath()
public String getPath()
public String getName()
public long length()
public long lastModified()
高级获取功能
public String[] list() 列出当前目录下所有文件以及文件夹的名字组成的字符串数组
public File[] listFiles() 列出当前目录下所有文件以及文件夹的对应的File对象组成的File对象数组

public class FileDemo4 {
    public static void main(String[] args) {
        File file = new File("src/shujia/day12/ketang/张成阳.txt");

        // public String getAbsolutePath() 获取完整路径
//        System.out.println(file.getAbsolutePath());

        System.out.println(file.getAbsoluteFile());
        // public String getPath() 获取相对路径
//        System.out.println(file.getPath()); // src\shujia\day12\ketang\张成阳.txt
        System.out.println(file.getPath());
        // public String getName() 获取目标的名字
        System.out.println(file.getName());

        // public long length() 获取的目标中的字节数
        System.out.println(file.length());

        // public long lastModified() 获取目标最后一次修改的时间戳
        System.out.println(DateUtil.getTime(file.lastModified()));
        System.out.println(file.lastModified());
        System.out.println("-----------------------------------");
        File file2 = new File("src/shujia/day12/ketang");

        String[] nameList = file2.list();
        System.out.println(nameList);
        System.out.println(Arrays.toString(nameList));
        
        File[] fileArray = file2.listFiles();
        System.out.println(Arrays.toString(fileArray));
    }
}

需求

/*
    判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称
    先获取所有的File对象,再从其中过滤出符合条件的。
 */
public class FileTest1 {
    public static void main(String[] args) {

        File file = new File("E:\\");

        File[] files = file.listFiles();

        if(files!=null){
            for (File file1 : files) {
                if(file1.isFile() && file1.getName().endsWith(".jpg")){
                    System.out.println(file1.getName());
                }
            }
        }
    }
}
public class FileTest2 {
    public static void main(String[] args) {
        File file = new File("E:\\");

        File[] files = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                // 这里返回是true还是false需要自己编写代码逻辑
//                return true;
                return pathname.isFile() && pathname.getName().endsWith(".jpg");
            }
        });

//        File[] files = file.listFiles(pathname -> pathname.isFile() && pathname.getName().endsWith(".jpg"));

        System.out.println(Arrays.toString(files));
    }
}

IO流介绍以及输入输出以及流向的介绍

根据流向:
    输入流:外部数据 -> java程序
    输出流:java程序 -> 外部数据

根据种类:
    字节流【万能流】:计算机中所有的文件格式数据都是以字节的方式存储的,可以读写任意一种任意类型的文件
        字节输出流:OutputStream【抽象类】

                     - FileOutputStream【子类】
                字节输入流:InputStream
       字符流 = 字节流+编码表:使用计算机自带的记事本打开能看懂
                字符输出流:
                字符输入流:
    为啥要学IO流呢?
  之前学了一个集合以及数组,可以保存数据,但是这两个都是临时存储(代码运行完毕,集合和数组会从内存中消失,从而数据就不存在了),所以集合和数组达不到永久保存的目的,我们希望咱们的数据永久保存起来,所以我们就可以将数据保存到硬盘上,此时我们就可以随时想拿到硬盘上的数据就随时拿
      
  而且我们将来传输数据,必然要用到输入,输出动作

IO流的流向_针对se阶段的IO

输入流:将数据从硬盘上读到内存中  Input
输出流:从内存出发,将数据写到硬盘上  Output
    
    
要是从电脑和电脑之间做数据传输,就是相对的
    发数据一方 : 输出
    接数据一方 : 输入

IO流分类

字节流:万能流,一切皆字节
      字节输出流:  OutputStream 抽象类
      字节输入流:  InputStream 抽象类
          
字符流:专门操作文本文档
      字符输出流:Writer 抽象类
      字符输入流:Reader 抽象类

OutputStream子类FileOutputStream

1.概述:字节输出流,OutputStream 是一个抽象类
        子类: FileOutputStream
 2.作用:往硬盘上写数据
     
 3.构造:
   FileOutputStream(File file) 
   FileOutputStream(String name)
       
 4.特点:
   a.指定的文件如果没有,输出流会自动创建
   b.每执行一次,默认都会创建一个新的文件,覆盖老文件 
       
 5.方法:
   void write(int b)  一次写一个字节
   void write(byte[] b)  一次写一个字节数组
   void write(byte[] b, int off, int len)  一次写一个字节数组一部分 
             b:写的数组
             off:从数组的哪个索引开始写
             len:写多少个
   void close()  -> 关闭资源
/*
       void write(int b)  一次写一个字节
     */
    private static void method01() throws IOException {
        FileOutputStream fos = new FileOutputStream("module21\\1.txt");
        fos.write(97);
        fos.close();
    }
  /*
       void write(byte[] b)  一次写一个字节数组
     */
    private static void method02()throws IOException {
        FileOutputStream fos = new FileOutputStream("module21\\1.txt");
        byte[] bytes = {97,98,99,100,101,102,103};
        fos.write(bytes);
        fos.close();
    }
 /*
         void write(byte[] b, int off, int len)  一次写一个字节数组一部分
             b:写的数组
             off:从数组的哪个索引开始写
             len:写多少个
     */
    private static void method03()throws IOException {
        FileOutputStream fos = new FileOutputStream("module21\\1.txt");
        byte[] bytes = {97,98,99,100,101,102,103};
        fos.write(bytes,0,2);
        fos.close();
    }
  /*
       void write(byte[] b)  一次写一个字节数组
     */
    private static void method04()throws IOException {
        FileOutputStream fos = new FileOutputStream("module21\\1.txt");
        //byte[] bytes = "abc".getBytes();
        fos.write("abcde".getBytes());
        fos.close();
    }
1.字节流的续写追加:
  FileOutputStream(String name, boolean append) 
                   append:true -> 会实现续写追加,文件不覆盖了
                       
2.换行:
  a.windows: \r\n -> 占2个字节   \n
  b.linux: \n
  c.mac os : \r
private static void method05()throws IOException {
        FileOutputStream fos = new FileOutputStream("module21\\1.txt",true);
        fos.write("床前明月光\r\n".getBytes());
        fos.write("疑是地上霜\n".getBytes());
        fos.write("举头望明月\n".getBytes());
        fos.write("低头思故乡\n".getBytes());
        fos.close();
    }

InputStream子类FileInputStream

1.概述:字节输入流 InputStream ,是一个抽象类
      子类:FileInputStream
          
2.作用:读数据,将数据从硬盘上读到内存中来
    
3.构造:
  FileInputStream(File file)
  FileInputStream(String path)  
      
4.方法:
  int read()   一次读一个字节,返回的是读取的字节
  int read(byte[] b)  一次读取一个字节数组,返回的是读取的字节个数
  int read(byte[] b, int off, int len)  一次读取一个字节数组一部分,返回的是读取的字节个数
  void close()  关闭资源        

一次读取一个字节

 /*
       int read()   一次读一个字节,返回的是读取的字节
     */
    private static void method01()throws Exception {
        FileInputStream fis = new FileInputStream("module21\\1.txt");
        //int data1 = fis.read();
        //System.out.println(data1);

        //int data2 = fis.read();
        //System.out.println(data2);

        //int data3 = fis.read();
        //System.out.println(data3);

        //int data4 = fis.read();
        //System.out.println(data4);// -1

        //int data5 = fis.read();
        //System.out.println(data5);//-1

        System.out.println("=================");
        //定义一个变量,接收读取到的字节
        int len;
        while((len = fis.read())!=-1){
            System.out.println((char)len);
        }

        fis.close();
    }

问题1:一个流对象,读完之后,就不要再读了;除非重新new一个新的对象

问题2:流关闭之后,流对象不能继续使用了

Exception in thread "main" java.io.IOException: Stream Closed
	at java.base/java.io.FileInputStream.read0(Native Method)
	at java.base/java.io.FileInputStream.read(FileInputStream.java:228)
	at com.atguigu.c_input.Demo01FileInputStream.method01(Demo01FileInputStream.java:38)
	at com.atguigu.c_input.Demo01FileInputStream.main(Demo01FileInputStream.java:7)

FileInputStream

IO流:
根据流向划分:
输入流
输出流
根据种类划分:
字节流【万能流】:
字节输出流:OutputStream

​ FileOutputStream

​ 字节输入流:InputStream

​ FileInputStream

​ 字符输出流
​ 字符输入流

FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

对于读取数据而言,要求,目标路径一定要存在

字节输入流读取数据的方式:
public int read() 一次读取一个字节
public int read(byte[] b) 一次读取一个字节数组

 */
public class FileInputStreamDemo1 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try{
            //FileInputStream(File file)
//            File file = new File("src/shujia/day13/a2.txt");
//            fis = new FileInputStream(file);

            //FileInputStream(String name)
            fis = new FileInputStream("src/shujia/day13/a1.txt");

//            System.out.print( (char)fis.read());
//            System.out.print((char) fis.read());
//            System.out.print((char) fis.read());
//            System.out.print((char) fis.read());
//            System.out.print((char) fis.read());
//            System.out.print((char) fis.read());

            // 使用while循环读取字节
            // 通过api发现,FileInputStream读取到文件末尾的时候返回-1
//            int i = 0;
//            while ((i = fis.read())!=-1){
//                System.out.print((char) i);
//            }

            //public int read(byte[] b)
//            byte[] bytes = new byte[2];
//            int length = fis.read(bytes); //返回实际读取到的字节数,读取到的数据在字节数组中
//            String s = new String(bytes, 0, length);
//            System.out.print(s);

//            int length2 = fis.read(bytes); //返回实际读取到的字节数,读取到的数据在字节数组中
//            String s2 = new String(bytes, 0, length2);
//            System.out.print(s2);
            byte[] bytes = new byte[1024]; // 字节数组大小,一般来说,是1024的倍数
            int length = 0;
            while ((length = fis.read(bytes))!=-1){
                String s2 = new String(bytes, 0, length);
                System.out.print(s2);
            }


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

BufferedOutputSream

/*
    IO流:
        根据流向划分:
            输入流
            输出流
        根据种类划分:
            字节流【万能流】:
                字节输出流:OutputStream
                            - FileOutputStream
                            - BufferedOutputStream 【加入了缓冲区,写数据要更快】
                字节输入流:InputStream
                            - FileInputSteam
                            - BufferedInputStream   【加入了缓冲区,读取数据要更快】
            字符流=字节流+编码表:
                字符输出流
                字符输入流
public class BufferedOutputStreamDemo1 {
    public static void main(String[] args) throws Exception{
        //BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/shujia/day13/a2.txt",true));

        // 一次写一个字节
        bos.write(101);
        // 一次写一个字节数组
        byte[] bytes = {101,102,103,104};
        bos.write(bytes);
        // 一次写一个字节数组的一部分
        bos.write(bytes,1,2);


        bos.flush();
        //释放资源
        bos.close(); // 底层关闭之前,做了一次刷新操作,将内存缓冲区中的数据刷到了磁盘中
//        bos.write(102);
//        bos.flush();
    }
}

BufferedOutputStream

/*
    IO流:
        根据流向划分:
            输入流
            输出流
        根据种类划分:
            字节流【万能流】:
                字节输出流:OutputStream
                            - FileOutputStream
                            - BufferedOutputStream 【加入了缓冲区,写数据要更快】
                字节输入流:InputStream
                            - FileInputSteam
                            - BufferedInputStream   【加入了缓冲区,读取数据要更快】
            字符流=字节流+编码表:
                字符输出流
                字符输入流
public class BufferedInputStreamDemo1 {
    public static void main(String[] args) throws Exception{
        //BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
        //创建字节缓冲输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/shujia/day13/a2.txt"));

        // 一次读取一个字节
//        int i = 0;
//        while ((i=bis.read())!=-1){
//            System.out.print((char) i);
//        }

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


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

字符输出流Write

字符流 = 字节流 + 编码表:
    字符输出流:Writer【抽象类】
                - OutputStreamWriter
    字符输入流:Reader


OutputStreamWriter构造方法:
    OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
    OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。

Writer字符输出流写数据的方式:
    public void write(int c)
    public void write(char[] cbuf)
    public void write(char[] cbuf,int off,int len)
    public void write(String str)
    public void write(String str,int off,int len)

注意:
    只要是字符流写数据,将来都需要进行flush操作
public class OutputStreamWriterDemo1 {
    public static void main(String[] args) throws Exception{
        //OutputStreamWriter(OutputStream out) 使用平台默认的编码写字符数据到文本文件中
//        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/shujia/day13/b1.txt"));
        //OutputStreamWriter(OutputStream out, String charsetName) 指定编码创建字符输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("src/shujia/day13/b1.txt"),"GBK");

        //public void write(int c) 一次写一个字符
//        osw.write(101);
//        osw.flush();

        //public void write(char[] cbuf) 一次写一个字符数组
//        char[] chars = {'我','爱','中','国'};
//        osw.write(chars);
//        osw.flush();

        // public void write(char[] cbuf,int off,int len) 一次写字符数组的一部分
//        osw.write(chars,2,2);
//        osw.flush();

        //public void write(String str) 直接写一个字符串
        osw.write("张是世界上最帅的男人!");
        osw.flush();

        //public void write(String str,int off,int len) 写字符串的一部分
//        osw.write("张是世界上最帅的男人!", 4,3);
//        osw.flush();

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

异常

异常:
Throwable:
- Error【非常严重的错误,我们自己解决不了】
- Exception【自己能处理的异常】
- RuntimeException【运行时期异常】
- 除了RuntimeException【编译时期异常】

常见的异常:
空指针异常
索引越界异常
ClassCastException

public class ExceptionDemo1 {
    public static void main(String[] args) {
//        String s1 = null;
////        System.out.println(s1.length()); // jvm默认处理异常的方式是抛出错误,程序停止,后续代码不执行
//
//        if(s1!=null){
//            System.out.println(s1.length()); // NullPointerException
//        }else {
//            System.out.println("字符串为null");
//        }
//
//        System.out.println("over");

        int[] arr = {11,22,33};
        System.out.println(arr[6]); // ArrayIndexOutOfBoundsException


    }
}

异常处理的方式:

​ 1、try...catch...【最终的处理方式】
​ 2、throws【仅为了程序能够通过编译】

注意:
1、try中的代码只要遇到异常,try中的其他代码不执行,直接进入到匹配catch的过程
2、如果没有对应的catch能够匹配,那么就说明该异常没有被处理,就由jvm默认处理了。
3、新版本的新写法,可以直接写一个catch,将多个异常类型使用|分割,缺点:多种异常的处理方案统一
4、只要是XxxException.一定是Exception子类,新版本的新写法中,异常类之间,不能存在继承关系
5、若写多个catch接收的话,异常类可以存在继承关系,但是父类必须写在后面
6、如果一个运行过程出现的问题,大概率是运行时期异常父类中一定会继承RuntimeException
而一般情况下,运行时期异常都是由于代码不够严谨导致的,可以通过提高代码逻辑严谨进行规避
也可以使用try...catch处理运行时期异常
7、如果一段代码,还没运行,就直接报错了,大概率是编译时期异常,父类直接继承自Exception
而编译时期异常必须要使用try...catch...进行处理

public class ExceptionDemo2 {
    public static void main(String[] args) {
        int[] arr = {11, 22, 33};
        try {
            System.out.println(arr[6]); // ArrayIndexOutOfBoundsException
            String s = null;
            System.out.println(s.length()); // NullPointerException
        }catch (ArrayIndexOutOfBoundsException e){ //ArrayIndexOutOfBoundsException e = new ArrayIndexOutOfBoundsException()
            System.out.println("出错啦!!数组索引越界异常");
        }catch (NullPointerException e){ // NullPointerException e = new NullPointerException();
            System.out.println("出错啦!!空指针异常");
        }

        try {
            System.out.println(arr[1]); // ArrayIndexOutOfBoundsException
            String s = null;
            System.out.println(s.length()); // NullPointerException
        }catch (NullPointerException | ArrayIndexOutOfBoundsException e){ //ArrayIndexOutOfBoundsException e = new ArrayIndexOutOfBoundsException()
            System.out.println("出错啦!!");
        }
        try {
            System.out.println(arr[1]); // ArrayIndexOutOfBoundsException
            String s = null;
            System.out.println(s.length()); // NullPointerException
        }catch (Exception e){ //Exception e = new ArrayIndexOutOfBoundsException()
            System.out.println("出错啦!!");
        }
        try {
            System.out.println(arr[6]); // ArrayIndexOutOfBoundsException
            String s = null;
            System.out.println(s.length()); // NullPointerException
        } catch (ArrayIndexOutOfBoundsException e) { //ArrayIndexOutOfBoundsException e = new ArrayIndexOutOfBoundsException()
            System.out.println("出错啦!!数组索引越界异常");
        } catch (NullPointerException e) { // NullPointerException e = new NullPointerException();
            System.out.println("出错啦!!空指针异常");
        } catch (Exception e){
            System.out.println("其他错误!!");
        }
//        编译时期异常,代码无法通过编译,必须通过try..catch处理
//        FileInputStream fis = new FileInputStream("");
        try {
            FileInputStream fis1 = new FileInputStream("");
        }catch (Exception e){
            System.out.println("出错啦");
        }
        System.out.println("over");
    }
}

try...catch...finally...

1、无论try中的代码是否报错,finally中的代码都会执行,以后的开发中finally中一般编写释放资源代码
2、有一种情况,finally不会执行,在执行finally之前,整个程序意外中止

public class ExceptionDemo3 {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4};

        try {
            System.out.println(arr[1]);
            System.exit(0);
        }catch (Exception e){
            System.out.println("出错啦!");
        }finally {
            System.out.println("好好学习");
        }



        System.out.println("over");
    }
}

throws

throws在方法上抛出异常类,表示一种可能性

public class ExceptionDemo5 {
    public static void main(String[] args) throws Exception{

//       try {
//           fun1();
//       }catch (Exception e){
//           e.printStackTrace();
//       }
        fun1();

        System.out.println("over");
    }

    public static void fun1() throws Exception{
        FileInputStream fileInputStream = new FileInputStream("");
    }
}

throw,throws try...catch区别

  • throws
    用在方法声明后面,跟的是异常类名
    可以跟多个异常类名,用逗号隔开
    表示抛出异常,由该方法的调用者来处理
    throws表示出现异常的一种可能性,并不一定会发生这些异常

  • throw
    用在方法体内,跟的是异常对象名
    只能抛出一个异常对象名
    表示抛出异常,由方法体内的语句处理
    throw则是抛出了异常,执行throw则一定抛出了某种异常

  • 原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
    区别:

  • 后续程序需要继续运行就try
    后续程序不需要继续运行就throws
    举例:
    感冒了就自己吃点药就好了,try
    吃了好几天药都没好结果得了新冠,那就的得throws到医院
    如果医院没有特效药就变成Error了

public class ExceptionDemo6 {
    public static void main(String[] args) {
        try {
            fun1();
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println("over");
    }

    public static void fun1() throws ArithmeticException{
        int a = 10;
        int b = 0;
        if(b==0){
            throw new ArithmeticException();
        }else {
            System.out.println(a/b);
        }

    }
}
posted @   09250327  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示