java基础(5)
1.异常
概述:程序出现了不正常的情况
程序的异常:Throwable,包含java中所有的问题和异常
- 严重问题:Error,我们不处理,这种问题一般都是很严重的,比如说内存溢出
- 一般问题:Exception
- 编译期问题:不是RuntimeException的异常,必须进行处理,因为不处理,编译就不能通过
- 运行期问题:RuntimeException 这种问题我们不处理,因为是你的问题,而且这个问题出现肯定是我们的代码不够严谨,需要修正代码的
JVM默认是如何处理异常的
如果程序出现了问题,我们没有做任何处理,最终jvm就会做出默认的处理:把异常的名称,原因及出现问题的行号等信息输出在控制台,同时会结束程序
自己如何处理异常
1.try...catch...finally:
一个异常的处理格式为:
try{
可能出现问题的代码;
}catch(异常名 变量名){
针对问题的处理;
}finally{
一定要执行的语句体;
}
过程:在try里面发现问题后,jvm会帮我们生成一个异常对象,然后把这个异常抛出,和catch里面的异常类进行匹配,如果该对象是某个类型的,就会执行该catch里面的处理信息
注意:
- try里面的代码越少越好
- catch里面必须要有东西,不然就不是捕捉异常,而是在隐藏异常了
多个异常的处理格式为:
A方法:每一个异常写一个try...catch
B方法:
try{
...
}catch(异常名 变量名){
...
}catch(){
...
}...
C方法(jdk7才有的):
try{
...
}catch(异常名1|异常名2|异常名3... 变量名){
...
}
C方法的两个弊端:
1.处理方式是一致的,针对的是同一类型的问题,给出同一个处理
2.多个异常间必须是平级关系
注意:
- 能明确的尽量明确,不要用大的异常类来处理
- 平级关系的异常谁前谁后无所谓,但是如果是子父关系,夫必须在后面
2.throws(常用于方法的异常抛出)
概述:有些时候,我们时可以对异常进行处理的,但是又有些时候,我们根本就没有权限去处理某个异常,或者说,我处理不了,我就不处理了,为了解决这种情况,java就提供了另一种处理方案:抛出(throws)
格式:throws 异常类名(可以多个)
注意:
- 尽量不要在main方法商抛出异常
- 编译期异常抛出,将来调用者必须处理,运行期异常抛出,调用者可以不用处理
编译期异常与运行期异常的区别
编译期异常:java程序必须显示处理(try...catch...捕捉异常),否则程序就会发生错误,无法通过编译;
运行时异常:无需显示处理,也可以和编译异常一样处理(try...catch...捕捉异常)
Throwable的几个常见方法
1.public String getMessage():返回该异常的消息字符串
2.public String toString():返回该异常的简单信息描述,信息描述部分由以下几部分构成:
- 此对象的类名(包括包名的全路径类名)
- ": "(冒号和一个空格)
- 调用次对象的getLocalizedMessage()方法的结果(默认返回的是getMessage()的内容)
3.public void printStackTrace():获取异常名和异常信息,以及异常出现在程序中的位置,把消息输出在控制台,它与由jvm抛出异常显示在控制台的显示内容一样,但是通过这个方法,后面的程序仍可以继续走
throw关键字
概述:如果出现了异常情况,我们可以把该异常抛出,这个时候的抛出的应该是异常的对象(只能一个),而不是类名
throws与throw的区别:
throws:
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示异常抛出,由该方法的调用者来处理
- throws表示的是出现异常的一种可能性,并不一定会发生
throw:
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常
- 表示抛出异常,由方法体内的语句处理
- throw表示抛出异常,执行throw则一定抛出了某种异常
finally关键字
概述:被finally控制的语句体一定会执行,但是在执行finally之前jvm推出了(比如System.exit(0))
finally的作用:用于释放资源,在IO流操作和数据库操作中可以见到
finally面试题:
1.final,finally,finalize的区别?
答:final:可以修饰类,成员变量,成员方法,修饰类,类不能被继承,修饰成员变量,变量是常量,修饰成员方法时,方法不能被重写
finally:是异常处理的一部分,用于释放资源,一般来说,代码肯定会执行,特殊情况下,如在执行到finally之前jvm推出了
finalize:是Object类的一个方法,用于垃圾回收
2.如果catch里面有return语句,请问finally里面的代码还会执行么?如果会,请问是return前,还是return后?
答:会,在return前(准确的说是在中间)
看这个面试题的示例代码:
public class Demo2 {
public static void main(String[] args) {
System.out.println(method());//30
}
public static int method(){
int a = 10;
try{
System.out.println(a / 0);
}catch(ArithmeticException e){
a = 30;
/*
* return a 在程序执行到这一步的时候,这里不是return a而是return 30,
* 这个返回路径就形成了,但是后面又看到了finally,所有继续执行finally的内容,a = 40
*/
return a;
}finally{
a = 40;
}
return a;
}
}
//结果:30
自定义异常
概述:java不可能对所有情况都考虑到,所以,在实际的开发中,我们可能需要自己定义异常,而我们随意写的一个类,是不能作为异常类来看的,要想你的类是一个异常类,就必须继承自Exception或RuntimeException
自定义异常示例:
public class MyException extends Exception{
public MyException(){
}
public MyException(String message){
super(message);
}
}
异常的注意事项:
1.子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类
2.如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是它的子类,子类不能抛出父类没有的异常
3.如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常,那么子类只能try,不能throws
2.File类
概述:我们要想实现IO的操作,就必须知道硬盘上文件的表现形式,而java就提供了一个类File供我们使用
File:文件和目录路径名的抽象表示方法
构造方法
1.File (String pathname):根据一个路径得到File对象
2.File (String parent, String child):根据一个目录和一个子文件得到File对象
3.File (File parent, String child):根据一个父File对象和一个子文件得到File对象
注意:如果你创建文件或者文件夹的时候没有填写盘符路径,那么,默认在项目路径下
成员方法
1.创建功能:
- public boolean createNewFile():创建文件,如果文件存在,就不创建了,返回false
- public boolean mkdir():创建文件夹,如果存在这样的文件夹,就不创建了,返回false
- public boolean mkdirs():创建多层次文件夹,如果父文件夹不存在,也会帮你创建出来,如果存在这样的文件夹,就不创建了,返回false
2.删除功能
- public boolean delete():可以删除文件或文件夹,java中的删除不走回收站,如果要删除一个文件夹,文件夹里面不能有文件或者文件夹
3 .重命名功能
- public boolean renameTo(File dest):如果路径相同,就是改名,如果路径不同,就是改名并剪切
4 . 判断功能
- public boolean isDirectory():判断是否是目录
- public boolean isFile():判断是否是文件
- public boolean exists():判断是否存在
- public boolean canRead():判断是否可读
- public boolean canWrite():判断是否可写
- public boolean isHidden():判断是否隐藏
5.获取功能
- public String getAbsolutePath():获取绝对路径
- public String getPath():获取相对路径(相对于当前项目的路径)
- public String getName():获取名称
- public String length:获取长度,字节数
- public long lastModified():获取最后一次的修改机会,毫秒值
- public String[] list():获取指定目录下的所有文件或者文件夹的名称数组,list()括号里面还可以放一个文件过滤器对象,实现的是FilenameFilter接口
- public File[] listFiles():获取指定目录下的所有文件或者文件夹得File数组,listFiles()括号里面也可以放一个文件过滤器对象,实现的是FilenameFilter接口
案例代码(获取d盘下的.jpg文件):
/*
* 获取d盘下的.jpg文件
*/
//第一种做法:先遍历所有的文件或问价夹再挑出满足条件的,再输出到控制台
import java.io.File;
public class Demo1 {
public static void main(String[] args) {
//封装d盘目录
File file = new File("d:\\");
//获取该目录下所有文件或者文件夹的File对象
File[] fileArray = file.listFiles();
for(File f:fileArray){
//判断是否是文件
if(f.isFile()){
//判断文件是否以.jpg结尾
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());
}
}
}
}
}
//第二种方法:先过滤出满足条件的,在显示到控制台
import java.io.File;
import java.io.FilenameFilter;
public class Demo2 {
public static void main(String[] args) {
File file = new File("d:\\");
//通过文件名称过滤器实现
String[] strArray = file.list(new FilenameFilter(){
@Override
public boolean accept(File dir, String name) {
//这两个条件都为true才会把文件或文件夹添加到数组中,这里的dir是盘符路径,后面的name为文件名
return new File(dir, name).isFile() && name.endsWith(".jpg");
}
});
for(String s: strArray){
System.out.println(s);
}
}
}
3.递归
概述:递归就是方法定义中调用方法本身的现象,需要注意的是方法的嵌套不是递归
注意事项:
- 递归一定要有出口,否则就是死递归
- 递归的次数不能太多,否则就内存溢出
- 构造方法不能递归使用
如何实现递归?
- 找规律和出口条件
- 写一个方法
- 写出口条件
代码示例1(求阶乘):
public class Demo3 {
public static void main(String[] args) {
System.out.println("5的阶乘为:" + factorial(5));
}
public static int factorial(int n){
if(n == 1){
return 1;
}else{
return n * factorial(n - 1);
}
}
}
代码示例2(斐波那契数列):
public class Demo4 {
public static void main(String[] args) {
System.out.println("第20个斐波那契数列值为:" + fib(20));//第20个斐波那契数列值为:6765
}
public static int fib(int n){
if(n == 1 || n == 2){
return 1;
}else{
return fib(n - 1) + fib(n - 2);
}
}
}
代码示例3(递归输出指定目录下的所有.java文件的绝对路径):
import java.io.File;
public class Demo5 {
public static void main(String[] args) {
//封装目录
File file = new File("d:\\javaProj");
//递归功能实现
getAllJavaFilePaths(file);
}
public static void getAllJavaFilePaths(File source){
//获取该目录下所有文件或者文件夹的File数组
File[] fileArray = source.listFiles();
for(File f: fileArray){
if(f.isDirectory()){
//如果是文件夹,就调用自身方法
getAllJavaFilePaths(f);
}else {
if(f.getName().endsWith(".java")){
System.out.println(f.getAbsolutePath());
}
}
}
}
}
4.IO流
概述:用来设备间的数据传输问题
IO流的分类
按流向分:
1.输出流:指的是从内存写内容到硬盘中
2.输入流:指的是从硬盘读取内容到内存
按数据类型分:
1.字节流:可以处理任何一种文件数据(文本,图片,声音,视频等),字节流分为字节输入流和字节输出流
- 字节输入流:读取数据:InputStream(抽象类)
- 字节输出流:写数据:OutputStream(抽象类)
2.字符流:只能处理纯文本文件,字符流也分为字符输入流和字符输出流
- 字符输入流:读取数据:Reader(抽象类)
- 字符输出流:写数据:Writer(抽象类)
5.字节流操作
FileOutputStream写入文件
概述:使用字节流往文件里写数据
创建字节输出流对象做了几件事情:
- 调用系统功能去创建文件(如果没有,则创建文件,有则直接使用)
- 创建字节输出流对象对象
- 把该字节输出流对象对象指向这个文件
构造方法:
- FileOutputStream(File file)
- FileOutputStream(String name)
字节输出流的操作步骤:
- 创建字节输出流对象
- 写数据
- 释放资源
- 让流对象变成垃圾,这样就可以被垃圾回收器回收了
- 通知系统去释放跟该文件相关的资源
字节输出流的三个write方法:
- public void write(int b):写一个字节
- public void write(byte[] b):写一个字节数组
- public void write(byte[] b, int offset, int len):写一个字节数组的一部分
写数据时如何实现换行呢?
换行其实也是一种字符,叫做“换行符”
不同的操作系统换行符的表示也不同:
- Windows:\r\n
- macOS:早期为\r,现在是\n
- Linux:\n
因此,我们在Windows上开发程序时,往文件里写入字符“\r\n”即可
如何实现追加写入?
我们的构造方法创建出来的字节输出流对象,使用这个字节输出流对象写数据时默认是在文件开始处写内容,这样就是重写了文件的内容,而其实我们的构造方法还有第二个为boolean值的参数,true
代表的是对文件进行追加操作,即在文件末尾处写入内容,false则是默认的重写
代码示例:
//没有做异常处理的版本
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6 {
public static void main(String[] args) throws IOException {
//创建字节流输出对象
FileOutputStream fos = new FileOutputStream("text.txt");
//往文件里写内容
fos.write("Hello, world".getBytes());
//释放资源
fos.close();
}
}
//有做异常处理的版本
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo6 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = null;
try{
//创建字节流输出对象
fos = new FileOutputStream("text.txt");
//往文件里写内容
fos.write("Hello, world,java".getBytes());
}catch(FileNotFoundException e){
e.printStackTrace();
}finally{
try{
//释放资源
if(fos != null){
fos.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
FileInputStream读取文件
概述:用于读取文件,可以类比FileOutputStream来学习
使用步骤:
- 创建字节流输入流对象
- 调用read()方法读取数据,并把数据显示到控制台
- 释放资源
两个read方法:
- int read()
- int read(byte[] b):返回实际读取字符的个数
示例代码1(读取文件):
import java.io.FileInputStream;
import java.io.IOException;
public class Demo7 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("text.txt");
int by = 0;
//如果读取不到数据了,就会返回-1
while((by = fis.read()) != -1){
//得到的是一个int类型的数字,需要把他转换为char类型,但是这里只能转换非中文字符,因为中文字符在gbk中用两个字节表示
System.out.print((char)by);
}
fis.close();
}
}
示例代码2(复制文本到另一个文件):
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo8 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("text2.txt");
FileInputStream fis = new FileInputStream("text1.txt");
int by = 0;
/*
* 这里读取并写入的文件里面的内容既可以是英文,也可以是中文,
* 因为gbk编码存储中文是用两个字节的,而第一个字节肯定是负数,
* 第二个字节常见的是负数,可能是整数,但是没影响;
*/
while((by = fis.read()) != -1){
fos.write(by);
}
//谁先close无所谓
fos.close();
fis.close();
}
}
示例代码3(一次读取多个字节)
import java.io.FileInputStream;
import java.io.IOException;
public class Demo9 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("text1.txt");
//这里的 byte数组长度最好为1024或者1024的倍数
byte[] bys = new byte[1024];
int len = 0;
//读取完了之后,返回的读取的长度为-1
while((len = fis.read(bys)) != -1){
System.out.println(new String(bys, 0, len));
}
fis.close();
}
}
缓冲区类
概述:通过定义数组的方式确实比以前一次读取一个字节的方式快很多,所以,看来有一个缓冲区还是非常好的,既然这样,那么java开始设计的时候,他也考虑到了这一点,因此提供了我们带缓冲区的字节类,这种类被称为缓冲区类(高效类)
写数据:BufferedOutputStream
- 构造方法:BufferedOutputStream(OutputStream out)
读数据:BufferedInputStream
- 构造方法:BufferedInputStream(InputStream in)
以上这两个缓冲区类的构造方法可以指定缓冲区大小,但是我们一般用不上,因为默认缓冲区大小就足够了,构造方法里传一个OutputStream或一个InputStream对象
为什么不传递一个具体的文件或者文件路径,而是传一个OutputStream对象呢?
答:因为字节缓冲区流仅仅提供缓冲区,为高效而设计的,但是,真正的读写操作还得靠基本的流对象实现
实例代码(四种方式实现复制文本到另一个文件的耗时):
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo10 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
method1("video1.mp4", "video2.mp4");
//method2("video1.mp4", "video2.mp4");
//method3("video1.mp4", "video2.mp4");
//method4("video1.mp4", "video2.mp4");
long end = System.currentTimeMillis();
System.out.println("花费了" + (end - start) + "s");
}
public static void method1(String srcString, String destString) throws IOException{
FileOutputStream fos = new FileOutputStream(destString);
FileInputStream fis = new FileInputStream(srcString);
int by = 0;
while((by = fis.read()) != -1){
fos.write(by);
}
fos.close();
fis.close();
};
public static void method2(String srcString, String destString) throws IOException{
FileOutputStream fos = new FileOutputStream(destString);
FileInputStream fis = new FileInputStream(srcString);
byte[] bys = new byte[1024];
int len = 0;
while((len = fis.read(bys)) != -1){
fos.write(bys, 0, len);
}
fos.close();
fis.close();
};
public static void method3(String srcString, String destString) throws IOException{
FileOutputStream fos = new FileOutputStream(destString);
FileInputStream fis = new FileInputStream(srcString);
BufferedOutputStream bos = new BufferedOutputStream(fos);
BufferedInputStream bis = new BufferedInputStream(fis);
int by = 0;
while((by = bis.read()) != -1){
bos.write(by);
}
bos.close();
bis.close();
};
public static void method4(String srcString, String destString) throws IOException{
FileOutputStream fos = new FileOutputStream(destString);
FileInputStream fis = new FileInputStream(srcString);
BufferedOutputStream bos = new BufferedOutputStream(fos);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
}
6.转换流
概述:由于字节流操作中文不是特别方便,所有,java就提供了转换流
字符流 = 字节流 + 编码表
编码表
概述:由现实世界的字符和对应的数值组成的一张表
ASCII码表:用一个字节表示一个字符,最高位为符号位,其余为数值位
ISO-8859-1:拉丁码表,8位表示一个数据
GB2312:中国的中文编码表
GBK:中国的中文编码升级,融合了更多的中文文字符号
GBK18030:GBK的取代版本
BIG-5码:通行于台湾,香港地区的一个繁体字编码方案,俗称“大五码”
Unicode:国际标准码,融合了多种文字,所有文字都用两个字节来表示,java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符,能用一个的就用一个(ASICC兼容),一个表示不了,就用两个,两个不行,就用三个
字符串的编码和解码问题
编码:从文字图形转化为二进制数字的过程
解码:从二进制数字转化为文字图形的过程
主要编码和解码的格式一样,就不会出现乱码等问题
代码示例:
import java.io.UnsupportedEncodingException;
public class Demo11 {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "你好";
byte[] bys = s.getBytes("utf-8");
//byte[] bys = s.getBytes("gbk");
//String ss = new String(bys, "gbk");
String ss = new String(bys, "utf-8");
System.out.println(ss);
}
}
转换流OutputStreamWriter和InputStreamReader的使用
概述:使用转换流可以指定编码,就可以很方便的往文件里写中文了,转换流本身也是字符流
OutputStreamWriter的五种写数据的方式
- public void write(int c):写一个字符
- public void write(char[] cbuf):写一个字符数组
- public void write(char[] cbuf, int off, int len):写一个字符数组的一部分
- public void write(String str):写一个字符串
- public void write(String str, int off, int len):写一个字符串的一部分
close()和flush()方法的区别:close()关闭流对象,但是先刷新一次缓冲区,关闭之后,流对象不可以继续使用;flush()方法仅仅刷新缓冲区,刷新之后,仍然可以使用
OutputStreamWriter的使用实例:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class Demo12 {
public static void main(String[] args) throws IOException {
//指定了编码为utf-8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
"text1.txt"), "utf-8");
osw.write("中国");
osw.close();
}
}
InputStreamReader的两种读数据的方式:
- int read():一次读取一个字符
- int read(char[] chs):一次读取一个字符数组
InputStreamReader的使用实例:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class Demo12 {
public static void main(String[] args) throws IOException {
//指定了编码为utf-8
InputStreamReader isr = new InputStreamReader(new FileInputStream(
"text1.txt"), "utf-8");
int ch = 0;
while((ch = isr.read()) != -1){
System.out.print((char)ch);
}
isr.close();
}
}
转换流的子类FileWriter和FileReader(本身就是字符流)
概述:由于我们常见的操作都是使用本地默认编码,所以,不用指定编码,而转换流的名称有点长,所以,java就提供了其子类(FileWriter和FileReader)供我们使用
OutputStreamWriter = FileOutputStream + 编码表(默认是gbk)
FileWriter = FileOutputStream + 编码表(默认是gbk)
InputStreamReader = FileInputStream +编码表(默认是gbk)
FileReader = FileInputStream +编码表(默认是gbk)
FileWriter和FileReader的使用代码示例:
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo13 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("text2.txt");
FileReader fr = new FileReader("text1.txt");
/*int ch = 0;
while((ch = fr.read()) != -1){
fw.write(ch);
}*/
char[] chs = new char[1024];
int len = 0;
while((len = fr.read(chs)) != -1){
fw.write(chs, 0, len);
fw.flush()
}
fw.close();
fr.close();
}
}
字符缓冲流(BufferedReader和BufferedWriter)
概述:从字符流中读取数据或写数据,缓冲各个数据,从而实现字符、数组和行的高效读取或写数据,可以指定缓冲区大小,或者可以使用默认的大小,大多数情况下,默认值就足够大了。可以类比字节缓冲流的BufferedOutputStream和BufferedInputStream来学习
代码示例(使用字符缓冲流复制文本到另一个文件):
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo14 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("text2.txt"));
BufferedReader br = new BufferedReader(new FileReader("text1.txt"));
char[] chs = new char[1024];
int len = 0;
while((len = br.read(chs)) != -1){
bw.write(chs, 0, len);
bw.flush();
}
bw.close();
br.close();
}
}
字符缓冲流的特殊功能:
BufferedWriter:
- public void newLine():根据系统来决定换行符
BufferedReader:
- public void readLine():一次读取一行数据,读到文件末尾时,返回null,而不是-1
代码示例1(使用特殊功能复制文本到另一个文件):
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo15 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("text2.txt"));
BufferedReader br = new BufferedReader(new FileReader("text1.txt"));
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
代码示例2(复制多级文件夹)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo16 {
public static void main(String[] args) throws IOException {
//封装数据源File
File srcFile = new File("D:\\java学习资料\\day01\\code");
//封装目的地File
File destFile = new File("D:\\");
//复制文件夹
copyFolder(srcFile, destFile);
System.out.println("操作成功");
}
//复制文件夹的方法
public static void copyFolder(File srcFile, File destFile) throws IOException {
if(srcFile.isDirectory()){
//判断是否为一个文件夹
File newFolder = new File(destFile, srcFile.getName());
newFolder.mkdir();
//获取目标盘符下的所有文件和文件夹
File[] fileArray = srcFile.listFiles();
for(File f:fileArray){
//遍历获取到的所有文件和文件夹,调用自己
copyFolder(f, newFolder);
}
}else{
File newFile = new File(destFile, srcFile.getName());
//调用复制文件的方法
copyFile(srcFile, newFile);
}
}
//复制文件到目标目录的方法
public static void copyFile(File srcFile, File newFile) throws IOException{
BufferedReader br = new BufferedReader(new FileReader(srcFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(newFile));
String line = null;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
}
}
LineNumberReader获取行号:BufferedReader的子类
- public int getLineNumber():获取当前行号
- pbblic void setLineNumber(int lineNumber):设置当前行号从lineNumber + 1开始
7.其他流(了解)
数据输入输出流
概述:可以对基本数据类型进行读写操作
数据输出流:DataOutputStream
- 构造方法:DataOutputStream(OutputStream out)
数据输入流:DataIntputStream
- 构造方法:DataInputStream(InputStream in)
示例代码:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo17 {
public static void main(String[] args) throws IOException {
write();
//read();
}
private static void read() throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("text1.txt"));
short s = dis.readShort();
int i = dis.readInt();
long l = dis.readLong();
byte by = dis.readByte();
float f = dis.readFloat();
double d = dis.readDouble();
char c = dis.readChar();
boolean b = dis.readBoolean();
dis.close();
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(by);
System.out.println(f);
System.out.println(f);
System.out.println(c);
System.out.println(b);
}
public static void write() throws IOException{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("text1.txt"));
dos.writeShort(100);
dos.writeInt(100);
dos.writeLong(1000);
dos.writeByte(97);
dos.writeFloat(10.0f);
dos.writeDouble(100.00);
dos.writeChar('a');
dos.writeBoolean(true);
dos.close();
}
}
内存操作流
概述:用于处理临时存储信息的,程序结束,数据就从内存消失,内存操作流的close方法没有什么作用,可以不写close方法
内存操作流可以操作一下内容:
字节数组:
- ByteArrayInputStream
- ByteArrayOutputStream
字符数组:
- CharArrayReader
- CharArrayWriter
字符串:
- StringReader
- StringWriter
打印流
特点:
- 只有写数据,没有读数据,只能操作目的地,不能操作数据源
- 可以操作任意类型的数据
- 如果启动了自动刷新,就可以不用手动刷新了
- 该流是可以直接操作文本文件的(查看流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的)
打印流:
- 字节流打印流:PrintStream,构造方法的第二个参数为true时,可以实现自动刷新(使用println方法才能自动刷新才能达到效果)
- 字符打印流:PrintWriter,构造方法的第二个参数为true时,可以实现自动刷新(使用println方法自动刷新才能达到效果)
实例代码(复制文件打印流版):
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo18 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("text1.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("text2.txt"), true);
String line = null;
while((line = br.readLine()) != null){
pw.println(line);
}
pw.close();
}
}
标准输入输出流
System类中的两个成员变量:
- public static final InputStream in:标准输入流
- public static final PrintStream out:标准输出流
System.in其实就是一个
InputStream对象,而System.out则是一个PrintStream对象,由此,可以得知,输出语句的本质其实就是IO流操作,把数据输出到控制台
实现控制台输出的另一种方式:
- BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
- bw.write("hello");
- bw.flush();
三种方式实现键盘录入
- main方法的args接收参数
- Scanner(JDK之后的)
- 通过字符缓冲流包装标准输入流实现
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
- br.nextLine()
随机访问流RandomAccessFile
概述:RandomAccessFile类不属于流,是Object类的子类,但他融合了InputStream和OutputStream的功能,支持对随机访问文件的读取和写
构造方法:
- public RandomAccessFile(String name, String mode):第一个参数是文件路径,第二个参数是操作文件的模式,模式有四种,我们最常用的是“rw
”,这种方式表示我们既可以写数据,也可以读数据
代码示例:
import java.io.IOException;
import java.io.RandomAccessFile;
public class Demo20 {
public static void main(String[] args) throws IOException {
//write();
read();
}
private static void read() throws IOException {
RandomAccessFile raf = new RandomAccessFile("text1.txt", "rw");
String s = raf.readUTF();
System.out.println(s);//中国
//utf8编码中中文一个字符占三个字节,而又因为readUTF()方法需要多加两个字节,所以指针指向为8
System.out.println("此时指针的位置在:" + raf.getFilePointer());//8
int i = raf.readInt();
System.out.println(i);//97
//int类型需要4个字节,加前面的8,所以为12
System.out.println("此时指针的位置在:" + raf.getFilePointer());//12
}
private static void write() throws IOException {
RandomAccessFile raf = new RandomAccessFile("text1.txt", "rw");
raf.writeUTF("中国");
raf.writeInt(97);
raf.close();
}
}
合并流SequenceInputStream
概述:SequenceInputStream类可以将多个输入流串流在一起,合并为一个输入流
构造方法:
- SequenceInputStream(InputStream s1, InputStream s2):针对两个流合并
- SequenceInputStream(Enumeration<? extends InputStream> e):针对多个流合并
示例代码1(合并流读取两个文件的内容复制到一个文件中):
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
public class Demo21 {
public static void main(String[] args) throws IOException {
InputStream is1 = new FileInputStream("text1.txt");
InputStream is2 = new FileInputStream("text2.txt");
SequenceInputStream sis = new SequenceInputStream(is1, is2);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("text3.txt"));
byte[] bys = new byte[1024];
int len = 0;
while((len = sis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
示例代码2(合并流读取多个文件的内容复制到一个文件中):
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;
public class Demo22 {
public static void main(String[] args) throws IOException {
Vector<InputStream> v = new Vector<InputStream>();
InputStream is1 = new FileInputStream("text1.txt");
InputStream is2 = new FileInputStream("text2.txt");
InputStream is3 = new FileInputStream("text3.txt");
v.add(is1);
v.add(is2);
v.add(is3);
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis= new SequenceInputStream(en);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("text4.txt"));
byte[] bys = new byte[1024];
int len = 0;
while((len = sis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bos.close();
sis.close();
}
}
序列化流ObjectOutputStream和ObjectInputStream
概述:把对象按照流一样的方式存入文本文件或在网络中传输,写和读取的是一个对象,而且这个对象的对应的类需要具有序列化标记接口Serializable,不然在使用的时候,就会抛出NotSerializableException异常
构造方法
- public ObjectOutputStream(OutputStream out)
- public ObjectInputStream(InputStream in)
transient关键字:被这个关键字修饰的变量就不会被序列化了
使用实例:
//Student.java
import java.io.Serializable;
//实现Serializable接口
public class Student implements Serializable{
//生成id值防止下次改动java文件时没有重写写入而导致错误
private static final long serialVersionUID = -7128679398892529111L;
private String name;
private int age;
public Student(){};
public Student(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
//Demo23.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Demo23 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//write();
read();
}
private static void read() throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("text1.txt"));
Object obj = ois.readObject();
ois.close();
System.out.println(obj);
}
private static void write() throws IOException, IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("text1.txt"));
Student stu1 = new Student("luyi", 19);
oos.writeObject(stu1);
oos.close();
}
}
注意:当我们修改Student.java文件中的内容时,当我们再次读取的时候,就会报错,这是因为Student类实现了序列化接口,那么它本身也应该有一个标记值,对class文件进行标记,而当我们修改了Student类的内容后,它的标记值就会发生改变,此时,如果你没有重写写一次,而是直接读取的话,就会报错,所以我们如果修改了Student类的内容的时候,就需要重新写一次;但我们在实际开发中,可能需要使用以前写过的数据,不能重新写入,怎么办呢?这时候就可以让这个标记值在java文件中是一个固定的值,这样,你修改文件的时候,这个标记值就不会发生改变了。而生成这个固定的标记值只需把鼠标移动到Student类上黄色警告线上,就可以点击生成了
8.Properties集合
概述:属性集合类,是一个可以和IO流相结合使用的集合类,可以保存在流中或从流中加载,属性列表中每个键及其对应值都是一个字符串,它是Hashtable的子类,说明它是一个Map集合,也可以作为一个Map集合来使用,但是它不是一个泛型类,不能加泛型
Properties自己独有的特殊方法:
- public Object setProperty(String key, String value):添加元素
- public String getProperty(String key):获取元素
- public Set
stringPropertyNames():获取所有的键的集合 - public void load(Reader reader):把文件中的数据读取到集合中
- public void store(Writer writer, String comments):把集合中的数据存储到文件中
代码示例1(Properties存储元素并遍历):
import java.util.Properties;
import java.util.Set;
public class Demo24 {
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("1", "卢一");
prop.setProperty("2", "黄伊");
prop.setProperty("3", "赵露思");
Set<String> set = prop.stringPropertyNames();
for(String key : set){
String value = prop.getProperty(key);
System.out.println(key + "----" + value);
}
}
}
代码示例2(load功能和store功能实现):
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
public class Demo25 {
public static void main(String[] args) throws IOException {
myLoad();
//myStore();
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("1", "卢一");
prop.setProperty("2", "黄伊");
prop.setProperty("3", "赵露思");
Writer w = new FileWriter("text1.txt");
prop.store(w, "这是描述信息");
w.close();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
Reader r = new FileReader("text1.txt");
prop.load(r);
r.close();
System.out.println("prop:" + prop);
}
}
9.NIO(new IO,java4之后有)
概述:JDK4之后出现了NIO,新IO和传统IO有相同的目的,都是用于进行输入输出,但新IO使用了不同的方式来处理输入输出,采用内存映射文件的方式,将文件或者文件的一段区域映射到内存中,就可以像访问内存一样来访问文件了,这种方法效率比IO高效,但是目前很多地方我们看到的还是旧IO的引用
JDK7的NIO增加的需要了解的类
- Path:与平台无关的路径
- Paths:包含了返回Path的静态方法
- public static Path get(URI uri):根据给定的URI来确定文件路径
- Files:操作文件的工具类,提供了大量的方法
- public static long copy(Path source, OutputStream out):复制文件
- public static Path write(Path path,Interable<? extends CharSequence> lines, Charset cs, OpenOption...options):写操作
代码示例:
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
public class Demo26 {
public static void main(String[] args) throws IOException {
//复制文件到指定目录
//Files.copy(Paths.get("text1.txt"), new FileOutputStream("copy.txt"));
//写操作
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
Files.write(Paths.get("array.txt"), array, Charset.forName("GBK"));
}
}
综合案例:五人游戏
游戏场景:
1.有五个人都在玩游戏
2.这五个人组成了一支队伍
3.这支队伍进行游戏
4.队伍成员彼此欣赏,决定以后还要组队一起游戏
5.一段时间后大家再次组队游戏
整体思路:
查找老朋友(加载文件到集合)->判断有没有老朋友(查看集合中是否有数据)->有老朋友则直接进行游戏(使用集合)->没有老朋友的话则找五个新朋友(新建五个对象)->玩游戏(使用集合)->判断是否是新朋友->是的话留联系方式(保存文件,结束程序)->不是的话直接程序结束
代码实现
//Hero.java
public class Hero {
private String name;//英雄的名字
private int attack;//英雄的攻击力
private String type;//英雄的类型
public Hero(){
}
public Hero(String name, int attack, String type) {
super();
this.name = name;
this.attack = attack;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAttack() {
return attack;
}
public void setAttack(int attack) {
this.attack = attack;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
//DemoGame.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
public class DemoGame {
public static void main(String[] args) throws IOException {
//1. 首先,创建一个集合,用来存储五个英雄
ArrayList <Hero> list = new ArrayList<Hero>();
//2.读文件,把数据加载到集合
loadFileToList(list);
//3.判断一下集合当中是否有内容(判断是不是新的队伍)
boolean isNew = list.size() == 0;
System.out.println("判断集合中是否没有内容:" + isNew);
//4.1如果是新的队伍,没有人,就需要创建五个对象加入到集合当中
if(isNew){
//调用方法,往集合添加英雄
addFileHeros(list);
}
//5.遍历集合,输出其中每一个对象的具体信息
showHeros(list);
//6.统计一下总战斗力
getTotalAttack(list);
//7.把集合当中的新的队伍写入文件当中去
saveToFile(list);
System.out.println("退出游戏!");
}
/*
* 定义一个方法用来读取文件,将数据添加到集合当中
* 返回值:void
* 方法名称:loadFileToList
* 参数列表:ArrayList<Hero> list
*/
public static void loadFileToList(ArrayList<Hero> list) throws IOException{
BufferedReader br = new BufferedReader(new FileReader("gameFile.txt"));
String line;
while((line = br.readLine()) != null){
//根据逗号切割字符串数据并添加到array数组中,切割后得到的就是一组数组
System.out.println(line.split(","));
String[] array = line.split(",");
String name = array[0];
//把字符串类型的数据转为int类型的数据
int attack = Integer.parseInt(array[1]);
String type = array[2];
Hero hero = new Hero(name, attack, type);
list.add(hero);
}
br.close();
}
/*
* 定义一个添加英雄到集合的方法
* 返回值:void
* 方法名称:addFileHeros
* 参数列表:ArrayList<Hero> list
*/
public static void addFileHeros(ArrayList<Hero> list){
Scanner sc = new Scanner(System.in);
for(int i = 1; i <= 5; i ++){
System.out.println("请输入第" + i +"个英雄的名字");
String name = sc.next();
System.out.println("请输入第" + i +"个英雄的攻击力");
int attack = sc.nextInt();
System.out.println("请输入第" + i +"个英雄的类型");
String type = sc.next();
Hero hero = new Hero(name, attack, type);
list.add(hero);//把英雄添加到集合当中
}
}
/*定义一个遍历英雄集合的方法
* 返回值:void
* 方法名:showHeros
* 参数列表:ArrayList<Hero> list
*/
public static void showHeros(ArrayList<Hero> list){
for(int i = 0; i < list.size(); i ++){
Hero hero = list.get(i);
System.out.println("英雄的名字为:" + hero.getName() + ",英雄的攻击力为" + hero.getAttack() + ",英雄的类型为" + hero.getType());
}
}
/*定义一个统计英雄总攻击力的方法
* 返回值:int
* 方法名:getTotalAttack
* 参数列表:ArrayList<Hero> list
*/
public static int getTotalAttack(ArrayList<Hero> list){
int total = 0;
for(int i = 0; i < list.size(); i ++){
Hero hero = list.get(i);
total += hero.getAttack();
}
return total;
}
/*定义一个方法,用来将集合当中的对象数据全部写到文件里
* 返回值:void
* 方法名称:saveToFile
* 参数列表:ArrayList<Hero>
*/
public static void saveToFile(ArrayList<Hero> list) throws IOException{
BufferedWriter bw = new BufferedWriter(new FileWriter("gameFile.txt"));
for(int i = 0; i < list.size(); i ++){
Hero hero = list.get(i);
//需要把一个Hero对象转换为字符串,将三个成员变量拼接成一个字符串
String str = hero.getName() + "," + hero.getAttack() + "," + hero.getType();
bw.write(str);
bw.newLine();//换行
}
bw.close();
}
}
作者:卢一
出处:http://www.cnblogs.com/luyi001/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。