Java课堂复习总结
本文章用于总结课程内容,如有错误请谅解,不常看私信,右侧有页面导航
本文作者:袁羽
1 初识Java和安装JDK
1.1 Java发展史
- 起源:Java最初由James Gosling、Mike Sheridan和Patrick Naughton在1991年6月启动的项目,当时被称为“Green计划”。该项目的目标是为数字设备如机顶盒和智能电视等开发一种语言。
- 命名:最初,Java被称为“Greentalk”,文件扩展名为.gt。后来,它被命名为“Oak”,作为Green项目的一部分。由于“Oak”已经被注册为商标,1995年,它被重新命名为“Java”。Java这个名字来源于Java岛,世界上最早的咖啡产地之一,同时也是程序员喜爱的咖啡豆的名字。
- 发布:1995年,Sun Microsystems公开介绍了Java,并将其推广为一种平台独立的语言,其核心承诺是“一次编写,到处运行”。同年,Time杂志将Java评为1995年十大最佳产品之一。
- Java 1.0发布:1996年初,Sun Microsystems发布了Java 1.0,标志着Java进入主流开发领域。
- JCP和JDK 1.1:1997年,Java社区进程(JCP)成立,旨在通过标准化特性和API来指导Java的发展。同年,Sun Microsystems发布了Java开发工具包(JDK 1.1),包括内部类、JavaBeans和JDBC等关键特性。
- Java 2:1998年,Sun Microsystems发布了Java 2,进一步巩固了Java在企业级应用中的地位。
- Java的多样化:到了21世纪初,Java已经超越了网络,开始在各种设备上运行,包括移动电话、零售和金融计算机,甚至是NASA火星探测器的机载计算机。Sun Microsystems为不同目的创建了Java的不同版本,包括Java SE用于家用计算机,Java ME用于嵌入式设备,Java EE用于互联网服务器和超级计算机。
- Java的收购:2010年,Oracle公司收购了Sun Microsystems,从而接管了Java的管理。
- Java的持续发展:Java继续发展,支持企业系统、Android应用和云原生解决方案,保持其作为编程世界基石的地位。
- Java 17和Java 20/21:2021年,Java 17作为长期支持(LTS)版本发布,专注于安全性和性能增强。2023年,Java 20和21发布,专注于通过虚拟线程和性能优化提高性能。
- Java语言的发展历程中,Java被分成三个版本:1.Java SE:定位在个人计算机上应用。2.Java EE:定位在服务端的应用。3.Java ME:定位在消费性电子产品的应用。
1.2 Java语言的特点
- 简单性:Java的设计去除了C++中一些复杂的概念,如指针运算、多重继承等,使得语言更加简洁易学。
- 面向对象:Java是一种面向对象的语言,这意味着它支持类和对象的概念,以及封装、继承和多态等面向对象的特性。
- 跨平台性:Java的“一次编写,到处运行”(Write Once, Run Anywhere,简称WORA)特性意味着可以在任何安装了Java虚拟机(JVM)的设备上运行Java程序。
- 可移植性:由于Java的跨平台特性,Java程序可以在不同的操作系统和硬件平台上运行,而无需修改代码。
- 安全性:Java提供了一个安全的环境,用于执行代码,特别是在网络和Web环境中,Java的沙箱模型可以帮助防止恶意代码的执行。
- 健壮性:Java的设计目标之一是减少错误,提供了强类型检查和异常处理机制,以帮助开发者捕捉和处理错误。
- 编译型语言:
- Java源代码(.java文件)首先需要被编译成字节码(.class文件)。这个编译过程是由Java编译器完成的,它将源代码转换成JVM可以理解的中间字节码。
- 编译后的字节码文件可以在任何安装了JVM的平台上运行,这体现了Java的跨平台特性。
- 高性能:虽然Java通常被认为是解释型语言,但随着JVM和即时编译器(JIT)技术的发展,Java程序的运行速度已经非常接近于编译型语言。
- 多线程:Java内置了对多线程的支持,使得开发多线程程序更加容易,这对于需要同时处理多个任务的应用程序来说非常重要。
- 动态性:Java可以在运行时动态加载和链接库,这使得Java程序可以在运行时扩展功能。
- 分布式:Java支持Internet应用的开发,提供了丰富的API用于网络通信,使得开发分布式应用更加容易。
1.3 面向对象的程序设计
面向对象程序设计是一种编程范式,它将现实世界中的实体抽象为对象,并通过对象之间的交互来设计和构建软件系统。面向对象程序设计的核心思想包括以下几个方面:
- 对象和类:
- 对象:对象是现实世界中事物的抽象,它包含状态(属性)和行为(方法)。
- 类:类是对象的蓝图或模板,定义了对象的属性和方法。对象是类的实例。
- 封装:
- 封装是指将数据(属性)和操作数据的方法(行为)捆绑在一起,并隐藏内部实现细节,只提供必要的接口与外部交互。
- 封装提高了代码的安全性和模块化,使得代码更易于维护和重用。
- 继承:
- 继承是一种创建新类的方式,新类(子类)可以继承现有类(父类或超类)的属性和方法。
- 继承支持代码的重用,并可以建立类之间的层次结构。
- 多态:
- 多态是指允许不同类的对象对同一消息做出响应的能力,即同一个接口可以被不同的实例以不同的方式实现。
- 多态性使得代码更加灵活和可扩展,它允许编写通用的代码来处理不同类型的对象。
面向对象程序设计思想强调了代码的模块化、重用性和可维护性,它通过模拟现实世界中的对象和它们之间的关系来构建软件系统,使得软件设计更加直观和自然。
面向过程一过程为核心,强调事件的流程、顺序,适合开发一些小型应用,代码的可维护性、可读性、复用性、可拓展性、不如面向对象。
1.4 Java开发环境的搭建
1.4.1 JDK与JRE的区别
- JDK:Java开发工具包,是为Java开发者提供的一套工具集,用于开发、编译和运行Java程序。
- JRE:Java运行时环境,是运行Java程序所必需的软件环境,包括Java虚拟机(JVM)和Java核心类库,但不包括开发工具。
1.4.2 JDK安装
- 下载JDK:
- 访问Oracle官网下载适合Windows的JDK安装包。
- 运行安装程序:
- 双击下载的安装包,启动安装向导。
- 按照提示完成安装。(默认安装到C盘,也可以自己选择安装路径)
- 配置环境变量:
- 右键点击“此电脑”或“计算机”,选择“属性”。
- 点击“高级系统设置”。
- 在“系统属性”窗口中,点击“环境变量”。
- 在“系统变量”区域,找到“Path”变量,选择并点击“编辑”。
- 点击“新建”,添加JDK安装目录下的
bin路径,例如:C:\Program Files\Java\jdk-14\bin。 - 点击“确定”保存更改。
- 验证安装:
- 打开命令提示符(CMD)。
- 输入
java -version和javac -version,如果安装成功,将显示JDK的版本信息。
注:JDK本身自带JRE,因此无需单独安装JRE,如果安装JDK时弹出了JRE安装窗口关闭就好
1.4.3 尝试第一个程序:Hello,World!
开发环境如下
系统:Windows 10 专业工作站版(22H2)
JDK Version:23.0.1
-
首先新建一个名为
HelloWorld.java的文件 -
打开并向写入以下代码
public class HelloWorld { public static void main(String[] args) { // 打印 "Hello, World!" 到控制台 System.out.println("Hello, World!"); } } -
保存文件,打开命令行工具(CMD)
-
导航到保存
HelloWorld.java文件的目录。 -
使用
javac HelloWorld.java命令编译Java程序,编译后会生成一个HelloWorld.class文件,这是编译后的字节码文件。 -
运行编译后的程序:
java HelloWorld -
控制台上看到输出:
Hello, World!
1.4.3.1 代码解析
public class HelloWorld {
public static void main(String[] args) {
// 打印 "Hello, World!" 到控制台
System.out.println("Hello, World!");
}
}
- 类声明:
public class HelloWorld:这一行声明了一个名为HelloWorld的公共类。在Java中,每个类的定义都以class关键字开始,后面跟着类名。类名通常首字母大写,并且遵循驼峰命名法。
- 主方法:
public static void main(String[] args):这是程序的入口点,即程序开始执行的地方。main方法是一个特殊的方法,当Java程序运行时,它总是从main方法开始执行。public:修饰符,表示这个方法是公共的,可以从类的外部访问。static:修饰符,表示这个方法属于类本身,而不是类的某个特定对象。这意味着可以在不创建类的实例的情况下调用main方法。void:返回类型,表示这个方法不返回任何值。main:方法名,Java程序的入口点。String[] args:参数,表示传递给main方法的命令行参数。args是一个字符串数组,每个元素代表一个命令行参数。
- 打印语句:
System.out.println("Hello, World!");:这行代码负责将字符串"Hello, World!"输出到控制台,并在输出后换行。System:是Java中的一个预定义类,它包含多个有用的类和方法,其中out是System类的一个静态成员,即PrintStream对象。out.println:是PrintStream类的一个方法,用于打印信息到控制台,并在打印结束后自动添加一个换行符。
- 代码块:
{}:大括号包含了类或方法的所有内容。在HelloWorld类中,大括号内包含了main方法的定义。
- 注释:
//:这是单行注释的开始。在//之后的文字会被编译器忽略,不会执行。注释用于解释代码,使代码更易读。
1.4.4 IDE的安装(使用IDEA作为IDE环境)
IntelliJ IDEA 是一款流行的Java集成开发环境(IDE),由JetBrains公司开发。
Windows系统上安装IntelliJ IDEA的基本步骤:
1.下载 IntelliJ IDEA
- 访问JetBrains的官方网站:JetBrains IntelliJ IDEA。
- 选择适合您需求的版本(Community版是免费的,适合开源项目和个人学习使用;Ultimate版是付费的,包含更多高级功能,适合企业开发)。
- 下载对应操作系统的安装包。
2.安装 IntelliJ IDEA
- 双击下载的安装包(.exe)文件启动安装向导。
- 选择“Next”进入安装选项。
- 选择安装路径,建议选择非系统盘。
- 选择是否将IDEA添加到开始菜单和桌面快捷方式。
- 点击“Install”开始安装。
- 安装完成后,勾选“Run IntelliJ IDEA”选项,点击“Finish”启动IDEA。
1.5 总结
通过学习第一章的内容,我对Java的起源、核心特点有了深入的了解。我对Java的“一次编写,到处运行”的理念印象深刻,这不仅意味着代码的可移植性,也体现了Jav在不同平台上的兼容性。学习Java语言的特点让我意识到,为什么Java能够在众多编程语言中脱颖而出。它的简单性降低了学习门槛,而面向对象的特性则提高了代码的组织性和可维护性。安全性和健壮性是企业级应用的关键,Java在这些方面的表现尤为出色。
2 Java编程基础
2.1 关键字和保留字
从字面含义上理解,保留字是语言中已经定义过的字,使用者不能再将这些字作为变量名或过程名使用。而关键字则指在语言中有特定含义,成为语法中一部分的那些字。在一些语言中,一些保留字可能并没有应用于当前的语法中,这就成了保留字与关键字的区别。一般出现这种情况可能是由于考虑扩展性。例如,Javascript有一些未来保留字,如abstract、double、goto等等。它可能未来要增加直接跳转的功能,那么为了使当前版本的程序代码能向后兼容,所以不允许使用goto作为变量名,但当前版本的语言并不支持goto的直接跳转功能,它目前就不是关键字。
具体有哪些关键字和保留字可参阅:https://blog.csdn.net/Alexwym/article/details/81943959
2.2 Java的标识符
2.2.1 命名规则
- 字符组成:
- 标识符可以包含字母、数字和下划线(_)。
- 标识符不能以数字开头。
- Java对大小写敏感,因此
myVariable和myvariable是两个不同的标识符。
- 关键字和保留字:
- 标识符不能是Java的关键字或保留字。
- 长度限制:
- 标识符没有长度限制,但为了可读性,建议保持标识符简短且描述性强。
2.3 Java的数据类型
Java中的数据类型可以分为两大类:基本数据类型(Primitive Data Types)和引用数据类型(Reference Data Types)。
2.3.1 基本数据类型
基本数据类型是Java预定义的,它们代表了最基本的数据单元。
-
数值类型
- 整型:
byte:8位有符号整数,取值范围从-128到127。short:16位有符号整数,取值范围从-32,768到32,767。int:32位有符号整数,默认的整数类型,取值范围从-231到231-1。long:64位有符号整数,取值范围从-263到263-1,数值常量需加上L或l后缀。
- 浮点型:
float:32位单精度浮点数,数值常量需加上F或f后缀。double:64位双精度浮点数,默认的浮点数类型。
- 字符型:
char:16位Unicode字符,取值范围从0到65535。
- 整型:
-
布尔类型
boolean:只有两个可能的值:true和false。
2.3.2 引用数据类型
引用数据类型指向内存中的一个对象,它们是通过引用变量来访问的。
-
类
- 如
String、Date等自定义类的对象。
- 如
-
接口
- 由接口类型引用的变量可以指向任何实现了该接口的类的实例。
-
数组
- 一种容器对象,可以包含固定大小的单一类型的元素。
2.4 变量定义与赋值
在Java中,变量是用来存储数据值的标识符。变量定义和赋值是编程中的基本操作。
-
变量声明:声明一个变量时,需要指定变量的类型和名称。
// 声明一个整型变量 int number; -
变量初始化:初始化变量是给变量赋予一个初始值。
// 声明并初始化一个整型变量 int number = 10; -
变量赋值:在声明变量后,可以在程序的任何地方给变量赋新的值。
// 给变量赋新的值 number = 20; -
数据类型:可参考上方的数据类型解释
-
变量作用域:变量的作用域是指变量可以被访问的代码区域。例如,局部变量只能在声明它的代码块中被访问,而成员变量(类的属性)可以在类的任何地方被访问。
-
变量命名规则:
- 变量名必须以字母、美元符号($)或下划线(_)开始。
- 变量名只能包含字母、数字、美元符号($)或下划线(_)。
- 变量名是区分大小写的。
-
类型转换:在Java中,有时需要将一个类型的变量值赋给另一个类型的变量,这称为类型转换。
// 从int转换为double int intValue = 10; double doubleValue = (double) intValue; -
复合赋值:Java支持复合赋值操作符,这些操作符结合了赋值和算术运算。
// 将变量number增加5 number += 5;
2.5 常量
在Java中,常量是那些一旦被赋值之后,其值就不能再被改变的变量。常量通常用于存储那些在程序执行过程中不应该改变的值。以下是Java中定义和使用常量的要点:
-
关键字:在Java中,使用
final关键字来定义常量。 -
常量命名约定:常量的命名通常使用全大写字母,并用下划线分隔单词,以区分它们与普通变量。
-
常量声明和初始化:常量必须在声明时初始化,因为它们之后不能被重新赋值。
-
常量的位置:常量可以声明在方法内部(局部常量),也可以声明为类的成员变量(全局常量)。
2.5.1 全局常量(类成员常量)
public class ConstantsExample {
// 定义一个全局常量
public static final int MAX_USERS = 100;
public static void main(String[] args) {
// 使用常量
System.out.println("Maximum users: " + MAX_USERS);
}
}
2.5.2 局部常量(方法内部常量)
public class ConstantsExample {
public static void main(String[] args) {
// 定义一个局部常量
final double PI = 3.14159;
// 使用常量
System.out.println("Value of PI: " + PI);
}
}
2.6 Scanner的使用
Scanner 类是 Java 程序中用于获取用户输入的一个非常有用的工具。它属于 java.util 包,可以解析原始类型和字符串的简单文本扫描器。以下是如何使用 Scanner 类的一些基本步骤和示例:
2.6.1 导入 Scanner 类
在使用 Scanner 类之前,需要先导入它:
import java.util.Scanner;
2.6.2 创建 Scanner 对象
创建一个 Scanner 对象来从不同的输入源读取数据,最常见的是从控制台(系统输入)读取:
Scanner scanner = new Scanner(System.in);
2.6.3 使用 Scanner 读取数据
Scanner 类提供了多种方法来读取不同类型的数据:
next(): 读取下一个完整的 token(默认由空白分隔)nextLine(): 读取输入行的剩余部分nextInt(): 读取下一个整数nextDouble(): 读取下一个双精度浮点数nextFloat(): 读取下一个浮点数nextLong(): 读取下一个长整数nextShort(): 读取下一个短整数nextByte(): 读取下一个字节值nextBoolean(): 读取下一个布尔值
示例代码
下面是一个使用 Scanner 从控制台读取用户输入的简单示例:
import java.util.Scanner;
public class ScannerExample {
public static void main(String[] args) {
// 创建 Scanner 对象
Scanner scanner = new Scanner(System.in);
// 提示用户输入
System.out.println("Enter your name:");
String name = scanner.nextLine(); // 读取字符串
System.out.println("Enter your age:");
int age = scanner.nextInt(); // 读取整数
// 使用输入的数据
System.out.println("Hello " + name + ", you are " + age + " years old.");
// 关闭 Scanner 对象
scanner.close();
}
}
2.7 运算符和表达式
Java中的运算符和表达式是用来进行各种运算和计算的基础工具。可以分为以下几类:
2.7.1 算术运算符
用于进行数学运算。
+:加法-:减法*:乘法/:除法%:取余(模运算)
2.7.2 关系运算符
用于比较两个值之间的关系,返回 true 或 false。
==:等于!=:不等于>:大于<:小于>=:大于或等于<=:小于或等于
2.7.3 逻辑运算符**
用于对布尔值进行逻辑运算。
&&:逻辑与(AND)||:逻辑或(OR)!:逻辑非(NOT)
2.7.4 赋值运算符**
用于将值赋给变量。
=:简单赋值+=:加法赋值-=:减法赋值*=:乘法赋值/=:除法赋值%=:取余赋值
2.7.5 自增与自减运算符
用于变量的自增和自减。
++:自增(前置或后置)--:自减(前置或后置)
2.7.6 位运算符
用于对整数类型的二进制位进行操作。
&:按位与|:按位或^:按位异或~:按位取反<<:左移>>:右移>>>:无符号右移
2.7.7 条件运算符(三元运算符)
根据条件表达式的结果,选择两个值中的一个。
2.7.8 实例运算符
用于对象和类的判断和操作。
instanceof:检查对象是否是某个类的实例。
String str = "Hello";
System.out.println(str instanceof String); // 输出 true
2.7.9 表达式
在Java中,表达式是由运算符、操作数和方法调用组成的语句,用于计算并返回一个值。例如:
int result = (a + b) * 2; // (a + b) * 2 是一个表达式
表达式可以包含变量、常量、运算符、方法调用等,通过计算返回值。
2.7.10 运算符优先级
- 优先级 1:圆括号
()最高,可以改变计算顺序。 - 优先级 2:后置自增 (
a++) 和后置自减 (a--) 优先级较高。 - 优先级 3:一元运算符(
+、-、++、--、~、!)的优先级高于基本算术运算。 - 优先级 4 和 5:乘法、除法、取余运算符(
*、/、%)和加法、减法运算符(+、-)。 - 优先级 6:位移运算符(
<<、>>、>>>)的优先级较低,但高于比较运算符。 - 优先级 7:关系运算符(
<、<=、>、>=、==、!=)进行值的比较。 - 优先级 8-10:位运算符(按位与、按位异或、按位或)。
- 优先级 11 和 12:逻辑运算符(
&&、||)的优先级相对较低,分别表示逻辑与和逻辑或。 - 优先级 13:三元运算符(条件运算符)
? :用于根据条件选择结果。 - 优先级 14:赋值运算符,赋值运算符的优先级最低。
2.8 选择结构
Java 中的选择结构允许程序根据条件来选择不同的执行路径。Java 中主要有三种选择结构:if 语句、switch 语句。这些结构都根据给定的条件判断是否执行某段代码,或者选择执行哪一段代码。
2.8.1 if 语句
if 语句是 Java 中最常见的选择结构。它用于根据条件判断执行不同的代码块。
基本语法:
if (condition) {
// 如果 condition 为 true,执行这部分代码
}
condition是一个布尔表达式,表示条件。如果它的值为true,则执行if代码块中的代码;如果为false,则跳过该代码块。
语法结构:
if:最基本的选择结构。if-else:如果条件为false,执行else中的代码。else-if:有多个条件进行选择判断。嵌套 if:if语句中包含另一个if语句。
int a = 5;
if (a > 3) {
System.out.println("a 大于 3");
} else {
System.out.println("a 小于或等于 3");
}
else-if 示例:
int a = 7;
if (a > 10) {
System.out.println("a 大于 10");
} else if (a == 7) {
System.out.println("a 等于 7");
} else {
System.out.println("a 小于 7");
}
2.8.2 switch 语句
switch 语句用于根据不同的值选择执行的代码。它是多重条件选择的另一种方式,通常用于替代多个 if-else if 的情况。
基本语法:
switch (expression) {
case value1:
// 如果 expression == value1,执行此代码块
break;
case value2:
// 如果 expression == value2,执行此代码块
break;
default:
// 如果没有匹配任何值,执行此代码块
}
expression:通常是一个整数、字符、字符串或枚举类型。case value1:如果expression的值等于value1,则执行该case后的代码。break:用于终止switch语句的执行。没有break时,程序会继续执行后续的case语句(即使条件不匹配),这称为“穿透”。default:如果没有任何case条件匹配时,执行default语句块。default是可选的。
注意:
switch语句中可以使用byte、short、char、int以及枚举类型、String等类型作为表达式。switch语句是基于值的匹配,而不是基于条件判断,因此不支持范围判断。
2.8.3 if 语句与 switch 语句的对比
| 特点 | if 语句 |
switch 语句 |
|---|---|---|
| 适用场景 | 适用于复杂条件判断,可以包含多种逻辑操作 | 适用于单一变量的多个固定值判断 |
| 表达式类型 | 支持任何类型的布尔表达式 | 支持整数、字符、字符串、枚举类型等固定值 |
| 多条件判断 | 可以处理复杂条件(如 a > 10 或 x == 5) |
只能处理简单的值匹配,不支持范围判断 |
| 代码简洁性 | 适合多种复杂情况 | 适合多个 case 的值匹配,不需要多个 if |
break 的需求 |
不需要 break |
需要 break 来终止匹配的执行(避免“穿透”) |
2.9 循环结构
以下是 Java 循环结构的概述和简短示例:
1. for 循环
用于已知循环次数的情况,常用于数组遍历或计数。
语法:
for (int i = 0; i < 5; i++) {
// 循环体
}
2. while 循环
适用于循环次数不确定,先判断条件再执行。
语法:
while (条件) {
// 循环体
}
3. do-while 循环
与 while 循环类似,但会至少执行一次循环体。
语法:
do {
// 循环体
} while (条件);
4. break 和 continue
break:提前结束循环。continue:跳过当前循环,进入下一次循环。
示例:
for (int i = 1; i <= 5; i++) {
if (i == 3) break; // 退出循环
System.out.println(i);
}
5. 循环嵌套
循环嵌套是指在一个循环中包含另一个循环。常用于遍历多维数组或打印图案。
基本语法
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 内部循环体
}
}
示例:for 循环嵌套
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.println("i = " + i + ", j = " + j);
}
}
输出:
i = 0, j = 0
i = 0, j = 1
i = 0, j = 2
i = 1, j = 0
i = 1, j = 1
i = 1, j = 2
i = 2, j = 0
i = 2, j = 1
i = 2, j = 2
for循环:适用于已知循环次数或需要用循环变量进行计数的情况。while循环:适用于循环次数不确定,且需要在每次循环前判断条件的情况。do-while循环:适用于至少需要执行一次循环体的情况。
2.10 方法
在 Java 中,方法是一个可以执行特定任务的代码块。它可以接受输入(参数),执行任务,并返回结果。方法有助于代码重用,组织和简化程序结构。
2.10.1 方法的基本组成
- 方法声明:包括返回类型、方法名、参数列表(可选)和方法体。
- 方法调用:通过方法名来调用已声明的方法。
2.10.2 方法的语法
返回类型 方法名(参数列表) {
// 方法体
// 执行代码
return 返回值; // 如果方法有返回类型
}
2.10.3 方法的组成部分
- 返回类型:方法执行后返回的数据类型。如果没有返回值,使用
void。 - 方法名:方法的名称,遵循命名规则。
- 参数列表:方法接受的输入,多个参数之间用逗号隔开。如果没有参数,括号为空。
- 方法体:包含方法要执行的代码块。
return语句:方法返回一个值(如果有返回类型)。
2.10.4 方法的分类
- 无返回值方法 (
void):- 用于执行某些操作,但不返回结果。
- 示例:打印输出、修改状态等。
- 有返回值方法:
- 执行操作后返回结果。
- 示例:计算结果、获取数据等。
2.10.5 方法的特点
- 可重用性:方法可以在程序的不同地方被调用,避免重复代码。
- 参数化:方法可以接受输入参数,执行不同的任务。
- 返回值:方法可以返回结果,也可以没有返回值。
在Java中,方法重载和递归是两种重要的编程概念,它们在不同的场景下有着广泛的应用。
2.11 方法重载
方法重载是指在同一个类中可以定义多个同名方法,但这些方法的参数列表必须不同。参数列表不同可以是参数的类型不同、参数的数量不同,或者两者都不同。方法重载的主要目的是提供多个具有相同功能但接受不同参数的方法版本。
特点:
- 方法名相同。
- 参数列表必须不同(参数类型、参数个数或者两者都不同)。
- 返回类型可以相同也可以不同。
- 与方法的访问修饰符无关。
示例:
public class MethodOverloadingExample {
// 方法重载示例
public void display() {
System.out.println("Display without parameters");
}
public void display(int a) {
System.out.println("Display with one integer parameter: " + a);
}
public void display(double a) {
System.out.println("Display with one double parameter: " + a);
}
public static void main(String[] args) {
MethodOverloadingExample example = new MethodOverloadingExample();
example.display(); // 调用无参数的方法
example.display(10); // 调用一个整数参数的方法
example.display(20.5); // 调用一个双精度参数的方法
}
}
2.12 方法递归
递归是一种编程技巧,它允许一个方法直接或间接地调用自身。递归通常用于解决那些可以分解为相似子问题的问题,这些问题的解决方式遵循相同的逻辑。
特点:
- 递归方法必须有一个基本情况(base case),用于停止递归。
- 递归方法必须有一个递归情况(recursive case),即方法调用自身。
示例:
public class RecursionExample {
// 递归计算阶乘
public static long factorial(int n) {
if (n <= 1) {
return 1; // 基本情况
} else {
return n * factorial(n - 1); // 递归情况
}
}
public static void main(String[] args) {
int number = 5;
System.out.println("Factorial of " + number + " is: " + factorial(number));
}
}
数组是Java中一种基本的数据结构,用于存储固定大小的同一类型的元素集合。数组可以是一维的或多维的(例如二维数组)。以下是数组的一些关键概念和用法:
在Java中,数组是一种用于存储固定大小的同一类型的元素集合的数据结构。数组可以是一维的或多维的,其中一维数组是最基本的形式,而二维数组可以看作是数组的数组。
2.13 数组
2.13.1 一维数组
一维数组是单列的数组,每个元素都通过索引来访问,索引从0开始。
声明一维数组:
int[] myArray; // 声明一个整型数组
创建和初始化一维数组:
int[] myArray = new int[5]; // 创建一个有5个元素的整型数组,默认初始化为0
int[] myArray = {1, 2, 3, 4, 5}; // 创建并初始化一个有5个元素的整型数组
访问一维数组元素:
myArray[0] = 10; // 将数组的第一个元素设置为10
int value = myArray[0]; // 读取数组的第一个元素
遍历一维数组:
for (int i = 0; i < myArray.length; i++) {
System.out.println(myArray[i]);
}
2.13.2 二维数组
二维数组可以视为数组的数组,通常用来表示表格或矩阵。
声明二维数组:
int[][] my2DArray; // 声明一个二维整型数组
创建和初始化二维数组:
int[][] my2DArray = new int[3][2]; // 创建一个3行2列的二维整型数组,默认初始化为0
int[][] my2DArray = {{1, 2}, {3, 4}, {5, 6}}; // 创建并初始化一个3行2列的二维整型数组
访问二维数组元素:
my2DArray[0][0] = 10; // 将二维数组的第一个元素设置为10
int value = my2DArray[0][0]; // 读取二维数组的第一个元素
遍历二维数组:
for (int i = 0; i < my2DArray.length; i++) {
for (int j = 0; j < my2DArray[i].length; j++) {
System.out.println(my2DArray[i][j]);
}
}
2.14 数组的特性
- 大小固定:数组一旦创建,其大小就不能改变。
- 类型安全:数组中的所有元素必须是同一种数据类型。
- 连续内存存储:数组元素存储在连续的内存位置,这使得数组访问速度快。
- 默认初始化:数组在声明时会被自动初始化为该类型的默认值(例如,int类型数组的默认值为0)。
2.15 数组排序算法
冒泡排序
冒泡排序是一种简单的排序算法,它通过重复遍历要排序的数列,比较每对相邻元素,并在顺序错误的情况下交换它们。这个过程会重复进行,直到没有再需要交换的元素为止,这意味着数列已经排序完成。
算法步骤:
- 比较相邻的两个元素,如果前一个比后一个大,则交换之。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
代码示例:
void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++)
for (int j = 0; j < n-i-1; j++)
if (arr[j] > arr[j+1]) {
// 交换 arr[j+1] 和 arr[j]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
选择排序
选择排序是一种简单直观的排序算法。它的工作原理是首先找到未排序部分的最小(或最大)元素,然后将其放到已排序序列的末尾,然后再从剩余未排序元素中继续寻找最小(或最大)元素,以此类推,直到所有元素均排序完毕。
算法步骤:
- 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
- 重复第二步,直到所有元素均排序完毕。
代码示例:
void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++) {
// 找到最小元素的索引
int min_idx = i;
for (int j = i+1; j < n; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
// 交换找到的最小元素与第i个位置的元素
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
二分查找法
二分查找法是一种在有序数组中查找特定元素的搜索算法。它通过比较数组中间元素和目标值来工作,如果中间元素正好是目标值,则搜索成功;如果目标值小于中间元素,则在数组的前半部分继续搜索;如果目标值大于中间元素,则在数组的后半部分继续搜索。这个过程会重复进行,直到找到目标值或搜索范围为空。
算法步骤:
- 首先,找到数组的中间元素。
- 如果中间元素正好是目标值,则搜索过程结束。
- 如果目标值小于中间元素,则在数组的前半部分继续搜索。
- 如果目标值大于中间元素,则在数组的后半部分继续搜索。
- 重复步骤2-4,直到找到目标值或搜索范围为空。
代码示例:
int binarySearch(int[] arr, int x) {
int l = 0, r = arr.length - 1;
while (l <= r) {
int m = l + (r - l) / 2;
// 检查中间元素是否是目标值
if (arr[m] == x)
return m;
// 如果目标值小于中间元素,则在左半部分继续搜索
if (arr[m] > x)
r = m - 1;
// 如果目标值大于中间元素,则在右半部分继续搜索
else
l = m + 1;
}
// 如果没有找到目标值,则返回-1
return -1;
}
2.16 Array工具类
下面是Arrays工具类中一些常用方法的格式化列表:
排序方法
sort(T[] a):对对象数组进行升序排序。sort(T[] a, int fromIndex, int toIndex):对对象数组的一部分进行升序排序。sort(int[] a):对整型数组进行升序排序。sort(double[] a):对浮点型数组进行升序排序。sort(long[] a):对长整型数组进行升序排序。
搜索和二分查找方法
binarySearch(T[] a, T key):在已排序的对象数组中使用二分查找法查找元素。binarySearch(T[] a, int fromIndex, int toIndex, T key):在对象数组的一部分中进行二分查找。binarySearch(int[] a, int key):在已排序的整型数组中查找元素。binarySearch(double[] a, double key):在已排序的浮点型数组中查找元素。
填充方法
fill(T[] a, T val):将指定的值填充到整个对象数组中。fill(T[] a, int fromIndex, int toIndex, T val):将指定的值填充到对象数组的一部分中。fill(int[] a, int val):将指定的值填充到整个整型数组中。fill(double[] a, double val):将指定的值填充到整个浮点型数组中。
比较方法
equals(Object[] a, Object[] a2):比较两个对象数组是否相等。equals(int[] a, int[] a2):比较两个整型数组是否相等。equals(double[] a, double[] a2):比较两个浮点型数组是否相等。
复制方法
copyOf(T[] original, int newLength):创建一个新的对象数组,并复制原数组的部分或全部元素。copyOfRange(T[] original, int from, int to):创建一个新的对象数组,包含原数组指定范围内的元素。copyOf(int[] original, int newLength):创建一个新的整型数组,并复制原数组的部分或全部元素。copyOfRange(int[] original, int from, int to):创建一个新的整型数组,包含原数组指定范围内的元素。
流操作
stream(T[] array):返回一个流,用于处理对象数组。stream(int[] array):返回一个流,用于处理整型数组。stream(double[] array):返回一个流,用于处理浮点型数组。
字符串表示方法
toString(Object[] a):返回对象数组内容的字符串表示。toString(int[] a):返回整型数组内容的字符串表示。toString(double[] a):返回浮点型数组内容的字符串表示。
深度比较方法
deepEquals(Object[] a, Object[] a2):比较两个对象数组是否深度相等。
2.17 堆内存
- 存储对象实例和数组:堆内存是Java对象和数组分配内存的主要区域。
- 垃圾回收:堆内存是垃圾回收器的主要工作区域,负责回收不再被引用的对象以释放内存。
- 跨线程共享:堆内存是所有线程共享的,因此存储在堆内存中的对象可以被多个线程访问。
- 动态内存分配:堆内存的大小可以在JVM启动时设置,并且可以在运行时动态扩展或收缩。
- 分代管理:堆内存通常分为新生代和老年代,以优化垃圾回收的效率。
2.18 栈内存
- 存储局部变量:栈内存用于存储方法执行过程中的局部变量。
- 方法调用:每个方法调用都会在栈内存中创建一个栈帧,用于存储方法调用的信息,包括参数、局部变量和返回地址。
- 线程私有:每个线程都有自己的栈内存,保证了线程的局部变量和其他线程是隔离的。
- 自动内存管理:栈内存的内存管理是自动的,当方法执行结束时,栈帧会自动弹出,局部变量随之销毁。
- 快速访问:由于栈内存是线程私有的,它避免了多线程环境下的同步问题,因此访问速度相对较快。
- 固定大小:栈内存的大小通常较小,且在JVM启动时确定,可以通过参数设置。
3面向对象程序设计基础
3.1 类的定义
-
定义:类是对象的蓝图,它定义了一组属性(成员变量)和方法(成员函数),这些属性和方法可以被对象所继承。
-
语法:
public class ClassName { // 成员变量 // 构造方法 // 方法 }
3.2 成员变量默认值
- 数值类型:默认值为0(整数类型),0.0(浮点类型),false(布尔类型)。
- 引用类型:默认值为null。
- 字符类型:默认值为'\u0000'(空字符)。
3.3 对象内存分析
- 对象创建:使用
new关键字在堆内存中创建对象。 - 引用变量:在栈内存中存储对象的引用,指向堆内存中的对象实例。
- 垃圾回收:当对象不再被引用时,可能成为垃圾回收的目标。
3.4 匿名对象
-
定义:匿名对象是指没有明确名称的对象,通常在声明时直接实例化。
-
语法:
new ClassName();
3.5 构造方法
- 作用:用于初始化新创建的对象。
- 特点:构造方法名必须与类名相同,没有返回类型。
- 重载:可以有多个构造方法,只要参数列表不同。
3.6 this关键字
- 用途:引用当前对象的上下文。
- 使用场景:
- 访问类的成员变量和方法。
- 调用其他构造方法:
this(arg);。 - 返回当前对象的引用:
return this;。
3.7 static关键字
- 静态变量:属于类,而不是对象,所有对象共享同一个静态变量。
- 静态方法:属于类,只能访问静态成员。
- 静态代码块:在类加载时执行,用于初始化静态成员。
3.8 静态方法和代码块
- 静态方法:可以通过类名直接调用,不需要创建对象。
- 静态代码块:用于初始化静态成员变量,只能声明静态变量和静态方法。
3.9 包
- 作用:用于组织类,避免命名冲突。
- 声明:使用
package关键字声明包。 - 导入:使用
import关键字导入其他包中的类。 - 默认包:没有显式声明包的类属于默认包,不推荐使用。
示例代码
package com.example; // 声明包
public class Person {
// 成员变量
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 静态变量和静态方法
private static int count = 0;
public static void incrementCount() {
count++;
}
// 实例方法
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
// 静态代码块
static void Static() {
System.out.println("Static block executed.");
}
// 主方法
public static void main(String[] args) {
Person p1 = new Person("Alice", 30); // 匿名对象
p1.introduce();
Person.incrementCount(); // 调用静态方法
}
}
4 面向对象程序设计进阶
4.1 封装
封装是面向对象编程的基本原则之一,它指的是将对象的状态(属性)和行为(方法)结合在一起,并对外隐藏对象的内部实现细节。
- 访问控制:通过访问修饰符(
public、private、protected)控制对类成员的访问。 - 信息隐藏:将类的成员变量设置为
private,提供public的getter和setter方法来访问和修改这些变量。 - 好处:减少耦合,提高代码维护性和安全性。
4.2 继承
继承是一种创建新类的方式,新类继承了父类的属性和方法,允许代码复用。
- 语法:使用
extends关键字。 - 特点:Java只支持单继承,但可以通过实现多个接口来实现类似多继承的行为。
- 方法覆盖(Override):子类可以提供与父类同名的方法实现,称为方法覆盖。
4.3 super关键字
super关键字用于引用父类(超类)的属性和方法。
- 调用父类的构造方法:
super()或super(param)。 - 访问父类的属性和方法:
super.variable或super.method()。
4.4 final关键字
final关键字用于表示某个实体不可改变。
- final变量:一旦赋值后,其值不能被改变。
- final方法:不能被子类覆盖。
- final类:不能被继承。
4.5 Object类
Object类是Java中所有类的根类,提供了一些通用的方法,如equals()、hashCode()、toString()等。
- 方法重写:可以通过重写
Object类的方法来改变对象的默认行为。 clone()方法:用于对象的复制,需要实现Cloneable接口。
4.6 多态
多态是指允许不同类的对象对同一消息做出响应的能力,即同一个接口,使用不同的实例而执行不同操作。
- 方法覆盖:子类覆盖父类的方法。
- 接口实现:不同的类实现同一个接口,但提供各自的实现。
- 向上转型(Upcasting):将子类对象赋值给父类引用。
- 向下转型(Downcasting):将父类引用转换为子类类型。
4.7 抽象类
抽象类是不能被实例化的类,通常用作基类。
- 抽象方法:没有方法体的方法,子类必须提供具体实现。
- 非抽象方法:可以有具体的方法实现。
- 使用:
abstract关键字声明。
4.8 接口
接口是定义方法规范的一种机制,任何实现该接口的类都必须实现接口中定义的所有方法。
- 默认方法(Default Methods):从Java 8开始,接口可以包含默认方法实现。
- 静态方法:从Java 8开始,接口可以包含静态方法。
- 多实现:一个类可以实现多个接口,实现多继承的效果。
4.9 内部类
内部类是定义在另一个类中的类。
- 成员内部类:作为外部类的一个成员,可以访问外部类的所有成员,包括私有成员。
- 静态内部类:使用
static修饰的内部类,不依赖于外部类的实例。 - 局部内部类:定义在方法或代码块中的类,只能在此方法或代码块中使用。
- 匿名内部类:没有名字的内部类,常用于单次实现接口或继承其他类。
5 异常
Java中的异常处理是一种结构,用于处理程序执行过程中发生的异常情况。异常是程序运行时发生的不正常情况,它可能会导致程序流程中断或错误。Java通过异常处理机制提供了一种优雅的方式来处理这些异常情况,确保程序的健壮性和稳定性。
5.1 异常类层次结构
Java的异常层次结构基于Throwable类,这是所有错误和异常的基类。Throwable有两个重要的子类:Exception(异常)和Error(错误)。
- Error:表示JVM无法处理的问题,如
OutOfMemoryError、StackOverflowError等。通常,开发者不需要捕获这些错误,因为它们通常是灾难性的,无法恢复。 - Exception:可以被程序处理的异常。
Exception又分为两类:- 受检异常(Checked Exception):必须被程序显式处理的异常,如
IOException、SQLException等。 - 非受检异常(Unchecked Exception):不需要被程序显式处理的异常,包括
RuntimeException及其子类,如NullPointerException、ArrayIndexOutOfBoundsException等。
- 受检异常(Checked Exception):必须被程序显式处理的异常,如
5.2 异常处理关键字
Java提供了几个关键字来处理异常:
- try:用于包裹可能抛出异常的代码块。
- catch:用于捕获并处理
try块中抛出的异常。 - finally:无论是否捕获异常,都会执行的代码块,常用于资源释放。
- throw:用于手动抛出异常。
- throws:用于声明方法可能抛出的异常。
5.3 异常处理流程
- try块:包含可能抛出异常的代码。
- catch块:捕获并处理特定类型的异常。
- finally块:无论是否捕获异常,都会执行的代码块,通常用于资源清理。
示例代码
try {
// 尝试执行的代码,可能会抛出异常
int divisionResult = 10 / 0;
} catch (ArithmeticException e) {
// 处理特定类型的异常
System.out.println("发生算术异常:" + e.getMessage());
} catch (Exception e) {
// 处理其他类型的异常
System.out.println("发生异常:" + e.getMessage());
} finally {
// 无论是否发生异常,都会执行的代码
System.out.println("这是finally块");
}
5.4 自定义异常
开发者可以创建自己的异常类,通常是通过继承Exception类或其子类来实现。
public class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}
// 使用自定义异常
try {
throw new MyCustomException("自定义异常消息");
} catch (MyCustomException e) {
System.out.println(e.getMessage());
}
6 Java常用类
6.1 包装类
包装类是Java为基本数据类型提供的类包装,使得基本数据类型可以用作对象。
-
基本数据类型对应的包装类:
Integer:对应基本类型intDouble:对应基本类型doubleFloat:对应基本类型floatLong:对应基本类型longShort:对应基本类型shortByte:对应基本类型byteCharacter:对应基本类型charBoolean:对应基本类型boolean
-
自动装箱与拆箱:
- 自动装箱(Autoboxing):自动将基本类型转换为对应的包装类对象。
- 自动拆箱(Unboxing):自动将包装类对象转换为对应的基本类型。
6.2 String类
String类在Java中用于表示字符串。
- 不可变性:
String对象是不可变的,任何修改操作都会产生新的String对象。 - 字符串连接:使用
+操作符或concat()、StringBuilder/StringBuffer。 - 字符串池:字符串字面量存储在字符串池中,相同内容的字符串字面量共享相同的内存空间。
6.3 StringBuffer类与StringBuilder类
这两个类都用于可变的字符串操作。
-
StringBuffer:- 线程安全的可变字符串类。
- 方法如
append()、insert()、replace()等会改变原有字符串内容。 - 由于线程安全,性能较
StringBuilder稍低。
-
StringBuilder:- 非线程安全的可变字符串类。
- 性能优于
StringBuffer,适用于单线程环境。 - 方法与
StringBuffer类似,但不受线程安全限制。
6.4 时间相关类
LocalDate、LocalTime、LocalDateTime:分别表示无时区的日期、时间、日期时间。ZonedDateTime:表示带时区的日期时间。Instant:表示时间戳,即自1970年1月1日UTC以来的秒数。Duration和Period:分别表示时间持续和日期持续。
6.5 Random类
Random类用于生成伪随机数。
- 随机数生成:提供
nextInt()、nextDouble()等方法生成随机数。 - 种子设置:可以通过构造函数设置种子,以产生可重复的随机数序列。
6.6 UUID类
UUID类用于生成统一唯一标识符(Universally Unique Identifier)。
- 随机UUID:使用
randomUUID()方法生成随机UUID。 - UUID结构:由128位长,分为5部分,包括时间戳、时钟序列、节点和随机或伪随机生成的数字。
示例代码
// 包装类
Integer integer = 10; // 自动装箱
int value = integer; // 自动拆箱
// String
String str = "Hello, World!";
// StringBuffer & StringBuilder
StringBuffer stringBuffer = new StringBuffer("Hello");
StringBuilder stringBuilder = new StringBuilder("Hello");
stringBuffer.append(" World!");
stringBuilder.append(" World!");
System.out.println(stringBuffer); // Hello World!
System.out.println(stringBuilder); // Hello World!
// 时间相关类
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
// Random
Random random = new Random();
int randomNumber = random.nextInt(100);
// UUID
UUID uuid = UUID.randomUUID();
System.out.println(uuid);
这些类和机制是Java编程中常用的工具,掌握它们对于日常编程任务至关重要。
个人总结
在过去的一段时间里,我深入学习了Java编程语言,从基础语法到高级特性,对Java有了全面的认识。Java作为一种成熟且功能强大的语言,其跨平台能力和面向对象的特性给我留下了深刻印象。我了解到Java的发展历史,从最初的“Green计划”到现在成为企业级开发的主流语言,Java的演变过程充满了创新和适应性。
在学习过程中,我对Java的基本数据类型、控制结构、方法和异常处理有了扎实的掌握。我学会了如何使用条件语句和循环来控制程序流程,以及如何定义和调用方法来重用代码。Java的异常处理机制让我学会了如何优雅地处理程序中的错误和异常情况。
面向对象编程是Java的核心,我掌握了类和对象的概念,以及如何通过封装、继承和多态性来构建复杂的软件系统。我还学习了抽象类和接口的使用,这些机制允许我创建灵活和可扩展的代码架构。
我对Java的常用类有了深入的了解,包括包装类、String类、StringBuffer和StringBuilder类,以及如何处理日期和时间。我还探索了Random类和UUID类,这些类在生成随机数和唯一标识符时非常有用。
这段学习经历不仅增强了我的编程技能,也提高了我的问题解决能力。我期待将这些知识应用到实际项目中,继续深化我的Java编程实践。

浙公网安备 33010602011771号