线程
多线程实现的方式
创建Thread子类实现多线程
实现步骤:
- 创建一个Thread类的子类
- 在Thread类的子类中重写run方法,在run方法中写线程任务,或者在run方法中调用要执行的线程任务方法
- 创建Thread类的子类对象,
- 调用start()方法,开启新的线程,JVM会去调用run方法(注意:不能直接调用run方法)
注意事项:
- 同一个对象不能多次执行start()方法,否者会报异常
- 不是直接调用run方法,而是调用start方法,让JVM去调用run方法
- java程序属于抢占式调度
ex:
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {//传递线程名字
super(name);//调用父类方法给线程命名
}
@Override
public void run() {
try{
Thread.sleep(100);
System.out.println(Thread.currentThread().getName());//获取当前线程名字
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
实现Runnable接口来实现多线程
实现步骤:
- 创建Runnable接口的实现类
- 在实现类中重写Runnable中的run方法
- 创建一个Runnable接口实例
- 创建Thread类的实例,构造方法中传递Runnable接口的实例对象
- 让Thread对象实例调用Thread类中的start方法,然后JVM会去调用run方法开启新的线程(注意JVM并不能开启新的线程,是JVM调用底层系统方法去开启新线程)
ex:
//1.创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable {
//2.在实现类中重写Runnable接口中的run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "--->" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo05Runnable {
public static void main(String[] args) {
//3.创建一个Runnable接口的实例
RunnableImpl run = new RunnableImpl();
//4.创建Thread类的实例,构造方法中传递Runnable接口的实现类对象
Thread t = new Thread(run);
//5.调用Thread类中的start方法,开启新的线程执行run方法
t.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
实现Callable接口来实现多线程
使用匿名内部类简化多线程的创建
匿名:没有名字
内部类:写在其他类的内部的类
匿名内部类的作用:简化代码(把子类继承父类,重写父类的方法,创建子类对象合成一步完成)
匿名内部类的最终产物是:实现类对象/子类,而这个类没有名字
格式:new 父类/接口(){重写父类/接口中的方法};
ex:
//使用匿名内部类实现多线程
new Thread("王冰冰"){//相当于Thread的子类
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();//开启新线程
//匿名接口实现多线程
Runnable run = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
new Thread(run,"高圆圆").start();//开启新的线程
//简化写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
},"古力娜扎").start();
使用Runnable接口创建多线程的优点
- 避免了单继承的局限性
一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其它类了
而实现Runnable接口,还可以继承其他的类,实现其他的接口 - 增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新的线程分离(解耦)
实现类中重写run方法:用来设置线程任务
创建Thread类对象,调用start方法:用来开启新的线程新线程
设置线程名称,获取线程名称
设置线程的名称有两种方法:
-
有参构造,传递线程的名称,调用父类的有参构造,让父类给线程起一个名称
-
调用setName(要设置的线程名称)方法
获取线程名称的方法有两种:
-
在Thread类或它的子类中调用getName()
-
使用Thread类的静态方法currentThread()方法,然后调用getName()方法
注意:没有继承Thread的类中只能用方法2
线程休眠
线程休眠,调用sleep(long millis)
//单位为毫秒值,sleep方法是静态方法
线程安全问题
解决办法:
-
同步代码块
格式:synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}注意:
1.通过代码块中的锁对象,可以是任意对象
2.但是必须要保证多线程使用的是同一个锁对象
3.锁对象的作用:
把同步代码块锁住,只让一个线程在同步代码块中执行 -
同步方法
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}注意:注意如果是静态方法的锁对象,不能使用this,因为静态优先于对象产生
静态方法的锁对象是本类的class属性-->class文件对象(反射) -
Lock锁
解决线程安全问题的第三种方法:使用Lock锁
java.util.concurrent.locks.Lock
接口Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作
Lock接口中的常用方法:
- void lock()获取锁
- void unlock()释放锁
java.util.concurrent.locks.ReentrantLock
(可重入锁)是Lock接口的实现类使用步骤:
- 在成员位置创建一个
ReentrantLock
对象 - 在可能会出现安全问题的代码前调用Lock接口中的方法lock()来获取锁
- 在可能会出现安全问题的代码后调用Lock接口中的方法unlock()来释放锁
ex:
lock.lock();//获取锁
if (ticket > 0){
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "-->" + ticket + "张票");
ticket--;//卖了一张,就减一
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//释放锁
}
线程之间的通信
wait(),notify()方法放在同步代码块中,因为wait()和notify()方法只能同时执行一个
ex:
public static void main(String[] args) {
//创建锁对象
Object obj = new Object();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
synchronized (obj){
System.out.println("我要买10个豆沙包子");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后会继续执行下面的代码
System.out.println("包子做好了,可以开吃了");
}
}
}.start();
//老板线程
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("老板做了5秒钟的包子");
obj.notify();
}
}
}.start();
}
线程池
JDK1.5之后提供的线程池
java.util.concurrent.Executors
:线程池的工厂类,用来生成线程池
Executors
类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池
参数:
int nThreads:创建线程池中包含的线程数量
返回值:
ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)java.util.concurrent.ExecutorService:线程池接口用来从线程池中获取线程,调用start方法,执行线程任务
**submit(Runnable task)**提交一个Runnable任务用于执行
关闭/销毁线程池的方法void shutdown()
线程池的使用步骤:
- 使用线程池的工厂类Executors里提供的静态方法newFixedThreadPool()生产一个指定线程数量的线程池
- 创建一个类,实现Runnable接口,重写run方法,设置线程任务
- 调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
- 调用ExecutorService中的方法shutdown销毁线程池(不推荐)
ex:
public class Demo01ThreadPool {
public static void main(String[] args) {
//1.调用Executors中的静态方法newFixedThreadPool来创建指定数量的线程池
ExecutorService services = Executors.newFixedThreadPool(2);
//3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
services.submit(new RunnableImpl());
services.submit(new RunnableImpl());
services.submit(new RunnableImpl());
//会一直运行,因为线程池一直都在,除非调用shutdown方法,销毁线程池,但不推荐这样做
services.shutdown();
//如果还继续调用submit方法,则会报错RejectedExecutionException
services.submit(new RunnableImpl());//RejectedExecutionException
}
}
public class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了一个新的线程");
}
}
Lambda表达式
面向对象编程思想:做一件事情,找一个能够解决这个事情的对象,然后调用对象的方法完成事情
函数式编程思想:只要获取到结果,谁去做的,如何做的都不重要,重视的是结果,而不是过程
解决冗余的Runnable代码
使用匿名内部类实现多线程
ex:
//使用匿名内部类实现多线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "创建了新的线程");
}
}).start();
使用Lambda表达式实现多线程
ex:
//使用Lambda表达式实现多线程
new Thread(()-> {
System.out.println(Thread.currentThread().getName() + "创建了新的线程");
}
).start();
Lambda的标准格式
由三部分组成
- 一些参数
- 一个箭头
- 一段代码
格式:
(参数列表)-> {一些重写方法的代码};
解释说明:
():接口的中的抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
->:传递的意思,把参数传递给方法体{}
{}:重写接口的抽象方法的方法体
ex:
public class Dome04Cook {
public static void main(String[] args) {
//调用invokeCook方法,参数是Cook接口,传递Cook接口的匿名内部类对象
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭了");
}
});
//使用Lambda
invokeCook(()->{
System.out.println("吃饭了");
});
}
//定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFood()
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
public interface Cook {
public abstract void makeFood();
}
Lambda表达式是可推导,可以省略
凡是根据上下文能够推导出来的内容,都可以省略
-
(参数列表) 括号中的参数列表的数据类型可以省略
-
(一个参数) 可以省略类型和()
-
{一些代码} 如果{}中的代码只有一行,无论是否有返回值都可以省略return和{}和分号
注意:要省略三个必须一起省略,参数中的类型也可以省略
ex:
invokeCalc(20,30,(a,b) -> a + b);
ex:
package top.lukeewin.demo12;
/*
* 给定一个计算器Calculator接口,内含抽象方法calc可以将两个int数字相加得到和
* */
public interface Calculator {
//定义一个计算两个int 类型的和的方法,并返回结果
public abstract int calc(int a,int b);
}
package top.lukeewin.demo12;
public class Demo06Lambda {
public static void main(String[] args) {
//调用invokeCalc方法,方法的参数是一个接口,可以使用匿名内部类
invokeCalc(10, 20, new Calculator() {
@Override
public int calc(int a, int b) {
return a + b;
}
});
//使用Lambda表达式简化匿名内部类的书写
invokeCalc(120,130,(int a,int b)->{
return a + b;
});
//优化Lambda表达式
invokeCalc(20,30,(a,b) -> a + b);
}
/*
* 定义一个方法,参数传递两个int
* 参数传递Calculator接口
* 方法内部调用Calculator中的方法calc计算两个数的和
* */
public static void invokeCalc(int a,int b,Calculator c){
int sum = c.calc(a,b);
System.out.println(sum);
}
}
使用Lambda表达式的前提条件
- 必须是接口,且接口中只有一个抽象方法
- 必须具有上下文推断
备注:有且只有一个抽象方法的接口称为函数式接口
文件File类
位置:java.io.File
文件和目录路径名的抽象表示形式
java把电脑中的文件和文件夹(目录)封装成了一个File类,我们可以使用File类中的方法对文件和文件夹进行操作
有哪些操作呢?如下:
- 创建文件/文件夹
- 删除文件/文件夹
- 获取文件/文件夹
- 判断文件/文件夹
- 对文件夹进行遍历
- 获取文件的大小
File类是一个与操作系统无关的类,任何的操作系统都可以使用这个类中的方法
重点记住这三个单词:
- file:文件
- directory:文件夹(目录)
- path:路径
File类中的字段
static String pathSeparator //与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
static char pathSeparatorChar//与系统有关的路径分隔符
static String separator //与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串
static char separatorChar //与系统有关的默认名称分隔符
所以路径不能写死,因为不同的操作系统,路径分隔符不同
c:\develop\a\a.txt windows
c:/develop/a/a.txt linux
推荐的写法是:
"c:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
package top.lukeewin.demo13File;
import java.io.File;
public class Demo01File {
public static void main(String[] args) {
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);//;
System.out.println(File.separator);//\
}
}
路径分为:==绝对路径==,==相对路径==
- 绝对路径:是一个完整的路径,以盘符开始的路径(以根路径开始)
- C:\\a.txt
- D:\\java\\demo\\Hello.java
- 相对路径:是一个简化的路径,相对是指相对于当前项目的根目录
- 例如完整的路径为:D:\\java\\demo\\Hello.java
- 如果当前项目在D:\\java\\demo目录下,那么写程相对路径为:Hello.java
==注意==:路径是不区分大小写的
路径中的目录分隔符,在windows中是使用反斜杠\,反斜杠是转义字符,所以路径中的反斜杠要写两个,才能转义为一个反斜杠
File类的构造方法
File类中构造方法1
File(String pathname)//通过将给定的路径名字字符串转化为抽象路径名来创建一个新File实例
参数:String pathname:字符串的路径名称
路径可以是以文件结尾,也可以是以文件夹结尾
路径可以是相对路径,也可以是绝对路径
路径可以是已经存在的,也可以是不存在的
创建File对象,只是把字符串格式的路径封装为File对象,不考虑路径的真假情况
private static void show01() {
File f1 = new File("D:\\Works\\IdeaProjects\\advanced_code\\java_code\\src\\top\\lukeewin\\demo13File\\a.txt");//不存在的文件
System.out.println(f1);//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\a.txt
File f2 = new File("D:\\Works\\IdeaProjects\\advanced_code\\java_code\\src\\top\\lukeewin\\demo13File\\Demo01File.java");//存在的文件
System.out.println(f2);//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\Demo01File.java
File f3 = new File("b.txt");//相对路径
System.out.println(f3);//b.txt
}
FIle类中的构造方法2
File(String parent,String child)根据parent路径名称字符串和child路径名称字符串创建一个新File实例
参数:==String== parent:父路径
String child:子路径
好处:父路径和子路径可以单独写,使用起来比较灵活,父路径和子路径都可以变化
public static void main(String[] args) {
// show01();
show02("D:\\","a.txt");
}
private static void show02(String parent,String child) {
File file = new File(parent,child);
System.out.println(file);//D:\a.txt
}
File类中的构造方法3
File(File parent,String child) 根据parent抽象路径和child路径字符串创建一个新File实例
参数:把路径划分为两部分:
==File== parent:父路径
String child:子路径
好处:1. 父路径和子路径可以单独写,使用起来比较灵活,父路径和子路径都可以变化
2. 父路径是File类型,可以使用File中的方法对路径进行一些操作,再使用路径创建对象
private static void show03() {
File parent = new File("D:\\demo");
File file = new File(parent,"HelloWorld");
System.out.println(file);//D:\demo\HelloWorld
}
File类中的常用方法
获取方法
-
public String getAbsolutePath()
返回此File的绝对路径名字字符串 -
public String getPath()
将此File转换为路径字符串 -
public String getName()
返回由此File表示的文件或目录的名称 -
public long length()
返回由此FIle表示的文件的长度 -
getAbsolutePath()
方法,不论File类型是绝对路径还是相对路径都返回绝对路径
import java.io.File;
public class Demo03File {
public static void main(String[] args) {
show01();
}
private static void show01() {
File file1 = new File("D:\\Works\\IdeaProjects\\advanced_code\\java_code\\src\\top\\lukeewin\\demo13File\\Demo01File.java");
String absolutePath = file1.getAbsolutePath();
System.out.println(absolutePath);//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\Demo01File.java
File file2 = new File("Demo01File.java");
String absolutePath1 = file2.getAbsolutePath();
System.out.println(absolutePath1);//D:\Works\IdeaProjects\advanced_code\Demo01File.java
}
}
getPath()
方法返回的是File传递的路径,传递的是绝对路径返回的就是绝对路径,传递的是相对路径返回的就是相对路径。
private static void show02() {
File f1 = new File("D:\\Works\\IdeaProjects\\advanced_code\\java_code\\src\\top\\lukeewin\\demo13File\\Demo01File.java");
File f2 = new File("Demo01File.java");
System.out.println(f1.getPath());//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\Demo01File.java
System.out.println(f2.getPath());//Demo01File.java
//这了的toString方法实质是调用了getPath方法
System.out.println(f1.toString());//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\Demo01File.java
System.out.println(f1);//D:\Works\IdeaProjects\advanced_code\java_code\src\top\lukeewin\demo13File\Demo01File.java
}
getName()
方法获取File中的最后一个字符串
private static void show03() {
File f1 = new File("D:\\works\\java\\java.txt");
File f2 = new File("D:\\works\\java");
System.out.println(f1.getName());//java.txt
System.out.println(f2.getName());//java
}
length()
方法返回File中指定的文件或文件夹的大小,单位为字节
private static void show04() {
File f1 = new File("D:\\Works\\javalianxi");
long length = f1.length();
System.out.println(length);
File f2 = new File("D:\\Works\\javalianxi\\a.txt");
System.out.println(f2.length());
File f3 = new File("D:\\Works\\javalianxi\\abc.txt");
System.out.println(f3.length());//0 当文件不存在时,length返回0
}
判断方法
-
public boolean exists()
判断File中构造方法传递的文件或文件夹是否存在存在返回true
不存在返回false
package top.lukeewin.demo13File; import java.io.File; public class Demo04File { public static void main(String[] args) { show01(); } /* * public boolean exists()//表示File中的文件或文件夹是否存在 * 存在:true; * 不存在:false; * */ private static void show01() { File f1 = new File("D:\\Works\\java"); System.out.println(f1.exists());//true File f2 = new File("D:\\hello"); System.out.println(f2.exists());//false } }
-
public boolean isDirectory()
用于判断File构造方法中指定的路径是否是文件夹 -
public boolean isFile()
用于判断File构造方法中指定的路径是否是文件注意:文件夹与文件是互斥的
这两个方法使用的前提是路径必须存在,否则返回false
private static void show02() { File f1 = new File("D:\\Works\\javalianxi\\chap10"); File f2 = new File("D:\\Works\\javalianxi\\a.txt"); System.out.println(f1.isDirectory());//true System.out.println(f1.isFile());//false System.out.println(f2.isDirectory());//false System.out.println(f2.isFile());//true }
对上面方法的改进,在使用
isDirectory()
和isFile()
方法前先要判断是否存在private static void show02() { File f1 = new File("D:\\Works\\javalianxi\\chap10"); File f2 = new File("D:\\Works\\javalianxi\\a.txt"); //构造一个不存在的文件或文件夹,返回的是false File f3 = new File("D:\\Works\\javalianxi\\abc.txt"); if (f1.exists()){ System.out.println(f1.isDirectory());//true System.out.println(f1.isFile());//false } if (f2.exists()){ System.out.println(f2.isDirectory());//false System.out.println(f2.isFile());//true } if (f3.exists()){ System.out.println(f3.isDirectory()); System.out.println(f3.isFile()); }else { System.out.println(f3.getPath() + "路径中不存在" + f3.getName() + "文件或文件夹"); } }
创建和删除方法
public boolean createNewFile()
当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
package top.lukeewin.demo13File;
/*
注意:1.此方法只能创建文件,不能创建文件夹
2.当文件已经存在时,返回false
3.创建文件的路径必须存在,否则会抛出异常
4.createNewFile方法存在IOException,所以必须要处理该异常,要么继续抛出,要么捕获处理
*/
import java.io.File;
import java.io.IOException;
public class Demo05File {
public static void main(String[] args) {
show01();
}
private static void show01() {
File f1 = new File("D:\\Works\\javalianxi\\c.txt");
try {
boolean b1 = f1.createNewFile();
System.out.println("是否创建成功: " + b1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public boolean delete()
删除由此File表示的文件或文件夹。
/*
* delete()方法可以删除文件夹和文件,注意删除的内容不会经过回收站
* */
private static void show02() {
//删除文件夹,注意java.txt是show01()方法创建的文件夹,而不是文件
File f1 = new File("D:\\Works\\javalianxi\\java.txt");
boolean b1 = f1.delete();
System.out.println(b1);
//删除文件
System.out.println(new File("D:\\Works\\javalianxi\\a.txt").delete());
}
public boolean mkdir()
创建由此File表示的文件夹public boolean mkdirs()
创建由此File表示的文件夹,包括任何必须但不存在的父文件夹
public boolean mkdir()//创建单级空的文件夹
public boolean mkdirs()//创建多级空的文件夹
//创建文件夹的路径和名称是在构造方法中给出的(即构造方法中的参数)
//返回值:boolean
//true:文件夹不存在,成功创建文件夹
//false:文件夹已经存在了,创建文件夹失败
private static void show01() {
//创建单级文件夹
File f1 = new File("D:\\Works\\javalianxi\\a");
boolean b1 = f1.mkdir();
System.out.println(b1);
//mkdir()不能创建多级文件夹
File f2 = new File("D:\\Works\\javalianxi\\a\\b\\c");
System.out.println(f2.mkdir());
//创建多级文件夹
File f3 = new File("D:\\Works\\javalianxi\\a\\b\\c");
System.out.println(f3.mkdirs());
//注意:mkdir和mkdirs方法创建的只能是文件夹,不会是文件
File f4 = new File("D:\\Works\\javalianxi\\java.txt");
System.out.println(f4.mkdir());
}
File类遍历文件夹
- public String[] list():返回一个String类型的数组,表示该File目录的所有文件和目录
- public File[] listFiles():返回一个File数组,表示该File目录中的所有的子文件夹
注意:
如果构造方法中给定的目录的路径不存在,则会抛出空指针异常
2.如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
/*
* list()方法能够获取隐藏的文件和文件夹
* */
private static void show01() {
File f1 = new File("D:\\Works\\javalianxi\\chap10");
String[] list = f1.list();
for (String s : list) {
System.out.println(s);
}
System.out.println("=============");
//遍历的不是文件夹,会报空指针异常java.lang.NullPointerException
File f2 = new File("D:\\Works\\javalianxi\\b.txt");
String[] list1 = f2.list();
for (String s : list1) {
System.out.println(s);//java.lang.NullPointerException
}
}
//获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息
private static void show02() {
File f1 = new File("D:\\Works\\javalianxi\\chap10");
File[] files = f1.listFiles();
for (File f : files) {
System.out.println(f);//会把完整的路径打印出来,与list()方法不同,list()方法只是打印出指定目录下的目录和文件
}
}
递归
- 递归的分类:
- 直接递归
- 间接递归
直接递归
main(){ a(); } a(){ a(); }
间接递归
main(){ b(); } b(){ c(); } c(){ b(); }
注意事项:
- 递归一定要有条件限制能够保证递归能够停止下来,否则会发生栈内存溢出
- 递归的次数不能太多,否则会发生栈内存溢出
- 构造方法禁止递归
递归的使用前提:
当调用方法的时候方法的主体不变,每次调用方法的参数不同,可以使用递归
package top.lukeewin.demo14Recursion;
public class Demo01DiGui {
public static void main(String[] args) {
// method01();
// method02(1);
}
/*
* 禁止递归构造方法,直接编译报错
* */
public Demo01DiGui() {
// Demo01DiGui();
}
//不能无限递归,会报StackOverflowError
private static void method01() {
System.out.println("method01方法");//java.lang.StackOverflowError
method01();
}
//递归的次数也不能太多,会报StackOverflowError
private static void method02(int i){
System.out.println(i);//StackOverflowError
if (i == 20000){
return;
}
method02(++i);
}
}
练习1:计算1到n之间的和
package top.lukeewin.demo14Recursion;
public class Demo02RecursionSum {
public static void main(String[] args) {
int s = sum(100);
System.out.println(s);
}
//计算1到n之间的和
private static int sum(int n){
if (n == 1){
return 1;
}
return n + sum(n-1);
}
}
练习2:求n!
package top.lukeewin.demo14Recursion;
public class Demo03JieCheng {
public static void main(String[] args) {
System.out.println(jc(5));
}
//求n!
private static int jc(int n){
if (n == 1){
return 1;
}
return n*jc(n-1);
}
}
练习3:递归打印多级目录
public class Demo04RecursionFile {
public static void main(String[] args) {
File f = new File("D:\\Works\\javalianxi");
getAllFiles(f);
}
/*
* 定义一个方法递归遍历传过来的文件夹
* */
private static void getAllFiles(File dir){
System.out.println(dir);//把文件夹输出
File[] files = dir.listFiles();
for (File f : files) {
//判断是否为文件夹,如果是文件夹则递归调用
if (f.isDirectory()){
getAllFiles(f);
}else {//否则输出文件
System.out.println(f);
}
}
}
}
练习4:打印指定文件夹下的指定类型的文件
package top.lukeewin.demo14Recursion;
import java.io.File;
public class Demo05RecursionFile {
public static void main(String[] args) {
File f = new File("D:\\Works\\javalianxi");
getFiles(f);
}
//打印目录下的指定类型的文件
private static void getFiles(File dir){
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()){
getFiles(file);
}else {
if (file.getName().toLowerCase().endsWith(".txt")){
System.out.println(file);
}
}
}
}
}
FileFilter和FilenameFilter文件过滤器接口
File[] listFiles(FileFilter filter)
作用:用来过滤文件
抽象方法:用来过滤文件的方法
boolean accept(File pathname)测试指定的抽象路径名是否应该包含某个路径名列表中
参数:File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
File[] listFiles(FilenameFilter filter)
作用:用于过滤文件名称
抽象方法:用来过滤文件的方法
boolean accept(File dir,String name)测试指定的文件是否应该包含摸一个文件列表中。
参数:File dir :构造方法中传递的被遍历的目录
String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:两个过滤器接口没有实现类,需要自己编写,重写accept方法,并定义过滤的规则
使用FileFilter接口实现文件名称过滤
package top.lukeewin.demo14Recursion;
import java.io.File;
public class Demo07GetFile {
public static void main(String[] args) {
File f = new File("D:\\Works\\javalianxi");
getFiles(f);
}
//编写获取参数中指定路径下的指定格式的文件
private static void getFiles(File dir){
// System.out.println(dir);
File[] files = dir.listFiles(pathname -> {
if (pathname.isDirectory()){
return true;
}
return pathname.getName().toLowerCase().endsWith(".txt");
});//把过滤好的文件放到文件数组中
for (File file : files) {//遍历文件数组
if (file.isDirectory()){//判断是否是文件夹,是则继续调用自己
getFiles(file);
}else {//不是文件夹则可以打印输出
System.out.println(file);
}
}
}
}
使用FilenameFilter接口实现文件名称过滤
package top.lukeewin.demo14Recursion;
import java.io.File;
import java.io.FilenameFilter;
public class Demo08GetFile {
public static void main(String[] args) {
File f = new File("D:\\Works\\javalianxi");
getFiles(f);
}
private static void getFiles(File f) {
//使用匿名内部类
/*File[] files = f.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
}
});*/
//使用lambda表达式
File[] files = f.listFiles((dir,name) -> new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java"));
for (File file : files) {
if (file.isDirectory()){
getFiles(file);
}else {
System.out.println(file);
}
}
}
}
分析:new File(dir,name).isDirectory()
是把传递过来的dir和name封装为File对象,然后调用File类中的isDirectory()
IO流
字节流
I:表示input
O:表示output
流:数据
分类:
- 字节流
字节输入流:InputStream
抽象类,是所有字节输入流的父类
字节输出流:OutputStream
抽象类,是所有字节输出流的父类
- 字符流
字符输入流:Reader
抽象类,是所有字符输入流的父类
字符输出流:Writer
抽象类,是所有字符输出流的父类
这里的输入:磁盘->内存
这里的输出:内存->磁盘
java.io.OutputStream
字节输出流 此抽象类是表示输出字节流的所有类的父类
该抽象类中定义了一些方法:
public void close()
//关闭此输出流并释放此流相关的任何系统资源
public void flush()
//刷新此输出流并强制任何缓冲的输出字节流被写
public void write(byte[] b)
//将b.length字节从指定的字节数组中写入到此输出流上
public void write(byte[] b, int off,int len)
//从指定的字节数组写入len字节,从偏移量off开始输出到此输出流上
public abstract void write(int b)
//将指定的字节输出流输出
java.io.FileOutputStream extends OutputStream
FileOutputStream:文件字节输出流
作用:把内存中的数据写入到磁盘的文件中
构造方法:
FileOutputStream(String name)//创建一个具有指定名称的文件中写入数据的输出文件流
FileOutputStream(File file)//创建一个指定File对象的文件中写入数据的文件输出流
参数:写入数据的目的地
String name:目的地是一个字符串类型的文件路径
File file:目的地是一个文件
构造方法的作用:
1.创建一个FileOutputStream对象
2.会根据构造方法中的参数创建一个空文件
3.会把FileOutputStream对象指向创建好的文件
重点:
- 创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
- 调用FileOutputStream对象中的方法write,把数据写入到硬盘中
- 释放资源
字节输出流
FileOuputStream
中的3中方法写入到文件中
public viod write(int b)
//b是ascii值,例如:fos.write(97)//表示向文件中写入了apublic void write(byte[] b)
//以字节数组的形式向文件中写入数据- .
public void write(byte[] b)
//先把字符串转为字节数组,然后再写入到文件中
**注意:**当数组中的第一个数值是0~127之间的数,那么在ascii表中查找对应的值即可
如果数组中的第一个数值是负数,那么在系统默认的格式表中查找对应的值,简体中文是GBK格式,GBK中一个汉字占2个字节, UTF-8中是3个字节表示一个汉字
1.public viod write(int b)
//b是ascii值,例如:fos.write(97)//表示向文件中写入了a
//一次写入一个字节数据到文件中,write(int b)
private static void show01() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\d.txt");
fos.write(97);//写入的是acsii对应的字母a
fos.close();
}
2.public void write(byte[] b)
//以字节数组的形式向文件中写入数据
//通过字节数组写入到文件中,write(byte[] b)
private static void show02() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\d.txt");
byte[] b = {-26, -120, -111, -25, -120, -79, -28, -67, -96};//A,B,C,D
fos.write(b);//不是一次写一个字节,而是把字节数组写入到文件中
fos.close();
}
3.public void write(byte[] b)
//先把字符串转为字节数组,然后再写入到文件中
//字符串转为字节数组然后写入文件中
private static void show03() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\d.txt");
byte[] b = "我爱你".getBytes();
//使用数组工具类中的toString方法转换为字符串类型输出
//注意:GBK默认是2个字节表示一个汉字,但UFT-8是3个字节表示一个汉字
System.out.println(Arrays.toString(b));//[-26, -120, -111, -25, -120, -79, -28, -67, -96]
fos.write(b);
fos.close();
}
如何续写
//续写:使用两个参数的构造方法即可
FileOutputStream(String name,boolean append)//创建一个指向具体路径的文件中,参数append表示是否可以追加
FileOutputStream(File file,boolean append)//创建一个指向File对象表示的文件中,参数append表示是否可以追加
//不换行的续写
private static void show01() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\e.txt",true);//表示可以追加
fos.write("我爱你".getBytes());
fos.close();
}
//换行的续写
private static void show02() throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\f.txt",true);//表示可以追加
for (int i = 0; i < 10; i++) {
fos.write("我爱你".getBytes());
/*
添加换行符,注意,不同系统换行符不一样,在Windows中是\r\n
Linux或Unix中是:/r
在Mac中是:/n
注意也需要调用getBytes()方法,转为字节数组才能写到文件中
*/
fos.write("\r\n".getBytes());
}
fos.close();
}
字节输入流
java.io.InputStream
:字节输入流
此抽象类是表示字节输入流的所有类的超类
定义了所有子类的共有方法:
int read()
//从输入流中读取数据的一个字节数据到内存中
int read(byte[] b)
//从输入流中读取整个字节数组到内存中
void close()
//关闭此输入流并释放与该流相关联的所有系统资源
文件字节输入流:java.io.FileInputStream
构造方法:
FileInputStream(String name)
FileInputStream(File file)
参数:
String name
:文件的路径
File file
:文件
构造方法的作用:
- 会创建一个
FileInputStream
对象 - 会把
FileInputStream
对象指向构造方法中要读取的文件
读取数据的原理:
java程序-->JVM-->OS-->OS读取数据的方法-->读取文件
字节输入流的使用步骤:
- 创建FIleInputStream对象,构造方法中绑定要读取的数据源
- 使用FileInputStream对象中的方法read,读取文件
- 释放资源
package top.lukeewin.demo15IO;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo04InputStream {
public static void main(String[] args) throws IOException {
//1.创建FileInputStream对象,构造方法中绑定需要读取的文件的数据源
FileInputStream fis = new FileInputStream("D:\\Works\\javalianxi\\a.txt");
//2.使用FileInputStream对象中的read方法读取文件
//int read()读取文件中的一个字节并返回,当读取到最后时返回-1
/*int len = fis.read();//一次只能读取一个字节
System.out.println(len);
int len = fis.read();
System.out.println(len);*/
/*
* 发现以上读取是一个重复的过程,所以可以使用循环来优化
* */
int len = 0;//记录读取到的字节
while ((len = fis.read())!=-1){
System.out.println((char)len);
}
fis.close();
}
}
一次读取多个字节数据:
int read(byte[] b)从输入流中读取一定量的字节,并将其存储在缓冲区数组b中
需要明确两件事:
-
方法的参数byte[]的作用是什么?
相当于缓冲区可以更高效地读取数据,一般定义大小为1024,即一次读取1KB
-
方法的返回值int是什么?
表示有效读取的长度,当读取到文件的最后时返回-1
package top.lukeewin.demo15IO;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class Demo05FileInputStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("D:\\Works\\javalianxi\\a.txt");
//byte[] b = new byte[2];
//int len = fis.read(b);//返回的是缓冲区中的读取的字节个数
/*System.out.println(len);
System.out.println(Arrays.toString(b));//输出的是ascii值,[97, 98]
System.out.println(new String(b));//ab
len = fis.read(b);
System.out.println(len);
System.out.println(Arrays.toString(b));
System.out.println(new String(b));
len = fis.read(b);
System.out.println(len);
System.out.println(Arrays.toString(b));
System.out.println(new String(b));
len = fis.read(b);
System.out.println(len);
System.out.println(Arrays.toString(b));
System.out.println(new String(b));*/
byte[] bytes = new byte[1024];//一般是定义为1024字节,即1KB
int len = 0;//读取的有效长度
while ((len = fis.read(bytes))!=-1){
//使用String中的构造方法public String(byte bytes[], int offset, int length)来输出显示指定长度的字符串数据
System.out.println(new String(bytes,0,len));
}
fis.close();
}
}
==注意不能使用Arrays.toString(bytes)方法直接输出字符串,而是应该使用字符串的构造方法==
String(byte[] bytes):把字节数组转为字符串
String(byte[] bytes,int offset, int length):把字节数组中的一部分转换为字符串
offset:数组的开始索引,length:要转换的长度
byte[] bytes = new byte[1024];//一般是定义为1024字节,即1KB
int len = 0;//读取的有效长度
while ((len = fis.read(bytes))!=-1){
//使用String中的构造方法public String(byte bytes[], int offset, int length)来输出显示指定长度的字符串数据
System.out.println(new String(bytes,0,len));
}
案例:文件的复制
步骤: * 1.创建一个字节输入流对象,构造方法中绑定需要读取的数据的源 * 2.创建一个字节输出流对象,构造方法中绑定要写入的目的地 * 3.使用字节输入流中的read方法读取数据到内存中 * 4.使用字节输出流中的write的方法把内存中的数据写入到目的地 * 5.释放资源
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo06CopyFile {
public static void main(String[] args) throws IOException {
long s = System.currentTimeMillis();
//1.创建一个字节输入流对象,构造方法中绑定需要读取的数据的源
FileInputStream fis = new FileInputStream("D:\\Works\\Photoshop\\ps作品\\index.png");
//2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\a\\b\\c\\index.png");
//3.使用字节输入流中的read方法读取数据到内存中
/*//一次只读写一个字节
int len = 0;
while ((len = fis.read())!=-1){
//4.使用字节输出流中的write的方法把内存中的数据写入到目的地
fos.write(len);
}*/
//使用数组缓冲来一次读写多个字节
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
//5.释放资源(先关闭写的流,最后关闭读的流,因为写完了,必定能读完)
fos.close();
fis.close();
long e = System.currentTimeMillis();
System.out.println("复制该图片一共消耗了" + (e-s) + "毫秒");
}
}
字符流
如果使用字节流读取文件时会出现乱码,因为编码格式的问题,GBK是一个汉字占2个字节,UTF-8是占3个字节,当读取的时候是一次读取一个字节时,很容易产生乱码,所以应该使用字符流,而是不字节流。
字符输入流Reader
Reader:是字符输入流的顶层父类,方法:
- int read()//一次读取一个==字符==并返回
- int read(char[] cbuf)//一次读取==多个字符==,将字符写入到数组中
- void close()//关闭该流并释放与之相关联的所有资源
==FileReader==是==Reader==的子类,文件字符输入流
**作用:**把硬盘中文件的数据以字符的方式读取到内存中
构造方法:
FileReader(String fileName)
FileReader(File file)
参数:
读取文件的数据源
String fileName:文件的路径
File file:一个文件
FileReader构造方法的作用:
- 创建一个FileReader对象
- 会把FileReader对象指向要读取的文件
字符输入流的使用步骤:
- 创建FileReader对象,构造方法中绑定要读取的数据源
- 使用FileReader对象中的方法read读取文件
- 释放资源
package top.lukeewin.demo15IO;
import java.io.FileReader;
import java.io.IOException;
public class Demo07FileReader {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\Works\\javalianxi\\b1.txt");
/*int len = 0;
while ((len = fr.read())!=-1){
System.out.print((char)len);
}*/
//一次读取多个字符
char[] cs = new char[1024];
int len = 0;//记录每次读取的有效字符个数
while ((len = fr.read(cs))!=-1){
/*
* String类中的构造方法
* String(char[] value)//把字符数组转为字符串
* String(char[] value,int offset,int count )//把字符数组中的一部分转为字符串,offset表示开始索引,count表示要转为的字符的个数
* */
System.out.println(new String(cs,0,len));
}
fr.close();
}
}
字符输出流Writer
Writer类是抽象类,是所有字符输出流的父类
共有的方法有:
void write(int c)
写入单个字符
public static void main(String[] args) throws IOException {
//1.创建FileWriter对象
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a1.txt");
//2.写入数据到内存缓冲区中(在内存中有字符转字节的过程)
fw.write(97);
//3.刷新数据到文件中,必须要调用flush方法,把内存中的字节数据刷新到文件中
fw.flush();
//4.关闭文件字符输出流
fw.close();
}
void write(char[] cbuf)
写入字符数组
private static void show01() throws IOException {
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\c1.txt");
char[] c = {'a','b','c'};
fw.write(c);//写入字符数组的内容到内存中
fw.flush();//把内存中的数据刷新到文件中
fw.close();
}
abstract void write(char[] cbuf,int off,int len)
写入字符数组中的某一部分,off数组的开始索引,len写的字符的个数
private static void show02() throws IOException {
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a2.txt");
char[] c = {'d','e','f','g'};
fw.write(c,1,2);//写入固定位置开始的固定长度的字符数组
fw.flush();
fw.close();
}
void write(String str)
写入字符串
private static void show03() throws IOException {
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a3.txt");
fw.write("你好java");//把字符串写入到内存中
fw.flush();
fw.close();
}
void write(String str,int off,int len)
写入字符串的某一部分
private static void show04() throws IOException {
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a4.txt");
fw.write("java程序员",4,3);
fw.flush();
fw.close();
}
-
void flush()
刷新该流的缓冲区 -
void close()
关闭此流,会刷新缓冲区
java.io.FileWriter extends OuputStreamWriter exends Writer
FileWriter:文件字符输出流
作用:把内存中的字符数据写入到文件中
构造方法:
1.FileWriter(File file)根据给定的File对象构造一个FIleWriter对象
2.FileWriter(String fileName)根据给定的文件名构造一个FileWriter对象
参数:写入数据的目的地
String fileName:文件的路径
File file:是一个文件
构造方法的作用:
- 会创建一个FileWriter对象
- 会根据构造方法中传递的文件/文件夹的路径,创建文件
- 会把FileWriter对象指向创建好的文件
字符流的使用步骤:
- 创建FileWriter对象,构造方法中绑定要写入数据的目的地
- 使用FileWriter中的方法write,把数据写入到==内存缓冲区中==(字符转换为字节的过程)
- ==使用FileWriter中的方法flush,把内存缓冲区中的数据刷新到文件中==
- 释放资源(会把内存缓冲区中的数据刷新到文件中)
public class Demo08FileWriter {
public static void main(String[] args) throws IOException {
//1.创建FileWriter对象
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a1.txt");
//2.写入数据到内存缓冲区中
fw.write(97);
//3.刷新数据到文件中
fw.flush();
//4.关闭文件字符输出流
fw.close();
}
}
flush()和close()的区别:
- flush()后可以继续写入
- close()后不能继续写入
续写和换行:
续写,追加:使用两个参数的构造方法
FileWriter(String fileName ,boolean append)
FileWriter(File file ,boolean append)
参数:
String fileName,File file:写入数据的目的地
boolean append:是否追加数据,true表示可以追加数据
换行:
Windows: \r\n
Linux: /n
mac: /r
例子:
public class Demo10FIleWriter {
public static void main(String[] args) throws IOException {
show01();
}
private static void show01() throws IOException {
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a1.txt",true);
for (int i = 0; i < 10; i++) {
fw.write("牛年大吉" + i + "\r\n");
}
fw.flush();
fw.close();
}
}
使用try-catch-finally处理流中的异常
格式:
try{
可能会产生异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}finally{
一定会指定的代码
资源释放
}
package top.lukeewin.demo15IO;
import java.io.FileWriter;
import java.io.IOException;
public class Demo11FileTryCatch {
public static void main(String[] args) {
FileWriter fw = null;
try{
fw = new FileWriter("D:\\Works\\javalianxi\\a3.txt",true);
for (int i = 0; i < 10; i++) {
fw.write("大家好" + "\r\n");
}
}catch (IOException e){
System.out.println(e);
}finally {
//fw.close()有IOException异常抛出,所以必须要处理异常,可以抛出,也可以捕获处理
//如果路径不对会报空指针异常,所以还要先判断fw是否为空,当不为空时才能调用close()方法
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
==JDK7之前的新特性==
在try的后边增加一个(),在小括号中可以定义流对象
那么这个流对象的作用域就在try中有效
try中的代码执行完毕会自动把流对象释放,不用写finally
格式:
try(定义流对象){
可能会产生异常的代码
}catch(异常类型变量 变量名){
异常的处理逻辑
}
==注意:多个对象流使用分号分隔开==
==不用finally来关闭流了,因为对象流的作用范围只在大括号内有效==
package top.lukeewin.demo15IO;
/*
* JDK1.7之后的新特性:
* try(定义对象流,多个对象流之间用分号分隔开){
*
* }catch(异常对象 异常变量名){
* 需要处理的异常逻辑
* }
* */
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo12FileTryCatch {
public static void main(String[] args) {
try(FileInputStream fis = new FileInputStream("D:\\Works\\javalianxi\\a.txt");
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\chap10\\a.txt")){
int len = 0;
while ((len = fis.read())!=-1){
fos.write(len);
}
}catch (IOException e){
System.out.println(e);
}
}
}
==JDK9的新特性:==
try的前边可以定义流对象
在try后边的()中直接引用流对象的名称即可(即变量名)
在try代码执行完毕之后,流对象也可以释放掉,所以不用写finally
==格式:==
A a = new A();
B b = new B();
try(a,b){
可能会产生异常的代码
}catch(异常变量 变量名){
异常的处理逻辑
}
package top.lukeewin.demo15IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo13 {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("D:\\Works\\javalianxi\\a1.txt");
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\a\\b\\c\\a1.txt");
try(fis;fos){
int len = 0;
while ((len = fis.read())!=-1){
fos.write(len);
}
}catch (IOException e){
System.out.println(e);
}
}
}
Properties集合
java.util.Properties extends Hashtable<k,v> implements Map<k,v>
Properties
类表示了一个持久的属性集合。Properties
可保持在流中或从流中加载。
Properties
集合是一个唯一和IO流相结合的集合
可以使用Properties
集合中的方法==store==,把集合中的临时数据持久化,写入到硬盘中存储
可以使用Properties
集合中的方法==load==,把硬盘中的文件(键值对),读取到集合中使用
属性列表中每一个键对应一个值都是字符串。
Properties
集合是一个双列集合,key
和value
默认都是字符串
常用方法有:
Object setProperties(String key,String value)
调用Hashtable的方法putString getProperties(String key)
通过key
找到value
,此方法相当于Map
集合中的get(key)
方法set<String> StringPropertiesNames()
返回此属性列表中的键的set集合,其中该键对应的值是字符串,此方法相当于Map
集合中的ketSet
方法
private static void show01() {
//创建Properties集合
Properties prop = new Properties();
//使用setProperties方法,往集合中添加数据
//注意必须是字符串
// prop.setProperty("赵丽颖",168);//错误写法,必须k和v都为字符串类型
prop.setProperty("赵丽颖","168");
prop.setProperty("古力娜扎","165");
prop.setProperty("迪丽热巴","160");
// prop.put(1,true);//也可以用put方法,put方法不规定类型,也不推荐使用put方法
//使用stringPropertiesNames方法把Properties集合中的每一个键都取出来,放到Set集合中
Set<String> set = prop.stringPropertyNames();
//遍历Set集合
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}
可以使用Properties集合中的方法store,把集合中的临时数据,持久化地写入到硬盘中
void store(OutputStream out ,String comments)
void store(Writer writer ,String comments)
参数:
OutputStream out:字节输出流,不能写入中文
Writer writer:字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件是做什么用的
==注意:不能使用中文,会产生乱码,因为默认是使用Unicode编码,而系统是使用GBK编码,所以一般使用空字符串==
使用步骤:
- 创建Properties集合对象,添加数据
- 创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
- 使用Properties集合中的方法store,把集合中的临时数据持久化写入到硬盘中
- 释放资源
private static void show02() throws IOException {
//1.创建Properties集合
Properties prop = new Properties();
prop.setProperty("赵丽颖","168");
prop.setProperty("古力娜扎","165");
prop.setProperty("迪丽热巴","160");
//2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
FileWriter fw = new FileWriter("D:\\Works\\javalianxi\\a\\b\\c\\prop.txt");
//使用字节流,注意中文会产生乱码
//prop.store(new FileOutputStream("D:\\Works\\javalianxi\\a\\b\\c\\prop2.txt"),"");
//3.使用Properties集合中的方法store,把集合中的数据,持久化地写入到硬盘中
prop.store(fw,"save data");
fw.close();
}
可以使用Properties集合中的方法load,把硬盘中的数据读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
参数:
InputStream inStream:字节输入流,不能读取含中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对
使用步骤:
- 创建Properties集合对象
- 使用Properties集合对象中的方法load获取保存键值对的文件
- 遍历Properties集合
注意:
- 存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其它符号)
- 存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
- 存储键值对的文件中,键与值默认都是字符串,不用加上双引号
private static void show03() throws IOException {
//1.创建Properties集合
Properties prop = new Properties();
//2.使用load方法读取文件到内存中
prop.load(new FileReader("D:\\Works\\javalianxi\\a\\b\\c\\prop.txt"));
//如果使用字节输入流,含有中文,会乱码
//prop.load(new FileInputStream("D:\\Works\\javalianxi\\a\\b\\c\\prop.txt"));
//3.遍历Properties集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}
缓冲流
java.io.BufferedOutputStream extends OutputStream
BufferedOutputStream
:字节缓冲输出流
继承了父类的方法:
public void close()
:关闭流public void flush()
:刷新数据public void write(byte[] b)
:将b.length字节从指定的字节数组写入此输出流public void write(byte[] b,int off,int len)
:从指定的字节数组中写入len字节,从偏移量off开始输出到输出流public abstract void write(int b)
:将指定的字节输出流
==构造方法==:
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流BufferedOutputStream(OutputStream out ,int size)
创建一个新的缓冲输出流,指定缓冲区大小
参数:
OutputStream out
:字节输出流
我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size
:指定缓冲区的大小,不指定,则默认
==使用步骤:==
- 创建
FileOutputStream
对象,构造方法中绑定要输出的目的地 - 创建
BufferedOutputStream
对象,构造方法中传递FileOutputStream
实例 - 使用
BufferedOutputStream
对象中的方法write
,把数据写入到内部的缓冲区中 - 使用
BufferedOutputStream
对象中的方法flush,把内部缓冲区中的数据,刷新到文件中 - 释放资源(会先调用
flush()
刷新数据,所以第4步可以省略)
package top.lukeewin.demo17Buffered;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo01BufferedOutputStream {
public static void main(String[] args) throws IOException {
//1.创建FileOutputStream 对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream("D:\\Works\\javalianxi\\a2.txt");
//2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream实例
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3.使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
bos.write("数据写入到缓冲区中,然后写入到文件中".getBytes());
//4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
bos.flush();
//5.释放资源(会先调用flush()刷新数据,所以第4步可以省略)
bos.close();
}
}
Q.E.D.