Java的基础入门(2)

简介: Java的基础入门(2)
  • Java异常体系

image.png

Throwable是Java语言中所有错误或异常的超类。下一层分为Error 和Exception。

Error:

是指java运行时系统的内部错误和资源耗尽错误。应用程序不会抛出该类对象。如果出现了这样的错误,除了告知用户,剩下的就是尽力使程序安全的终止。

Exception包含:RuntimeException、CheckedException;

编程错误可以分成三类:语法错误、逻辑错误和运行错误。

语法错误(也称编译错误)是在编译过程中出现的错误,由编译器检查发现语法错误。

逻辑错误指程序的执行结果与预期不符,可以通过调试定位并发现错误的原因。

运行错误是引起程序非正常终端的错误,需要通过异常处理的方式处理运行错误。

RuntimeException:运行时异常,程序应该从逻辑角度尽可能避免这类异常的发生。

如NullPointerException、ClassCastException ;

CheckedException:受检异常,程序使用trycatch进行捕捉处理;

如IOException、SQLException、NotFoundException;


 数据结构


image.png


  • ArrayList和LinkedList


ArrayList:

底层基于数组实现,支持对元素进行快速随机访问,适合随机查找和遍历,不适合插入和删除。(提一句实际上)

默认初始大小为10,当数组容量不够时,会触发扩容机制(扩大到当前的1.5倍),需要将原来数组的数据复制到新的数组中;当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。

LinkedList:

底层基于双向链表实现,适合数据的动态插入和删除;
内部提供了List接口中没有定义的方法,用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。(比如jdk官方推荐使用基于linkedList的Deque进行堆栈操作)

ArrayList与LinkedList区别:

都是线程不安全的,ArrayList适用于查找的场景,LinkedList适用于增加、删除多的场景。

实现线程安全:

可以使用原生的Vector,或者是Collections.synchronizedList(List list)函数返回一个线程安全的ArrayList集合。

建议使用concurrent并发包下的CopyOnWriteArrayList的。

Vector:底层通过synchronize修饰保证线程安全,效率较差。

CopyOnWriteArrayList:写时加锁,使用了一种叫写时复制的方法;读操作是可以不用加锁的。


  • List遍历快速和安全失败


普通for循环遍历List删除指定元素





for(int i=0; i < list.size(); i++){   if(list.get(i) == 5)        list.remove(i);}


迭代遍历,用list.remove(i)方法删除元素








Iterator<Integer> it = list.iterator();while(it.hasNext()){    Integer value = it.next();    if(value == 5){        list.remove(value);    }}


foreach遍历List删除元素




for(Integer i:list){    if(i==3) list.remove(i);}


fail—fast:快速失败

当异常产生时,直接抛出异常,程序终止。

fail-fast主要是体现在当我们在遍历集合元素的时候,经常会使用迭代器,但在迭代器遍历元素的过程中,如果集合的结构(modCount)被改变的话,就会抛出异常ConcurrentModificationException,防止继续遍历。这就是所谓的快速失败机制。

fail—safe:安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。由于在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

缺点:基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。


  • 详细介绍HashMap


角度:数据结构+扩容情况+put查找的详细过程+哈希函数+容量为什么始终都是2^N,JDK1.7与1.8的区别。

参考:https://www.jianshu.com/p/9fe4cb316c05

数据结构:

HashMap在底层数据结构上采用了数组+链表+红黑树,通过散列映射来存储键值对数据。

扩容情况:

默认的负载因子是0.75,如果数组中已经存储的元素个数大于数组长度的75%,将会引发扩容操作。

【1】创建一个长度为原来数组长度两倍的新数组

【2】1.7采用Entry的重新hash运算,1.8采用高于运算。

put操作步骤: 


image.png


1、判断数组是否为空,为空进行初始化;2、不为空,则计算 key的hash值,通过(n - 1) & hash计算应当存放在数组中的下标 index;3、查看table[index] 是否存在数据,没有数据就构造一个Node节点存放在 table[index] 中;4、存在数据,说明发生了hash冲突(存在二个节点key的hash值一样), 继续判断key是否相等,相等,用新的value替换原数据;5、若不相等,判断当前节点类型是不是树型节点,如果是树型节点,创造树型节点插入红黑树中;6、若不是红黑树,创建普通Node加入链表中;判断链表长度是否大于8,大于则将链表转换为红黑树;7、插入完成之后判断当前节点数是否大于阈值,若大于,则扩容为原数组的二倍;哈希函数:通过hash函数(优质因子31循环累加)先拿到key的hashcode,是一个32位的值,然后让hashcode的高16位和低16位进行异或操作。该函数也称为扰动函数,做到尽可能降低hash碰撞,通过尾插法进行插入。容量为什么始终都是2^N:先做对数组的⻓度取模运算,得到的余数才能⽤来要存放的位置也就是对应的数组下标。这个数组下标的计算⽅法是“ (n - 1) & hash ”。(n代表数组⻓度)。方便数组的扩容和增删改时的取模。JDK1.7与1.8的区别:JDK1.7 HashMap:底层是 数组和链表 结合在⼀起使⽤也就是链表散列。如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。扩容翻转时顺序不一致使用头插法会产生死循环,导致cpu100%JDK1.8 HashMap:底层数据结构上采用了数组+链表+红黑树;当链表⻓度⼤于阈值(默认为 8-泊松分布),数组的⻓度大于 64时,链表将转化为红⿊树,以减少搜索时间。(解决了tomcat臭名昭著的url参数dos攻击问题)

  • ConcurrentHashMap


可以通过ConcurrentHashMapHashtable来实现线程安全;Hashtable 是原始API类,通过synchronize同步修饰,效率低下;ConcurrentHashMap通过分段锁实现,效率较比Hashtable要好。ConcurrentHashMap的底层实现:JDK1.7的ConcurrentHashMap底层采⽤ 分段的数组+链表 实现;采用 分段锁(Sagment) 对整个桶数组进⾏了分割分段(Segment默认16个),每⼀把锁只锁容器其中⼀部分数据,多线程访问容器⾥不同数据段的数据,就不会存在锁竞争,提⾼并发访问率。

image.png

JDK1.8的 ConcurrentHashMap采⽤的数据结构跟HashMap1.8的结构⼀样,数组+链表/红⿊树;摒弃了Segment的概念,⽽是直接⽤ Node 数组+链表+红⿊树的数据结构来实现,通过并发控制synchronizedCAS来操作保证线程的安全。

  • 序列化和反序列化


序列化的意思就是将对象的状态转化成字节流,以后可以通过这些值再生成相同状态的对象。对象序列化是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输。反序列化就是根据这些保存的信息重建对象的过程。

序列化:将java对象转化为字节序列的过程。反序列化:将字节序列转化为java对象的过程。 优点:

  1. 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)Redis的RDB
  2. 利用序列化实现远程通信,即在网络上传送对象的字节序列。Google的protoBuf。

反序列化失败的场景:序列化ID:serialVersionUID不一致的时候,导致反序列化失败。

  • String


String使用数组存储内容,数组使用final修饰,因此String定义的字符串的值也是不可变的。

StringBuffer对方法加了同步锁,线程安全,效率略低于StringBuilder。


 设计模式与原则


  • 单例模式


某个类只能生成一个实例,该实例全局访问,例如Spring容器里一级缓存里的单例池。优点:唯一访问:如生成唯一序列化的场景、或者spring默认的bean类型。提高性能:频繁实例化创建销毁或者耗时耗资源的场景,如连接池、线程池。缺点:不适合有状态且需变更的;实现方式:饿汉式:线程安全速度快;懒汉式:双重检测锁,第一次减少锁的开销、第二次防止重复、volatile防止重排序导致实例化未完成;静态内部类:线程安全利用率高;枚举:effictiveJAVA推荐,反射也无法破坏;

  • 工厂模式


定义一个用于创建产品的接口,由子类决定生产何种产品。优点:解耦:提供参数即可获取产品,通过配置文件可以不修改代码增加具体产品。缺点:每增加一个产品就得新增一个产品类。

  • 抽象工厂模式


提供一个接口,用于创建相关或者依赖对象的家族,并由此进行约束。

优点:可以在类的内部对产品族进行约束。缺点:假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。

相关文章
|
7天前
|
Java 应用服务中间件 数据库连接
Java 新手入门:Spring Boot 启动揭秘,小白也能秒懂的超详细指南
Java 新手入门:Spring Boot 启动揭秘,小白也能秒懂的超详细指南
24 2
|
7天前
|
Java 测试技术 API
Java 新手入门:Java单元测试利器,Mock详解
Java 新手入门:Java单元测试利器,Mock详解
30 1
|
3天前
|
设计模式 前端开发 Java
【前端学java】SpringBootWeb极速入门-分层解耦(03)
【8月更文挑战第13天】SpringBootWeb极速入门-分层解耦(03)
8 2
【前端学java】SpringBootWeb极速入门-分层解耦(03)
|
4天前
|
开发框架 前端开发 Java
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
【8月更文挑战第12天】SpringBootWeb极速入门-实现一个简单的web页面01
15 3
【前端学java】SpringBootWeb极速入门-实现一个简单的web页面01
|
4天前
|
JSON 前端开发 Java
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
【8月更文挑战第12天】SpringBootWeb极速入门-请求参数解析(02)
10 1
【前端学java】SpringBootWeb极速入门-请求参数解析(02)
|
12天前
|
机器学习/深度学习 人工智能 算法
AI入门必读:Java实现常见AI算法及实际应用,有两下子!
本文全面介绍了人工智能(AI)的基础知识、操作教程、算法实现及其在实际项目中的应用。首先,从AI的概念出发,解释了AI如何使机器具备学习、思考、决策和交流的能力,并列举了日常生活中的常见应用场景,如手机助手、推荐系统、自动驾驶等。接着,详细介绍了AI在提高效率、增强用户体验、促进技术创新和解决复杂问题等方面的显著作用,同时展望了AI的未来发展趋势,包括自我学习能力的提升、人机协作的增强、伦理法规的完善以及行业垂直化应用的拓展等...
96 3
AI入门必读:Java实现常见AI算法及实际应用,有两下子!
|
1天前
|
前端开发 IDE Java
"揭秘前端转Java的秘径:SpringBoot Web极速入门,掌握分层解耦艺术,让你的后端代码飞起来,你敢来挑战吗?"
【8月更文挑战第19天】面向前端开发者介绍Spring Boot后端开发,通过简化Spring应用搭建,快速实现Web应用。本文以创建“Hello World”应用为例,展示项目基本结构与运行方式。进而深入探讨三层架构(Controller、Service、DAO)下的分层解耦概念,通过员工信息管理示例,演示各层如何协作及依赖注入的使用,以此提升代码灵活性与可维护性。
|
7天前
|
存储 Java 编译器
|
6天前
|
Java 测试技术 Spring
Java 新手入门:依赖注入的 N 种姿势,总有一款适合你!
Java 新手入门:依赖注入的 N 种姿势,总有一款适合你!
15 2
|
7天前
|
安全 Java 开发者
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
Java 新手入门:Spring 两大利器IoC 和 AOP,小白也能轻松理解!
14 1