递归就是“在一个方法内可以再次调用自身”,如下,method方法又调用了method方法。
package cn.demo.digui;
public class DiGui {
/*
* 递归: 方法自己调用自己 . public void fun(){ fun(); }
*/
public static void main(String[] args) {
story();
}
static int count = 2;
public static void story() {
count++;
System.out.println("东方红");
System.out.println("太阳升");
System.out.println("中国出了个毛泽东");
System.out.println("他为人民谋幸福 呼儿嗨哟 ");
System.out.println(count);
story();
}
}
上面的程序运行后,控制台会一直打印:调用method();当然,运行不久就会看到JVM抛出“堆栈溢出异常”——它类似于“无限循环”,但它比循环更消耗内存;
堆栈溢出异常如下:
递归方法开始调用后,在内存中会存在多个method方法,每个method方法均会等待它调用的method方法执行完毕,自己才会执行完毕。
2 递归的注意事项
-
递归调用必须要在某个条件下结束,否则就是死递归。
-
递归调用的层次不能太多,否则会堆栈溢出。
-
构造方法不能递归调用
IO概述
回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了。那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据读出来继续使用呢?其实要把数据持久化存储,就需要把内存中的数据存储到内存以外的其他持久化设备(硬盘、光盘、U盘等)上。
当需要把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作。
当把持久设备上的数据读取到内存中的这个动作称为输入(读)Input操作。
因此我们把这种输入和输出动作称为IO操作。
1 IO流分类
按照流向分:输入流与输出流,每个IO流对象均要绑定一个IO资源
按照类型分:常以字节流与字符流区分
分类关系如下:
-
字节流
-
字节输入流 InputStream 抽象类
-
FileInputStream 操作文件的字节输入流
-
-
字节输出流 OuputStream 抽象类
-
FileOutputStream 操作文件的字节输出流
-
-
-
字符流
-
字符输入流 Reader抽象类
-
InputStreamReader 输入操作的转换流
-
FileReader 用来操作文件的字符输入流(简便的流)
-
-
字符输出流 Writer抽象类
-
OutputStreamWriter 输出操作的转换流
-
FileWriter 用来操作文件的字符输出流(简便的流)
-
-
-
数据单位:
-
操作字节的流: 字节流 , 任何数据,底层都是字节. (文档,图片,mp3.....)
-
操作字符的流: 字符流 . 只能操作文本文件.
-
-
2 字节流
2.2.1 一切均为字节
在数据传输过程中,一切数据(文本、图像、声音等)最终存储的均为一个个字节,即二进制数字。所以数据传输过程中使用二进制数据可以完成任意数据的传递。
我们向一个文件中存储一定数据(一些数字),如果使用文本方式打开,则会以文本的方式解释数据。如果以视频的方式打开,则会以视频的方式解释数据。音频、可行执行文件等亦是如此。所以,在文件传输过程中,我们要时刻明确,传输的始终为数据。
2.2.2 字节输出流
输出流:OutputStream(抽象类):FileOutputStream(基本输出流)
构造方法:需要绑定IO资源
public FileOutputStream(String name) 创建覆盖写出对象
public FileOutputStream(String name,boolean append) 创建指定是否追加写出对象
其他方法: 写出时,如果没有该文件对象,会自动创建文件对象
-
-
write(int n):输出一个字节;(使用int替代了byte)
write(byte[] b):输出一个字节数组;
write(byte[] b, int off , int len):输出字节数组的一部分;
flush():刷新此输出流并强制写出所有缓冲的输出字节;
close(): 由于每个IO流都需要绑定一个IO资源,在使用时,需要回收资源
3 FileOutputStream类,文件输出流
OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。
FileOutputStream类,即文件输出流,是用于将数据写入 File的输出流。
2 FileOutputStream类写入数据到文件中
-
将数据写到文件中,代码演示如下:
-
package cn.demo.digui; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class Demo2 { public static void main(String[] args) throws IOException { // 需求:将数据写入到文件中。 // 创建存储数据的文件。 File file = new File("D:\\FileDemo\\01.txt"); // 创建一个用于操作文件的字节输出流对象。一创建就必须明确数据存储目的地。 // 输出流目的是文件,会自动创建。如果文件存在,则覆盖。 FileOutputStream fos = new FileOutputStream(file); // 调用父类中的write方法。 byte[] data = "我是中国人".getBytes(); fos.write(data); // 关闭流资源。 fos.close(); } }
输出结果如下:
4 给文件中续写和换行
我们直接new FileOutputStream(file)这样创建对象,写入数据,会覆盖原有的文件,那么我们想在原有的文件中续写内容怎么办呢?
继续查阅FileOutputStream的API。发现在FileOutputStream的构造函数中,可以接受一个boolean类型的值,如果值true,就会在文件末位继续添加。
package cn.demo.digui;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo3 {
public static void main(String[] args) throws IOException {
// fun();
fun2();
// 追加数据
// 创建存储数据的文件。
File file = new File("D:\\FileDemo\\02.txt");
// 创建流对象
FileOutputStream fos = new FileOutputStream("D:\\FileDemo\\02.txt", true); // true
// 表示可以追加
// 写出数据
fos.write(65);
// 关闭 资源
fos.close();
}
private static void fun2() throws FileNotFoundException, IOException {
//1.创建流对象
FileOutputStream fos = new FileOutputStream("D:\\FileDemo\\02.txt");
// 2.写出数据
String s = "hello io ";
byte[] bytes = s.getBytes();
fos.write(bytes);
// 3.关闭流资源
fos.close();
}
}
文本复制:这块很重要,在实际开发中,经常用到,在这里只是非常的简单,还请多多练习,
(一个人可以被毁灭,但不能被打败。人生中到处是坎坷,你若不努力,爱情与面包都是别人的成果,对自己加油,不放弃!没有学习计算机的天分,那就只能多多练习)
package cn.demo.digui;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 复制文本与图片
*/
public class CopyPhoto {
public static void main(String[] args) throws IOException {
// 1,明确源和目的。复制文本
File srcFile = new File("D:\\FileDemo\\02.txt");// D:\\FileDemo\\02.txt
File destFile = new File("D:\\FileDemo\\copyTest.txt");
// 复制图片
// File srcFile = new
// File("D:\\FileDemo\\008.png");//D:\\FileDemo\\008.png
// File destFile = new File("D:\\FileDemo\\copyTest.png");
// 2,明确字节流 输入流和源相关联,输出流和目的关联。
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
// 3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
int ch = 0;
while ((ch = fis.read()) != -1) {
fos.write(ch);
}
// 4,关闭资源。
fos.close();
fis.close();
}
}
输出结果如下图所示:
上述代码输入流和输出流之间是通过ch这个变量进行数据交换的。
上述复制文件有个问题,每次都从源文件读取一个,然后在写到指定文件,接着再读取一个字符,然后再写一个,一直这样下去。效率极低。
临时数组方式复制文件
上述代码复制文件效率太低了,并且频繁的从文件读数据,和写数据,能不能一次多把文件中多个数据都读进内容中,然后在一次写出去,这样的速度一定会比前面代码速度快。
package cn.demo.digui;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 复制文本与图片
*/
public class CopyPhoto2 {
public static void main(String[] args) throws IOException {
// 1,明确源和目的。复制文本,
File srcFile = new File("D:\\FileDemo\\03.txt");// D:\\FileDemo\\02.txt
//创建文件,如果文件不存在,创建true,如果文件存在,则创建false,如果文件路径错误,IOException.
//判断03.txt是否存在
boolean b1 = srcFile.exists();
System.out.println("b1="+b1);
//创建03.txt文本
boolean b2 = srcFile.createNewFile();
System.out.println("b2="+b2);
//复制的路径
File destFile = new File("D:\\FileDemo\\copyTest3.txt");
// 复制图片
// File srcFile = new
// File("D:\\FileDemo\\008.png");//D:\\FileDemo\\008.png
// File destFile = new File("D:\\FileDemo\\copyTest.png");
// 2,明确字节流 输入流和源相关联,输出流和目的关联。
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
/*// 3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
int ch = 0;
while ((ch = fis.read()) != -1) {
fos.write(ch);
}*/
//3.1定义一个缓冲区。
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);// 将数组中的指定长度的数据写入到输出流中,buf 表示 存数据的数组, 0 表示开始的位置, len 有效字符个数
}
// 4,关闭资源。
fos.close();
fis.close();
}
}