如果程序出现了问题,我们没有做任何处理,最终JVM会做默认的处理 , 那么JVM是如何处理的呢 ?
注意 : 程序中出现了异常 , 会在当前位置创建此异常的对象 , 对象中包含了异常的信息 , 并把此异常交给本方法的调用者处理
缺点 : 用户体验不好
声明异常—— throws
作用 :
package com.itheima.exception_demo;
import sun.java2d.pipe.SpanShapeRenderer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
声明异常—— throws
格式 : 修饰符 返回值类型 方法名(参数列表) throws 异常类型1 , 异常的类型2... { ... }
举例 : public void show() throws NullPointerException , ArrayIndexOutOfBoundsException { .... }
作用 :
1 表示告知调用者当前的方法可能会出现某些异常,使用时需要注意哦!
2 如果当前方法没有出现任何异常, 那么代码会正常执行
3 如果当前方法中出现了异常 , 会把异常交给本方法调用者处理(甩锅)
需求 :
练习 : 定义两个方法一个运行时期异常 , 一个声明编译时期异常 !
注意 :
1 编译时异常因为在编译时就会检查,所以必须要写在方法后面进行显示声明
2 运行时异常因为在运行时才会发生,所以在方法后面可以不写
3 如果声明多个异常有子父类关系 , 那么只要声明一个父类即可(多态)
*/
public class Exception_Throws {
public static void main(String[] args) throws ParseException{
printArr();// 如果此方法出现了异常 , 会交给jvm进行处理
StringToDate();// 如果此方法出现了异常 , 会交给jvm进行处理
}
// 1 告诉调用者 , 此方法可能会出现异常哦
// 2 如果此方法没有出现异常 , 那么会正常执行
// 3 如果此方法中出现了异常 , 会把此异常交给调用者处理
// 注意 : 如果声明的异常是一个运行时期异常 , 那么此声明可以省略
public static void printArr() /*throws NullPointerException*/ {
int[] arr = null;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
// 1 告诉调用者 , 此方法可能会出现异常哦
// 2 如果此方法没有出现异常 , 那么会正常执行
// 3 如果此方法中出现了异常 , 会把此异常交给调用者处理
// 注意 : 如果声明的异常 是一个编译时期异常 , 那么在编译时期必须处理 , 要么程序无法执行
public static void StringToDate() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2000-03-11 12:12:12");
}
}
修饰符 返回值类型 方法名(参数列表) {
throw new 异常对象();
}
package com.itheima.exception_demo;
/*
抛出异常演示 :
格式 :
修饰符 返回值类型 方法名(参数列表) {
throw new 异常对象();
}
注意 :
1 抛出异常的格式必须在方法的内部完成
2 如果手动抛出一个异常,下面的代码无法执行
*/
public class Exception_Throw {
public static void main(String[] args) {
System.out.println("家里有一个貌美如花的老婆");
System.out.println("还有一个当官的兄弟");
System.out.println("自己还有一个买卖");
System.out.println("这样的生活你要不要?");
// 程序不想往下执行了 ,怎么做 ???
// 1 自己手动制造出一个异常
// 2 当前异常也是交给了方法的调用者处理 , 也就是jvm处理
// 3 下面代码无法执行
throw new RuntimeException();
// System.out.println("武大郎的标准生活!");
}
}
package com.itheima.exception_demo;
/*
抛出异常存在的意义所在 :
1 在方法中,当传递的参数有误,没有继续运行下去的意义了,则采取抛出处理,表示让该方法结束运行。
2 告诉调用者方法中出现了问题
练习 : 定义一个方法 , 方法的参数接收一个数组 , 在方法中遍历数组 .
需求1 : 如果方法接收的数组为null , 使用输出语句提示
需求2 : 如果方法接收的数组为null , 使用抛出异常解决
思考 : 两种方式的区别在哪里 ?
*/
public class Exception_Throw2 {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
arr = null;
// printArr1(arr);
printArr2(arr);// 接收方法返回的异常 , 但是此异常有jvm进行处理
}
// 需求1 : 如果方法接收的数组为null , 使用输出语句提示
public static void printArr1(int[] arr) {
if (arr == null) {
System.out.println("数组为null");
} else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
// 需求2 : 如果方法接收的数组为null , 使用抛出异常解决
public static void printArr2(int[] arr) {
if (arr == null) {
throw new RuntimeException();
} else {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
}
捕获处理异常介绍 : try, catch
捕获异常的格式
try {
try中存放可能会出现问题的代码
1.代码...
2.代码...
3.代码...
} catch (异常类型 变量名) {
4.处理异常方案
打印异常,获取异常原因记录日志......)
}
5.其他代码...
执行方式
多异常捕获处理方案
多个异常,每个异常单独处理
try{
异常1
}catch(异常1){
}
try{
异常2
}catch(异常2){
}
多个异常,一次捕获,多次处理
try{
异常1
异常2
}catch(异常1){
}catch(异常2){
}
多个异常,异常一次捕获,一次处理
try{
异常1
异常2
}catch(Exception e){
}
方法名 | 说明 |
---|---|
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
package com.itheima.exception_demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
/*
定义一个方法接收一个生日日期字符串(xxxx年xx月xx)
main方法中让用户输入一个生日日期字符串,调用设计好的方法计算在地球上活了多少天。
要求:如果解析发生异常,捕获异常,提示用户要重新输入生日日期字符串,直到输入正确的日期为止。
思考:设计代此码的过程中想想什么时候捕获异常,什么时候声明异常?
*/
public class ExceptionTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入生日(xxxx-xx-xx):");
while (true) {
String birthday = sc.nextLine();
try {
method(birthday);
break;// 如果生日没有问题结束死循环
} catch (ParseException e) {
System.out.println("录入生日格式有误!");
}
}
}
public static void method(String strDate) throws ParseException {
// 创建日期模板对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 解析字符串
Date date = sdf.parse(strDate);
// 获取生日到1970/1/1 经历的毫秒值
long time1 = date.getTime();
// 当前系统时间到1970/1/1 经历的毫秒值
Date d2 = new Date();
long time2 = d2.getTime();
System.out.println("活了" + (time2 - time1) / (1000L * 60 * 60 * 24) + "天");
}
}
基本步骤:
package com.itheima.thread_demo;
/*
线程的创建方式1:继承Thread方式
基本步骤 :
1 创建一个类继承Thread类。
2 在类中重写run方法(线程执行的任务放在这里)
3 创建线程对象,调用线程的start方法开启线程。
需求 :
我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程)
接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。
此时主线程和启动的线程在并发执行,观察控制台打印的结果。
*/
public class MyThread01 {
public static void main(String[] args) {
// 创建线程对象,调用线程的start方法开启线程。
MyThread mt = new MyThread();
mt.start();
// main方法中的任务
for (int i = 1; i <= 100; i++) {
System.out.println("i:" + i);
}
}
}
// 创建一个类继承Thread类。
class MyThread extends Thread {
// 在类中重写run方法(线程执行的任务放在这里)
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("i:" + i);
}
}
}
构造方法
实现步骤
package com.itheima.thread_demo;
/*
线程的创建方式2:实现Runnable方式
基本步骤 :
1 定义任务类实现Runnable,并重写run方法
2 创建任务对象
3 使用含有Runnable参数的构造方法,创建线程对象并指定任务。
4 调用线程的start方法,开启线程
需求 :
我们启动一个Java程序,其实默认就存在一个主线程(main方法所在线程)
接下来,我们在主线程启动一个线程,打印1到100的数字,主线程启动完线程后又打印1到100的数字。
此时主线程和启动的线程在并发执行,观察控制台打印的结果。
*/
public class MyThread02 {
public static void main(String[] args) {
// 创建线程对象,调用线程的start方法开启线程。
MyRunnable mr = new MyRunnable();
Thread thread= new Thread(mr);
thread.start();
// main方法中的任务
for (int i = 1; i <= 100; i++) {
System.out.println("i:" + i);
}
}
}
// 1 定义任务类实现Runnable,并重写run方法
class MyRunnable implements Runnable {
// 在类中重写run方法(线程执行的任务放在这里)
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("i:" + i);
}
}
}
String getName():返回此线程的名称
Thread类中设置线程的名字
public static Thread currentThread():返回对当前正在执行的线程对象的引用
public static void sleep(long time):让线程休眠指定的时间,单位为毫秒
线程有两种调度模型
package com.itheima.thread_demo.thread_method;
/*
线程有两种调度模型
1 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
2 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程
获取的 CPU 时间片相对多一些
注意 : Java使用的是抢占式调度模型
优先级高 , 只是抢夺到cpu执行的概率高而已 , 只是一种概率问题
*/
public class PriorityDemo {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
});
// 优先级最低
thread1.setPriority(1);
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
});
// 优先级最高
thread2.setPriority(10);
thread2.start();
}
}
package com.itheima.ticket_demo;
/*
电影院
*/
public class Ticket implements Runnable {
private int ticketCount = 100; // 一共有一百张票
@Override
public void run() {
while (true) {
// 如果票的数量为0 , 那么停止买票
if (ticketCount == 0) {
break;
} else {
// 有剩余的票 , 开始卖票
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩下" + ticketCount + "张");
}
}
}
}
package com.itheima.ticket_demo;
/*
1 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2 在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3 定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
*/
public class TicketDemo {
public static void main(String[] args) {
// 创建任务类对象
Ticket ticket = new Ticket();
// 创建三个线程类对象
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
// 给三个线程命名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启三个线程
t1.start();
t2.start();
t3.start();
}
}
package com.itheima.ticket_demo;
/*
电影院
*/
public class Ticket implements Runnable {
private int ticketCount = 100; // 一共有一百张票
@Override
public void run() {
while (true) {
// 如果票的数量为0 , 那么停止买票
if (ticketCount <= 0) {
break;
} else {
// 模拟出票的时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 有剩余的票 , 开始卖票
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩下" + ticketCount + "张");
}
}
}
}
package com.itheima.ticket_demo;
/*
1 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2 在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3 定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
*/
public class TicketDemo {
public static void main(String[] args) {
// 创建任务类对象
Ticket ticket = new Ticket();
// 创建三个线程类对象
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
// 给三个线程命名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启三个线程
t1.start();
t2.start();
t3.start();
}
}
概述 : java允许多线程并发执行,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证该变量的唯一性和准确性
分类
同步代码块 : 锁住多条语句操作共享数据,可以使用同步代码块实现
第一部分 : 格式
synchronized(任意对象) {
多条语句操作共享数据的代码
}
第二部分 : 注意
1 默认情况锁是打开的,只要有一个线程进去执行代码了,锁就会关闭
2 当线程执行完出来了,锁才会自动打开
第三部分 : 同步的好处和弊端
好处 : 解决了多线程的数据安全问题
弊端 : 当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
public class Ticket implements Runnable {
private int ticketCount = 100; // 一共有一百张票
@Override
public void run() {
while (true) {
synchronized (Ticket.class) {
// 如果票的数量为0 , 那么停止买票
if (ticketCount <= 0) {
break;
} else {
// 模拟出票的时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 有剩余的票 , 开始卖票
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩下" + ticketCount + "张");
}
}
}
}
}
package com.itheima.synchronized_demo1;
/*
1 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2 在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3 定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
*/
public class TicketDemo {
public static void main(String[] args) {
// 创建任务类对象
Ticket ticket = new Ticket();
// 创建三个线程类对象
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
// 给三个线程命名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启三个线程
t1.start();
t2.start();
t3.start();
}
}
同步方法:就是把synchronized关键字加到方法上
格式:修饰符 synchronized 返回值类型 方法名(方法参数) { }
同步代码块和同步方法的区别:
1 同步代码块可以锁住指定代码,同步方法是锁住方法中所有代码
2 同步代码块可以指定锁对象,同步方法不能指定锁对象
注意 : 同步方法时不能指定锁对象的 , 但是有默认存在的锁对象的。
1 对于非static方法,同步锁就是this。
2 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。 Class类型的对象
package com.itheima.synchronized_demo2;
/*
同步方法:就是把synchronized关键字加到方法上
格式:修饰符 synchronized 返回值类型 方法名(方法参数) { }
同步代码块和同步方法的区别:
1 同步代码块可以锁住指定代码,同步方法是锁住方法中所有代码
2 同步代码块可以指定锁对象,同步方法不能指定锁对象
注意 : 同步方法时不能指定锁对象的 , 但是有默认存在的锁对象的。
1 对于非static方法,同步锁就是this。
2 对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。 Class类型的对象
*/
public class Ticket implements Runnable {
private int ticketCount = 100; // 一共有一百张票
@Override
public void run() {
while (true) {
if (method()) {
break;
}
}
}
private synchronized boolean method() {
// 如果票的数量为0 , 那么停止买票
if (ticketCount <= 0) {
return true;
} else {
// 模拟出票的时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 有剩余的票 , 开始卖票
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩下" + ticketCount + "张");
return false;
}
}
}
package com.itheima.synchronized_demo2;
/*
1 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2 在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3 定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
*/
public class TicketDemo {
public static void main(String[] args) {
// 创建任务类对象
Ticket ticket = new Ticket();
// 创建三个线程类对象
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
// 给三个线程命名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启三个线程
t1.start();
t2.start();
t3.start();
}
}
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例
注意:多个线程使用相同的Lock锁对象,需要多线程操作数据的代码放在lock()和unLock()方法之间。一定要确保unlock最后能够调用
package com.itheima.synchronized_demo3;
import java.util.concurrent.locks.ReentrantLock;
/*
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock中提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
ReentrantLock():创建一个ReentrantLock的实例
注意:多个线程使用相同的Lock锁对象,需要多线程操作数据的代码放在lock()和unLock()方法之间。一定要确保unlock最后能够调用
*/
public class Ticket implements Runnable {
private int ticketCount = 100; // 一共有一百张票
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();// 加锁
// 如果票的数量为0 , 那么停止买票
if (ticketCount <= 0) {
break;
} else {
// 模拟出票的时间
Thread.sleep(100);
// 有剩余的票 , 开始卖票
ticketCount--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,剩下" + ticketCount + "张");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();// 释放锁
}
}
}
}
package com.itheima.synchronized_demo3;
/*
1 定义一个类Ticket实现Runnable接口,里面定义一个成员变量:private int ticketCount = 100;
2 在Ticket类中重写run()方法实现卖票,代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:票数要减1
C:卖光之后,线程停止
3 定义一个测试类TicketDemo,里面有main方法,代码步骤如下
A:创建Ticket类的对象
B:创建三个Thread类的对象,把Ticket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
*/
public class TicketDemo {
public static void main(String[] args) {
// 创建任务类对象
Ticket ticket = new Ticket();
// 创建三个线程类对象
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
// 给三个线程命名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
// 开启三个线程
t1.start();
t2.start();
t3.start();
}
}
package com.itheima.deadlock_demo;
/*
死锁 :
死锁是一种少见的,而且难于调试的错误,在两个线程对两个同步锁对象具有循环依赖时,就会大概率的出现死锁。
我们要避免死锁的产生。否则一旦死锁,除了重启没有其他办法的
*/
public class DeadLockDemo {
public static void main(String[] args) {
String 筷子A = "筷子A";
String 筷子B = "筷子B";
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (筷子A) {
System.out.println("小白拿到了筷子A ,等待筷子B....");
synchronized (筷子B) {
System.out.println("小白拿到了筷子A和筷子B , 开吃!!!!!");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "小白").start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (筷子B) {
System.out.println("小黑拿到了筷子B ,等待筷子A....");
synchronized (筷子A) {
System.out.println("小黑拿到了筷子B和筷子A , 开吃!!!!!");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "小黑").start();
}
}
线程间的通讯技术就是通过等待和唤醒机制,来实现多个线程协同操作完成某一项任务,例如经典的生产者和消费者案例。等待唤醒机制其实就是让线程进入等待状态或者让线程从等待状态中唤醒,需要用到两种方法,如下:
等待方法 :
唤醒方法 :
注意
package com.itheima.waitnotify_demo;
/*
1 线程进入无限等待
注意:进入无限等待需要使用锁在同步代码中调用wait方法
*/
public class Test1 {
public static void main(String[] args) {
Object obj = new Object(); // 作为锁对象
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
System.out.println("线程开始执行");
System.out.println("线程进入无线等待....");
try {
obj.wait(); // 进入无线等待状态 , 并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("无线等待被唤醒....");
}
}
}).start();
}
}
package com.itheima.waitnotify_demo;
/*
线程进入无限等待后被唤醒
注意:等待和唤醒是两个或多个线程之间实现的。进入无限等待的线程是不会自动唤醒,只能通过其他线程来唤醒。
*/
public class Test2 {
public static void main(String[] args) {
Object obj = new Object(); // 作为锁对象
new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
System.out.println("线程开始执行");
System.out.println("线程进入无线等待....");
try {
obj.wait(); // 进入无线等待状态 , 并释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("无线等待被唤醒....");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj) {
obj.notify();// 随机唤醒此监视器中等待的线程 , 不会释放锁
System.out.println("唤醒后 , 5秒钟后释放锁");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}// 释放锁
}
}).start();
}
}
package com.itheima.waitnotify_demo;
/*
3 线程进入计时等待并唤醒
注意:进入计时等待的线程,时间结束前可以被其他线程唤醒。时间结束后会自动唤醒
*/
public class Test3 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (Test3.class) {
System.out.println("获取到锁 , 开始执行");
try {
System.out.println("进入计时等待...3秒");
Test3.class.wait(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("自动唤醒.");
}
}
}).start();
}
}
package com.itheima.waitnotify_demo2;
import sun.security.krb5.internal.crypto.Des;
/*
生产者步骤:
1,判断桌子上是否有汉堡包
如果有就等待,如果没有才生产。
2,把汉堡包放在桌子上。
3,叫醒等待的消费者开吃
*/
public class Cooker implements Runnable {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
if (Desk.flag) {
// 桌子上有食物
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 桌子上没有食物
System.out.println("厨师生产了一个汉堡包...");
Desk.flag = true;
Desk.lock.notify();
}
}
}
}
}
}
package com.itheima.waitnotify_demo2;
import sun.security.krb5.internal.crypto.Des;
/*
消费者步骤:
1,判断桌子上是否有汉堡包。
2,如果没有就等待。
3,如果有就开吃
4,吃完之后,桌子上的汉堡包就没有了
叫醒等待的生产者继续生产
汉堡包的总数量减一
*/
public class Foodie implements Runnable {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
if (Desk.flag) {
// 桌子上有食物
System.out.println("吃货吃了一个汉堡包...");
Desk.count--; // 汉堡包的数量减少一个
Desk.flag = false;// 桌子上的食物被吃掉 , 值为false
Desk.lock.notify();
} else {
// 桌子上没有食物
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
package com.itheima.waitnotify_demo2;
public class Test {
public static void main(String[] args) {
new Thread(new Foodie()).start();
new Thread(new Cooker()).start();
}
}
package com.itheima.threadpool_demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
1 需求 :
使用线程池模拟游泳教练教学生游泳。
游泳馆(线程池)内有3名教练(线程)
游泳馆招收了5名学员学习游泳(任务)。
2 实现步骤:
创建线程池指定3个线程
定义学员类实现Runnable,
创建学员对象给线程池
*/
public class Test1 {
public static void main(String[] args) {
// 创建指定线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 提交任务
threadPool.submit(new Student("小花"));
threadPool.submit(new Student("小红"));
threadPool.submit(new Student("小明"));
threadPool.submit(new Student("小亮"));
threadPool.submit(new Student("小白"));
threadPool.shutdown();// 关闭线程池
}
}
class Student implements Runnable {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
String coach = Thread.currentThread().getName();
System.out.println(coach + "正在教" + name + "游泳...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(coach + "教" + name + "游泳完毕.");
}
}
package com.itheima.threadpool_demo;
import java.util.concurrent.*;
/*
需求: Callable任务处理使用步骤
1 创建线程池
2 定义Callable任务
3 创建Callable任务,提交任务给线程池
4 获取执行结果
Future submit(Callable task) : 提交Callable任务方法
返回值类型Future的作用就是为了获取任务执行的结果。
Future是一个接口,里面存在一个get方法用来获取值
练一练:使用线程池计算 从0~n的和,并将结果返回
*/
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建指定线程数量的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
Future<Integer> future = threadPool.submit(new CalculateTask(100));
Integer sum = future.get();
System.out.println(sum);
}
}
// 使用线程池计算 从0~n的和,并将结果返回
class CalculateTask implements Callable<Integer> {
private int num;
public CalculateTask(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;// 求和变量
for (int i = 0; i <= num; i++) {
sum += i;
}
return sum;
}
}
概述 :
提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子,在该池子中存储很多个线程。
线程池存在的意义:
系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系
统资源的消耗,这样就有点"舍本逐末"了。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就
会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。
线程池的设计思路 :
概述 : JDK对线程池也进行了相关的实现,在真实企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池。
我们可以使用Executors中所提供的静态方法来创建线程池
static ExecutorService newCachedThreadPool() 创建一个默认的线程池
static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
代码实现 :
package com.itheima.mythreadpool;
//static ExecutorService newCachedThreadPool() 创建一个默认的线程池
//static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
//1,创建一个默认的线程池对象.池子中默认是空的.默认最多可以容纳int类型的最大值.
ExecutorService executorService = Executors.newCachedThreadPool();
//Executors --- 可以帮助我们创建线程池对象
//ExecutorService --- 可以帮助我们控制线程池
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
//Thread.sleep(2000);
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
executorService.shutdown();
}
}
使用Executors中所提供的静态方法来创建线程池
static ExecutorService newFixedThreadPool(int nThreads) : 创建一个指定最多线程数量的线程池
代码实现 :
package com.itheima.mythreadpool;
//static ExecutorService newFixedThreadPool(int nThreads)
//创建一个指定最多线程数量的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class MyThreadPoolDemo2 {
public static void main(String[] args) {
//参数不是初始值而是最大值
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
System.out.println(pool.getPoolSize());//0
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
executorService.submit(()->{
System.out.println(Thread.currentThread().getName() + "在执行了");
});
System.out.println(pool.getPoolSize());//2
// executorService.shutdown();
}
}
创建线程池对象 :
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
代码实现 :
package com.itheima.mythreadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyThreadPoolDemo3 {
// 参数一:核心线程数量
// 参数二:最大线程数
// 参数三:空闲线程最大存活时间
// 参数四:时间单位
// 参数五:任务队列
// 参数六:创建线程工厂
// 参数七:任务的拒绝策略
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime: 空闲线程最大存活时间,不能小于0
unit: 时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂,不能为null
handler: 任务的拒绝策略,不能为null
RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行。
注:明确线程池对多可执行的任务数 = 队列容量 + 最大线程数
案例演示1:演示ThreadPoolExecutor.AbortPolicy任务处理策略
public class ThreadPoolExecutorDemo01 {
public static void main(String[] args) {
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;
// 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
for(int x = 0 ; x < 5 ; x++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
});
}
}
}
控制台输出结果
pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-3---->> 执行了任务
控制台报错,仅仅执行了4个任务,有一个任务被丢弃了
案例演示2:演示ThreadPoolExecutor.DiscardPolicy任务处理策略
public class ThreadPoolExecutorDemo02 {
public static void main(String[] args) {
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;
// 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
for(int x = 0 ; x < 5 ; x++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
});
}
}
}
控制台输出结果
pool-1-thread-1---->> 执行了任务
pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
控制台没有报错,仅仅执行了4个任务,有一个任务被丢弃了
案例演示3:演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略
public class ThreadPoolExecutorDemo02 {
public static void main(String[] args) {
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor;
threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy());
// 提交5个任务
for(int x = 0 ; x < 5 ; x++) {
// 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
final int y = x ;
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
});
}
}
}
控制台输出结果
pool-1-thread-2---->> 执行了任务2
pool-1-thread-1---->> 执行了任务0
pool-1-thread-3---->> 执行了任务3
pool-1-thread-1---->> 执行了任务4
由于任务1在线程池中等待时间最长,因此任务1被丢弃。
案例演示4:演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略
public class ThreadPoolExecutorDemo04 {
public static void main(String[] args) {
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor;
threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());
// 提交5个任务
for(int x = 0 ; x < 5 ; x++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
});
}
}
}
控制台输出结果
pool-1-thread-1---->> 执行了任务
pool-1-thread-3---->> 执行了任务
pool-1-thread-2---->> 执行了任务
pool-1-thread-1---->> 执行了任务
main---->> 执行了任务
通过控制台的输出,我们可以看到次策略没有通过线程池中的线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。
package com.itheima.lambda_demo;
/*
Lambda表达式体验 :
*/
public class LambdaDemo {
public static void main(String[] args) {
// 匿名内部类方式完成
goSwimming(new Swimming() {
@Override
public void swim() {
System.out.println("铁汁 , 我们去游泳吧....");
}
});
// lambda表达式的方式完成
goSwimming(() -> System.out.println("铁汁 , 我们去游泳吧...."));
}
public static void goSwimming(Swimming swimming) {
swimming.swim();
}
}
interface Swimming {
public abstract void swim();
}
lambda表达式可以理解为对匿名内部类的一种简化 , 但是本质是有区别的
面向对象思想 :
函数式编程思想 :
package com.itheima.lambda_test;
/*
练习1:
1 编写一个接口(ShowHandler)
2 在该接口中存在一个抽象方法(show),该方法是无参数无返回值
3 在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler)
方法的的参数是ShowHandler类型的,在方法内部调用了ShowHandler的show方法
*/
public class LambdaTest1 {
public static void main(String[] args) {
useShowHandler(() -> {
System.out.println("我是一个lambda表达式....");
});
}
public static void useShowHandler(ShowHandler showHandler) {
showHandler.show();
}
}
interface ShowHandler {
public abstract void show();
}
package com.itheima.lambda_test;
/*
1 首先存在一个接口(StringHandler)
2 在该接口中存在一个抽象方法(printMessage),该方法是有参数无返回值
3 在测试类(StringHandlerDemo)中存在一个方法(useStringHandler),
方法的的参数是StringHandler类型的,
在方法内部调用了StringHandler的printMessage方法
*/
public class LambdaTest2 {
public static void main(String[] args) {
useStringHandler((String msg) -> {
System.out.println(msg);
});
}
public static void useStringHandler(StringHandler stringHandler){
stringHandler.printMessage("今天天气不错 , 挺风和日丽的...");
}
}
@FunctionalInterface
interface StringHandler {
public abstract void printMessage(String msg);
}
package com.itheima.lambda_test;
import java.util.Random;
/*
1 首先存在一个接口(RandomNumHandler)
2 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler),方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法
*/
public class LambdaTest3 {
public static void main(String[] args) {
useRandomNumHandler(() -> {
return new Random().nextInt(10) + 1;
});
}
public static void useRandomNumHandler(RandomNumHandler randomNumHandler) {
int number = randomNumHandler.getNumber();
System.out.println(number);
}
}
interface RandomNumHandler {
public abstract int getNumber();
}
package com.itheima.lambda_test;
/*
1 首先存在一个接口(Calculator)
2 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
3 在测试类(CalculatorDemo)中存在一个方法(useCalculator)
方法的的参数是Calculator类型的
在方法内部调用了Calculator的calc方法
*/
public class LambdaTest4 {
public static void main(String[] args) {
useCalculator((int a , int b) -> { return a + b;});
}
public static void useCalculator(Calculator calculator) {
int calc = calculator.calc(10, 20);
System.out.println(calc);
}
}
@FunctionalInterface
interface Calculator {
public abstract int calc(int a, int b);
}
package com.itheima.stream_demo;
import java.util.ArrayList;
/*
体验Stream流的好处
需求:按照下面的要求完成集合的创建和遍历
1 创建一个集合,存储多个字符串元素
"张无忌" , "张翠山" , "张三丰" , "谢广坤" , "赵四" , "刘能" , "小沈阳" , "张良"
2 把集合中所有以"张"开头的元素存储到一个新的集合
3 把"张"开头的集合中的长度为3的元素存储到一个新的集合
4 遍历上一步得到的集合
*/
public class StreamDemo {
public static void main(String[] args) {
// 传统方式完成
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("张翠山");
list.add("张三丰");
list.add("谢广坤");
list.add("赵四");
list.add("刘能");
list.add("小沈阳");
list.add("张良");
// 把集合中所有以"张"开头的元素存储到一个新的集合
ArrayList<String> list2 = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")) {
list2.add(s);
}
}
// 把"张"开头的集合中的长度为3的元素存储到一个新的集合
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
if (s.length() == 3) {
list3.add(s);
}
}
// 遍历list3集合
for (String s : list3) {
System.out.println(s);
}
System.out.println("================================");
// Stream流的方式
list.stream().filter(s -> s.startsWith("张") && s.length() == 3).forEach(s -> System.out.println(s));
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
/*
Stream流中三类方法之一 : 获取方法
1 单列集合
可以使用Collection接口中的默认方法stream()生成流
default Stream stream()
2 双列集合
双列集合不能直接获取 , 需要间接的生成流
可以先通过keySet或者entrySet获取一个Set集合,再获取Stream流
3 数组
Arrays中的静态方法stream生成流
*/
public class StreamDemo2 {
public static void main(String[] args) {
// 单列集合的获取
// method1();
// 双列集合的获取
// method2();
// 数组获取
// method3();
}
private static void method3() {
int[] arr = {1, 2, 3, 4, 5, 6};
Arrays.stream(arr).forEach(s -> System.out.println(s));
}
private static void method2() {
HashMap hm = new HashMap<>();
hm.put("it001", "曹植");
hm.put("it002", "曹丕");
hm.put("it003", "曹熊");
hm.put("it004", "曹冲");
hm.put("it005", "曹昂");
// 获取map集合的健集合 , 在进行输出打印
hm.keySet().stream().forEach(s -> System.out.println(s));
// 获取map集合的entry对象 , 在输出打印
hm.entrySet().stream().forEach(s -> System.out.println(s));
}
private static void method1() {
// 可以使用Collection接口中的默认方法stream()生成流
ArrayList list = new ArrayList<>();
list.add("迪丽热巴");
list.add("古力娜扎");
list.add("马尔扎哈");
list.add("欧阳娜娜");
list.stream().forEach(s -> System.out.println(s));
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.stream.Stream;
/*
Stream流中三类方法之一 : 中间方法
1 Stream filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法 : boolean test(T t):对给定的参数进行判断,返回一个布尔值
2 Stream limit(long maxSize):截取指定参数个数的数据
3 Stream skip(long n):跳过指定参数个数的数据
4 static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
5 Stream distinct():去除流中重复的元素。依赖(hashCode和equals方法)
6 Stream sorted () : 将流中元素按照自然排序的规则排序
7 Stream sorted (Comparator super T> comparator) : 将流中元素按照自定义比较器规则排序
*/
public class StreamDemo3 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("张无忌");
list.add("张翠山");
list.add("张三丰");
list.add("谢广坤");
list.add("赵四");
list.add("刘能");
list.add("小沈阳");
list.add("张良");
list.add("张良");
list.add("张良");
list.add("张良");
// Stream limit(long maxSize):截取指定参数个数的数据
// list.stream().limit(3).forEach(s -> System.out.println(s));
// Stream skip(long n):跳过指定参数个数的数据
// list.stream().skip(3).forEach(s-> System.out.println(s));
// Stream distinct():去除流中重复的元素。依赖(hashCode和equals方法)
// list.stream().distinct().forEach(s->{System.out.println(s);});
}
// // Stream sorted (Comparator super T> comparator) : 将流中元素按照自定义比较器规则排序
private static void method3(ArrayList list) {
list.stream().sorted(new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
}).forEach(s->{
System.out.println(s);
});
}
// Stream sorted () : 将流中元素按照自然排序的规则排序
private static void method3() {
ArrayList list2 = new ArrayList<>();
list2.add(3);
list2.add(1);
list2.add(2);
list2.stream().sorted().forEach(s->{
System.out.println(s);
});
}
// // static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
private static void method2(ArrayList list) {
ArrayList list2 = new ArrayList<>();
list2.add("迪丽热巴");
list2.add("古力娜扎");
list2.add("欧阳娜娜");
list2.add("马尔扎哈");
Stream.concat(list.stream(), list2.stream()).forEach(s -> {
System.out.println(s);
});
}
// Stream filter(Predicate predicate):用于对流中的数据进行过滤
private static void method1(ArrayList list) {
// filter方法会获取流中的每一个数据
// s就代表的是流中的每一个数据
// 如果返回值为true , 那么代表的是数据留下来
// 如果返回值的是false , 那么代表的是数据过滤掉
// list.stream().filter(new Predicate() {
// @Override
// public boolean test(String s) {
// boolean result = s.startsWith("张");
// return result;
// }
// }).forEach(s -> System.out.println(s));
list.stream().filter(s ->
s.startsWith("张")
).forEach(s -> System.out.println(s));
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
import java.util.function.Consumer;
/*
Stream流中三类方法之一 : 终结方法
1 void forEach(Consumer action):对此流的每个元素执行操作
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
2 long count():返回此流中的元素数
*/
public class StreamDemo4 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("张无忌");
list.add("张翠山");
list.add("张三丰");
list.add("谢广坤");
// long count():返回此流中的元素数
long count = list.stream().count();
System.out.println(count);
}
// void forEach(Consumer action):对此流的每个元素执行操作
private static void method1(ArrayList list) {
// 把list集合中的元素放在stream流中
// forEach方法会循环遍历流中的数据
// 并循环调用accept方法 , 把数据传给s
// 所以s就代表的是流中的每一个数据
// 我们只要在accept方法中对数据做业务逻辑处理即可
list.stream().forEach(new Consumer() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("=====================");
list.stream().forEach( (String s) -> {
System.out.println(s);
});
System.out.println("=====================");
list.stream().forEach( s -> { System.out.println(s); });
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
/*
Stream流的收集操作 : 第一部分
需求:过滤元素并遍历集合
定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
将集合中的奇数删除,只保留偶数。
遍历集合得到2,4,6,8,10
结论:在Stream流中无法直接修改集合,数组等数据源中的数据。
*/
public class StreamDemo5 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
list.stream().filter(num -> num % 2 == 0).forEach(num -> System.out.println(num));
System.out.println("=============");
// 结论:在Stream流中无法直接修改集合,数组中的数据。
System.out.println(list);
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/*
Stream流的收集操作 : 第二部分
使用Stream流的方式操作完毕之后,我想把流中的数据起来,该怎么办呢?
Stream流的收集方法
R collect(Collector collector) : 此方法只负责收集流中的数据 , 创建集合添加数据动作需要依赖于参数
工具类Collectors提供了具体的收集方式
public static Collector toList():把元素收集到List集合中
public static Collector toSet():把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
需求 :
定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
将集合中的奇数删除,只保留偶数。
遍历集合得到2,4,6,8,10
*/
public class StreamDemo6 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
list.add(10);
list.add(10);
list.add(10);
list.add(10);
list.add(10);
// collect只负责收集流中的数据
// Collectors.toList()会负责在底层创建list集合 ,并把数据添加到集合中 , 返回集合对象
List list2 = list.stream().filter(num -> num % 2 == 0 ).collect(Collectors.toList());
System.out.println(list2);
Set set = list.stream().filter(num -> num % 2 == 0 ).collect(Collectors.toSet());
System.out.println(set);
}
}
package com.itheima.stream_demo;
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Collectors;
/*
Stream流的收集操作 : 第三部分
1 创建一个ArrayList集合,并添加以下字符串。字符串中前面是姓名,后面是年龄
"zhangsan,23"
"lisi,24"
"wangwu,25"
2 保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值
收集方法 :
public static Collector toMap(Function keyMapper , Function valueMapper):把元素收集到Map集合中
*/
public class StreamDemo7 {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("zhangsan,23");
list.add("lisi,24");
list.add("wangwu,25");
// collect 只负责收集数据
// Collectors.toMap负责在底层创建集合对象 , 添加元素
// toMap方法中的s就是代表的是集合中的每一个元素
// 第一个参数 : 如何获取map集合中的键
// 第二个参数 : 如何获取map集合中的值
Map map = list.stream().filter(s -> Integer.parseInt(s.split(",")[1]) > 23).collect(
Collectors.toMap(
(String s) -> {
return s.split(",")[0];
}
,
(String s) -> {
return s.split(",")[1];
}
)
);
System.out.println(map);
}
}
package com.itheima.file_demo;
import java.io.File;
/*
File:它是文件和目录路径名的抽象表示
文件和目录可以通过File封装成对象
File封装的对象仅仅是一个路径名。它可以是存在的,也可以是不存在的。
构造方法 :
1 File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
2 File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例
3 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例
*/
public class FileDemo1 {
public static void main(String[] args) {
// 1 File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
File f1 = new File("D:\\abc.txt");
System.out.println(f1);
// 2 File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例
File f2 = new File("D:\\aaa", "bbb.txt");
System.out.println(f2);
// 3 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例
File f3 = new File(new File("D:\\aaa"), "bbb.txt");
System.out.println(f3);
}
}
package com.itheima.file_demo;
import java.io.File;
import java.io.IOException;
/*
File类的创建功能 :
1 public boolean createNewFile() : 创建一个新的空的文件
2 public boolean mkdir() : 创建一个单级文件夹
3 public boolean mkdirs() : 创建一个多级文件夹
*/
public class FileDemo2 {
public static void main(String[] args) throws IOException {
File f1 = new File("D:\\a.txt");
// 1 public boolean createNewFile() : 创建一个新的空的文件
System.out.println(f1.createNewFile());
File f2 = new File("day10_demo\\aaa");
// 2 public boolean mkdir() : 创建一个单级文件夹
System.out.println(f2.mkdir());
File f3 = new File("day10_demo\\bbb\\ccc");
// 3 public boolean mkdirs() : 创建一个多级文件夹
System.out.println(f3.mkdirs());
}
}
package com.itheima.file_demo;
import java.io.File;
import java.io.IOException;
/*
File类删除功能 :
public boolean delete() 删除由此抽象路径名表示的文件或目录
注意 :
1 delete方法直接删除不走回收站。
2 如果删除的是一个文件,直接删除。
3 如果删除的是一个文件夹,需要先删除文件夹中的内容,最后才能删除文件夹
*/
public class FileDemo3 {
public static void main(String[] args) throws IOException {
File f1 = new File("D:\\a.txt");
// 1 public boolean createNewFile() : 创建一个新的空的文件
System.out.println(f1.delete());
File f2 = new File("day10_demo\\aaa");
// 2 public boolean mkdir() : 创建一个单级文件夹
System.out.println(f2.delete());
File f3 = new File("day10_demo\\bbb");
// 3 public boolean mkdirs() : 创建一个多级文件夹
System.out.println(f3.delete());
}
}
package com.itheima.file_demo;
import java.io.File;
/*
File类判断和获取功能
public boolean isDirectory() 测试此抽象路径名表示的File是否为目录
public boolean isFile() 测试此抽象路径名表示的File是否为文件
public boolean exists() 测试此抽象路径名表示的File是否存在
public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
public String getPath() 将此抽象路径名转换为路径名字符串
public String getName() 返回由此抽象路径名表示的文件或目录的名称
*/
public class FileDemo4 {
public static void main(String[] args) {
File f1 = new File("day10_demo\\aaa");
File f2 = new File("day10_demo\\a.txt");
// public boolean isDirectory() 测试此抽象路径名表示的File是否为目录
System.out.println(f1.isDirectory());
System.out.println(f2.isDirectory());
// public boolean isFile() 测试此抽象路径名表示的File是否为文件
System.out.println(f1.isFile());
System.out.println(f2.isFile());
// public boolean exists() 测试此抽象路径名表示的File是否存在
System.out.println(f1.exists());
// public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
System.out.println(f1.getAbsolutePath());
// public String getPath() 将此抽象路径名转换为路径名字符串
System.out.println(f1.getPath());
// public String getName() 返回由此抽象路径名表示的文件或目录的名称
System.out.println(f2.getName());
}
}
package com.itheima.file_demo;
import java.io.File;
/*
File类高级获取功能
public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组
listFiles方法注意事项:
1 当调用者不存在时,返回null
2 当调用者是一个文件时,返回null
3 当调用者是一个空文件夹时,返回一个长度为0的数组
4 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
5 当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
*/
public class FileDemo5 {
public static void main(String[] args) {
File file = new File("day10_demo\\bbb\\ccc");
// 返回此抽象路径名表示的目录中的文件和目录的File对象数组
File[] files = file.listFiles();
// 遍历File类的数组
for (File f : files) {
// 拿到每一个文件的文字
System.out.println(f.getName());
}
}
}
package com.itheima.file_demo;
import java.io.File;
import java.util.HashMap;
/*
练习二 :统计一个文件夹中每种文件的个数并打印。
打印格式如下:
txt:3个
doc:4个
jpg:6个
…
*/
public class FileTest2 {
public static void main(String[] args) {
File file = new File("day10_demo\\统计文件个数文件夹");
getFileCount(file);
}
public static void getFileCount(File f) {
HashMap hm = new HashMap<>();
// 是文件夹在获取所有的子文件
if (f.isDirectory()) {
File[] files = f.listFiles();
// 遍历数组
for (File file : files) {
String fileName = file.getName();
String name = fileName.split("\\.")[1];
if (hm.containsKey(name)) {
hm.put(name, hm.get(name));
} else {
hm.put(name, 1);
}
}
}
System.out.println(hm);
}
}
package com.itheima.recursion_demo;
/*
需求:用递归求5的阶乘,并把结果在控制台输出
思路:
1 定义一个方法,用于递归求阶乘,参数为一个int类型的变量
2 在方法内部判断该变量的值是否是1
是:返回1
不是:返回n*(n-1)!
3 调用方法
4 输出结果
*/
public class Demo1 {
public static void main(String[] args) {
int result = jc(5);
System.out.println("5的阶乘是:" + result);
}
private static int jc(int n) {
if (n == 1) {
return 1;
}
return n * jc(n - 1);
}
}
package com.itheima.recursion_demo;
import java.io.File;
/*
需求 : 使用递归删除计算机中指定的文件夹
删除D盘中的aaa文件夹!
*/
public class Demo2 {
public static void main(String[] args) {
File f = new File("D:\\aaa");
deleteFiles(f);
}
private static void deleteFiles(File f) {
File[] files = f.listFiles();
for (File file : files) {
if (file.isDirectory()) {
// 递归
deleteFiles(file);
} else {
// 删除文件
file.delete();
}
}
// 删除当前文件夹
f.delete();
}
}
OutputStream
有很多子类,我们从最简单的一个子类开始。java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件public FileOutputStream(File file)
:创建文件输出流以写入由指定的 File对象表示的文件。public FileOutputStream(String name)
: 创建文件输出流以指定的名称写入文件。public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使用File对象创建流对象
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("b.txt");
}
}
package com.itheima.outputstream_demo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节输出流写数据快速入门 :
1 创建字节输出流对象。
2 写数据
3 释放资源
*/
public class OutputStreamDemo1 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// 如果指定的文件不存在 , 会自动创建文件
// 如果文件存在 , 会把文件中的内容清空
FileOutputStream fos = new FileOutputStream("day11_demo\\a.txt");
// 写数据
// 写到文件中就是以字节形式存在的
// 只是文件帮我们把字节翻译成了对应的字符 , 方便查看
fos.write(97);
fos.write(98);
fos.write(99);
// 释放资源
// while(true){}
// 断开流与文件中间的关系
fos.close();
}
}
字节流写数据的方法
package com.itheima.outputstream_demo;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节流写数据的3种方式
1 void write(int b) 一次写一个字节数据
2 void write(byte[] b) 一次写一个字节数组数据
3 void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
*/
public class OutputStreamDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("day11_demo\\a.txt");
// 写数据
// 1 void write(int b) 一次写一个字节数据
fos.write(97);
fos.write(98);
fos.write(99);
// 2 void write(byte[] b) 一次写一个字节数组数据
byte[] bys = {65, 66, 67, 68, 69};
fos.write(bys);
// 3 void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
fos.write(bys, 0, 3);
// 释放资源
fos.close();
}
}
package com.itheima.outputstream_demo;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节流写数据的换行和追加写入
1 字节流写数据如何实现换行呢?
写完数据后,加换行符
windows : \r\n
linux : \n
mac : \r
2 字节流写数据如何实现追加写入呢?
通过构造方法 : public FileOutputStream(String name,boolean append)
创建文件输出流以指定的名称写入文件。如果第二个参数为true ,不会清空文件里面的内容
*/
public class OutputStreamDemo3 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("day11_demo\\a.txt");
// void write(int b) 一次写一个字节数据
fos.write(97);
// 因为字节流无法写入一个字符串 , 把字符串转成字节数组写入
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
// 释放资源
fos.close();
}
}
package com.itheima.outputstream_demo;
import java.io.FileOutputStream;
import java.io.IOException;
/*
字节流写数据的换行和追加写入
1 字节流写数据如何实现换行呢?
写完数据后,加换行符
windows : \r\n
linux : \n
mac : \r
2 字节流写数据如何实现追加写入呢?
通过构造方法 : public FileOutputStream(String name,boolean append)
创建文件输出流以指定的名称写入文件。如果第二个参数为true ,不会清空文件里面的内容
*/
public class OutputStreamDemo3 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// 追加写数据
// 通过构造方法 : public FileOutputStream(String name,boolean append) : 追加写数据
FileOutputStream fos = new FileOutputStream("day11_demo\\a.txt" , true);
// void write(int b) 一次写一个字节数据
fos.write(97);
// 因为字节流无法写入一个字符串 , 把字符串转成字节数组写入
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
// 释放资源
fos.close();
}
// 写完数据换行操作
private static void method1() throws IOException {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("day11_demo\\a.txt");
// void write(int b) 一次写一个字节数据
fos.write(97);
// 因为字节流无法写入一个字符串 , 把字符串转成字节数组写入
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
// 释放资源
fos.close();
}
}
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.IOException;
/*
字节输入流写数据快速入门 : 一次读一个字节
第一部分 : 字节输入流类
InputStream类 : 字节输入流最顶层的类 , 抽象类
--- FileInputStream类 : FileInputStream extends InputStream
第二部分 : 构造方法
public FileInputStream(File file) : 从file类型的路径中读取数据
public FileInputStream(String name) : 从字符串路径中读取数据
第三部分 : 字节输入流步骤
1 创建输入流对象
2 读数据
3 释放资源
*/
public class FileInputStreamDemo1 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
// 读取的文件必须存在 , 不存在则报错
FileInputStream fis = new FileInputStream("day11_demo\\a.txt");
// 读数据 , 从文件中读到一个字节
// 返回的是一个int类型的字节
// 如果想看字符, 需要强转
int by = fis.read();
System.out.println((char) by);
// 释放资源
fis.close();
}
}
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.IOException;
/*
字节输入流写数据快速入门 : 读多个字节
第一部分 : 字节输入流类
InputStream类 : 字节输入流最顶层的类 , 抽象类
--- FileInputStream类 : FileInputStream extends InputStream
第二部分 : 构造方法
public FileInputStream(File file) : 从file类型的路径中读取数据
public FileInputStream(String name) : 从字符串路径中读取数据
第三部分 : 字节输入流步骤
1 创建输入流对象
2 读数据
3 释放资源
*/
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
// 读取的文件必须存在 , 不存在则报错
FileInputStream fis = new FileInputStream("day11_demo\\a.txt");
// 读数据 , 从文件中读到一个字节
// 返回的是一个int类型的字节
// 如果想看字符, 需要强转
// int by = fis.read();
// System.out.println(by);
// by = fis.read();
// System.out.println(by);
// by = fis.read();
// System.out.println(by);
//
// by = fis.read();
// System.out.println(by);
// by = fis.read();
// System.out.println(by);
// by = fis.read();
// System.out.println(by);
// 循环改进
int by;// 记录每次读到的字节
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
// 释放资源
fis.close();
}
}
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
需求 : 把 "图片路径\xxx.jpg" 复制到当前模块下
分析:
复制文件,其实就把文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
数据源:
xxx.jpg --- 读数据 --- FileInputStream
目的地:
模块名称\copy.jpg --- 写数据 --- FileOutputStream
*/
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg");
// 创建字节输出流
FileOutputStream fos = new FileOutputStream("day11_demo\\copy.jpg");
// 一次读写一个字节
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
// 释放资源
fis.close();
fos.close();
}
}
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
需求 : 对上一个赋值图片的代码进行使用捕获方式处理
*/
public class FileInputStreamDemo4 {
public static void main(String[] args) {
FileInputStream fis = null ;
FileOutputStream fos = null;
try {
// 创建字节输入流对象
fis = new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg");
// 创建字节输出流
fos = new FileOutputStream("day11_demo\\copy.jpg");
// 一次读写一个字节
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 释放资源
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 释放资源
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
JDK7优化后可以使用 try-with-resource 语句 , 该语句确保了每个资源在语句结束时自动关闭。
简单理解 : 使用此语句,会自动释放资源 , 不需要自己在写finally代码块了
格式 :
格式 :
try (创建流对象语句1 ; 创建流对象语句2 ...) {
// 读写数据
} catch (IOException e) {
处理异常的代码...
}
举例 :
try (
FileInputStream fis1 = new FileInputStream("day11_demo\\a.txt") ;
FileInputStream fis2 = new FileInputStream("day11_demo\\b.txt") )
{
// 读写数据
} catch (IOException e) {
处理异常的代码...
}
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
JDK7版本优化处理方式
需求 : 对上一个赋值图片的代码进行使用捕获方式处理
*/
public class FileInputStreamDemo5 {
public static void main(String[] args) {
try (
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg");
// 创建字节输出流
FileOutputStream fos = new FileOutputStream("day11_demo\\copy.jpg")
) {
// 一次读写一个字节
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
// 释放资源 , 发现已经灰色 , 提示多余的代码 , 所以使用 try-with-resource 方式会自动关流
// fis.close();
// fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileInputStream类 :
package com.itheima.inputstream_demo;
import javax.sound.midi.Soundbank;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
FileInputStream类 :
public int read(byte[] b):
1 从输入流读取最多b.length个字节的数据
2 返回的是真实读到的数据个数
*/
public class FileInputStreamDemo6 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("day11_demo\\a.txt");
// public int read(byte[] b):
// 1 从输入流读取最多b.length个字节的数据
// 2 返回的是真实读到的数据个数
byte[] bys = new byte[3];
// int len = fis.read(bys);
// System.out.println(len);// 3
// System.out.println(new String(bys));// abc
//
// len = fis.read(bys);
// System.out.println(len);// 2
// System.out.println(new String(bys));// efc
System.out.println("==========代码改进===============");
// int len = fis.read(bys);
// System.out.println(len);// 3
// System.out.println(new String(bys, 0, len));// abc
//
// len = fis.read(bys);
// System.out.println(len);// 2
// System.out.println(new String(bys, 0, len));// ef
System.out.println("==========代码改进===============");
int len;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys , 0 , len));
}
fis.close();
}
}
对复制图片的代码进行使用一次读写一个字节数组的方式进行改进
package com.itheima.inputstream_demo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
需求 : 对复制图片的代码进行使用一次读写一个字节数组的方式进行改进
FileInputStream类 :
public int read(byte[] b):
1 从输入流读取最多b.length个字节的数据
2 返回的是真实读到的数据个数
*/
public class FileInputStreamDemo7 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg");
// 创建字节输出流
FileOutputStream fos = new FileOutputStream("day11_demo\\copy.jpg");
byte[] bys = new byte[1024];
int len;// 每次真实读到数据的个数
int by;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
// 释放资源
fis.close();
fos.close();
}
}
字节缓冲流:
构造方法:
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
package com.itheima.bufferedstream_demo;
import java.io.*;
/*
字节缓冲流:
BufferOutputStream:缓冲输出流
BufferedInputStream:缓冲输入流
构造方法:
字节缓冲输出流:BufferedOutputStream(OutputStream out)
字节缓冲输入流:BufferedInputStream(InputStream in)
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
字节缓冲流仅仅提供缓冲区,不具备读写功能 , 而真正的读写数据还得依靠基本的字节流对象进行操作
需求 : 使用缓冲流进行复制文件
*/
public class BufferedStreamDemo1 {
public static void main(String[] args) throws IOException {
// 创建高效的字节输入流对象
// 在底层会创建一个长度为8192的数组
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg"));
// 创建高效的字节输出流
// 在底层会创建一个长度为8192的数组
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day11_demo\\copy.jpg"));
// 使用高效流 , 一次读写一个字节
int by;
while ((by = bis.read()) != -1) {
bos.write(by);
}
// byte[] bys = new byte[1024];
// int len;// 每次真实读到数据的个数
// while ((len = bis.read(bys)) != -1) {
// bos.write(bys, 0, len);
// }
// 释放资源
// 在底层会把基本的流进行关闭
bis.close();
bos.close();
}
}
package com.itheima.bufferedstream_demo;
import java.awt.image.DataBufferDouble;
import java.io.*;
/*
需求:把“xxx.avi”复制到模块目录下的“copy.avi” , 使用四种复制文件的方式 , 打印所花费的时间
四种方式:
1 基本的字节流一次读写一个字节 : 花费的时间为:196662毫秒
2 基本的字节流一次读写一个字节数组 : 花费的时间为:383毫秒
3 缓冲流一次读写一个字节 : 花费的时间为:365毫秒
4 缓冲流一次读写一个字节数组 : 花费的时间为:108毫秒
分析 :
数据源 : "D:\a.wmv"
目的地 : "day11_demo\copy.wmv"
*/
public class BufferedStreamDemo2 {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
// method1();
// method2();
// method3();
method4();
long endTime = System.currentTimeMillis();
System.out.println("花费的时间为:" + (endTime - startTime) + "毫秒");
}
// 4 缓冲流一次读写一个字节数组
private static void method4() throws IOException {
// 创建高效的字节输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\a.wmv"));
// 创建高效的字节输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day11_demo\\copy.wmv"));
// 一次读写一个字节数组
byte[] bys = new byte[1024];
int len;// 每次真实读到数据的个数
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
// 释放资源
bis.close();
bos.close();
}
// 3 缓冲流一次读写一个字节
private static void method3() throws IOException {
// 创建高效的字节输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\a.wmv"));
// 创建高效的字节输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day11_demo\\copy.wmv"));
// 一次读写一个字节
int by;
while ((by = bis.read()) != -1) {
bos.write(by);
}
// 释放资源
bis.close();
bos.close();
}
// 2 基本的字节流一次读写一个字节数组
private static void method2() throws IOException {
// 创建基本的字节输入流对象
FileInputStream fis = new FileInputStream("D:\\a.wmv");
// 创建基本的字节输出流对象
FileOutputStream fos = new FileOutputStream("day11_demo\\copy.wmv");
// 一次读写一个字节数组
byte[] bys = new byte[1024];
int len;// 每次真实读到数据的个数
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
// 释放资源
fis.close();
fos.close();
}
// 1 基本的字节流一次读写一个字节
private static void method1() throws IOException {
// 创建基本的字节输入流对象
FileInputStream fis = new FileInputStream("D:\\a.wmv");
// 创建基本的字节输出流对象
FileOutputStream fos = new FileOutputStream("day11_demo\\copy.wmv");
// 一次读写一个字节
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
// 释放资源
fis.close();
fos.close();
}
}
properties是一个Map体系的集合类
public class Properties extends Hashtable
为什么在IO流部分学习Properties
当做双列集合使用
package com.itheima.properties_demo;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/*
1 properties是一个Map体系的集合类
- `public class Properties extends Hashtable
public class PropertiesDemo1 {
public static void main(String[] args) {
// 创建集合对象
Properties properties = new Properties();
// 添加元素
properties.put("it001" , "张三");
properties.put("it002" , "李四");
properties.put("it003" , "王五");
// 遍历集合 : 键找值
Set<Object> set = properties.keySet();
for (Object key : set) {
System.out.println(key + "---" + properties.get(key));
}
System.out.println("========================");
// 遍历集合 : 获取对对象集合 , 获取键和值
Set<Map.Entry<Object, Object>> set2 = properties.entrySet();
for (Map.Entry<Object, Object> entry : set2) {
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + "---" + value);
}
}
}
package com.itheima.properties_demo;
import java.util.Properties;
import java.util.Set;
/*
Properties作为集合的特有方法
Object setProperty(String key, String value) 设置集合的键和值,都是String类型,相当于put方法
String getProperty(String key) 使用此属性列表中指定的键搜索属性 , 相当于get方法
Set stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 , 相当于keySet方法
*/
public class PropertiesDemo2 {
public static void main(String[] args) {
// 创建集合对象
Properties properties = new Properties();
// 添加元素
properties.setProperty("it001", "张三");
properties.setProperty("it002", "李四");
properties.setProperty("it003", "王五");
// 遍历集合 : 键找值
Set<String> set = properties.stringPropertyNames();
for (String key : set) {
System.out.println(key + "---" + properties.getProperty(key));
}
}
}
package com.itheima.properties_demo;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/*
Properties和IO流结合的方法
void load(InputStream inStream) 以字节流形式 , 把文件中的键值对, 读取到集合中
//void load(Reader reader) 以字符流形式 , 把文件中的键值对, 读取到集合中
void store(OutputStream out, String comments) 把集合中的键值对,以字节流形式写入文件中 , 参数二为注释
//void store(Writer writer, String comments) 把集合中的键值对,以字符流形式写入文件中 , 参数二为注释
*/
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
// 创建Properties集合对象
Properties properties = new Properties();
// void load(InputStream inStream) 以字节流形式 , 把文件中的键值对, 读取到集合中
properties.load(new FileInputStream("day11_demo\\prop.properties"));
// 打印集合中的数据
System.out.println(properties);
}
}
package com.itheima.properties_demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
/*
Properties和IO流结合的方法
void load(InputStream inStream) 以字节流形式 , 把文件中的键值对, 读取到集合中
//void load(Reader reader) 以字符流形式 , 把文件中的键值对, 读取到集合中
void store(OutputStream out, String comments) 把集合中的键值对,以字节流形式写入文件中 , 参数二为注释
//void store(Writer writer, String comments) 把集合中的键值对,以字符流形式写入文件中 , 参数二为注释
*/
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.setProperty("zhangsan" , "23");
properties.setProperty("lisi" , "24");
properties.setProperty("wangwu" , "25");
properties.store(new FileOutputStream("day11_demo\\prop2.properties") , "userMessage");
}
private static void method1() throws IOException {
// 创建Properties集合对象
Properties properties = new Properties();
// void load(InputStream inStream) 以字节流形式 , 把文件中的键值对, 读取到集合中
properties.load(new FileInputStream("day11_demo\\prop.properties"));
// 打印集合中的数据
System.out.println(properties);
}
}
java.util.ResourceBundle
它是一个抽象类,我们可以使用它的子类PropertyResourceBundle来读取以.properties结尾的配置文件。
static ResourceBundle getBundle(String baseName) 可以根据名字直接获取默认语言环境下的属性资源。
参数注意: baseName
1.属性集名称不含扩展名。
2.属性集文件是在src目录中的
比如:src中存在一个文件 user.properties
ResourceBundle bundle = ResourceBundle.getBundle("user");
ResourceBundle中常用方法:
String getString(String key) : 通过键,获取对应的值
通过ResourceBundle工具类
将一个属性文件 放到src目录中,使用ResourceBundle去获取键值对数据
package com.itheima.resourcebundle_demo;
import java.util.ResourceBundle;
/*
1 java.util.ResourceBundle : 它是一个抽象类
我们可以使用它的子类PropertyResourceBundle来读取以.properties结尾的配置文件
2 static ResourceBundle getBundle(String baseName) 可以根据名字直接获取默认语言环境下的属性资源。
参数注意: baseName
1.属性集名称不含扩展名。
2.属性集文件是在src目录中的
比如:src中存在一个文件 user.properties
ResourceBundle bundle = ResourceBundle.getBundle("user");
3 ResourceBundle中常用方法:
String getString(String key) : 通过键,获取对应的值
优点 : 快速读取属性文件的值
需求 :
通过ResourceBundle工具类
将一个属性文件 放到src目录中,使用ResourceBundle去获取键值对数据
*/
public class ResourceBundleDemo {
public static void main(String[] args) {
// 获取ResourceBundle类的对象
ResourceBundle resourceBundle = ResourceBundle.getBundle("user");
// String getString(String key) : 通过键,获取对应的值
String username = resourceBundle.getString("username");
System.out.println(username);
String password = resourceBundle.getString("password");
System.out.println(password);
}
}
如果要使用ResourceBundle加载属性文件,属性文件需要放置在哪个位置?
src的根目录
请描述使用ResourceBundle获取属性值的大致步骤是怎样的?
1 获取ResourceBundle对象
2 通过ResourceBundle类中的getString(key) : 根据键找值
计算机中储存的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
按照编码表规则,将字符存储到计算机中,称为编码。
按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码 。
编码和解码使用的码表必须一致,否则会导致乱码。
构造方法 :
成员方法 :
void write(int c) | 写一个字符 |
---|---|
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
flush() | 刷新流,还可以继续写数据 |
---|---|
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
/*
Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileWriter类
FileWriter类 : 用来写入字符文件的便捷类
构造方法 :
public FileWriter(File file) : 往指定的File路径中写入数据
public FileWriter(String fileName) : 往指定的String路径中写入数据
成员方法
void write(int c) 写一个字符
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
*/
public class WriterDemo1 {
public static void main(String[] args) throws IOException {
// 创建字符输出流对象
// 如果文件不存在会创建一个空的文件
// 如果文件存在 , 会把文件中的内容清空
FileWriter fw = new FileWriter("day12_demo\\charstream2.txt");
// 写数据
fw.write('a');
fw.write('b');
// 刷新流 , 把流中的数据刷到硬盘中 , 刷新之后可以继续写数据
// fw.flush();
// 释放资源
// 关闭流 , 但是会先刷新流
fw.close();
// 一旦关闭流无法写数据
// fw.write('c');
}
}
package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
/*
Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileWriter类
FileWriter类 : 用来写入字符文件的便捷类
构造方法 :
public FileWriter(File file) : 往指定的File路径中写入数据
public FileWriter(String fileName) : 往指定的String路径中写入数据
成员方法
void write(int c) 写一个字符
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
*/
public class WriterDemo2 {
public static void main(String[] args) throws IOException {
// 创建字符输出流对象
FileWriter fw = new FileWriter("day12_demo\\charstream2.txt");
// 写数据
// void write(int c) 写一个字符
// fw.write('a');
// fw.write('b');
// fw.write('c');
// void write(char[] cbuf) 写入一个字符数组
char[] chs = {'a', 'b', 'c', 'd', 'e'};
// fw.write(chs);
// void write(char[] cbuf, int off, int len) 写入字符数组的一部分
// fw.write(chs , 2 , 3);
// void write(String str) 写一个字符串
// fw.write("abcadaasda");
// void write(String str, int off, int len) 写一个字符串的一部分
// fw.write("abnacna", 3, 2);
// 释放资源
fw.close();
}
}
package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
/*
需求 : 将用户键盘录入的用户名和密码保存到本地实现永久化存储。
要求 : 用户名和密码在文件中各占一行
步骤:
1 用户键盘录入用户名
2 创建字符输出流对象
3 将用户名和密码写到本地文件中
*/
public class WriterTest {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
// 创建字符输出流对象
FileWriter fw = new FileWriter("day12_demo\\user.txt");
// 往文件中写入用户名和密码
fw.write(username);
// 换行
fw.write("\r\n");
fw.write(password);
// 刷新
fw.flush();
// 释放资源
fw.close();
}
}
构造方法 :
成员方法 :
int read() | 一次读一个字符数据 |
---|---|
int read(char[] cbuf) | 一次读一个字符数组数据 |
package com.itheima.reader_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileReader类
FileReader类 : 用来读取字符文件的便捷类
构造方法 :
public FileReader(File file) : 从指定的File路径中读取数据
public FileReader(String fileName) : 从指定的String路径中读取数据
成员方法 :
int read() : 一次读一个字符数据
int read(char[] cbuf) : 一次读一个字符数组数据
*/
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
// 创建字符输入流对象
FileReader fr = new FileReader("day12_demo\\charstream.txt");
// 一次读一个字符数据
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
// 释放资源
fr.close();
}
}
package com.itheima.reader_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileReader类
FileReader类 : 用来读取字符文件的便捷类
构造方法 :
public FileReader(File file) : 从指定的File路径中读取数据
public FileReader(String fileName) : 从指定的String路径中读取数据
成员方法 :
int read() 一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据
*/
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 创建字符输入流对象
FileReader fr = new FileReader("day12_demo\\charstream.txt");
// 一次读一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
System.out.println(new String(chs, 0, len));
}
// 释放资源
fr.close();
}
}
package com.itheima.bufferedstream_demo;
import java.io.*;
/*
需求 : 使用字符缓冲流复制纯文本文件
将当日课程资料中的 ‘斗罗大陆.txt’ 复制到当前模块下 'copy.txt'
*/
public class BufferedStreamDemo1 {
public static void main(String[] args) throws IOException {
// 创建高效的字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("D:\\传智播客\\上海-JavaSE进阶面授\\day12【缓冲流、转换流、序列化流、装饰者模式、commons-io工具包】\\资料\\斗罗大陆.txt"));
// 创建高效的字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\copy.txt"));
// 一次读写一个字符
// int ch;
// while ((ch = br.read()) != -1) {
// bw.write(ch);
// }
// 一次读写一个字符数组
char[] chs = new char[1024];
int len;
while ((len = br.read(chs)) != -1) {
bw.write(chs, 0, len);
}
// 释放资源
br.close();
bw.close();
}
}
package com.itheima.bufferedstream_demo;
import java.io.*;
/*
1 字符缓冲流:
BufferedWriter:可以将数据高效的写出
BufferedReader:可以将数据高效的读入到内存
2 字符缓冲流特有功能
BufferedWriter类
void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
BufferedReader类
public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
远桥之下泛莲舟
岱岩石上松溪流
万仞翠山梨亭在
莫闻空谷声悠悠
*/
public class BufferedStreamDemo2 {
public static void main(String[] args) throws IOException {
// 创建高效的字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\abc.txt"));
// void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
bw.write("远桥之下泛莲舟");
bw.newLine();
bw.write("岱岩石上松溪流");
bw.newLine();
bw.write("万仞翠山梨亭在");
bw.newLine();
bw.write("莫闻空谷声悠悠");
bw.flush();
// 创建高效的字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("day12_demo\\abc.txt"));
// public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
// String s = br.readLine();
// System.out.println(s);
// s = br.readLine();
// System.out.println(s);
// s = br.readLine();
// System.out.println(s);
// s = br.readLine();
// System.out.println(s);
// System.out.println("============");
// s = br.readLine();
// System.out.println(s);
// s = br.readLine();
// System.out.println(s);
// 循环改进
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
// 释放资源
br.close();
bw.close();
}
}
package com.itheima.bufferedstream_demo;
import java.io.*;
import java.util.Arrays;
/*
需求:读取文件中的数据 : 33 22 11 55 44
排序后 : 11 22 33 44 55 再次写到本地文件
步骤 :
1 创建高效的字符输入流对象
2 读取文件中的一行数据
3 将数据按照空格切割
4 把字符串数组转成int类型数组
5 对int类型的数组进行排序
6 创建高效的字符输出流对象
7 遍历数组,把数组中的数据写入到文件中
8 释放资源
*/
public class BufferedStreamDemo3 {
public static void main(String[] args) throws IOException {
// 1 创建高效的字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("day12_demo\\sort.txt"));
// 2 读取文件中的一行数据
String line = br.readLine();
// 3 将数据按照空格切割
String[] strs = line.split(" ");
// 4 把字符串数组转成int类型数组
int[] arr = new int[strs.length];
for (int i = 0; i < strs.length; i++) {
arr[i] = Integer.parseInt(strs[i]);
}
// 5 对int类型的数组进行排序
Arrays.sort(arr);
// 6 创建高效的字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\sort.txt"));
// 7 遍历数组,把数组写入到文件中
for (int i = 0; i < arr.length; i++) {
bw.write(arr[i] + " ");
bw.flush();
}
// 8 释放资源
br.close();
bw.close();
}
}
InputStreamReader是从字节流到字符流的桥梁
OutputStreamWriter是从字符流到字节流的桥梁
package com.itheima.conversion_demo;
import java.io.*;
/*
转换流就是来进行字节流和字符流之间转换的桥梁
InputStreamReader是从字节流到字符流的桥梁
public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。
public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
OutputStreamWriter是从字符流到字节流的桥梁
public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
数据如下 :
远桥之下泛莲舟
岱岩石上松溪流
万仞翠山梨亭在
莫闻空谷声悠悠
*/
public class ConversionDemo2 {
public static void main(String[] args) throws IOException {
// 创建转换输出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day12_demo\\conversion.txt"), "GBK");
osw.write("远桥之下泛莲舟");
osw.write("\r\n");
osw.write("岱岩石上松溪流");
osw.write("\r\n");
osw.write("万仞翠山梨亭在");
osw.write("\r\n");
osw.write("莫闻空谷声悠悠");
osw.write("\r\n");
osw.close();
// 创建转换输入流
InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\conversion.txt"), "GBK");
int ch;
while ((ch = isr.read()) != -1) {
System.out.print((char) ch);
}
isr.close();
}
}
package com.itheima.conversion_demo;
import java.io.*;
/*
转换流就是来进行字节流和字符流之间转换的桥梁
InputStreamReader是从字节流到字符流的桥梁
public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。
public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
OutputStreamWriter是从字符流到字节流的桥梁
public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
需求2 : 将模块根目录中GBK编码的文本文件 , 转换为UTF-8编码的文本文件
*/
public class ConversionDemo2 {
public static void main(String[] args) throws IOException {
// 创建转换输入流
InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\GBK编码的文件.txt"), "GBK");
// 创建转换输出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day12_demo\\UTF编码的文件.txt"), "UTF-8");
int ch;
while ((ch = isr.read()) != -1) {// 以GBK编码进去读取
osw.write(ch);// 以UTF-8编码进行写入
}
// 释放资源
isr.close();
osw.close();
}
}
注意 : 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
如果出问题了,如何解决呢?
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
package com.itheima.objectstream_demo;
import java.io.Serializable;
/*
如果此类对象想要被序列化 , 那么此类需要实现Serializable接口
Serializable接口的含义 :
是一个标记性接口 , 里面没有任何抽象方法
只要一个类实现了此接口 , 表示此类的对象可以被序列化
*/
public class User implements Serializable {
/*
问题分析 :
serialVersionUID : 序列号
序列号是根据类的信息进行生成的
如果没有自己给出序列号 , JVM会根据类的信息自动计算一个序列号
如果改动了类的信息 , 那么JVM会重新计算一个序列号
第一步 : 把对象序列化到本地中 , 序列号为 -4446663370728791812 也会存储到本地中
第二步 : 我们自己修改了类 , 会重新计算一个新的序列号 2908680347500030933
第三步 : 当把对象读到内存中时 , 本地中的序列号和类中的序列号不一致就会发生 InvalidClassException异常
解决方案 :
我们自己手动给出序列号, 不让虚拟机自动生成 , 并且这个值恒久不变
private static final long serialVersionUID = 值L;
*/
private static final long serialVersionUID = 111L;
private String username;
private transient String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
PrintStream类 打印
作用 : 就是为了方便记录数据(日志)
1)构造方法
public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径
2)方法
public void println(数据) 打印后换行
public void print(数据) 打印不换行
创建打印流对象,记录一些数据到指定文件
package com.itheima.printstream_demo;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/*
PrintStream类 : 打印流
构造方法 :
public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径
成员方法 :
public void println(数据) 打印后换行
public void print(数据) 打印不换行
需求 : 创建打印流对象,记录一些数据到指定文件
*/
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
// public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径
PrintStream printStream = new PrintStream("day12_demo\\print.txt");
// public void println(数据) 打印后换行
printStream.println(123);
printStream.println("abc");
printStream.println(true);
printStream.println(new Object());
printStream.close();
}
}
System.out 对象类型,其实就是PrintStream类型。
可以改变系统输出的流向:System.setOut(打印流)
装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
不使用继承技术扩展功能, 可以降低耦合
已知有接口Star和其子类型LiuDeHua。
public interface Star {
public abstract void sing();
public abstract void dance();
}
需求 :在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
思路 :
定义一个装饰类,去装饰增强 LiuDehua类。
步骤:
创建LiuDeHua类并实现接口Star【被装饰类】
定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
在装饰类中对sing方法进行功能扩展
对dance不做改动
测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
package com.itheima.design_demo;
/*
装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能
使用原则 :
1. 装饰类和被装饰类需要有共同的父类型。
2. 装饰类要传入被装饰类的对象
3. 在装饰类中把要增强扩展的功能进行扩展
4. 对于不要增强的功能直接调用
需求 : 在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
步骤:
1. 创建LiuDeHua类并实现接口Star【被装饰类】
2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
4. 在装饰类中对sing方法进行功能扩展
5. 对dance不做改动
6. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
*/
public class Test {
public static void main(String[] args) {
// 6. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
// 创建被装饰的类对象
LiuDeHua huaZai = new LiuDeHua();// 0x001
// 创建装饰类对象
LiuDeHuaWrapper liuDeHuaWrapper = new LiuDeHuaWrapper(huaZai);
liuDeHuaWrapper.sing();
liuDeHuaWrapper.dance();
}
}
// 明星接口 , 装饰类和被装饰类的父类型
interface Star {
public abstract void sing(); // 唱歌
public abstract void dance();// 跳舞
}
// 1. 创建LiuDeHua类并实现接口Star【被装饰类】
class LiuDeHua implements Star {
@Override
public void sing() {
System.out.println("唱忘情水....");
}
@Override
public void dance() {
System.out.println("华仔在跳老年迪斯高...");
}
}
// 2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
class LiuDeHuaWrapper implements Star {
// 3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象
private LiuDeHua huaZai;// 0x001
public LiuDeHuaWrapper(LiuDeHua huaZai) {// 0x001
this.huaZai = huaZai;
}
// 4. 在装饰类中对sing方法进行功能扩展
@Override
public void sing() {
System.out.print("华仔正在深情的");
huaZai.sing();
}
// 5. 对dance不做改动
@Override
public void dance() {
huaZai.dance();
}
}
下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
把commons-io-2.6.jar包复制到指定的Module的lib目录中
将commons-io-2.6.jar加入到项目中
1)org.apache.commons.io.IOUtils类
public static int copy(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
public static long copyLarge(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
代码实践 :
package com.itheima.commons_io;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
org.apache.commons.io.IOUtils类
public static int copy(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
public static long copyLarge(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
*/
public class Test1 {
public static void main(String[] args) throws IOException {
IOUtils.copy(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin2.jpg"), new FileOutputStream("day12_demo\\liqin.jpg"));
}
}
2)org.apache.commons.io.FileUtils
public static void copyFileToDirectory(final File srcFile, final File destFile):
复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
复制src目录到dest位置。
代码实践:
package com.itheima.commons_io;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/*
org.apache.commons.io.FileUtils
public static void copyFileToDirectory(final File srcFile, final File destFile):
复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
复制src目录到dest目录中。
*/
public class Test2 {
public static void main(String[] args) throws IOException {
FileUtils.copyDirectoryToDirectory(new File("D:\\传智播客\\安装包\\好看的图片") , new File("D:\\图片"));
}
}
commons-io可以简化IO操作。