Java基础+集合+多线程+JVM(一)

目录
    1 Java基础
    2 Java集合

    3 Java多线程
    4 JVM
    5 常见问题汇总

参考资料
    ·《Java编程思想》
    ·《Java Web 技术内幕》
    ·《Java 并发编程实战》

1 Java基础

1.1 对象

1.1.1 创建对象

1.1.1.1 引用

    用引用(reference)操纵对象。犹如遥控器和电视机的关系。

1.1.1.2 new关键字

    创建对象,并存储在堆heap中。

1.1.1.3 特例:基本类型

(1)基本类型与包装类

        · 装箱过程是通过调用valueOf方法(Integer Integer.valueOf(int))实现的,拆箱为xxxValue()(int Integer.intValue())。

        · Integer装箱时,在[-128, 127]之间不创建新对象,否则创建新对象。

        · Integer、Short、Byte、Character、Long类似。Double、Float装箱类似,每次创建新对象。

        · 当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。

demo:

public class Main {

    public static void main(String[] args) {

        Integer a = 1;

        Integer b = 2;

        Integer c = 3;

        Integer d = 3;

        Integer e = 321;

        Integer f = 321;

        Long g = 3L;

        Long h = 2L;

        System.out.println(c==d);

        System.out.println(e==f);

        System.out.println(c==(a+b));

        System.out.println(c.equals(a+b));

        System.out.println(g==(a+b));

        System.out.println(g.equals(a+b));

        System.out.println(g.equals(a+h));

    }

}

true   false    true    true   true    false     true

  第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

(2)存储位置:堆栈

(3)8中基本类型的字节大小。

Java基础+集合+多线程+JVM(一)_第1张图片
图1-1 基本类型字节大小

1.1.2 不需要手动销毁对象

    · 作用域:花括号决定

    · GC机制决定不需要手动销毁对象。

1.1.3 类

(1)成员变量(字段)、成员函数(方法)。其中对于基本类型的成员变量有默认值。

(2)Java程序:

        · 无“向前引用”问题

        · 通配符 *,import java.lang.*

        · static 修饰时,属于类属性,与实例无关。

        · java.lang 在程序中被自动导入。

        · 编码风格:命名规则采用“驼峰命名法”。类名的首字母要大写;如果类名由几个单词构成,则并在一起,每个内部单词的首字母采用大写;方法、字段以及对象引用名称等与类相似,只是这些标识符的第一个字母采用小写

1.1.4 对象的构成

Java的对象头和对象组成详解_Clarence-CSDN博客

1.2 操作符

1.2.1 关系操作符

    “==”与equals()区别:

    · ==对基本类型来说比较数值,对引用类型来说比较的是内存地址。

    · equals()在没有被覆盖时,等价于“==”。当被覆盖时,一般比较的是对象的内容。

    · String对象中的equals()方法被覆盖过了,比较的是内容。

1.2.2 按位操作符

    非(~)不能用于布尔类型,可用!(逻辑操作符)。

1.2.3 移位符

    · >>> “无符号”右移

    · char、byte、short 移位前需要转int类型

1.2.4 字符串操作

    String中的“+”操作符底层原理:

        new StringBuffer(str).append(c).toString();

1.2.5 类型转换(cast)

· 基本数据类型

    · 窄化转换(危险)

    · 扩展转换(安全)

1.3 控制执行

    foreach 用于数组和容器

            · 任何一个返回数组的方法 for(String str : getString()){ }

            · 任何Iterable对象

1.4 初始化与清理

1.4.1 构造器

    如果已经定义了一个构造器(有/无参),编译器不会自动创建默认构造器,此时调用默认无参构造器报错

1.4.2 方法重载

    同一个类中,方法名相同,参数的类型、个数、顺序不同。

1.4.3 this

    · 获得当前对象的引用

    · 只能在方法内部使用

    · 构造器之间调用时,this 只能最多有一个,且只能放第一行

    · 不能再staitc修饰的方法中调用

1.4.4 终结处理与垃圾回收

1.4.4.1 finalize()方法

(1)finalize()方法是Object类中提供的一个方法,在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法。其在Object中定义如下:protected void finalize() throws Throwable { }

(2)finalize()调用的时机

        与C++的析构函数(对象在清除之前析构函数会被调用)不同,在Java中,由于GC的自动回收机制,因而并不能保证finalize方法会被及时地执行(垃圾对象的回收时机具有不确定性),也不能保证它们会被执行(程序由始至终都未触发垃圾回收)。

public class Finalizer {

    @Override

    protected void finalize() throws Throwable {

        System.out.println("Finalizer-->finalize()");

    }

    public static void main(String[] args) {

        Finalizer f = new Finalizer();

        f = null;

    }

}

//无输出

public class Finalizer {

    @Override

    protected void finalize() throws Throwable {

        System.out.println("Finalizer-->finalize()");

    }

    public static void main(String[] args) {

        Finalizer f = new Finalizer();

        f = null;

        System.gc();//手动请求gc

    }

}

//输出 Finalizer-->finalize()

(3) 什么时候应该使用它

    finalize()方法中一般用于释放非Java 资源(如打开的文件资源、数据库连接等),或是调用非Java方法(native方法)时分配的内存(比如C语言的malloc()系列函数)。

(4)为什么避免使用

    首先,由于finalize()方法的调用时机具有不确定性,从一个对象变得不可到达开始,到finalize()方法被执行,所花费的时间这段时间是任意长的。我们并不能依赖finalize()方法能及时的回收占用的资源,可能出现的情况是在我们耗尽资源之前,gc却仍未触发,因而通常的做法是提供显示的close()方法供客户端手动调用。

    另外,重写finalize()方法意味着延长了回收对象时需要进行更多的操作,从而延长了对象回收的时间。

1.4.4.2 显示GC( system.gc() )也不一定执行

1.4.4.3 GC算法

(1) 引用计数

            描述工作方式,未实现。

(2)复制

(3)标记清除

(4)标记整理

(5)分代收集机制

        · 新生代(使用复制算法)

        · 老年代(使用标记清除和标记整理算法)

1.4.5 初始化顺序

(1)字段在任何方法(包括构造器)之前得到初始化。

(2)static

        · 只有在必要时刻才会进行(访问该类,因为staic修饰属于类属性)

        · 代码块加载顺序

            静态代码块(类加载的时候加载一次)---> 普通代码块(在实例化时加载,可加载多次)---> 构造函数(先调用父类构造函数)

1.5 访问控制

(1)每个.java文件只能包含一个public类

(2)访问权限修饰词

            · 包访问权限(默认)

            · public 对每个人可见

            · private 仅该类可见(子类可以通过set、get方法调用private属性),不可修饰类

            · protected 继承的子类可见  不可修饰类

            · 将构造函数用private修饰,可以做成单例模式。

// 利用private修饰构造器,完成单例模式

Class Demo {

    private Demo(){};

    private static Demo p = new Demo();

    public static Demo access(){

        return p;

    }

}

1.6 复用类

1.6.1 组合类

    在类的字段中包含对其他类的引用。

1.6.2 继承

    所有类都隐式继承Object类。

    Object类的方法:

Java基础+集合+多线程+JVM(一)_第2张图片
图1-2 Object类

    · 关于clone方法:Java 浅拷贝和深拷贝 - 、java中clone方法的理解(深拷贝、浅拷贝)_xinghuo0007的博客-CSDN博客

(1)语法

        · 父类中,成员变量用private,成员方法用public

        · 使用extends关键字

        · 使用super.function()在覆盖的方法中访问父类方法。

(2)初始化

        · 子类没有构造器,调用父类的

        · 基类先加载。基类在导出类构造器可以访问它之前,就已经完成初始化。

(3)覆盖(复写)

        · 重载基类的方法,基类本身的方法并不会被屏蔽。

        · 只想覆盖 @override注解(不加注解,基类的方法依然存在)

(4)向上转型

1.6.3 final

(1)数据

        · 编译时常量,无法创建setter方法

        · 运行时初始化并不变

        · 使用前总被初始化

(2)方法

        防止继承类修改。(private 隐式在子类中指定为final)

(3)类

        不可被继承,所有方法隐式为final

1.6.4 初始化及加载

(1)有基类,先加载

(2)加载static

(3)new 先加载基类构造器,再加载子类构造器。

1.7 多态

    面向对象的三大特征:封装、继承、多态。

1.7.1 方法调用绑定

(1)前期绑定:在程序编译期间绑定

(2)后期绑定:在运行时绑定(动态绑定),static方法不具有多态。

1.7.2 多态设计

    · 继承

    · 组合:通过类似set方法,修改成员变量

    · 接口

1.7.3 向上转型(安全)

1.8 接口

    提供了一种将接口和实现分离的方法。

1.8.1 抽象类

(1)包含抽象方法的类 abstract

        可以含有构造方法,子类实例化时,先调用父类构造器。

(2)继承

        · 子类必须实现抽象方法

        · 可以继续使用abstact修饰,可以不用实现抽象方法

(3)不需要所有方法都是抽象

(4)不可以实例化

1.8.2 接口interface

(1)所以方法都抽象,且隐式为public的

(2)可以包含域,隐式为static和final的

(3)实现:子类实现所有接口方法 implements

(4)不可以实例化

1.8.3 多重继承

(1)extends 一个基类 implements 多个接口

(2)接口之间可以使用extends继承

(3)可以向上转型为任意接口

1.8.4 设计模式

(1)策略设计模式:根据所传参对象不同,具有不同方法(重载)

(2)适配器设计模式:接受拥有的接口,产生所需要的对象

(3)工厂方法设计模式:返回接口的某个实现的对象

1.9 异常处理

(1)基本异常Exception

        · new 异常对象

        · 异常参数:

                · 默认构造器

                · 字符串

(2)捕获异常

        · try

        · catch

            异常处理的结果有终止模型和恢复模型两种。Java支持终止模型,认为错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。

        · finally

                · 作用:把内存之外的资源恢复到初始状态,如已经打开的文件或者网络等。
                · 含有return时,覆盖其他return。

(3)自定义异常:extends Exception 异常基类

(4)异常说明 throws

(5)发送异常时的清理:在创建需要清理的对象之后,立即进入try-catch-fianlly(嵌套)

(6)java.lang.Throwable

Java基础+集合+多线程+JVM(一)_第3张图片
图1-3 异常类

        · Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。编译时和系统错误

        · Exception(异常):是程序本身可以处理的异常。可以被抛出的异常。

1.10 字符串

1.10.1 String对象不可变

    定义:private final byte/char value[] (byte定义是JDK1.9出现的)

字符串与常量池的关系:Java提高篇之常量池_#include-CSDN博客

1.10.2  “+”“+=”的重载

        · java中仅有的两个重载过的操作符

        · “+”底层用了StringBuffer()的append()和toString()方法。

1.10.3 正则表达式

(1)创建正则 java.util.regex.Pattern

(2)表达式

        · \\\\ 普通的反斜线

        · \\d 一个数字

        · split( regex )

        · replaceAll(regx,String)

正则表达式参考:Java正则表达式

1.10.4 对比

(1)StringBuilder

        · extends AbstractStringBuilder,不用final修饰

        · 线程不安全

(2)StringBuffer

        · extends AbstractStringBuilder

        · 线程安全,对方法加同步锁

(3)常用方法:append、insert、indexOf、toString()

1.11 类型信息

1.11.1 RTTI

        运行时类型识别。

    (1)“传统”,假定在编译时知道了所有类型

    (2)“反射”,在运行时发现和使用类的信息

1.11.2 Class对象

    java.lang.Class类

(1)被编译后产生,保存在.class文件中

(2)用来创建类的所有对象。创建前,用类加载器加载Class对象

(3)获取Class对象的方式:

        · Class c1 = Class.forName(“类的全限定名”);

        · Class c2 = 类名.class;

        · Class c3 = this.getClass();

1.11.3 RTTI应用场景

(1)类型转换

(2)class对象

(3)instance of

(4)反射

        class类与java.lang.reflect类库

(5)JDK动态代理

        Proxy.newProxyInstance(类加载器,接口列表(目标接口),InvocationHandler实例)

1.12 IO

1.12.1 分类

(1)基于字节操作:InputStream/OutputStream

        数据持久化或者网络传输都是以字节进行的。

(2)基于字符操作:Reader/Writer

(3)基于磁盘操作:File

(4)基于网络操作:Socket

    InputStreamReader从字节到字符转化,需要制定字符集

    OutputStreamWriter从字符到字节转化

字符和字节的区别:字节与字符的区别 | 菜鸟教程

1.12.2 模式

(1)BIO

        · 同步阻塞

        · 面向流

        · 数据读取阻塞在线程中

(2)NIO

        · 同步非阻塞

        · 面向缓冲、基于通道,有选择器

        · channel、selector、Buffer

(3)AIO

        · 异步非阻塞

        · 基于事件和回调机制

1.12.3 磁盘IO工作模式

(1)调用方式

         读取和写入文件IO操作都调用操作系统提供的接口,因为磁盘设备是由操作系统管理的。

    系统调用read()/write() ----》内核空间(含缓存机制) -----》用户空间

(2)访问文件方式

    · 标准访问文件方式:先查缓存,再查磁盘。write()时,内核缓存同步sync时间由操作系统决定。

Java基础+集合+多线程+JVM(一)_第4张图片
图1-4 标准访问文件

    · 直接IO: 不经过内核空间

Java基础+集合+多线程+JVM(一)_第5张图片
图1-5 直接IO方式

    · 同步访问文件:数据写到磁盘才返回成功标志给应用程序。

Java基础+集合+多线程+JVM(一)_第6张图片
图1-6 同步访问文件

    · 异步访问文件:不阻塞等待

Java基础+集合+多线程+JVM(一)_第7张图片
图1-7 异步访问文件

1.12.4 Java序列化技术

    (1)java序列化技术就是将一个对象转化成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的。

    (2)对象必须继承java.io.Serializable接口。

    (3)反序列化时,必须有原始类作为模板,才能将这个对象还原。

1.12.5 NIO

1.12.5.1 简介

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。

· Java NIO: Channels and Buffers(通道和缓冲区)
        标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

· Java NIO: Non-blocking IO(非阻塞IO)
        Java NIO可以让你非阻塞的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。

· Java NIO: Selectors(选择器)
        Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道

Java基础+集合+多线程+JVM(一)_第8张图片
图1-8 传统IO
Java基础+集合+多线程+JVM(一)_第9张图片
图1-9 NIO

1.12.5.2 Buffers缓冲区

(1)java.nio包中定义对基本类型的容器。

  Buffer就像一个数组,可以保存多个相同类型的数据。根据类型不同(boolean除外),有以下Buffer常用子类:

    ·ByteBuffer

    ·CharBuffer

    ·ShortBuffer

    ·IntBuffer

    ·LongBuffer

    ·FloatBuffer

    ·DoubleBuffer

(2)属性

        ·容量(capacity):表示Buffer最大数据容量,缓冲区容量不能为负,并且建立后不能修改。

        ·限制(limit):第一个不应该读取或者写入的数据的索引,即位于limit后的数据不可以读写。缓冲区的限制不能为负,并且不能大于其容量(capacity)。

        ·位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制(limit)。

        ·标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark()方法指定Buffer中一个特定的position,之后可以通过调用reset()方法恢复到这个position。

(3)分类

    · 非直接缓冲区:通过allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中。数据存储传输需要先复制到内存地址空间,再到磁盘。

Java基础+集合+多线程+JVM(一)_第10张图片
图1-10 非直接缓冲区

    · 直接缓冲区

        ·通过allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率。

        · 也可以通过FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。

Java基础+集合+多线程+JVM(一)_第11张图片
图1-11 直接缓冲区

1.12.5.3 Channels通道

(1)作用

        表示打开到IO 设备(例如:文件、套接字)的连接。若需要使用NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。Channel 负责传输, Buffer 负责存储。

(2)java.nio.channels.Channel 接口:

        ·FileChannel

        ·SocketChannel

        ·ServerSocketChannel

        ·DatagramChannel

(3)分散读取、聚集写入

            scattering Reads:将通道中的数据分散到多个缓冲区中。

            gathering Writes:将多个缓冲区的数据聚集到通道中

Java基础+集合+多线程+JVM(一)_第12张图片
图1-12 分散读取、聚集写入

1.12.5.4 阻塞与非阻塞

(1)阻塞:客户端连接请求时,服务器只能等待连接请求,不能处理其他事情。

(2)非阻塞:客户端连接注册到多路复用器上,当轮询到连接有I/O请求时,才启动线程处理。

(3)几种IO模式的线程之间对比

    BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 

    NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 

    AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。 

    伪异步:使用线程池管理

图1-13 IO模型线程对比

1.12.5.5 Selector选择器

(1)SelectionKey类

    在SelectionKey类的源码中我们可以看到如下的4中属性,四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接

        ·SelectionKey.OP_CONNECT

        ·SelectionKey.OP_ACCEPT

        ·SelectionKey.OP_READ

        ·SelectionKey.OP_WRITE

    如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:

        int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

Java基础+集合+多线程+JVM(一)_第13张图片
图1-14 选择器

1.12.5.6 NIO的Socket请求

Java基础+集合+多线程+JVM(一)_第14张图片
图 1-15 NIO的Socket请求

    分为两个线程共工作,一个监听客户端连接,一个用于处理数据交互。

1.13 JPA

(1)Java Persistence API 持久层API

        JPA仅仅是一种规范,也就是说JPA仅仅定义了一些接口,而接口是需要实现才能工作的。所以底层需要某种实现

(2)基于O/R映射的标准规范

(3)主要实现:Hibernate、EclipseLink、openJPA

(4)提供

        · XML或注解,将实体对象持久化到数据库中。@Entity、@Table、@Column、@Transient

        · API,用来操作实体对象,执行CRUD

        · JPQL查询语句

(5)避免字段持久化到数据库方法

        · static java序列化之-static|transient 修饰的字段能否被序列化?_CD-CSDN博客

        · final

        · transient 也可以在Java中修饰,防止序列化。

        · @Transient

2 Java集合

2.1 泛型

    指定容器可以保存的类型,在编译器检查错误。

2.2 分类

    Java容器类类库的用途是“保存对象”。

2.2.1 Collection

    · Collection容器继承了Iterable接口( 实现iterator()方法 ),都可以使用foreach。

    · Collection容器都覆盖了toString(),可以直接使用print()

2.2.1.1 List表

(1)必须按照插入的顺序保存元素

(2)常见接口方法

    · boolean add(E e); 在尾部追加元素        · void add(int index, E element) 

     · void clear()                                            · boolean contains(Object o)                  

     · E get(int index);获取指定下标的元素    · int indexOf(Object o)获取指定元素的下标

     · boolean isEmpty() 判断是否为空          · E remove(int index / Object o) 移除                   

     · E set(int index, E element) 修改元素    · int size() 获得大小                                

     · Object[] toArray[] 转化为数组

2.2.1.2 Set

(1)不能用重复元素

(2)常见接口方法

    · boolean add(E e)        · void clear()     · boolean contains(Object o)

    · boolean isEmpty()       · boolean remove(Object o)       · int size()

    · Object[] toArray()

2.2.1.3 Queue

(1)按照排队规则来确定对象产生的顺序

(2)@常见接口方法

    · Throws exception

        · boolean add(E e)      · E remove() 返回并删除头元素      · E element() 返回头元素

    · Returns special value

        · boolean offer(E e)      · E poll() 返回并弹出头节点            · E peek() 返回头元素

2.2.2 Map

(1)一组成对的“键值对”对象,允许使用键来查找值

(2)常见接口方法

    · void clear()          · boolean containsKey(Object key)     

    · boolean containsValue(Object value)   · Set> entrySet()        

    · boolean equals(Object o)                     · Object get(Object key)

    · boolean isEmpty()                                · Object put(Object k, Object v) 会覆盖前面的值       

    · Object remove(Object key)

    · default boolean replace(Object key, Object value, Object newValue)                                 

    · int size()        · remove(Object key)    

    · getOrDefault(Object key, V defaultValue)

    说明:

        调用Map.entrySet(),将集合中的映射关系对象存储Set中。迭代Set集合,通过entry的getKey()和getValue()方法获取映射关系对象。

2.3 迭代器Iterator

2.3.1 概念

    · 迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层的结构。

    · 迭代器只能单向移动。

    · 不能对正在被迭代的集合进行结构上的改变,否则抛出异常。但可以使用iterator自己的remove方法

2.3.2 使用

(1)使用iterator()返回一个Iterator对象,并准备返回序列的第一个元素。

(2)使用next()获得序列的下一个元素

(3)使用hasNext()检查序列中是否还有元素

(4)使用remove()将迭代器新近返回的元素删除

public class Demo{

    public static void mian(String[] args){

        List pets = Pets.arrayList(12);
        Iterator it = pets.iteratror();
        while(it.hasNext()){
            Pet p = it.next();
            system.out.print(p);
        }
        for(Pet p : pets){
            system.out.print(p);
        }

    }

}

使用foreach语法更简洁。

2.4 List的实现

2.4.1 ArrayList

2.4.1.1 基本特性

(1)擅长于随机访问元素,但插入和移除元素慢

(2)线程不安全,底层为Object数组

2.4.1.2 扩容机制

(1)无参构造时,初始为空数组。当添加第一个元素时,扩为10(默认)。

(2)填满时,自动扩容,变为原来的1.5倍左右(奇数偶数不同)。

2.4.2 LinkedList

(1)插入删除代价较低,但随即访问慢。

(2)线程不安全,底层为双向链表

(3)没有实现RandomAccess接口(标识是否具有随机访问能力)。

(4)实现了基本的List接口,同时添加了可以使用其作栈、队列或者双端队列的方法。

2.4.3 Vector

(1)Vector简介

Vector 是矢量队列,它是JDK1.0版本添加的类。继承于AbstractList,实现了List, RandomAccess, Cloneable这些接口。

Vector 继承了AbstractList,实现了List;所以,它是一个队列,支持相关的添加、删除、修改、遍历等功能。

Vector 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。

Vector 实现了Cloneable接口,即实现clone()函数。它能被克隆。

和ArrayList不同,Vector中的操作是线程安全的。

(2)底层分析

     ·Vector实际上是通过一个数组去保存数据的。当我们构造Vecotr时;若使用默认构造函数,则Vector的默认容量大小是10。

     ·当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数 >0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。

     ·Vector的克隆函数,即是将全部元素克隆到一个数组中。

2.5 Stack栈

    (1)后进先出(LIFO)的容器,是限制插入和删除只能在一个位置上进行的

    (2)LinkedList作为底层实现。

    (3)常用方法:

        · push(Object o)                        · pop()                                    · peek()

    (4)常见应用:

            · 后缀表达式

2.6 Set的实现

2.6.1 HashSet

(1)最快的获取元素的方式

(2)利用HashMap的Key存储

(3)判断重复元素原理:

        · 利用hashCode()和equals()方法。先计算加入对象的hashcode值来判断加入的位置,同时与其他对象的hashcode值比较,如果没有相同的hashcode,则假设对象没有重复出现。若hashcode相同,则通过equals方法价差hashcode相等的对象是否真的相同,若相同,则插入操作不成功;若不同,则重新散列。

        · equals()方法被覆盖,则hashCode()方法也必须被覆盖。原因:
                · 两个对象相等,hashcode值一定相同。
                · 两个对象相等,equals()方法放回true。
                · 相同hashcode值,对象不一定相等。

2.6.2 TreeSet

(1)具有有序的存储顺序,按照比较结果的升序保存对象

(2)利用红黑树结构实现

2.6.3 LinkedHashSet

(1)按照添加的顺序保存对象

(2)使用链表来维护元素的插入顺序

2.7 Map的实现

2.7.1 HashMap

2.7.1.1 概念

(1)提供最快的查找技术

(2)基于散列表实现

(3)线程不安全

(4)hash()方法

static final int hash(Object key) {

    int h;

    // key.hashCode():返回散列值也就是hashcode

    // ^ :按位异或

    // >>>:⽆符号右移,忽略符号位,空位都以0补⻬

    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

    · HashMap通过key的hashCode经过扰动函数处理过后得到hash值

    · 然后通过(n - 1) & hash判断当前元素存放的位置,n为数组的长度。

    · 判断该元素与存入的元素的hash值和key是否相同,相同则直接覆盖,不相同就通过“拉链法”解决冲突。

2.7.1.2 底层数据结构

(1)JDK1.8之前,使用数组+链表

        数组中存储Node(implements Map.Entry)。数组初始大小为16.

Java基础+集合+多线程+JVM(一)_第15张图片
图2-1 HashMap1.8之前

(2)JDK1.8之后,使用数组+链表+红黑树,当链表的长度大于阈值(默认8)时 ,变为红黑树

Java基础+集合+多线程+JVM(一)_第16张图片
图2-2 HashMap1.8之后

2.7.1.3 rehash扩容机制

(1)哈希桶数组table的长度length大小必须为2的n次方(一定是合数),这是一种非常规的设计,常规的设计是把桶的大小设计为素数。相对来说素数导致冲突的概率要小于合数

为什么要是2的n次方:取余(%)操作中,如果除数是2的幂次则等价于其除数减一的与(
&)操作,即hash%length == hash & (length - 1)。采用二进制的位操作后,相对于%能提高运算效率。

(2)扩容条件

    int threshold; // 所能容纳的key-value对极限

    final float loadFactor;    // 负载因子

    int modCount; 

    int size;

    首先,Node[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold是HashMap所能容纳的最大数据量的Node(键值对)个数。threshold = length * Load factor。也就是说,在数组定义好长度之后,负载因子越大,所能容纳的键值对个数越多。

if (++size > threshold)

         resize();

当大于阈值threshold 时,进行扩容。

(3)rehash过程

    · 数组大小扩容为原来的2倍。

    · 再散列到新数组。

(4)弊端

    多线程操作可能导致形成循环链表(rehash过程)。参考https://zhuanlan.zhihu.com/p/21673805

参考:https://mp.weixin.qq.com/s?src=11×tamp=1613981827&ver=2905&signature=vByozoJ33qW-bwQ1g-YMiVEpM7Y2B8Woya-sfEgileo2AigmyECAdZFyCtdLUoDtorLq3N8w0kx-WrN9ZRD4BLY6P21gLKQxf*GS4ZSUuWh0rsKKvQ45*dK*pm0NWeKF&new=1

2.7.2 TreeMap

(1)按照比较结果的升序保存键值

(2)基于红黑树(自平衡的排序二叉树)实现,次序由Comparable或者Comparator决定

2.7.3 LinkedHashMap

(1)按照插入顺序保存键值,同时保留HashMap的查询速度

(2)使用链表维护内部顺序。

(3)采用基于访问的最近最少使用(LRU)算法,没有被访问过的元素就会出现在队列的前面。

2.7.4 HashTable

(1)线程安全,内部的方法基本都是经过synchronized修饰,其他使用与HashMap类似。

(2)与concurrentHashMap相比,HashTable使用全表锁

Java基础+集合+多线程+JVM(一)_第17张图片
图2-3 HashTable

2.7.5 ConcurrentHashMap

    与HashMap相比,线程安全,适合并发环境。

(1)JDK1.7 分段锁

        对整个通数组进行分割分段(Segment),多线程访问容器不同段数据时,不会存在锁竞争,提高并发访问效率。

Java基础+集合+多线程+JVM(一)_第18张图片
图2-4 ConcurrentHashMap1.7

(2)JDK1.8 synchronized + CAS

        看起来是一个优化过且线程安全的HashMap。synchronized只锁定当前链表或红黑树的首节点。

Java基础+集合+多线程+JVM(一)_第19张图片
图2-5 ConcurrentHashMap1.8

2.8 Queue

    先进先出(FIFO)的容器,在一端进行插入而在另一端进行删除的

2.8.1 实现方式

(1)Deque接口,双端队列,底层实现可以选择LinkedList。

(2)LinkedList

(3)PriorityQueue

            优先级队列(堆),用Comparator对象修改排序顺序,默认为最小堆。

2.8.2 分类

(1)单队列,存在“假溢出”问题

(2)循环队列

2.9 比较器

2.9.1 Comparable

    来自java.lang包,使用comparaTo(Object obj)方法排序。

    Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。

    此外,实现此接口的对象可以用作有序映射中的键或有序集合中的集合,无需指定比较器。

public class Person implements Comparable{

    @Override

    publicint compareTo(Person p)

    {

        return this.age-p.getAge();

    }

}

    上述demo表示按照从小到大的年纪排序对象。

2.9.2 Comparator

    来自java.util包,使用compare(Object obj1, Object obj2)方法排序。

    Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口)。

    int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”.

public class PersonCompartor implements Comparator{

    @Override

    publicint compare(Person o1, Person o2)

    {

        return o1.getAge()-o2.getAge();//从小到大排序

    }

}

Arrays.sort(people,new PersonCompartor());

// 定制排序的⽤法

Collections.sort(arrayList, new Comparator() {
     @Override
     public int compare(Integer o1, Integer o2) {
         return o2.compareTo(o1); //从大到小排序
         }
  }); 

注意:上面demo的compareTo方法,在String类和8种基本类型的包装类中都含有,按字典序或自然顺序排序。

2.10 工具类Collections和Arrays

2.10.1 Arrays

    在java.util中有一个Arrays类,此类包含用于操纵数组的各种方法,例如:二分查找(binarySearch)、拷贝操作(copyOf)、比较(equals)、填充(fill)、排序(sort)等,功能十分强大。

排序 :sort() 默认从小到大排序

查找 :binarySearch()

比较: equals

填充 :fill(int[], int),可以用作批量初始化

转列表: asList(int, int, int) 参数用逗号隔开

哈希:hashCode()

转字符串 :toString()

拷贝:copyOfRange(int[], int from, int to)


2.10.2 Collections

(1)排序

void reverse(List list)//反转

void shuffle(List list)//随机排序

void sort(List list)//按自然排序的升序排序

void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑

void swap(List list, int i , int j)//交换两个索引位置的元素

void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。

(2)查找替换

int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的

int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)

int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)

void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。

int frequency(Collection c, Object o)//统计元素出现次数

int indexOfSubList(List list, List target)//统计targe在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).

boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素


你可能感兴趣的:(Java基础+集合+多线程+JVM(一))