
Java知识笔记
文章平均质量分 83
本专栏主要记录Java基础相关的知识笔记。
川峰
8年+Android相关工作经验。专注于移动开发领域。
展开
-
《深入理解JAVA虚拟机笔记》并发与线程安全原理
如果出现两条以上的线程争用同一个锁的情况,那轻量级锁就不再有效,必须要膨胀为重量级锁,锁标志的状态值变为“10”,此时Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也必须进入阻塞状态。不过这种读取到“半个变量”的情况是非常罕见的,经过实际测试,在目前主流平台下商用的64位Java虚拟机中并不会出现非原子性访问行为,但是对于 32 位的 Java 虚拟机,譬如比较常用的32位x86平台下的HotSpot虚拟机,对long类型的数据确实存在非原子性访问的风险。原创 2023-12-30 07:15:40 · 954 阅读 · 0 评论 -
如何理解面向对象的OO设计原则和设计模式?
高层模块不应该依赖低层模块,二者都应依赖其抽象;抽象不应依赖细节,细节应该依赖抽象。模块间的依赖关系通过接口或抽象类发生,实现类之间不发生直接的依赖关系。开发者不能干预这些生命周期,只能选择在某一个回调中处理。单一职责、开闭原则、里氏替换、接口隔离、依赖倒置。一个类应该仅有一个引起它变化的原因。一个对象应该对其他对象有最少的了解。(知道的太多,可能活不到剧终)。客户端不应该依赖它不需要的接口。面向接口编程,或面向抽象编程。面向接口编程,或面向抽象编程。永远不要过早的依赖细节。接口隔离原则 ISP。原创 2023-12-31 07:38:06 · 1195 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》编译与优化
方法内联的优化行为理解起来是没有任何困难的,不过就是把目标方法的代码原封不动地“复制”到发起调用的方法之中,避免发生真实的方法调用而已。在一般应用中,完全不会逃逸的局部对象和不会逃逸出线程的对象所占的比例是很大的,如果能使用栈上分配,那大量的对象就会随着方法的结束而自动销毁了,垃圾收集子系统的压力将会下降很多。)〉的技术,这是整个应用程序范围内的类型分析技术,用于确定在目前已加载的类中,某个接口是否有多于一种的实现、某个类是否存在子类、某个子类是否覆盖了父类的某个虚方法等信息。根据上面的分析可知,原创 2023-12-29 14:07:49 · 969 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》运行时栈帧、方法分派、动态类型
Java 虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行结束的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。在 Java 编译程序代码时,栈帧中需要多大的局部变量表,需要多深的操作数栈就已经被分析计算原创 2023-12-29 13:19:30 · 1407 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》Class文件格式、字节码指令
之所以要专门使用这样一个属性去记录泛型类型,是因为Java语言的泛型采用的是擦除法实现的伪泛型,字节码(Code属性)中所有的泛型信息编译(类型变量、参数化类型)在编译之后都通通被擦除掉。Code属性是Class文件中最重要的一个属性, 如果把一个Java程序中的信息分为代码(Code, 方法体里面的Java代码) 和元数据(Metadata, 包括类、 字段、 方法定义及其他信息) 两部分, 那么在整个Class文件里, Code属性用于描述代码, 所有的其他数据项目都用于描述元数据。原创 2023-12-29 08:28:06 · 1233 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》类加载机制
这个阶段的验证是基于二进制字节流进行的,只有通过了这个阶段的验证后,这段字节流才允许进入java虚拟机内存的方法区中进行存储,所以后面的三个验证阶段全部是基于方法区的存储结构上进行的,不会再直接读取,操作字节流了。正如OSGi中的类加载器的设计不符合传统的双亲委派模型的类加载器架构,且业界对其为了实现热部署而带来的额外的高复杂度还存在不少争议,但对这方面有了解的技术人员基本还是能达成一个共识,认为OSGi中对类加载器的运用是值得学习的,完全弄懂了OSGi的实现,就算是掌握了类加载器的精粹。原创 2023-12-29 09:01:19 · 948 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》垃圾回收器
很多教科书判断对象是否存活的算法是这样的:客观地说,引用计数算法(Reference Counting)虽然占用了一些额外的内存空间来进行计数,但它的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。也有一些比较著名的应用案例,例如微软COM(Component Object Model)技术、使用ActionScript 3的FlashPlayer、Python语言以及在游戏脚本领域得到许多应用的Squirrel中都使用了引用计数算法进行内存管理。但是,原创 2023-12-29 08:27:29 · 870 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》OutOfMemoryError 异常
而使用 JDK 7 或更高版本的 JDK 来运行这段程序并不会得到相同的结果,无论是在 JDK 7 中继续使 用-XX:MaxPermSize参数或者在 JDK 8 及以上版本使用-XX:MaxMetaspaceSize参数把方法区容量同样限制在 6MB,也都不会重现 JDK 6 中的溢出异常,循环将一直进行下去。由直接内存导致的内存溢出,一个明显的特征是在 Heap Dump 文件中不会看见有什么明显的异常情况,如果发现内存溢出之后产生的 Dump 文件很小,而程序中又直接或间接使用了。原创 2023-12-28 23:32:57 · 1215 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》对象的创建和访问、对象头
对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的最大限度,但对象头里的信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个有着动态定义的数据结构,以便在极小的空间内存储尽量多的数据,根据对象的状态复用自己的存储空间。,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。,因为虚拟机可以通过普通 Java对象的元数据信息确定Java对象的大小,但是如果数组的长度是不确定的,将无法通过元数据中的信息推断出数组的大小。原创 2023-12-28 22:42:53 · 899 阅读 · 0 评论 -
《深入理解JAVA虚拟机笔记》Java 运行时内存区域
Java虚拟机对Class文件的每一部分(自然也包括常量池)的格式都有严格的规定,如每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、加载和执行,但对于运行时常量池,《Java虚拟机规范》并没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个内存区域,不过一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。但对于大对象(典型的如数组对象),多数虚拟机实现出于实现简单、存储高效的考虑,很可能会要求连续的内存空间。原创 2023-12-27 21:17:11 · 507 阅读 · 0 评论 -
Java 对象内存布局
在虚拟机中,Java对象在内存中的布局可以分为三块:longdoubleshortcharbyteboolean又包括:对于 64 位上的一个空对象:也就是说,原创 2023-12-27 19:56:59 · 491 阅读 · 1 评论 -
Java 何时会触发一个类的初始化
Java 何时会触发一个类的初始化?使用new关键字创建对象访问类的静态成员变量 或 对类的静态成员变量进行赋值调用类的静态方法反射调用类时,如 Class.forName()初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话)遇到启动类时,如果一个类被标记为启动类(即包含main方法),虚拟机会先初始化这个主类。实现带有默认方法的接口的类被初始化时(拥有被default关键字修饰的接口方法的类)使用 JDK7 新加入的动态语言支持时 MethodHandle虚拟机在何时加载类原创 2023-12-10 23:10:26 · 1183 阅读 · 0 评论 -
Java 匿名内部类使用的外部变量,为什么一定要加 final?
这一切都是编译器为我们自动实现的,但对于开发者而言,体验上就会跟 Java 有明显的不同:“Java 需要。保证匿名内部类捕获的副本引用和外部的局部变量始终都指向同一个对象,也就是没有人可以修改它们的指向。捕获”,但这是一个错觉,实际上 Kotlin 也需要,只不过你看不到而已。内存泄漏的根本原因就是一个长生命周期的对象被一个短生命周期的对象所引用。所以虽然与 Java 的解决方式不同,但本质上看思想是一致的,,可以认为是 Kotlin 为了解决。注意:匿名内部类捕获的局部变量加。,Kotlin 中还有。原创 2023-12-10 16:14:08 · 887 阅读 · 0 评论 -
Java 泛型相关知识
所以对于这两种通配符,需要结合所调用函数的传参的意图来理解,否则你单独定义一个变量类型是上界或下界,你就会非常莫名奇妙根本不能理解为什么要这么做,这里的上界或下界其实是针对传入的实参而言的。其实说白了,就是为了入参和出参使用需求,一个只读一个只写。至于读取或写入的类型,很容易能推断出来。上界通配符和下界通配符是 Java 中一种很奇怪的设计,或许它的意图非常巧妙,但是很难让人理解。关于上界通配符和下界通配符的使用,例如 Java 中。即用最顶层的类接受下面的子类型都可以。为什么使用泛型,使用泛型的好处?原创 2023-12-09 20:14:27 · 549 阅读 · 0 评论 -
Java String相关问题
局部变量在stack中,包括基本类型变量和引用变量。成员变量在heap中,包括基本类型变量和引用变量。例如,这句如果是定义在方法中,左边的str是分配在栈中的,如果是定义在类的成员变量,则str是存放在堆中的。所有显示的字面量会放入常量池中,如果常量池中已经有对应的字符串就无需创建,否则就会新创建一个。所有的字面量 + 字面量、final变量 + final变量拼接都合并会生成一个新的字面常量放入常量池中。所有方式都会创建一个String对象,存放在堆中。原创 2023-12-07 15:45:06 · 110 阅读 · 0 评论 -
Java 中 char 和 Unicode、UTF-8、UTF-16、ASCII、GBK 的关系
关于这几种字符编码的关系,经过各种资料研究,总结如下图(请右键在新标签页打开查看或者下载后使用看图工具放大查看):1[0-127]A1-F7A1-A9XX7F4~6HEX由于只规定了表示符号的二进制代码,却没有规定如何存储这个二进制代码。所以如何存储有不同的实现。而就是针对的不同存储方式的具体实现。0-1271282340000~FFFF注意:很多资料和文章中将 UTF-8 和 UTF-16 都称为定长编码,但实际上它们是可变长编码的,例如一个中文汉字用 UTF-8 表示的话,就需要 3 个字节。原创 2023-12-07 06:47:25 · 1112 阅读 · 0 评论 -
《Java 并发编程艺术》笔记(下)
子类推荐被定义为自定义同步组件的静态内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用,同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(在图5-4中,由于非首节点线程前驱节点出队或被中断而从等待状态返回,随后检查自己的前驱是否是头节点,如果是则尝试获取同步状态,可以看到节点与及节点之间在循环检查的过程中基本上不相互通信,而是简单地判断自己的前驱是否为头节点,这样就使得节点的释放符合。原创 2023-12-07 06:43:27 · 208 阅读 · 0 评论 -
Java 之注解相关知识
Java注解按照运行机制来分有以下几类classOverrideDeprected除了JDK自带的常见注解外,java中提供了四种元注解:@Retention@Target@Document@Inherited;元注解负责注解其他注解,可以用来自定义注解。原创 2023-12-07 01:15:08 · 64 阅读 · 0 评论 -
《Java 并发编程艺术》笔记(上)
从 JDK 5 开始,Java使用新的 JSR-133 内存模型,JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。JSR-133 对happens-before关系的定义如下1)原创 2023-12-06 21:42:05 · 165 阅读 · 0 评论 -
Java 线程池到底是如何复用线程的
其实 Java 线程池的实现原理很简单,说白了就是和。当用户向线程池提交一个任务时,线程池会先将任务放入中。中的线程会不断的从中获取线程然后执行。当中没有任务的时候,就会。上图是一张线程池工作的精简图,实际的过程比这个要复杂的多,不过这些应该能够完全覆盖到线程池的整个工作流程了。原创 2023-12-06 16:01:13 · 1546 阅读 · 0 评论 -
Java 深入理解反射(反射的性能开销以及优化)
影响反射调用耗时有以下原因:方法表查找构建Object数组以及可能存在的自动装拆箱操作运行时权限检查方法内联/逃逸分析如何优化反射性能开销?尽量避免反射调用虚方法关闭运行时权限检查可能需要增大基本数据类型对应的包装类缓存关闭 Inflation 机制提高 JVM 关于每个调用能够记录的类型数目JVM 是如何实现反射的?Java反射原理简析关于反射调用方法的一个log。原创 2023-12-04 09:35:11 · 1091 阅读 · 0 评论 -
Java 利用反射修改 static + final修饰的成员变量的值
Java 利用反射修改 static + final修饰的成员变量的值原创 2023-12-04 08:54:55 · 959 阅读 · 0 评论 -
Java 反射使用总结
invoke方法有两个参数,第一个参数是要调用方法的对象,上面的代码中就是Bird的对象,第二个参数是调用方法要传入的参数。Class类对象就相当于B超的探头,将一个类的方法、变量、接口、类名、类修饰符等信息告诉运行的程序。和私有变量一样,私有方法也是不允许其他的类随意调用的,但是通过反射可以饶过这一限制。通过反射可以在运行时获取到类的所有成员变量,还可以给成员变量赋值和获取成员变量的值。方法,必须提供要获取的方法名以及方法名的参数。都只能获取到类声明的成员方法,不能获取到从父类继承来的方法。原创 2023-12-04 08:43:10 · 50 阅读 · 0 评论 -
Java 反射机制知识点
我们知道,类和类的成员变量及方法都是要求有权限控制的(public、protected、private);而当类中的信息封装为私有时,外部对该类中私有的信息是没有访问权限的,也就是说当该类里的内容信息均受private权限控制时,外部想要获取和处理该类里的私有信息是几乎不可能的;但是,有时候这种需求是有的,而当我们非得需要去动用别的类里封装的私有信息时,java的反射机制就起到了非常关键的作用了;原创 2023-12-04 08:06:37 · 71 阅读 · 0 评论 -
Java 不要在父类的构造方法里面调用可以被子类重写的方法
换言之,如果设置了默认值,即使之前已经被赋值,也会被默认值覆盖。相比于第一个坑,第二个坑显得更加隐式和不容易理解。原创 2023-12-04 07:28:43 · 872 阅读 · 0 评论 -
Java 学习之多态
静态方法只能继承,不能重写Override,如果子类中定义了同名同形式的静态方法,它对父类方法只起到隐藏的作用。因为父类引用指向的是 Cat 类的对象,而要强制转换成 Dog 类,这是不可能的。,如上面的例子中,将 p 转换为子类 Child 类型的引用。如果想要调用子类中有而父类中没有的方法,需要进行强制类型转换。因为多态是一种运行期的行为,不是编译期的行为。,即不需要加上前面的小括号和父类类型名。,即指向谁才能转换成谁。父类型的引用必须指向子类的对象。对于向上的类型转换,对于向下的类型转换,原创 2023-12-04 06:37:27 · 103 阅读 · 0 评论 -
Java 中如何正确的将 float 转换成 double?
时候会补位,如果这里补位不出现误差的话应该可以实现。输出结果是:127.0999984741211。如何避免这样的问题发生,让。输出结果是:3.14;为什么结果会是这样呢?原创 2023-12-04 05:39:34 · 1608 阅读 · 0 评论 -
Java 如何正确比较两个浮点数
看下面这段代码,将 d1 和 d2 两个浮点数进行比较,输出的结果会是什么?按照正常逻辑来看,d1经过计算之后的结果应该是0.3,最后打印的结果应该是true,对吧?但是运行一下就会发现结果并不是true而是false。输出一下d1,发现得到的答案不是想象中的0.3而是,所以和d2进行比较结果自然是false。如何正确地比较浮点数(单精度的float和双精度的double),不单单是 Java 特定的问题,在计算机的内存中,存储浮点数时使用的是 IEEE 754 标准,就会有精度的问题。==”原创 2023-12-04 05:21:03 · 729 阅读 · 0 评论 -
为什么重写 equals() 时必须重写 hashCode()?
默认情况下也就是从超类 Object 继承而来的 equals 方法与 ‘==’ 是完全等价的,比较的都是对象的内存地址,但我们可以重写 equals 方法,使其按照我们的需求的方式进行比较。在java应用程序执行期间,如果在 equals 方法比较中所用的信息没有被修改,那么在同一个对象上多次调用 hashCode 方法时必须一致地返回相同的整数。如果多次执行同一个应用时,不要求该整数必须相同。如果两个对象通过调用 equals 方法是相等的,那么这两个对象调用 hashCode 方法必须返回相同的原创 2021-05-10 23:26:09 · 131 阅读 · 1 评论 -
Java死锁的原因例子及解决方法
Java发生死锁的根本原因是:在申请锁时发生了交叉闭环申请。即线程在获得了锁A并且没有释放的情况下去申请锁B,这时,另一个线程已经获得了锁B,在释放锁B之前又要先获得锁A,因此闭环发生,陷入死锁循环。死锁发生的例子1:public class DeadLockA extends Thread { @Override public void run() { t...原创 2018-07-17 09:26:04 · 24813 阅读 · 9 评论 -
Java的String.trim()方法无效的原因
最近遇到String.trim()无效的问题,字符串后面明明有空格就是去不掉,搜索了下资料,原来是因为trim()方法只能去掉半角空格,因为在英文中用的是半角,中文才有全角。。。两种方法解决:1.重写trim()方法:/** * 去除字符串前后各种编码类型的空格 * update 2019 */public class StringUtil { /**普通的英文半角...原创 2018-07-13 09:06:18 · 11108 阅读 · 2 评论 -
Java终止线程的三种方法
使用标志位退出线程使用stop方法强制终止线程使用interrupt终止线程1. 使用标志位退出线程这种也是最常用的方法,就是定义一个boolean型的标志位,在线程的run方法中根据这个标志位是true还是false来判断是否退出,这种情况一般是将任务放在run方法中的一个while循环中执行的。public class ThreadFlag extends Thread...原创 2018-07-12 10:06:31 · 15719 阅读 · 0 评论 -
Java transient关键字的使用
1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列...转载 2018-05-31 01:15:40 · 231 阅读 · 0 评论 -
在java类中实现不同接口的同名方法时,该方法属于哪个接口?
一个类实现了两个接口,在这两个接口当中有相同的方法(同名同参),那么类中的改实现方法属于哪个接口的呢?原创 2018-05-10 13:17:08 · 6910 阅读 · 0 评论