JAVA基础知识

什么是字节码?

在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以, Java 程序运行时相对来说还是高效的(不过,和 C、 C++,Rust,Go 等语言还是有一定差距的),而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。

为什么说 Java 语言"编译与解释并存"?

这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。

自增自减运算符

++-- 运算符可以放在变量之前,也可以放在变量之后:

1.前缀形式(例如 ++a--a):先自增/自减变量的值,然后再使用该变量,例如,b = ++a 先将 a 增加 1,然后把增加后的值赋给 b

2.后缀形式(例如 a++a--):先使用变量的当前值,然后再自增/自减变量的值。例如,b = a++ 先将 a 的当前值赋给 b,然后再将 a 增加 1。

移位运算符(高效、节省内存)

移位操作中,被操作的数据被视为二进制数,移位就是将其向左或向右移动若干位的运算。

移位运算符最常用于快速乘以或除以 2 的幂次方。除此之外,它还在以下方面发挥着重要作用:

  1. 位字段管理:例如存储和操作多个布尔值。
  2. 哈希算法和加密解密:通过移位和与、或等操作来混淆数据。
  3. 数据压缩:例如霍夫曼编码通过移位运算符可以快速处理和操作二进制数据,以生成紧凑的压缩格式。
  4. 数据校验:例如 CRC(循环冗余校验)通过移位和多项式除法生成和校验数据完整性。
  5. 内存对齐:通过移位操作,可以轻松计算和调整数据的对齐地址。

Java 中有三种移位运算符:

  • << :左移运算符,向左移若干位,高位丢弃,低位补零。x << n,相当于 x 乘以 2 的 n 次方(不溢出的情况下)。
  • >> :带符号右移,向右移若干位,高位补符号位,低位丢弃。正数高位补 0,负数高位补 1。x >> n,相当于 x 除以 2 的 n 次方。
  • >>> :无符号右移,忽略符号位,空位都以 0 补齐。

移位操作符实际上支持的类型只有 intlong,编译器在对 shortbytechar 类型进行移位前,都会将其转换为 int 类型再操作。


  1. continue:指跳出当前的这一次循环,继续下一次循环。
  2. break:指跳出整个循环体,继续执行循环下面的语句。
  3. return;:直接使用 return 结束方法执行,用于没有返回值函数的方法
  4. return value;:return 一个特定值,用于有返回值函数的方法

Java 中有 8 种基本数据类型,分别为:

  • 6 种数字类型:
    • 4 种整数型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean
基本类型 位数 字节 默认值 取值范围
byte 8 1 0 -128 ~ 127
short 16 2 0 -32768(-2^15) ~ 32767(2^15 - 1)
int 32 4 0 -2147483648 ~ 2147483647
long 64 8 0L -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1)
char 16 2 'u0000' 0 ~ 65535(2^16 - 1)
float 32 4 0f 1.4E-45 ~ 3.4028235E38
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false true、false

基本类型和包装类型的区别?基本类型和包装类型的区别 基本类型和包装类型的区别?

  • 用途:除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可用于泛型,而基本类型不可以。

  • 存储方式:基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰)存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。

  • 占用空间:相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。

  • 默认值:成员变量包装类型不赋值就是 null,而基本类型有默认值且不是 null

  • 比较方式:对于基本数据类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。

  • 装箱(Boxing):将基本类型用它们对应的引用类型包装起来;
  • 拆箱(Unboxing):将包装类型转换为基本数据类型;

成员变量与局部变量的区别

  • 语法形式:从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。

  • 存储方式:从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。

  • 生存时间:从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。

  • 默认值:从变量是否有默认值来看,成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。

成员变量有默认值核心原因是为了保证对象状态的安全和可预测性。

成员变量和局部变量在这个规则上不同,主要是因为它们的生命周期不一样,导致了编译器对它们的"控制力"也不同。

  • 局部变量只活在一个方法里,编译器能清楚地看到它是否在使用前被赋值,所以编译器会强制你必须手动赋值,否则就报错。

  • 成员变量是跟着对象走的,它的值可能在构造函数里赋,也可能在后面的某个 setter 方法里赋。编译器在编译时无法预测它到底什么时候会被赋值。

并且,如果一个变量没有被初始化,它的内存里存放的就是"垃圾值"------之前那块内存遗留下的任意数据。如果程序读取并使用了这个垃圾值,就会产生完全不可预测的结果,比如一个数字变成了随机数,一个对象引用变成了非法地址,这会直接导致程序崩溃或出现诡异的 bug。

为了避免你拿到一个含有"垃圾值"的危险对象,Java 干脆为所有成员变量提供了一个安全的默认值(如 null 或 0),作为一种安全兜底机制。

静态变量也就是被 static 关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。也就是说,静态变量只会被分配一次内存,即使创建多个对象,这样可以节省内存。


字符型常量和字符串常量的区别

  • 形式 : 字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。

  • 含义 : 字符常量相当于一个整型值(ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。

  • 占内存大小:字符常量只占 2 个字节; 字符串常量占若干个字节。

静态方法和实例方法有何不同

调用方式:调用静态方法无需创建对象

访问类成员是否存在限制:静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制

  • 对象的相等一般比较的是内存中存放的内容是否相等。
  • 引用相等一般比较的是他们指向的内存地址是否相等。
区别点 重载 (Overloading) 重写 (Overriding)
发生范围 同一个类中。 父类与子类之间(存在继承关系)。
方法签名 方法名必须相同 ,但参数列表必须不同(参数的类型、个数或顺序至少有一项不同)。 方法名、参数列表必须完全相同
返回类型 与返回值类型无关,可以任意修改。 子类方法的返回类型必须与父类方法的返回类型相同 ,或者是其子类
访问修饰符 与访问修饰符无关,可以任意修改。 子类方法的访问权限不能低于父类方法的访问权限。(public > protected > default > private)
绑定时期 编译时绑定或称静态绑定 运行时绑定 (Run-time Binding) 或称动态绑定

构造方法具有以下特点:

  • 名称与类名相同:构造方法的名称必须与类名完全一致。

  • 没有返回值:构造方法没有返回类型,且不能使用 void 声明。

  • 自动执行:在生成类的对象时,构造方法会自动执行,无需显式调用。

构造方法不能被重写(override),但可以被重载(overload)。因此,一个类中可以有多个构造方法,这些构造方法可以具有不同的参数列表,以提供不同的对象初始化方式。

封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间,提高我们的开发效率。

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

接口和抽象类的共同点

实例化:接口和抽象类都不能直接实例化,只能被实现接口或继承抽象类后才能创建具体的对象

抽象方法:接口和抽象类都可以包含抽象方法。抽象方法没有方法体,必须在子类或实现类中实现。

接口和抽象类的区别

  • 设计目的:接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。

  • 继承和实现:一个类只能继承一个类(包括抽象类),因为 Java 不支持多继承。但一个类可以实现多个接口,一个接口也可以继承多个其他接口。

  • 成员变量:接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值。抽象类的成员变量可以有任何修饰符(private, protected, public),可以在子类中被重新定义或赋值。

  • 方法:

    • Java 8 之前,接口中的方法默认是 public abstract,也就是只能有方法声明。自 Java 8 起,可以在接口中定义 default(默认) 方法和 static(静态)方法。 自 Java 9 起,接口可以包含 private 方法。

    • 抽象类可以包含抽象方法和非抽象方法。抽象方法没有方法体,必须在子类中实现。非抽象方法有具体实现,可以直接在抽象类中使用或在子类中重写。

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

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

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

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

hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是:ObjecthashCode() 方法是本地方法,也就是用 C 语言或 C++ 实现的。

  • 操作少量的数据: 适用 String
  • 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

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

  1. 保存字符串的数组被 final 修饰且为私有的,并且 String 类没有提供/暴露修改这个字符串的方法。
  2. String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

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

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

  • Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checked Exception(受检查异常,必须处理) 和 Unchecked Exception(不受检查异常,可以不处理)。

  • ErrorError 属于程序无法处理的错误

  • ClassNotFoundException 是 Exception,发生在使用反射等动态加载时找不到类,是可预期的,可以捕获处理。
  • NoClassDefFoundError 是 Error,是编译时存在的类,在运行时链接不到了(比如 jar 包缺失),是环境问题,导致 JVM 无法继续。

泛型一般有三种使用方式:泛型类泛型接口泛型方法

Java 反射 (Reflection) 是一种在程序运行时,动态地获取类的信息并操作类或对象(方法、属性)的能力灵活性和动态性、解耦合和通用性、性能开销、安全性问题、代码可读性和维护性

相关推荐
就叫_这个吧2 小时前
Activiti7工作流入门+案例应用
java·activiti7·工作流
Z_W_H_2 小时前
minikube start --driver=docker --memory=4000mb出现PROVIDER_DOCKER_NEWGRP
java·docker·minikube
折哥的程序人生 · 物流技术专研2 小时前
第3篇:手写促销策略引擎(满减、打折、立减)
java·设计模式·策略模式·开闭原则·编程实战·扩充系列·促销引擎
xxie1237942 小时前
Python装饰器与语法糖
开发语言·python
郑州光合科技余经理2 小时前
海外外卖平台源码改造实战——多语言核心代码实现
java·开发语言·前端·后端·mysql·架构·php
耀耀_很无聊3 小时前
12_Redis Lua 脚本踩坑:为什么 tonumber(ARGV[x]) 会得到 nil?
java·redis·lua
凯瑟琳.奥古斯特3 小时前
力扣1012数位DP解法详解
开发语言·c++·算法·leetcode·职场和发展
Railshiqian3 小时前
UserPickerActivity 内部逻辑分析
开发语言·python
奶油话梅糖3 小时前
锐捷常用命令速查表
java·网络·windows