Java——IO流详解

文章目录

    • 文件流介绍
    • 常用的文件操作
      • IO流原理及流的分类
      • Java IO流原理
      • 流的分类
      • 常用的类
          • FileInputStream 介绍
          • FileoutputStream 介绍
          • 拷贝文件案例分析
          • FileReader 常用方法:
          • FileWriter 常用方法
          • FileReader 应用案例
          • FileWriter应用案例
      • 节点流和处理流
          • BufferedReader 应用案例
          • BufferedWriter 应用案例
          • 综合使用 BufferedReader 和 BufferedWriter 完成 文本文件的拷贝,注意文件编码
          • 处理流-BufferedInputStream 和 BufferedOutStream*
          • 应用案例
      • 对象流 -ObjectInputStream(提供反序列化功能) 和 ObjectOutputStream(提供系列化功能)
          • 序列化和反序列化
          • 注意事项
      • 转换流- InputStreamReader 和 OutputStreamWriter
      • 打印流- PrintStream 和 PrintWriter
      • Properties类
          • Properties应用案例
      • IO流经典习题分享
          • 判断创建文件
          • BufferedReader
          • properties文件处理

文件流介绍

文件在程序中是以流的形式来操作的

Java 程序(内存) —— 输出流 —— > 文件(磁盘)
文件(磁盘) < —— 输入流 —— Java 程序(内存)

  • 流:数据在数据源(文件)和程序(内存)之间经历的路径
  • 输入流:读出文件
  • 输出流:写入文件

常用的文件操作

  • 创建文件对象相关构造器和方法
// 相关方法
new File(String pathname)  //根据路径构建一个File 对象
new FIle(File partent,String child) // 根据父目录文件+子路径构建
new File(String parent,String child) // 根据父目录 + 子路径构建
    
creatNewFile 创建
  • 获取文件信息:名称;路径;父级目录;大小。
//调用相应的方法,得到对应信息
System.out.println("文件名字=" + file.getName());
System.out.println("文件的绝对路径=" + file.getAbsolutePath());
System.out.println("文件的父级目录=" + file.getParentFile());
System.out.println("文件大小(字节)=" + file.length());
  • 目录的操作和文件删除

mkdir 创建一级目录、mkdirs 创建多级目录、delete 删除空目录或文件

判断 d:\\news1.txt是否存在,如果存在就删除
判断 D:\\demo02 (目录) 是否存在,存在就删除,否则提示不存在。
判断 D:\\demo\\a\\b\\c目录是否存在,如果存在就提示已经存在,否则就创建

    //创建多级目录
    @Test
    public void m3() {
    String directoryPath = "E:\\demo\\a\\b\\c";
    File file = new File(directoryPath);
    if(file.exists()) {
        System.out.println(directoryPath + "存在");
    } else {
        if(file.mkdirs()) { // 创建一级目录使用mkdir(),创建多级目录使用mkdirs()方法
            System.out.println(directoryPath + "创建成功。。");
        } else {
            System.out.println(directoryPath + "创建失败。。。");
        }
    }
}

IO流原理及流的分类

Java IO流原理

  1. I/O是Input/Output 的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读写文件,网络通讯等。
  2. Java 程序中,对于数据的输入/输出操作以“ 流(stream ” 的方式进行。
  3. java.io包下提供了各种“ 流 ” 类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
  4. 输入 input :读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类

  1. 按照数据单位不同分为:字节流(8 bit),字符流(按字符,对应字节,和编码有关;比如 UFT - 8 一个英文字母 1 字节,一个中文汉字 3 字节)

  2. 按数据流的流向不同分为:输入流和输出流

  3. 按流的角色不同分为:节点流、处理流/ 包装流

    抽线基类 字节流 字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer
    1. java的IO流共涉及40多个类,实际上非常规则,都是从上面4个抽象基类派生的。
    2. 由这四个类派生出来的子类名称都是以其父类作为子类名后缀。

Java——IO流详解_第1张图片

常用的类

  1. InputStream 抽象类是所有类字节输入流的超类
  2. InputStream 常用的子类
  3. FileInputStream :文件输入流
  4. BufferedInoutStream: 缓冲字节输入流
  5. ObjectInputStream:对象字节输入流
  • FileInputStream 介绍
package File_;

import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * @author Java_豆豆子
 * @version 1.0
 * 演示FileInputStream的使用(字节输入流  文件——程序)
 */
public class FileInputStream_ {
    public static void main(String[] args) {

    }
    /*
    * 演示读取文件
    * 单个字节的读取,效率低
    * ——> 使用 read(byte[] b) 这个方式
    * */
    @Test
    public void readFile01() throws IOException {
        String filePath = "E:\\hello.txt";
        int readDate =0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取文件
            fileInputStream = new FileInputStream(filePath);
            // 从该输入流读取一个字节的数据。如果没有输入可用,此方法将阻止。
            // 如果返回 -1 表示读取完毕
            while((readDate = fileInputStream.read()) != -1)  {
                System.out.print((char)readDate);  //转成char 显示
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭文件流,释放资源
            fileInputStream.close();
        }
    }
    @Test
    public void readFile02() throws IOException {
        String filePath = "E:\\hello.txt";
        int readDate =0;
        byte[] buf = new byte[8]; //一次读取8个字节
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取文件
            fileInputStream = new FileInputStream(filePath);
            // 从该输入流读取最多b.length字节的数据到字节数据。此方法将阻塞,直到某些输入可以
            // 如果返回 -1 表示读取完毕
            // 如果读取正常,返回实际读取的字节数
            while((readLen = fileInputStream.read(buf)) != -1)  {
                System.out.print(new String(buf,0,readLen));  //转成char 显示
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //关闭文件流,释放资源
            fileInputStream.close();
        }
    }
}
  • FileoutputStream 介绍
package File_;

import org.junit.jupiter.api.Test;

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

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class FileOutputStream_ {
    public static void main(String[] args) {

    }

    /**
     * 演示使用 FileOutputStream 将数据写到文件中,
     * 如果该文件不存在则创建该文件
     */
    @Test
    public void fileWrite() {
        String filePath = "E:\\b.txt";
        FileOutputStream fileOutputStream = null;
        try {
            //1. new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容
            //2. new FileOutputStream(filePath,true) 创建方式,当写入内容时,会追加到原来内容的后面
            fileOutputStream = new FileOutputStream(filePath);
            // str.getBytes() 可以把 字符串-> 字节数组
             String str = "world!";
            // fileOutputStream.write(str.getBytes());
            // write(byte[] b,int off,int len) 将len字节从位于偏移量 off的指定字节数组
            fileOutputStream.write(str.getBytes(),0,str.length());

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  • 拷贝文件案例分析
package File_;

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

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class FileCopy {
    public static void main(String[] args) throws IOException {
        // 完成 文件拷贝,将e:\\Koala.jpg 拷贝 c: \\
        // 思路分析
        // 1.创建文件的输入流,将文件读入到程序
        // 2.创建文件的输出流,将读入到的文件数据,写入到指定路径
        String filePath = "E:\\1.jpg";
        String destFilePath = "D:\\1.jpg";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream =null;
        try {
            fileInputStream = new FileInputStream(filePath);
            fileOutputStream = new FileOutputStream(destFilePath);
            //定义一个字节数组,提高读取效果
            byte[] bytes = new byte[1024];
            int readLen = 0;
            while ((readLen = fileInputStream.read(bytes)) != -1) {
                //读取到后就写入文件
                //即是,一边读一边写
                fileOutputStream.write(bytes,0,readLen);
            }
            System.out.println("拷贝成功!");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(fileInputStream != null) {
                fileInputStream.close();
            }
            if(fileOutputStream != null) {
                fileOutputStream.close();
            }
        }
    }
}

FileReader 和FileWriter 是字符流,即按照字符来操作IO

  • FileReader 常用方法:
  1. new FileReader(File/String)
  2. read: 每次读取单个字符,返回该字符,如果到文件末尾返回-1
  3. read(char[]): 批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾则返回-1
  • 相关 API :
  1. new String(char[]) : 将char[] 转换成String
  2. new String(char[], off, len) : 将char[]的指定部分转换成String
  • FileWriter 常用方法
  1. new FileWriter(File/String) :覆盖模式,相当于流的指针在首端
  2. new FileWriter(File/String, true): 追加模式,相当于流的指针在尾端
  3. write(int): 写入单个字符
  4. writer(char[]) : 写入指定数组
  5. writer(char[],off, len) :写入指定数组的指定部分
  6. writer(string) : 写入整个字符串
  7. writer(string ,off , len) :写入整个字符串的指定部分
  • 相关API : String类 : toCharArray : 将String转换成char[]

**注意:**FileWriter使用后,必须要关闭(close) 或者刷新(flush),否则写入不到指定的文件!

  • FileReader 应用案例
//使用FileReader 从E:\story.txt 读取内容,并显示
package File_;

import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.io.IOException;

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class FileRead_ {
    public static void main(String[] args) {
    }

    /**
     * 单个字符读取文件
     * @throws IOException
     */
    @Test
    public void fileRead01() throws IOException {
        String FilePath = "E:\\story.txt";
        FileReader fileReader = null;
        int data = 0;
        try {
            fileReader = new FileReader(FilePath);
            //进行读取
            while( (data = fileReader.read()) != -1) {
                System.out.print((char)data);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if(fileReader != null) {
                fileReader.close();
            }
        }
    }
    /**
     * 使用字符数组读取文件
     */
    @Test
    public void FileRead02() throws IOException {
        String FilePath = "E:\\story.txt";
        FileReader fileReader = new FileReader(FilePath);
        char[] data =new char[1024];
        int readLen = 0;
        // 循环读取 使用read(buf), 返回实际读取到的字符数
        // 如果返回-1 说明到文件结束
        while((readLen = fileReader.read(data)) != -1) {
            System.out.println(new String(data,0,readLen));
        }
        if(fileReader != null) {
            fileReader.close();
        }
    }
}
  • FileWriter应用案例
// 使用FileWriter 将"路漫漫其修远兮,吾将上下而求索!" 写入到 note.txt 文件中.

package File_;

import org.junit.jupiter.api.Test;

import java.io.FileWriter;
import java.io.IOException;

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class FileWriter_ {
    public static void main(String[] args) {

    }
    @Test
    public void FileWriter() throws IOException {
        String filePath = "E:\\note.txt";
        FileWriter fileWriter = new FileWriter(filePath);
        char arr[] = {'a','b','v','c','w'};
        // 写入文件
        fileWriter.write('d');
        fileWriter.write("路漫漫其修远兮,吾将上下而求索!".toCharArray(),0,6);
        fileWriter.write(arr);
        // 在数据量大时可以采用循环操作
        if(fileWriter != null) {
            fileWriter.close();
            System.out.println("写入成功!");
        }
    }
}
  • 节点流和处理流

  1. 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
  2. 处理流(也叫包装流)是"连接"在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活。如 BufferedReader、BufferedWriter
  • 节点流和处理流的区别的练习
  1. 节点流是底层流/低级流,直接跟数据源相接。
  2. 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  3. 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
  • 处理流的功能主要体现在以下两个方面:
  1. 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
  2. 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
  • BufferedReader 应用案例
package File_;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * @author Java_豆豆子
 * @version 1.0
 * 演示bufferedReader 使用
 */
public class BufferedReader_ {
    public static void main(String[] args) throws IOException {
          String filePath = "E:\\note.txt";
          BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
          String readLine;
          while ((readLine = bufferedReader.readLine()) != null) {
              System.out.println(readLine);
          }
          if(bufferedReader.readLine() == null) {
              bufferedReader.close();
          }
    }
}
  • BufferedWriter 应用案例
package File_;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @author Java_豆豆子
 * @version 1.0
 * 演示 BufferWriter 使用
 */
public class BufferWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "E:\\note.txt";
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("路漫漫其修远兮,吾将上下而求索!");
        bufferedWriter.write("时间就是金钱!");
        bufferedWriter.close();
        System.out.println("写入成功");
    }
}
  • 综合使用 BufferedReader 和 BufferedWriter 完成 文本文件的拷贝,注意文件编码
package File_;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class BufferedCopy_ {
    public static void main(String[] args) throws Exception {
        //说明:
        //1. BufferedReader 和 BufferedWriter 是按照字符操作的
        //2. 不要去操作 二进制文件[声音,视频,doc,pdf等等], 可能造成文件损坏
        String srcFilePath = "E:\\note.txt";
        String copyFilePath = "E:\\note02.txt";
        String line;
        BufferedReader bufferedReader = new BufferedReader(new FileReader(srcFilePath));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(copyFilePath));
        while( (line = bufferedReader.readLine()) != null ){
            bufferedWriter.write(line); //读取一行
            bufferedWriter.newLine();   //插入一个换行
        }
        System.out.println("拷贝完毕!");
        if(bufferedReader != null) {
            bufferedReader.close();
        }
        if (bufferedWriter != null) {
            bufferedWriter.close();
        }
    }
}
  • 处理流-BufferedInputStream 和 BufferedOutStream*

​ 介绍BufferedInputStream:BufferedInputStream是字节流,在创建 BufferedInputStream时,会创建一个内部缓冲数组。

Java——IO流详解_第2张图片

  • 应用案例
package File_;

import java.io.*;

/**
 * @author Java_豆豆子
 * @version 1.0
 * 演示使用 BufferedOutputStream 和 BufferedInputStream 使用
 * 使用它们,可以完成二进制文件拷贝
 */
public class BufferedCopy02 {
    public static void main(String[] args) throws FileNotFoundException {
        String srcFilePath = "E:\\壁纸小程序教程.mp4";
        String destFilePath = "E:\\note03.mp4";
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFilePath));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destFilePath));
        // 循环的读取文件,并写入到 destFilePath
        byte[] buff = new byte[1024];
        int readLen;
        // 当返回-1 时,就表示文件读取完毕
        try {
            while ((readLen = bufferedInputStream.read(buff)) != -1) {
                bufferedOutputStream.write(buff, 0, readLen);
            }
            System.out.println("文件拷贝完毕~~~");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭外层的处理流即可, 底层会去关闭节点流
            try {
                bufferedInputStream.close();
                bufferedOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  • 对象流 -ObjectInputStream(提供反序列化功能) 和 ObjectOutputStream(提供系列化功能)

序列化和反序列化
  1. 序列化就是在保存数据时,保存数据的值和数据的类型

  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型

  3. 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现下面的两个接口之一:

    Serializble // 标记接口,没有方法;

    Externalizable // 该接口有方法需要实现 ,所以一般实现 Serializable

  • 注意事项
  1. 读写顺序要一致
  2. 要求实现序列化或反序列化对象,需要实现 Serializable
  3. 序列化的类中建议添加 SerialVersionUID【序列化的版本号】,为了提高版本的兼容性
  4. 序列化对象时,要求里面属性的类型也需要实现序列化接口
  5. 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
  6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

转换流- InputStreamReader 和 OutputStreamWriter

  1. InputStreamReader : Reader的子类,可以将InputStream(字节流)包装成Reader(字节流)
  2. OutputStreamWriter :Writer的子类,实现将OutputStream(字节流)包装成Writer(字节流)
  3. 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
  4. 可以在使用时指定编码格式(比如 UTF-8, GBK, GB2312, ISO8859-1等)。

打印流- PrintStream 和 PrintWriter

Properties类

  1. 专门用于读写配置文件的集合类

    配置文件的格式:键=值;键=值

  2. 注意:键值对不需要有空格,值不需要用引号一起来。默认类型是String

  3. Properties的常见方法

    load: 加载配置文件的键值对到Properties对象

    list: 将数据显示到指定设备

    getProperty(key): 根据键获取值

    setProperty(key,value):设置键值对到Properties 对象

    store: 将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为unicode码

Properties应用案例
package properties01;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * @author Java_豆豆子
 * @version 1.0
 */
public class properties03 {
    public static void main(String[] args) throws IOException {
        // 使用Properties 类来创建 配置文件, 修改配置文件内容
        Properties properties = new Properties();
        // 创建
        // 如果该文件没有 key 就是创建,如果有key就是,就是修改
        /*
            properties 父类是Hashtable, 底层就是Hashable,
         */
        properties.setProperty("charset","uft-8");
        properties.setProperty("user","汤姆");// 注意保存时,是中文的unicode 码值
        properties.setProperty("pwd","abc111");
        // 将 k-v 存储到文件中
        properties.store(new FileOutputStream("src\\mysql2.properties"),"此部分为注释");
        System.out.println("保存配置文件成功");
    }
}

IO流经典习题分享

  • 判断创建文件
package homework01;

import java.io.*;

/**
 * @author Java_豆豆子
 * @version 1.0
 * 判断e盘下是否有文件夹myTemp,如果没有就创建myTemp
 * 在E:\\myTemp 目录下,创建文件 hello.txt
 * 如果hello.txt 已经存在,提示该文件已经存在,就不要再重复创建了
 */
public class Homework01 {
    public static void main(String[] args) throws IOException {
        String directoryPath = "E:\\myTemp";
        File file = new File(directoryPath);
        String filePath = "E:\\myTemp\\hello.txt";
        File file1 = new File(filePath);
        if(!(file.exists())) {
            file.mkdir();
            System.out.println("目录myTemp创建成功!");
        } else {
            System.out.println("目录myTemp存在");
        }
        if(file1.exists()){
            System.out.println("该文件hello.txt存在!");
        } else {
            if(file1.createNewFile()) {
                System.out.println("hello.txt创建成功!");
                BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file1));
                bufferedWriter.write("hello.世界!");
                System.out.println("文件写入成功!");
                bufferedWriter.close();
            }
        }
    }
}
  • BufferedReader
package homework01;

import java.io.*;
/**
 * @author Java_豆豆子
 * @version 1.0
 * 使用BufferedReader 读取一个文本文件,为每行加上行号,再连同内容一并输出到屏幕上
 * 文件 编码格式错误,出现中文乱码;使用转换流解决
 */
public class homework02 {
    public static void main(String[] args) throws IOException {
        String filePath = "E:\\1.txt";
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath),"GBK");
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String readLen = " ";
        int i = 0;
        while( (readLen = bufferedReader.readLine()) != null) {
            i++;
            System.out.println(i+" "+ readLen);
        }
        if(bufferedReader!= null){
            bufferedReader.close();
        }
     }
}
  • properties文件处理
package properties01;

import org.junit.jupiter.api.Test;

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

/**
 * @author Java_豆豆子
 * @version 1.0
 * 编写一个dog.properties
 * 编写Dog 类(name,age,color)创建一个对象,读取dog.properties 用相应的内容完成属性初始化,并输出
 * 将创建的Dog 对象,序列化到文件dog.dat文件
 */
public class properties_ {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("name","Tom");
        properties.setProperty("age", "5");
        properties.setProperty("color","red");
        // 将键值对存储到文件中
        properties.store(new FileWriter("src\\dog.properties"),"注释");
        System.out.println("文件配置成功!");
        //读取文件
        properties.load(new FileReader("src\\dog.properties"));
        properties.list(System.out);
        String name =(String) properties.get("name") ;   //Object -> String
        int age = Integer.parseInt(properties.get("age") + "");  // Object -> int
        String color = (String) properties.get("color"); //Object ->String
        Dog dog = new Dog(name, age, color);
        System.out.println("=========dog对象信息=======");
        System.out.println(dog);
        // 序列化后,保存的文件格式,不是纯文本,而是按照他的格式来保存
        String filePath = "e:\\dog.dat";

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(dog);
        oos.close();
    }
    //编写一个方法,反序列化测试dog
    @Test
    public void m1() throws IOException, ClassNotFoundException {
        String filePath = "e:\\dog.dat";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Dog dog = (Dog) ois.readObject();
        System.out.println("===反序列化后 dog====");
        ois.close();
        System.out.println(dog);
    }
}
class Dog implements Serializable {
    String name;
    int age;
    String color;

    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

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

你可能感兴趣的:(java,java,jvm,开发语言)