第一章“初识JAVA与面向对象程序设计”
廖利凯
1、JAVA概述
1.1 计算机编程语言发展史
机器语言:由微处理器理解和使用的二进制代码,是第一代编程语言,如0001 1111 1110 1111等,直接控制硬件操作,但难记且不通用。
汇编语言:用英文单词指令编写程序,是第二代编程语言,如MOV AX, 1等,相比机器语言更易理解,但仍依赖硬件且指令因平台而异,使用门槛较高。
高级语言:类似自然语言和数学公式,如num1 = int(input("加数:"))(Python 示例),是第三代编程语言,编程难度低,促进了软件发展,包括面向过程(如 Fortran、PASCAL、C 等)和面向对象(如 C++、Java、C# 等)两类。
1.2 Java 语言发展史
1990 年末,Sun 公司启动 “Green 计划”,由詹姆斯・高斯林领导,旨在为智能家电开发通用控制系统,创造了 Oak 语言(Java 前身)。
1995 年推出 Java 测试版,1996 年发布正式版 JDK1.0,此后发展迅速,历经多个版本改进,如 1997 年的 JDK1.1、1998 年的 Java 1.2(更名为 Java 2)等。
1999 年 Java 分为标准版 J2SE、企业版 J2EE 和微型版 J2ME,JSP/Servlet 技术诞生。2005 年版本名称变更,J2EE 改为 Java EE,J2SE 改为 Java SE,J2ME 改为 Java ME。
2009 年 SUN 公司被 Oracle 收购,“Java 语言之父” 詹姆斯・高斯林于 2010 年辞职。Oracle 继续推动 Java 发展,发布了多个 Java SE 版本,如 2011 年的 Java SE 7、2014 年的 Java SE 8、2017 年的 Java SE 9 等,至今仍在持续更新,目前最受欢迎的版本是 Java SE 8 和 Java SE 11。
1.3 Java 语言特点
简单性:继承 C 和 C++ 优点,摒弃复杂易错部分,如取消指针、手动内存管理等,提供自动垃圾回收机制,语法清晰,规模小,学习和使用相对容易。
面向对象:完全面向对象,具备抽象、封装、继承和多态特性,支持类的单继承和接口多继承,以及类与接口实现机制和动态绑定,代码可维护性、复用性和扩展性强,如创建类和对象实现功能。
跨平台性:通过 Java 虚拟机(JVM)实现,源程序编译为字节码文件(.class),可在装有 JVM 的不同平台运行,真正做到 “Write once, run anywhere”。
可移植性:依赖跨平台性,JVM 对其提供直接支持,Java 编译器和运行环境具有可移植性,且严格规定基本数据类型长度,编写程序可在多操作系统使用。
安全性:数据结构为完整对象,提供数组下标检测、屏蔽指针、自动内存管理、运行时类加载和字节码校验等机制,还设置权限和安全机制,保障程序和系统安全。
健壮性:设计初衷为高可靠稳健软件,通过类型安全检查、异常处理和自动垃圾收集保证,吸收其他语言优点,避免易出错部分,检查编译和运行时错误,操纵内存减少出错可能。
编译和解释性:先编译后解释,源程序编译为字节码,运行时由解释器解释执行,字节码接近机器码提高解释速度,但比全编译语言慢,不过可通过 “及时(JIT)” 编译程序实现全编译提升性能。
高性能:字节码格式便于 JIT 编译生成机器代码,运行速度随 JIT 技术发展接近 C++,比解释型脚本语言性能高,在性能要求高的场景下可通过 JIT 优化。
动态性:程序所需类可动态载入运行环境,支持网络载入,类库新增内容不影响原程序执行,利于软件升级,如动态加载新类实现功能扩展。
多线程:允许应用程序同时存在多个线程,提高执行效率,共享公共内存区域,Java 提供类和方法管理多线程,支持同步机制确保线程安全操作共享数据,如多线程实现并发任务处理。
分布性:支持分布式计算和网络编程,提供网络应用编程接口(java.net),包括 URL、Socket 等类,支持客户机 / 服务器模式和 RMI 机制,实现操作和数据分布,如开发分布式应用系统。
2. 面向对象程序设计思想
2.1 面向过程程序设计
核心是分析问题解决步骤,用函数实现步骤,按流程调用,以过程为核心,注重事件流程顺序,适合小型应用开发,如单片机、嵌入式开发,但代码可维护性、可读性、复用性和扩展性较差。
2.2 面向对象程序设计
将问题事物分解为对象,描述对象在问题解决中的行为,具有抽象、封装、继承和多态特性,符合 “高内聚、低耦合” 主旨,适合大型商业应用开发,如 APP 开发,虽宏观把控复杂但微观仍需面向过程实现具体功能,代码可维护性等方面优于面向过程。
2.3 面向对象与面向过程程序设计的比较
以计算三角形和矩形周长为例,面向过程先输入三角形三边计算周长,再输入矩形长宽计算周长;面向对象则先分析参与事物(三角形和矩形),创建类并定义属性(边、长、宽)和行为(计算周长方法),最后调用方法计算。面向对象看似繁琐但维护成本低,适合复杂项目开发。
3. Java 开发环境搭建
3.1 JDK 与 JRE
JRE(Java Runtime Environment):Java 程序运行环境,包含 Java 虚拟机和基础类库,计算机运行 Java 程序至少需安装 JRE。
JDK(Java Development Kit):Java 开发工具包,包含 JRE、编译器及调试分析工具,用于开发 Java 程序。若仅运行 Java 程序,安装 JRE 即可;若开发 Java 程序,则需安装 JDK。
3.2 JDK 安装
可从 SUN 公司官网(现 Oracle 官网https://www.oracle.com/java/technologies/)下载指定版本 JDK,如 Java SE 8u421 等,根据操作系统和机器位数选择合适安装包,下载后安装,安装完成后需重启计算机使配置生效。
3.3 环境变量配置
新建环境变量 JAVA_HOME:设置为 JDK 安装路径,如C:\Program Files\java\jdk1.8.0_131。
编辑 PATH 环境变量:加入 Java 的 bin 目录(%JAVA_HOME%\bin),使系统能在任意路径找到 Java 命令。
验证配置:在命令行窗口输入java -version,查看 Java 版本,若显示版本信息则配置成功。
4. 第一个 Java 程序:HelloWorld!
4.1 显示文件扩展名
在 Windows 系统中,若新建文本文件默认不显示扩展名,需开启文件扩展名显示。以 Windows 10 为例,在文件资源管理器中单击 “查看”,勾选 “文件扩展名”,然后将新建文本文件命名为HelloWorld.java。
4.2 编写代码
右击HelloWorld.java文件,选择 “编辑”,输入以下代码:
public class HelloWorld{
public static void main(String[] args){
System.out.println("HelloWorld!");
}
}
4.3 编译与执行
在当前文件所在目录打开命令行窗口(在文件管理器上方输入cmd并回车),输入javac HelloWorld.java进行编译,无输出则编译成功,生成HelloWorld.class字节码文件;再输入java HelloWorld执行程序,控制台输出HelloWorld!。
4.4 代码解析
public class HelloWorld:声明公共类,类名需与文件名一致,遵循类名命名规范(首字母大写,单词间首字母大写,由数字、字母、下划线、美元符号组成,首字符不能为数字)。
public static void main(String[] args):程序入口方法,写法固定,args为命令行参数数组。
System.out.println("HelloWorld!");:输出HelloWorld!到控制台,System.out是标准输出流对象,println是输出方法,语句以分号结束,Java 代码对大小写敏感。
4.5 代码的注释
单行注释:以//开头,其后内容为注释,如// 这是单行注释。
多行注释:以/开头,以/结尾,中间内容为注释,可换行,如/* 这是多行注释 /。
文档注释:以/**开头,以/结尾,包含说明文字和 JavaDoc 标签,用于生成 API 文档,如 /** 这是文档注释 */。
5. Java 常用开发工具
5.1 Eclipse 的安装与使用
可从https://www.eclipse.org/downloads/下载 Eclipse 安装包,根据操作系统选择合适版本,安装过程按提示操作,安装完成后可启动使用,是一款常用的 Java 集成开发环境(IDE),提供代码编辑、编译、调试等功能,支持多种插件扩展功能,方便 Java 项目开发。
5.2 IntelliJ IDEA 的安装与使用
从https://www.jetbrains.com.cn/en-us/idea/download/?section=windows/downloads/下载 IntelliJ IDEA,有旗舰版(Ultimate)和社区版(Community Edition),旗舰版功能更强大,社区版免费且满足基本 Java 开发需求。下载对应版本安装包后按提示安装,安装完成后启动,可创建 Java 项目、编写代码、运行调试程序等,是一款智能高效的 Java IDE,提供丰富的代码提示、自动完成、重构等功能,提高开发效率。
此章个人理解与学习心得
学习本章让我对 Java 有了全面的认识,仿佛打开了一扇通往编程新世界的大门。Java 语言的发展历程犹如一部科技进步史,见证了它从诞生到不断完善的过程,这使我意识到技术的发展是持续演进的,需要不断学习才能跟上步伐。
Java 的特点是其强大的基石。跨平台性让我惊叹不已,它打破了操作系统的限制,使程序能够自由穿梭于不同环境,这种灵活性为软件开发带来了极大便利。面向对象的特性则是一种全新的思维模式,刚开始理解时有些困难,但随着深入学习,我逐渐体会到它在构建复杂系统时的优势,就像搭建积木一样,每个对象都是一个独立的模块,组合起来就能创造出丰富多彩的功能。
编写 “HelloWorld” 程序是一次有趣的实践,从简单的代码中感受到了 Java 程序的运行机制。同时,也明白了代码规范和注释如同程序的 “说明书”,不可或缺。开发环境搭建过程虽有些繁琐,但成功配置后带来的成就感难以言表,这也让我认识到良好的开发环境是高效编程的前提。
对于常用开发工具,我意识到它们是提升编程效率的得力助手,不同工具各有千秋,选择适合自己的工具能够事半功倍。这一章的学习为我后续深入探索 Java 编程世界注入了强大动力,我将继续努力,在实践中不断提升自己对 Java 的理解和应用能力。
第二章“JAVA编程基础”
一、变量与常量
1.1 关键字和保留字
关键字:预先定义好的有特别意义的单词,如public、class、void等。
保留字:预先保留,在以后版本中可能使用的特殊标识符,如goto、const等。
1.2标识符与命名规范
只能由字母、数字、下划线、美元符号组成,且不能以数字开头。
不可以是 Java 关键字和保留字。
大小写敏感,长度无限制,变量名采用小写字母开头的驼峰规则,常量名所有字母大写,单词间用 “_” 隔开。
1.3数据类型
基本类型:
整数型:byte(1 字节,-128 ~ 127)、short(2 字节,-32768 ~ 32767)、int(4 字节)、long(8 字节)。
浮点型:float(4 字节)、double(8 字节)。
字符型:char(2 字节,能表示任何 Unicode 字符,可与int在一定范围内互相转换)。
布尔型:boolean(只有true和false两个值,官方未明确指出占用空间大小)。
引用类型:类、数组等。
1.4变量的定义与赋值
变量声明:数据类型 变量名;或数据类型 变量名1, 变量名2, 变量名3;。
赋值:变量名 = 变量值;或数据类型 变量名 = 变量值;或数据类型 变量名1 = 变量值1,变量名2 = 变量值2,变量名3 = 变量值3。
示例:
int num;
num = 10;
int num2 = 20;
int a, b, c;
a = 1; b = 2; c = 3;
1.5常量
声明:final 数据类型 常量名 = 常量值;或先声明后赋值(保证只赋值一次)。
示例:final double PI = 3.14159;
1.6 变量的类型转换
自动类型转换(隐式转换):占用字节数少的数据类型可直接赋值给占用字节数多的数据类型变量,如short类型值可赋给int类型变量,int类型值可赋给double类型变量;特例是int类型常量在不超范围时可直接赋给char、short、byte。
强制类型转换(显式转换):语法为(数据类型)变量值,可能会丢失数据精度。
示例:
int num1 = 10;
double num2 = num1;
double d = 3.14;
int i = (int)d;
1.7 Scanner 的使用
用于获取用户键盘输入内容,在java.util包下,使用时需创建对象,再通过相应方法获取不同类型输入。
示例:
二、运算符与表达式
2.1 算术运算符
一元运算符:++(自增)、--(自减)。
二元运算符:+(加法或字符串拼接)、-(减法)、*(乘法)、/(除法,整数相除结果为整数)、%(取模,求余数)。
示例:
int a = 5;
int b = 2;
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);
int c = 5;
System.out.println(c++);
System.out.println(++c);
2.2 赋值运算符
基本赋值运算符:=。
扩展赋值运算符:+=、-=、*=、/=、%=。
示例:
int num = 10;
num += 5;
num *= 2;
2.3关系运算符
用于比较,结果为布尔类型,包括>(大于)、<(小于)、==(等于)、>=(大于等于)、<=(小于等于)、!=(不等于)。
示例:
int a = 5;
int b = 10;
System.out.println(a > b);
System.out.println(a < b);
System.out.println(a == b);
System.out.println(a!= b);
2.4逻辑运算符
用于连接布尔值,表示 “与、或、非” 关系,包括&&(简洁逻辑与)、&(非简洁逻辑与)、||(简洁逻辑或)、|(非简洁逻辑或)、!(逻辑非)。
示例:
boolean flag1 = true;
boolean flag2 = false;
System.out.println(flag1 && flag2);
System.out.println(flag1 || flag2);
System.out.println(!flag1);
2.5位运算符
直接对二进制进行计算,包括<<(左移)、>>(右移)、>>>(无符号右移)、&(与运算)、|(或运算)、^(异或运算)、~(按位取反运算)。
示例:
int a = 5; // 二进制为0101
int b = 3; // 二进制为0011
System.out.println(a << 1);
System.out.println(a >> 1);
System.out.println(a & b);
System.out.println(a | b);
System.out.println(a ^ b);
System.out.println(~a);
2.6三元运算符
语法:条件表达式? 表达式1 : 表达式2;,根据条件表达式结果选择返回表达式 1 或表达式 2 的值。
示例:
int a = 10;
int b = 20;
int max = (a > b)? a : b;
System.out.println(max);
2.7运算符优先级
优先级从高到低依次为:括号、正负号、一元运算符、乘除、加减、移位运算、比较大小、比较是否相等、按位与运算、按位异或运算、按位或运算、逻辑与运算、逻辑或运算、三元运算符、赋值运算符。
三、选择结构
3.1. if 语句
语法:
if(条件表达式1) {
// 代码块1
} else if(条件表达式2) {
// 代码块2
} else if(条件表达式3) {
// 代码块3
} else {
// 代码块n
}
示例:判断分数等级
import java.util.Scanner;
public class Demo11IF {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入分数: ");
int score = scanner.nextInt();
if (score >= 90 && score <= 100) {
System.out.println("优秀");
} else if (score >= 70 && score < 90) {
System.out.println("良好");
} else if (score >= 60 && score < 70) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
scanner.close();
}
}
3.2 switch 语句
语法:
switch (变量) {
case 值1:
代码块1;
break;
case 值2:
代码块2;
break;
case 值3:
代码块3;
break;
default:
代码块n;
break;
}
示例:计算器
import java.util.Scanner;
public class Demo12Switch {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入第一个数字: ");
double num1 = scanner.nextDouble();
System.out.print("请输入运算符(+、-、*、/): ");
String operator = scanner.next();
System.out.print("请输入第二个数字: ");
double num2 = scanner.nextDouble();
double result = 0;
switch (operator) {
case "+":
result = num1 + num2;
break;
case "-":
result = num1 - num2;
break;
case "*":
result = num1 * num2;
break;
case "/":
if (num2!= 0) {
result = num1 / num2;
} else {
System.out.println("除数不能为0");
}
break;
default:
System.out.println("无效的运算符");
}
if (operator.matches("[+\\-*/]")) {
System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
}
scanner.close();
}
}
3.3 选择结构的嵌套
在if或switch语句中可以嵌套另一种选择结构,用于判断更复杂的逻辑。
示例:判断闰年
import java.util.Scanner;
public class Demo14IF {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入年份: ");
int year = scanner.nextInt();
if ((year % 400 == 0) || ((year % 4 == 0) && (year % 100!= 0))) {
System.out.println(year + " 是闰年");
} else {
System.out.println(year + " 不是闰年");
}
scanner.close();
}
}
3.4两种选择结构对比
if语句适用于区间值判断,switch语句适用于确定值判断,凡是switch能实现的,if语句都能实现,但反之不一定。
四、循环结构
4.1 for 语句
语法:
for(循环初始化表达式; 循环条件表达式; 循环后的操作表达式) {
// 循环体
}
示例:输出 1 - 1000 中能被 5 和 3 整除的数
public class Demo15For {
public static void main(String[] args) {
int count = 0;
for (int i = 1; i <= 1000; i++) {
if (i % 5 == 0 && i % 3 == 0) {
System.out.print(i + " ");
count++;
if (count % 5 == 0) {
System.out.println();
}
}
}
}
}
4.2 while 语句
语法:
while (条件表达式) {
// 循环体
}
示例:找出前 10 个能被 7 和 11 整除的数
public class Demo17Dowhile {
public static void main(String[] args) {
int count = 0;
int i = 1;
while (count < 10) {
if (i % 7 == 0 && i % 11 == 0) {
System.out.print(i + " ");
count++;
}
i++;
}
}
}
4.3 do…while 语句
语法:
do {
// 循环体
} while(条件表达式);
示例:模拟登录
import java.util.Scanner;
public class Demo17DoWhile {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String username;
String password;
do {
System.out.print("请输入用户名: ");
username = scanner.next();
System.out.print("请输入密码: ");
password = scanner.next();
if (!username.equals("admin") ||!password.equals("123456")) {
System.out.println("用户名或密码错误,请重新登录");
}
} while (!username.equals("admin") ||!password.equals("123456"));
System.out.println("登录成功");
scanner.close();
}
}
4.4 break 和 continue 语句
break用于强行退出循环,不执行循环中剩余语句;continue只能终止某次循环,继续下一次循环。
示例:韩信点兵
public class Demo18While {
public static void main(String[] args) {
int i = 1;
while (true) {
if (i % 3 == 2 && i % 5 == 3 && i % 7 == 4 && i % 2 == 0) {
System.out.println("最少需要 " + i + " 名士兵");
break;
}
i++;
}
}
}
4.5 循环语句的嵌套
循环结构可以任意嵌套,用于解决更复杂的问题。
示例:寻找前 20 个素数并按每行 5 个输出
public class Demo19For {
public static void main(String[] args) {
int count = 0;
for (int i = 2; count < 20; i++) {
boolean isPrime = true;
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
System.out.print(i + " ");
count++;
if (count % 5 == 0) {
System.out.println();
}
}
}
}
}
4.6 三种循环结构应用场景
for循环适用于确定循环次数的场景;while循环适用于不确定循环次数或想让循环永远执行的场景;do...while循环适用于让循环体至少执行一次的场景。
示例:运用所学的三种循环语句,编程实现分别计算1.01、1.02和0.99、0.98的365次方的值
使用for循环
public class Wake_up_the_formula_of_your_life
{
public static void main(String[] args) {
double num1 = 1.01;
double num2 = 1.02;
double num3 = 0.99;
double num4 = 0.98;
double result1 = 1;
double result2 = 1;
double result3 = 1;
double result4 = 1;
int i = 0;
// 计算1.01的365次方
do {
result1 *= num1;
i++;
} while (i < 365);
i = 0;
// 计算1.02的365次方
do {
result2 *= num2;
i++;
} while (i < 365);
i = 0;
// 计算0.99的365次方
do {
result3 *= num3;
i++;
} while (i < 365);
i = 0;
// 计算0.98的365次方
do {
result4 *= num4;
i++;
} while (i < 365);
System.out.println("1.01的365次方: " + result1);
System.out.println("1.02的365次方: " + result2);
System.out.println("0.99的365次方: " + result3);
System.out.println("0.98的365次方: " + result4);
}
}
使用while循环
public class PowerCalculationWithWhile {
public static void main(String[] args) {
double num1 = 1.01;
double num2 = 1.02;
double num3 = 0.99;
double num4 = 0.98;
double result1 = 1;
double result2 = 1;
double result3 = 1;
double result4 = 1;
int i = 0;
// 计算1.01的365次方
while (i < 365) {
result1 *= num1;
i++;
}
i = 0;
// 计算1.02的365次方
while (i < 365) {
result2 *= num2;
i++;
}
i = 0;
// 计算0.99的365次方
while (i < 365) {
result3 *= num3;
i++;
}
i = 0;
// 计算0.98的365次方
while (i < 365) {
result4 *= num4;
i++;
}
System.out.println("1.01的365次方: " + result1);
System.out.println("1.02的365次方: " + result2);
System.out.println("0.99的365次方: " + result3);
System.out.println("0.98的365次方: " + result4);
}
}
使用do...while循环
public class PowerCalculationWithDoWhile {
public static void main(String[] args) {
double num1 = 1.01;
double num2 = 1.02;
double num3 = 0.99;
double num4 = 0.98;
double result1 = 1;
double result2 = 1;
double result3 = 1;
double result4 = 1;
int i = 0;
// 计算1.01的365次方
do {
result1 *= num1;
i++;
} while (i < 365);
i = 0;
// 计算1.02的365次方
do {
result2 *= num2;
i++;
} while (i < 365);
i = 0;
// 计算0.99的365次方
do {
result3 *= num3;
i++;
} while (i < 365);
i = 0;
// 计算0.98的365次方
do {
result4 *= num4;
i++;
} while (i < 365);
System.out.println("1.01的365次方: " + result1);
System.out.println("1.02的365次方: " + result2);
System.out.println("0.99的365次方: " + result3);
System.out.println("0.98的365次方: " + result4);
}
}
五、方法
5.1 方法介绍
定义在类中的具有特定功能的独立小程序,用于完成某个功能操作,可将程序功能拆分成多个方法,提高代码维护性。
5.2 方法声明与调用
声明语法:
修饰符 返回值类型 方法名(参数类型 参数名1, 参数类型 参数名2,...) {
// 方法体
return 返回值;
}
调用语法:方法名(实际参数1, 实际参数2,...);
示例:
无返回值方法:判断三角形
public class Demo21Triangle {
public static void main(String[] args) {
isTriangle(3, 4, 5);
}
public static void isTriangle(int a, int b, int c) {
if (a + b > c && a + c > b && b + c > a) {
System.out.println("三角形的三条边为
示例:
有返回值方法:判断疫情风险等级。
import java.util.Scanner;
public class Demo22Disease {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入确诊人数:");
int count = scanner.nextInt();
String riskLevel = getRiskLevel(count);
System.out.println("风险等级为:" + riskLevel);
scanner.close();
}
public static String getRiskLevel(int count) {
if (count < 100) {
return "低风险";
} else if (count >= 100 && count <= 1000
5.3 方法重载
概念:在同一类中,可存在多个同名方法,只要参数个数或参数类型不同。与返回值类型、形参名称、方法修饰符无关。
示例:计算两数之和的重载方法。
public class Demo23Overload {
public static void main(String[] args) {
int num1 = 5;
int num2 = 10;
double num3 = 3.5;
double num4 = 2.5;
System.out.println(add(num1, num2));
System.out.println(add(num3, num4));
}
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
}
5.4方法递归
概念:方法直接或间接调用自身。合理使用可解决一些循环难以解决的问题,但要注意防止栈溢出。
示例:计算斐波那契数列指定位置的值。
import java.util.Scanner;
public class Demo22Fibonacci {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个数字:");
int n = scanner.nextInt();
long result = fibonacci(n);
System.out.println("斐波那契数列第" + n + "项的值为:" + result);
scanner.close();
}
public static long fibonacci(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
六、数组
6.1 数组概述
一种存放相同数据类型的有序集合,是存储数据的容器。创建方式有三种。
示例:
//方式一
int[] arr1 = new int[5];
//方式二
int[] arr2 = new int[]{1, 2, 3, 4, 5};
//方式三
int[] arr3 = {1, 2, 3, 4, 5};
6.2 数组的常见操作
通过索引操作元素:索引范围是 [0, 数组长度 - 1],超出范围会抛出ArrayIndexOutOfBoundsException异常。
数组的遍历
使用for循环:
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
使用foreach循环(增强for循环):
int[] arr = {1, 2, 3, 4, 5};
for (int num : arr) {
System.out.println(num);
}
获取数组的最值:假设法,先假设一个最值,遍历数组比较更新。
通过值获取索引:遍历数组查找,若未找到返回 -1。
数组元素的反转:借助新数组或在原数组上交换元素。
6.3 数组排序算法
冒泡排序:相邻元素比较交换,使较大元素下沉,较小元素上浮。
选择排序:每次选择未排序部分的最小(或最大)元素与当前位置元素交换。
6.4 二分查找法
适用于有序数组,每次查找中间值,根据比较结果决定在左或右半边继续查找,性能较好,但数组需有序且插入、删除操作困难。
6.5 方法中的可变参数
当方法参数个数不确定但类型确定时使用,语法:数据类型... 参数名,实际处理时当作数组。
示例:求和方法。
public class Demo34Method {
public static void main(String[] args) {
System.out.println(add(1, 2, 3, 4, 5));
}
public static int add(int... nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
return sum;
}
}
6.6 二维数组
每一个元素都是一个数组,可用于表示多维数据结构。
示例:计算三个班级学生平均成绩。
import java.util.Scanner;
public class Demo35Array {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[][] scores = new int[3][];
scores[0] = new int[3];
scores[1] = new int[4];
scores[2] = new int[5];
for (int i = 0; i < scores.length; i++) {
System.out.println("请输入第" + (i + 1) + "个班级的成绩:");
for (int j = 0; j < scores[i].length; j++) {
scores[i][j] = scanner.nextInt();
}
}
for (int i = 0; i < scores.length; i++) {
int sum = 0;
for (int j = 0; j < scores[i].length; j++) {
sum += scores[i][j];
}
double average = (double) sum / scores[i].length;
System.out.println("第" + (i + 1) + "个班级的平均成绩为:" + average);
}
scanner.close();
}
}
6.7. Arrays 工具类
提供了操作数组的便捷方法,如toString(将数组转换为字符串)、equals(比较两个数组内容是否相同)、sort(对数组元素排序)、binarySearch(二分查找元素索引)。
七、JVM 中的堆内存与栈内存
7.1堆和栈
栈内存:存储局部变量和对象引用,由系统自动分配,性能高,具有先进后出特点。每个线程有独立的虚拟机栈,方法执行时创建栈帧存储局部变量,方法执行完毕栈帧弹栈。
堆内存:存储引用类型数据(对象和数组),全局唯一,所有线程共用。对象或数组在堆中产生后,栈中可定义引用变量指向其在堆中的首地址。
7.2 数据类型传递
Java 只有值传递,引用类型变量传递的是对象在堆内存中的地址值。
7.3 方法中的数据交换
交换变量值不成功,交换数组中元素值成功,原因是变量交换是在不同栈帧中操作不同变量,而数组交换是通过引用变量操作堆内存中的同一数组元素。
此章个人理解与学习心得
此章全面且细致地阐述了 Java 编程的基础要素,让我对 Java 语言有了更深入的理解。
变量与常量作为数据存储的基本方式,其规范和类型体系为程序的数据处理奠定了基础。在使用中,我明确了变量的灵活多变以及常量的不可变性,同时掌握了类型转换的规则,避免数据精度丢失。
运算符与表达式丰富多样,从算术到逻辑,从位运算到三元运算符,它们赋予了程序强大的计算和判断能力。不同运算符的优先级和结合性,使程序的执行顺序更加清晰。
选择和循环结构则是程序流程控制的关键,if和switch的条件判断,for、while、do...while的循环执行,让程序能够根据不同情况做出决策并重复执行特定任务。
方法的引入提高了代码的复用性和可读性,通过合理定义方法参数和返回值类型,能够更好地组织程序逻辑。
数组作为存储相同类型数据的容器,其各种操作和排序算法的学习,让我能够更高效地处理批量数据。
最后,对 JVM 中堆内存和栈内存的理解,使我明白了数据在内存中的存储和操作机制,特别是值传递和引用传递的特点,对优化程序性能和避免错误有很大帮助。这一章的学习为我进一步探索 Java 编程世界打开了坚实的大门。
第三章“面向对象程序设计(基础)”
一、面向对象的概念
1.1面向对象思维方式
以更符合人们思考习惯的方式分析和解决问题,涉及事物或对象时,创建类,定义属性和行为,按需创建对象并调用行为。
1.2 类和对象
万物皆对象,现实事物抽象后形成类。类是抽象概念,对象是具体实例。如 “桌子” 是类,“某张桌子” 是对象;学生类包含姓名、年龄等属性和学习、睡觉等行为。
1.3 面向对象特性
封装:隐藏对象属性和实现细节,仅提供公共访问方式。
继承:子类继承父类特征和行为,拥有父类实例属性和方法或相同行为。
多态:同一方法调用,因对象不同行为可能不同。面向对象代码具有高可维护性、可读性、复用性和可扩展性,但性能相对面向过程偏低。
二、面向对象编程
2.1类的定义
使用class关键字定义类,一个 Java 文件中可多个类,但最多一个public修饰的类且类名与文件名一致,类名遵循大写字母开头的驼峰规则。
类由变量(字段)和方法组成,变量定义格式为修饰符 变量类型 变量名 = [默认值];,方法定义格式为修饰符 返回值类型 方法名(形参列表){}。
示例:创建Student类,包含name和age变量,以及eat()和study()方法。
package com.yyds.unit3.demo;
public class Student {
// 成员变量
String name;
int age;
// 成员方法
void eat(String food) {
System.out.println(name + "吃" + food);
}
void study() {
System.out.println(name + "年龄" + age + "岁,在学习java");
}
}
2.2对象的创建与使用
通过new关键字创建对象,语法为类名称 对象名称 = new 类名称();。
成员变量和成员方法隶属于对象,不同对象成员变量占用不同地址空间。
2.3成员变量默认值
若未显式初始化,Java 虚拟机会按数据类型赋予默认值(整型为 0,浮点型为 0.0,字符型为\u0000,布尔型为false,引用类型为null)。
2.4匿名对象
没有名字的对象,适用于方法参数只需一个对象或仅调用对象成员方法时,是定义对象的简写方式。
2.5对象内存分析
成员变量和局部变量存储位置不同,成员变量生命周期跟随对象,存储在堆内存;局部变量基本数据类型存储在栈内存。
引用类型对象存储在堆内存,栈内存存储对象地址,操作引用类型数据最终在堆内存进行,所以在方法中修改堆内存中对象属性,其他地方可感知到;基本数据类型在不同栈帧中是不同变量,方法中修改不影响其他地方。
三、构造方法
3.1构造方法定义与作用
用于给对象初始化,名称与类名相同,无返回值,不能出现return关键字。
语法格式为修饰符 类名(形参列表){}。
3.2构造方法的使用
若未定义构造方法,编译器自动提供默认无参构造方法;若手动提供构造方法,编译器不再提供默认构造方法。
可同时提供无参和有参构造方法,new关键字创建对象时会调用相应构造方法。
示例
package com.yyds.unit3.demo;
public class Student {
String name;
int age;
// 无参构造方法
public Student() {
System.out.println("无参构造执行了");
}
// 有参构造方法
public Student(String stuName, int stuAge) {
name = stuName;
age = stuAge;
System.out.println("有参构造执行了");
}
}
3.3构造方法的重载
与方法重载类似,根据调用形式匹配相应构造方法。当局部变量与成员变量同名时,需改名或显式使用this关键字调用成员变量。
四、this 关键字
4.1 this 关键字介绍
代表当前对象,在成员方法或构造方法中用于调用当前对象的成员变量、成员方法或构造方法。
4.2this 关键字的使用
可调用成员变量、成员方法、构造方法,但成员方法中不能用this调用构造方法,构造方法中调用其他构造方法必须在第一行。
示例:在坐标类Point中,使用this关键字实现计算两个坐标点之间的距离。
package com.yyds.unit3.demo;
public class Point {
double x;
double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double calclength(Point p) {
double xLen = this.x - p.x;
double yLen = this.y - p.y;
return Math.sqrt(xLen * xLen + yLen * yLen);
}
}
五、static 关键字
5.1静态变量(类变量)
用static修饰,优先于对象存在,随类加载而存在,类的所有实例共用同一地址空间。
调用方式建议使用类名.变量名,也可用对象名.变量名(不推荐)。
示例:在Student类中添加静态变量grade。
package com.yyds.unit3.demo;
public class Student {
String name;
int age;
static String grade;
}
5.2静态方法(类方法)
用static修饰,属于类,优先于对象存在,调用方式建议使用类名.方法名,也可用对象名.方法名(不推荐)。
静态方法中不能调用成员变量和成员方法,不能使用this关键字。
当类中大多核心方法为静态方法时,该类可作为工具类,工具类一般用private修饰空参构造方法防止创建对象。
示例:在Student类中定义静态方法goHome()。
package com.yyds.unit3.demo;
public class Student {
static void goHome() {
System.out.println("学生回家");
}
}
5.3静态代码块
用static修饰,与成员变量和静态变量同级,用大括号包裹。随类加载而加载,每个类中只执行一次。
构造代码块随对象创建而加载,每创建一个对象执行一次。
执行顺序:类加载时先执行静态代码块,创建对象时先初始化成员变量,再执行构造代码块,最后调用构造方法(new关键字先创建对象,再调用构造方法)。
结论:同一类中,成员变量不能赋值给静态变量,静态变量可赋值给成员变量和静态变量;静态方法不能调用成员变量和成员方法,成员方法可调用静态或非静态方法和变量;静态代码块不能调用成员变量和成员方法,构造代码块可调用静态或非静态方法和变量。
六、包
6.1包的概念
是 Java 重要的类管理方式,用于解决类重名问题和有效管理类。通过package关键字声明包,包名遵循标识符规则,企业中一般反写企业域名,不能以java开头。
6.2类的访问与导包
一般类需定义在包下,使用类时,若类与当前程序在同一包或在java.lang包中可省略包名,否则需导入包。
使用import关键字导入包,语法为import 包名.类名;。
若使用两个同名类,需用包名。类名(全类名)的方式区分。
此章个人理解与学习心得
学习这章内容后,我对面向对象程序设计有了更深入的理解。面向对象的思维方式让编程更贴近现实世界的逻辑,代码的组织结构更清晰,易于维护和扩展。然而,在学习过程中也遇到了一些挑战。
在理解对象内存分析时,由于涉及到栈内存和堆内存的复杂操作,以及引用类型和基本数据类型的不同传递方式,我通过反复阅读文档中的解释,并结合实际编写代码进行调试,逐步理清了思路。
对于this关键字的使用,起初有些困惑于它在不同场景下的准确作用。我仔细研究了示例代码,自己动手实践,不断尝试在构造方法和成员方法中运用this关键字,慢慢掌握了其使用技巧。
静态相关概念的限制,如静态方法不能调用成员变量和成员方法等,理解起来相对困难。我深入学习了类加载机制,查阅了更多资料来辅助理解,同时思考在实际项目中如何合理运用这些特性,从而加深了对静态概念的理解。
总的来说,通过不断实践和探索,我逐渐克服了这些难点,对面向对象编程的理解和运用能力有了显著提升。
第4章“面向对象程序设计(进阶)”
一、封装
1.1 概念
隐藏对象的属性和实现细节,仅对外提供公共的访问方式。
提高代码安全性和复用性。
1.2 访问修饰符
private:只能在同一类中访问。
默认(无修饰符):能在同一包中的类访问。
protected:能在同一包中的类及子类访问。
public:能在所有类中访问。
1.3 get()/set()方法
用于访问和修改私有属性,增强对属性的控制。
示例:
package com.yyds.unit4.demo;
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0) {
age = 0;
}
if (age > 200) {
age = 200;
}
this.age = age;
}
}
二、继承
2.1 概念
描述事物之间的所属关系,子类拥有父类所有属性和方法,并可扩展独有属性和方法。
使用extends关键字实现。
类之间只有单继承,但支持多层继承。
2.2 方法重写
子类重写父类方法需满足:方法名和参数列表完全一样;修饰符权限大于或等于父类方法;返回值类型符合特定规则(引用类型子类小于或等于父类,基本数据类型必须一致)。
示例:
class People {
private String name;
private int age;
public void eat() {
System.out.println(this.age + "岁的" + name + "正在吃饭");
}
// get/set方法省略
}
class Teacher extends People {
public void teach() {
System.out.println(getAge() + "岁的" + getName() + "正在教书");
}
@Override
public void eat() {
System.out.println(getAge() + "岁的" + getName() + "老师正在食堂吃饭");
}
}
class Worker extends People {
public void work() {
System.out.println(getAge() + "岁的" + getName() + "正在工作");
}
@Override
public void eat() {
System.out.println(getAge() + "岁的" + getName() + "工人正在工地吃饭");
}
}
三、super关键字
3.1 作用
指向子类对象的父类对象存储空间,用于访问父类中被子类覆盖的方法或属性,解决子类和父类中属性、方法重名问题。
可调用父类属性、方法(包括构造方法、成员方法)。
示例
class Teacher extends People {
public void teach() {
System.out.println(getAge() + "岁的" + getName() + "正在教书");
}
@Override
public void eat() {
super.eat();
System.out.println(getAge() + "岁的" + getName() + "老师正在食堂吃饭");
}
}
四、final关键字
4.1用法
修饰类:不可被继承。
修饰方法:不可被重写。
修饰变量:常量,只能赋值一次。
修饰引用类型变量:不能改变引用地址,但可改变对象内部属性值。
示例
final class MyFinalClass {
// 类中的属性和方法等
}
class AnotherClass {
private final int myFinalVariable = 10;
public final void myFinalMethod() {
// 方法体
}
}
五、Object类
5.1介绍
是 Java 中所有类的父类(包括数组),定义了一些基本方法,部分可重写。
5.2常见方法
toString():默认返回对象类型 @哈希码,一般需重写以返回对象属性值。
equals():默认比较对象地址,常重写为比较内容是否相同。
hashCode():计算对象哈希码值,重写equals()方法时一般需重写hashCode()方法,且参与计算属性与equals()方法中一致。
六、多态
6.1概念
同一方法调用因对象不同而有不同行为,前提是继承、子类重写父类方法且父类引用指向子类对象。
6.2实现
通过子类重写父类方法体现,如垃圾类的分类方法在不同子类中有不同实现。
6.3引用类型数据转换
向上转型:父类引用指向子类对象,自动类型转换,提高代码扩展性,但只能使用父类共性内容。
向下转型:子类引用指向父类对象,强制类型转换,可调用子类特有方法,但需注意类型安全,常与instanceof关键字一起使用。
6.4变量与方法调用规则
非静态方法:编译看左边,运行看右边。
静态方法、静态变量、成员变量:编译和运行都看左边。
七、抽象类
7.1概念
高度抽象的类,包含抽象方法,只知道子类应包含的方法但不知如何实现,需子类实现抽象方法。
7.2定义与使用
用abstract关键字修饰,抽象方法只有声明无方法体,子类若不是抽象类必须实现父类所有抽象方法,抽象类不能实例化,但可有构造方法用于子类创建对象时给属性赋值。
示例:
abstract class Graph {
private String name;
public Graph(String name) {
this.name = name;
}
public abstract double calcPerimeter();
public void showPerimeter() {
double perimeter = calcPerimeter();
System.out.println(getName() + "的周长为:" + perimeter);
}
// get/set方法省略
}
class Triangle extends Graph {
private double a;
private double b;
private double c;
public Triangle(double a, double b, double c) {
super("三角形");
this.a = a;
this.b = b;
this.c = c;
}
@Override
public double calcPerimeter() {
return a + b + c;
}
}
八、接口
8.1概念
比抽象类更抽象,不提供具体实现,所有方法都是抽象方法,规定了一批类的公共方法规范,用于功能扩展和规范约束。
8.2定义与使用
用interface修饰,方法默认public abstract修饰,JDK8 后可包含static方法和default方法。类通过implements实现接口,必须重写接口抽象方法,一个类可实现多个接口,接口之间可多继承。
示例:
interface USB {
void transfer();
default void charge() {
System.out.println("USB接口正在充电");
}
static void install() {
System.out.println("正在安装USB驱动");
}
}
class Mouse implements USB {
@Override
public void transfer() {
System.out.println("鼠标传输点击和滚轮操作");
}
}
九、内部类
9.1概述
定义在其他类内部的类,分为成员内部类、静态内部类、局部内部类、匿名内部类。
9.2成员内部类
与成员变量、成员方法同级,访问时需通过外部类对象,可与外部类互相访问成员变量(外部类访问内部类变量用内部类对象名。变量名,内部类访问外部类变量用外部类名.this. 变量名)。
示例:
class OuterClass1 {
private String name;
private InnerClass innerClass;
// get/set方法省略
public class InnerClass {
private String name;
public InnerClass() {}
public InnerClass(String name) {
this.name = name;
}
public void test() {
System.out.println("调用内部类的test()方法");
System.out.println("内部类的name:" + this.name);
System.out.println("外部类的name:" + OuterClass1.this.name);
}
}
public void test() {
System.out.println("调用外部类的test()方法");
System.out.println("外部类的name:" + this.name);
System.out.println("内部类的name:" + this.innerClass.name);
}
}
9.3静态内部类
与静态变量和静态方法平级,用static修饰,对象创建只用外部类名,无法访问外部类非静态属性和方法。
示例:
class OuterClass2 {
private String name;
private InnerClass innerClass;
// get/set方法省略
public static class InnerClass {
private String name;
public InnerClass() {}
public InnerClass(String name) {
this.name = name;
}
public void test() {
System.out.println("调用内部类的test()方法");
System.out.println("内部类的name:" + this.name);
System.out.println("静态内部类无法调用外部类的非静态属性和方法");
}
}
public void test() {
System.out.println("调用外部类的test()方法");
System.out.println("外部类的name:" + this.name);
System.out.println("内部类的name:" + this.innerClass.name);
}
}
9.4 局部内部类
定义在代码块中,与局部变量平级,如在方法中定义。
9.5 匿名内部类
是局部内部类的引申,用于创建接口或抽象类的子类对象并顺便重写方法,语法为new接口()|父类() { // 其他代码 },创建的不是接口对象而是其匿名子类对象,常用于方法只需要传入一个在该方法中使用的对象的场景。
此章个人理解与学习心得
在学习这章内容后,我深刻体会到面向对象程序设计进阶部分的强大与精妙。封装让代码变得更加安全和易于维护,通过合理设置访问修饰符,能清晰地界定类的边界,防止外部不恰当的访问。继承和多态极大地提升了代码的复用性和扩展性,比如在处理多种类型的对象时,多态使代码更加简洁和灵活,能够以统一的方式处理不同类型的对象,增强了程序的适应性。
然而,学习过程中也遇到了一些挑战。多态中变量和方法的调用规则较为复杂,特别是静态和非静态方法在多态场景下的不同表现,容易让人混淆。为了克服这个难点,我通过反复阅读相关内容、绘制内存结构图来理解对象的存储和方法调用机制,并大量编写示例代码进行验证,逐步掌握了其规律。
接口和抽象类的理解与运用也需要一定的实践积累。它们的区别和适用场景在实际项目中需要仔细权衡,我通过分析开源项目中的代码结构,学习优秀的设计模式,逐渐明白了如何根据具体需求选择合适的抽象方式。
内部类的各种类型和复杂的访问规则起初让人眼花缭乱,但通过实际编写代码,从简单到复杂逐步尝试,我逐渐熟悉了成员内部类、静态内部类、局部内部类和匿名内部类的特点和用法,体会到它们在描述复杂对象关系时的便利性。总体而言,这章内容虽然有一定难度,但通过不断学习和实践,我对面向对象编程有了更深入的理解,为编写高质量的代码奠定了坚实的基础。
第5章“异常”
一、异常概述
1.1 异常概念
程序运行过程中出现的不正常现象,如文件读不到、链接打不开等。Java 通过创建异常类对象并抛出相关信息来处理异常,采用面向对象方式处理,能更快定位问题。
1.2 异常与错误区别
异常:影响程序正常执行流程,但程序可处理,如文件读不到。
错误:程序脱离程序员控制,由硬件或操作系统问题导致,程序无法处理,如内存溢出。
1.3 Throwable 与异常体系
Throwable 是异常和错误的父类,定义了getMessage()、toString()、printStackTrace()方法。
Exception 分为运行时异常(RuntimeException)和编译时异常。
RuntimeException 是不检查异常,编译时不检查,如NullPointerException、ArrayIndexOutOfBoundsException。
异常处理编译时异常在编译时会被检测,需显式处理,如IOException、SQLException。
二、异常处理
2.1抛出异常
使用throw关键字抛出异常对象,语法:throw new异常名称(参数列表);。
示例:定义获取数组指定索引值方法,索引不合法时抛出异常。
public class Demo4Throw {
public static int getElement(int[] arr, int index) {
if (index < 0 || index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("索引不合法");
}
return arr[index];
}
}
2.2声明异常
使用throws关键字在方法上声明可能抛出的异常,语法:修饰符 返回值类型 方法名(参数列表) throws异常类名1,异常类名2 {...}。
运行时异常编译期不强制声明,编译时异常必须声明。
示例:改造上述获取数组元素方法,声明异常。
public class Demo5Throws {
public static int getElement(int[] arr, int index) throws ArrayIndexOutOfBoundsException {
if (index < 0 || index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("索引不合法");
}
return arr[index];
}
}
2.3 捕获异常
使用try...catch...finally结构处理异常。try包裹可能出现异常的代码,catch捕获并处理异常,finally在异常处理后执行(一般用于清理资源)。
一个try必须跟随至少一个catch或finally。
异常处理链式传递,若方法未处理异常,会向上抛给调用者,直至main方法,虚拟机处理异常方式是打印堆栈信息并停止程序。
示例:处理获取数组元素方法中的异常。
public class Demo6TryCatch {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
int element = getElement(arr, 5);
System.out.println(element);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获到异常:" + e.getMessage());
} finally {
System.out.println("finally块执行");
}
}
public static int getElement(int[] arr, int index) {
if (index < 0 || index >= arr.length) {
throw new ArrayIndexOutOfBoundsException("索引不合法");
}
return arr[index];
}
}
三、异常进阶
3.1 自定义异常
语法:创建类继承Exception或RuntimeException,提供构造方法。
示例:登录案例,定义UserNotFoundException和PasswordWrongException异常类。
// 用户不存在异常类
package com.yyds.unit5.demo;
public class UserNotFoundException extends Exception {
public UserNotFoundException() {}
public UserNotFoundException(String message) {
super(message);
}
}
// 密码错误异常类
package com.yyds.unit5.demo;
public class PasswordWrongException extends RuntimeException {
public PasswordWrongException() {}
public PasswordWrongException(String message) {
super(message);
}
}
3.2方法重写中的异常
子类重写父类方法时,编译时异常声明规则:
父类方法未声明异常,子类重写不能声明异常。
父类方法声明了异常,子类重写可声明父类异常或其子类异常,也可不声明异常。
此章个人理解与学习心得
异常处理机制对编写健壮程序至关重要,合理运用能增强程序稳定性,避免崩溃,提升用户体验,如在网络请求和文件操作中。理解运行时异常和编译时异常区别,有助于编写代码时采取合适策略,运行时异常多因逻辑错误,应尽量避免,编译时异常需强制处理,能提前发现问题。掌握异常处理流程,能让程序在异常时合理响应,多层调用中要明确异常传递机制,避免处理不当。通过实际项目经验积累,能更好把握异常处理逻辑复杂性,提前分析异常情况,分层处理使逻辑清晰。同时,理解了自定义异常在业务逻辑特定错误处理中的作用,能让代码逻辑和错误处理更精准。不过,要注意异常处理与性能平衡,避免过度使用影响性能,必要时提前判断避免异常产生。
第6章“JAVA常用类”
一、包装类
1.1概念
为使基本数据类型能像对象一样操作,为每个基本数据类型设计的对应类,位于 java.lang 包下。
示例代码(以 Integer 为例)
// 构造方法
Integer integer1 = new Integer(10);
Integer integer2 = new Integer("20");
// 静态方法
Integer integer3 = Integer.valueOf(30);
Integer integer4 = Integer.valueOf("40");
int intValue = Integer.parseInt("50");
String stringValue = Integer.toString(60);
// 成员方法
byte byteValue = integer1.byteValue();
int intValue2 = integer1.intValue();
String toStringValue = integer1.toString();
1.2自动装箱与拆箱
自动装箱:基本类型数据处于需要对象的环境中时自动转换为包装类,如Integer i = 10;。
自动拆箱:包装类在需要数值的环境中时自动转换成基本类型,如int j = new Integer(20);。
1.3大数字运算
BigInteger
用于计算远大于 long 类型的数值,算术运算通过调用其方法实现。
示例代码:
BigInteger bigInteger1 = new BigInteger("10000000000000000000");
BigInteger bigInteger2 = new BigInteger("20000000000000000000");
BigInteger sum = bigInteger1.add(bigInteger2);
BigInteger difference = bigInteger1.subtract(bigInteger2);
BigInteger product = bigInteger1.multiply(bigInteger2);
BigInteger quotient = bigInteger1.divide(bigInteger2);
BigInteger remainder = bigInteger1.divideAndRemainder(bigInteger2)[1];
int intValue3 = bigInteger1.intValue();
1.4 BigDecimal
用于表示大浮点数,解决浮点数运算精度问题。创建时需传入字符串作为值,除法运算需指定保留小数位。
示例代码:
BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = new BigDecimal("0.2");
BigDecimal sum2 = bigDecimal1.add(bigDecimal2);
BigDecimal difference2 = bigDecimal1.subtract(bigDecimal2);
BigDecimal product2 = bigDecimal1.multiply(bigDecimal2);
BigDecimal quotient2 = bigDecimal1.divide(bigDecimal2, 2, BigDecimal.ROUND_HALF_UP);
BigDecimal setScale = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_UP);
二、String 类
2.1概述
代表不可变的 Unicode 字符序列,内部用 final 修饰的字符数组存储数据。
示例代码(构造方法)
String string1 = new String();
String string2 = new String("Hello");
char[] charArray = {'W', 'o', 'r', 'l', 'd'};
String string3 = new String(charArray);
byte[] byteArray = {72, 101, 108, 108, 111};
String string4 = new String(byteArray);
2.2查找方法
示例代码:
String string = "Hello World";
int length = string.length();
char charAt = string.charAt(6);
int indexOf = string.indexOf('o');
boolean startsWith = string.startsWith("Hello");
boolean endsWith = string.endsWith("World");
boolean contains = string.contains("llo");
2.4转换方法
示例代码:
String string5 = "Hello,World";
String[] split = string5.split(",");
char[] toCharArray = string5.toCharArray();
byte[] getBytes = string5.getBytes();
String trim = " Hello ".trim();
String toUpperCase = string5.toUpperCase();
String toLowerCase = string5.toLowerCase();
String substring = string5.substring(7);
String substring2 = string5.substring(0, 5);
String replace = string5.replace("World", "Java");
三、StringBuffer 类与 StringBuilder 类
3.1 StringBuffer 类
代表可变的 Unicode 字符序列,是 AbstractStringBuilder 的子类。
示例代码(构造方法):
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = new StringBuffer("Hello");
常见方法示例:
StringBuffer stringBuffer = new StringBuffer("Hello");
stringBuffer.append(" World");
stringBuffer.insert(5, ",");
stringBuffer.delete(5, 6);
stringBuffer.replace(0, 5, "Hi");
StringBuffer reverse = stringBuffer.reverse();
int capacity = stringBuffer.capacity();
int length2 = stringBuffer.length();
3.2 StringBuilder 类
与 StringBuffer 类类似,继承自 AbstractStringBuilder 类,代表可变的 Unicode 字符序列,但不是线程安全的,效率较高。
3.3字符串拼接效率比较
在大量字符串拼接时,StringBuilder 和 StringBuffer 性能差距不大,但 StringBuilder 比 StringBuffer 快,String 性能较差。
3.4 链式编程(以 StringBuffer 为例)
示例代码:
StringBuffer stringBuffer3 = new StringBuffer("Hello");
stringBuffer3.append(" World").append("!").insert(5, ",");
四、时间和日期相关类
4.1时间戳
以 1970 年 1 月 1 日 00:00:00 为基准时间,每 1 毫秒为 1 个刻度,Java 中用 long 类型记录,获取当前时间戳的方式为System.currentTimeMillis()。
4.2Date 类
示例代码:
Date date = new Date();
long time = date.getTime();
Date date2 = new Date(1609459200000L);
4.3 SimpleDateFormat 类
用于时间格式化,创建时需指定时间格式。
示例代码:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(new Date());
Date parse = simpleDateFormat.parse("2023-01-01 00:00:00");
4.4Calendar 类
Java 中的日历类,提供日期时间计算方式。
示例代码:
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
calendar.set(Calendar.YEAR, 2024);
calendar.add(Calendar.DAY_OF_MONTH, 100);
Date time2 = calendar.getTime();
五、其他常用类
5.1 Math 类
提供大量数学计算相关方法。
示例代码:
double ceil = Math.ceil(3.14);
double floor = Math.floor(3.14);
long round = Math.round(3.14);
int max = Math.max(5, 10);
int min = Math.min(5, 10);
double pow = Math.pow(2, 3);
double sqrt = Math.sqrt(9);
double random = Math.random();
5.2Random 类
用于生成各种伪随机数,可指定范围。
示例代码:
Random random1 = new Random();
int nextInt = random1.nextInt();
int nextInt2 = random1.nextInt(100);
long nextLong = random1.nextLong();
double nextDouble = random1.nextDouble();
boolean nextBoolean = random1.nextBoolean();
5.3UUID 类
生成通用唯一识别码。
示例代码:
UUID uuid = UUID.randomUUID();
String uuidString = uuid.toString();
5.4枚举类
Java 5 后新增特性,用 enum 关键字定义,用于列举某个字段所有允许的取值。
示例代码:
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
public enum Week {
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期日
}
此章个人理解与学习心得
在学习 Java 常用类的过程中,深刻体会到这些类在实际编程中的重要性和实用性。包装类解决了基本数据类型在面向对象编程中的不足,让数据处理更加灵活;String 类的不可变性虽然在某些操作上可能会产生性能问题,但也保证了字符串操作的安全性和稳定性,其丰富的方法为字符串处理提供了极大便利。
StringBuffer 和 StringBuilder 类的可变特性在频繁字符串操作场景中优势明显,通过性能对比实验能更直观地理解它们的适用场景。时间和日期相关类的学习让我掌握了在 Java 中处理时间的各种方式,从时间戳到格式化输出,再到日期计算,为开发中涉及时间的功能提供了有力支持。
Math 类和 Random 类满足了数学计算和随机数生成的需求,而 UUID 类和枚举类则在特定场景如唯一标识生成和有限取值列举中发挥重要作用。在学习过程中,对于一些复杂的方法参数和功能实现细节理解起来可能有一定难度,通过多写代码、多调试程序以及查阅更多相关资料,逐渐克服了这些问题,对这些常用类的运用也更加熟练。