Java异常

异常处理机制主要回答了三个问题

What:异常类型回答了什么被抛出

Where:异常堆栈跟踪回答了在哪抛出

Why:异常信息回答了为什么被抛出

Java的异常体系

Error和Exception的区别

从概念角度解析Java的异常处理机制:

1.Error:程序无法处理的系统处理,编辑器不做检查(如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等)

2.Exception:程序可以处理的异常,捕获后可能恢复

  • RuntimeException:不可预知的,程序应当自行避免
  • 非RuntimeException:可预知的,从编译器校验的异常

从责任角度看:

1.Error属于JVM需要负担的责任

2.RuntimeException是程序应该负担的责任

3.Checked Exception可检查异常是Java编译器应该负担的责任(编译时就应该捕获或者抛出的异常)

常见Error以及Exception

RuntimeException

1.NullPointerException - 空指针引用异常

2.ClassCastException - 类型强制转换异常

3.IllegalArgumentException - 传递非法参数异常

4.IndexOutOfBoundsException - 下标越界异常

5.NumberFormatException - 数字格式异常

非RuntimeException

1.ClassNotFoundException - 找不到指定class的异常

2.IOException - IO操作异常

3.SQLException - SQL异常

Error

1.NoClassDefFoundError - 找不到class定义的异常(原因:类依赖的class或者jar不存在;类文件存在,但是存在不同的域中;大小写问题,javac编译的时候是无视大小写的,很有可能编译出来的class文件就与想要的不一样)

2.StackOverflowError - 深递归导致栈被耗尽而抛出的异常

3.OutOfMemoryError - 内存溢出异常

Java的异常处理机制

  • 抛出异常:创建异常对象,交由运行时系统处理
  • 捕获异常:寻找合适的异常处理器处理异常,否则终止运行
private static int doSomething(){
try {
int i = 10/0;
System.out.println("i= " + i);
} catch (ArithmeticException e) {
System.out.println("ArithmeticException: " + e);
return 0;
} catch (Exception e) {
System.out.println("Exception: " + e);
return 1;
} finally {
System.out.println("Finally");
return 2;
}
} public static void main(String[] args) {
System.out.println("执行后的值为:" + doSomething());
System.out.println("执行结束");
}

执行结果

ArithmeticException: java.lang.ArithmeticException: / by zero
Finally
执行后的值为:2
执行结束

注意

尽量细化异常,别抛出异常的父类,也是为了方便定位问题

避免在finally中写return语句,其他return不会执行

Java异常的处理原则

  • 具体明确:抛出的异常应能通过异常类名和message准确说明异常的类型和产生异常的原因
  • 提早抛出:应尽可能早的发现并抛出异常,便于精确定位问题
  • 延迟捕获:异常的捕获和处理应尽可能延迟,让掌握更多信息的作用域来处理异常

高效主流的异常处理框架

在用户看来,应用系统发生的所有异常都是应用系统内部的异常

  • 设计一个通用的继承自RuntimeException的异常来统一处理
  • 其余异常都统一转译为上述异常AppException
  • 在catch之后,抛出上述异常的子类,并提供足以定位的信息
  • 由前端接收AppException做统一处理

看看try-catch的性能问题

private static void testException(String[] array){
try {
System.out.println(array[0]);
}catch (NullPointerException e){
System.out.println("array cannot be null");
}
} private static void testIf(String[] array){
if (array != null){
System.out.println(array[0]);
} else {
System.out.println("array cannot be null");
}
} public static void main(String[] args) {
long start = System.nanoTime();
testException(null);
System.out.println("testException cost " + (System.nanoTime() - start));
} // 运行结果
array cannot be null
testException cost 322997 public static void main(String[] args) {
long start = System.nanoTime();
testIf(null);
System.out.println("testIf cost " + (System.nanoTime() - start));
} // 运行结果
array cannot be null
testIf cost 238152

Java异常处理消耗性能的地方

  • try-catch块影响JVM的优化
  • 异常对象实例需要保持栈快照等信息,开销较大

Java集合框架

集合之List和Set

集合之Map

HashMap、HashTable、ConcurrentHashMap

HashMap

Java8以前(不包含):数组+链表

数组长度默认是16,数组中每个元素存储着链表的头节点,通过hash(key.hashCode())%len(哈希函数取模操作)获得添加元素所要存放的数组的位置。

有个问题,如果哈希函数计算的值一直是同一个,会导致bucket(桶)过长,链表查询,就需要从头部开始遍历,最坏的情况下,性能会从O(1)变成O(n)。

Java8以后:数组+链表+红黑树

Java8之后做了调整,会通过一个常量TREEIFY_THRESHOLD控制是否将链接转成红黑树,性能从O(n)提高到O(logn)

HashMap使用懒加载,首次使用才初始化

HashMap:put方法的逻辑

1、如果HashMap未被初始化过,则初始化

2、对Key求Hash值,然后计算下标

3、如果没有碰撞,直接放入桶中

4、如果碰撞了,以链表的方式链接到后面

5、如果链表长度超过阈值,就把链表转成红黑树

6、如果链表长度低于6,就把红黑树转回链表

7、如果节点已经存在就替换旧值

8、如果桶满了(容量16*加载因子0.75),就需要resize(扩容2倍后重排)

HashMap:如何有效减少碰撞

  • 扰动函数:促使元素位置分布均匀,减少碰撞几率(两个不相等的对象返回不同的哈希值)
  • 使用final对象,并采用合适的equals()和hashCode()方法

HashMap:从获取hash到散列的过程

HashMap:扩容问题

  • 多线程环境下,调整大小会存在条件竞争,容易造成死锁
  • rehashing是一个比较耗时的过程

Hashtable

注意点

  • 多线程安全,锁住整个HashTable,力度大
  • 底层:数组+链表
  • 无论key还是value都不能为null

如何优化Hashtable

通过锁细粒度化,将整锁拆解成多个锁进行优化,所以出现了ConcurrentHashMap

ConcurrentHashMap

早期的ConcurrentHashMap:通过分段锁Segment来实现

数组+链表

当前的ConcurrentHashMap:CAS+synchronized使锁更细化

数组+链表+红黑树

ConcurrentHashMap:put方法的逻辑

1.判断Node[]数组是否初始化,没有则进行初始化操作

2.通过hash定位数组的索引坐标,是否有Node节点,如果没有则使用CAS进行添加(链表的头节点),添加失败则进入下次循环

3.检查到内部正在扩容,就帮助它一块扩容。

4.如果f!=null,则使用synchronized锁住f元素(链表/红黑二叉树的头元素)

4.1如果是Node(链表结构)则执行链表的添加操作

4.2如果是TreeNode(树型结构)则执行树添加操作

5.判断链表长度已经达到临界值8(默认值),当节点数超过这个值就需要把链表转换为树结构

ConcurrentHashMap总结

  • 比起Segment,锁拆得更细
  • 首先使用无锁操作CAS插入头节点,失败则循环重试
  • 若头节点已存在,则尝试获取头节点的同步锁,再进行操作

HashMap、Hashtable、ConcurrentHashMap区别

  • HashMap线程不安全,数组+链表+红黑树
  • Hashtable线程安全,锁住整个对象,数组+链表
  • ConcurrentHashMap线程安全,CAS+同步锁,数组+链表+红黑树
  • HashMap的key、value均可为null,而其他的两个类不支持

J.U.C知识点梳理

java.util.concurrent:提供了并发编程的解决方案

  • CAS是java.util.concurrent.atomic包的基础
  • AQS是java.util.concurrent.locks包以及一些常用类比如Semophore,ReentrantLock等类的基础

J.U.C包的分类

  • 线程执行器executor
  • 锁locks
  • 原子变量类atomic
  • 并发工具类tools
  • 并发集合collections

并发工具类

  • 闭锁CountDownLatch
  • 栅栏CyclicBarrier
  • 信号量Semaphore
  • 交换器Exchanger

CountDownLatch

让主线程等待一组事件发生后继续执行,事件指的是CountDownLatch里的countDown()方法

CyclicBarrier

阻塞当前线程,等待其他线程

  • 等待其它线程,且会阻塞自己当前线程,所有线程必须同时到达栅栏位置后,才能继续执行
  • 所有线程到达栅栏处,可以触发执行另外一个预先设置的线程

Semaphore

控制某个资源可被同时访问的线程个数

Exchanger

主要用于线程间的数据交换,它提供一个同步点,一个线程到达同步点就会被阻塞,直到另外一个线程进入到同步点为止,两个线程到达同步点后,相互交换数据。

BlockingQueue

提供可阻塞的入队和出队操作,如果队列满了,入队操作将阻塞,直到有空间可用,如果队列空了,出队操作将阻塞,直到有元素可用。

主要用于生产者-消费者模式,在多线程场景时生产者线程在队列尾部添加元素,而消费者线程则在队列头部消费元素,通过这种方式能够达到将任务的生产和消费进行隔离的目的

主要有以下七个队列实现,都是线程安全的

1、ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(有界指的是容量大小有限制,先进先出)

2、LinkedBlockingQueue:一个由链表结构组成的有界/无界阻塞队列(先进先出)

3、PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列

4、DealyQueue:一个使用优先级队列实现的无界阻塞队列

5、SynchronousQueue:一个不存储元素的阻塞队列

6、LinkedTransferQueue:一个由链表结构组成的无界阻塞队列

7、LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列

Java的IO机制

BIO

Block-IO:其实是传统的java.net,java.io包下的接口或者类,比如java.net下的socket,servletSocket和http,因为网络通信都是IO行为,所以可以说是BIO的范畴,传统的IO基于字节流和字符流进行操作,比如InputStream和OutputStream,Reader和Writer。

BIO是基于流模型实现的,这意味着其交互方式是同步阻塞的,在读取输入流或者写入输出流时,在读写操作完成之前,线程会一直阻塞,它们之间的调用是可靠的线性顺序,程序发送请求给内核,然后由内核进行通讯,在内核准备好数据之前,线程是被挂起的,所以在两个阶段,程序都处于挂起状态,类比成ClientServlet模式,其实现模式为一个连接一个线程,即客户端有连接请求时,则服务端启动一个线程处理,待操作系统返回结果,如果这个连接不做任何事情,会造成不必要的线程开销,当然,我们可以通过线程池机制来改善。

特点:在IO执行的阶段,都被阻塞住了

好处:代码比较简单

缺点:IO扩展性和效率存在瓶颈

NIO(Java4)

NonBlock-IO:构建多路复用的、同步非阻塞的IO操作,提供了更接近操作系统底层的高性能数据操作方式

特点:程序要不断去询问内核是否准备好

NIO核心

  • Channels
  • Buffers
  • Selectors

AIO

Asynchronous IO:基于事件和回调机制

AIO如何进一步加工处理结果

  • 基于回调:实现CompletionHandler接口,调用时触发回调函数
  • 返回Future:通过isDone()查看是否准备好,通过get()等待返回数据

区别

AIO连server都免了,所以是0:N

BIO适用于连接数少且固定的架构,这种方式对服务器的资源要求较高,1.4之前的唯一选择

NIO适用于连接数多且短的架构,比如聊天服务器,编程复制

AIO适用于连接数多且长的架构,比如相册服务器,可以充分调用OS来参与并发操作,编程复制,1.7才出现的

Java常用类库与技巧的更多相关文章

  1. Java 常用类库与技巧【笔记】

    Java 常用类库与技巧[笔记] Java异常体系 Java异常相关知识 Java在其创立的时候就设置了比较有效的处理机制,其异常处理机制主要回答了三个问题:what,where,why what表示 ...

  2. Google的Java常用类库 Guava资料

    java的人应该都知道Apache commons的java常用类库吧,这个Guava和commons一样,封装出一套比jdk本身提供的常用类库强大.既然有了这个这么强大的类库,我们就没必要重复造轮子 ...

  3. JAVA(三)JAVA常用类库/JAVA IO

    成鹏致远 | lcw.cnblog.com |2014-02-01 JAVA常用类库 1.StringBuffer StringBuffer是使用缓冲区的,本身也是操作字符串的,但是与String类不 ...

  4. JAVA常用类库简介(转)

    Java编程语言中为方便学习者学习,编制了许多类,这些类已经经过测试,都是我们编程的基础.如果不利用这些已存在的类,我们的编程工作将变得异常复杂并且效率低下.所以我们应尽可能多的掌握Java基本类库的 ...

  5. Java常用类库API之MD5简单使用

    常用类库--MD5简单使用 MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash v ...

  6. Java常用类库2

    1.java.util.Date类 package LESSON9; import java.util.Date; public class demo1 { public static void ma ...

  7. 菜鸡的Java笔记 - java 常用类库

    CommonClassLibrary 常用类库        定时调度            定时调度指的是每到一个时刻,都会自动的产生某些特定的操作形式                    con ...

  8. 谈谈Java常用类库中的设计模式 - Part Ⅰ

    背景 最近一口气看完了Joshua Bloch大神的Effective Java(下文简称EJ).书中以tips的形式罗列了Java开发中的最佳实践,每个tip都将其意图和要点压缩在了标题里,这种做法 ...

  9. Java常用类库(一) : Object 和日期类的简单使用

    顶哥说:Java是世界的,但项目不是! Java有非常多的类库,而我们不会也不用都去学习,毕竟你也仅仅掌握了你手机20%的功能却足够你使用,不是吗? 今天介绍以下类: l  Object l  Dat ...

随机推荐

  1. 华盛顿邮报专访:SPC能否再掀起币圈新浪潮?

    近日,美国知名媒体华盛顿邮报对话NGK灵石团队技术副总裁Daphne Patel女士,对话主题为"SPC能否再掀起币圈新浪潮".此次对话以问答的形式展开,将SPC的最新情况呈现在你 ...

  2. 微信附近的人,用redis也能实现?(GEO)

    相信微信附近的人的功能大家都应该用过 我可以很随意的通过我自己的定位能看到我附近的人,并且能看到那个人距离我的距离,大家有没有思考过这个是怎么实现的? 作为一个程序猿任何问题应该都有一个思考的过程,而 ...

  3. CMD 中运行 xx 命令提示 不是内部或外部命令,也不是可运行的程序或批处理文件的问题

    出现这个问题的原因一般有2个 这个命令依赖某个软件,而你又没有安装 这里你只需要去下载安装好对应的软件,基本上就可以解决上面的问题了. 软件安装好了,但是需要配置环境变量 第二个原因就按照下图,去设置 ...

  4. ElasticSearch 文档及操作

    公号:码农充电站pro 主页:https://codeshellme.github.io 本节介绍 ES 文档,索引及其基本操作. 1,ES 中的文档 在 ES 中,文档(Document)是可搜索数 ...

  5. TERSUS无代码开发(笔记05)-简单实例电脑端页面设计

    案例笔记电脑端页面设计   1.新建项目(请假管理qjgl)   2.开发软件界面介绍(常用的功能按键)      3.目录中显示元件对象      4.对元件对象的操作主要方式是双击(双击哪个元件, ...

  6. 可以设置过期时间的Java缓存Map

    前言 最近项目需求需要一个类似于redis可以设置过期时间的K,V存储方式.项目前期暂时不引进redis,暂时用java内存代替. 解决方案 1. ExpiringMap 功能简介 : 1.可设置Ma ...

  7. 基于西门子S7-1500的大型焊接机全套程序,使用博图V14打开(带全部注释)

    程序说明:本套程序是在从事自动化行业时候的做的项目的程序,经过在设备上运行测试,其中包含20多个轴的伺服控制以及模拟量,数字量IO的控制,包括扫描枪的读取,属于大型程序,总步数有好几万步. 本程序注释 ...

  8. JAVA基础(二)—— 常用的类与方法

    JAVA基础(二)-- 常用的类与方法 1 Math类 abs ceil floor 绝对值 大于等于该浮点数的最小整数 小于等于该浮点数的最大整数 max min round 两参数中较大的 两参数 ...

  9. 绿色城市之地下综合管廊3D可视化平台

    前言 现阶段,我国绿色城市建设发展正在热火朝天的进行,面对迅速城镇化建设导致的城市病,需要不断寻求足以丰富城市的资源,以此实现城市绿色化智能化发展,比如改造地下管廊.路灯等城市基础设施. 地下综合管廊 ...

  10. PAT-1099(Build A Binary Search Tree)Java实现+二叉排序树的中序遍历和层次遍历

    Build A Binary Search Tree PAT-1099 本题有意思的一个点就是:题目已经给出了一颗排序二叉树的结构,需要根据这个结构和中序遍历序列重构一棵二叉排序树. 解法:可以根据中 ...