博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaSE基础之IO流
阅读量:2348 次
发布时间:2019-05-10

本文共 23480 字,大约阅读时间需要 78 分钟。

Java中把输入/输出(input/output)操作称为流(Stream)。

流:即为起点到接收点有序的数据序列。

流的分类:

1.按照流的方向分为:

                输入流:只读,只会从流中读取数据;

               输出流:只写,只会向流中写入数据;

        (这里记录一下对输入流是读数据,输出流是写数据的理解:这里是站在内存的角度来理解的,输入流也就是数据从目标文件到内存,也就是内存去读目标文件的数据;输出流也就是数据从内存到目标文件,也就是将数据从内存中写到目标文件中。)

2.按照处理的数据分:

               字节流(InputStream/OutputStream):读或写的时候以字节为单位;

              字符流(Reader/Writer):读或写的时候以字符为单位;

       一般情况下(排除特殊情况),一个汉字占一个字符,一个字符占两个字节,如果读或写的时候只读或写一个字节的话,那么就会出现乱码。

3.按照功能分为:

              低级流:直接从数据的源头读取数据或者直接把数据写到目标位置的流,称为低级流,也称为节点流;

              高级流:对一个已经存在的流的连接和封装,称为高级流,也称为处理流;

 

先从字节流中的FileInputStream和FileOutputStream说起:以下是两者的模型

代码如下:

package com.Jevin.io;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class ByteStreamTest {    public static void main(String[] args) {        //readFile1();        //readFile2();        //writeFile1();        writeFile2();    }    /**     * 用FileInputStream读取文件的内容,一个一个字节的读取(出现中文乱码问题)     */    public static void readFile1() {        FileInputStream fin = null;        try {            //文件的完整路径有三部分组成:路径名称+分隔符+文件名称;            //如果指定文件不存在,或者他是一个目录,而不是一个常规文件,抑或因为其他原因无法打开进行读取,则抛出FileNotFoundException;            //注意:FileInputStream只能读取并显示纯文本文件的内容(也就是能用记事本打开的文件)            fin = new FileInputStream("d:\\aa.txt");            int i = 0;            //从文件中读取一个字节以int值返回,当读到文件末尾没有数据时返回-1;            while((i=fin.read()) != -1){                //byte->int->char                System.out.print((char)i);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //流打开之后,必须进行关闭            try {                if(fin != null){                    fin.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 用FileInputStream读取文件的内容,一次读取多个字节的内容(有些中文没有乱码,有些乱码;并且还多读了一些内容)     */    public static void readFile2() {        FileInputStream fin = null;        try {            fin = new FileInputStream("d:\\aa.txt");            int i = 0;            byte[] b = new byte[100];            //从文件中读取100个字节,放大数组中,i返回的是读取到的字节的数量,当读到文件末尾没有数据时返回-1;            while((i=fin.read(b)) != -1){                //byte[] -> char[] -> String                String str = new String(b);                System.out.print(str);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //流打开之后,必须进行关闭            try {                if(fin != null){                    fin.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 用FileOutputStream写String到文件     */    public static void writeFile1(){        FileOutputStream fout = null;        try {            //当目标文件不存在时,JVM会自动创建这个文件,若已存在,则将内容写到这个文件中;            fout = new FileOutputStream("d:\\bb.txt",true); //true表示追加写文件            String str = "2.用FileOutputStream写String到文件";            //将String转换为byte[]            byte[] b = str.getBytes();            fout.write(b);            System.out.println("文件写入完成");        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //流打开之后,必须进行关闭            try {                if(fout != null){                    fout.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 用FileOutputStream写String到文件,解决换行     */    public static void writeFile2(){        FileOutputStream fout = null;        try {            //当目标文件不存在时,JVM会自动创建这个文件,若已存在,则将内容写到这个文件中;            fout = new FileOutputStream("d:\\bb.txt",true); //true表示追加写文件            for(int i=0;i<=10;i++){                //由于FileOutputStream不会处理换行,需要我们在String结尾加入\n                //记事本不能识别\n,能够识别\r\n;                String str = i+".用FileOutputStream写String到文件\r\n";                //将String转换为byte[]                byte[] b = str.getBytes();                fout.write(b);            }            System.out.println("文件写入完成");        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //流打开之后,必须进行关闭            try {                if(fout != null){                    fout.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

 然后将FileInputStream和FileOutputStream拼接起来,就组成了拷贝粘贴的功能;

但是这样直接使用低级流去读和写文件显然不是我们想要的,因为效率有点低,于是我们需要在低级流上接上一个高级流,像这样:

下面有代码来实现上面的过程,也即是拷贝文件的过程:

package com.Jevin.io;import java.io.*;/** * 文件拷贝器 */public class FileCoper {    private FileCoper(){    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(String srcFile,String desPath){        //从源文件的String中取出文件名称,也就是从例如:"D:\\tools\\Navicat.rar"中取到"Navicat.rar";        String[] arr = srcFile.split("\\\\");        String fileName = arr[arr.length-1];        //组织目标文件:路径名称+分隔符+文件名称        String desFile = desPath+"\\"+fileName;        //声明输入流:        FileInputStream fin = null;        BufferedInputStream bin = null;        //声明输出流:        FileOutputStream fout = null; //低级输出流        BufferedOutputStream bout = null; //高级输出流        try {            //创建输入流:            fin = new FileInputStream(srcFile);            bin = new BufferedInputStream(fin);            //创建输出流:首先初始化低级流,然后初始化高级流            fout = new FileOutputStream(desFile);            bout = new BufferedOutputStream(fout);            int i=0;            byte[] b = new byte[1024*1024];            while((i=bin.read(b))!=-1){                bout.write(b,0,i);            }            /**             * 如果关闭低级流,那么会导致小于8k的文件滞留在bout的缓冲区,所以要刷新缓存区             * 拷贝过程中,只要文件的大小大于8k,则无需刷新缓存             */            //bout.flush();            //由于采用了正确的流的关闭方式,所以无论文件大小,无需刷新缓存            System.out.println("文件拷贝完成,"+srcFile+"拷贝到目标文件"+desFile+"完成");        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //错误的关闭方式            //if(fin != null){            //    try {            //        fin.close();            //    } catch (IOException e) {            //        e.printStackTrace();            //    }            //}            //if(fout != null){            //    try {            //        fout.close();            //    } catch (IOException e) {            //        e.printStackTrace();            //    }            //}            //正确的关闭方式            //高级流在关闭的时候:1.会自动关闭低级流;2.会自动刷新缓存                try {                    if(bin != null){                        bin.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }                try {                    //bout关闭的时候,会自动刷新缓冲区                    if(bout != null){                        bout.close();                    }                } catch (IOException e) {                    e.printStackTrace();                }        }    }    public static void main(String[] args){        String file = "D:\\tools\\aa.txt";        //String file = "D:\\tools\\Navicat.rar";        copyFile(file,"d:\\");    }}

       但是上面的代码有点问题,问题出在取文件名和拼接目标文件路径上,采用了"\\"和"\\\\"这两种分隔符,但是不同操作系统上的分隔符是不一样的,这样写的话就写死了,违背java的可移植性特点,所以我们采用java.io.File来解决这个问题:

package com.Jevin.io;import java.io.*;/** * 文件拷贝器 */public class FileCoper {    private FileCoper(){    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(String srcFile,String desPath){        //从源文件的String中取出文件名称        //File file = new File(srcFile);        //copyFile(file,desPath);        //继续简化        copyFile(new File(srcFile),desPath);    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(File srcFile,String desPath){        //从源文件的String中取出文件名称        String fileName = srcFile.getName();        //组织目标文件:路径名称+分隔符+文件名称        String desFile = desPath+File.separator+fileName;        //声明输入流:        FileInputStream fin = null;        BufferedInputStream bin = null;        //声明输出流:        FileOutputStream fout = null; //低级输出流        BufferedOutputStream bout = null; //高级输出流        try {            //创建输入流:            fin = new FileInputStream(srcFile);            bin = new BufferedInputStream(fin);            //创建输出流:首先初始化低级流,然后初始化高级流            fout = new FileOutputStream(desFile);            bout = new BufferedOutputStream(fout);            int i=0;            byte[] b = new byte[1024*1024];            while((i=bin.read(b))!=-1){                bout.write(b,0,i);            }            System.out.println("文件拷贝完成,"+srcFile+"拷贝到目标文件"+desFile+"完成");        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //正确的关闭方式            //高级流在关闭的时候:1.会自动关闭低级流;2.会自动刷新缓存            try {                if(bin != null){                    bin.close();                }            } catch (IOException e) {                e.printStackTrace();            }            try {                //bout关闭的时候,会自动刷新缓冲区                if(bout != null){                    bout.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    public static void main(String[] args){        String file = "D:\\tools\\aa.txt";        //String file = "D:\\tools\\Navicat.rar";        copyFile(file,"d:\\");    }}

        但是这里还有一个问题,一个相对较大的文件拷贝是需要时间的,如果在拷贝的这段时间内,程序突然停下来的,文件看似拷贝下来了,但是这个文件是不完整的,那么该如何解决这个问题呢?下面代码演示:

package com.Jevin.io;import java.io.*;/** * 文件拷贝器 */public class FileCoper {    private FileCoper(){    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(String srcFile,String desPath){        //从源文件的String中取出文件名称        //File file = new File(srcFile);        //copyFile(file,desPath);        //继续简化        copyFile(new File(srcFile),desPath);    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(File srcFile,String desPath){        //从源文件的String中取出文件名称        String fileName = srcFile.getName();        //判断目标路径是否存在,如果不存在,就把他创造出来;        File dpath = new File(desPath);        if(!dpath.exists()){            dpath.mkdirs();        }        //组织目标文件:路径名称+分隔符+文件名称        String desFile = desPath+File.separator+fileName;        String tempFile = desFile + ".td"; //拷贝过程中的临时文件名称        //声明输入流:        FileInputStream fin = null;        BufferedInputStream bin = null;        //声明输出流:        FileOutputStream fout = null; //低级输出流        BufferedOutputStream bout = null; //高级输出流        try {            //创建输入流:            fin = new FileInputStream(srcFile);            bin = new BufferedInputStream(fin);            //创建输出流:首先初始化低级流,然后初始化高级流            fout = new FileOutputStream(tempFile);            bout = new BufferedOutputStream(fout);            int i=0;            byte[] b = new byte[1024*1024];            while((i=bin.read(b))!=-1){                bout.write(b,0,i);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //正确的关闭方式            //高级流在关闭的时候:1.会自动关闭低级流;2.会自动刷新缓存            try {                if(bin != null){                    bin.close();                }            } catch (IOException e) {                e.printStackTrace();            }            try {                //bout关闭的时候,会自动刷新缓冲区                if(bout != null){                    bout.close();                }            } catch (IOException e) {                e.printStackTrace();            }            //当文件拷贝完成之后,把临时文件名称改成目标文件名称            //这里注意:需要在流关闭的时候才可以重命名,要不然流将线程锁住,无法改名            File file1 = new File(desFile);            File file2 = new File(tempFile);            if(file2.renameTo(file1)){                System.out.println("文件拷贝完成,"+srcFile+"拷贝到目标文件"+desFile+"完成");            }else{                System.err.println("拷贝失败");            }        }    }    public static void main(String[] args){        //String file = "D:\\tools\\aa.txt";        String file = "D:\\tools\\Navicat.rar";        copyFile(file,"d:\\abc");    }}

         但是这里还有一个问题,以上只是解决了拷贝一个文件到任意指定的文件夹中,但是实际还有将任意文件夹(包括其中的文件)拷贝到任意文件夹中,这个又该如何实现呢?一下代码演示:

package com.Jevin.io;import java.io.*;/** * 文件拷贝器 */public class FileCoper {    private FileCoper(){    }    /**     * 拷贝文件的操作     * @param srcFile 源文件     * @param desPath 目标路径     */    public static void copyFile(File srcFile,String desPath){        //从源文件的String中取出文件名称        String fileName = srcFile.getName();        //判断目标路径是否存在,如果不存在,就把他创造出来;        File dpath = new File(desPath);        if(!dpath.exists()){            dpath.mkdirs();        }        //组织目标文件:路径名称+分隔符+文件名称        String desFile = desPath+File.separator+fileName;        String tempFile = desFile + ".td"; //拷贝过程中的临时文件名称        //声明输入流:        FileInputStream fin = null;        BufferedInputStream bin = null;        //声明输出流:        FileOutputStream fout = null; //低级输出流        BufferedOutputStream bout = null; //高级输出流        try {            //创建输入流:            fin = new FileInputStream(srcFile);            bin = new BufferedInputStream(fin);            //创建输出流:首先初始化低级流,然后初始化高级流            fout = new FileOutputStream(tempFile);            bout = new BufferedOutputStream(fout);            int i=0;            byte[] b = new byte[1024*1024];            while((i=bin.read(b))!=-1){                bout.write(b,0,i);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            //正确的关闭方式            //高级流在关闭的时候:1.会自动关闭低级流;2.会自动刷新缓存            try {                if(bin != null){                    bin.close();                }            } catch (IOException e) {                e.printStackTrace();            }            try {                //bout关闭的时候,会自动刷新缓冲区                if(bout != null){                    bout.close();                }            } catch (IOException e) {                e.printStackTrace();            }            //当文件拷贝完成之后,把临时文件名称改成目标文件名称            //这里注意:需要在流关闭的时候才可以重命名,要不然流将线程锁住,无法改名            File file1 = new File(desFile);            File file2 = new File(tempFile);            if(file2.renameTo(file1)){                System.out.println("文件拷贝完成,"+srcFile+"拷贝到目标文件"+desFile+"完成");            }else{                System.err.println("拷贝失败");            }        }    }    /**     * 拷贝文件夹的功能     * @param srcPath 原文件夹     * @param desPath 目标路径     */    public static void copyDir(String srcPath,String desPath){        copyDir(new File(srcPath),desPath);    }    /**     * 拷贝文件夹的功能     * @param srcPath 原文件夹     * @param desPath 目标路径     */    public static void copyDir(File srcPath,String desPath){        //判断srcPath是文件还是文件夹:        if(srcPath.isFile()){            //是文件,那么调用之前拷贝文件的方法:            copyFile(srcPath,desPath);        }else{            //是文件夹:            //从原文件中获取要拷贝的文件夹名称,例如,我要拷贝jdk源代码"D:\\file\\src",从中获取src            String pathName = srcPath.getName();            //在目标路径中创建要拷贝的目录,例如在"d:\\"创建"src"目录:            String dPath = desPath+File.separator+pathName; //"d:\\src"            File file = new File(dPath);            file.mkdirs();            //获取原文件夹中所有子文件(夹)            File[] files = srcPath.listFiles();            //遍历存放子文件的数组,一个个的拷贝到目标路径中,也就是dPath            for(File file1:files){                copyDir(file1,dPath); //递归调用            }        }    }    public static void main(String[] args){        //String file = "D:\\tools\\aa.txt";        //String file = "D:\\tools\\Navicat.rar";        //copyFile(file,"d:\\abc");        String file = "D:\\file\\src";        copyDir(file,"d:\\");    }}

这里文件或文件夹拷贝告一段落了,反观之前的FileOutputStream这个低级流在实现写的时候,有一些瑕疵,模型如下:

package com.Jevin.io;import java.io.*;public class ByteStreamTest {    public static void main(String[] args) {        writeFile3();    }    /**     * 在FileOutputStream上接一个高级流PrintStream输出     */    public static void writeFile3(){        FileOutputStream fout = null;        PrintStream ps = null;        try {            fout = new FileOutputStream("d:\\dd.txt",true);            ps = new PrintStream(fout);            for(int i=0;i<1000;i++){                String str = i+"在FileOutputStream上接一个高级流PrintStream输出";                //为了方便操作,ps.println(str)/ps.close()暂时不需要处理异常;                ps.println(str);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        }finally{            if(ps != null){                ps.close();            }        }    }}

================================================================================================

以上讲的都是字节流,从这里我们开始讲讲字符流的使用。

首先是使用字符流去读取文件中的数据,模型如下:

以下是代码演示:

package com.Jevin.io;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;public class CharacterStreamTest {    public static void main(String[] args){        //readFile1();        readFile2();    }    /**     * 用FileReader读取文件的内容,一个一个字符的读取     */    public static void readFile1(){        FileReader fr = null;        try {            fr = new FileReader("d:\\aa.txt");            int i = 0;            //读取一个字符,当做int返回,直到文件末尾没有字符时返回-1;            while((i=fr.read()) != -1){                //char -> int -> char                System.out.print((char)i);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            try {                if(fr != null){                    fr.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 用FileReader读取文件的内容,一次读取多个字符的数据     */    public static void readFile2(){        FileReader fr = null;        try {            fr = new FileReader("d:\\aa.txt");            int i = 0;            char[] c = new char[1024];            //读取多个字符,保存到数组中,i是读取到的字符的数量            while((i=fr.read(c)) != -1){                //char[] -> String                String str = new String(c,0,i);                System.out.print(str);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            try {                if(fr != null){                    fr.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

但是这样用低级流去读取文件是字符数组,然后还要手动转换为字符串,这样在读取大文件时效率不高,这时我没需要在低级流上接一个高级流,利用高级流的缓存一行一行的读取数据,效率大大提高,一下模型演示:

 下面是代码演示:

package com.Jevin.io;import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;public class CharacterStreamTest {    public static void main(String[] args) {        readFile3();    }    /**     * 用FileReader和BufferReader读取文件中的内容     */    public static void readFile3() {        FileReader fr = null;        BufferedReader br = null;        try {            fr = new FileReader("d:\\aa.txt");            br = new BufferedReader(fr);            String str = null;            while ((str = br.readLine()) != null) {                System.out.println(str);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }}

      这里简单的字节流和字符流就介绍到这里,接下来介绍一个有意思的流:RandomAccessFile,它可以从文件的任意位置读,也可以从文件的任意位置写。写个功能有什么用呢?换言之:可以将一个大文件切割成多个小文件分别读写

      模型如下所示:

代码如下所示:

package com.Jevin.io.demo;import java.io.File;/** * 文件拷贝包工头 */public class FileCopyContractor {    private File srcFile; //源文件    private String desFile; //目标文件    private String tempFile; //拷贝过程中的临时文件    private int splitCount; //文件切分的份数    public FileCopyContractor(){}    public FileCopyContractor(File srcFile, String desPath, int splitCount) {        super();        this.srcFile = srcFile;        this.splitCount = splitCount;        //组织目标文件:        String fileName = srcFile.getName();        this.desFile = desPath + File.separator + fileName;        this.tempFile = desFile + ".td";    }    public FileCopyContractor(String srcFile, String desPath, int splitCount) {        this(new File(srcFile),desPath,splitCount);    }    /**     * 包工头开始工作:     */    public void start(){        //获取原文件的大小:        long fileSize = srcFile.length();        System.out.println("文件的大小是:"+fileSize);        //根据切分的份数和源文件的大小,计算每个工人的平均工作量        long perWorkerSize = fileSize/this.splitCount;        //计算第一个工人的开始位置和结束位置:        long startPost = 0L;        long endPost = perWorkerSize;        //包工头创建多个工人:        for(int i=0;i
package com.Jevin.io.demo;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;/** * 文件拷贝工人 */public class FileCopyWorker {    private String name; //工人名称    private File srcFile; //源文件    private String desFile; //目标文件    private long startPost; //开始位置    private long endPost; //结束位置    private long copyedPost; //已经拷贝的位置    public FileCopyWorker(String name, File srcFile, String desFile, long startPost, long endPost) {        super();        this.name = name;        this.srcFile = srcFile;        this.desFile = desFile;        this.startPost = startPost;        this.endPost = endPost;        this.copyedPost = this.startPost; //初始化拷贝位置即为初始位置        System.out.println(name+"[开始位置是:"+this.startPost+",结束位置是:"+this.endPost+"]");    }    /**     * 工人开始工作     */    public void work(){        RandomAccessFile rin = null; //读数据流        RandomAccessFile rout = null; //写数据流        try {            rin = new RandomAccessFile(this.srcFile,"r");            rout = new RandomAccessFile(this.desFile,"rw");            //定位读写的位置:            rin.seek(this.startPost); //开始读的位置            rout.seek(this.startPost); //开始写的位置            byte[] b = new byte[1024*1024];            int i = 0;            //当已经拷贝的位置小于结束位置,并且未拷贝到文件结尾,就一直循环拷贝下去:            while((this.copyedPost < this.endPost) && (i=rin.read(b)) != -1){                if((this.copyedPost + i) > this.endPost){                    i = (int) (this.endPost - this.copyedPost);                }                rout.write(b,0,i);                this.copyedPost += i;                System.out.println(name+"正在工作,已经拷贝的位置是:"+this.copyedPost+",结束位置是:"+this.endPost);            }            System.out.println(name+"结束工作,已经拷贝的位置是:"+this.copyedPost+",结束位置是:"+this.endPost);        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally{            try {                if(rin != null){                    rin.close();                }                if(rout != null){                    rout.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}
package com.Jevin.io.demo;public class MainTest {    public static void main(String[] args){        String file = "D:\\tools\\Navicat.rar";        FileCopyContractor fileCopyContractor = new FileCopyContractor(file,"d:\\",15);        fileCopyContractor.start();    }}

“流”暂时就到这里吧,又补充的再继续吧!

转载地址:http://trtvb.baihongyu.com/

你可能感兴趣的文章
自学Java最起码要学到什么程度才能就业?
查看>>
零基础学Java需要做哪些准备?需要注意些什么呢?
查看>>
有了这份阿里大牛手写630页Java高级面试手册,offer稳了【建议收藏】
查看>>
学习Java,需要学到什么程度,才能出去找工作?
查看>>
2021年Java发展怎么样?现在学了Java技术出来是否还能找到工作?
查看>>
Java程序员面试大厂的技术标准,你达到要求了吗?
查看>>
为什么Java程序员需求量这么大,还会有人找不到合适的工作?
查看>>
过来人对程序员学习Java的10条建议,第2点很重要!
查看>>
大学生如何学好Java?过来人给你7点建议
查看>>
过来人的经验:Java实习生在公司里一般都做什么?
查看>>
Java入门基础知识点整理大放送,推荐收藏
查看>>
如何成为月入25k的高级程序员?建议养成这7个习惯
查看>>
学 Java还是Python, 哪个更好找工作?
查看>>
Java基础知识:如何计算Java对象占用内存大小?
查看>>
一份Java程序员的珍藏书单,请您注意查收
查看>>
为什么这么多人Java基础薄弱?该如何提升?
查看>>
如何成为一名合格的Java程序员?
查看>>
spring框架中5种自动装配模式介绍
查看>>
如何学习Spring框架?有哪些建议?
查看>>
Java程序员烂大街了吗?是,也不是
查看>>