前篇链接
字符流读写文件:
package cn.cast.demo8;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
//需求:通过字符流读取数据
//1.创建字符输入流对象
Reader reader=new FileReader("lib/1.txt");
//2.读取数据
/* int ch1=reader.read();
System.out.println(ch1);//97
int ch2=reader.read();
System.out.println(ch2);//98
int ch3=reader.read();
System.out.println(ch3);//99
int ch4=reader.read();
System.out.println(ch4);//-1,文件内容读完了*/
//优化上述的读法,用循环改进,又因为不知道循环的次数,所以用while循环
int ch1;//定义变量用来接收读取到的字符
while((ch1=reader.read())!=-1){
//ch1= reader.read();
System.out.println("ch1 = " + ch1);
//97 98 99
}
//(ch1=reader.read())!=-1做了三件事
//1.执行reader.read(),去文件中读取一个字符
//2.ch1=reader.read()将读取到的字符复制给变量
//3.(ch1=reader.read())!=-1用读取到的字符和-1进行比较
//3.释放资源
reader.close();
}
}
package cn.cast.demo8;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
//需求:通过字符流读取数据,一次读取一个字符数组。
//1.创建字符输入流对象
Reader reader =new FileReader("lib/2.txt");
//2.读取数据
/*char[] chs=new char[3];//字符数组初始长度3
int len1=reader.read(chs);
System.out.println(chs);//abc
System.out.println(len1);//3
int len2=reader.read(chs);
System.out.println(chs);//def
System.out.println(len2);//3
int len3=reader.read(chs);
System.out.println(chs);//gef,只读取到了一个,后面两个保留了上一次读取的值,没有被覆盖
System.out.println(len3);//1,读取的有效字符数
int len4=reader.read(chs);
System.out.println(chs);//gef
System.out.println(len4);//-1
*/
//优化上述的代码,while循环
char[] chs=new char[3];//定义一个字符数组
int len;//定义一个变量记录读取到的有效字符数
while((len=reader.read(chs))!=-1){
//将读取到到的内容转换成字符串然后打印
String s =new String(chs,0,len);
//chs要操作的数组 0:起始索引 len表示要操作的字符的个数
System.out.println(s);
//System.out.println(len);
//abc
//def
//g
//若写法为:String s =new String(chs);==String s=new String(chs);
//abc
//def
//gef
}
//3.释放资源
reader.close();
}
}
字符流写数据
按单个字符写入
按字符数组写入
按字符串写入
write方法返回类型为void
package cn.cast.demo8;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriteDemo {
public static void main(String[] args) throws IOException {
//需求:通过字符流写数据
//1.创建字符输出流对象
Writer writer=new FileWriter("lib/1.txt");
//2.写数据
//一次写一个字符
//writer.write("好");//不是往文件后追加,而是会覆盖文件中的所有内容,执行完文件只剩好
//一次写一个指定的字符数组
char[] chs={'白','日','依','山','尽'};
//writer.write(chs);//写入全部字符
writer.write(chs,0,3);//第二个参数表示从哪个索引开始,第三个参数表示要操作的个数
//一次写一个字符串
writer.write("像手腕上散发的香水味");
//3.释放资源
writer.close();
}
}
write方法写入时,在第一次写入时,会把文件之前的内容给覆盖,但连续多次时则是追加在之前写入后的地方
字符流拷贝文件——按单个字符读写
package cn.cast.demo8;
import java.io.*;
public class CopyFile1 {
public static void main(String[] args) throws IOException {
//需求:通过字符流拷贝文件。一次读写一个字符
//将1.txt文件中的内容复制到2.txt中
/*
IO流拷贝文件核心六步:
1.创建字符输入流对象,关联数据源文件
2.创建字符输出流对象,关联目的地文件
3.定义变量,记录读取到的内容
4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
5.将读取到的数据写入到目的地文件中,
6.释放资源
*/
//1.
//Reader reader=new FileReader("lib/1.txt");多态,这次我们利用FileReader本身来接受
FileReader fr=new FileReader("lib/1.txt");
//2.
FileWriter fw=new FileWriter("lib/2.txt");
//细节,如果目的地文件不存在,程序会自动创建
//3.
int len;
//4.
while((len=fr.read())!=-1){
//5
fw.write(len);
}
//6.
fr.close();
fw.close();
}
}
定义字符串长度最好定义成1024的整数倍,因为计算机底层的换算单位就是1024
package cn.cast.demo8;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFile2 {
//需求:通过字符流拷贝文件,一次读写一个字符数组
//例如:将1.txt文件中的内容复制到2.txt中
/*
IO流拷贝文件核心六步:
1.创建字符输入流对象,关联数据源文件
2.创建字符输出流对象,关联目的地文件
3.定义变量,记录读取到的内容
4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
5.将读取到的数据写入到目的地文件中,
6.释放资源
*/
//1.
public static void main(String[] args) throws IOException {
//1
FileReader fr= new FileReader("lib/1.txt");
//2
FileWriter fw=new FileWriter("lib/2.txt");
//3
char[] chs=new char[1024];
int len;
//4
while((len=fr.read(chs))!=-1){
//5
fw.write(chs,0,len);//使用设置长度的写法,设置实际读取到的长度,否则就会出现下图中文件后出现大量NUL
}
//6
fr.close();
fw.close();
}
}
字符缓冲流普通用法
好处在于即使你没有手动定义字符数组,底层也会按照字符数组进行读写,默认了一个缓冲池,大小8192个字符,16kb,代码看起来像是按照一个字符一个字符读写,但实际上是按字符数组读写
package cn.cast.demo8;
import java.io.*;
public class Buffer {
/*
IO流拷贝文件核心六步:
1.创建字符输入流对象,关联数据源文件
2.创建字符输出流对象,关联目的地文件
3.定义变量,记录读取到的内容
4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
5.将读取到的数据写入到目的地文件中,
6.释放资源
*/
public static void main(String[] args) throws IOException {
//需求:通过字符缓冲流,将1.txt文件中的内容,拷贝到2.txt中
//1
//1.1创建普通的字符输入流对象
FileReader fr=new FileReader("lib/1.txt");
//1.2创建字符缓冲输入流对象
BufferedReader br=new BufferedReader(fr);//先有普通的输入流对象,再有缓冲流对象
//简化上述代码
//BufferedReader br2=new BufferedReader(new FileReader("lib/1.txt"));
//2
//2.1创建普通的字符输出流对象
FileWriter fw=new FileWriter("lib/2.txt");
//2.2创建字符缓冲输出流对象
BufferedWriter bw=new BufferedWriter(fw);
//3
int len;
//4
while((len=br.read())!=-1){
//5
bw.write(len);
}
//6
br.close();
bw.close();
}
}
字符流缓冲独有拷贝方式-一次拷贝一行
注意:字符流只能拷贝纯文本文件,(图片就不可以了,可以利用字节流来完成拷贝)
package cn.cast.demo8;
import java.io.*;
public class Buffer2 {
public static void main(String[] args) throws IOException {
//需求:通过字符缓冲流”一次读写一行“的方式,将1.txt文件中的内容,拷贝到2.txt中
//1
BufferedReader br=new BufferedReader(new FileReader("lib/1.txt"));
//2
BufferedWriter bw=new BufferedWriter(new FileWriter("lib/2.txt"));
//3
String str;
//4
while((str=br.readLine())!=null){
//5
bw.write(str);
//注意一个容易忽略的细节
//千万别忘记换行
//bw.write("\n");考虑不同平台下的编译情况换行符不同
bw.newLine();///这样做会使目的地文件末尾多一个换行符
}
//6
br.close();
bw.close();
}
}
字节流读写文件:
package cn.cast.demo8;
import java.io.*;
public class CopyFile1 {
public static void main(String[] args) throws IOException {
//需求:通过普通的字节流,一次读写一个字节的方式,将a.jpg复制到b.jpg中
//1.创建字节输入流关联数据源文件
FileInputStream fis = new FileInputStream("lib/1.jpg");
//2.创建字节输出流,关联目的地文件
FileOutputStream fos = new FileOutputStream("lib/2.jpg");//目的地文件不存在程序会自动创建
//3.定义变量,用来记录读取到的内容
int len;
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while ((len = fis.read()) != -1) {
//5.将读取到的内容写入到目的地中
fos.write(len);
}
//6.释放资源
fis.close();
fos.close();
}
}
package cn.cast.demo8;
import java.io.*;
public class CopyFile1 {
public static void main(String[] args) throws IOException {
//需求:通过普通的字节流,一次读写一个字节数组的方式,将a.jpg复制到b.jpg中
//1.创建字节输入流关联数据源文件
FileInputStream fis = new FileInputStream("lib/1.jpg");
//2.创建字节输出流,关联目的地文件
FileOutputStream fos = new FileOutputStream("lib/2.jpg");//目的地文件不存在程序会自动创建
//3.定义变量,用来记录读取到的内容
int len;//用来记录读取到的有效字节数
byte[] bytes=new byte[1024];
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while ((len = fis.read(bytes)) != -1) {
//5.将读取到的内容写入到目的地中
fos.write(bytes,0,len);//起始索引 要操作的有效字节数
}
//6.释放资源
fis.close();
fos.close();
}
}
高效字节流的用法:
好处:有自己内置的缓冲区,大小为8kb,你没有手动定义字节数组,底层也会按照字节数组进行读写
总结:拷贝纯文本文件使用字符流,拷贝其他(图片,音频,视频等)使用字节流,(字节流也能拷贝文本文件)
package cn.cast.demo8;
import java.io.*;
public class CopyFile1 {
public static void main(String[] args) throws IOException {
//需求:通过字节缓冲流,将a.jpg复制到b.jpg中
//1.创建字节输入流关联数据源文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("lib/1.jpg"));//也可以像字符流一样拆开写
//2.创建字节输出流,关联目的地文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("lib/2.jpg"));
//3.定义变量,用来记录读取到的内容
int len;//用来记录读取到的有效字节数
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while ((len = bis.read()) != -1) {
//5.将读取到的内容写入到目的地中
bos.write(len);//起始索引 要操作的有效字节数
}
//6.释放资源
bis.close();
bos.close();
}
}
实际应用:模拟文件上传功能
package cn.cast.demo9;
import java.io.*;
import java.util.Scanner;
public class UpLoadFile {
//需求:模拟用户上传头像的功能,假设所有的用户头像都应该上传到项目下的lib文件中
//1.定义一个方法,用来获取要上传的用户头像的路径 getPath()
//2.定义一个方法用来判断要上传的用户头像,在lib文件夹中是否存在。
//3.如果存在,提示:该用户头像已经存在,上传失败
//4.如果不存在,就上传该用户头像,并提示上传成功
public static void main(String[] args) throws IOException {
//1.定义一个方法,用来获取要上传的用户头像的路径 getPath()
File path=getPath();
System.out.println(path);
//2.定义一个方法用来判断要上传的用户头像,在lib文件夹中是否存在。
boolean flag=isExists(path.getName());//path.getName()只拿到路径的最后的名字
if(flag){
//3.如果存在,提示:该用户头像已经存在,上传失败
System.out.println("该用户头像已经存在,上传失败");
}else{
//4.如果不存在,就上传该用户头像,并提示上传成功
//数据源文件 目的地文件 (文件名一致)
//你上传的文件路径---> lib/(和上传的文件名一致)
UploadFile(path);
}
}
//1.定义一个方法,用来获取要上传的用户头像的路径 getPath()
/*
*用来获取要上传的用户头像的路径
* @return 具体要上传的用户头像的路径
*/
public static File getPath(){
//1.1.提示用户录入要上传的用户头像的路径,并接受
Scanner sc=new Scanner(System.in);
while(true) {
//1.7.因为不知道用户多少次能录入正确,所以用while(true)改进
System.out.println("请录入您要上传的用户头像的路径:");
String path = sc.nextLine();
//1.2.判断该路径的后缀名是否是:.jpg .png .bmp
//1.3.如果不是,就提示:您录入的不是图片,请重新录入:
if (!path.endsWith(".jpg") && !path.endsWith(".png") && !path.endsWith(".bmp"))//判断指定的字符串,是否是以给定的内容结尾的
{
//是三个同时不满足才成立,因为用或的话,直接检测了一个比如png不是jpg,也进入了这个循环,但他是正确的
System.out.println("您录入的不是图片,请重新录入");
//细节,千万注意别忘了写
continue;
}
//1.4.如果是,程序接着执行,判断该路径是否存在,并且是否是文件
File file = new File(path);
if (file.exists() && file.isFile()) {
//1.6.如果是,说明就是我们想要的数据(图片,文件),直接返回
return file;
} else {
//1.5.如果不是,就提示:您录入的路径不合法,请重新录入:
System.out.println("您录入的路径不合法,请重新录入:");
}
}
}
//2.定义一个方法用来判断要上传的用户头像,在lib文件夹中是否存在。
public static boolean isExists(String path){//想检验的只是文件是否存在,eg:1.png
//2.1.将lib文件夹封装成file对象
File file=new File("lib");
//2.2.获取lib文件夹中的所有文件(夹)的名称数组
String[] names=file.list();
//2.3.遍历第二部获取到的数组,用获取到的数组依次和path进行比较
for (String name : names) {
if(name.equals(path)){
//2.4.如果一致,说明该用户头像已经存在了,就返回true
return true;
}
}
return false;
//2.5.否则就返回false
}
//4.定义方法用来上传具体的用户头像
/*
用来上传具体的用户头像
@param path 数据源文件的路径
*/
public static void UploadFile(File path) throws IOException {
//1.创建字节输入流关联数据源文件
//FileInputStream类中的构造方法:
// public FileInputStream(File file)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));//也可以像字符流一样拆开写
//2.创建字节输出流,关联目的地文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("lib/"+path.getName()));
//3.定义变量,用来记录读取到的内容
int len;//用来记录读取到的有效字节数
//4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
while ((len = bis.read()) != -1) {
//5.将读取到的内容写入到目的地中
bos.write(len);//起始索引 要操作的有效字节数
}
//6.释放资源
bis.close();
bos.close();
System.out.println("上传成功");
}
}
22.反射
反射
一般的正常流程
获取字节码文件对象的三种方式
解释:一个后缀名为.java的文件对应一个后缀名为.class的字节码文件,一个字节码文件就对应一个class对象
当你第一次使用类中的成员时,类加载器就会将该类的字节码文件,加载到内存中
(一个类的字节码文件只会被加载一次)
package cn.cast.demo9;
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//需求:获取class对象
//方式1:
Student stu =new Student();
Class clazz1=stu.getClass();
//方式2:
Class clazz2=Student.class;
//方式3:
Class clazz3=Class.forName("cn.cast.demo9.Student");
//如何验证码三个Class对象是同一个对象:
System.out.println(clazz1==clazz2);//true
System.out.println(clazz2==clazz3);//true
//三个对象获取到的字节码文件是同一个
}
}
//Student是一个空类,里面什么都没有,只是用于测试效果
通过反射的方法获取构造方法并使用:
…表示可变参数,传任意个字节码文件都行
getConstructors返回的是一个数组:
package cn.cast.demo9;
public class Student {
public Student() {
}//公共的无参构造
public Student(String name) {
//公共的带参构造
System.out.println("录入的name的值是"+name);
}
private Student(int age) {
//私有的带参构造
System.out.println("录入的age的值是"+age);
}
}
package cn.cast.demo9;
import java.lang.reflect.Constructor;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception{//ClassNotFoundException, NoSuchMethodException {
//需求:通过过反射的方式创建Student类型的对象
//1.获取的Student类的字节码文件对象
Class clazz1=Class.forName("cn.cast.demo9.Student");
//2.根据第一步获取到的字节码文件对象,获取指定的构造器对象
/*//2.1获取公共的无参构造
Constructor con1=clazz1.getConstructor();
System.out.println("con1 = " + con1);//con1 = public cn.cast.demo9.Student()
//2.2获取公共的有参构造
Constructor con2=clazz1.getConstructor(String.class);//带参构造要的是一个string类型,需要传入一个string类型,又因参数需要的是string的字节码文件类型,所以利用string.class
System.out.println("con2 = " + con2);//con2 = public cn.cast.demo9.Student(java.lang.String)
//2.3获取私有的有参构造
Constructor con3=clazz1.getDeclaredConstructor(int.class);
System.out.println("con3 = " + con3);//con3 = con3 = private cn.cast.demo9.Student(int)
//2.4获取Student的所有公共构造函数
Constructor[] cons=clazz1.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
//public cn.cast.demo9.Student()
//public cn.cast.demo9.Student(java.lang.String)
}*/
Constructor con2=clazz1.getConstructor(String.class);//带参构造要的是一个string类型,需要传入一个string类型,又因参数需要的是string的字节码文件类型,所以利用string.class
System.out.println("con2 = " + con2);//con2 = public cn.cast.demo9.Student(java.lang.String)
//获取构造器的名字,看看他是那个类的构造
String name=con2.getName();
System.out.println("name = " + name);//name = cn.cast.demo9.Student
//3.根据构造器对象和参数,创建对应的Student对象
//Object obj=con2.newInstance("roxxane");//因为选中的第二个构造函数,需要我们传入一个string参数
//newinstance方法创建的是一个object类型的对象
Student stu=(Student) con2.newInstance("roxxane");
//录入的name的值是roxxane
//cn.cast.demo9.Student@30dae81
//4.打印结果
System.out.println(stu);
}
}
反射方式获取成员方法并使用:
obj是指对象名,args,调用方法时所需要的具体参数。
package cn.cast.demo9;
public class Student {
public void show1(){
System.out.println("公共的空参方法");
}
public void show2(int a){
System.out.println("公共的带参方法,传入值是"+a);
}
private int show3(int a,int b){
System.out.println("私有的带参方法");
return a+b;
}
}
package cn.cast.demo9;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
//需求:通过反射的方式获取Student类成员方法并调用
//1.获取student类的字节码文件对象
Class clazz=Class.forName("cn.cast.demo9.Student");
//2.(不能直接获取方法对象,只能通过对象名.的形式才能调用)获取该类的构造器对象,然后创建Student类的对象
Constructor con= clazz.getConstructor();
Student stu=(Student)con.newInstance();
//System.out.println(stu);//cn.cast.demo9.Student@b4c966a
//3.获取该类的成员方法对象,然后调用此方法。
//3.1调用公共的空参方法
Method method1=clazz.getMethod("show1");
//打印方法对象
System.out.println(method1);//public void cn.cast.demo9.Student.show1()
//打印方法名
System.out.println(method1.getName());//show1
//调用此方法
method1.invoke(stu);//公共的空参方法
//3.2调用公共的带参方法
Method method2=clazz.getMethod("show2",int.class);//show2需要一个int类型的参数,所以传入一个int类型的字节码文件
method2.invoke(stu,100);//公共的带参方法,传入值是100
//3.3调用私有的带参方法
Method method3=clazz.getDeclaredMethod("show3",int.class,int.class);
//开启暴力反射
method3.setAccessible(true);
int sum=(int)method3.invoke(stu,10,20);//直接用会报错,因为私有的不能直接调用,利用setAccessible开启暴力反射
System.out.println("sum = " + sum);
//私有的带参方法
//sum = 30
//3.4获取Student类中所有的成员方法
Method[] methods=clazz.getMethods();
for (Method method : methods) {
System.out.println("method = " + method);
//method = public void cn.cast.demo9.Student.show2(int)
//method = public void cn.cast.demo9.Student.show1()
//method = public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
//method = public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
//method = public final void java.lang.Object.wait() throws java.lang.InterruptedException
//method = public boolean java.lang.Object.equals(java.lang.Object)
//method = public java.lang.String java.lang.Object.toString()
//method = public native int java.lang.Object.hashCode()
//method = public final native java.lang.Class java.lang.Object.getClass()
//method = public final native void java.lang.Object.notify()
//method = public final native void java.lang.Object.notifyAll()
//有这么多因为所有的类都直接或间接继承Object,除了本身定义的方法,还有继承的方法
}
}
}
package cn.cast.demo9;
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
package cn.cast.demo9;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
//需求:通过反射获取类的setter方法,使用该方法为属性赋值
//1.通过反射获取Student类的字节码文件对象
Class clazz=Class.forName("cn.cast.demo9.Student");
//2.通过反射获取student类的构造方法,并创建该类的对象
Constructor con= clazz.getConstructor();
Student stu=(Student)con.newInstance();
//3.获取到指定的setName方法,给Student对象设置值
Method method=clazz.getMethod("setName",String.class);
method.invoke(stu,"roxanne");
//4.打印学生对象
System.out.println(stu);
}
}
反射获取成员变量并使用
类的加载时机:当你第一次使用类的成员,但类还没有在内存中存在时,类加载器就会在内存中加载他的字节码文件