异常

程序在运行过程中发生的意外情况,称为异常。如:除数为0,访问下标不存在的数组元素等

异常是一种信号,用于向调用者传递信息,表示程序发生了意外情况。

程序运行时一旦出现了异常,将会导致程序立即终止,异常之后的代码将无法继续执行,所以需要对异常进行处理。

常见异常

异常 含义 发生时机
ArithmeticException 算术异常 除数为0
ArrayIndexOutOfBoundsException 数组下标越界异常 访问下标不存在的数组元素
NullPointerException 空指针异常 对null调用了方法
ClassCastException 类型转换异常 将两个不相关的类进行了强转
NumberFormatException 数字格式异常 字符串不满足数字的格式要求,不能转化为数字
ClassNotFoundException 类找不到异常 指定的类不存在
ParseException 解析异常 字符串格式不正确

不同的异常携带了不同的信息,表示发生了不同的意外情况

检查异常

  • 所有继承自Exception类的异常,称为检查异常Checked Exception
  • 该类异常是可预期的,很有可能发生的
  • 编译器要求必须显式处理该异常,即编写代码时就强制要处理运行时异常
  • 所有继承自RuntimeException类的异常,称为运行时异常
  • 该类异常并不一定可预测发生
  • 如果代码没有逻辑性错误,是不会出现运行时异常
  • 编译器不要求必须处理该异常,即编写代码时可以不处理

异常的产生和处理

产生

每种异常都是使用一个Java类来表示。

  1. 当程序发生异常时,会自动生成一个对应异常类的对象,然后将该异常对象提交给JRE,这个过程称为抛出异常throw

  2. 当JRE接收到异常对象时,会寻找能处理此异常的代码并把当前异常对象交给其处理,这个过程称为捕获异常catch

  3. 如果JRE找不到可以捕获异常的代码,则运行时系统将终止,程序将退出

    (PS:所以需要对异常进行处理,否则程序将立即终止,无法继续执行。)

处理

异常处理的两种方式:

使用try…catch

使用try…catch…finally捕获并处理异常

try{//可能出现的异常代码}
catch(异常类型 异常对象){//捕获异常//对异常进行处理的代码}
finally{//无论是否出现异常都必须要执行的代码}
  • try是必须的,catch和finally至少要有一个
  • catch可以有多个,用来捕获多个不同类型的异常
使用throws

如果一个方法可能会产生某种异常,但并不知道如何处理这种异常,此时可以声明该方法会抛出异常,表明该方法将不对这种异常进行处理,而由该方法的调用者来处理

使用throws和throw关键字:

  • throws用来声明方法中会抛出异常
  • throw用来在方法内手动抛出异常

自定义异常

自定义异常时,需要继承Exception类或其子类

一般多继承自Exception或RuntimeException

  • 如果继承Exception,则为检查异常,必须处理
  • 如果继承RuntimeException,则为运行时异常,可以不处理

方法重写的异常问题

方法重写时的异常问题

  • 若父类不抛出异常,则子类不能抛出检查异常,但可以抛出 运行时异常或在方法内部使用try…catch捕获处理异常
  • 若父类抛出异常,子类可以不抛出异常
  • 重写方法不能抛出比被重写方法范围更大的异常类型
异常的定位和解决

查找异常的出现的位置并解决:

  1. 首先查看没有Caused by,如果有则从Caused by开始找,如果没有则从头开始找
  2. 然后找到第一行自己写的代码,问题就在这里
  3. 最后根据Caused by或第一行的,所以行的异常类型和异常消息,确定产生异常的原因

File类

java.io.File表示磁盘上的文件或目录

  • 无论是文件还是目录谁都通过File类来表示(目录是一种特殊的文件)
  • 提供了对文件和目录的基本操作,如查看文件名,文件大小,新建或删除等
  • File类不能访问文件的内容,如果要访问文件内容,需要使用输入/输出流

构造方法

路径分类:

绝对路径——以根开始的路径

​ Windows:盘符,如:D:\JAVA\Filetext\a.txt

​ Linux/MacOs:/正斜杠,如:/home/soft01/JAVA/Filetext/a.txt

关于路径分隔符——Windows使用\反斜杠来表示分隔符。Linux/MacOS使用/正斜杠来表示路径分隔符

(由于在Java中\表示转义符,所以在Java中表示Windows路径时需要使用\\或使用/来表示路径分隔符)

相对路径——不是以根开始的路径,相对于某个路径的路径

例子:…/Filetext/a.txt (.表示当前目录,..表示上一级目录)

        //创建一个File对象,有4种方法//1.指定文件的全路径File file=new File("D:\\JAVA\\Filetext\\a.txt");File file=new File("D/JAVA/Filetext/a.txt");File file=new File("/home/soft01/JAVA/Filetext/a.txt");File file=new File("a.txt");//2.指定父目录的路径和文件名File file=new File("D:/JAVA/Filetext","code/a.txt");//        3.指定父目录的File对象和文件名File file=new File(new File("D:/JAVA/Filetext"),"a.txt");//4.指定URL统一资源标识符File file=new File(FileTest.class//获取类加载器.getClassLoader().//加载类路径下的文件,返回URL(Uniform Resource Locator统一资源定位符)getResource("data.properties")//转换为URL.toURI());//判断指定路径文件是否存在,存在则输出if(file.exists()){System.out.println(file);}
常用方法:
方法名 作用 返回值
getName() 获得文件名 String
getPath() 获得路径名 String
getAbsolutePath() 获得绝对路径名 String
getParent() 获得父目录 String
getParentFile() 获得父目录File对象 File
length() 获得长度 int
lastModified() 获得最后一次修改时间(毫秒) long
exists() 是否存在 boolean
canRead() 是否可读 boolean
canWrite() 是否可写 boolean
isFile() 是否是文件 boolean
isDirectory() 是否是目录 boolean
isHidden() 是否是隐藏的 boolean
createFile() 创建一个空的文件,成功true,失败flase boolean
renameTo() 重命名文件 boolean
delete() 删除文件 boolean
mkdir() 创建目录。(如果父目录不存在,会导致创建失败) boolean
mkdirs() 创建包括父目录的目录,即联级创建 boolean
list() 获取目录下的所有文件和目录的名称 String[]
listFiles() 获取目录下的所有文件和目录的对象 File[]
File.separator 代表系统目录中的间隔符(斜线),自动解决系统的兼容问题 /或者\\或者\

IO流

Input Output输入和输出流

  • 通过IO流实现文件的输入和输出功能
  • 用于对文件进行读写的操作

流stream:可以理解为一组有顺序的,有起点和终点的动态数据集合

  • 文件是数据在硬盘上的静态存储
  • 流是数据在传输时的动态形态
文件的分类
  1. 文本文件:基于字符编码的文件:.txt .java .xml

  2. 二进制文件:除了文本文件,其他所有文件都是二进制文件

    建议参考链接:https://blog.csdn.net/u012501054/article/details/91543773

流的分类
  1. 按流的方向(站在Java的角度)

    • 输入流:用于读取数据,比如从文件读取数据到程序中,由InputStream和Reader作为父类
    • 输出流:用于写出数据,比如将程序中的数据写出到文件中,有OutputStream和Writer作为父类
  2. 按流中的数据的单位

    • 字节流byte:所操作的最小数据单元为字节,有InpitStream和OutputStream作为父类

    • 字符流char:所操作的最小数据单元为字符,由Reader和Writer作为父类

      (一个英文字符占1个字节,一个汉字占2个字节(GBK)或3个字节(UTF-8))

  3. 按数据的来源

    • 节点流:直接对数据源进行操作,如操作文件
    • 包装流:对一个节点流进行操作(包装)

字节流

InputStream是字节输入流的顶层父类,常用子类有:FileInputStreamByteArrayInputStreamObjectInputStream

OutputStream是字节输出流的顶层父类,常用子类有:FileOutputStreamByteArrayOutputStreamObjectOutputStream

文件输入流输出流

FileInputStream

文件字节输入流:以字节为单位,从文件中读取数据

        FileInputStream fis=null;try{fis=new FileInputStream(new File("a.txt"));//处于阻塞状态,读取一个字节,返回int类型的字节值,如果读取到末尾,这返回-1int data=-1;while((data=fis.read())!=-1){System.out.println((char)data);}} catch (IOException e) {e.printStackTrace();}finally {//需要判断是否为null,防止出现NullPointerExceptionif(fis!=null){try {//关闭输入流,只要是打开了外部的资源(文件,数据库连接,网络连接),在使用后都需要关闭,释放资源fis.close();}catch (IOException e){e.printStackTrace();}}}

FileOutputStream

文件字节输出流:以字节为单位,将数据写出到文件中

字节数组输入输出流

流(数据)的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组

ByteArrayInputStream

字节数组输入流:从字节数组中读取数据,即将字节数组当作流输入的来源

ByteArrayOutputStream

字节数组输出流:将数据写出到内置的字节数组中,即将字节数组当作输出流的目的地

对象输入输出流

如果希望将Java对象写入到IO流中,或从IO流中读取Java对象,则要使用对象输入输出流,称为对象的序列化和反序列化

序列化和反序列化

序列化:将Java对象写入到IO流中,实现将对象保存在磁盘上或在网络中传递对象
反序列化:从IO流中读取Java对象,实现从磁盘上或网络中恢复对象
要求:
  • 对象必须实现Serializable接口,才能被序列化,转换为二进制流,通过网络进行传输

  • 通过serialVersionUID判断对象的序列化版本的一致性:

    在反序列时,会将流中的serialVersionUID与本地相应实体对象/类的serialVersionUID进行比较

    ​ 如果相同就认为版本一致,则可以进行反序列化

    ​ 如果不相同,则会出现序列化版本不一致的异常InvalidClassException

ObjectInputStream

对象输入流:用来读取对象,即反序列化

ObjectInputStreamObjectOutputSream属于包装流

  • 用于对节点流进行功能扩展/包装
  • 在创建包装流,需要传入要操作的节点流对象
  • 当关闭流时,只需要关闭包装流,节点流也会被关闭

ObjectOutputStream

对象输出流:用来写入对象,即序列化

字符流

Reader是字符输入流的顶层父类,常用子类(FileReaderBufferedReaderInputStreamReader

Writer是字符输出流的顶层父类,常用子类(FileWriterBufferedWriter/PrintWriter,``OutputStreamWriter

文件输入输出流

FileReader

文件字符输入流:以字符为单位,从文件读取数据

FileWriter

文件字符输出流:以字符为单位,将数据写出到文件中

缓冲输入输出流

缓冲输入输出流属于包装流,为字符流添加缓冲的功能

当读取或写出数据时,先从缓冲区读取,减少对磁盘IO操作,提高效率

BufferedReader

缓冲字符输入流:为字符输入流添加缓冲

BufferedWriter

缓冲字符输出流:为字符输出流添加缓冲

PrintWriter

打印流,功能更强,操作更简单

转换流

用于将字节流转换为字符流,同时可以实现编码的转换

在转换时需要指定使用的字符集,如果不指定默认使用JVM的字符集

在Java中没有提供将字符流转换为字节流的方法,不支持该操作

InputStreamReader

将字节输入流转换为字符输入流

OutputStreamWriter

将字节输出流转换为字符输出流

RandomAccessFile

随机读写流,是一个字节流,可以对文件进行随机读写

  • 随机:可以定位到文件的任意位置进行读写,通过移动指针(Pointer)来实现
  • 读写:使用该流既能读取文件,也能写入文件

用法

        /* 文件模式:* r   以只读的方式打开文本,也就意味着不能用write来操作文件(如果文件不存在,会报FileNotFoundException)* rw  读操作和写操作都是允许的(如果文件不存在,会自动创建文件)* rws 每当进行写操作,同步的刷新到磁盘,刷新内容和元数据* rwd    每当进行写操作,同步的刷新到磁盘,刷新内容*/try(RandomAccessFile raf=new RandomAccessFile("x.txt", "rw");){//获取当前指针的位置,从0开始System.out.println(raf.getFilePointer());//当前使用utf-8,一个汉字占3个字节,一个字母占1个字节raf.write("张三".getBytes());raf.write("hello".getBytes());//11System.out.println(raf.getFilePointer());System.out.println("写入成功");//将指针移动到8位置raf.seek(8);raf.write("李四".getBytes());//14System.out.println(raf.getFilePointer());raf.seek(6);byte[] buffer=new byte[2];raf.read(buffer);//读取2个字节,指针对应后移,即到8的位置System.out.println(new String(buffer));System.out.println(raf.getFilePointer());//将指针向后跳过指定的字节,只能往前,不能倒退raf.skipBytes(3);buffer=new byte[1024*1024];int num=-1;while ((num=raf.read(buffer))!=-1){//四(在写入李四时将llo给覆盖掉了,使用slipBytes跳过3个字节即跳过了李)System.out.println(new String(buffer,0,num));}//修改数据raf.seek(8);raf.write("赵".getBytes());System.out.println("修改成功");} catch (IOException e) {e.printStackTrace();}

线程

进程

进程:在操作系统中独立运行的程序,每运行一个应用程序就对应着一个进程process

多进程:在操作系统中可以同时运行多个程序

线程

线程:线程是进程内部的一个执行单元,用来执行应用程序中的一个功能thread

多线程:在一个应用程序中可以同时执行多个功能。

特性:

  • 一个进程中可以包含多个线程,且至少要有一个线程
  • 一个线程必须属于某个进程,进程是线程的容器
  • 一个进程中的多个线程共享该进程的所有资源

CPU时间片

对于单核CPU,在某个时间点只能处理一个程序

CPU分配给各个程序的时间,称为时间片,即该进程允许运行时间(时间很短)

  • 从表面上看各个程序是同时运行的,实际上CPU在同一时间只能执行一个程序
  • 只是CPU在很短的时间类,在不同程序间切换,轮流执行每个程序,执行速度很快,所以感觉上像是同时在运行

创建线程

  1. 继承Thread类

    1. 定义一个类,继承自Thread类,重写run*()方法
    2. 创建该类的实例,即创建一个线程
    3. 调用start()方法,启动线程(不能直接调用run()方法)
  2. 实现Runnable接口

    1. 定义一个类,实现Runnable接口,实现run()方法
    2. 创建实现类的实例
    3. 创建Thread类的一个实例,将上一步的实现类的实例传入
    4. 调用start()方法,启动线程
  3. 两种方式的对比

    继承Thread类

    • 线程执行的代码放在Thread类的子类run方法中
    • 无法再继承其他类

    实现Runnable接口

    • 线程执行的代码放在Runnable接口的实现类的run()方法中
    • 可以继承其他类,避免单继承的局限性
    • 适合多个相同程序代码的线程去处理同一个资源
    • 增强程序的健壮性

线程的生命周期

方法名 作用 说明
start 启动线程,线程进入就绪状态(可运行状态)
sleep 休眠线程,线程从执行状态进入阻塞状态 静态方法
yield 暂停执行线程,线程从执行状态进入就绪状态 静态方法
join 暂停执行线程,等待另一个线程执行完毕后再执行,线程从执行状态进入阻塞状态
interrupt 中断线程的休眠或等待状态

参考链接:https://www.jianshu.com/p/468c660d02da

线程安全问题

多个线程同时访问共享数据时可能会出现问题,称为线程安全问题(当多线程访问共享数据时,由于CPU的切换,导致一个线程只执行了关键代码的一部分,还没执行完此时另一个线程参与进来,导致共享数据发生异常)

**解决:**线程同步机制synchronized+锁

  • 被synchronized包围的代码块,称为同步代码块
  • 被synchronized修饰的方法,称为同步方法
  • 锁,也称为对象锁,每个对象都自带一个锁(标识),且不同对象的锁是不一样的

执行过程

  1. 当线程执行同步代码同步方法时,必须获取特定对象的锁才行
  2. 且一旦对象的锁被获取,则该对象就不再拥有说,直到线程执行完同步代码块或同步方法时,才会释放对象的锁
  3. 如果线程无法获得特定对象上的锁,则线程会进入该对象的锁池中等待,直到锁被归还对象,此时需要改锁的线程会进行竞争

线程同步的优缺点

  • 优点:解决了线程安全的问题,使代码块在某一时间只能被一个线程访问
  • 缺点:由于需要进行锁的判断,消耗资源,效率变低

解决:

两种方式:同步代码块,同步方法

 synchronized(this){if(num>0){try{Thread.sleep(10);}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"-----出售车票"+num--);}}}public synchronized void sellTicket(){if(num>0){try{Thread.sleep(10);}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"-----出售车票"+num--);}}

线程间的通信

锁池和等待池

每个对象都自带锁池和等待池

锁池
  • 当线程执行synchronized块时如果无法获取特定对象上的锁,此时会进入该对象的锁池
  • 当锁被归还给该对象时,锁池中的多个线程会竞争获取该对象的锁
  • 获取对象锁的线程将执行synchronized块,执行完毕后会释放锁
等待池:
  • 当线程获取对象的锁后,可以调用wait()方法放弃锁,此时会进入该对象的等待池
  • 当其他对象调用该对象的notify()notifyAll()方法时,等待池中的线程会被唤醒,会进入该对象的锁池
  • 当线程获取对象的锁后,将从它上次调用wait()方法的位置开始继续运行

相关方法

方法名 作用 说明
wait 使线程放弃对象锁,线程进入等待池 可以调用等待超时时间,超时后线程会自动唤醒
notify 随机唤醒等待池中的一个线程,线程进入锁池 唤醒的是特定对象的等待池中的线程
notifyAll 唤醒等待池中的所有线程

注意

  • 这三个方法都只能在synchronized块中使用,即只有获取了锁的线程才能调用
  • 等待和唤醒必须使用的是同一个对象

生产者-消费者问题

生产者-消费者问题是多线程同步的一个经典问题,即并发协作的问题。

所谓生产者-消费者问题,实际上主要是包含了两种线程:生产者线程,消费者线程

生产者线程:
  • 生产商品并放入缓冲区

  • 当缓冲区满时,生产者不可再生产商品

消费者线程:
  • 从缓冲区中取出商品
  • 当缓冲区为空时,消费者不可再取出商品

(注:生产者和消费者使用的是同一个缓冲区)

实现:

    @Overridepublic void run() {while(true){synchronized (pool){if(pool.isFull()){try{pool.wait();}catch (InterruptedException e){e.printStackTrace();}}else {pool.put();System.out.println(this.name+"生产了一个商品,现在商品数量:"+pool.getNum());pool.notifyAll();}}try{Thread.sleep(3000);}catch (InterruptedException e){e.printStackTrace();}}}
@Override
public void run() {while(true){synchronized (pool){if(pool.isEmpty()){try{pool.wait();}catch (InterruptedException e){e.printStackTrace();}}else {pool.get();System.out.println(this.name+"消费了一个商品,现在商品数量:"+pool.getNum());pool.notifyAll();}}try{Thread.sleep(2000);}catch (InterruptedException e){e.printStackTrace();}}
}

线程单例

为每个线程提供一个实例

  • 同一个线程获取的是一个实例
  • 不同线程获取的是不同的实例

Java中提供了一个ThreadLocal,直接提供了线程单例的解决方案

  • 用于管理变量,提供了线程局部变量
  • 它为变量在每个线程中都存储了一个本地的副本

实现:

public class MyThreadLocal<T> {private Map<Thread,T> map=new HashMap<>();public void set(T t){//将当前线程作为Keymap.put(Thread.currentThread(), t);}public T get(){return map.get(Thread.currentThread());}
}

JAVA潜心修炼五天——第4天相关推荐

  1. JAVA潜心修炼五天——第2天

    (PS:不好意思,鸽了一天,昨天玩去了) 面向对象的对象的三大特征 封装 Java代码规范 将类的属性封装在类中,不允许在类的外部直接访问,保护数据的安全,使内容可控 只能通过被授权的方法才能对数据进 ...

  2. 《Java工程师修炼之道》内容概览

    最近几个月由于工作和正在筹备<Java工程师修炼之道>一书的原因一直没有写新的文章.不过,忙里偷闲,自己陆续读完了<格鲁夫给经理人的第一课>.<架构真经>.< ...

  3. Java工程师修炼之道

    转自:Java工程师修炼之道 一.软件开发的核心原则 此处所说的是软件开发应该遵循的一些核心原则: 1.Don't Repeat Yourself: 这是软件开发的一个基础原则,即不要做重复性劳动.也 ...

  4. Java工程师修炼之道! | 送10本书籍

    作者:著有<Java工程师修炼之道> 出处:来自Java工程师修炼之道一书节选内容 一.软件开发的核心原则 此处所说的是软件开发应该遵循的一些核心原则: 1.Don't Repeat Yo ...

  5. Java闭关修炼64课 很适合新手学习的JAVA视频教程

    Java闭关修炼64课 很适合新手学习的JAVA视频教程 java闭关修炼第一课 什么是java(1).rar   java闭关修炼第一课 什么是java.rar   java闭关修炼第七课 基础语言 ...

  6. Java工程师修炼之路(校招总结)

    前言 在下本是跨专业渣考研的985渣硕一枚,经历研究生两年的学习积累,有幸于2019秋季招聘中拿到几个公司的研发岗offer,包括百度,阿里,腾讯,今日头条,网易,华为等. 一路走来也遇到很多困难,也 ...

  7. Java工程师修炼之路(从小白到BAT的两年学习历程)...

    ​ 微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里研发工程师,于2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer 个人擅长领域 :自学编程.技术校园招聘.软 ...

  8. Java工程师修炼之路(从小白到BAT的两年学习历程)

    前言 在下本是跨专业渣考研的985渣硕一枚,经历研究生两年的学习积累,有幸于2019秋季招聘中拿到几个公司的研发岗offer,包括百度,阿里,腾讯,今日头条,网易,华为等. 一路走来也遇到很多困难,也 ...

  9. 职场新人成功修炼五诀 迅速在职场占一席之地

    谦虚求问 孤芳自赏.恃才傲物只会让自己失去很多学习的机会,作为职场新手处在一个新环境中,不管你曾经获得多少奖学金,不管你曾经有多大的能耐,从走出校门的那一刻开始,一切都要从零开始,本着谦虚求问的态度& ...

最新文章

  1. 剑指offer_第7题_斐波那契数列
  2. MySQL杂记(更新时间——2014.05.23)
  3. java8-接口新特性
  4. MyBatis——动态SQL语句——if标签和where标签复合使用
  5. javascript --- 使用对象关联简化整体设计
  6. 字符串之字符数组种是否所有的字符都只出现过一次
  7. linux查询设备文件信息失败怎么办,Linux下使用blkid命令查询设备及文件系统信息的方法...
  8. python学习第三十二节(进程间通信、进程池、协程)
  9. 网络爬虫中URLConnection的使用[以科学网为例]
  10. c语言if case语句怎么用,这样的case和if一起运用有问题吗?
  11. l0phtcrack 7(爆破管理员密码)使用教程
  12. 小众绿软|媒体:Soprano Audio Player v1.60
  13. mysql语句中limt_mysql sql语句中的limit用法
  14. 程序员必备《新手手册》
  15. oracle11g 企业管理器无法登陆
  16. 《别看了,你学不会的》——Redis原理与实战(一)
  17. jbpm 历史查询笔记
  18. windows10内置Linux子系统挂载文件
  19. 局域网git服务器搭建(基于win7 + bonobo git server)
  20. 个人理财太平亿康学生怎样才能做好投资理财工作

热门文章

  1. 热插拔48块硬盘服务器,中云网眼WEM-SAN100/48B48盘位网络存储设备IP-SAN
  2. Qt添加菜单栏和工具栏
  3. java实现交叉报表_交叉填报表的制作
  4. 《南瓜书pumpkin-book》项目链接
  5. 微信小程序实现换肤功能
  6. linux设置用户的执行权限,Linux下ACL权限控制以及用sudo设置用户对命令的执行权限...
  7. PHP文字间距怎么调,在html中怎么设置文字间距
  8. ElasticSearch重启脚本
  9. 企鹅号绑定微信公众号 问题 微信授权失败!输入的微信号和微信公众平台设置的不一致
  10. python输入输出拓展: 制作简单的exe程序(温度转换器)