目录:
- Object类、常用API
- Collection、泛型
- List、Set、数据结构、Collections
- Map与斗地主案例
- 异常、线程
- 线程、同步
- 等待与唤醒案例、线程池、Lambda表达式
- File类、递归
- 字节流、字符流
- 缓冲流、转换流、序列化流、Files
- 网络编程 十二、函数式接口
- Stream流、方法引用
一、Object类、常用API
二、Collection、泛型
三、List、Set、数据结构、Collections
四、Map与斗地主案例
五、异常、线程
目标:
1、说出进程的概念
2、说出线程的概念
3、能够理解并发与并行的区别
4、能够开启新线程
4.1 并发与并行
- 并发:指两个或多个事件在同一个时间段内发生。
- 并行:指两个或多个事件在同一时刻发生(同时发生)。
4.2 线程与进程
-
进程:
是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。 -
进程:
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
理解版:
-
进程: 进入到内存的程序,叫作“进程”。
在ROM(永久)存储的应用程序通过点击运行,进入RAM(临时)内存中 -
进程: 点击运行应用程序or功能就会开启一条应用程序到CPU的执行路径,这个路径有个名字,叫作“线程”。
进程:
线程:
线程调度:
-
分时调度:
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。 -
抢占式调度:
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
抢占式调度详解
大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,”感觉这些软件好像在同一时刻运行着“。
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
主线程: 执行主方法(main)的线程
单线程程序:Java程序中只有一个线程,执行从main方法开始,从上到下
4.3 创建线程类之一
Java使用 java.lang.Thread 类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
第一种方法:
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
实现步骤:
1、创建一个Thread的子类(自定一个继承thread的类)。
2、在Thread的子类中重写run方法,设置线程任务(开启线程要做什么)。
3、创建Thread类的对象
4、调用Thread类中的start方法,开启新的线程,执行run方法
void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(main线程)和另一个线程(执行其 run 方法)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。(start完就没了,不能够继续start)Java程序属于抢占式调度,哪个线程优先级高,哪个线程优先执行;同一个优先级,随机选择执行(优先级相等,谁线抢占cpu谁就先执行)。
Demo:
Thread的子类
public class Mythread extends Thread {
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("run:"+i);
}
}
}
测试类
public class MyThread_Demo {
public static void main(String[] args) {
Mythread run = new Mythread();
run.start();
for (int i = 0; i < 20; i++) {
System.out.println("main:"+i);
}
}
}
结果:main线程和run线程的执行顺序是随机的,不是固定的。
因为他们的线程优先级是一样的,所以抢占cpu资源的机会是平分的,谁先抢到了,谁就先执行。
六、线程、同步
目标:
- 能够描述Java中多线程运行原理
- 能够使用继承类的方式创建多线程
- 能够使用实现接口的方式创建多线程
- 能够说出实现接口方式的好处
- 能够解释安全问题的出现的原因
- 能够使用同步代码块解决线程安全问题
- 能够使用同步方法解决线程安全问题
- 能够说出线程6个状态的名称
1.1 多线程原理(结合上面的Demo代码)
-------------------------------------------------------------不明白?那就看一下详细的吧!----------------------------------------------------------------------------
Demo Code:
自定义类
public class MyThread extends Thread{
/*
* 利用继承中的特点
* 将线程名称传递 进行设置
*/
public MyThread(String name){
super(name);
}
/*
* 重写run方法
* 定义线程要执行的代码
*/
public void run(){
for (int i = 0; i < 20; i++) {
//getName()方法 来自父亲
System.out.println(getName()+i);
}
}
}
测试类
public class Demo {
public static void main(String[] args) {
System.out.println("这里是main线程");
MyThread mt = new MyThread("小强");
mt.start();//开启了一个新的线程
for (int i = 0; i < 20; i++) {
System.out.println("旺财:"+i);
}
}
}
流程图:
说明:
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。
通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。
简易版本:
请注意mt.run()与mt.start()的区别。
1.2 Thread类之获取线程名称
Thread的子类
/*
获取线程名称:
1、使用THread类中的getName方法
String getName() 返回该线程的名称。
2、可以获取到当前正在执行的线程,使用线程中的方法getName()获取线程名称
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
*/
//定义一个Thread的子类
public class Mythread extends Thread{
//重写Thread中run方法,设置线程任务
@Override
public void run() {
//获取线程名称
//第一种:getName方法
String name = getName();
System.out.println(name);
//第二种:currentThread静态方法
Thread t = Thread.currentThread();//为什么可以使用类名调用该方法?因为是静态方法
System.out.println(t);
//第三种:链式编程
System.out.println(Thread.currentThread().getName());//currentThread()返回的是线程对象的引用
//所以可以调用getName方法
}
测试类
/*
线程名称:
主线程:main
新线程:Thread-0、Thread-1、Thread-2
*/
public class MyThread_Demo {
public static void main(String[] args) {
//创建Thread子类的对象
Mythread mt = new Mythread();
//调用start方法,开启新线程,执行run方法
mt.start();//Thread-1
//使用匿名对象调用start
new Mythread().start();//Thread[Thread-2,5,main] //start一个就新创建一个线程
new Mythread().start();//Thread[Thread-0,5,main]
//第三种:链式编程
System.out.println(Thread.currentThread().getName());
}
}
1.2 Thread类之设置线程名称(了解)
Thread的子类
/*
设置线程名称:(了解)
1、使用Thread类中的方法setName(名字)
void setName(String name) 改变线程名称,使之与参数 name 相同。
2、创建一个带参构造方法,参数传递线程的名称;调用父类的带参构造方法,把线程名称传递给父类,让父类(Thread)给子子线程起一个名字
Thread(String name) 分配新的 Thread 对象。
*/
public class Mythread extends Thread{
//方法二:带参构造
public Mythread(){
};
public Mythread(String name){
super(name);
};
//方法一:
@Override
public void run() {
System.out.println(getName());
}
}
测试类
public class MyThread_Demo {
public static void main(String[] args) {
Mythread mt = new Mythread();
// 方法一:开启线程
mt.setName("小强");
mt.start();
//方法二:开启线程
new Mythread("旺财").start();
}
}
1.2 Thread类之常用方法
利用sleep方法制作秒表:
public class MyThread_Demo {
public static void main(String[] args) {
//模拟秒表
for (int i = 1; i < 60; i++) {
System.out.println(i);
//使用Thread类的sleep方法让程序睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
1.3 创建线程之二(尽量使用)
采用 java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。(也就是,让Runnable的实现类对象作为Thread的参数传递进去,这样的Thread才变成线程对象)
- 调用线程对象的start()方法来启动线程。
Runnable实现类
/*
创建多线程程序的第二种实现方式:实现Runnable (只有run方法)
java.lang.Runnable
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
java.lang.Thread
Thread(Runnable target) 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。
实现步骤:
1-创建一个Runnable接口的实现类
2-在实现类中重写Runnable接口的run方法,设置线程任务
3-创建一个Runnable接口的实现类对象
4-创建Thread类对象,构造方法中传递Runnable接口的实现类对象
5-调用Thread类中的start方法,开启新的线程执行run方法
*/
public class RunnableDemo implements Runnable{ //1-创建一个Runnable接口的实现类
//2-在实现类中重写Runnable接口的run方法,设置线程任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
测试类
public class Runnable_Demo {
public static void main(String[] args) {
//3-创建一个Runnable接口的实现类对象
RunnableDemo run = new RunnableDemo();
//4-创建Thread类对象,构造方法中传递Runnable接口的实现类对象
Thread t = new Thread(run,"小强");
//5-调用Thread类中的start方法,开启新的线程执行run方法
t.start();
//主线程
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
1.4 Thread和Runnable的区别
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
解释:
1-避免单继承的局限性
一个类只能有一个父类(一个人只有一个亲爸),子类继承了Thread类就不能继承其他类了。
2-增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把【设置线程任务】和【开启新的线程】进行了分离。(解耦)
-
Runnable实现类中,重写了run方法:用来设置线程任务
-
测试类中创建Thread类对象,把不同的Runnable实现类对象作为参数传给Thread对象调用不同的run方法。
一个重写run,另一个调用run,开启新的线程。
扩充:
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。
1.5 匿名内部类方式实现线程的创建
匿名:没有名字
内部类:写在其他类内部的类
匿名内部类的作用:简化代码
过程:
-
把【子类继承父类、重写父类的方法、创建子类对象】合一完成
-
把【实现类接口、重写接口中的方法、创建实现类对象】合成完成
内部类的最终产物是【子类/实现类对象】,而这个类没有名字。
格式:
new 父类/接口{
重写父类/接口的方法
};
Demo Dode:
public class Runnable_Demo {
public static void main(String[] args) {
//线程的父类是Thread
//之前是MyThread mt = new MyThread();等于Thread t = new MyThread();也等于Thread t = new Thread();[多态]
new Thread() {
//重写run,设置线程任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + "我是Thread");
}
}
}.start();
//线程的接口是Runnable
//Runnable r = new RunnbaleImpl();[同上:多态]
Runnable r = new Runnable() {
//重写run,设置线程任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + "我是Runnable");
}
}
};
new Thread(r).start();
//再一次简化Runnable接口的方式
new Thread(new Runnable() { //把参数内相当于new Thread(r).start()的r
//重写run,设置线程任务
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + "我是Runnable的加强简化");
}
}
}).start();
}
}
2.1 线程安全
线程安全的概述:
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
多线程代码实现
Runnable实现类
public class RunnableImpl implements Runnable {
//定义一个多线程共享的票源
int ticket = 100;
//设置线程任务:卖票
@Override
public void run() {
//死循环,让卖票操作重复运行
while (true){
//判断是否有票
if (ticket>0){
//提高安全问题出现的概率,让程序睡眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
测试类
/*
模拟卖票案例
创建3个线程,同时开启,对共享的票进行售出
*/
public class Demo2Ticket {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
RunnableImpl run = new RunnableImpl();
//创建Thread类对象,构造方法中传递Runbable接口的实现类
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
//调用start方法开启多线程
t0.start();
t1.start();
t2.start();
}
}
多线程原理
有意思的是,在开始的时候,如果t0抢到了cpu的执行权进入到run方法,遇到sleep也会失去cpu执行权,然后,t1、t2再开始抢夺,到sleep的时候也会陆续失去cpu执行权。当3个线程睡醒了,他们继续开始抢夺执行权。而且,继续执行的时候不是重头开始,而是在哪里睡着了,就在哪里醒来接着跑往下跑。(所以,下面跳过了if判断,出现0,-1)
图中,假设3个线程共享资源为1票,而t2优先抢夺成功并卖票,则资源变成0票,停止循环执行;然后,t1、t2会跟着抢夺执行,执行的时候是以资源为0执行,所以才出现0票和-1票。
怎么会出现开头出现3个线程同时打印3个100,下面都没有重复呢?
因为3个线程同时进入run方法时,从打印输出卖出几张票执行到ticket减减的时候,是需要时间的。
2.2 线程同步
线程同步的3种方式:
- 同步代码块。2. 同步方法。3. 锁机制。
2.3 同步代码块
同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
2. 锁对象 可以是任意类型。
3. 多个线程对象 要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
需要注意的是:
- Object lock = new Object();一定要设置在修改共享资源的外边。(为了只有1个锁对象)
- synchronized设置在哪里
把访问共享资源代码包起来。
同步技术原理
图中,t0抢到了执行权拿到了lock对象,进入sleep的时候,t1、t2再一次进行抢夺,但是发现没有对象锁的时候,只能进入阻塞状态,进行在外面等待t0执行完毕。
2.4 同步方法
Demo Code :
同步方法的对象是this,
静态同步方法:
2.5 Lock锁
还可以配合finally这么玩:不管有没有出现一次,都会释放锁
3.1 线程的状态
3.2 Timed Waiting(计时等待)
3.3 BLOCKED(锁阻塞)
3.4 Waiting(无限等待)
等待唤醒案例的分析:
等待唤醒案例:
等待唤醒案例:线程之间的通信
创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子
注意:
顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行
同步使用的锁对象必须保证唯一
只有锁对象才能调用wait和notify方法
Obejct类中的方法
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
会继续执行wait方法之后的代码
public class Demo01WaitAndNotify {
public static void main(String[] args) {
//创建锁对象,保证唯一
Object obj = new Object();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
//一直等着买包子
while(true){
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("告知老板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,开吃!");
System.out.println("---------------------------------------");
}
}
}
}.start();
//创建一个老板线程(生产者)
new Thread(){
@Override
public void run() {
//一直做包子
while (true){
//花了5秒做包子
try {
Thread.sleep(5000);//花5秒钟做包子
} catch (InterruptedException e) {
e.printStackTrace();
}
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("老板5秒钟之后做好包子,告知顾客,可以吃包子了");
//做好包子之后,调用notify方法,唤醒顾客吃包子
obj.notify();
}
}
}
}.start();
}
}
3.5 Object类中wait带参方法和notifyAll方法
进入到TimeWaiting(计时等待)有两种方式
1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
唤醒的方法:
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。
public class Demo02WaitAndNotify {
public static void main(String[] args) {
//创建锁对象,保证唯一
Object obj = new Object();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
//一直等着买包子
while(true){
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("顾客1告知老板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,顾客1开吃!");
System.out.println("---------------------------------------");
}
}
}
}.start();
// 创建一个顾客线程(消费者)
new Thread(){
@Override
public void run() {
//一直等着买包子
while(true){
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("顾客2告知老板要的包子的种类和数量");
//调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,顾客2开吃!");
System.out.println("---------------------------------------");
}
}
}
}.start();
//创建一个老板线程(生产者)
new Thread(){
@Override
public void run() {
//一直做包子
while (true){
//花了5秒做包子
try {
Thread.sleep(5000);//花5秒钟做包子
} catch (InterruptedException e) {
e.printStackTrace();
}
//保证等待和唤醒的线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("老板5秒钟之后做好包子,告知顾客,可以吃包子了");
//做好包子之后,调用notify方法,唤醒顾客吃包子
//obj.notify();//如果有多个等待线程,随机唤醒一个
obj.notifyAll();//唤醒所有等待的线程
}
}
}
}.start();
}
}
3.5 补充知识点
七、等待与唤醒案例、线程池、Lambda表达式
1.1 线程间通信
1.2 等待与唤醒机制
1.3 生产者与消费者问题
2.1 线程池思想概述
2.2 线程池概念
2.3 线程池的使用
线程池: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()
线程池的使用步骤:
1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
3.1 函数式编程思想概
3.2 冗余的Runnable代码
3.3 编程思想转换
3.4 体验Lambda的更优写法
3.5 回顾匿名内部类
3.6 Lambda的格式
Lambda表达式的标准格式:
由三部分组成:
a.一些参数
b.一个箭头
c.一段代码
格式:
(参数列表) -> {一些重写方法的代码};
解释说明格式:
():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔
->:传递的意思,把参数传递给方法体{}
{}:重写接口的抽象方法的方法体
3.7 练习:使用Lambda标准格式(无参无返回)
3.8 Lambda的参数和返回值
3.9 练习:使用Lambda标准格式(有参有返回)
3.10 Lambda省略格式
Lambda表达式:是可推导,可以省略
凡是根据上下文推导出来的内容,都可以省略书写
可以省略的内容:
1.(参数列表):括号中参数列表的数据类型,可以省略不写
2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略
3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)
注意:要省略{},return,分号必须一起省略
3.11 练习:使用Lambda省略格式
八、File类、递归
1.1 概述
java.io.File类
文件和目录路径名的抽象表示形式。
java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作
我们可以使用File类的方法
创建一个文件/文件夹
删除文件/文件夹
获取文件/文件夹
判断文件/文件夹是否存在
对文件夹进行遍历
获取文件的大小
File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法
重点:记住这三个单词
file:文件
directory:文件夹/目录
path:路径
File类的静态成员:
static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
static char pathSeparatorChar 与系统有关的路径分隔符。
static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
static char separatorChar 与系统有关的默认名称分隔符。
操作路径:路径不能写死了
C:developaa.txt windows
C:/develop/a/a.txt linux
"C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);//路径分隔符 windows:分号; linux:冒号:
String separator = File.separator;
System.out.println(separator);// 文件名称分隔符 windows:反斜杠 linux:正斜杠/
绝对路径和相对路径
路径:
绝对路径:是一个完整的路径
以盘符(c:,D:)开始的路径
c:\a.txt
C:\Usersitcast\IdeaProjects\shungyuan\123.txt
D:\demo\b.txt
相对路径:是一个简化的路径
相对指的是相对于当前项目的根目录(C:\Usersitcast\IdeaProjects\shungyuan)
如果使用当前项目的根目录,路径可以简化书写
C:\Usersitcast\IdeaProjects\shungyuan\123.txt-->简化为: 123.txt(可以省略项目的根目录)
注意:
1.路径是不区分大小写
2.路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠
1.2 构造方法
public static void main(String[] args) {
/*
File类的构造方法
*/
//show02("c:\","a.txt");//c:a.txt
//show02("d:\","a.txt");//d:a.txt
show03();
File f = new File("C:\Users\itcast\IdeaProjects\shungyuan");
long length = f.length();
System.out.println(length);
}
/*
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
参数:把路径分成了两部分
File parent:父路径
String child:子路径
好处:
父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
*/
private static void show03() {
File parent = new File("c:\");
File file = new File(parent,"hello.java");
System.out.println(file);//c:hello.java
}
/*
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
参数:把路径分成了两部分
String parent:父路径
String child:子路径
好处:
父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
*/
private static void show02(String parent, String child) {
File file = new File(parent,child);
System.out.println(file);//c:a.txt
}
/*
File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
参数:
String pathname:字符串的路径名称
路径可以是以文件结尾,也可以是以文件夹结尾
路径可以是相对路径,也可以是绝对路径
路径可以是存在,也可以是不存在
创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
*/
private static void show01() {
File f1 = new File("C:\Users\itcast\IdeaProjects\shungyuan\a.txt");
System.out.println(f1);//重写了Object类的toString方法 C:UsersitcastIdeaProjectsshungyuana.txt
File f2 = new File("C:\Users\itcast\IdeaProjects\shungyuan");
System.out.println(f2);//C:UsersitcastIdeaProjectsshungyuan
File f3 = new File("b.txt");
System.out.println(f3);//b.txt
}
}
1.3 常用方法
创建删除的方法
/*
File类判断功能的方法
- public boolean exists() :此File表示的文件或目录是否实际存在。
- public boolean isDirectory() :此File表示的是否为目录。
- public boolean isFile() :此File表示的是否为文件。
*/
public class Demo04File {
public static void main(String[] args) {
show02();
}
/*
public boolean isDirectory() :此File表示的是否为目录。
用于判断构造方法中给定的路径是否以文件夹结尾
是:true
否:false
public boolean isFile() :此File表示的是否为文件。
用于判断构造方法中给定的路径是否以文件结尾
是:true
否:false
注意:
电脑的硬盘中只有文件/文件夹,两个方法是互斥
这两个方法使用前提,路径必须是存在的,否则都返回false
*/
private static void show02() {
File f1 = new File("C:\Users\itcast\IdeaProjects\shung");
//不存在,就没有必要获取
if(f1.exists()){
System.out.println(f1.isDirectory());
System.out.println(f1.isFile());
}
File f2 = new File("C:\Users\itcast\IdeaProjects\shungyuan");
if(f2.exists()){
System.out.println(f2.isDirectory());//true
System.out.println(f2.isFile());//false
}
File f3 = new File("C:\Users\itcast\IdeaProjects\shungyuan\shungyuan.iml");
if(f3.exists()){
System.out.println(f3.isDirectory());//false
System.out.println(f3.isFile());//true
}
}
/*
public boolean exists() :此File表示的文件或目录是否实际存在。
用于判断构造方法中的路径是否存在
存在:true
不存在:false
*/
private static void show01() {
File f1 = new File("C:\Users\itcast\IdeaProjects\shungyuan");
System.out.println(f1.exists());//true
File f2 = new File("C:\Users\itcast\IdeaProjects\shung");
System.out.println(f2.exists());//false
File f3 = new File("shungyuan.iml");//相对路径 C:UsersitcastIdeaProjectsshungyuanshungyuan.iml
System.out.println(f3.exists());//true
File f4 = new File("a.txt");
System.out.println(f4.exists());//false
}
}
File类的创建删除方法
/*
File类创建删除功能的方法
- public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
- public boolean delete() :删除由此File表示的文件或目录。
- public boolean mkdir() :创建由此File表示的目录。
- public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
*/
public class Demo05File {
public static void main(String[] args) throws IOException {
show03();
}
/*
public boolean delete() :删除由此File表示的文件或目录。
此方法,可以删除构造方法路径中给出的文件/文件夹
返回值:布尔值
true:文件/文件夹删除成功,返回true
false:文件夹中有内容,不会删除返回false;构造方法中路径不存在false
注意:
delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎
*/
private static void show03() {
File f1 = new File("08_FileAndRecursion\新建文件夹");
boolean b1 = f1.delete();
System.out.println("b1:"+b1);
File f2 = new File("08_FileAndRecursion\abc.txt");
System.out.println(f2.delete());
}
/*
public boolean mkdir() :创建单级空文件夹
public boolean mkdirs() :既可以创建单级空文件夹,也可以创建多级文件夹
创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值
true:文件夹不存在,创建文件夹,返回true
false:文件夹存在,不会创建,返回false;构造方法中给出的路径不存在返回false
注意:
1.此方法只能创建文件夹,不能创建文件
*/
private static void show02() {
File f1 = new File("08_FileAndRecursion\aaa");
boolean b1 = f1.mkdir();
System.out.println("b1:"+b1);
File f2 = new File("08_FileAndRecursion\111\222\333\444");
boolean b2 = f2.mkdirs();
System.out.println("b2:"+b2);
File f3 = new File("08_FileAndRecursion\abc.txt");
boolean b3 = f3.mkdirs();//看类型,是一个文件
System.out.println("b3:"+b3);
File f4 = new File("08_F\ccc");
boolean b4 = f4.mkdirs();//不会抛出异常,路径不存在,不会创建
System.out.println("b4:"+b4);
}
/*
public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
创建文件的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值
true:文件不存在,创建文件,返回true
false:文件存在,不会创建,返回false
注意:
1.此方法只能创建文件,不能创建文件夹
2.创建文件的路径必须存在,否则会抛出异常
public boolean createNewFile() throws IOException
createNewFile声明抛出了IOException,我们调用这个方法,就必须的处理这个异常,要么throws,要么trycatch
*/
private static void show01() throws IOException {
File f1 = new File("C:\Users\itcast\IdeaProjects\shungyuan\08_FileAndRecursion\1.txt");
boolean b1 = f1.createNewFile();
System.out.println("b1:"+b1);
File f2 = new File("08_FileAndRecursion\2.txt");
System.out.println(f2.createNewFile());
File f3 = new File("08_FileAndRecursion\新建文件夹");
System.out.println(f3.createNewFile());//不要被名称迷糊,要看类型
File f4 = new File("08_FileAndRecursi\3.txt");
System.out.println(f4.createNewFile());//路径不存在,抛出IOException
}
}
1.4 遍历文件夹(目录)功能
/*
File类遍历(文件夹)目录功能
public String[] list() :返回一个String数组,表示该File目录中的所有子文
件或目录。
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的
子文件或目录。
注意:
list方法和listFiles方法遍历的是构造方法中给出的目录
如果构造方法中给出的目录的路径不存在,会抛出空指针异常
如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
public class Demo06File {
public static void main(String[] args) {
show02();
}
/*
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
遍历构造方法中给出的目录,会获取目录中所有的文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中
*/
private static void show02() {
File file = new File("C:\Users\itcast\IdeaProjects\shungyuan\08_FileAndRecursion");
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f);
}
}
/*
public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
*/
private static void show01() {
//File file = new File("C:\Users\itcast\IdeaProjects\shungyuan\08_FileAndRecursion\1.txt");//NullPointerException
//File file = new File("C:\Users\itcast\IdeaProjects\shungyuan\08_Fi");//NullPointerException
File file = new File("C:\Users\itcast\IdeaProjects\shungyuan\08_FileAndRecursion");
String[] arr = file.list();
for (String fileName : arr) {
System.out.println(fileName);
}
}
}
2.1 递归概念&分类&注意事项
/*
递归:方法自己调用自己
- 递归的分类:
- 递归分为两种,直接递归和间接递归。
- 直接递归称为方法自身调用自己。
- 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
- 注意事项:
- 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢
出。
- 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存
溢出。
- 构造方法,禁止递归
递归的使用前提:
当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递
归
*/
public class Demo01Recurison {
public static void main(String[] args) {
//a();
b(1);
}
/*
构造方法,禁止递归
编译报错:构造方法是创建对象使用的,一直递归会导致内存中有无数多个对象,直接编译报错
*/
public Demo01Recurison() {
//Demo01Recurison();
}
/*
在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
11157
Exception in thread "main" java.lang.StackOverflowError
*/
private static void b(int i) {
System.out.println(i);
if(i==20000){
return; //结束方法
}
b(++i);
}
/*
递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
Exception in thread "main" java.lang.StackOverflowError
*/
private static void a() {
System.out.println("a方法!");
a();
}
}
九、字节流、字符流
第二章 字节流
------------------------------字节输出流
/*
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对象指向创建好的文件
写入数据的原理(内存-->硬盘)
java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中
字节输出流的使用步骤(重点):
1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
2.调用FileOutputStream对象中的方法write,把数据写入到文件中
3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
*/
Demo :
public class Demo01OutputStream {
public static void main(String[] args) throws IOException {
//1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
FileOutputStream fos = new FileOutputStream("09_IOAndProperties\a.txt");
//2.调用FileOutputStream对象中的方法write,把数据写入到文件中
//public abstract void write(int b) :将指定的字节输出流。
fos.write(97);
//3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
//fos.close();
}
}
一次读取一个字节:
package com.itheima.demo01.OutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
/*
一次写多个字节的方法:
- public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
- public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
*/
public class Demo02OutputStream {
public static void main(String[] args) throws IOException {
//创建FileOutputStream对象,构造方法中绑定要写入数据的目的地
FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\b.txt"));
//调用FileOutputStream对象中的方法write,把数据写入到文件中
//在文件中显示100,写个字节
fos.write(49);
fos.write(48);
fos.write(48);
/*
public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
一次写多个字节:
如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
*/
byte[] bytes = {65,66,67,68,69};//ABCDE
//byte[] bytes = {-65,-66,-67,68,69};//烤紻E
fos.write(bytes);
/*
public void write(byte[] b, int off, int len) :把字节数组的一部分写入到文件中
int off:数组的开始索引
int len:写几个字节
*/
fos.write(bytes,1,2);//BC
/*
写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组
byte[] getBytes() 把字符串转换为字节数组
*/
byte[] bytes2 = "你好".getBytes();
System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67]
fos.write(bytes2);
//释放资源
fos.close();
}
}
一次读取一个字节:
注意:
1、new FileInputStream()之后会指向文件源,而且指针是指向文件的第1个字节,也就是a
2、调用fis.read()方法后,原本指向a的指针会往后跑1个字节,也就是跑到b
其中,read方法找JVM,JVM找OS,OS调用方法把a传给JVM,JVM再返回给read()
一次性读取多字节:
注意:
1:new FileInputStream()指向文件源,且指针指向文件源中第1个字节
2:创建了1个长度为2的空字节数组
3:read(bytes)读取的字节存入数组中,存进2个字节,即“A,B”,
再调用获取的是下2个(C,D),这时候ABCDE中只有E没有获取
再调用获取的是E,就是单单的E,至于为什么输出的时候是ED
是因为重复调用read(bytes)获取是把新的字符覆盖掉已存有的字节。
图中,获取C、D之后,只剩E,再调用获取,E只是把C给覆盖了,D没有被覆盖,所以还是存在,所以打印的时候输出E、D
补充:window系统在文件系统会有一个结束标记(看不见的),当指针指向“结束标记”的时候,就返回-1,返回-1后,read()方法执行结束
数据追加续写
追加写/续写:使用两个参数的构造方法
FileOutputStream(String name, boolean append)创建一个向具有指定 name 的文件中写入数据的输出文件流。
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
参数:
String name,File file:写入数据的目的地
boolean append:追加写开关
true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
false:创建一个新文件,覆盖源文件
写换行:写换行符号
windows:
linux:/n
mac:/r
Demo:
public class Demo03OutputStream {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("09_IOAndProperties\c.txt",true);
for (int i = 1; i <=10 ; i++) {
fos.write("你好".getBytes());
fos.write("
".getBytes());
}
fos.close();
}
}
----------------------------------字节输入流
java.io.InputStream:字节输入流
此抽象类是表示字节输入流的所有类的超类。
定义了所有子类共性的方法:
int read()从输入流中读取数据的下一个字节。
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
void close() 关闭此输入流并释放与该流关联的所有系统资源。
java.io.FileInputStream extends InputStream
FileInputStream:文件字节输入流
作用:把硬盘文件中的数据,读取到内存中使用
构造方法:
FileInputStream(String name)
FileInputStream(File file)
参数:读取文件的数据源
String name:文件的路径
File file:文件
构造方法的作用:
1.会创建一个FileInputStream对象
2.会把FileInputStream对象指定构造方法中要读取的文件
读取数据的原理(硬盘-->内存)
java程序-->JVM-->OS-->OS读取数据的方法-->读取文件
字节输入流的使用步骤(重点):
1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.使用FileInputStream对象中的方法read,读取文件
3.释放资源
一次读取多个字节
字节输入流一次读取多个字节的方法:
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
明确两件事情:
1.方法的参数byte[]的作用?
起到缓冲作用,存储每次读取到的多个字节
数组的长度一把定义为1024(1kb)或者1024的整数倍
2.方法的返回值int是什么?
每次读取的有效字节个数
String类的构造方法
String(byte[] bytes) :把字节数组转换为字符串
String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
文件复制
文件复制练习:一读一写
明确:
数据源: c:\1.jpg
数据的目的地: d:\1.jpg
文件复制的步骤:
1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
3.使用字节输入流对象中的方法read读取文件
4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
5.释放资源
第三章 字符流
----------------------------输入流
java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
共性的成员方法:
int read() 读取单个字符并返回。
int read(char[] cbuf)一次读取多个字符,将字符读入数组。
void close() 关闭该流并释放与之关联的所有资源。
java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中
构造方法:
FileReader(String fileName)
FileReader(File file)
参数:读取文件的数据源
String fileName:文件的路径
File file:一个文件
FileReader构造方法的作用:
1.创建一个FileReader对象
2.会把FileReader对象指向要读取的文件
字符输入流的使用步骤:
1.创建FileReader对象,构造方法中绑定要读取的数据源
2.使用FileReader对象中的方法read读取文件
3.释放资源
public class Demo02Reader {
public static void main(String[] args) throws IOException {
//1.创建FileReader对象,构造方法中绑定要读取的数据源
FileReader fr = new FileReader("09_IOAndProperties\c.txt");
//2.使用FileReader对象中的方法read读取文件
//int read() 读取单个字符并返回。
/*int len = 0;
while((len = fr.read())!=-1){
System.out.print((char)len);
}*/
//int read(char[] cbuf)一次读取多个字符,将字符读入数组。
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));
}
//3.释放资源
fr.close();
}
}
----------------------------输入出
java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类
共性的成员方法:
- void write(int c) 写入单个字符。
- void write(char[] cbuf)写入字符数组。
- abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
- void write(String str)写入字符串。
- void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
- void flush()刷新该流的缓冲。
- void close() 关闭此流,但要先刷新它。
java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter:文件字符输出流
作用:把内存中字符数据写入到文件中
构造方法:
FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象。
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。
参数:写入数据的目的地
String fileName:文件的路径
File file:是一个文件
构造方法的作用:
1.会创建一个FileWriter对象
2.会根据构造方法中传递的文件/文件的路径,创建文件
3.会把FileWriter对象指向创建好的文件
字符输出流的使用步骤(重点):
1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
Flush与close区别
flush方法和close方法的区别
- flush :刷新缓冲区,流对象可以继续使用。
- close: 先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
Demo :
public class Demo02CloseAndFlush {
public static void main(String[] args) throws IOException {
//1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
FileWriter fw = new FileWriter("09_IOAndProperties\e.txt");
//2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
//void write(int c) 写入单个字符。
fw.write(97);
//3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
fw.flush();
//刷新之后流可以继续使用
fw.write(98);
//4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
fw.close();
//close方法之后流已经关闭了,已经从内存中消失了,流就不能再使用了
fw.write(99);//IOException: Stream closed
}
}
字节输出流的其他方法
字符输出流写数据的其他方法
- void write(char[] cbuf)写入字符数组。
- abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
- void write(String str)写入字符串。
- void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
Demo:
public class Demo03Writer {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("09_IOAndProperties\f.txt");
char[] cs = {'a','b','c','d','e'};
//void write(char[] cbuf)写入字符数组。
fw.write(cs);//abcde
//void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
fw.write(cs,1,3);//bcd
//void write(String str)写入字符串。
fw.write("传智播客");//传智播客
//void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
fw.write("黑马程序员",2,3);//程序员
fw.close();
}
}
数据追加续写:
续写和换行
续写,追加写:使用两个参数的构造方法
FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
参数:
String fileName,File file:写入数据的目的地
boolean append:续写开关 true:不会创建新的文件覆盖源文件,可以续写; false:创建新的文件覆盖源文件
换行:换行符号
windows:
linux:/n
mac:/r
public class Demo04Writer {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("09_IOAndProperties\g.txt",true);
for (int i = 0; i <10 ; i++) {
fw.write("HelloWorld"+i+"
");
}
fw.close();
}
}
第四章 IO异常的处理
JDK7前
在jdk1.7之前使用try catch finally 处理流中的异常
格式:
try{
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}finally{
一定会指定的代码
资源释放
}
---------------------------------------------------------------
public class Demo01TryCatch {
public static void main(String[] args) {
//提高变量fw的作用域,让finally可以使用
//变量在定义的时候,可以没有值,但是使用的时候必须有值
//fw = new FileWriter("09_IOAndProperties\g.txt",true); 执行失败,fw没有值,fw.close会报错
FileWriter fw = null;
try{
//可能会产出异常的代码
fw = new FileWriter("w:\09_IOAndProperties\g.txt",true);
for (int i = 0; i <10 ; i++) {
fw.write("HelloWorld"+i+"
");
}
}catch(IOException e){
//异常的处理逻辑
System.out.println(e);
}finally {
//一定会指定的代码
//创建对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断,不是null在把资源释放
if(fw!=null){
try {
//fw.close方法声明抛出了IOException异常对象,所以我们就的处理这个异常对象,要么throws,要么try catch
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
JDK7后
JDK7的新特性
在try的后边可以增加一个(),在括号中可以定义流对象
那么这个流对象的作用域就在try中有效
try中的代码执行完毕,会自动把流对象释放,不用写finally
格式:
try(定义流对象;定义流对象....){
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}
----------------------------------------------------------------
public class Demo02JDK7 {
public static void main(String[] args) {
try(//1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("c:\1.jpg");
//2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
FileOutputStream fos = new FileOutputStream("d:\1.jpg");){
//可能会产出异常的代码
//一次读取一个字节写入一个字节的方式
//3.使用字节输入流对象中的方法read读取文件
int len = 0;
while((len = fis.read())!=-1){
//4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
fos.write(len);
}
}catch (IOException e){
//异常的处理逻辑
System.out.println(e);
}
}
}
JDK9后
JDK9新特性
try的前边可以定义流对象
在try后边的()中可以直接引入流对象的名称(变量名)
在try代码执行完毕之后,流对象也可以释放掉,不用写finally
格式:
A a = new A();
B b = new B();
try(a,b){
可能会产出异常的代码
}catch(异常类变量 变量名){
异常的处理逻辑
}
public class Demo03JDK9 {
public static void main(String[] args) throws IOException {
//1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("c:\1.jpg");
//2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
FileOutputStream fos = new FileOutputStream("d:\1.jpg");
try(fis;fos){
//一次读取一个字节写入一个字节的方式
//3.使用字节输入流对象中的方法read读取文件
int len = 0;
while((len = fis.read())!=-1){
//4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
fos.write(len);
}
}catch (IOException e){
System.out.println(e);
}
//fos.write(1);//Stream Closed
}
}
第五章 属性集
---------------------------------------------------------------------------------
java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties集合是一个唯一和IO流相结合的集合
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串。
Properties集合是一个双列集合,key和value默认都是字符串
---------------------------------------------------------------------------------
public class Demo01Properties {
public static void main(String[] args) throws IOException {
show03();
}
/*
可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)
参数:
InputStream inStream:字节输入流,不能读取含有中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对
使用步骤:
1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合
注意:
1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
3.存储键值对的文件中,键与值默认都是字符串,不用再加引号
*/
private static void show03() throws IOException {
//1.创建Properties集合对象
Properties prop = new Properties();
//2.使用Properties集合对象中的方法load读取保存键值对的文件
prop.load(new FileReader("09_IOAndProperties\prop.txt"));
//prop.load(new FileInputStream("09_IOAndProperties\prop.txt"));
//3.遍历Properties集合
Set<String> set = prop.stringPropertyNames();
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编码
一般使用""空字符串
使用步骤:
1.创建Properties集合对象,添加数据
2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
4.释放资源
---------------------------------------------------------------------------------
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("09_IOAndProperties\prop.txt");
//3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
//prop.store(fw,"save data");
//4.释放资源
//fw.close();
prop.store(new FileOutputStream("09_IOAndProperties\prop2.txt"),"");
}
---------------------------------------------------------------------------------
使用Properties集合存储数据,遍历取出Properties集合中的数据
Properties集合是一个双列集合,key和value默认都是字符串
Properties集合有一些操作字符串的特有方法
Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法
Set<String> stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
---------------------------------------------------------------------------------
private static void show01() {
//创建Properties集合对象
Properties prop = new Properties();
//使用setProperty往集合中添加数据
prop.setProperty("赵丽颖","168");
prop.setProperty("迪丽热巴","165");
prop.setProperty("古力娜扎","160");
//prop.put(1,true);
//使用stringPropertyNames把Properties集合中的键取出,存储到一个Set集合中
Set<String> set = prop.stringPropertyNames();
//遍历Set集合,取出Properties集合的每一个键
for (String key : set) {
//使用getProperty方法通过key获取value
String value = prop.getProperty(key);
System.out.println(key+"="+value);
}
}
}
十、缓冲流、转换流、序列化流、Files
十一、网络编程
十二、函数式接口
十三、Stream流、方法引用