I /O 流 和反射机制——JAVA

本文深入探讨Java中文件操作及流的基本概念,包括File类的使用、常见方法解析,以及字节流、字符流的输入输出处理。涵盖File类构造方法、属性获取与设置,流的分类、构造及常用操作,如读写文件、缓冲处理等。
摘要由CSDN通过智能技术生成

@月月鸟

1.操作文件或目录属性

在计算机中 ,通常使用各种各样的文件来保存数据,如何在java程序中操作这些文件呢?java.io包提供了一些接口和类,对文件进行基本操作,首先学习如何使用File类操作文件或目录。

File类的构造方法:
在这里插入图片描述
在这里插入图片描述

// 1.File(String pathname)
// File file = new File("d:/a.txt");
// File file = new File("d:\\a.txt");

// 2.File(String parent父路径,String child子路径)
// 通常应用于:你已经获取到了某个文件的父路径  然后只需要通过本构造即可实现路径自动拼接
// File file = new File("d:", "a.txt");

// 3.File(File parent,String child)
// 通常应用于:你已经获取到了某个文件的父路径(File对象型的)....
File parent = new File("d:");
File file = new File(parent , "a.txt");
// 获取文件的字节数
System.out.println(file.length());

File类常用的方法:

  1. String getName():返回此对象表示的文件或目录最后一级文件夹名称
  2. String getParent():返回此File对象的父目录路径名;如果此路径名没有指定父目录,则返回 null
  3. File getParentFile():返回File对象所在的父目录File实例;如果File对象没有父目录,则返回 null
  4. String getPath() :返回File对象所表示的字符串路径。
  5. .boolean mkdir():创建此File类对象指定的目录,不包含父目录。创建成功回true,否则返回false;
  6. boolean mkdirs():创建此File对象指定的目录,包括所有必需但不存在的父目录,创建成功返回true;否则返回false。注意,此操作失败时也可能已经成功地创建了一部分必需的父目录。
  7. boolean createNewFile():如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在,则返回 false;如果所创建文件所在目录不存在则创建失败并出现IOException异常。
    (注意:mkdir()和mkdirs()只能创建目录,不能创建文件;而createNewFile()只能创建文件,不能创建目录;
  8. boolean exists():判断文件或目录是否存在
  9. boolean delete():删除File类对象表示的目录或文件。如果该对象表示一个目录,则该目录必须为空才能删除;文件或目录删除成功返回true,否则false。
  10. boolean isDirectory():判断此File对象代表的路径表示是不是目录,只有File对象代表路径存在且是一个目录时才返回true,否则返回false
  11. boolean isFile():判断此File对象代表的路径是否是一个标准文件,只有File对代表路径存在且是一个标准文件时才返回true,否则返回false。
  12. String[] list():返回由File对象对应目录所包含文件名或文件夹名组成的字符串数组。
  13. File[] listFiles():返回由当前File对象对应目录所包含文件路径或文件夹路径组成的File类型的数组。
  14. boolean renameTo(File dest):重新命名此File对象表示的文件,重命名成功返回true,否则返回false。

(static separator:指定文件或目录路径时使用斜线或反斜线来写,但是考虑到跨平台,斜线反斜线最好使用File类的separator属性来表示)

JAVA的流

流是一个相对抽象的概念,所谓流就是一个传输数据的通道,这个通道可以传输相应类型的数据。进而完成数据的传输。这个通道被实现为一个具体的对象。

在这里插入图片描述

输入流:从外部空间(文件、网络连接、内存块)读入字节序列的管道(对象)。

输出流:可以向外部空间(文件、网络连接、内存块)写入字节序列的管道(对象)。

两种最基本的抽象类:

处理字节:InputStream OutputStream

处理字符:Reader Writer

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种:

1) 字节流:数据流中最小的数据单元是字节

2) 字符流:数据流中最小的数据单元是字符,Java中的字符是Unicode编码,一个字符占用两个字节。

基类是抽象类无法创建对象,所以需要寻找对应的子类。

字节流输入流:

InputStream类的常用子类有FileInputStream,用于从文件中读取数据。

构造:

FileInputStream(File file);

FileInputStream(String pathname);
常用方法有:

  1. 读取一个字节数据:int read();
  2. 读取一个字节数组长度的字节数据 返回实际读取到的字节数量:int read(byte[] b);
  3. 读取输入流中从指定索引开始,指定长度的字节数据到字节数组中:int read(byte b,int offs,int len);
  4. 关流:void colse();
字节输入流类:FileInputStream、BufferedInputStream和DataInputStream

FileInputStream:此类用于从本地文件系统中读取文件内容。

构造方法:

·FileInputStream(File file):打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定。

·FileInputStream(String name):打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。

常用方法:

·int available():返回下一次对此输入流调用的方法不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。

·void close():关闭此文件输入流并释放与该流关联的所有系统资源。

BufferedInputStream:此类本身带有一个缓冲区,在读取数据时,先放到缓冲区中,可以减少对数据源的访问,提高运行的效率。

构造方法:

·BufferedInputStream(InputStream in):创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。

·BufferedInputStream(InputStream in,int size):创建一个具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流in,以便将来使用。

常用方法:

·int available():返回下一次对此输入流调用的方法不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。

·void close():关闭此输入流并释放与该流关联的所有系统资源。

·int read():从输入流中读取数据的下一个字节。

·int read(byte[] b,int off,int len):从此字节输入流中给定偏移量处开始将各字节读取到指定的byte数组中。

DataInputStream:该类提供一些基于多字节读取方法,从而可以读取基本数据类型的数据。

构造方法:

·DataInputStream(InputStream in):使用指定的底层InputStream创建一个DataInputStream。

常用方法:

·int read(byte[] b):从包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组b中。

·int read(byte[] b,int off,int len):从包含的输入流中将最多len个字节读入一个byte数组中。

字节输出流:

OutputStream类的常用子类有FileInputStream,用于向文件写入数据。

构造
FileOutputStream(File file);

FileOutputStream(String path);

FileOutputStream(File/String file,boolean append); 如果为true表示可以追加数据 而不是覆盖数据

常用方法有:

  1. void write(int b); 输出一个字节
  2. void write(byte[] b); 输出一个字节数组的内容
  3. void write(byte[] b,int offs,int len); 输出一个字节数组中的指定范围的内容
  4. void close(); 关流
字节输出流类:FileOutputStream、BufferedOutputStream和DataOutputStream

FileOutputStream:此类用于从本地文件系统的文件中写入数据。

构造方法:

·FileOutputStream(File file):创建一个向指定File对象表示的文件中写入数据的文件输出流。

·FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。

常用方法:

·void close():关闭此文件输出流并释放与此流有关的所有系统资源。

·FileDescriptor getFD():返回与此流有关的文件描述符。

·void write(byte[] b):将b.length个字节从指定byte数组写入此文件输出流中。

·void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。

·void write(int b):将指定字节写入此文件输出流。

BufferedOutputStream:此类本身带有一个缓冲区,在写入数据时,先放到缓冲区中,实现缓冲的数据流。

构造方法:

·BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,来将数据写入指定的底层输入流。

·BufferedOutputStream(OutputStream out,int size):创建一个新的缓冲输出流,来将具有指定缓冲区大小的数据写入指定的底层输出流。

常用方法:

·void flush():刷新此缓冲的输出流。

·void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入此缓冲的输出流。

·void write(int b):将指定的字节写入此缓冲的输出流。

DataOutputStream(OutputStream out):创建一个新的数据输出流,将数据写入指定基础输出流。

常用方法:

·void flush():清空此数据输出流。

·int size():返回计数器written的当前值,即到目前为止写入此数据输出流的字节数。

·void write(byte[] b,int off,int len):将指定byte数组中从偏移量off开始的len个字节写入基础输出流。

·void write(int b):将指定字节(参数b的八个低位)写入基础输出流。

字符输入流

Reader类的常用子类为BufferedReader,接受Reader对象作为参数,并对其添加字符缓冲器。

构造:
FileReader(File file);

FileReader(String pathname);

常用方法:

int read() 从输入流中读取单个字符并返回

int read(char[] buffer); 读取数据到字符数组中 返回读取的字符数

int read(char[] buffer,int offset,int len); 读取指定长度的字符数据到数组中 返回读取的字符数

FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。

构造方法:

·FileReader(File file):在给定从中读取数据的File的情况下创建一个新的FileReader。

·FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新的FileReader。

BufferedReader类是Reader类的子类,为Reader对象添加字符缓冲器,为数据输入分配内存存储空间,存取数据更为有效。

构造方法:

·BufferedReader(Reader in):创建一个使用默认大小输入缓冲区的缓冲字符输入流。

·BufferedReader(Reader in,int sz):创建一个使用指定大小输入缓冲区的缓冲字符输入流。

操作方法:

·void close():关闭该流并释放与之关联的所有资源。

·void mark(int readAheadLimit):标记流中的当前为止。

·boolean markSupported();判断此流是否支持mark()操作。

·int read():读取单个字符。

·int read(char[] cbuf,int off,int len):将字符读入数组的某一部分。

·String readLine():读取一个文本行。

·boolean ready():判断此流是否已准备好被读取。

·void reset():将流重置到最新的标记。

·long skip(long n):跳过字符。

字符输出流

Writer类的常用子类为BufferedReader,接受Reader对象作为参数,并对其添加字符缓冲器。

构造:

FileWriter(File file);

FileWriter(String pathname);

FileWriter(String/File pathname,boolean append); 追加数据

常用方法:

void write(String str); 输出一个字符串

void write(String str,int offset,int len); 输出一个字符串的一部分

void write(char[] buffer); 输出一个字符数组

void writer(char[] buffer,int offset, int len); 输出一个字符数组的一部分

FileWriter:用来写入字符文件的便捷类,可用于写入字符流。

构造方法:

·FileWriter(File file):根据给定的File对象构造一个FileWriter对象。

·FileWriter(String filename):根据给定的文件名构造一个FileWriter对象。

BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

反射机制

在java的世界里,一切皆对象。其实从某种意义上说,在java中有两种对象:实例对象和Class对象。实例对象就是我们平常定义的一个类的实例:


//然后利用new关键字:
public class Person {
    public static void main(String[] args){
        Person p = new Person();
    }
}

而Class对象是没办法用new关键字得到的,因为它是jvm生成用来保存对应类的信息的,换句话说,当我们定义好一个类文件并编译成.class字节码后,编译器同时为我们创建了一个Class对象并将它保存.class文件中。我不知道这样描述是否妥当,因为我也见过某些书上直接把.class文件称之为Class对象。同时在jvm内部有一个类加载机制,即在需要的时候(懒加载)将.class文件和对应的Class对象加载到内存中。总之要有这样一个意识,Person.java文件编译成Person.class的同时也会产生一个对应的Class对象。

Class对象的获得

Class对象是jvm用来保存对象实例对象的相关信息的,除此之外,我们完全可以把Class对象看成一般的实例对象,事实上所有的Class对象都是类Class的实例。得到一个实例对象对应的Class对象有以下三种方式:

  1. 通过实例变量的getClass()方法:
    Dog dog = new Dog(); Class d = dog.getClass();

  2. 通过类Class的静态方法forName():

try {
           Class dog1 = Class.forName("Dog");
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }
  1. 直接给出对象类文件的.class:
Class dog2 = Dog.class;

Class对象的使用和反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简而言之,我们可以从.class逆向到.java(反编译),我们可以通过反射机制来访问一个类java对象的属性,方法,甚至我们可以轻易改变一个私有成员,看代码,我们先来定义一个Cat类:

class Cat{
    public static int count;
    public int age;
    private String name;
 
    static {
        count = 0;
    }
 
    public Cat(){
        age = count++;
        System.out.println("this is class Cat!");
 
    }
 
    public void run(){
 
    }
 
    private void ruff(){}
}

注意到我们的类中包含静态成员,私有变量,静态初始化以及私有方法。这里在提一下所谓的懒加载:当Cat.java编译成Cat.class文件后并不会立即被加载到内存,而是在它的的静态成员第一次被访问时才被加载(这么看来,Cat的默认构造方法也是静态的!)

Class c = Cat.class;
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields){
            System.out.println(field);
        }

结果如下:

public static int Cat.count
public int Cat.age
private java.lang.String Cat.name

可以看到我们轻而易举的得到了Cat类的字段信息,再来:

Method[] methods = c.getDeclaredMethods();
        for (Method method : methods){
            System.out.println(method);
        }

结果如下 :

public void Cat.run()
private void Cat.ruff()

我们竟然可以在运行时得到类的信息。同时我们发现Cat类中的静态初始化代码段并没有执行。接下来我们通过Class对象来获得对应的实例对象:

try {
            Cat cat = (Cat) c.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

这时候静态代码块执行了:

this is class Cat!

接下来我们做一件神奇的事情:

try {
            Class catClass = Class.forName("Cat");
            Field name = catClass.getDeclaredField("name");
            name.setAccessible(true);
            Cat cat2 = (Cat) catClass.newInstance();
            name.set(cat2,"Aristark");
            System.out.println(cat2.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

这次我们使用Class.forname()来获取Class对象,它的作用是让jvm查找并加载指定的类,也就是说Cat类的静态代码块会被执行。其次值得注意的是,我们通过Class的几个方法访问了原本不可以被访问的name属性:

this is class Cat!
Aristark

从这个意义上来说,反射机制并不符合OOP的思想,所以我们仅在必要的时候使用这个特性就行了。

以上内容可能会有出入,仅供参考,部分出自转载,欢迎大家踊跃留言指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值