😊Java 简介
一、简介
1.1 Java 是什么
Java 是一种广泛使用的面向对象编程语言,具有卓越的通用性、高效性、跨平台性和安全性,从语法和结构上,Java 具有清晰的语法规则和简洁的代码风格,这使得开发者能够更轻松地编写和理解代码。
1.2 Java 诞生背景
Java 语言诞生于 1991 年,起初起名为 Oak 语言,是 Sun 公司为一些消费性电子产品(如有线电视交换盒、PDA 等)所设计的一个通用环境。其诞生的背景主要包括以下几点:
当时 Sun 公司的一个计算机开发小组,包括 James Gosling(Java之父) 等人,想要开发一款用于控制嵌入在这些设备微处理器的计算机语言。他们最初考虑直接扩展 C++,但后来发现 C++ 无法满足需求,于是便基于 C++ 重新开发了名为 Oak 的语言。
在 Java 出现以前,互联网上的信息内容主要是一些乏味死板的 HTML 文档,人们迫切希望在网页中看到一些交互式的内容,开发人员也希望能创建一类无需考虑软硬件平台就可以执行的应用程序,并且要有极大的安全保障。而传统的编程语言对此显得无能为力。
1994 年起,Sun 公司的工程师们开始将 Oak 技术应用于 Web 上,并开发出了 HotJava 的第一个版本。1995 年,Sun 公司正式以 Java 这个名字推出该语言后,受到了广大 Web 开发人员的欢迎,因为它正好满足了用户和开发人员对于交互式网页内容和跨平台应用程序的需求。
Oak 改名为 Java 的原因是,商标搜索显示 Oak 已被一家显卡制造商注册,所以团队需要寻找一个新名字,于是 Gosling 就开始到处找灵感,结果看见了同事桌上有一瓶咖啡,包装上写着 Java,自此以后,Oak 就改名为 Java 了。
1.3 Java 三个方向
-
Java SE(Java Platform Standard Edition,Java 平台标准版)这是 Java 平台的核心,提供了 Java 语言的基础类库和核心功能,是学习 Java 语言的基础,包含了 Java 语言的基本语法、面向对象编程的概念、基本的输入输出操作等。
-
Java EE(Java Platform Enterprise Edition,Java 平台企业版):基于 Java SE,专门为企业级应用开发而设计的。
-
Java ME(Java Platform Micro Edition,Java 平台微型版):针对于资源受限的设备,如移动电话、PDA(个人数字助理)等。具有较小的运行时环境和精简的类库,以适应设备的内存和处理能力限制。
例如,一个简单的计算器应用,如果是在桌面运行,可能会基于 Java SE 开发;一个大型的在线购物网站,则很可能基于 Java EE 来构建;而一款手机上的小游戏,可能会使用 Java ME 进行开发。
二、特性
2.1 面向对象
强调将现实世界中的事物抽象为对象,通过对象之间的交互来实现程序的功能,支持封装、继承和多态,提高了代码的可复用性、可维护性和可扩展性。
2.2 跨平台性
Java 程序编译后生成的字节码可以在不同的操作系统和硬件平台上运行,只要有相应的 Java 运行环境(JRE),这使得开发者能够一次编写,到处运行,大大降低了开发和维护成本。
2.3 垃圾回收机制
Java 虚拟机会自动管理内存的分配和回收,开发者无需手动释放不再使用的内存,这降低了内存泄漏和指针错误的风险,提高了程序的稳定性。
2.4 安全性
具有严格的访问控制机制,防止非法的内存访问和代码篡改,支持数字签名和加密技术,保障了代码和数据的安全性。
2.5 健壮性
在编译和运行时进行严格的类型检查,减少错误的发生,处理异常的机制使得程序在遇到错误时能够更优雅地恢复或退出。
2.6 多线程
支持多线程编程,能够同时执行多个任务,提高程序的并发性和效率,方便开发者实现并发操作,如同时处理多个网络连接或后台任务。
2.7 丰富的类库
Java 拥有庞大且丰富的标准类库,涵盖了各种常见的功能,如网络编程、文件操作、数据库连接等,开发者可以直接使用这些类库,节省了大量的开发时间。
2.8 混合型语言
Java 是混合型的编程语言,也称为解释型与编译型结合的语言,它首先将源代码 编译 成字节码,在运行时,Java 虚拟机(JVM)会 解释 执行字节码。
三、应用领域
3.1 Web 开发
许多大型的 Web 应用程序,如电商网站、社交媒体平台等,都是基于 Java 技术构建的。例如,淘宝的部分后端服务就使用了 Java 来处理高并发的用户请求。
3.2 企业级应用
银行、保险等行业的核心业务系统通常采用 Java 开发。比如各大银行的在线交易系统,依靠 Java 的稳定性和安全性保障业务的正常运行。
3.3 移动开发
Android 应用的开发主要依赖 Java 语言。像微信、支付宝等热门的 Android 应用,其底层代码有很大一部分是用 Java 编写的。
3.4 大数据处理
Hadoop 等大数据处理框架中的许多组件是用 Java 实现的。Java 在处理大规模数据时的性能和稳定性表现出色。
3.5 游戏开发
一些桌面游戏和手机游戏的服务器端逻辑使用 Java 编写。例如《我的世界》的服务器端就可以用 Java 来实现。
3.6 科学计算和金融领域
在金融交易系统和科学计算模型中,Java 也发挥着重要作用。比如高频交易系统和气象预测模型。
四、JDK、JRE和JVM
4.1 JDK
开发工具包(Java Development Kit),包含了 Java 的开发工具和 JRE(运行时环境)。所以安装了JDK,就无需再单独安装 JRE 了。其中的开发工具包括:编译工具(javac.exe),运行工具(java.exe),打包工具(jar.exe)等。
4.2 JRE
运行时环境(Java Runtime Environment),包含了 JVM(Java 虚拟机)和核心类库。JRE 是 Java 程序运行的基础环境,为 Java 应用程序的部署和使用提供了必要的支持。
4.3 JVM
Java 虚拟机(Java Virtual Machine),JVM 是实现 Java 语言 “一次编写,到处运行” 的关键。它可以在不同的操作系统上运行,将 Java 字节码转换为特定操作系统能够理解的机器码。例如,同一个 Java 程序可以在 Windows、Linux 和 macOS 等不同操作系统上运行,无需为每个平台单独编写代码,只要提供对应的系统的 JVM。
4.4 包含关系
五、长期支持版本(LTS)
截至 2024 年 8 月,Java 的长期支持版本包括 Java 8、Java 11、 Java 17 和 Java 21。
在选择使用哪个版本时,需要考虑项目需求、稳定性要求以及对新特性的需求等因素。如果项目对稳定性要求较高,且不需要使用最新版本的特性,那么 Java 8 或 Java 11 可能是合适的选择;
如果希望利用最新的语言特性和性能优化,并且可以接受更频繁的升级,那么可以考虑使用 Java 17 或 Java 21。同时,还需考虑相关框架和库对 Java 版本的要求。
😍开发环境搭建
一、简介
配置 Java 环境变量主要有以下几个关键原因:
-
方便系统识别 Java 命令:当我们在命令行中输入诸如 java 或 javac 这样的命令时,系统需要知道去哪里找到对应的可执行文件。通过配置环境变量,系统能够快速定位到 Java 相关的命令工具,无需我们每次都输入完整的路径。
-
支持跨目录执行 Java 程序:即使我们在不同的目录下开发,配置好环境变量后,系统都能正确找到 Java 工具来编译和运行代码。假设我们在一个复杂的项目结构中,各个子目录都有 Java 代码需要编译,如果没有配置环境变量,就无法在当前目录直接执行编译命令。
-
便于其他应用调用 Java:有些第三方应用程序可能需要调用 Java 来执行特定的任务。配置了环境变量后,这些应用程序能够顺利找到并使用 Java 运行环境,确保整个系统的协同工作正常。比如,某些自动化脚本或持续集成工具,需要依赖正确配置的 Java 环境来完成相关的任务。
二、安装 JDK
JDK(Java Development Kit)即 Java 开发工具包,它是 Java 语言的核心软件开发工具,包含了 JRE(Java 运行时环境)和 Java 开发工具,以支持 Java 程序的开发、编译、调试和运行。
Oracle 官网:https://www.oracle.com/cn/java/technologies/downloads/
推荐大家下载 JDK17,方便后续的学习。
安装 JDK 的时候,可以不放在 C盘,需要注意的是,安装路径不要有中文和空格。
三、配置环境变量
进入刚下载 JDK 的目录,将文件路径复制下来。
- 右键点击 此电脑,选择 属性;
- 点击 高级系统设置;
- 在弹出的窗口中,点击 环境变量;
- 在 系统变量 中,新建 JAVA_HOME 变量:
- 变量名:JAVA_HOME
- 变量值:JDK 的安装路径
- 在 系统变量 中,编辑 Path 变量:
先删除掉这个变量,这个是安装 JDK 自带的,我们配置自己的变量就好。
点击新建,输入:%JAVA_HOME%\bin,最后点击确定。
四、验证是否成功
打开命令行窗口(Windows 系统中按下 Win + R 键,输入cmd 并回车),输入命令:java -version:
如果能够正确显示 Java 的版本信息,则说明开发环境搭建成功。
希望以上步骤对您搭建 Java 开发环境有所帮助。如果在过程中遇到问题,可以在评论区留言。
❤️注释
一、简介
在计算机语言中,注释是计算机语言的一个重要组成部分,用于在源代码中解释代码的作用,可以增强程序的可读性,可维护性。Java 注释是一种在 Java 程序中用于提供代码功能说明的文本,注释不会被编译器包含在最终的可执行程序中,因此不会影响程序的运行。
Java 注释主要有三种类型:
- 单行注释
- 多行注释
- 文档注释
编写注释是一个良好的编程习惯,它们可以帮助程序员更容易地理解代码的用途和功能,并且在团队协作中非常有用。但需要注意的是代码的注释不是越多越详细越好,要尽量规范和优化自己的代码来减少不必要的注释,减轻阅读者的负担。
二、单行注释
使用 //
开头,其后的内容直到行尾都被视为注释,一般是用于解释某个单行代码的作用。
public class Main {
public static void main(String[] args) {
// 这是一个单行注释
}
}
三、多行注释
以 /*
开头, */
为尾,使用 Ctrl + Shift + /
可以快速生成多行注释,在多行注释内所有的内容都会被视为注释,一般用于解释一段代码的作用。
public class Main {
public static void main(String[] args) {
/*
这是一个多行注释
用于解释多行代码
*/
}
}
四、文档注释
以 /**
开始,以 */
结束,出现在类、方法、字段等的声明前面,用于生成代码文档,一帮用于生成 Java 开发文档。
public class Main {
/**
* @param a 参数一
* @param b 参数二
* @return 返回两个参数的和
*/
public static int sum(int a, int b) {
return a + b;
}
}
文档注释的格式通常包含一些特定的标签,如 @param 用于描述方法参数,@return 用于描述返回值,@throws 用于描述可能抛出的异常等等,这些标签有助于生成清晰的API文档,以便其他开发者能够更好地理解和使用你的代码。
更多文档注释的内容可以参考:Java 文档注释 。
😎标识符和关键字
一、简介
在我们编写程序的时候,会需要大量地为程序中的代码取名字,例如,类、变量、方法等,这些名字就是我们所说的标识符,而在 Java 中,对于一些标识符,Java 语言已经赋予了其特殊的含义,只能用于特定的地方,这些特殊的标识符就是 Java 的关键字 。
二、标识符
Java 所有的组成部分都需要名字,包名、类名、变量名以及方法名都被称为标识符,关于 Java 标识符,有以下几点需要注意:
标识符的命名规则(硬性要求)
- 所有的标识符都以字母( A - Z 或 a - z )、数字( 0 - 9 )、美元符( $ )或者下划线( _ )组成;
- 标识符不能以数字开头,且不能含有空格;
- 标识符不能使用关键字或者保留字,但是可以包含关键字和保留字;
- 标识符是区分大小写的。
标识符的命名规范
- 包:全部小写,用点号( . )隔开,并且域名反写,如:com.hui.mapper(mapper 指的是包的功能);
- 类、接口:遵循大驼峰命名法,每个单词的首字母大写,如:UserMapper ;
- 变量、方法:遵循小驼峰命名法,第一个单词首字母小写,其后的单词首字母大写,如:getName() ;
- 常量:单词的所有字母都大写,单词与单词之间有下划线( _ )隔开,如:MAX_SPEED 。
Tips
:自己定义的标识符要使用有意义的名字,能够准确地反映变量的用途,避免使用单个字符或无意义的缩写。
三、关键字
关键字在 Java 语言中具有特定的用途和语法意义,你不能将它们用作变量名、方法名或类名等标识符。如果不小心使用了关键字作为标识符,编译器会报错,下表列出了 Java 的关键字:
类别 | 关键字 | 含义 |
---|---|---|
访问控制修饰符 | private | 私有的 |
protected | 受保护的 | |
public | 公有的 | |
类,方法和变量修饰符 | abstract | 抽象的 |
class | 定义类 | |
extends | 继承 | |
final | 最终的 | |
implements | 实现(接口) | |
interface | 定义接口 | |
native | 本地方法(这个方法的实现是其他语言的) | |
new | 创建对象 | |
static | 静态的 | |
strictfp | 用于确保浮点运算的精确性 | |
synchronized | 线程同步 | |
transient | 被标记的变量不会被序列化 | |
volatile | 保证变量的可见性和禁止指令重排序 | |
enum | 定义枚举类型 | |
程序控制语句 | break | 终止本层循环 |
case | 定义一个常量值匹配 switch 分支 | |
continue | 跳过本次循环,继续下一次循环 | |
do | 运行 | |
else | 否则 | |
for | 循环 | |
if | 如果 | |
instanceof | 用于判断当前引用是哪个对象 | |
return | 返回 | |
switch | 根据常量值执行分支 | |
while | 循环 | |
default | 默认 | |
assert | 断言 | |
错误处理 | try | 捕获异常 |
catch | 捕捉异常 | |
throw | 抛出一个异常 | |
throws | 可能被抛出的异常 | |
finally | 有没有异常最后都会执行 | |
包相关 | import | 引入 |
package | 包 | |
基本数据类型 | boolean | 布尔型 |
char | 字符型 | |
double | 双精度浮点 | |
float | 单精度浮点 | |
int | 整数 | |
long | 长整型 | |
byte | 字节型 | |
short | 短整型 | |
变量引用 | this | 当前对象 |
super | 父类对象 | |
void | 无返回值 | |
保留字 | goto | 在 Java 中还没被定义的关键字,不能使用 |
const | 在 Java 中还没被定义的关键字,不能使用 |
Tips
:在 Java 中,所有的关键字都是小写的,在 IDEA 中会以特殊颜色显示。
default 这个关键字比较特殊,既属于程序控制语句,也属于类,方法和变量修饰符,还属于访问控制修饰符。
- 在程序控制语句中,当在 switch 中匹配不到任何情况时,可以使用 default 来编写默认匹配的情况。
- 在类,方法和变量修饰符中,从 JDK8 开始引入了默认方法,可以使用 default 关键字来定义一个方法的默认实现。
- 在访问控制修饰符中,如果一个方法前没有任何修饰符,则默认会有一个修饰符 default,但是如果加上了这个修饰符就会报错。
⚠️ 注意:虽然 true,false 和 null 看起来像关键字但实际上他们是字面量,所以不可以将其作为标识符来使用。
🥹修饰符
一、简介
在 Java 中,修饰符可以分为访问修饰符和非访问修饰符,一般是放在语句的最前端,用于定义类、方法、变量等的访问权限或其他特性。
二、访问修饰符
2.1 public(公有的)
这是最宽松的访问级别。被 public
修饰的类、方法或变量可以被任何其他类访问,无论它们是否在同一个包中,通常用于定义对外提供服务的接口或重要的复用类。
2.2 protected(受保护的)
被 protected
修饰的成员可以被同一个包中的类访问,也可以被不同包下的子类访问,保护成员在一定程度上平衡了封装性和继承性的需求。
2.3 default(默认的)
当没有显式指定任何访问修饰符时,成员具有默认的访问权限。具有默认访问权限的成员只能被同一个包中的其他类访问,适用于在同一个包内协作的类之间共享的实现细节。
⚠️注意:default
这个关键字比较特殊,既属于程序控制语句,也属于类,方法和变量修饰符,还属于访问控制修饰符。
- 在程序控制语句中,当在 switch 中匹配不到任何情况时,可以使用 default 来编写默认匹配的情况。
- 在类,方法和变量修饰符中,从 JDK8 开始引入了默认方法,可以使用 default 关键字来定义一个方法的默认实现。
- 在访问控制修饰符中,如果一个方法前没有任何修饰符,则默认会有一个修饰符 default,但是如果加上了这个修饰符编译器就会报错。
2.4 private(私有的)
这是最严格的访问级别。被 private
修饰的成员只能在其所属的类内部访问,它提供了最高级别的封装性,隐藏了类的内部实现细节,防止外部类直接访问和修改。
2.5 总结(访问权限)
下表列出了被访问修饰符修饰的类、方法、变量等的访问权限:
访问修饰符 | 本类 | 同一包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | |
default(默认) | √ | √ | ||
private | √ |
三、非访问修饰符
3.1 static(静态的)
静态变量
-
共享特性:由于静态变量属于类本身,而不是类的对象,所以无论创建多少个类的实例,它们都共享同一个静态变量。这在需要维护一些全局状态或者统计信息时非常有用。
-
访问方式:可以通过类名直接访问静态变量,无需创建对象。这种访问方式使得静态变量在整个程序中都很容易被访问到,提高了代码的可读性和可维护性。
静态方法
-
独立性:静态方法属于类本身,不依赖于类的实例。这意味着静态方法可以在没有创建类的对象的情况下被调用,非常适合执行一些与类相关的通用操作,而不需要涉及类的具体实例状态。
-
访问限制:在静态方法中只能访问静态成员(包括静态变量和静态方法),不能直接访问实例成员。这是因为静态方法在类加载时就存在了,而实例成员只有在创建对象后才存在。
静态代码块
-
初始化时机:静态代码块在类加载时执行,且只执行一次。这使得静态代码块非常适合进行一些一次性的初始化操作,比如初始化静态变量、加载资源文件等。
-
顺序执行:如果一个类中有多个静态代码块,它们会按照在代码中的顺序依次执行。这可以用于确保一些初始化操作的顺序性。
3.2 final(最终的)
修饰变量
-
基本数据类型变量:修饰基本数据类型的变量时,一旦变量被初始化,它的值就不能再被改变,否则编译器报错。这就像给变量设定了一个不可更改的常量值。
-
引用数据类型变量:对于引用数据类型的变量,其引用不能再指向其他对象。这意味着一旦这个变量被赋值为一个对象的引用,它就不能再被重新赋值为另一个对象的引用。但是,这个被引用的对象的内容是可以修改的。
修饰方法
- 禁止重写:方法被修饰时,它不能被子类重写。这在一些情况下非常有用,比如当一个方法的实现是确定的,不希望在子类中被改变时,这个时候就可以使用。
修饰类
- 禁止继承:类被修饰时,这个类不能被继承,即不能有子类。这可以确保这个类的实现是最终的,不能被扩展或修改,有时候,为了防止恶意的继承和修改,可以将一些关键的类标记为final。这样可以确保这些类的行为是可靠的,不会被意外地破坏。
3.3 abstract(抽象的)
抽象类
-
不能创建对象:抽象类不能被实例化,即不能使用 new 关键字来创建抽象类的对象。这是因为抽象类通常只是作为一个框架,定义了一些共同的属性和方法,但可能缺少具体的实现。
-
提供框架:抽象类通常包含抽象方法和非抽象方法。非抽象方法可以提供一些通用的实现,而抽象方法则为子类定义了必须实现的行为,为一组相关的类提供了一个共同的结构和行为规范,有助于提高代码的可维护性和可扩展性。
抽象方法
-
没有方法体:抽象方法只有方法签名(方法的名称、参数类型和参数顺序),没有具体的方法体。这意味着抽象方法只定义了方法的名称、参数列表和返回类型,但没有具体的实现代码。
-
强制子类实现:包含抽象方法的类必须声明为抽象类。当一个子类继承一个抽象类时,它必须实现父类中的所有抽象方法,除非子类本身也是抽象类。这样可以确保子类具有特定的行为,满足抽象类所定义的约定。
3.4 synchronized(同步的)
同步方法
-
作用机制:方法被修饰时,它会在方法上实现同步。这意味着在同一时刻,只能有一个线程可以执行这个方法。当一个线程进入同步方法时,它会获取该方法所属对象的内置锁。其他线程如果也想执行这个方法,就必须等待当前线程释放这个锁。
-
适用场景:适用于多个线程需要同时访问一个对象的某个方法,并且这个方法对对象的状态进行修改的情况。通过同步方法可以确保对对象状态的修改是线程安全的,避免数据不一致的问题。
同步代码块
-
锁对象:在同步代码块中,需要指定一个唯一的锁对象。这个锁对象可以是任何对象,但通常选择一个专门用于同步的对象,以避免与其他可能的锁对象冲突。
-
作用范围控制:同步代码块可以更精确地控制同步的范围,只对需要同步的关键代码部分进行同步,而不是整个方法。这样可以减少同步带来的性能开销,提高程序的执行效率。
性能考虑
- 性能下降原因:过度使用 synchronized 可能会导致性能下降。这是因为当一个线程获取 synchronized 锁后,其他线程必须等待这个锁被释放,这会导致线程阻塞和切换,增加了系统的开销。特别是在高并发的情况下,如果大量线程竞争同一个锁,可能会导致线程频繁地阻塞和唤醒,从而降低程序的执行效率。
- 替代方案选择:在实际应用中,需要根据具体的业务场景和性能需求,合理地使用或者选择其他更高效的同步机制。例如,ReentrantLock 是一种可重入锁,它提供了与 synchronized 类似的功能,但在一些情况下可能具有更好的性能。此外,还可以使用并发容器(如ConcurrentHashMap)、原子类(如AtomicInteger)等非阻塞的同步机制来提高程序的并发性能。
3.5 transient(瞬态的)
防止敏感信息序列化
-
保护隐私:在某些情况下,对象中可能包含敏感信息,如用户密码、加密密钥等。这些信息不应该被序列化并存储在磁盘上或通过网络传输,以防止信息泄露。通过将包含敏感信息的变量声明为 transient,可以确保在对象序列化时,这些变量不会被包含在序列化的数据中。
-
符合安全规范:在处理敏感数据时,遵循安全最佳实践通常要求对敏感信息进行特殊处理。使用 transient 关键字是一种简单而有效的方法,可以确保敏感信息不会被意外地序列化。
优化序列化效率
- 减少数据量:有些变量在反序列化时可以重新计算或获取,或者不是关键的状态信息。将这些变量标记为 transient 可以减少序列化后的数据量,从而提高序列化和反序列化的效率。
3.6 volatile(易变的)
保证可见性
-
多线程同步问题:在多线程环境中,每个线程都有自己的工作内存,线程对变量的操作首先在自己的工作内存中进行,然后再刷新到主内存中。如果没有适当的同步机制,一个线程对变量的修改可能不会立即被其他线程看到,这就可能导致数据不一致的问题。
-
作用机制:变量被修饰时,Java 内存模型会确保所有线程看到这个变量的最新值。每次读取这个变量时,都会直接从主内存中读取,而不是从线程自己的工作内存中读取。每次写入这个变量时,都会立即刷新到主内存中,使得其他线程能够看到这个修改。
禁止指令重排序
-
指令重排序的影响:Java 编译器和处理器为了提高性能,可能会对代码的执行顺序进行重排序。在单线程环境中,这种重排序通常不会影响程序的正确性。但是,在多线程环境中,指令重排序可能会导致一些难以察觉的错误。
-
作用机制:变量被修饰时,Java 内存模型会禁止与其他内存操作进行重排序。这确保了对 volatile 变量的读写操作按照程序代码的顺序执行,从而避免了由于指令重排序而导致的错误。
😰基本数据类型
一、简介
在 Java 中,会根据变量的数据类型为变量分配存储空间,被分配的空间只能用来储存该类型数据,不同的数据类型会占用不同的内存空间。合理的使用可以避免空间的浪费,确保数据的准确性,便于理解变量的用途,用于表示最基本的数据形式。
二、基本数据类型
Java 中有 8 种基本数据类型,分别为:
- 6 种数值类型:
- 4 种整数型:byte、short、int、long
- 2 种浮点型:float、double
- 1 种字符类型:char
- 1 种布尔型:boolean。
这 8 种基本数据类型的默认值以及所占空间的大小如下表:
基本类型 | 字节数 | 默认值 | 取值范围 |
---|---|---|---|
byte | 1 | 0 | -27 ~ 27 -1 |
short | 2 | 0 | -215 ~ 215 -1 |
int | 4 | 0 | -231 ~ 231 -1 |
long | 8 | 0L | -263 ~ 263 -1 |
float | 4 | 0.0F | 1.4E-45 ~ 3.4028235E38 |
double | 8 | 0.0d | 4.9E-324 ~ 1.7976931348623157E308 |
char | 2 | ‘u0000’ | 0 ~ 65535 |
boolean | 1 | false | true、false |
可以看到,像 byte、short、int、long能表示的最大正数都减 1 了。这是为什么呢?这是因为在二进制补码表示法中,最高位是用来表示符号的(0 表示正数,1 表示负数),其余位表示数值部分。所以,如果我们要表示最大的正数,我们需要把除了最高位之外的所有位都设为 1。如果我们再加 1,就会导致溢出,变成一个负数。
⚠️注意:
- Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为 int 类型解析。
- Java 里使用 float 类型的数据一定要在数值后面加上 f 或 F,否则将作为 double 类型解析。
这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean 。
三、数据类型装换
3.1 自动类型转换
当把一个取值范围小的数据类型的值赋给一个取值范围大的数据类型的变量时,会自动进行类型转换。
public class Main {
public static void main(String[] args) {
byte b = 10;
// 会自动将 byte 转换为 int
int i = b;
}
}
自动类型转换遵循的规则是从低精度向高精度转换,常见的转换顺序为:
低 --------------------------------------------- 高
byte, short, char -> int -> long -> float -> double
3.2 强制类型转换
当把一个取值范围大的数据类型的值赋给一个取值范围小的数据类型的变量时,需要进行强制类型转换。但强制类型转换可能会导致数据丢失或精度降低。
public class Main {
public static void main(String[] args) {
double d = 10.5;
// 强制将 double 转换为 int 类型,小数部分会被抹去,而不是四舍五入
int i = (int) d;
}
}
在进行强制类型转换时,需要特别小心,确保转换的结果是符合预期的,否则可能会出现意外的结果。
public class Main {
public static void main(String[] args) {\
// 二进制:00000000 00000000 00000001 00000001
int i = 257;
// 二进制:00000001
byte b = (byte) i;
System.out.println("强制转换后的值:" + b);
}
}
运行结果:
强制转换后的值:1
⚠️解释: 由于 byte 类型是 1 个字节,而 int 类型是 4 个字节。在进行强制转换时,由于 byte 只能存储一个字节,byte 类型会取 int 类型的最后一个字节,所以将 int 类型的最后一个字节转换成 10 进制的整数后,所以就为 1。
😏变量
一、简介
在 Java 中,变量是指在程序运行过程中可以改变的量,用于存放运算过程中需要用到的数据,以便在后续的程序中复用,与之相对的是常量,指的是在运行过程中不可改变的量。它们都代表着一块内存空间,用于存储特定类型的数据
二、声明和初始化
声明变量
- 语法:数据类型 变量名;
int num;
初始化变量
- 声明变量的同时进行初始化。
int num = 10;
- 先声明变量,后进行初始化。
int num;
num = 10;
三、作用域
3.1 局部变量
定义在方法、构造函数或者代码块内部的变量。局部变量的作用域仅限于声明它的方法、构造函数或者代码块,局部变量在使用前必须进行初始化。
3.2 成员变量
定义在类内部但在方法、构造函数和代码块之外的变量。成员变量可以被类中的方法、构造函数和特定的语句块访问。如果没有显式地初始化,成员变量会被自动初始化为默认值。
3.3 静态变量
用 static 关键字修饰的成员变量。静态变量属于类本身,而不属于类的任何一个对象。它可以通过类名直接访问,也可以通过对象名访问,类变量在类加载时被初始化,并且只初始化一次。
四、命名规则
- 变量名必须是一个合法的标识符,由字母、数字、下划线和美元符号组成,但不能以数字开头。
- 变量名应该具有描述性,能够清晰地表达变量所代表的含义。
- 变量名应该遵循小驼峰命名法,即多个单词组成的变量名,除第一个单词外,其他单词的首字母大写。
- 变量名不能是 Java 中的关键字或保留字。
五、注意事项
- 在使用变量之前,必须确保变量已经被初始化,否则会导致编译错误或运行时异常。
- 变量的作用域决定了它的可见性和可访问性,在作用域之外无法访问变量。
- 不同类型的变量在进行运算时,可能需要进行类型转换,以确保结果的正确性。
- 尽量避免使用未初始化的变量,以防止出现不可预测的结果。
⚠️总之,变量是 Java 编程中不可或缺的一部分,理解变量的作用、声明和初始化、作用域和命名规则等概念,对于编写正确、高效的 Java 程序至关重要。
🥲运算符
一、简介
在 Java 中,运算符起着至关重要的作用,这些符号可以使 Java 程序可以进行各种数学计算、条件判断和数据处理,从而实现丰富的功能和逻辑。
二、算术运算符
运算符 | 功能 |
---|---|
+ | 加法运算符,将两个数相加 |
- | 减法运算符,将两个数相减 |
* | 乘法运算符,将两个数相乘 |
/ | 除法运算符,将两个数相除 |
% | 取余运算符,求两个数相除的余数 |
public class Main {
public static void main(String[] args) {
int a = 15;
int b = 4;
// 算法运算符
System.out.println("加法运算符:" + (a + b));
System.out.println("减法运算符:" + (a - b));
System.out.println("乘法运算符:" + (a * b));
System.out.println("除法运算符:" + (a / b));
System.out.println("取余运算符:" + (a % b));
}
}
运行结果:
加法运算符:19
减法运算符:11
乘法运算符:60
除法运算符:3
取余运算符:3
三、自增自减运算符
在 Java 中,自增自减运算符是一种特殊的算数运算符,它的操作数只要有一个就可以了,也叫一目运算符或单目运算符,具体有以下分类:
- 自增运算符 ++:
- 前置自增( ++a ):先将变量的值加 1,然后再使用变量的值进行其他操作。
- 后置自增( a++ ):先使用变量的值进行其他操作,然后再将变量的值加 1。
- 自减运算符 --:
- 前置自减( --a ):先将变量的值减 1,然后再使用变量的值进行其他操作。
- 后置自减( a-- ):先使用变量的值进行其他操作,然后再将变量的值少 1。
public class Main {
public static void main(String[] args) {
int a = 10;
// 自增自减运算符
System.out.println("后置自增:" + a++);
System.out.println("前置自增:" + ++a);
System.out.println("后置自减:" + a--);
System.out.println("前置自减:" + --a);
}
}
运行结果:
后置自增:10
前置自增:12
后置自减:12
前置自减:10
四、关系运算符
关系运算符主要用于比较两个值之间的关系,其结果只能是布尔值(true 或 false),一般用于逻辑处理和循环控制语句当中。
运算符 | 功能 |
---|---|
> | 大于运算符,判断左侧值是否大于右侧值 |
< | 小于运算符,判断左侧值是否小于右侧值 |
>= | 大于等于运算符,判断左侧值是否大于等于右侧值 |
<= | 小于等于运算符,判断左侧值是否小于等于右侧值 |
== | 等等于运算符,用于判断两个值是否相等 |
!= | 不等于运算符,用于判断两个值是否不相等 |
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 20;
// 关系运算符
System.out.println("大于运算符:" + (a > b));
System.out.println("小于运算符:" + (a < b));
System.out.println("大于等于运算符:" + (a >= b));
System.out.println("小于等于运算符:" + (a <= b));
System.out.println("等等于运算符:" + (a == b));
System.out.println("不等于运算符:" + (a != b));
}
}
运行结果:
大于运算符:false
小于运算符:true
大于等于运算符:false
小于等于运算符:true
等等于运算符:false
不等于运算符:true
⚠️在关系运算符中,需要特别注意 等等于运算符 的细节,对于不同类型的比较数据,它的功能也不一样,具体如下:
- 基本数据类型:比较的是两个基本类型变量的值是否相等。
- 引用数据类型:比较的是两个引用类型变量的引用是否相等,换句话说比较的是两个变量是否指向同一个的对象。如果要比较对象的内容是否相等,需要重写对象的 equals 方法。
五、逻辑运算符
逻辑运算符主要用于对布尔值进行逻辑判断,其结果只能是布尔值(true 或 false),一般用于逻辑处理和循环控制语句当中。
运算符 | 功能 |
---|---|
&& | 逻辑与运算符,当两个布尔值都是 true 时,结果才为 true |
|| | 逻辑或运算符,当两个布尔值都是 false 时,结果采薇 false |
^ | 逻辑异或运算符,当两个布尔值都是一样的,返回 false,不一样的,返回 true |
! | 逻辑非运算符,将当前的布尔值取反,true 变 false,false 变 true |
public class Main {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
// 逻辑运算符
System.out.println("逻辑与运算符:" + (a && b));
System.out.println("逻辑或运算符:" + (a || b));
System.out.println("逻辑异或运算符:" + (a ^ b));
System.out.println("逻辑非运算符:" + (!a));
}
}
运行结果:
逻辑与运算符:false
逻辑或运算符:true
逻辑异或运算符:true
逻辑非运算符:false
⚠️在上述的 逻辑与运算符 和 逻辑或运算符 都具有短路特性,具体如下:
- 逻辑与:如果第一个操作数为 false,那么就不会再判断第二个操作数,直接返回 false。
- 逻辑或:如果第一个操作数为 true,那么就不会再判断第二个操作数,直接返回 true。
六、位运算符
位运算是针对整数类型数据的一种运算符,相对于算数运算符,位运算符的效率会高很多。算数运算符可能需要先将数据进行一些格式转换或复杂的计算逻辑,而位运算可以直接针对二进制位进行操作,更加高效。
运算符 | 功能 |
---|---|
& | 按位与运算符,对应的二进制位都为 1,结果为 1,否则为 0 |
| | 按位或运算符,对应的二进制位只要有一个 1,结果就为 1,二进制位都为 0,结果才为 0 |
^ | 按位异或运算符,对应的二进制位不同时,结果为 1,相同时为 0 |
〜 | 按位取反运算符,将操作数的二进制位取反,0 变 1,1 变 0 |
<< | 左移运算符,将操作数的二进制位向左移动指定的位数,右边补 0,左移一位相当于乘二 |
>> | 右移运算符,将操作数的二进制位向右移动指定的位数,如果操作数为正数,左边补 0;如果为负数,左边补 1, 右移一位相当于除二 |
>>> | 无符号右移运算符,将操作数的二进制位向右移动指定的位数,无论操作数的正负,左边都补 0,右移一位相当于除二 |
public class Main {
public static void main(String[] args) {
int a = 5; // 二进制:0000 0101
int b = 4; // 二进制:0000 0100
// 位运算符
System.out.println("按位与运算符:" + (a & b)); // 二进制:0000 0100 = 4
System.out.println("按位或运算符:" + (a | b)); // 二进制:0000 0101 = 5
System.out.println("按位异或运算符:" + (a ^ b)); // 二进制:0000 0001 = 1
System.out.println("按位取反运算符:" + (~a)); // 二进制:1000 0110 = -6
System.out.println("左移运算符:" + (a << 2)); // 二进制:0001 0100 = 20
System.out.println("右移运算符:" + (a >> 1)); // 二进制:0000 0010 = 2
System.out.println("无符号右移运算符:" + (a >>> 1)); // 二进制:0000 0010 = 2
}
}
运行结果:
按位与运算符:4
按位或运算符:5
按位异或运算符:1
按位取反运算符:-6
左移运算符:20
右移运算符:2
无符号右移运算符:2
⚠️位运算是 Java 运算符中最难的一种,平时开发不太常见,一般在阅读源码或者参加一些算法比赛中可能会遇到,这边要求至少要知道位运算有什么作用。
七、赋值运算符
赋值运算符用于将一个值赋值给一个变量或者进行一系列运算后在赋值给变量。
运算符 | 功能 |
---|---|
= | 赋值运算符,将等号右边的值赋给左边 |
+= | 将右侧的值与左侧变量的值相加,结果赋给左侧变量 |
-= | 将右侧的值从左侧变量的值中减去,结果赋给左侧变量 |
*= | 将左侧变量的值乘以右侧的值,结果赋给左侧变量 |
/= | 将左侧变量的值除以右侧的值,结果赋给左侧变量 |
%= | 将左侧变量的值与右侧的值进行取余运算,结果赋给左侧变量 |
&= | 将左侧变量的值与右侧的值进行按位与运算,结果赋给左侧变量 |
|= | 将左侧变量的值与右侧的值进行按位或运算,结果赋给左侧变量 |
^= | 将左侧变量的值与右侧的值进行按位异或运算,结果赋给左侧变量 |
<<= | 将左侧变量的值左移指定的位数,结果赋给左侧变量 |
>>= | 将左侧变量的值右移指定的位数,结果赋给左侧变量 |
>>>= | 将左侧变量的值无符号右移指定的位数,结果赋给左侧变量 |
public class Main {
public static void main(String[] args) {
int num = 10;
// 赋值运算符
num += 5;
System.out.println("+=:" + num);
num -= 3;
System.out.println("-=:" + num);
num *= 2;
System.out.println("*=:" + num);
num /= 4;
System.out.println("/=:" + num);
num %= 5;
System.out.println("%=:" + num);
num = 20;
System.out.println("=:" + num);
}
}
运行结果:
+=:15
-=:12
*=:24
/=:6
%=:1
=:20
⚠️赋值运算只要知道上述几种即可,后面的用的非常少,作为一个知识面知道即可。
八、条件运算符
在 Java 中,条件运算符也称为三目运算符,其作用是根据一个条件表达式的结果来选择两个值中的一个,一般用于简化简单的 if else 语句。
语法:条件表达式 ? 表达式1 : 表达式2
解释:如果条件表达式为 true,则返回是表达式1的值;如果条件表达式为 false,则返回是表达式2的值。
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 20;
// 三目运算符
System.out.println("最大值:" + (a > b ? a : b)); // 判断两个数的最大值
}
}
运行结果:
最大值:20
九、字符串连接符
在 Java 中,字符串连接符是加号(+)。它可以将两个字符串或一个字符串与其他类型的数据连接起来。
public class Main {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = " World!";
// 字符串连接符
System.out.println("连接符:" + str1 + str2);
}
}
运行结果:
连接符:Hello World!
十、运算符优先级
当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及到运算符的优先级别的问题。在一个多运算符的表达式中,运算符优先级不同会导致最后得出的结果差别很大。
优先级 | 运算符 |
---|---|
1 | ( ) [ ] . |
2 | ! ~ ++ – |
3 | * / % |
4 | + - |
5 | << >> >>> |
6 | < <= > >= instanceof |
7 | == != |
8 | & |
9 | ^ |
10 | | |
11 | && |
12 | |
13 | ? : |
14 | = += -= *= /= %= &= |
15 | , |
运算符的优先级不需要全部记住,只要知道当你想要改变运算符执行的顺序时, 小括号的优先级是最高的即可。
😇流程控制语句
一、简介
在 Java 中,流程控制语句可以使 Java 程序能够根据不同的情况做出操作、重复执行任务、提前结束执行或跳过某些操作,从而实现更加复杂和灵活的程序逻辑。
二、条件语句
在 Java 中,条件语句主要有 if 语句、if - else 语句、 if - else if - else 语句和 switch 语句,它们用于根据不同的条件执行不同的代码块。
2.1 if
语法结构
if (条件表达式) {
代码块
}
⚠️当条件表达式的值为 true 时,执行语句块中的代码;如果为 false,则不执行语句块中的代码,直接跳过。
代码示例
public class Main {
public static void main(String[] args) {
int num = 10;
if (num > 5) {
System.out.println("num 大于 5");
}
}
}
运行结果:
num 大于 5
2.2 if - else
语法结构
if (条件表达式) {
语句块1
} else {
语句块2
}
⚠️当条件表达式的值为 true 时,执行语句块1中的代码;如果为 false,则执行语句块2中的代码。
代码示例
public class Main {
public static void main(String[] args) {
int num = 10;
if (num < 5) {
System.out.println("num 大于 5");
}else {
System.out.println("num 不大于 5");
}
}
}
运行结果:
num 不大于 5
2.3 if - else if - else
语法结构
if (条件表达式1) {
语句块1
} else if (条件表达式2) {
语句块2
} else {
语句块3
}
⚠️可以进行多个条件的判断。依次判断条件表达式,当某个条件表达式为 true,则执行对应的语句块;如果所有条件表达式都为 false,则执行 else 后的语句块。
代码示例
public class Main {
public static void main(String[] args) {
int num = 10;
if (num > 15) {
System.out.println("num 大于 15");
} else if (num > 5) {
System.out.println("num 大于 5 但不大于 15");
} else {
System.out.println("num 不大于 5");
}
}
}
运行结果:
num 大于 5 但不大于 15
2.4 switch
switch 语句通过将表达式的值与 case 后面的常量值进行匹配来确定执行的代码块,表达式通常是整数类型、枚举类型或者 String 类型(从 Java 7 开始支持)。
语法结构
switch(表达式) {
case 值1:
// 当表达式的值等于值1时执行的代码
break;
case 值2:
// 当表达式的值等于值2时执行的代码
break;
// 可以有多个 case 语句
default:
// 当表达式的值与所有 case 中的值都不匹配时执行的代码
}
⚠️匹配过程遵循以下规则:
- 首先计算 switch 后面表达式的值;
- 然后将该值与每个 case 后面的常量值进行比较。如果找到匹配的 case ,则从该 case 处开始执行代码,直到遇到 break 语句或者 switch 语句结束;
- 如果没有找到匹配的 case ,则执行 default 分支(如果存在)。
public class Main {
public static void main(String[] args) {
int num = 2;
switch (num) {
case 1:
System.out.println("这是 1");
break;
case 2:
System.out.println("这是 2");
break;
case 3:
System.out.println("这是 3");
break;
default:
System.out.println("没有匹配的");
}
}
}
运行结果:
这是 2
case 穿透:
因为 case 语句中缺少 break 语句所导致的错误结果。
public class Main {
public static void main(String[] args) {
int num = 2;
switch (num) {
case 1:
System.out.println("这是 1");
case 2:
System.out.println("这是 2");
case 3:
System.out.println("这是 3");
break;
default:
System.out.println("没有匹配的");
}
}
}
运行结果:
这是 2
这是 3
如果当前匹配成功的 case 语句块没有 break 语句,则从当前 case 开始,后续所有 case 的值都会输出,直到后续的 case 语句块中有 break 关键字才会中断 switch。
新特性
在 JDK12 的版本,可以简写 switch 语句,如下所示:
public class Main {
public static void main(String[] args) {
int num = 2;
switch (num) {
case 1 -> System.out.println("这是 1");
case 2 -> System.out.println("这是 2");
case 3 -> System.out.println("这是 3");
default -> System.out.println("没有匹配的");
}
}
}
运行结果:
这是 2
这种书写方式是 JDK12 出现的,所以比之低的版本的JDK是不可以使用的,默认自带了 break 语句,不需要在代码里书写,并且如果代码块只有一条语句,大括号也可以不写。
三、循环语句
在 Java 中,循环语句主要有 for 循环、while 循环和 do - while 循环,用于重置执行同样的操作或循环遍历数据。
3.1 for
for 循环相较于其他两种循环结构,for 循环的结构清晰,常用于已知循环次数的情况,并且可以处理下标。
语法结构
for (初始化表达式; 条件表达式; 更新表达式) {
循环体
}
⚠️ 匹配过程遵循以下规则:
- 初始化表达式在循环开始时执行一次,通常用于初始化循环变量;
- 条件表达式在每次循环开始前进行判断,如果为 true,则执行循环体;如果为 false,则结束循环;
- 更新表达式在每次循环体执行完后执行,通常用于更新循环变量,之后重复执行第2步的操作。
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
}
}
运行结果:
0
1
2
3
4
3.2 while
while 循环相较于 for 循环,用于不知道循环次数的情况,循环体内必须有改变条件表达式值的操作,否则可能会导致死循环。
语法结构
while (条件表达式) {
循环体
}
⚠️ 匹配过程遵循以下规则:
- 先判断条件表达式,如果为 true,则执行循环体;如果为 false,则直接结束循环;
- 然后重复执行第1步操作。
public class Main {
public static void main(String[] args) {
int i = 0;
while (i < 5) {
System.out.println(i);
i++;
}
}
}
运行结果:
0
1
2
3
4
3.3 do - while
do - while 循环相较于 while 循环,do - while 循环无论条件表达式一开始是否为 true,循环体至少会执行一次,用于不知道循环次数、不管是否为 true 都执行的情况。
语法结构
do {
循环体
} while (条件表达式);
⚠️ 匹配过程遵循以下规则:
- 先执行一次循环体,然后再判断条件表达式。如果为 true,则继续执行循环体;如果为 false,则结束循环;
- 然后重复执行第1步操作。
public class Main {
public static void main(String[] args) {
int i = 0;
do {
System.out.println(i);
i++;
} while(i < 5);
}
}
运行结果:
0
1
2
3
4
四、跳转语句
在 Java 中,跳转语句主要有 break 关键字、continue 关键字和 return 关键字,用于根据特定条件灵活地改变程序的执行次序。
4.1 break
break 用于跳出循环或 switch 语句。
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 当i的值为5时,终止循环
if (i == 5) {
break;
}
System.out.println(i);
}
}
}
运行结果:
0
1
2
3
4
4.2 continue
continue 用于跳过当前循环中的剩余语句,直接进入下一次循环。
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 当i的值为5时,跳过本次循环
if (i == 5) {
continue;
}
System.out.println(i);
}
}
}
运行结果:
0
1
2
3
4
6
7
8
9
4.3 return
return 用于从方法中返回值,并结束方法的执行。它和 break 关键字不是一个量级的,return 会直接终止当前所在的方法,而 break 只是终止循环或 switch。
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
// 当i的值为5时,终止当前方法
if (i == 5) {
return;
}
System.out.println(i);
}
System.out.println("这里的代码不会执行");
}
}
运行结果:
0
1
2
3
4
🤡方法
一、简介
在 Java 中,方法指的是解决一类问题的代码的集合,这样可以提高代码的复用性,增强程序的可读性和可理解性,另外,在 C/C++ 语言中,方法也叫函数,这两个指的是一个东西。
二、方法的定义
语法结构
修饰符 返回值类型 方法名(参数列表) {
方法体
}
- 修饰符:可以是访问控制符、静态修饰符等,用于控制方法的访问权限和特性;
- 返回值类型:指定了方法执行完毕后返回的值的类型,如果方法没有返回值,则返回值类型为 void;
- 方法名:是标识符,用于标识方法;
- 参数列表:是方法接收的输入参数,可以有零个或多个参数,每个参数包括参数类型和参数名;
- 方法体:是实现方法功能的代码块。
public int add(int a, int b) {
return a + b;
}
这个方法名为 add,接受两个整数参数 a 和 b,返回它们的和。
三、方法的调用
通过方法名和参数列表来调用方法,执行方法中的代码。当调用一个有返回值的方法时,可以将方法的返回值赋给一个变量或者在表达式中使用;对于没有返回值的方法,直接调用即可。
public class Main {
public static void main(String[] args) {
// 调用方法
int sum = add(5, 20);
System.out.println("两数之和:" + sum);
}
// 定义方法
public static int add(int a, int b) {
return a + b;
}
}
运行结果:
两数之和:25
四、可变长参数
可变参数是一种特殊类型的方法参数,允许你在调用方法时传递任意数量的参数给该方法,可变参数在方法声明中用省略号 (…) 表示,紧跟在参数类型的后面,如果还有其他方法参数,那么可变参数要放在最后,值得注意的是,可变长参数的底层是由数组实现的,所以可以通过下标来访问元素。
public class Main {
public static void main(String[] args) {
int[] nums = {1, 5, 7, 2, 9};
// 调用方法
System.out.println("数组的最大值是:" + printMax(nums));
}
// 求一个数组的最大值
public static int printMax(int... nums) {
// 判断数组是否为空
if (nums.length == 0) return -1;
// 创建临时数组,用来存放数组的最大值
int temp = nums[0];
// 通过循环得到最大值
for (int i = 1; i < nums.length; i++) {
if (nums[i] > temp) {
temp = nums[i];
}
}
return temp;
}
}
运行结果:
数组的最大值是:9
五、静态方法为什么不能调非静态成员
静态方法是属于类的而不是对象,在类加载的时候就会分配内存,可以直接通过类名访问。
而非静态成员(变量和方法)属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
由于在类的非静态成员不存在的时候静态方法就已经存在了,此时调用在内存中还不存在的非静态成员,如果调用的话,编译器会报错。
六、方法的重载和重写
方法的重载
重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理,发生在同一个类中(或者父类和子类之间),方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
方法的重载发生在编译期,编译器会根据方法签名来具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。 如果编译器找不到匹配的参数, 则在编译期报错。
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
方法的重写
方法的重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
⚠️对于方法的重写需要注意以下几点:
- 构造方法无法被重写。
- 方法名、参数列表必须相同,子类比父类的返回值类型更小或相等,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
- 如果父类方法访问修饰符为 private、final、static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。
总结
区别 | 重载 | 重写 |
---|---|---|
范围 | 同类 | 子类 |
发生阶段 | 编译期 | 运行期 |
参数 | 必须不一样 | 不能发生改变 |
返回类型 | 可以修改 | 子类比父类的返回值类型更小或相等 |
异常 | 可以修改 | 子类比父类抛出的异常更小或相等 |
修饰符 | 可以修改 | 不能比父类的修饰符更加严格 |