java基础知识漏洞三

Object

Object 类的常见方法有哪些?

/*** native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。*/
public final native Class<?> getClass()
/*** native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。*/
public native int hashCode()
/*** 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。*/
public boolean equals(Object obj)
/*** native 方法,用于创建并返回当前对象的一份拷贝。*/
protected native Object clone() throws CloneNotSupportedException
/*** 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。*/
public String toString()
/*** native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。*/
public final native void notify()
/*** native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。*/
public final native void notifyAll()
/*** native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。*/
public final native void wait(long timeout) throws InterruptedException
/*** 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。。*/
public final void wait(long timeout, int nanos) throws InterruptedException
/*** 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念*/
public final void wait() throws InterruptedException
/*** 实例被垃圾回收器回收的时候触发的操作*/
protected void finalize() throws Throwable { }

为什么重写 equals() 时必须重写 hashCode() 方法?

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

思考:重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。

  1. 违反HashMap的合同HashMap依赖于hashCode()equals()方法来确定键的唯一性。如果两个相等的对象(根据equals()方法)有不同的哈希码,那么HashMap可能会错误地将它们视为不同的键,导致数据结构的不一致。

  2. 无法正确检索对象:如果两个相等的对象有不同的哈希码,那么当尝试从HashMap中获取一个对象时,可能会得到错误的对象或者根本找不到对象。

  3. 性能问题:当hashCode()方法没有被正确重写时,HashMap可能无法有效地分布键,导致哈希冲突的增加。这会降低HashMap的性能,因为更多的比较需要通过equals()方法来解决冲突。

  4. 其他集合类的问题:不仅仅是HashMap,任何依赖于哈希码的集合类(如HashSetHashtable)都可能出现类似的问题。

总结

  • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

  • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

更多关于 hashCode()equals() 的内容可以查看:Java hashCode() 和 equals()的若干问题解答

String

String 为什么是不可变的?

String 类中使用 final 关键字修饰字符数组来保存字符串,所以String 对象是不可变的(注意这种说法是错误的)。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];//...
}

🐛 修正:我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。

String 真正不可变有下面几点原因:

  1. 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。

  2. String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

相关阅读:如何理解 String 类型值的不可变? - 知乎提问

补充(来自issue 675):在 Java 9 之后,StringStringBuilderStringBuffer 的实现改用 byte 数组存储字符串。

public final class String implements java.io.Serializable,Comparable<String>, CharSequence {// @Stable 注解表示变量最多被修改一次,称为“稳定的”。@Stableprivate final byte[] value;
}abstract class AbstractStringBuilder implements Appendable, CharSequence {byte[] value;}

Java 9 为何要将 String 的底层实现由 char[] 改成了 byte[] ?

新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,byte 占一个字节(8 位),char 占用 2 个字节(16),byte 相较 char 节省一半的内存空间。

JDK 官方就说了绝大部分字符串对象只包含 Latin-1 可表示的字符。

如果字符串中包含的汉字超过 Latin-1 可表示范围内的字符,bytechar 所占用的空间是一样的。

这是官方的介绍:JEP 254: Compact Strings 。

字符串拼接用“+” 还是 StringBuilder?

Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。

String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;

上面的代码对应的字节码如下:

可以看出,字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象

String[] arr = {"he", "llo", "world"};
String s = "";
for (int i = 0; i < arr.length; i++) {s += arr[i];
}
System.out.println(s);

StringBuilder 对象是在循环内部被创建的,这意味着每循环一次就会创建一个 StringBuilder 对象。

如果直接使用 StringBuilder 对象进行字符串拼接的话,就不会存在这个问题了。

String[] arr = {"he", "llo", "world"};
StringBuilder s = new StringBuilder();
for (String value : arr) {s.append(value);
}
System.out.println(s);

如果你使用 IDEA 的话,IDEA 自带的代码检查机制也会提示你修改代码。

不过,使用 “+” 进行字符串拼接会产生大量的临时对象的问题在 JDK9 中得到了解决。在 JDK9 当中,字符串相加 “+” 改为了用动态方法 makeConcatWithConstants() 来实现,而不是大量的 StringBuilder 了。这个改进是 JDK9 的 JEP 280 提出的,这也意味着 JDK 9 之后,你可以放心使用“+” 进行字符串拼接了。关于这部分改进的详细介绍,推荐阅读这篇文章:还在无脑用 StringBuilder?来重温一下字符串拼接吧 。

String#equals() 和 Object#equals() 有何区别?

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Objectequals 方法是比较的对象的内存地址。

字符串常量池的作用了解吗?

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true

更多关于字符串常量池的介绍可以看一下 Java 内存区域详解 这篇文章。

String s1 = new String("abc");这句话创建了几个字符串对象?

会创建 1 或 2 个字符串对象。

1、如果字符串常量池中不存在字符串对象“abc”的引用,那么它会在堆上创建两个字符串对象,其中一个字符串对象的引用会被保存在字符串常量池中。

示例代码(JDK 1.8):

String s1 = new String("abc");

对应的字节码:

ldc 命令用于判断字符串常量池中是否保存了对应的字符串对象的引用,如果保存了的话直接返回,如果没有保存的话,会在堆中创建对应的字符串对象并将该字符串对象的引用保存到字符串常量池中。

2、如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”。

示例代码(JDK 1.8):

// 字符串常量池中已存在字符串对象“abc”的引用
String s1 = "abc";
// 下面这段代码只会在堆中创建 1 个字符串对象“abc”
String s2 = new String("abc");

对应的字节码:

这里就不对上面的字节码进行详细注释了,7 这个位置的 ldc 命令不会在堆中创建新的字符串对象“abc”,这是因为 0 这个位置已经执行了一次 ldc 命令,已经在堆中创建过一次字符串对象“abc”了。7 这个位置执行 ldc 命令会直接返回字符串常量池中字符串对象“abc”对应的引用。

String#intern 方法有什么作用?

String.intern() 是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中,可以简单分为两种情况:

  • 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。

  • 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。

示例代码(JDK 1.8) :

// 在堆中创建字符串对象”Java“
// 将字符串对象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s2 = s1.intern();
// 会在堆中在单独创建一个字符串对象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一个对象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的对象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一个对象
System.out.println(s1 == s4); //true

String 类型的变量和常量做“+”运算时发生了什么?

先来看字符串不加 final 关键字拼接的情况(JDK1.8):

String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";
String str4 = str1 + str2;
String str5 = "string";
System.out.println(str3 == str4);//false
System.out.println(str3 == str5);//true
System.out.println(str4 == str5);//false

注意:比较 String 字符串的值是否相等,可以使用 equals() 方法。 String 中的 equals 方法是被重写过的。 Objectequals 方法是比较的对象的内存地址,而 Stringequals 方法比较的是字符串的值是否相等。如果你使用 == 比较两个字符串是否相等的话,IDEA 还是提示你使用 equals() 方法替换。

对于编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化。

在编译过程中,Javac 编译器(下文中统称为编译器)会进行一个叫做 常量折叠(Constant Folding) 的代码优化。《深入理解 Java 虚拟机》中是也有介绍到:

常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,这是 Javac 编译器会对源代码做的极少量优化措施之一(代码优化几乎都在即时编译器中进行)。

对于 String str3 = "str" + "ing"; 编译器会给你优化成 String str3 = "string";

并不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以:

  • 基本数据类型( bytebooleanshortcharintfloatlongdouble)以及字符串常量。

  • final 修饰的基本数据类型和字符串变量

  • 字符串通过 “+”拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>> )

引用的值在程序编译期是无法确定的,编译器无法对其进行优化。

对象引用和“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

String str4 = new StringBuilder().append(str1).append(str2).toString();

我们在平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer

不过,字符串使用 final 关键字声明之后,可以让编译器当做常量来处理。

示例代码:

final String str1 = "str";
final String str2 = "ing";
// 下面两个表达式其实是等价的
String c = "str" + "ing";// 常量池中的对象
String d = str1 + str2; // 常量池中的对象
System.out.println(c == d);// true

final 关键字修饰之后的 String 会被编译器当做常量来处理,编译器在程序编译期就可以确定它的值,其效果就相当于访问常量。

如果 ,编译器在运行时才能知道其确切值的话,就无法对其优化。

示例代码(str2 在运行时才能确定其值):

final String str1 = "str";
final String str2 = getStr();
String c = "str" + "ing";// 常量池中的对象
String d = str1 + str2; // 在堆上创建的新的对象
System.out.println(c == d);// false
public static String getStr() {return "ing";
}

参考

  • 深入解析 String#intern:深入解析String#intern - 美团技术团队

  • R 大(RednaxelaFX)关于常量折叠的回答:https://www.zhihu.com/question/55976094/answer/147302764

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/851815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

读取CSV文件生成RDD去掉标题行

文章目录 1. 创建CSV文件2. 上传CSV文件3. 读取CSV文件生成RDD4. 去掉标题行生成新RDD5. 查看新生成的RDD 1. 创建CSV文件 执行命令&#xff1a;vim scores.csv 在WPS里查看CSV文件 2. 上传CSV文件 执行命令&#xff1a;hdfs dfs -put scores.csv /park 3. 读取CSV文件生…

公司活动想找媒体报道宣传怎样邀请媒体?

在当今信息爆炸的时代,对于正处于成长阶段的中小企业而言,有效且成本控制得当的宣传策略是推动品牌发展、扩大市场影响力的关键。尤其是在预算有限的情况下,如何让“好钢用在刀刃上”,实现宣传效果的最大化,成为众多企业共同面临的挑战。在此背景下,智慧软文发布系统网站作为一…

智能体,大模型的“增程路线”?

智能体&#xff08;AI Agent&#xff09;&#xff0c;正在将用户对大语言模型的使用分成两种截然不同的方式&#xff1a;同样是写一篇文章&#xff0c;在非智能体工作流中&#xff0c;用户输入提示词&#xff0c;然后等待大模型生成答案的整个过程&#xff0c;就像中间不使用退…

补偿 EMI 滤波器 X 电容对有源 PFC 功率因数的影响

现代开关模式电源使用 X 电容器和 Y 电容器与电感器的组合来过滤共模和差模 EMI。滤波器元件位于任何有源&#xff08;或无源&#xff09;功率因数校正 (PFC) 电路的前面&#xff08;图 1&#xff09;&#xff0c;因此 EMI 滤波器的电抗对功率因数 (PF) 造成的任何失真都会改变…

日本2024年最受欢迎的转职行业是IT 通信

2024年有关机构针对超1000名人力资源专业人士进行了“推荐转职行业”的调查。结果显示&#xff0c;日本目前最受欢迎的转职行业是 1、“IT/通信行业”&#xff08;45.9%&#xff09;&#xff0c; 2、其次是“互联网/广告/游戏”&#xff08;31.9%&#xff09;&#xff0c; 3、“…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA与朋友们的石头剪刀布游戏(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍓OJ题目截图 📎在线评测链接 LYA与朋友们的石头剪刀布游戏(100分) 🌍 评…

清华大学与智谱AI重磅开源 GLM-4:掀起自然语言处理新革命

在强大的预训练基础上&#xff0c;GLM-4-9B 的中英文综合性能相比 ChatGLM3-6B 提升了 40%。尤其是中文对齐能力 AlignBench、指令遵从能力 IFeval&#xff0c;以及工程代码处理能力 Natural Code Bench 方面都实现了显著提升。 自 2023 年 3 月 14 日开源 ChatGLM-6B 以来&am…

《Brave New Words 》致谢

Acknowledgments 致谢 Thank you to Umaima Marvi for being the best life partner, whose support has been instrumental in every step of this journey. 感谢 Umaima Marvi&#xff0c;她是最好的生活伴侣&#xff0c;她的支持在这段旅程中的每一步都起到了关键作用。 Imr…

Vue2/Vue3使用video播放视频--捕获截图

基本步骤 在JavaScript中&#xff0c;实现从<video>元素中截图的基本步骤如下&#xff1a; 1、创建Canvas元素&#xff1a;首先&#xff0c;需要创建一个<canvas>元素&#xff0c;因为截图操作会借助Canvas的绘图上下文来完成。 2、获取Video帧&#xff1a;从<v…

【云原生】Kubernetes----Metrics-Server组件与HPA资源

目录 引言 一、概述 &#xff08;一&#xff09;Metrics-Server简介 &#xff08;二&#xff09;Metrics-Server的工作原理 &#xff08;三&#xff09;HPA与Metrics-Server的作用 &#xff08;四&#xff09;HPA与Metrics-Server的关系 &#xff08;五&#xff09;HPA与…

QT属性系统,简单属性功能快速实现 QT属性的简单理解 属性学习如此简单 一文就能读懂QT属性 QT属性最简单的学习

4.4 属性系统 Qt 元对象系统最主要的功能是实现信号和槽机制&#xff0c;当然也有其他功能&#xff0c;就是支持属性系统。有些高级语言通过编译器的 __property 或者 [property] 等关键字实现属性系统&#xff0c;用于提供对成员变量的访问权限&#xff0c;Qt 则通过自己的元对…

【算法与数据结构】【数组篇】【题11-题15】

系列文章 本人系列文章-CSDN博客https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5502 1.数组基本知识点 1.1概念 数组就是一个集合。数组会用一些名为索引的数字来标识每项数据在数组中的位置&#xff0c;且在大多数编程语言中&…

机器学习-- 如何清洗数据集

文章目录 引言&#xff1a;数据清洗的具体步骤数据清洗的具体方法和示例1. 处理缺失值2. 去除重复数据3. 修正数据格式4. 处理异常值5. 标准化和归一化6. 处理不一致的数据7. 转换数据类型8. 数据集成 总结 引言&#xff1a; 数据清洗是数据处理和分析的关键步骤&#xff0c;旨…

2024/6/11 英语每日一段

They found that, regardless of culture, greater mental well-being is linked with feeling emotions that we believe are appropriate to our situation, rather than just having positive emotions regardless of context--“feeling right” as opposed to “feeling g…

什么是电表无人抄表?

1.电表无人抄表&#xff1a;智能时代的新式计量方法 随着科技的发展的迅猛发展&#xff0c;传统电表抄表方法正被一种全新的、高效率的方式所替代——电表无人抄表。这类技术的普及&#xff0c;不仅提升了电力行业的经营效率&#xff0c;同时也为用户增添了更贴心的服务。 2.…

从零开始手把手Vue3+TypeScript+ElementPlus管理后台项目实战九(整体布局02)

使用el-menu和el-sub-menu及el-menu-item导航 src/layout目录下新增components目录&#xff0c;components目录下新增PageSidebar.vue 代码基本思想为&#xff1a;读取router中定义的routes数组&#xff0c;渲染绑定到el-menu。 el-menu和el-sub-menu及el-menu-item的区别&…

SprringCloud Gateway动态添加路由不重启

文章目录 前言&#xff1a;一、动态路由必要性二、SpringCloud Gateway路由加载过程RouteDefinitionLocator接口PropertiesRouteDefinitionLocator类DiscoveryClientRouteDefinitionLocatorInMemoryRouteDefinitionRepositoryCompositeRouteDefinitionLocator类CachingRouteDef…

【话题】程序员应该有什么职业素养

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章&#xff0c;这是《话题》系列文章 目录 背景职业素养的重要性职业素养的核心1.1 承诺与责任感1.2 沟通与团队合作1.3 学习与持续进步 态度和价值观的作用2.1 诚实和诚信2.2 责任和自我管理2.3 尊重和多样性 职…

Linux crontabs定时执行任务

文章目录 前言一、安装二、服务1. 启动crond服务2. 关闭crond服务3. 重启crond服务4. 设置crond开机启动5. 禁用crond开机启动6. 查看crond是否开机启动7. 重新载入配置8. 查看crond运行状态 三、使用1. 查看当前用户的crontab2. 编辑用户的crontab3. 删除用户的crontab的内容 …

JEPaaS 低代码平台 j_spring_security_check SQL注入漏洞复现

0x01 产品简介 JEPaaS是一款优秀的软件平台产品,可视化开发环境,低代码拖拽式配置开发,操作极其简单,可以帮助解决Java项目80%的重复工作,让开发更多关注业务逻辑,大大提高开发效率,能帮助公司大幅节省人力成本和时间成本,同时又不失灵活性。适用于搭建 OA、ERP、CRM、…