Java
文章平均质量分 82
吴声子夜歌
个人学习记录
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
Java——Apache Commons CSV
读取 CSV(文件 / 流 / URL)写入 CSV支持多种“方言”(Excel、MySQL、RFC4180 等)表头映射(按列名访问)流式处理(适合大文件)</</</</</tom,18jerry,20mike,33.builder().build();原创 2026-05-17 22:21:52 · 297 阅读 · 0 评论 -
Java——类加载机制
Java类加载机制的强大之处在于,我们可以创建自定义的ClassLoader,自定义Class-Loader是Tomcat实现应用隔离、支持JSP、OSGI实现动态模块化的基础。怎么自定义呢?一般而言,继承类ClassLoader,重写findClass就可以了。怎么实现findClass呢?name表示类名,b是存放字节码数据的字节数组,有效数据从off开始,长度为len。原创 2026-05-17 16:46:53 · 422 阅读 · 0 评论 -
Java——动态代理
cglib的实现机制与Java SDK不同,它是通过继承实现的,它也是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有public非final方法,改为调用Callback中的相关方法,在上例中,调用SimpleInterceptor的intercept方法。从代理的角度看,Java SDK代理的是对象,需要先有一个实际对象,自定义的InvocationHandler引用该对象,然后创建一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法;原创 2026-05-17 14:35:24 · 504 阅读 · 0 评论 -
Java——反射
每个已加载的类在内存都有一份类信息,每个对象都有指向它所属类信息的引用。Java中,类信息对应的类就是java.lang.Class。注意不是小写的class, class是定义类的关键字。所有类的根父类Object有一个方法,可以获取对象的Class对象:Class是一个泛型类,有一个类型参数,getClass()并不知道具体的类型,所以返回Class<?>。获取Class对象不一定需要实例对象,如果在写程序时就知道类名,可以使用<类名>.class获取Class对象,比如:接口也有Class对象,且原创 2026-05-17 13:00:48 · 409 阅读 · 0 评论 -
Java——定时任务
后台只有一个线程在运行;固定频率的任务被延迟后,可能会立即执行多次,将次数补够;固定延时任务的延时相对的是任务执行前的时间不要在定时任务中使用无限循环;一个定时任务的未处理异常会导致所有定时任务被取消。原创 2026-05-16 23:14:06 · 537 阅读 · 0 评论 -
Java——显示条件
signal与notify一样,挑选一个线程进行唤醒,signalAll与notifyAll一样,唤醒所有等待的线程,但这些线程被唤醒后都需要重新竞争锁,获取锁后才会从await调用中返回。await在进入等待队列后,会释放锁,释放CPU,当其他线程将它唤醒后,或等待超时后,或发生中断异常后,它都需要重新获取锁,获取锁后,才会从await方法中退出。另外,与Object的wait方法一样,await返回后,不代表其等待的条件就一定满足了,通常要将await的调用放到一个循环内,只有条件满足后才退出。原创 2026-05-16 22:50:41 · 369 阅读 · 0 评论 -
Java——显示锁
保证公平整体性能比较低,低的原因不是这个检查慢,而是会让活跃线程得不到锁,进入等待状态,引起频繁上下文切换,降低了整体的效率,通常情况下,谁先运行关系不大,而且长时间运行,从统计角度而言,虽然不保证公平,也基本是公平的。主体是一个死循环,在每次循环中,首先检查当前节点是不是第一个等待的节点,如果是且能获得到锁,则将当前节点从等待队列中移除并返回,否则最终调用LockSupport. park放弃CPU,进入等待,被唤醒后,检查是否发生了中断,记录中断标志,在最终方法返回时返回中断标志。原创 2026-05-16 22:36:40 · 436 阅读 · 0 评论 -
Java——原子变量和CAS
原子变量的更新逻辑是乐观的,它假定冲突比较少,但使用CAS更新,也就是进行冲突检测,如果确实冲突了,那也没关系,继续尝试就好了。原子变量的更新逻辑是非阻塞式的,更新冲突的时候,它就重试,不会阻塞,不会有上下文切换开销。在MyLock中,使用status表示锁的状态,0表示未锁定,1表示锁定,lock()、unlock()使用CAS方法更新,lock()只有在更新成功后才退出,实现了阻塞的效果,不过一般而言,这种阻塞方式过于消耗CPU,我们后续章节介绍更为高效的方式。我们看个例子,如代码所示。原创 2026-05-16 10:11:49 · 395 阅读 · 0 评论 -
Java——线程的中断
test方法在持有锁lock的情况下启动线程a,而线程a也去尝试获得锁lock,所以会进入锁等待队列,随后test调用线程a的interrupt方法并调用join等待线程线程a结束,线程a会结束吗?如果线程在运行中,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,没有任何其他作用。如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正“中断”。原创 2026-05-16 09:15:01 · 445 阅读 · 0 评论 -
Java——线程的基本协作机制
简单总结一下,wait/notify方法看上去很简单,但往往难以理解wait等的到底是什么,而notify通知的又是什么,我们需要知道,它们被不同的线程调用,但共享相同的锁和条件等待队列(相同对象的synchronized代码块内),它们围绕一个共享的条件变量进行协作,这个条件变量是程序自己维护的,当条件不成立时,线程调用wait进入条件等待队列,另一个线程修改了条件变量后调用notify,调用wait的线程唤醒后需要重新检查条件变量。一个不带时间参数,表示无限期等待,实际就是调用wait(0)。原创 2026-05-15 23:18:02 · 360 阅读 · 0 评论 -
Java——synchronized
显然是否定的,这是一个检查然后再更新的复合操作,在多线程的情况下,可能有多个线程都执行完了检查这一步,都发现Map中没有对应的键,然后就会都调用put,这就破坏了putIf-Absent方法期望保持的语义。可重入是通过记录锁的持有线程和持有数量来实现的,当调用被synchronized保护的代码时,检查对象是否已被锁,如果是,再检查是否被当前线程锁定,如果是,增加持有数量,如果不是被当前线程锁定,才加入等待队列,当释放锁时,减少持有数量,当数量变为0时才释放整个锁。如果还是出现了死锁,怎么办呢?原创 2026-05-15 21:35:26 · 416 阅读 · 0 评论 -
Java——标准序列化机制
因为LinkedList表示一个List,它的逻辑信息是列表的长度,以及列表中的每个对象,但LinkedList类中的字段表示的是链表的实现细节,如头尾节点指针,对每个节点,还有前驱和后继节点指针等。这一行是必需的,它会调用默认的序列化机制,默认机制会保存所有没声明为transient的字段,即使类中的所有字段都是transient,也应该写这一行,因为Java的序列化机制不仅会保存纯粹的数据信息,还会保存一些元数据描述等隐藏信息,这些隐藏的信息是序列化之所以能够神奇的重要原因。序列化到底是如何发生的呢?原创 2026-05-15 20:39:26 · 4144 阅读 · 0 评论 -
Java——内存映射文件
queueName表示队列名,BasicQueue会使用以queueName开头的两个文件来保存队列信息,一个扩展名是.data,保存实际的消息,另一个扩展名是.meta,保存元数据信息,如果这两个文件存在,则会使用已有的队列,否则会建立新队列。这种按需加载的方式,使得内存映射文件可以方便高效地处理非常大的文件,内存放不下整个文件也不要紧,操作系统会自动进行处理,将需要的内容读到内存,将修改的内容保存到硬盘,将不再使用的内存释放。比如,它不太适合处理小文件,它是按页分配内存的,对于小文件,会浪费空间;原创 2026-05-15 11:36:15 · 421 阅读 · 0 评论 -
Java——随机读写文件RandomAccessFile
比如,如果name为student,则两个文件为student.meta和student.data,这两个文件不一定存在,如果不存在,则创建新的数据库,如果已存在,则加载已有的数据库。看上去,writeBytes方法可以直接写入字符串,而readLine方法可以按行读入字符串,实际上,这两个方法都是有问题的,它们都没有编码的概念,都假定一个字节就代表一个字符,这对于中文显然是不成立的,所以,应避免使用这两个方法。它先检查长度,长度满足的情况下,定位到指定位置,写实际数据的长度、写内容、最后补白。原创 2026-05-15 11:01:44 · 371 阅读 · 0 评论 -
Java——常见文件类型处理
一般而言,一行表示一条记录,一条记录包含多个字段,字段之间用逗号分隔。它们分别是OutputStream和InputStream的子类,都是装饰类,GZIPOutputStream加到已有的流上,就可以实现压缩,而GZIPInputStream加到已有的流上,就可以实现解压缩。对于这些问题,CSV有一个参考标准:RFC-4180(https://tools.ietf.org/html/rfc4180),但实践中不同程序往往有其他处理方式,所幸的是,处理方式大体类似,大概有以下两种处理方式。原创 2026-05-15 10:04:21 · 385 阅读 · 0 评论 -
Java——文件和目录操作
pathSeparator和pathSeparatorChar表示多个文件路径中的分隔符,比如,环境变量PATH中的分隔符,Java类路径变量classpath中的分隔符,在执行命令时,操作系统会从PATH指定的目录中寻找命令,Java运行时加载class文件时,会从classpath指定的路径中寻找类文件。在修改方法中,如果修改成功,返回true,否则返回false。通过new新建一个File对象,不会实际创建一个文件,只是创建一个表示文件或目录的对象,new之后,File对象中的路径是不可变的。原创 2026-05-14 22:58:03 · 434 阅读 · 0 评论 -
Java——通用容器类
c)> c)> c)AbstractCollection又不知道数据是怎么存储的,它是如何实现这些方法的呢?抛出“操作不支持”异常,如果子类集合是不可被修改的,这个默认实现就可以了,否则,必须重写add方法。addAll方法的实现就是循环调用add方法。size方法是抽象方法,子类必须重写。isEmpty方法就是检查size方法的返回值是否为0。toArray方法依赖size方法的返回值分配数组大小。iterator方法也是抽象方法,它返回一个实现了迭代器接口的对象,子类必须重写。原创 2026-05-14 22:39:45 · 639 阅读 · 0 评论 -
Java——堆与优先级队列(PriorityQueue)
实现了优先级队列,最先出队的总是优先级最高的,即排序中的第一个。优先级可以有相同的,内部元素不是完全有序的,如果遍历输出,除了第一个,其他没有特定顺序。查看头部元素的效率很高,为O(1),入队、出队效率比较高,为O(log2(N)),构建堆heapify的效率为O(N)。根据值查找和删除元素的效率比较低,为O(N)。原创 2026-05-14 17:24:32 · 414 阅读 · 0 评论 -
Java——EnumMap和EnumSet
对于其他操作,JumboEnumSet的思路是类似的,主要算法与RegularEnumSet一样,主要是增加了寻找对应long位向量的操作,或者有一些循环处理,逻辑也都比较简单,后文就只介绍RegularEnumSet的实现了。对于之前的枚举类Day,它有7个枚举值,一个Day的集合就可以用一个字节byte表示,最高位不用,设为0,最右边的位对应顺序最小的枚举值,从右到左,每位对应一个枚举值,1表示包含该元素,0表示不含该元素。getUniverse的代码与EnumMap是一样的,就不赘述了。原创 2026-05-13 23:18:26 · 467 阅读 · 0 评论 -
Java——ArrayDeque
进行与操作保证了索引在正确范围,与(elements.length-1)相与就可以得到下一个正确位置,是因为elements.length是2的幂次方,(elements.length-1)的后几位全是1,无论是正数还是负数,与(elements.length-1)相与都能得到期望的下一个正确位置。如果head小于tail,就是从head开始复制size个,否则,复制逻辑与doubleCapacity方法中的类似,先复制从head到末尾的部分,然后复制从0到tail的部分。分配了一个长度为16的数组。原创 2026-05-13 22:21:34 · 414 阅读 · 0 评论 -
Java——LinkedList
简单总结下:LinkedList的用法是比较简单的,与ArrayList用法类似,支持List接口,只是,LinkedList增加了一个接口Deque,可以把它看作队列、栈、双端队列,方便地在两端进行操作。队列满时,addⅩⅩⅩ会抛出异常,offerⅩⅩⅩ只是返回false。Java中有一个类Stack,单词意思是栈,它也实现了栈的一些方法,如push/pop/peek等,但它没有实现Deque接口,它是Vector的子类,它增加的这些方法也通过synchronized实现了线程安全,具体就不介绍了。原创 2026-05-13 21:54:21 · 357 阅读 · 0 评论 -
Java——ArrayList
需要注意的是,这个方法返回的List,它的实现类并不是本节介绍的ArrayList,而是Arrays类的一个内部类,在这个内部类的实现中,内部用的数组就是传入的数组,没有拷贝,也不会动态改变大小,所以对数组的修改也会反映到List中,对List调用add、remove方法会抛出异常。上面的代码中,为便于理解,我们删减了一些边界情况处理的代码,完整代码要晦涩复杂一些,但接口一般都是简单直接的,这就是使用容器类的好处,这也是计算机程序中的基本思维方式,封装复杂操作,提供简化接口。代码比较简单,就不解释了。原创 2026-05-12 23:55:01 · 2547 阅读 · 0 评论 -
Java——泛型
不能通过类型参数创建对象。泛型类类型参数不能用于静态变量和方法。了解多个类型限定的语法。为什么非法呢?因为如果允许,那么用户会以为创建的就是对应类型的对象,但由于类型擦除,Java只能创建Object类型的对象,而无法创建T类型的对象,容易引起误解,所以Java干脆禁止这么做。那如果确实希望根据类型创建对象呢?需要设计API接受类型对象,即Class对象,并使用Java中的反射机制。try {原创 2026-05-12 22:29:35 · 413 阅读 · 0 评论 -
Java——随机
其中,prepare()方法计算每个选项的累计概率,保存在数组cumulativeProbabilities中, nextItem()方法根据权重随机选择一个,具体就是,首先生成一个0~1的数,然后使用二分查找,如果没找到,返回结果是-(插入点)-1,所以-index-1就是插入点,插入点的位置就对应选项的索引。数学运算是固定的,所以种子确定后,产生的随机数序列就是确定的,确定的数字序列当然不是真正的随机数,但种子不同,序列就不同,每个序列中数字的分布也都是比较随机和均匀的,所以称之为伪随机数。原创 2026-05-12 00:10:35 · 423 阅读 · 0 评论 -
Java——Arrays
将不变和变化相分离,排序的基本步骤和算法是不变的,但按什么排序是变化的,sort方法将不变的算法设计为主体逻辑,而将变化的排序方式设计为参数,允许调用者动态指定,这也是一种常见的设计模式,称为策略模式,不同的排序方式就是不同的策略。这个算法是Java 7引入的,在此之前,Java采用的算法是普通的快速排序。多维数组到底是什么呢?回顾一下,String计算hashCode的算法也是类似的,数组中的每个元素都影响hash值,位置不同,影响也不同,使用31一方面产生的哈希值更分散,另一方面计算效率也比较高。原创 2026-05-11 21:51:19 · 322 阅读 · 0 评论 -
Java——String
String内部是按UTF-16BE处理字符的,对BMP字符,使用一个char,两个字节,对于增补字符,使用两个char,四个字节。实际上,这些常量就是String类型的对象,在内存中,它们被放在一个共享的地方,这个地方称为字符串常量池,它保存所有的常量字符串,每个常量只会保存一份,被所有使用者共享。Java中的字符串常量是非常特殊的,除了可以直接赋值给String变量外,它自己就像一个String类型的对象,可以直接调用String的各种方法。String中的大部分方法内部也都是操作的这个字符数组。原创 2026-05-11 17:37:34 · 362 阅读 · 0 评论 -
Java——Character
UTF-16是一种编码方式,或者叫映射方式,它将编号映射为两个或4个字节,对BMP字符,它直接用两个字节表示,对于增补字符,使用4个字节表示,前两个字节叫高代理项(high surrogate),范围为0xD800~ 0xDBFF,后两个字节叫低代理项(low surrogate),范围为0xDC00~0xDFFF。Java内部采用UTF-16编码,char表示一个字符,但只能表示BMP中的字符,对于增补字符,需要使用两个char表示,一个表示高代理项,一个表示低代理项。原创 2026-05-11 15:59:00 · 419 阅读 · 0 评论 -
Java——Integer与二进制算法
这段代码虽然很短,但非常晦涩,到底是什么意思呢?代码第一行是一个注释,HD表示的是一本书,书名为Hacker’s Delight,中文版为《算法心得:高效算法的奥秘》, HD是它的缩写,Figure 7-1是书中的图7-1,reverse的代码就是复制了这本书中图7-1的代码,书中也说明了代码的思路,我们简要说明。所谓循环移位,是相对于普通的移位而言的,普通移位,比如左移2位,原来的最高两位就没有了,右边会补0,而如果是循环左移两位,则原来的最高两位会移到最右边,就像一个左右相接的环一样。原创 2026-05-11 13:02:53 · 440 阅读 · 0 评论 -
Java——异常
除了Java API中定义的异常类,也可以自己定义异常类,一般是继承Exception或者它的某个子类。如果父类是RuntimeException或它的某个子类,则自定义异常也是未受检异常;如果是Exception或Exception的其他子类,则自定义异常是受检异常。super();和很多其他异常类一样,我们没有定义额外的属性和代码,只是继承了Exception,定义了构造方法并调用了父类的构造方法。原创 2026-05-10 22:23:24 · 352 阅读 · 0 评论 -
Java——枚举的本质
因为ordinal值会随着枚举值在定义中的位置变化而变化,但一般来说,我们希望id值和枚举值的关系保持不变,尤其是表示枚举值的id已经保存在了很多地方的时候。需要说明的是,枚举值的定义需要放在最上面,枚举值写完之后,要以分号(;枚举还有一些高级用法,比如,每个枚举值可以有关联的类定义体,枚举类型可以声明抽象方法,每个枚举值中可以实现该方法,也可以重写枚举类型的其他方法。比如,上面的Size例子,每个枚举值可能有关联的缩写和中文名称,可能需要静态方法根据缩写返回对应的枚举值,修改后的Size代码如代码所示。原创 2026-05-10 20:49:27 · 321 阅读 · 0 评论 -
Java——内部类的本质
所说的类都对应于一个独立的Java源文件,但一个类还可以放在另一个类的内部,称之为内部类,相对而言,包含它的类称之为外部类。一般而言,内部类与包含它的外部类有比较密切的关系,而与其他类关系不大,定义在类内部,可以实现对外部完全隐藏,可以有更好的封装性,代码实现上也往往更为简洁。不过,内部类只是Java编译器的概念,对于Java虚拟机而言,它是不知道内部类这回事的,每个内部类最后都会被编译为一个独立的类,生成一个独立的字节码文件。也就是说,每个内部类其实都可以被替换为一个独立的类。原创 2026-05-10 17:25:42 · 365 阅读 · 0 评论 -
Java——接口的细节
在没有默认方法之前,Java是很难给接口增加功能的,比如List接口,因为有太多非Java JDK控制的代码实现了该接口,如果给接口增加一个方法,则那些接口的实现就无法在新版Java上运行,必须改写代码,实现新的方法,这显然是无法接受的。在接口不能定义静态方法之前,相关的静态方法往往定义在单独的类中,比如,Java API中,Collection接口有一个对应的单独的类Collections,在Java 8中,就可以直接写在接口中了,比如Comparator接口就定义了多个静态方法。原创 2026-05-10 14:24:00 · 364 阅读 · 0 评论 -
Java——继承实现的基本原理
子类的addAll方法首先调用了父类的add-All方法,而父类的addAll方法通过add方法添加,由于动态绑定,子类的add方法会执行,子类的add也会做汇总操作。上面的代码创建了Child类型的对象,赋值给了Child类型的引用变量c,通过c调用action方法,又赋值给了Base类型的引用变量b,通过b也调用了action,最后通过b和c访问静态变量s并输出。在Java中,类是动态加载的,当第一次使用这个类的时候才会加载,加载一个类时,会查看其父类是否已加载,如果没有,则会加载其父类。原创 2026-05-10 13:46:21 · 362 阅读 · 0 评论 -
Java——继承的细节
第一次输出是在new过程中输出的,在new过程中,首先是初始化父类,父类构造方法调用test()方法,test()方法被子类重写了,就会调用子类的test()方法,子类方法访问子类实例变量a,而这个时候子类的实例变量的赋值语句和构造方法还没有执行,所以输出的是其默认值0。继承反映的是“is-a”的关系,即子类对象也属于父类,子类必须支持父类所有对外的行为,将可见性降低就会减少子类对外的行为,从而破坏“is-a”的关系,但子类可以增加父类的行为,所以提升可见性是没有问题的。如果重名,访问的是哪一个呢?原创 2026-05-09 23:10:46 · 443 阅读 · 0 评论 -
Java——字符编码
比如,法国人发送的是Pékin, Windows-1252的二进制(采用十六进制)是50 E9 6B 69 6E,第二个字节E9对应é,其他都是ASCII码,中国人收到的也是这个二进制,但是他把它看成了GB18030编码,GB18030中E9 6B对应的是字符“閗”,于是他看到的就是“P閗in”,这看来就是一个乱码。在四字节编码中,第一个字节的值为0x81~0xFE,第二个字节的值为0x30~0x39,第三个字节的值为0x81~0xFE,第四个字节的值为0x30~0x39。那超出范围的字符怎么表示呢?原创 2026-05-09 21:54:56 · 665 阅读 · 0 评论 -
Java——内存分析工具jol-core
JOL(Java Object Layout)不是传统意义上的内存泄漏检测工具(不像 MAT / VisualVM / jprofiler),但它非常适合在“对象膨胀、重复引用、结构不合理”导致的慢泄漏场景中做微观分析。数组对象头 = 16 bytes(普通对象是 12 bytes,数组多了 length)典型输出(64 位 HotSpot,开启压缩指针)原创 2026-05-09 14:51:08 · 389 阅读 · 0 评论 -
Java—— CompletableFuture
在JAVA8开始引入了全新的CompletableFuture类,它是Future接口的一个实现类。也就是在Future接口的基础上,额外封装提供了一些执行方法,用来解决Future使用场景中的一些不足,对流水线处理能力提供了支持。CompletableFuture 可以从全局的 ForkJoinPool.commonPool() 获得一个线程中执行这些任务。但也可以创建一个线程池并传给 runAsync() 和 supplyAsync() 来让他们从线程池中获取一个线程执行它们的任务。原创 2024-11-02 14:21:42 · 1754 阅读 · 0 评论 -
Java——正则表达式详解
Java对正则表达式的支持是从1.4版本开始的,此前的JRE版本不支持正则表达式。Java语言中的正则表达式匹配功能主要是通过和类实现的。find():在一个字符串里寻找一个给定模式的匹配:用一个给定的模式尝试匹配一个字符串的开头matches():用一个给定的模式去尝试匹配一个完整的字符串:进行替换操作,对所有的匹配都进行替换:进行替换操作,只对第一个匹配进行替换compile():把一个正则表达式编译成一个模式flags():返回某给定模式的匹配标志matches()原创 2024-03-07 13:29:16 · 20359 阅读 · 4 评论 -
Java——JAVE(音视频格式转换)
JAVE (Java Audio Video Encoder)是一个纯Java的音视频编码器和解码器库,它是基于FFmpeg。JAVE库提供了一些简单易用的API,用于音频和视频格式的转换、编码、解码等操作。它对于一些基本的音视频处理任务来说是一个不错的选择。这些库都是基于FFmpeg的,并允许在Java中处理音频和视频文件。使用它们可以避免直接调用外部的FFmpeg命令行工具,而是通过Java API来实现音频格式转换等操作。原创 2024-03-03 17:53:39 · 20679 阅读 · 0 评论 -
Java——高级Swing和图形化编程(二)
目录高级Swing和图形化编程(二)3、高级AWT3.1、绘图操作流程3.2、形状1)、形状类层次结构2)、使用形状类3.3、区域3.4、笔画3.5、着色3.6、坐标变换3.7、剪切3.8、透明与组合4、像素图4.1、图像的读取器和写入器1)、获得适合图像文件类型的读取器和写入器2)、读取和写入带有多个图像的文件3)、示例4)、API4.2、图像处理1)、构建像素图2)、图像过滤高级Swing和图形化编程(二)3、高级AWT3.1、绘图操作流程在最初的JDK1.0中,用来绘制形状的是一种非常简单的机原创 2024-02-19 21:42:36 · 1952 阅读 · 0 评论
分享