Java基础知识总结(中)

本文部分内容节选自Java Guide, 地址: https://javaguide.cn/java/basis/java-basic-questions-02.html

🚀 基础(上) → 🚀 基础(中) (正在更新中……)

面向对象基础

对象相等和引用相等的区别

  • 对象相等一般比较的是存储在内存中的内容是否相等

  • 引用相等一般比较的是内存地址是否相等

请看代码示例

String str1 = "hello";
String str2 = new String("hello");
String str3 = "hello";// '=='比较的是引用
System.out.println(str1 == str2);
System.out.println(str1 == str3);
// 'equals()'比较的是对象
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));

输出结果如下

false
true
true
true

从上面的代码输出结果可以看出:

  • str1str2 不相等, 而 str1str3 相等. 这是因为 == 运算符比较的是字符串的引用是否相等
  • str1str2str3 三者的内容都相等. 这是因为equals 方法比较的是字符串的内容, 即使这些字符串的对象引用不同, 只要它们的内容相等, 就认为它们是相等的

如果一个类没有声明构造方法, 该程序能正确运行吗?

答案是可以的

假如一个类没有声明构造方法, 它也会有一个默认的不带参数的构造方法. 但是请注意, 如果我们声明了一个含参数的构造方法, 那么就不会自动生成默认不带参数构造方法了

构造函数有哪些特点? 是否能被override?

特点如下:

  • 名字和类名相同
  • 没有返回值, 但这不意味着可以用 void 声明构造函数, 构造函数不能用 void 声明
  • 不需要手动调用构造函数, 在生成类的对象的时候会自动调用

构造函数不能被override (重写), 但是可以被overload (重载)

面向对象三大特征?

封装

封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。就好像我们看不到挂在墙上的空调的内部的零件信息(也就是属性),但是可以通过遥控器(方法)来控制空调。如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。就好像如果没有空调遥控器,那么我们就无法操控空凋制冷,空调本身就没有意义了

public class Student {private int id;//id属性私有化private String name;//name属性私有化//获取id的方法public int getId() {return id;}//设置id的方法public void setId(int id) {this.id = id;}//获取name的方法public String getName() {return name;}//设置name的方法public void setName(String name) {this.name = name;}
}

继承

不同类型的对象,相互之间经常有一定数量的共同点。例如,小明同学、小红同学、小李同学,都共享学生的特性(班级、学号等)。同时,每一个对象还定义了额外的特性使得他们与众不同。例如小明的数学比较好,小红的性格惹人喜爱;小李的力气比较大。继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。

  1. 子类拥有父类对象所有的属性和方法(包括私有的), 但是父类的私有属性和私有方法子类不能访问, 只是拥有而已
  2. 子类可以拥有自己的属性和方法
  3. 子类可以用自己的方式实现父类的方法

多态

多态, 顾名思义就是一个对象具有多种状态, 具体表现为父类的引用指向子类的实例

多态的特点

  • 对象类型和引用类型之间具有继承/实现的关系
  • 引用类型变量发出的方法调用的到底是哪个类中的方法, 必须在程序运行期间才能确定
  • 多态不能调用"只在子类存在但父类不存在"的方法
  • 如果子类重写了父类的方法, 真正执行的是子类覆盖的方法, 如果子类没有重写父类方法, 那么执行的是父类的方法

接口和抽象类有什么共同点和区别?

共同点

  • 都不能被实例化
  • 都可以包含抽象方法
  • 都可以有默认实现的方法( Java 8 可以用 default 关键字在接口中定义默认方法)

区别

  • 接口主要是对类的行为进行约束, 实现了某个接口就有了对应的行为, 抽象类主要用于代码复用, 强调的是所属关系
  • 一个类只能继承一个类, 但是可以实现多个接口
  • 接口中的成员变量只能是 public static final 类型的, 不能被修改且必须有初始值, 而抽象类的成员变量默认default, 可在子类中重新定义, 也可被重新赋值

深拷贝和浅拷贝了解吗? 什么是引用拷贝?

  • 浅拷贝 : 浅拷贝会在堆上创建一个新对象(区别于引用拷贝的一点), 不过如果原对象内部的属性是引用类型的话, 浅拷贝会直接复制内部对象的引用地址, 也就是说拷贝对象和原对象共用同一个内部对象

  • 深拷贝 : 深拷贝会完全复制整个对象, 包括这个对象所包含的内部对象

  • 引用拷贝 : 两个不同的引用指向同一个对象

Object

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() 的区别

== 对于引用类型和基本类型的作用是不同的

  • 对于引用类型, == 比较的是内存地址
  • 对于基本类型, == 比较的是值

因为 Java 只有值传递, 所以对于 == 来说, 不管是比较基本数据类型还是引用数据类型的变量, 其本质比较的都是值, 只不过引用类型变量存的值是对象的地址

equals() 不能用于判断基本数据类型的变量, 只能用于判断两个对象是否相等. equals() 方法存在于 Object 类中, 而 Object 类是所有类的直接或间接父类, 因此所有的类都有 equals() 方法

equals() 方法存在两种使用情况:

  • 类没有重写 equals() 方法 : 通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Objectequals()方法。
  • 类重写了 equals() 方法 : 一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。

hashCode() 有什么用?

hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 定义在 Object 类中, 这就意味着 Java 中的任何类都包含有 hashCode() 函数

散列表存储的是键值对(key-value), 它的特点是:能根据“键”快速的检索出对应的“值”. 这其中就利用到了散列码!(可以快速找到所需要的对象)

为什么要有hashCode()?

在一些容器中(例如 HashMap , HashSet ), 有了 hashCode() 之后, 判断元素是否在容器中的效率会更高

先用 hashCode() 判断, 如果一个 hashCode 对应有多个对象, 它会继续使用 equals() 来判断是否真的相同

为什么不只提供hashCode() 方法?

因为可能存在哈希碰撞 (什么是哈希碰撞? 这是数据结构与算法的内容, 自己回去补数据结构与算法吧 ) 的情况, 即多个不同的对象对应到同一个哈希值, 为了避免不同对象有相同哈希值导致误判为同一对象, 必须再提供一个 equals() 方法

简单来说就是

  • 如果两个对象的哈希值相等, 那么这两个对象不一定相等, 需要用 equals() 方法再做一次判断
  • 如果两个对象哈希值不等, 那么这两个对象一定不相等
  • 如果两个对象的哈希值相等且equals() 返回 true , 这两个对象是相等的

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

因为两个相等的对象的哈希值必须是相等的, 也就是说如果equals() 返回true, 说明这两个对象的哈希值也必须是相等的, 所以必须重写 hashCode()

如果重写了 equals() 方法却没重写 hashCode() 方法, 那么就会出现一种情况: equals() 返回true(两个对象应该是相等的) , 但是 hashCode() 的返回值也就是哈希值不一定相等

如果重写了 equals() 但是没有重写 hashCode() , 却使用了 HashMap 呢? 假设重写了 equals() 方法, 没有重写 hashCode() 方法, 那么会出现这样的情况: 两个对象在 equals() 方法返回了true, 但是它们的哈希值不同, 这就有可能两个相同的对象重复存储在了一个 HashMap 里面 (因为它们的哈希值不同, 存储的位置是不一样的)

String

String, StringBuffer, StringBuilder的区别?

可变性

String 是不可变的

StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类, 在 AbstractStringBuilder 中也是使用字符数组保存字符串, 不过没有使用 finalprivate 关键字修饰, 最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法.

abstract class AbstractStringBuilder implements Appendable, CharSequence {char[] value;public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;}//...
}

线程安全性

String 中的对象是不可变的, 也就可以理解为常量, 线程安全. AbstractStringBuilderStringBuilderStringBuffer 的公共父类, 定义了一些字符串的基本操作, 如 expandCapacityappendinsertindexOf 等公共方法. StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁, 所以是线程安全的. StringBuilder 并没有对方法进行加同步锁, 所以是非线程安全的。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结

  1. 操作少量数据: 使用 String
  2. 单线程操作大量数据: 使用 StringBuilder
  3. 多线程操作大量数据: 使用 StringBuffer

String为什么是不可变的

  1. 保存字符串的数组被 final 修饰且私有的, 并且 String 类没有提供修改字符串的方法
  2. String 类被 final 修饰所以它不能被继承, 进而避免子类破坏 String 不可变的情况

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

其实都可以

在 String 中, “+” 和 “+=” 两个运算符其实是通过 StringBuilder 调用 append() 方法实现的, 拼接完成之后再调用一个 toString() 方法返回一个 String. 学过C++的应该会意识到这里出现了重载运算符, 没错这两个运算符也是 Java 唯二的重载运算符

在 JDK9 之前, 如果在循环内使用 “+” 进行字符串拼接, 会导致产生大量的 StringBuilder . 所以在 JDK9 之前, 建议用 StringBuilder 进行拼接

当然, 现在这个问题已经解决了. JDK9 中, 字符串相加改为用动态方法 makeConcatWithConstants() 来实现, 这样就不会导致产生大量的 StringBuilder

String中的 equals() 和 Object中的 equals() 有什么区别?

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

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

1个或者2个

  1. 假如字符串常量池中没有 “abc” 的引用, 那么就会产生两个字符串对象, 其中一个存在字符串常量池
  2. 假如字符串常量池中有 “abc” 的引用, 那么只会在堆中创建一个字符串对象

String的intern方法有什么作用?

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

  • 如果字符串常量池中保存了对应的字符串对象的引用, 就直接返回该引用
  • 如果字符串常量池中没有保存了对应的字符串对象的引用, 那就在常量池中创建一个指向该字符串对象的引用并返回
// 在堆中创建字符串对象”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 类型的变量和常量做 “+” 运算时发生了什么?

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

在编译过程中, Javac 编译器(下文中统称为编译器)会进行一个叫做 常量折叠(Constant Folding) 的代码优化

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

也就是说, 对于 String str3 = "str" + "ing" , 编译器会优化为 String str3 = "string"

只有编译器在程序编译器可以确定值的常量才可以进行常量折叠

  • 基本数据类型以及字符串常量
  • final 修饰的基本数据类型和字符串变量
  • 字符串通过 “+” 拼接得到的字符串, 基本数据类型之间算术运算, 基本数据类型的位运算

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

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

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

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

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

相关文章

【每日力扣】491. 非递减子序列与122. 买卖股票的最佳时机 II

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害。 491. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少…

提升交付效率:Booking.com 金融技术团队的成功实践

Booking.com 金融技术业务部门的团队对其平台的后端和前端实施了一系列改进措施&#xff0c;并通过 DORA 指标将交付性能提高了一倍。此外&#xff0c;还使用了微前端 (MFE) 模式&#xff0c;将单体 FE 应用程序分解为多个可单独部署的分解应用程序。 2022 年年中&#xff0c;B…

GTC 2024,英伟达官宣四大量子突破

3月18日&#xff0c;英伟达&#xff08;NVIDIA&#xff09;公司宣布推出一项创新的云服务。该服务旨在帮助研究人员和开发人员在化学、生物学和材料科学等关键科学领域&#xff0c;推动量子计算的探索。 英伟达高性能计算和量子计算部门主管蒂姆科斯塔&#xff08;Tim Costa&am…

GIS设计与开发的学习笔记

目录 一、简答题 1.GeoDatabase数据模型结构类型与四种关系。 2.组件式GIS的基本思想是什么&#xff1f; 3.请简述创建空间书签的实现逻辑。 4.请问与地理要素编辑相关的类有哪些&#xff1f;&#xff08;列举至少五个类&#xff09; 5.利用ArcGIS Engine提供的栅格运算工…

恶劣天气对高速公路交通的影响

恶劣天气对高速公路交通的影响 高速低能见度会对安全驾驶造成以下影响&#xff1a; 降低驾驶员的感知能力&#xff1a;在低能见度条件下&#xff0c;驾驶员的视线距离缩短&#xff0c;难以看清周围的环境&#xff0c;包括道路状况、其他车辆和行人等。这会导致驾驶员对周围情况…

蓝桥杯 EDA 组 2023模拟+真题原理图解析

本文解析了标题内的原理图蓝桥杯EDA组真题&#xff0c;2021-2022 省赛真题/模拟题在上一篇文中。本文中重复或者是简单的电路节约篇幅不在赘述。 其中需要补充和计算原理图的题目解析都放在最下面 一、2023 年第十四届省赛模拟题1 1.1 Type-C 接口电路 通过 CH340N 将数据转化为…

解密Mysql数据库引擎:探究其背后的神秘力量(二)

本系列文章简介&#xff1a; 在本系列文章中&#xff0c;我们将从MySQL的基础知识入手&#xff0c;逐步深入到数据库引擎的内部机制。我们将详细介绍MySQL中常用的几种数据库引擎&#xff0c;包括InnoDB、MyISAM等&#xff0c;分析它们的优缺点以及适用场景。同时&#xff0c;我…

MySQL面试复习记录

一、mysql文章地址汇总 以下均为蓝云飘飘的文章&#xff1a; MySQL数据库&#xff08;一&#xff09;_写出sql语句,列出薪资比‘王海涛’的薪资高的所有员工,显示姓名,薪资-CSDN博客 MySQL数据库&#xff08;二&#xff09;_sql里的性别是什么代表-CSDN博客 ★★★★★ My…

Git Flow模型之美:优化团队协作开发流程

流程图 主要分支 master: 永远处在**即将发布(production-ready)状态**&#xff1b; develop: 最新的开发状态&#xff1b; 辅助分支 feature: 开发新功能的分支, 基于 develop, 完成后 merge 回 develop&#xff1b; release: 准备要发布版本的分支, 用来修复 bug. 基于 devel…

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密 上一篇某招聘软件的sig及sp参数被和谐掉了,所以懂得都懂啊! 因为web的api没有那么全,所以来看了下app的,ios的防护几乎没有,纸糊的一样,android端的有点复杂了,到最后我也没能完整的实现整个加密过程,我也只复现到DF…

Hadoop安装部署-单机版

Apache Hadoop是一个使用HDFS&#xff08;Hadoop Distributed File System&#xff09;分布式文件系统执行可靠的、规模化的分布式计算的开源项目&#xff0c;Hadoop是使用Java语言开发&#xff0c;其运行在Linux操作系统上集群规模最大支持几千个分布式节点&#xff0c;本文主…

【嵌入式——QT】QThread创建多线程

【嵌入式——QT】QThread创建多线程 概述主要函数图示代码示例 概述 QThread类提供不依赖于平台的管理线程的方法&#xff0c;一个QThread类的对象管理一个线程&#xff0c;一般从QThread继承一个自定义类&#xff0c;并重定义虚函数run()&#xff0c;在run()函数里实现线程需…

基于java的健身房管理系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本健身房管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&…

【MySQL】数据库的基础概念

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

Redis 更新开源许可证 - 不再支持云供应商提供商业化的 Redis

原文&#xff1a;Rowan Trollope - 2024.03.20 未来的 Redis 版本将继续在 RSALv2 和 SSPLv1 双许可证下提供源代码的免费和宽松使用&#xff1b;这些版本将整合先前仅在 Redis Stack 中可用的高级数据类型和处理引擎。 从今天开始&#xff0c;所有未来的 Redis 版本都将以开…

Vue.js前端开发零基础教学(二)

目录 前言 2.1 单文件组件 2.2 数据绑定 2.2.2 响应式数据绑定 2.3 指令 2.3.1 内容渲染指令 2.3.2 属性绑定指令 ​编辑 2.3.3 事件绑定指令 2.3.4 双向数据绑定指令 2.3.5 条件渲染指令 2.3.6 列表渲染指令 2.4 事件对象 2.5 事件修饰符 学习目标&am…

sentinel使用控制台实现

1、添加依赖 <!--整合控制台--><dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.0</version></dependency> 此项方法&#xff0…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

JVM第八讲:GC - Java 垃圾回收基础知识

GC - Java 垃圾回收基础知识 本文是JVM第八讲&#xff0c; Java 垃圾回收基础知识。垃圾收集主要是针对堆和方法区进行&#xff1b;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的&#xff0c;只存在于线程的生命周期内&#xff0c;线程结束之后也会消失&#xff0…

单片机第四季-第二课:uCos2源码-BSP

1&#xff0c;初始uCos2 文件中uC开头的为uCos相关的。 2&#xff0c;uCos2源码工程建立 建立Source Insight工程 寻找main函数 (1)RTOS其实就是一个大的裸机程序&#xff0c;也是从main开始运行的 (2)main之前也是有一个汇编的启动文件的 (3)main中调用了很多初始化函数 bsp部…