JAVA基础 三 19-27 异常、IO字符流、多线程

Day19异常之Exception

Throwable 分为错误和异常

1、Error

2、Exception

常见的异常

ClassCastException 类型转换异常

RuntimeException 运行时异常

IndexOutOfBoundsException 下标(索引) 越界异常

SystemException 系统异常

NullPointerException 空指针异常

TimeoutException 阻塞操作超时

SQLException SQL异常

ArrayStoreException 数组存储异常,试图将错误类型的对象存储到一个对象数组时抛出的异常

异常处理的两种方式

1、try ... catch ... finally 的三种搭配方式

a、 try catch

b、 try catch finally

c、 try finally

当通过try catch 将问题处理后,程序会继续执行,

案例

try ... catch ... finally 三者的作用分别是什么

try :用来检测异常

catch:用来捕获异常

finally:释放资源

try后面如果跟多个catch,那么小的异常放在前面,大的异常放在后面,

根据多态的原理,如果大的放在前面,就会将所有的子类对象接收后面的catch就没有意义了。

JDK7新特性 在一个catch里面通过 “|” 可以添加多个异常,基本不用,面试用

Throwable的几个常见方法

try {

System.out.println(1/0);

} catch (Exception e) {

System.out.println( e.getMessage()); 获取异常信息,返回字符串。 //输出 : / by zero获取异常信息

System.out.println(e);//获取异常类名和异常信息,返回字符串 //java.lang.ArithmeticException: / by zero

System.out.println(e.toString()); //java.lang.ArithmeticException: / by zero

输出 e.toString() == e 默认调用toString方法

e.printStackTrace(); //JVM默认就是这种方式处理异常。获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

}

throws的方式处理异常

Throws和Throw的区别

throws

用在方法声明的后面,跟的是异常类名;

可以跟多个异常类名,用逗号隔开,(不分先后顺序)

表示抛出异常,由方法的调用者来处理;

throw

用在方法体内,跟的是异常对象名

只能抛出一个异常对象名

表示抛出异常,由方法体内的语句处理。

A :finally的特点

被finally控制的语句体一定会执行

特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))

B : finally的作用

用于释放资源,在IO流操作和数据库操作中会见到

C: 案例

面试题1 final,finally和finalize的区别,

fianl 可以

修饰类:不能继承

修饰方法 :不能重写

修饰变量:只能赋值一次

finally

是try语句中的一个语句体,被finally控制的语句体一定会执行,

除非jvm被关闭后不执行,在执行到finally之前jvm退出了(比如System.exit(0))

不能单独使用,用来释放资源

finalize:是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

面试题2

如果catch里面有return语句,请问finally的代码还会执行吗? 如果会,请问是在return前还是return后?

答案:会执行 , return先去执行建立一个返回路径,然后再去执行finally,最后return再彻底返回。

案例

public static void main(String[] args) {

Demo d = new Demo();

System.out.println(d.method());

}

class Demo{

public int method(){

int x = 10;

try {

x = 20;

System.out.println(1/0);

return x; //在try要写返回值

} catch (Exception e) {

x=30;

return x; //在catch要写返回值。 因为这两个语句具体返回哪个不确定

}finally{

x = 40;//千万不要在finally不能写return返回语句,finally的作用是释放资源,是一定会执行的,

//,如果在这里写返回语句,因为不管是执行try还是catch都会执行finally,所以会被覆盖

}

}

19.12_异常(异常的注意事项及如何使用异常处理)

A:异常注意事项

* a:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)

* b:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常

* c:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws

B:如何使用异常处理

原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws

区别:

* 后续程序需要继续运行就try

* 后续程序不需要继续运行就throws

Day20-IO字节流

IO流分为 :字节流和字符流

IO流四个顶层父类 InputStream、OutputStream,Reader、Writer

字节流 :

可以操作任何数据,因为计算机中任何数据都是以字节的形式存储的

字节流输入:InputStream,字节流输出:OutputStream

什么什么的 InputStream或者 什么什么的 OutputStream 就是字节流

字符流:

字符流只能操作纯字符数据,比较方便。

字符流输入: Reader,和字符流输出 :Writer

输入是读,输出是写(例如:输入到大脑就是读的过程,从大脑输出就是写的过程)

案例:输入流 FileInputStream

FileInputStream fis = new FileInputStream("xxx.txt");//创建流对象

int a ; //声明一个 int数

while((a= fis.read()) != -1){//通过while遍历,将int数等于IO流读取的字节,再不等于-1,即是所读取的字节

System.out.println(a);

}

fis.close(); //关闭流资源

案例:输出流 FileOutputStream

FileOutputStream fos = new FileOutputStream("yyy.txt"); //创建输出流对象

fos.write(97); //写入流

fos.write(98);

fos.write(99);

fos.write(100);

fos.close(); //关闭流资源

FileOutputStream创建对象时若没有这个文件会创建文件,若有这个文件会先清空这个文件,再重新写入,是下面的情况

FileOutputStream fos = new FileOutputStream("yyy.txt");

如果不想清空原文件那么就在第二个参数写一个true即可

FileOutputStream(String name, boolean append)

name - 与系统有关的文件名

append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处

FileOutputStream fos = new FileOutputStream("yyy.txt" ,true); //创建输出流对象

20.06_IO流(拷贝图片) 核心,会这个Copy就对IO流理解了一半

文件Copy 第一种copy方式,效率低

FileInputStream读取

FileOutputStream写出

FileInputStream fis = new FileInputStream("xxxx.jpg"); //创建输入流对象,关联xxxx.jpg文件

FileOutputStream fos = new FileOutputStream("yyy.jpg"); //创建输出流对象,关联yyy.jpg文件

int b;

while((b = fis.read()) != -1){ //读取文件的每一个字节

fos.write(b); //将每一个字节写出

}

fis.close(); //关闭流资源

fos.close();

原理图

大型文件复制Copy 第二种copy方式

//开发中不推荐使用,因为较大的文件会导致内存溢出

FileInputStream fis = new FileInputStream("YuanWenJian.rar");//创建输入流对象,关联YuanWenJian.rar文件

FileOutputStream fos= new FileOutputStream("CopyYuanWenJian.rar");//创建输出流对象关联CopyYuanWenJian.rar文件

byte[] arr = new byte[fis.available()]; //创建与文件一样大小的字符数组

System.out.println(arr.length);

fis.read(arr); //将文件上的字节读取到内存中

fos.write(arr); //将字节数组中的字符数据写到文件上

fis.close(); //关闭流资源

fos.close();

第三种copy复制文件方式

write(byte[] b, int off, int len)

b - 数据。 off - 数据中的起始偏移量。 len - 要写入的字节数。

FileInputStream fis = new FileInputStream("建党伟业.mp4");//创建输入流对象,关联建党伟业.mp4文件

FileOutputStream fos = new FileOutputStream("Copy建党伟业.mp4");//创建输出流对象,关联Copy建党伟业.mp4文件

byte[] arr = new byte[1024 * 8]; //自定义一个小数组,一般是1024的整数倍

int len ;

while((len = fis.read(arr)) != -1){ //如果忘记写arr,返回的就不是读取的字节个数,而是字节的码表值

fos.write(arr,0,len); //b - 数据。 off - 数据中的起始偏移量。 len - 要写入的字节数。

}

fis.close(); //关闭流资源

fos.close();

BufferedInputStream 的缓冲区和BufferedOutputStream 的缓冲区,通过内存进行计算,效率高很多

FileInputStream fis = new FileInputStream("建党伟业.mp4"); //创建输入流对象,关联建党伟业.mp4文件

FileOutputStream fos =new FileOutputStream("Copy建党伟业.mp4"); //创建输出对象,关联Copy建党伟业.mp4文件

BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对象,对输入流进行包装让其变得更加强大

BufferedOutputStream bos = new BufferedOutputStream(fos);//创建缓冲区对象,对输出流进行包装让其变得更加强大

int b ; //声明一个int值

while(( b = bis.read()) != -1){ //通过BufferedInputStream进行读取

bos.write(b); //写入到BufferedOutputStream文件中

}

bis.close(); //关闭流资源

bos.close();

原理:20.11_IO流(BufferedInputStream和BufferOutputStream拷贝)

* A:缓冲思想

* 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,

* 这是加入了数组这样的缓冲区效果,java本身在设计的时候,

* 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流

* B.BufferedInputStream

* BufferedInputStream内置了一个缓冲区(数组)

* 从BufferedInputStream中读取一个字节时

* BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个

* 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取

* 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个

* C.BufferedOutputStream

* BufferedOutputStream也内置了一个缓冲区(数组)

* 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中

* 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里

原理图:

小数组的读写和带Buffered的读取哪个更快?

* 定义小数组如果是8192个字节大小和Buffered比较的话

* 定义小数组会略胜一筹,因为读和写操作的是同一个数组

* 而Buffered操作的是两个数组

close方法和flush方法的区别

close方法:具备刷新的功能,在关闭流之前,就会刷新一次缓冲区,将缓冲区的字节全部刷新到文件上;

再关闭,close关闭之后就不能写了; colse方法的输入流和输出流都要关闭

flush方法;具备刷新的功能,刷完之后还可以继续写,可以写一次就刷新一次。

应用场景比如QQ聊天,实现实时刷新。 flush方法只有输出流关闭,没有输入流关闭。

例子:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("xxxx.jpg")); //输入流缓冲区

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("yyy.jpg"));//输出流缓冲区

int b ; //声明一个int值

while(( b = bis.read()) != -1){ //通过BufferedInputStream进行读取

bos.write(b); //写入到BufferedOutputStream文件中

bos.flush();

}

//bos.flush(); flush方法只有输出流关闭,没有输入流关闭。

//bis.close(); //关闭流资源

//bos.close();

20.13_IO流(字节流读写中文)

* 字节流读取中文的问题

* 字节流在读中文的时候有可能会读到半个中文,造成乱码

* 字节流写出中文的问题

* 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组

* 写出回车换行 write("\r\n".getBytes());

图片加密 将写出的字节异或上一个数,这个数就是密钥,解密的时候再次异或就可以了

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Copyyyy.jpg"));

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Copyyyy2.jpg"));

int b ; //声明一个int值

while(( b = bis.read()) != -1){ //通过BufferedInputStream进行读取

bos.write(b ^ 123); //通过异或加密,异或一次加密,异或2次还原(取消加密),

}

bis.close(); //关闭流资源

bos.close();

通过输入文件路径,拷贝文件

创建一个录入文件信息方法

public static File getFile(){

Scanner sc = new Scanner(System.in); //创建键盘录入对象

System.out.println("请输入文件夹路径");

while(true){

String nextLine = sc.nextLine(); /接收键盘录入的路径

File file = new File(nextLine); //封装成File对象,并对其判断

if(!file.exists()){

System.out.println("文件不存在,请重新输入?");

}else if(file.isDirectory()){ //判断是否为文件夹路径

System.out.println("是文件夹路径,请重新输入?");

}else{

return file;

}

}

}

调用文件信息方法

File file = getFile(); //获取文件

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName()));

int b ; //声明一个int值

while(( b = bis.read()) != -1){ //通过BufferedInputStream进行读取

bos.write(b); //通过异或加密,异或一次加密,异或2次还原(取消加密),

}

bis.close(); //关闭流资源

bos.close();

录入数据拷贝到文件

// 1、 创建键盘录入对象

Scanner sc = new Scanner(System.in);

// 2、 创建输出流对象,关联text.txt问价

FileOutputStream fos = new FileOutputStream("text.txt");

System.out.println("请输入信息");

// 3、 定义无限循环

while(true){

String nextLine = sc.nextLine();

// 4、遇到quit退出循环

if("quit".equals(nextLine)){

break;

}

// 5、如果不quit,就继续写出

fos.write(nextLine.getBytes());

fos.write("\r\n".getBytes());

}

// 6、关闭流

fos.close();

Day20- IO字符流

读入字符流FileReader

FileReader fr = new FileReader("xxx.txt"); //创建字符流

int b ;

while((b = fr.read()) != -1){ //通过项目默认的码表一次读取一个字符

char ch = (char) b;

System.out.print(ch);

}

  1. fr.close();

写出字符流FileWriter

FileWriter fw = new FileWriter("yyy.txt",true); //true追加,可以再不清除源文件的情况下继续添加内容

fw.write("哈哈");

fw.close();

字符流 读入和写出

FileReader fr = new FileReader("xxx.txt"); //创建读入字符流

FileWriter fw = new FileWriter("zzz.txt"); //创建写出字符流

int b;

while((b= fr.read()) != -1){ //判断读取到最后的字符位置

fw.write(b); //写入字符

}

fr.close(); //关闭流资源

fw.close();

//Writer类中有一个2K的小缓冲区;如故不关流,就会将内容写到缓冲区里,关流会将缓冲区内容刷新出来,再关闭

什么情况下使用字符流或字节流:

什么情况下使用字符流

程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流

读取的时候是按照字符的大小读取的,不会出现半个中文

写出的时候可以直接将字符串写出,不用转换为字节数组

字符流不可以拷贝非纯文本的文件

原因: 因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去 , 如果是?,直接写出,这样写出之后的文件就乱了,看不了了

自定义字符数组的拷贝

FileReader fr = new FileReader("text.txt"); //创建读入字符流

FileWriter fw = new FileWriter("aa.txt"); //创建写出字符流

char[] arr = new char[1024]; //自定义一个字符数组

int len ;

while((len = fr.read(arr)) != -1){ //将文件上的数据读取到字符数组中

fw.write(arr); //将字符数组中的数据写到文件上

}

fr.close(); //关闭流

fw.close();

字符流缓冲区的Copy

BufferedReader bfr = new BufferedReader(new FileReader("text.txt"));//读入字符流缓冲区

BufferedWriter bfw = new BufferedWriter(new FileWriter("aa.txt")); //写出字符流缓冲区

int b ;

while((b = bfr.read()) != -1){

bfw.write(b);

}

bfr.close(); //关闭流

bfw.close();

readLine()方法和newLine()方法

读取流:readLine()是属于BufferedReader: 整行读取

写出流:newLine()是属于BufferedWriter: 换行写出

newLine与 \r\n 换行区别

newLine()是跨平台的方法; \r\n 只支持是windows系统

案例:

BufferedReader bfr = new BufferedReader(new FileReader("text.txt")); //读入字符流缓冲区

BufferedWriter bfw = new BufferedWriter(new FileWriter("aa.txt")); //写出字符流缓冲区

String line ; //是String,而不是int

while((line = bfr.readLine()) != null){ //整行读取 不是等于 -1 ,而是null

bfw.write(line);

bfw.newLine(); //换行写出

//bfw.write("\r");

//bfw.write("\n");

//bfw.write("\r\n");

}

bfr.close(); //关闭流

bfw.close(); //关闭流

将文本Copy后倒叙整行输出

//1、创建流对象

BufferedReader br = new BufferedReader(new FileReader("text.txt"));

BufferedWriter bw = new BufferedWriter(new FileWriter("aa.txt"));

//2、创建集合对象

ArrayList list =new ArrayList<>();

//3、将读取的数据添加到集合对象中

String arr;

while((arr = br.readLine()) != null){ //整行读入; 不等于 null

list.add(arr); //读取后添加到集合

}

//4、倒叙遍历

for(int i = list.size() - 1; i> 0 ; i--){

bw.write(list.get(i)); //写入到每一行里面,get(i)就是集合里的行数

bw.newLine(); //换行

}

//5、关流

br.close();

bw.close();

setLineNumber和getLineNumber 设置行号和获取行号

//创建LineNumberReader读对象

LineNumberReader lr = new LineNumberReader(new FileReader("text.txt"));

String line ;

//底层private int lineNumber = 0;

lr.setLineNumber(100); //设置行号 , 从101开始计数

while((line =lr.readLine()) != null ){

System.out.println(lr.getLineNumber()+ ":"+line); //获取行号

}

lr.close();

Day24多线程

什么是多线程

线程是程序执行的一条路径,一个进程中可以包含多条线程

多线程并发执行可以提高程序的效率,可以同时完成多项工作

多线程的应用场景

迅雷同时下载多个文件

360可以同时执行多项任务

QQ可以和多个人一起视频等等

多线程并发原理图

24.02_多线程(多线程并行和并发的区别)(了解)

并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)

并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊,这就叫并行。

如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,再跟甲聊,再跟乙聊。这就叫并发。

并行:就是两个任务同时进行,

并发:是指两个任务都请求运行,而处理器只能按受一个任务,那这两个任务就得伦理进行运行

开启多线程的第一个方法(共5不步骤)

继承Thread,重写 Thread 类的 run 方法

在主方法中执行

MyThread mt = new MyThread(); //4、创建Thread的子类对象

mt.start(); //5、start()开启线程的方法 ,并不是run方法开启

for (int i = 0; i < 1000; i++) {

System.out.println("我是主方法");

}

定义Thread的子类

class MyThread extends Thread{ //1、创建方法去继承Thread方法

public void run(){ //2、重写run方法

for (int i = 0; i < 1000; i++) { //3、将要执行的代码写在run方法中

System.out.println("我是run方法");

}

}

}

开启多线程的第二个方法(共6不步骤)

实现多线程的方法,实现runnable接口,重写run方法

MyRunnable mr = new MyRunnable(); //4、创建runnable的子类对象

//Runnable rb = mr; //父类指向子类的对象

//Thread t = new Thread(rb);

Thread t = new Thread(mr); //5、将其当作参数传递给Thread的构造函数。

t.start(); //6、开启多线程

for (int i = 0; i < 1000; i++) {

System.out.println("我是主方法");

}

定义Runnable的子类

class MyRunnable implements Runnable{ //1、定义一个类,实现Runnable接口

public void run() { //2、重写run方法

for(int i=0; i //3、将要执行的代码写在run方法中

System.out.println("我是Runnable");

}

}

}

两种方式的区别

继承Thread

好处是:可以直接使用Thread类中中的方法,代码简单

弊端是:如果已经有了父类,就不能用这种方法

实现runnable接口

好处是:即使自己定义的线程有了父类也没关系,因为有了父类也可以实现接口,接口是多实

现的

弊端是: 不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到threa的方法,

代码复杂

24.08_多线程(匿名内部类实现线程的两种方式)

new Thread(){ //1、继承Thread类

public void run(){ //2、重写run方法;

for (int i = 0; i //3、将要执行的代码写在run方法中

System.out.println("aaaaaaaaa");

}

}

}.start(); //4、开启线程

new Thread( //1、将Runnable的子类对象传递给Thread方法中

new Runnable() { //2、实现Runnable的接口;

public void run() { //3、重写run方法

for (int i = 0; i //3、将要执行的代码写在run方法中

System.out.println("bb");

}

}

}).start();//4、开启线程

设置多线程的名字

方法一

private static void demo() {

new Thread("张三"){ //通过构造函数设置名字

public void run(){

System.out.println(this.getName()+"...aaaaaaa");//获得方法

}

}.start();

new Thread("李四"){

public void run(){

System.out.println(this.getName()+"...bb");

}

}.start();

}

方法二

new Thread(){

public void run(){

this.setName("张三"); //通过set方法设置

System.out.println(this.getName()+"...aaaaaaa");//获得方法

}

}.start();

new Thread(){

public void run(){

this.setName("李四");

System.out.println(this.getName()+"...bb");

}

}.start();

//获取当前线程 Thread.currentThread();获取当前正在执行的线程

new Thread(){ //1、继承Thread类

public void run(){ //重写run方法

System.out.println(this.getName()+"..aaaaaaaaaaa");

}

}.start();

new Thread( //1、将Runnable的子类对象传递给Thread方法中

new Runnable() { //2、实现Runnable的接口;

public void run() {

//Thread.currentThread();获取当前正在执行的线程

System.out.println(Thread.currentThread()+"bb");

}

}).start();

Thread.currentThread().setName("我是主线程");//设置主线程

System.out.println(Thread.currentThread().getName());//获取主线程

休眠线程 Thread.sleep

new Thread(){

public void run(){

for (int i = 0; i

try {

Thread.sleep(1000);//设置停留时间1000

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(getName()+"aaaaaaaaaa");

}

}

}.start();

加入线程 join

final Thread t1 = new Thread(){

public void run(){

for (int i = 0; i < 20 ; i++) {

System.out.println(getName()+"aaaaaaaaaaa");

}

}

};

Thread t2 = new Thread(){

public void run(){

for (int i = 0; i

if(i==2){

try { //匿名内部类就是局部内部类

//t1.join();//匿名内部类在使用它所在方法中的局部变量的时候必

须用final修饰

t1.join(1); //有参构造,可以传入毫秒

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(getName()+"bb");

}

}

};

t1.start();

t2.start();

同步代码块

public static void main(String[] args) {

final Print p = new Print();

new Thread(){

public void run(){

while(true){

p.print1(); //在匿名内部类中调用所在方法中的成员变量时需用final修饰

}

}

}.start();

new Thread(){

public void run(){

while(true){

p.print2();

}

}

}.start();

}

}

class Print{

Demo d= new Demo();

public void print1(){

//synchronized(new Demo()){

synchronized(d){ //同步代码块,锁机制,锁对象可以是任意对象

System.out.print("黑");

System.out.print("马");

System.out.print("程");

System.out.print("序");

System.out.print("员");

System.out.print("\r\n");

}

}

public void print2(){

//synchronized(new Demo()){ //锁对象不能是匿名对象,因为匿名对象不是同一个对象

synchronized(d){

System.out.print("传智播客");

System.out.print("智");

System.out.print("播");

System.out.print("客");

System.out.print("\r\n");

}

}

}

class Demo{}

同步方法

public static void main(String[] args) {

final Print2 p = new Print2();

new Thread(){

public void run(){

while(true){

p.print1(); //在匿名内部类中调用所在方法中的成员变量时需用final修饰

}

}

}.start();

new Thread(){

public void run(){

while(true){

p.print2();

}

}

}.start();

}

}

class Print2{

//静态方法锁对象不能是this,因为this创建对象的时候才有值;

//静态方法是随着类的加载而加载的,在类加载的时候有对象,这个对象就是字节码对象;

静态的同步方法的锁对象是该类的字节码对象,Print2.class就是字节码对象

//非静态的同步方法的锁对象是this

public static synchronized void print1(){

System.out.print("黑");

System.out.print("马");

System.out.print("程");

System.out.print("序");

System.out.print("员");

System.out.print("\r\n");

}

public void print2(){

//synchronized(this){

synchronized(Print2.class){ //Print2.class就是字节码对象

System.out.print("传智播客");

System.out.print("智");

System.out.print("播");

System.out.print("客");

System.out.print("\r\n");

}

}

}

//多线程的安全问题 需要代码同步

public static void main(String[] args) {

new Ticket().start();

new Ticket().start();

new Ticket().start();

new Ticket().start();

}

class Ticket extends Thread{

private static int ticket = 100;

//private static Object obj = new Object();//如果引用数据类型成员变量当作锁对

象,必 须是静态的

public void run(){

while(true){

//synchronized(obj ){

synchronized(Thread.class){ //当多线程并发想改变同一个数据时 ticket,

建议使用同步或者同步代码块, Thread.class

if(ticket

break;

}

try {

Thread.sleep(10); //线程1睡, 线程2睡, 线程3睡, 线程4睡, == 时就

会跳过0,所以会一直循环下去-...

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(getName()+"...第"+ ticket-- +"个");

}

}

}

}

24.21_多线程(以前的线程安全的类回顾)(掌握)

A:回顾以前说过的线程安全问题

* Vector是线程安全的, ArrayList是线程不安全的

* StringBuffer是线程安全的, StringBuilder是线程不安全的

* Hashtable是线程安全的, HashMap是线程不安全的

Day25多线程

public static void main(String[] args) {

//Singleton s1 = new Singleton();

//Singleton s2 = new Singleton();

/*Singleton s3 = Singleton.s; //静态方法通过类名.调用。

Singleton s4 = Singleton.s; //静态方法通过类名.调用。

System.out.println(s3==s4);// s3、s4指向的同一个对象的地址; 输出true;*/

/*Singleton s4 = Singleton.s; //静态方法通过类名.调用。

Singleton.s = null; //修改s地址后s4==s5地址就会不同,输出false

Singleton s5 = Singleton.s; //静态方法通过类名.调用。

System.out.println(s4==s5);// s4==s5指向的不是同一个对象的地址; 输出false; 有弊端*/

Singleton s7 = Singleton.getSs();

Singleton s8 = Singleton.getSs();

System.out.println(s7 == s8); //s7、s8指向的同一个对象的地址; 输出true;

}

}

//饿汉式 上来先创建对象,消耗内存

class Singleton{

//1.私有构造方法,其他类就不能通过new创建对象了, 例如 s1、s2

private Singleton(){};

//2、创建本类对象 必须是静态的可以通过类名调用

//public static Singleton s = new Singleton();

//3、创建本类 私有成员变量

private static Singleton ss = new Singleton();

//对外提供get方法;

public static Singleton getSs() { //获取实例(对象)

return ss;

}

}

//懒汉式 //等用的时候才会创建对象,消耗时间

//如果多线程访问,有可能会一次创建多个对象

class Singletons{

//1.私有构造方法,其他类就不能通过new创建对象了

private Singletons (){};

//2、创建静态私有属性;必须是静态的可以通过类名调用

private static Singletons st ;

//3、提供get方法

public static Singletons getSt() { //获取对象

if(st == null){

//线程1等待 ; 线程2等待;

st = new Singletons();

}

return st;

}

饿汉式和懒汉式的区别

推荐使用饿汉式

1、饿汉式是空间(内存)转换时间, 内存可以扩大,时间不能变

懒汉式是时间转换空间(内存)

2、在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象。

Timer在指定时间内做什么事

//定义一个要执行的事

class MyTimerTask extends TimerTask{

@Override

public void run() {

System.out.println("起床了");

}

}

Timer t = new Timer();

//第一个参数在指定时间做什么事,第二个参数指定执行时间,第三个参数间隔多少时间执行一次

t.schedule(new MyTimerTask(), new Date(117, 8, 14, 21, 11, 10),1000);

//t.schedule(new MyTimerTask(), new Date(117, 8, 14, 21, 11, 10));

while(true){

Thread.sleep(1000);

System.out.println(new Date());

}

25.04两个线程间的通信

class Prienter{

private int falg= 1;

public void prient1() throws InterruptedException{

synchronized(this){ //开启方法同步 this就是同步的一把锁

if(falg != 1){

this.wait();

//在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待

}

System.out.print("黑");

System.out.print("马");

System.out.print("程");

System.out.print("序");

System.out.print("员");

System.out.println();

falg = 2;

this.notify(); //唤醒在此对象监视器上等待的单个线程

}

}

public void prient2() throws InterruptedException{

synchronized(this){

if(falg != 2){

this.wait();

}

System.out.print("传");

System.out.print("智");

System.out.print("播");

System.out.print("客");

System.out.println();

falg = 1;

this.notify()

}

}

}

在main方法中测试

final Prienter p = new Prienter();

new Thread(){

public void run(){

while(true){

try {

p.prient1();

} catch (InterruptedException e) {

e.printStackTrace();

} //匿名内部类调用所在方法中的成员变量时,需用final修饰

}

}

}.start();

new Thread(){

public void run(){

while(true){

try {

p.prient2();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}.start();

注意事项

1、 在同步代码块中,用哪个锁对象,就用哪个对象调用wait方法

2、为什么wait和notify方法定义在Object类中?

答:因为锁对象可以是任意的,Object是所有类的父类,所以wait和notify方法需要定义在Object这个类中

3、sleep方法和wait方法的区别?

答案:(1)sleep方法必须传入参数,参数就是时间,时间到了自动醒来

wait方法可以传入参数也可以不传入参数,传入参数就是在在参数时间结束后等待,不传入参数就是直接等待;

(2) sleep方法在同步函数或者同步代码块中,不释放锁,睡着也要抱着锁睡

wait方法在同步函数或者同步代码块中,释放锁

25.06三个线程间的通信,不是随便唤醒所有,而是指定唤醒下一个目标

class Prienter3{

private ReentrantLock r = new ReentrantLock();//可重复使用的锁,获取lock和unlock的方法

private Condition c1 = r.newCondition(); //newCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例

private Condition c2 = r.newCondition();

private Condition c3 = r.newCondition();

private int falg= 1;

public void prient1() throws InterruptedException{

r.lock(); //获取锁

if(falg != 1){

c1.await(); //造成当前线程在接到信号或被中断之前一直处于等待状态

}

System.out.print("黑");

System.out.print("马");

System.out.print("程");

System.out.print("序");

System.out.print("员");

System.out.println();

falg = 2;

c2.signal(); //唤醒一个等待线程

r.unlock(); //释放锁

}

public void prient2() throws InterruptedException{

r.lock(); //获取锁

if(falg != 2){

c2.await();

}

System.out.print("传");

System.out.print("智");

System.out.print("播");

System.out.print("客");

System.out.println();

falg = 3;

c3.signal();

r.unlock(); //释放锁

}

public void prient3() throws InterruptedException{

r.lock(); //获取锁

if(falg != 3){

c3.await();

}

System.out.print("i");

System.out.print("t");

System.out.print("h");

System.out.print("m");

System.out.println();

falg = 1;

c1.signal();

r.unlock(); //释放锁

}

}

在main方法中测试代码如下

final Prienter2 p = new Prienter2();

//线程一

new Thread(){

public void run(){

while(true){

try {

p.prient1();

} catch (InterruptedException e) {

e.printStackTrace();

} //匿名内部类调用所在方法中的成员变量时,需用final修饰

}

}

}.start();

//线程二

new Thread(){

public void run(){

while(true){

try {

p.prient2();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}.start();

//线程三

new Thread(){

public void run(){

while(true){

try {

p.prient3();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}.start();

 

代码如下:

Day19异常之Exception
Throwable 分为错误和异常
       1、Error
       2、Exception
                 常见的异常
              ClassCastException  类型转换异常
	      RuntimeException 运行时异常
	      IndexOutOfBoundsException 下标(索引) 越界异常
	      SystemException 系统异常
	      NullPointerException 空指针异常
	      TimeoutException 阻塞操作超时
	      SQLException SQL异常
	     ArrayStoreException 数组存储异常,试图将错误类型的对象存储到一个对象数组时抛出的异常
 异常处理的两种方式
    1、try ... catch ... finally 的三种搭配方式 
         a、 try  catch  
  b、 try  catch   finally
  c、 try  finally
  当通过try  catch 将问题处理后,程序会继续执行,
 
案例



<=================================================================>
try ... catch ... finally  三者的作用分别是什么
        try :用来检测异常
        catch:用来捕获异常
        finally:释放资源
  try后面如果跟多个catch,那么小的异常放在前面,大的异常放在后面,
  根据多态的原理,如果大的放在前面,就会将所有的子类对象接收后面的catch就没有意义了。
JDK7新特性 在一个catch里面通过  “|”  可以添加多个异常,基本不用,面试用

       Throwable的几个常见方法
              try {
			System.out.println(1/0);
		} catch (Exception e) {
		    System.out.println( e.getMessage());   获取异常信息,返回字符串。    //输出 : / by zero获取异常信息
		    System.out.println(e);//获取异常类名和异常信息,返回字符串  //java.lang.ArithmeticException: / by zero
	            System.out.println(e.toString());                                              //java.lang.ArithmeticException: / by zero
	            输出 e.toString() == e 默认调用toString方法
		    e.printStackTrace(); //JVM默认就是这种方式处理异常。获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
		}
throws的方式处理异常


Throws和Throw的区别
	  throws 
	    用在方法声明的后面,跟的是异常类名;
	    可以跟多个异常类名,用逗号隔开,(不分先后顺序)
	    表示抛出异常,由方法的调用者来处理;
	  throw
	     用在方法体内,跟的是异常对象名
	     只能抛出一个异常对象名
	     表示抛出异常,由方法体内的语句处理。

A :finally的特点
		被finally控制的语句体一定会执行
	        特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))
B : finally的作用
	       用于释放资源,在IO流操作和数据库操作中会见到
C: 案例

 面试题1   final,finally和finalize的区别,
	    fianl 可以
	          修饰类:不能继承
	          修饰方法 :不能重写
	          修饰变量:只能赋值一次
	    finally
	           是try语句中的一个语句体,被finally控制的语句体一定会执行,
	           除非jvm被关闭后不执行,在执行到finally之前jvm退出了(比如System.exit(0))
	           不能单独使用,用来释放资源
	    finalize:是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
面试题2 
	   如果catch里面有return语句,请问finally的代码还会执行吗? 如果会,请问是在return前还是return后?
	   答案:会执行 , return先去执行建立一个返回路径,然后再去执行finally,最后return再彻底返回。

案例 
	public static void main(String[] args) {
		Demo d = new Demo();
		System.out.println(d.method());
	} 
class Demo{
	public int method(){
		int x = 10;
		try {
			x = 20;
			System.out.println(1/0);
			return x;          //在try要写返回值
		} catch (Exception e) {
			 x=30;
			 return x;        //在catch要写返回值。  因为这两个语句具体返回哪个不确定
		}finally{
			x = 40;//千万不要在finally不能写return返回语句,finally的作用是释放资源,是一定会执行的,
			       //,如果在这里写返回语句,因为不管是执行try还是catch都会执行finally,所以会被覆盖
		} 
	}
19.12_异常(异常的注意事项及如何使用异常处理)
A:异常注意事项
	* a:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
	* b:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
	* c:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
B:如何使用异常处理
	  原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
	     区别:
		* 后续程序需要继续运行就try
		* 后续程序不需要继续运行就throws

Day20-IO字节流
	 IO流分为 :字节流和字符流
                 IO流四个顶层父类 InputStream、OutputStream,Reader、Writer
		    字节流 :
                      可以操作任何数据,因为计算机中任何数据都是以字节的形式存储的
		      字节流输入:InputStream,字节流输出:OutputStream
		        什么什么的 InputStream或者   什么什么的  OutputStream 就是字节流
		    字符流:
                     字符流只能操作纯字符数据,比较方便。
		     字符流输入: Reader,和字符流输出 :Writer        
输入是读,输出是写(例如:输入到大脑就是读的过程,从大脑输出就是写的过程)
      案例:输入流 FileInputStream 
		FileInputStream fis = new FileInputStream("xxx.txt");//创建流对象
		int a ;            //声明一个 int数
		while((a= fis.read()) != -1){//通过while遍历,将int数等于IO流读取的字节,再不等于-1,即是所读取的字节
			System.out.println(a);
		}
		fis.close();    //关闭流资源

             案例:输出流 FileOutputStream 
               FileOutputStream fos = new FileOutputStream("yyy.txt");  //创建输出流对象
		fos.write(97);  //写入流
		fos.write(98);
		fos.write(99);
		fos.write(100);
		fos.close(); //关闭流资源
      
 	  FileOutputStream创建对象时若没有这个文件会创建文件,若有这个文件会先清空这个文件,再重新写入,是下面的情况
	  FileOutputStream fos = new FileOutputStream("yyy.txt");

	   如果不想清空原文件那么就在第二个参数写一个true即可
	   FileOutputStream(String name, boolean append)
	   name - 与系统有关的文件名 
	   append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 
           FileOutputStream fos = new FileOutputStream("yyy.txt" ,true);  //创建输出流对象

20.06_IO流(拷贝图片)  核心,会这个Copy就对IO流理解了一半
        文件Copy 第一种copy方式,效率低
 FileInputStream读取
 FileOutputStream写出
		FileInputStream fis = new FileInputStream("xxxx.jpg"); //创建输入流对象,关联xxxx.jpg文件
		FileOutputStream fos = new FileOutputStream("yyy.jpg"); //创建输出流对象,关联yyy.jpg文件
		int b;
		while((b = fis.read()) != -1){  //读取文件的每一个字节
			fos.write(b);                //将每一个字节写出
		}
		fis.close();  //关闭流资源
		fos.close();
原理图

大型文件复制Copy  第二种copy方式
		//开发中不推荐使用,因为较大的文件会导致内存溢出
		FileInputStream fis = new FileInputStream("YuanWenJian.rar");//创建输入流对象,关联YuanWenJian.rar文件
		FileOutputStream fos= new FileOutputStream("CopyYuanWenJian.rar");//创建输出流对象关联CopyYuanWenJian.rar文件	
		byte[] arr =  new byte[fis.available()]; //创建与文件一样大小的字符数组
		System.out.println(arr.length);
		fis.read(arr);       //将文件上的字节读取到内存中
		fos.write(arr);      //将字节数组中的字符数据写到文件上
		fis.close();      //关闭流资源
		fos.close();
 第三种copy复制文件方式 
 write(byte[] b, int off, int len) 
  b - 数据。  off - 数据中的起始偏移量。  len - 要写入的字节数。 
	    FileInputStream fis = new FileInputStream("建党伟业.mp4");//创建输入流对象,关联建党伟业.mp4文件
	    FileOutputStream fos = new FileOutputStream("Copy建党伟业.mp4");//创建输出流对象,关联Copy建党伟业.mp4文件
	    byte[] arr = new byte[1024 * 8];  //自定义一个小数组,一般是1024的整数倍
	    int len ; 
	    while((len = fis.read(arr)) != -1){  //如果忘记写arr,返回的就不是读取的字节个数,而是字节的码表值
	    	 fos.write(arr,0,len);  //b - 数据。  off - 数据中的起始偏移量。  len - 要写入的字节数。
	    }
	    fis.close();  //关闭流资源
	    fos.close(); 
BufferedInputStream 的缓冲区和BufferedOutputStream 的缓冲区,通过内存进行计算,效率高很多
		FileInputStream fis = new FileInputStream("建党伟业.mp4"); //创建输入流对象,关联建党伟业.mp4文件
		FileOutputStream fos =new FileOutputStream("Copy建党伟业.mp4"); //创建输出对象,关联Copy建党伟业.mp4文件
		BufferedInputStream bis = new BufferedInputStream(fis); //创建缓冲区对象,对输入流进行包装让其变得更加强大
		BufferedOutputStream bos = new BufferedOutputStream(fos);//创建缓冲区对象,对输出流进行包装让其变得更加强大
		int b ;                //声明一个int值
		while(( b = bis.read()) != -1){  //通过BufferedInputStream进行读取
			bos.write(b);              //写入到BufferedOutputStream文件中
		}
		bis.close(); //关闭流资源
		bos.close();
原理:20.11_IO流(BufferedInputStream和BufferOutputStream拷贝)   
* A:缓冲思想
	* 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
	* 这是加入了数组这样的缓冲区效果,java本身在设计的时候,
	* 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
* B.BufferedInputStream
	* BufferedInputStream内置了一个缓冲区(数组)
	* 从BufferedInputStream中读取一个字节时
	* BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
	* 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
	* 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
* C.BufferedOutputStream
	* BufferedOutputStream也内置了一个缓冲区(数组)
	* 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
	* 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
原理图:

   小数组的读写和带Buffered的读取哪个更快?
	* 定义小数组如果是8192个字节大小和Buffered比较的话
	* 定义小数组会略胜一筹,因为读和写操作的是同一个数组
	* 而Buffered操作的是两个数组

 close方法和flush方法的区别
	    close方法:具备刷新的功能,在关闭流之前,就会刷新一次缓冲区,将缓冲区的字节全部刷新到文件上;
	    再关闭,close关闭之后就不能写了; colse方法的输入流和输出流都要关闭 
	    flush方法;具备刷新的功能,刷完之后还可以继续写,可以写一次就刷新一次。
	    应用场景比如QQ聊天,实现实时刷新。 flush方法只有输出流关闭,没有输入流关闭。
例子:
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("xxxx.jpg")); //输入流缓冲区
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("yyy.jpg"));//输出流缓冲区
		int b ;                //声明一个int值
		while(( b = bis.read()) != -1){  //通过BufferedInputStream进行读取
			bos.write(b);              //写入到BufferedOutputStream文件中  
			bos.flush();
		}  
		//bos.flush(); flush方法只有输出流关闭,没有输入流关闭。 
	       //bis.close(); //关闭流资源
		//bos.close();
20.13_IO流(字节流读写中文) 
* 字节流读取中文的问题
	* 字节流在读中文的时候有可能会读到半个中文,造成乱码 
* 字节流写出中文的问题
	* 字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组 
	* 写出回车换行 write("\r\n".getBytes());
 图片加密    将写出的字节异或上一个数,这个数就是密钥,解密的时候再次异或就可以了
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Copyyyy.jpg"));  
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Copyyyy2.jpg")); 
		int b ;                          //声明一个int值
		while(( b = bis.read()) != -1){  //通过BufferedInputStream进行读取
			bos.write(b ^ 123);  //通过异或加密,异或一次加密,异或2次还原(取消加密),            
		}
		bis.close();                     //关闭流资源
		bos.close();
通过输入文件路径,拷贝文件
创建一个录入文件信息方法
	public static File getFile(){
		Scanner sc = new Scanner(System.in);    //创建键盘录入对象
		System.out.println("请输入文件夹路径");
		while(true){
			String nextLine = sc.nextLine();     /接收键盘录入的路径
			File file = new File(nextLine);        //封装成File对象,并对其判断
			if(!file.exists()){
				 System.out.println("文件不存在,请重新输入?");
			}else if(file.isDirectory()){    //判断是否为文件夹路径
				System.out.println("是文件夹路径,请重新输入?");
			}else{
				return file;
			}
		} 
	}
  调用文件信息方法
		File file = getFile();   //获取文件
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));  
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName())); 
		int b ;                          //声明一个int值
		while(( b = bis.read()) != -1){  //通过BufferedInputStream进行读取
			bos.write(b); //通过异或加密,异或一次加密,异或2次还原(取消加密),            
		}
		bis.close();                     //关闭流资源
		bos.close();

录入数据拷贝到文件 
		// 1、 创建键盘录入对象
		Scanner sc = new Scanner(System.in); 
		// 2、 创建输出流对象,关联text.txt问价
		FileOutputStream fos = new FileOutputStream("text.txt");
		System.out.println("请输入信息");
		// 3、 定义无限循环
		while(true){
			String nextLine = sc.nextLine();
			// 4、遇到quit退出循环
			if("quit".equals(nextLine)){
				break;
			}
      		       // 5、如果不quit,就继续写出
			fos.write(nextLine.getBytes());
			fos.write("\r\n".getBytes());
		}
		// 6、关闭流
		fos.close();
Day20- IO字符流
    读入字符流FileReader
		FileReader fr = new FileReader("xxx.txt"); //创建字符流
		int b ;
		while((b = fr.read()) != -1){  //通过项目默认的码表一次读取一个字符
			char ch = (char) b;
			System.out.print(ch);
		}
		fr.close();
 写出字符流FileWriter
    	      FileWriter fw = new FileWriter("yyy.txt",true); //true追加,可以再不清除源文件的情况下继续添加内容
		fw.write("哈哈");
		fw.close();
字符流 读入和写出
  		FileReader fr = new FileReader("xxx.txt"); //创建读入字符流
		FileWriter fw = new FileWriter("zzz.txt"); //创建写出字符流
		int b;
		while((b= fr.read()) != -1){  //判断读取到最后的字符位置
			fw.write(b);              //写入字符
		}
		fr.close();   //关闭流资源
		fw.close();
//Writer类中有一个2K的小缓冲区;如故不关流,就会将内容写到缓冲区里,关流会将缓冲区内容刷新出来,再关闭
什么情况下使用字符流或字节流:

什么情况下使用字符流
 程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
 读取的时候是按照字符的大小读取的,不会出现半个中文
 写出的时候可以直接将字符串写出,不用转换为字节数组
字符流不可以拷贝非纯文本的文件
原因:  因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去 , 如果是?,直接写出,这样写出之后的文件就乱了,看不了了
自定义字符数组的拷贝
		FileReader fr = new FileReader("text.txt"); //创建读入字符流
		FileWriter fw = new FileWriter("aa.txt");   //创建写出字符流
		char[] arr = new char[1024];   //自定义一个字符数组
		int len ;
		while((len = fr.read(arr)) != -1){ //将文件上的数据读取到字符数组中
			fw.write(arr);               //将字符数组中的数据写到文件上
		}
		fr.close(); //关闭流
		fw.close();
字符流缓冲区的Copy
	      BufferedReader bfr = new BufferedReader(new FileReader("text.txt"));//读入字符流缓冲区
	      BufferedWriter bfw = new BufferedWriter(new FileWriter("aa.txt")); //写出字符流缓冲区
		int b ;
		while((b = bfr.read()) != -1){
			bfw.write(b);
		}
		bfr.close(); //关闭流
		bfw.close();
readLine()方法和newLine()方法
       读取流:readLine()是属于BufferedReader:  整行读取
       写出流:newLine()是属于BufferedWriter:  换行写出  
	newLine与 \r\n 换行区别
	newLine()是跨平台的方法; \r\n 只支持是windows系统
案例:
	     BufferedReader bfr = new BufferedReader(new FileReader("text.txt")); //读入字符流缓冲区
	     BufferedWriter bfw = new BufferedWriter(new FileWriter("aa.txt")); //写出字符流缓冲区
		String line ;   //是String,而不是int
		while((line = bfr.readLine()) != null){    //整行读取 不是等于 -1 ,而是null
			bfw.write(line);
			bfw.newLine();   //换行写出  
			//bfw.write("\r");
			//bfw.write("\n");
			//bfw.write("\r\n");
		}
		bfr.close(); //关闭流 
		bfw.close(); //关闭流 
将文本Copy后倒叙整行输出
		//1、创建流对象
		BufferedReader br = new BufferedReader(new FileReader("text.txt"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("aa.txt"));
		//2、创建集合对象
		 ArrayList<String > list =new ArrayList<>();
		//3、将读取的数据添加到集合对象中
		 String arr;
		 while((arr = br.readLine()) != null){  //整行读入; 不等于 null
			 list.add(arr);  //读取后添加到集合
		 }
		//4、倒叙遍历
		 for(int i = list.size() - 1; i> 0 ; i--){
			 bw.write(list.get(i)); //写入到每一行里面,get(i)就是集合里的行数
			 bw.newLine();          //换行
		 }
		//5、关流
		 br.close();
		 bw.close();
setLineNumber和getLineNumber 设置行号和获取行号
		//创建LineNumberReader读对象
		LineNumberReader lr = new LineNumberReader(new FileReader("text.txt"));
		String line ;   
		//底层private int lineNumber = 0;
		lr.setLineNumber(100); //设置行号 , 从101开始计数
		while((line =lr.readLine()) != null ){  
			System.out.println(lr.getLineNumber()+ ":"+line); //获取行号
		}
		lr.close();
Day24多线程
          什么是多线程
	    线程是程序执行的一条路径,一个进程中可以包含多条线程
	      多线程并发执行可以提高程序的效率,可以同时完成多项工作
	  多线程的应用场景
	    迅雷同时下载多个文件
	      360可以同时执行多项任务
	      QQ可以和多个人一起视频等等

多线程并发原理图


24.02_多线程(多线程并行和并发的区别)(了解)
        并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
        并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
     比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊,这就叫并行。
     如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,再跟甲聊,再跟乙聊。这就叫并发。
   并行:就是两个任务同时进行,
   并发:是指两个任务都请求运行,而处理器只能按受一个任务,那这两个任务就得伦理进行运行

开启多线程的第一个方法(共5不步骤)
   继承Thread,重写 Thread 类的 run 方法
       在主方法中执行
                MyThread mt = new MyThread(); //4、创建Thread的子类对象
		mt.start();      //5、start()开启线程的方法 ,并不是run方法开启
		for (int i = 0; i < 1000; i++) {
			System.out.println("我是主方法");
		}
       定义Thread的子类
               class MyThread extends Thread{  //1、创建方法去继承Thread方法
	       public void run(){          //2、重写run方法
		for (int i = 0; i < 1000; i++) {  //3、将要执行的代码写在run方法中
			System.out.println("我是run方法");			
		}
	}	 
                }

开启多线程的第二个方法(共6不步骤)
     实现多线程的方法,实现runnable接口,重写run方法
		MyRunnable mr = new MyRunnable();    //4、创建runnable的子类对象
		//Runnable rb = mr; //父类指向子类的对象
		//Thread t = new Thread(rb);   
		Thread t = new Thread(mr);                    //5、将其当作参数传递给Thread的构造函数。
		t.start();                                                    //6、开启多线程
		 
		for (int i = 0; i < 1000; i++) {
			System.out.println("我是主方法");
		}
      定义Runnable的子类
                class MyRunnable implements Runnable{     //1、定义一个类,实现Runnable接口
	        public void run() {                                           //2、重写run方法
		for(int i=0; i <1000;i++){                                  //3、将要执行的代码写在run方法中
			System.out.println("我是Runnable");
		}
	}
   }

两种方式的区别
     继承Thread
     好处是:可以直接使用Thread类中中的方法,代码简单
     弊端是:如果已经有了父类,就不能用这种方法

     实现runnable接口
            好处是:即使自己定义的线程有了父类也没关系,因为有了父类也可以实现接口,接口是多实   
        现的
           弊端是: 不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到threa的方法,
        代码复杂
24.08_多线程(匿名内部类实现线程的两种方式)
new Thread(){            //1、继承Thread类
			public void run(){   //2、重写run方法;
				for (int i = 0; i <1000; i++) {//3、将要执行的代码写在run方法中
					System.out.println("aaaaaaaaa");
				}
			}
}.start();            //4、开启线程

new Thread(              //1、将Runnable的子类对象传递给Thread方法中
		new Runnable() {     //2、实现Runnable的接口;
			public void run() { //3、重写run方法
				for (int i = 0; i <1000; i++) {//3、将要执行的代码写在run方法中
				    System.out.println("bb");
			        }	
		         }
    }).start();//4、开启线程

     设置多线程的名字
     方法一
         private static void demo() {
		new	Thread("张三"){    //通过构造函数设置名字
	    	public void run(){
	    		System.out.println(this.getName()+"...aaaaaaa");//获得方法
	    	}
	    }.start();
	    new	Thread("李四"){
	    	public void run(){
	    		System.out.println(this.getName()+"...bb");
	    	}
	    }.start();
	}
  方法二
   new Thread(){
	    	public void run(){
	    		this.setName("张三"); //通过set方法设置
	    		System.out.println(this.getName()+"...aaaaaaa");//获得方法
	    	}
	    }.start();
	    new	Thread(){
	    	public void run(){
	    		this.setName("李四");
	    		System.out.println(this.getName()+"...bb");
	    	}
	    }.start();
//获取当前线程    Thread.currentThread();获取当前正在执行的线程
		new Thread(){  //1、继承Thread类
			public void run(){ //重写run方法
				System.out.println(this.getName()+"..aaaaaaaaaaa");
			}
		}.start();
		
		new Thread(              //1、将Runnable的子类对象传递给Thread方法中
			new Runnable() {     //2、实现Runnable的接口;
			   public void run() {
				                 //Thread.currentThread();获取当前正在执行的线程
				  System.out.println(Thread.currentThread()+"bb");
			   }
		}).start();
		Thread.currentThread().setName("我是主线程");//设置主线程
		System.out.println(Thread.currentThread().getName());//获取主线程
休眠线程 Thread.sleep
  	     new Thread(){
			public void run(){
				for (int i = 0; i <10; i++) {
					try {
						Thread.sleep(1000);//设置停留时间1000
					} catch (InterruptedException e) {
						
						e.printStackTrace();
					}
					System.out.println(getName()+"aaaaaaaaaa");
				}
			}
		}.start();
 加入线程 join
		final Thread t1 = new Thread(){
			public void run(){
				for (int i = 0; i < 20 ; i++) {
					System.out.println(getName()+"aaaaaaaaaaa");
				}
			}
		}; 
    		
		Thread t2 = new Thread(){
			public void run(){ 
				for (int i = 0; i <30 ; i++) {
					if(i==2){
						try {               //匿名内部类就是局部内部类
							//t1.join();//匿名内部类在使用它所在方法中的局部变量的时候必  
                                                                            须用final修饰
						    t1.join(1); //有参构造,可以传入毫秒
						} catch (InterruptedException e) { 
							e.printStackTrace();
						}         
					}
					System.out.println(getName()+"bb");
				}
			}
		};
		t1.start();
		t2.start();
同步代码块
public static void main(String[] args) {
		final Print p = new Print();
		new Thread(){
			public void run(){
				while(true){
					p.print1(); //在匿名内部类中调用所在方法中的成员变量时需用final修饰
				} 
			}
		}.start(); 
		new Thread(){
			public void run(){
				while(true){
					p.print2();
				}
			}
		}.start();
	} 
} 
class Print{
	Demo d= new Demo();
	public void print1(){ 
		//synchronized(new Demo()){ 
		synchronized(d){    //同步代码块,锁机制,锁对象可以是任意对象
		System.out.print("黑");
		System.out.print("马");
		System.out.print("程");
		System.out.print("序");
		System.out.print("员");
		System.out.print("\r\n");
		}
	}
	public void print2(){ 
		//synchronized(new Demo()){ //锁对象不能是匿名对象,因为匿名对象不是同一个对象
		synchronized(d){
		System.out.print("传智播客");
		System.out.print("智");
		System.out.print("播");
		System.out.print("客");
		System.out.print("\r\n");
		}
	 } 
}
class Demo{}
同步方法
public static void main(String[] args) {
		final Print2 p = new Print2();
		new Thread(){
			public void run(){
				while(true){
					p.print1(); //在匿名内部类中调用所在方法中的成员变量时需用final修饰
				} 
			}
		}.start();
		
		new Thread(){
			public void run(){
				while(true){
					p.print2();
				}
			}
		}.start();
	}
	
}

class Print2{ 
	//静态方法锁对象不能是this,因为this创建对象的时候才有值;
	    //静态方法是随着类的加载而加载的,在类加载的时候有对象,这个对象就是字节码对象;
       静态的同步方法的锁对象是该类的字节码对象,Print2.class就是字节码对象
	//非静态的同步方法的锁对象是this 
	public static synchronized void print1(){     
		System.out.print("黑");
		System.out.print("马");
		System.out.print("程");
		System.out.print("序");
		System.out.print("员");
		System.out.print("\r\n");
		} 
	public void print2(){  
		//synchronized(this){
	    synchronized(Print2.class){ //Print2.class就是字节码对象
		System.out.print("传智播客");
		System.out.print("智");
		System.out.print("播");
		System.out.print("客");
		System.out.print("\r\n");
		}
	 } 
}
//多线程的安全问题 需要代码同步
public static void main(String[] args) {
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
		new Ticket().start();
}
class Ticket extends Thread{ 
	private static int ticket = 100; 
	//private static Object obj = new Object();//如果引用数据类型成员变量当作锁对
                                                                                象,必 须是静态的
	public void run(){
		while(true){ 
			//synchronized(obj ){
		  synchronized(Thread.class){ //当多线程并发想改变同一个数据时 ticket, 
                                                                    建议使用同步或者同步代码块, Thread.class
				if(ticket <= 0){
					break;
				} 
				try {
					Thread.sleep(10); //线程1睡, 线程2睡, 线程3睡, 线程4睡, == 时就
                                                                        会跳过0,所以会一直循环下去-...
				} catch (InterruptedException e) { 
					e.printStackTrace();
				}
				System.out.println(getName()+"...第"+ ticket--  +"个"); 
		   }
		}
	}
}
24.21_多线程(以前的线程安全的类回顾)(掌握)
  A:回顾以前说过的线程安全问题 
	* Vector是线程安全的,      ArrayList是线程不安全的
	* StringBuffer是线程安全的,     StringBuilder是线程不安全的
	* Hashtable是线程安全的,      HashMap是线程不安全的


Day25多线程
   public static void main(String[] args) {
		//Singleton s1 = new Singleton();
		//Singleton s2 = new Singleton();
		/*Singleton s3 = Singleton.s;	//静态方法通过类名.调用。
		Singleton s4 = Singleton.s;	//静态方法通过类名.调用。 
		System.out.println(s3==s4);// s3、s4指向的同一个对象的地址; 输出true;*/
		
		/*Singleton s4 = Singleton.s;	//静态方法通过类名.调用。
		Singleton.s = null;      //修改s地址后s4==s5地址就会不同,输出false 
		Singleton s5 = Singleton.s;	//静态方法通过类名.调用。
		System.out.println(s4==s5);// s4==s5指向的不是同一个对象的地址; 输出false; 有弊端*/		
		
		Singleton s7 = Singleton.getSs(); 
		Singleton s8 =  Singleton.getSs();
		System.out.println(s7 == s8);  //s7、s8指向的同一个对象的地址; 输出true; 
	}
}
//饿汉式 上来先创建对象,消耗内存
class Singleton{
	//1.私有构造方法,其他类就不能通过new创建对象了, 例如 s1、s2
	private Singleton(){};
	//2、创建本类对象 必须是静态的可以通过类名调用
	//public static Singleton s = new Singleton();
	//3、创建本类 私有成员变量
	private static Singleton ss = new Singleton();
	//对外提供get方法;
	public static Singleton getSs() { //获取实例(对象)
		return ss;
	}	
}
//懒汉式  //等用的时候才会创建对象,消耗时间
//如果多线程访问,有可能会一次创建多个对象
class Singletons{
	//1.私有构造方法,其他类就不能通过new创建对象了
	private Singletons (){};
	//2、创建静态私有属性;必须是静态的可以通过类名调用
	private static Singletons st ; 
	//3、提供get方法
	public static Singletons getSt() { //获取对象
		if(st == null){
			//线程1等待 ; 线程2等待;
			st = new Singletons();
		}
		return st;
	}

饿汉式和懒汉式的区别
推荐使用饿汉式
1、饿汉式是空间(内存)转换时间, 内存可以扩大,时间不能变    
      懒汉式是时间转换空间(内存)
2、在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象。

Timer在指定时间内做什么事 
//定义一个要执行的事
class MyTimerTask extends TimerTask{
	@Override
	public void run() {
		System.out.println("起床了");
	} 
}
 		Timer t = new Timer();
//第一个参数在指定时间做什么事,第二个参数指定执行时间,第三个参数间隔多少时间执行一次
		t.schedule(new MyTimerTask(), new Date(117, 8, 14, 21, 11, 10),1000);
		//t.schedule(new MyTimerTask(), new Date(117, 8, 14, 21, 11, 10));
		while(true){
	    	Thread.sleep(1000);
	    	System.out.println(new Date());
	    }

25.04两个线程间的通信
    class Prienter{
	private int falg= 1;
	public void prient1() throws InterruptedException{
		synchronized(this){ //开启方法同步 this就是同步的一把锁
			if(falg != 1){
				this.wait();
//在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待
			}
			System.out.print("黑");
			System.out.print("马");
			System.out.print("程");
			System.out.print("序");
			System.out.print("员");
			System.out.println();
			falg = 2;
			this.notify();          //唤醒在此对象监视器上等待的单个线程
		}
	}
	public void prient2() throws InterruptedException{ 
		synchronized(this){
			if(falg != 2){
				this.wait();			
			}
			System.out.print("传");
			System.out.print("智");
			System.out.print("播");
			System.out.print("客"); 
			System.out.println();
			falg = 1;
			this.notify()
		}
	}
}
   在main方法中测试
       final Prienter p = new Prienter(); 
		new Thread(){
			public void run(){
				while(true){
					try {
						p.prient1();
					} catch (InterruptedException e) { 
						e.printStackTrace();
					}   //匿名内部类调用所在方法中的成员变量时,需用final修饰 					
				}
			}
		}.start();
		
		new Thread(){
			public void run(){
				while(true){
					try {
						p.prient2();
					} catch (InterruptedException e) { 
						e.printStackTrace();
					} 
				}
			}
		}.start();

注意事项
 1、 在同步代码块中,用哪个锁对象,就用哪个对象调用wait方法
 2、为什么wait和notify方法定义在Object类中?   
 答:因为锁对象可以是任意的,Object是所有类的父类,所以wait和notify方法需要定义在Object这个类中
         3、sleep方法和wait方法的区别?
答案:(1)sleep方法必须传入参数,参数就是时间,时间到了自动醒来
 wait方法可以传入参数也可以不传入参数,传入参数就是在在参数时间结束后等待,不传入参数就是直接等待;
   (2)  sleep方法在同步函数或者同步代码块中,不释放锁,睡着也要抱着锁睡
               wait方法在同步函数或者同步代码块中,释放锁

25.06三个线程间的通信,不是随便唤醒所有,而是指定唤醒下一个目标
class Prienter3{
	private ReentrantLock r = new ReentrantLock();//可重复使用的锁,获取lock和unlock的方法
	private Condition c1 = r.newCondition(); //newCondition() 返回用来与此 Lock 实例一起使用的 Condition 实例
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	
	private int falg= 1;
	public void prient1() throws InterruptedException{
		    r.lock(); //获取锁
			if(falg != 1){
				 c1.await();    //造成当前线程在接到信号或被中断之前一直处于等待状态
			}
			System.out.print("黑");
			System.out.print("马");
			System.out.print("程");
			System.out.print("序");
			System.out.print("员");
			System.out.println();
			falg = 2;
			c2.signal();         //唤醒一个等待线程
		    r.unlock(); //释放锁
	}
	public void prient2() throws InterruptedException{ 
		 r.lock(); //获取锁
			if(falg != 2){
				c2.await();	 
			}
			System.out.print("传");
			System.out.print("智");
			System.out.print("播");
			System.out.print("客"); 
			System.out.println();
			falg = 3;
			c3.signal();
			r.unlock(); //释放锁
	}
	public void prient3() throws InterruptedException{ 
		 r.lock(); //获取锁
			if(falg != 3){
				c3.await();
			}
			System.out.print("i");
			System.out.print("t");
			System.out.print("h");
			System.out.print("m"); 
			System.out.println();
			falg = 1;
			c1.signal();
			r.unlock(); //释放锁
	}
}
在main方法中测试代码如下
		final Prienter2 p = new Prienter2(); 
		//线程一
		new Thread(){
			public void run(){
				while(true){
					try {
						p.prient1();
					} catch (InterruptedException e) { 
						e.printStackTrace();
					}   //匿名内部类调用所在方法中的成员变量时,需用final修饰 					
				}
			}
		}.start();
		//线程二
		new Thread(){
			public void run(){
				while(true){
					try {
						p.prient2();
					} catch (InterruptedException e) { 
						e.printStackTrace();
					} 
				}
			}
		}.start();
		//线程三
		new Thread(){
			public void run(){
				while(true){
					try {
						p.prient3();
					} catch (InterruptedException e) { 
						e.printStackTrace();
					} 
				}
			}
		}.start();

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值