由于我是初学者,所以有错误请一定要指正
总共11章,24000字,手把手带你零基础入门Java_(:з」∠)_从Java基础到进阶内容,通俗易懂,适合初学者!完美实现前端变前台,后端变后厨,全栈变全完,运维变运输,测试变测绘!
Kawaii LOGO of Java LOGO图源 Sawaratsuki
目录
第一章:Java 环境搭建
Java 是一种计算机编程语言,除了 Java 之外,还有很多其他编程语言,如 C、C++、C#、Python 等。不同编程语言就像不同国家的语言,每种编程语言的语法和应用场景都不同。Java 主要用于后端开发。
一、Java 历史
- 1995年,Sun 公司推出了一款面向对象的编程语言——Java。
- JDK:Java 开发的必要工具。
- 1997年:JDK 1.1
- 1998年:JDK 1.2
- 2004年:JDK 1.5 版本,更名为 JDK 5.0,添加了新特性如泛型和增强的 for 循环。
- 随后版本:
- JDK 6.0
- JDK 7.0
- JDK 8.0 -> 企业级,添加 lambda 表达式和 stream 流
- 最新版本:JDK 22
- 2009年:Sun 被 Oracle(甲骨文)收购。
Java 的发展方向:
- JavaSE:Java 语言核心基础
- JavaEE:企业级开发应用
- JavaME:通信相关
二、Java 语言的特点
- 简单性:相对于 C、C++ 等。
- 开源性:开放源代码,编程人员可以查看和修改。
- 资源广泛性:有很多编程爱好者研究,大厂也积极推动。
- 跨平台性:可以在不同操作系统上运行,如 Windows、Linux、Unix、macOS 等。
- 面向对象性:强调对象的概念,后续会详细讲解。
三、Java 运行机制
-
Java 运行机制:先编译后解释运行。
- 源码文件:
.java
文件,由开发人员编写。 - 编译:将
.java
源文件通过编译器生成对应的字节码文件(.class
文件)。 - 运行:将
.class
文件通过解释器逐行翻译并运行。
- 源码文件:
-
注意:实际运行时,运行的是
.class
文件。.java
文件只需编译一次,.class
文件可以被多次运行。如果源代码更改,需要重新编译,生成新的.class
文件。
四、环境搭建
-
名词解释:
- JVM:Java 虚拟机,屏蔽不同操作系统之间的差异。
- JRE:Java 运行环境,包含解释器和 JVM。
- JDK:Java 开发工具,包含 JRE、编译器和工具类。
-
安装 JDK:
- 点击 JDK 的安装文件,按照步骤完成安装。
- 不要重复安装 JDK,如需卸载,请通过控制面板操作。
- JDK 没有提供操作界面,需要安装文本编辑器(如 Notepad++)进行代码编写。
-
配置环境变量:
- JAVA_HOME:JDK 的根目录(如
C:\Program Files\Java\jdk1.8.0_131
)。 - Path:JDK 的 bin 目录(如
C:\Program Files\Java\jdk1.8.0_131\bin
)。 - ClassPath:告诉 JVM 去哪里找
.class
文件,.
代表当前路径。
- JAVA_HOME:JDK 的根目录(如
-
测试 JDK 是否安装成功:
- 打开 CMD 命令窗口进行测试。
- 在命令窗口中输入
javac
和java
。(或者java -version)
五、Java 的第一个开发程序
- Java 的源文件以
.java
结尾。 - Class:类,是代码的容器。
- 编译和运行:
class Main { public static void main(String[] args) { System.out.println("Hello World"); System.out.println("LXH Cat is here"); } }
- Main 函数:程序的入口,一个类中最多只能有一个主函数。
- System.out.println():控制台打印输出信息。
- 编译:
javac 源文件名.java
- 运行:
java 类名
六、Package(包)
-
作用:类似于文件夹,方便进行分类管理。
-
语法:
package 包名;
-
注意事项:
package
语句必须位于源文件中第一行有效语句。- 一个源文件中最多只能定义一个
package
语句。
-
带包的编译和运行:
- 编译:
javac -d . 源文件名.java
- 运行:
java 包名.类名
- 编译:
七、编码规范
-
良好的标识符命名规范:
- 硬性要求:
- Java 中标识符只能以字母、数字、下划线(_)或美元符号($)组成,不能以数字开头。
- 没有长度限制。
- 严格区分大小写。
- 不能使用 Java 中的关键字、保留字、特殊符号命名(如
true
、false
、null
)。
- 建议要求:
- 名字应具有描述性,如
Student
、Worker
。 - 包名:全小写(如
package demo.com.zmj
)。 - 类名:每个单词首字母大写(如
TestStudent
)。 - 变量名/函数名/方法名:第一个单词首字母小写,其余单词首字母大写(如
ageAndName
)。 - 常量:全大写(如
PI
)。
- 名字应具有描述性,如
- 硬性要求:
-
良好的编码格式:
- 一行只写一句代码。
- 层级之间要有严格缩进,一个 Tab 键。
-
良好的注释习惯:
- 单行注释:
//
,只能写一行注释内容。 - 多行注释:
/* ... */
,可以写多行注释内容。 - 注意:注释内容不参与编译。
- 注意:单行注释不能嵌套多行注释,但多行注释可以嵌套单行注释。
- 单行注释:
练习
创建一个源文件命名为 TestStudent.java
,定义包结构,包名为 demo
,定义公开类,类中打印输出信息如下:
package demo;
public class TestStudent {
public static void main(String[] args) {
System.out.println("Hello there, this is just a simple print code");
System.out.println("LXH Cat is here");
}
}
编译和运行:
- 编译:
javac -d . TestStudent.java
- 运行:
java demo.TestStudent
第二章:变量、数据类型、运算符、表达式
一、变量
- 概念:变量是计算机中一块内存空间,用于存储数据的基本单元。
- 变量的组成部分:数据类型、变量名、数据。
- 语法:
- 先声明,再赋值:
数据类型 变量名; // 声明 变量名 = 值; // 赋值
- 声明的同时赋值:
数据类型 变量名 = 值;
- 同时定义多个相同类型的变量:
数据类型 变量名1, 变量名2 = 值, 变量名3;
- 先声明,再赋值:
二、Java 中的数据类型分类
-
基本数据类型(原始数据类型):
- 整数类型(4种):
byte
:1字节,范围 -128 到 127。short
:2字节,范围 -32768 到 32767。int
:4字节,范围 -2147483648 到 2147483647。long
:8字节,范围 -9223372036854775808 到 9223372036854775807,字面值后面需要加L
或l
(建议大写,区分数字1)。
- 浮点类型(2种):
float
:单精度,4字节,字面值后面必须加f
或F
。double
:双精度,8字节,字面值后面可以加d
或D
,或不加。
- 字符类型(1种):
char
:2字节,字面值形式为单引号引起来的一个字符,如'A'
。也可以用整数表示字符,范围 0 到 65535,如char c = 65;
。- 转义字符:
\n
(换行)、\t
(水平制表符)、\'
(单引号)、\"
(双引号)。
- 布尔类型(1种):
boolean
:字面值为true
或false
。
- 整数类型(4种):
-
引用数据类型:
- 类、数组、接口等。
- 例如,字符串
String
使用双引号引起来的一个或多个字符:String str = "你快去给我看罗小黑战记";
-
数据类型的实际开发总结:
- 整数类型通常使用:
int
- 小数类型:
double
- 布尔类型:常用于判断、循环结构中
- 字符类型:实际开发不常用
- 字符串:实际开发中频繁使用
- 实际存储数据根据项目业务需求
String name = "LXH Cat"; // 姓名 int age = 10; // 年龄 double score = 0; // 成绩 String sex = "男"; // 性别 char gender = '男'; // 性别 int flag = 1; // 性别标识,1-男,0-女 String tel = "123123123123"; // 电话 String addr = "北京市"; // 地址 String id = "123456789012345678"; // 身份证号
- 整数类型通常使用:
三、数据类型之间的转换
- 自动类型提升:
- 场景:小数据类型赋值给大数据类型(存储的数据范围衡量数据的大小)。
- 规则:
byte
->short
->int
->long
->float
->double
。 - 规则:
char
->int
->long
->float
->double
。
- 强制类型转换:
- 场景:大数据类型赋值给小数据类型,需要强制类型转换。
- 语法:
源数据类型 变量名 = 值; 目标类型 变量名2 = (目标类型)变量名;
四、表达式
- 理解:表达式由字面值、变量、运算符组成,通常有一个结果。
- 表达式的结果规则:
- 如果表达式中有
double
类型,结果为double
。 - 表达式中没有
double
类型,有float
,结果为float
。 - 表达式中没有
double
和float
类型,有long
,结果为long
。 - 其余结果为
int
。
- 如果表达式中有
五、运算符
- 算术运算符:
+
(加法/字符串拼接)-
(减法)*
(乘法)/
(除法)%
(取余数/取模)- 注意:
+
的两种用法:
- 两端为数值类型,则为加法运算。
- 两端有一端为字符串类型,则为字符串拼接。
- 赋值运算符:
=
(赋值)+=
(加后赋值)-=
(减后赋值)*=
(乘后赋值)/=
(除后赋值)- 比较运算符(关系运算符):
>
(大于)>=
(大于等于)<
(小于)<=
(小于等于)==
(等于)!=
(不等于)- 注意:关系运算符连接的表达式结果类型为布尔类型,称为布尔表达式(结果为
true
或false
)。- 逻辑运算符:
&&
(逻辑与):两端表达式都为true
,结果才为true
。||
(逻辑或):只要有一端为true
,结果为true
。!
(逻辑非):对结果取反。- 一元运算符:
++
(自增)--
(自减)- 应用:
a++
和++a
:将a
自增1。a--
和--a
:将a
自减1。- 面试/考试常见内容:
int c = a++;
先使用,再自增。int c = ++a;
先自增,再使用。- 三元运算符:
- 语法:
布尔表达式 ? 表达式1 : 表达式2
- 原理:判断表达式是否成立,结果为
true
,执行表达式1,否则执行表达式2。
六、Scanner(扫描仪)
- 作用:让用户输入信息,提高人机交互。
- 导包:
- 语法:
import 包名1.包名2.类名; // 导入指定类 import 包名1.包名2.*; // 导入包中所有类
- 位置:定义在
package
语句的后面,所有类的上面。 - 例如:
import java.util.Scanner;
- 语法:
- 步骤:
- 导入包:
import java.util.Scanner; import java.util.*;
- 创建对象:
Scanner sc = new Scanner(System.in);
-
使用:
int n = sc.nextInt(); // 输入一个整数 double d = sc.nextDouble(); // 输入一个小数 String s = sc.next(); // 输入一个字符串 char c = sc.next().charAt(0); // 输入一个字符
- 导入包:
第三章:分支结构
一、if 分支结构
1. 基本 if 结构
- 语法:
if (判断条件/布尔表达式) { // 语句 }
- 执行原理:如果判断条件成立,则执行
{}
中的语句。
2. 带 else 的 if 结构
- 语法:
if (判断条件/布尔表达式) { // 语句1 } else { // 语句2 }
- 执行原理:如果
if
条件成立,则执行语句1,否则执行语句2。
3. 多重 if 结构
- 语法:
if (判断条件1) { // 语句1 } else if (判断条件2) { // 语句2 } else if (判断条件3) { // 语句3 } else { // 语句n }
- 执行原理:从上往下依次判断,哪个条件成立,则执行对应
{}
中的语句。
4. if 嵌套结构
- 语法:
if (判断条件/布尔表达式) { if (判断条件/布尔表达式) { // 语句 } else if (判断条件) { // 语句 } else { // 语句 } } else { // 语句 }
- 执行原理:外层
if
条件成立时,进入内层判断。
二、等值分支结构(switch...case 结构)
1. 语法
- 语法:
switch (表达式) { case 值1: 语句1; break; case 值2: 语句2; break; case 值3: 语句3; break; ... default: 语句n; break; }
2. 执行原
首先获取 switch
中表达式的结果,根据结果从上往下匹配 case
后面的值,结果与哪一个 case
的值相等,则执行对应的语句。如果都不相等,则执行 default
后面的语句。
3. 细节
- 表达式的结果类型:可以是
byte
、short
、int
、char
、String
。 - break:终止
switch
结构,防止case
穿透。 - default:没有位置先后要求,可以放在任意位置。
三、局部变量
1. 概念
定义在函数/方法内部的变量。
2. 特点
- 必须先赋值,再使用:
- 报错:可能尚未初始化变量。
- 作用范围:从定义位置开始,到定义它的代码块结束。
- 报错信息为:找不到符号。
- 在重合的作用范围内,不允许命名冲突:
- 报错信息为:已在方法 xxx 中定义了变量 xx。
四、if 语句示例
public class IfExample {
public static void main(String[] args) {
int number = 10;
if (number > 0) {
System.out.println("这个数是正数");
} else {
System.out.println("这个数是负数,也可能是0");
}
if (number > 0) {
System.out.println("这个数是正数");
} else if (number < 0) {
System.out.println("这个数是负数");
} else {
System.out.println("这个数是0");
}
}
}
五、switch 语句示例
public class SwitchExample {
public static void main(String[] args) {
int day = 3;
String love;
switch (day) {
case 1:
love = "Monday left me broken";
break;
case 2:
love = "Tuesday I was through with hopin";
break;
case 3:
love = "Wednesday my empty arms were open";
break;
case 4:
love = "Thursday waiting for love, waiting for love";
break;
case 5:
love = "Thank the stars it's Friday";
break;
case 6:
love = "I'm burning like a fire gone wild on Saturday";
break;
case 7:
love = "Guess I won't be coming to church on Sunday";
break;
default:
love = "Invalid day. Waiting for love";
break;
}
System.out.println(love);
}
}
六、局部变量示例
public class LocalVariableExample {
public static void main(String[] args) {
int a = 5; // 定义并初始化局部变量 a
{
int b = 10; // 定义局部变量 b
System.out.println(a); // 可以访问变量 a
System.out.println(b); // 可以访问变量 b
}
// System.out.println(b); // 报错,变量 b 超出作用范围
}
}
第四章:循环结构
一、循环概述
1. 循环的概念
循环是通过某个条件,重复并且有规律地执行一段代码。
2. 循环的组成部分
- 循环变量的初始化
- 循环条件
- 循环变量的改变
- 循环体
3. 循环的分类
while
循环do..while
循环for
循环
二、while 循环
1. 语法
// 循环变量的初始化
while (循环条件) {
// 循环体
// 循环变量的改变
}
2. 执行原理
先执行循环变量的初始化,接着判断循环条件。如果循环条件满足(结果为 true
),则执行循环体,然后执行循环变量的改变,再次判断循环条件。如果满足(结果为 true
),则再次执行循环体,同时循环变量进行改变。如此重复,直到循环条件不满足(结果为 false
),则终止并跳出循环结构。
3. 循环特点
- 先判断,再执行,执行次数:0~n次
- 如果程序写得不合适,可能会出现死循环
三、do..while 循环
1. 语法
// 循环变量的初始化
do {
// 循环体
// 循环变量的改变
} while (循环条件); // 分号不能省
2. 执行原理
先执行循环变量的初始化,然后执行循环体,接着对循环变量进行改变,判断循环条件。如果满足(结果为 true
),则再次执行循环体,对循环变量进行改变,再判断循环条件。如此重复,直到循环条件结果为 false
,结束并跳出循环结构。
3. 特点
先执行,再判断,执行次数为 1~n 次。
四、for 循环
1. 语法
for (循环变量的初始化; 循环条件; 循环变量的改变) {
// 循环体
}
2. 执行原理
执行顺序:
- 循环变量的初始化
- 判断循环条件
- 如果循环条件为
true
,执行循环体 - 执行循环变量的改变
- 回到步骤2,继续循环,直到循环条件为
false
,终止循环
3. 执行特点
- 先判断,再执行,执行次数 0~n 次
for
中的循环变量初始化可以定义在外面,但变量的作用范围扩大了for
中的判断条件如果为空语句,则默认结果为true
循环总结
while
和for
:循环特点相同,先判断,再执行do...while
:先执行,再判断- 实际开发中:
for
循环比较常用- 循环次数不明确时,建议使用
while
循环 - 循环次数明确时,建议使用
for
循环
五、循环控制语句
1. break
- 终止、结束当前循环结构
- 可以应用在
switch..case
结构中,防止case
穿透
2. continue
- 中止、结束本次循环,从而进入下一次循环
示例:
public class LoopControlExample {
public static void main(String[] args) {
// 示例:break
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // 当i等于5时,终止循环
}
System.out.println("i = " + i);
}
System.out.println("----------");
// 示例:continue
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // 跳过当前循环,进入下一个循环
}
System.out.println("i is: " + i);
}
}
}
六、循环的嵌套
1. 理解
在循环结构中,定义一个完整的循环结构,即外层循环和内层循环。
2. 循环的次数
外层循环次数 * 内层循环次数
3. 循环控制语句应用的嵌套中
break
:终止、结束本层循环continue
:中止、结束本层本次循环
4. 利用循环结构打印图形
- 外层循环控制行数
- 内层循环控制列数
示例代码
while
public class DoWhileExample {
public static void main(String[] args) {
int i = 0; // 初始化循环变量
while (i < 5) { // 循环条件
System.out.println("i = " + i);
i++; // 循环变量的改变
}
}
}
do..while
public class DoWhileExample {
public static void main(String[] args) {
int i = 0; // 初始化循环变量
do {
System.out.println("i = " + i);
i++; // 循环变量的改变
} while (i < 5); // 循环条件
}
}
for
public class ForExample {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) { // 初始化、条件、改变都在同一个语句中
System.out.println("i is: " + i);
}
}
}
循环嵌套示例
public class NestedLoopExample {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) { // 外层循环控制行数
for (int j = 1; j <= i; j++) { // 内层循环控制列数
System.out.print("*");
}
System.out.println(); // 换行
}
}
}
第五章:函数
一、函数的基本概念
1. 理解
函数是一段执行特定功能的代码块,并且可以通过名字反复调用。
2. 函数的使用流程
- 函数的定义:确定函数的功能和名称
- 函数的声明:确定函数的名字
- 函数的实现:确定函数的功能
- 函数的调用:通过函数名进行调用
3. 函数的定义位置
定义在类的内部,与其他方法(如 main
方法)并列。
二、函数的基本使用
1. 函数的定义
public static void 函数名() {
// 函数的实现
}
- 函数的声明:
public static void 函数名()
- 函数名通常采用驼峰命名法
- 函数的实现:用
{}
包围的代码块
2. 函数的调用
- 通过函数名直接调用函数
- 语法:
函数名(参数);
三、参数的使用
1. 参数的作用
多数情况下,函数与调用者之间需要数据交互。调用者必须提供必要的数据,才能使函数完成相应的功能。这些数据称为参数。
2. 形式参数(形参)
- 形式参数:简称形参,是函数和调用者之间的一种约定,定义了调用者需要传递什么数据。
- 形参的定义:
public static void 函数名(数据类型 变量名1, 数据类型 变量名2, 数据类型 变量名3) {
// 函数体
}
- 形参在函数内部相当于局部变量。
- 一个函数可以定义多个形参。
3. 实际参数(实参)
- 实际参数:简称实参,是函数调用时传递的数据。
- 实参的作用:给形参赋值。
- 注意:实参的个数、顺序、数据类型必须与形参一致。
示例
定义一个函数 selectMax
,接收两个整数 m
和 n
,打印两个数据中的较大值,并在 main
函数中调用此函数。
public class MaxSelector {
public static void main(String[] args) {
selectMax(10, 20);
}
public static void selectMax(int m, int n) {
if (m > n) {
System.out.println("最大值是:" + m);
} else {
System.out.println("最大值是:" + n);
}
}
}
四、函数的返回值
1. 理解
函数和调用者之间的一种数据交互,调用者通过函数获取某些数据结果。
2. 返回值的语法
public static 返回值类型 函数名(形参列表) {
// 函数体
}
3. 函数的返回值分类
- 返回值类型为
void
,代表函数没有返回值:public static void 函数名(参数) { // 函数体 }
- 返回值类型为 8 种基本数据类型或对象,代表函数有返回值:
public static int 函数名(参数) { return 返回值; } public static double 函数名(参数) { return 返回值; } public static char 函数名(参数) { return 返回值; } public static String 函数名(参数) { return 返回值; } public static int[] 函数名(参数) { return 返回值; } public static ArrayList<String> 函数名(参数) { return 返回值; } public static 各种返回值类型 函数名(参数) { return 返回值; }
4. 函数返回值的处理
- 定义同类型的变量接收返回值。
- 直接使用返回值。
5. return
的作用
- 将返回值返回给调用者。
- 结束当前函数。
public static void example() { // 如果返回类型处是void,那么也可以使用return //但是不能在return后面加东西,直接写return就好,这就代表结束当前函数 return; }
示例
定义一个函数 add
,接收两个整数 a
和 b
,返回它们的和。
public class Addition {
public static void main(String[] args) {
int sum = add(10, 20);
System.out.println("sum=" + sum);
}
public static int add(int a, int b) {
return a + b;
}
}
五、函数的执行机制
1. 理解
程序以 main
函数作为入口,从上往下依次执行。如果遇到函数调用,则优先执行被调用的函数内部代码。被调用函数执行完毕后,带着返回值返回到调用位置,继续执行后续代码。
2. 函数的嵌套调用
被调用的函数内部,又调用了其他的函数。
示例
public class NestedFunctionExample {
public static void main(String[] args) { // 主函数
outerFunction(); // 调用外层函数
}
public static void outerFunction() {
System.out.println("这是外层函数,第一个被调用");
innerFunction(); // 调用内层函数
}
public static void innerFunction() {
System.out.println("这是内层函数,第二个被调用");
}
}
六、递归调用
1. 理解
一个函数中调用自身函数。
2. 注意
使用递归解决问题时,必须设置一个出口,否则会出现无穷递归,最终导致 java.lang.StackOverflowError
(栈溢出)。
3. 递归的思想
- 递进:每次推进,计算都比上一次变得简单,直到简单到无需继续推进,就能获得结果。
- 回归:基于出口的结果,逐层向上回归,依次计算每一层的结果,直至回归到最顶层。
4. 案例:计算阶乘
public class Factorial {
public static void main(String[] args) {
int result = factorial(8);
System.out.println("8的阶乘是:" + result);
}
public static int factorial(int n) {
if (n == 1 || n == 0) {
return 1;
}
return n * factorial(n - 1);
}
}
第六章:数组
一、理解数组
1. 数组定义
数组是一次性定义多个同类型变量的集合,可以存储多个数据,并对这些变量进行统一管理。
2. 数组的重要因素
- 数据类型:决定数组中存储数据的类型。
- 数组的长度:数组中元素的个数。
3. 数组的使用
(1) 声明数组
确定数据类型和数组名:
数据类型[] 数组名; // 推荐的方式 数据类型 数组名[]; 数据类型 []数组名;
例如:
int[] a;
(2) 分配空间
确定数组的长度:
数组名 = new 数据类型[长度];
例如:
a = new int[4];
4. 细节
(1) 数组下标
- 下标从 0 开始,依次为 0、1、2、3...数组的长度-1。
(2) 访问数组元素
通过数组名和下标访问:
数组名[下标]
(3) 数组下标越界
如果下标超出数组的合理范围,编译通过但运行时报错,错误信息为:
java.lang.ArrayIndexOutOfBoundsException: (数组的下标越界)
(4) 遍历数组
对数组元素进行逐一操作(遍历):
for(int i = 0; i < 数组名.length; i++) {
System.out.println("元素 " + i + " 的值为:" + 数组名[i]);
}
(5) 获取数组长度
数组名.length
5. 数组的默认值
- 整数类型:0
- 小数类型:0.0
- 布尔类型:false
- 字符类型:空字符
'\u0000'
- 引用类型:null
6. 数组的不同定义方式
(1) 先声明,再分配空间
数据类型[] 数组名;
数组名 = new 数据类型[长度];
(2) 声明的同时分配空间
数据类型[] 数组名 = new 数据类型[长度];
(3) 显示初始化
数据类型[] 数组名 = new 数据类型[]{值1, 值2, 值3};
(4) 简化的显示初始化
数据类型[] 数组名 = {值1, 值2, 值3};
二、数组的内存
1. 连续的内存空间
数组在内存中占用连续的空间。
2. 存储首地址
数组类型的变量存储数组内存空间的首地址。
3. 寻址方式
通过首地址和下标计算实际内存地址:
首地址 + 下标 * 数据类型字节数
三、数组的扩容
1. 扩容思想
- 创建一个更大空间的新数组,通常为原数组的两倍。
- 将原数组的内容复制到新数组中。
- 使用新数组替换旧数组。
2. 扩容的实现
(1) 手动实现扩容
int[] a = {4, 7, 3};
System.out.println("数组的长度为:" + a.length);
for(int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
// 扩容
int[] b = new int[a.length * 2];
for(int i = 0; i < a.length; i++) {
b[i] = a[i];
}
a = b;
System.out.println("扩容后的长度为:" + a.length);
for(int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
(2) 使用工具类扩容
int[] a = {4, 7, 3};
a = java.util.Arrays.copyOf(a, a.length * 2);
System.out.println("扩容后的长度为:" + a.length);
四、二维数组
1. 二维数组概述
类似于 Excel 表格,由行和列组成。
2. 访问二维数组
通过行标和列标进行访问:
数组名[行下标][列下标]
3. 二维数组的定义
(1) 定义一个 4 行 3 列的二维数组
int[][] a = new int[4][3];
(2) 示例
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
五、数组的排序
1. 排序概述
将数组中元素按一定顺序存储。
2. 排序方式
(1) 冒泡排序
int[] a = {9, 4, 5, 2, 6, 3, 1, 9};
// 冒泡排序
for(int i = 0; i < a.length - 1; i++) {
for(int j = 0; j < a.length - 1 - i; j++) {
if(a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
for(int i = 0; i < a.length; i++) {
System.out.print(a[i] + "\t");
}
(2) 使用工具类排序
int[] a = {9, 4, 5, 2, 6, 3, 1, 9};
java.util.Arrays.sort(a);
for(int i = 0; i < a.length; i++) {
System.out.print(a[i] + "\t");
}
第七章:面向对象
一、编程思想
1. 面向过程
- 定义:将问题分为若干步骤,逐步解决。
- 特点:适用于解决小型、简单的业务问题,但面对复杂业务时难以处理。
2. 面向对象
- 定义:将业务过程看作对象的集合,通过对象之间的关系解决问题。
- 特点:适用于处理复杂的业务问题。
二、对象
1. 对象概述
- 定义:对象是现实世界中事物在计算机中的抽象表示。Java 中一切皆对象。
2. 对象的组成部分
- 属性:对象的特征,对应静态数据。
- 方法:对象的行为,对应动态功能。
3. 类
- 定义:类是描述一类对象的模板,包括对象的属性和方法。
- 类和对象的关系:类是对象的模板,对象是类的实例。一个类可以创建多个对象。
三、类的组成
1. 类名
- 命名规范:每个单词首字母大写。
2. 类的基本结构
class 类名 {
// 属性
// 方法
}
3. 属性
- 定义:也称为成员变量。
- 位置:定义在类内,方法外。
- 语法:
数据类型 变量名;
数据类型 变量名 = 值;
- 默认值:
- 整数类型:0
- 小数类型:0.0
- 布尔类型:false
- 字符类型:空字符
'\u0000'
- 对象类型:null
- 作用范围:在本类中有效。
4. 方法
- 定义:也称为成员方法。
- 位置:定义在类内,其他方法外。
- 语法:
public 返回值类型 方法名(形参) {
// 方法体
}
- 方法的组成:
- 声明:包括修饰符、返回值类型、方法名、形参列表和异常。
- 实现:方法体部分
{}
。
5. 创建对象
- 语法:
类名 对象名 = new 类名();
- 使用:
对象名.属性名;
对象名.方法名(实参);
四、方法的重载 (Overload)
1. 定义
- 一个类中可以定义多个同名的方法,但参数列表不同。
2. 要求
- 方法名相同。
- 形参列表不同(数据类型、个数、顺序)。
- 返回值类型、修饰符、异常没有要求。
3. 使用
- 根据调用时传递的实际参数决定调用哪个方法。
五、构造方法
1. 定义
- 构造方法是一种特殊的方法,用于创建对象。
2. 定义位置
- 定义在类内,方法外。
3. 特点
- 方法名必须与类名一致。
- 没有返回值类型。
- 语法:
修饰符 类名(形参) {}
- 允许重载(一个类中可以定义多个构造方法,但无参数的构造方法只能有一个)。
4. 使用
- 在创建对象时,根据传递的实际参数决定调用哪个构造方法。
- 如果类中没有提供任何构造方法,JVM 会默认提供一个无参数的构造方法。
5. 作用
- 用于创建对象。
- 可以给属性赋值。
6. 开发应用技巧
- 提供两个构造方法:一个无参数的构造方法和一个有参数的构造方法。
六、this的应用
1. this.
- 代表当前对象,用于调用本类中的属性或方法。
- 用法:
this.属性名;
this.方法名(实参);
- 通常可以省略,但在成员变量和局部变量命名冲突时需要使用
this.
2. this()
- 只能在构造方法中调用本类的其他构造方法。
- 必须是构造方法中的第一行有效语句。
this本质上就是一个存储了当前对象的地址值的特殊变量
七、引用
1. 定义
- 对象类型的变量。Java 是强类型语言,对应类型的数据需要存储在对应类型的变量中。
- 语法:
类名 引用名 = new 类名(实参);
2. 引用存储
- 存储对象在堆空间中的首地址。
3. 对象的独立性
- 每个对象在堆空间是相互独立的,操作一个对象不会影响其他对象。
4. 单独声明引用
- 语法:
类名 引用名;
5. 引用赋值
- 相同类型的引用可以相互赋值,传递的是对象在堆空间中的首地址。
6. null引用
- 可以使用
null
作为引用的初始化值,表示空地址。 - 如果使用存储
null
的引用调用属性或方法,运行时报错,错误信息为:
java.lang.NullPointerException (空指针异常)
7. 引用的应用场景
- 形式参数:对象类型和引用都可以作为实际参数传递。
- 返回值类型:对象或引用都可以作为返回值。
第八章:面向对象的三大特性
一、封装
1. 现有问题
- 程序中数据安全性无法保证,容易导致业务数据错误。
2. 使用 private
修饰符
- 定义:
private
表示私有,被private
修饰的内容只能在本类中访问。
3. 为私有化属性提供公开的 get
和 set
方法
-
get
方法:获取私有属性的值。public 返回值类型 get属性名() { return 属性名; }
- 注意:返回值类型取决于属性的类型,方法名为
get
+ 属性名首字母大写。
- 注意:返回值类型取决于属性的类型,方法名为
-
set
方法:为私有属性赋值。public void set属性名(数据类型 变量名) { this.属性名 = 变量名; }
- 注意:参数的数据类型取决于属性类型,方法名为
set
+ 属性名首字母大写。
- 注意:参数的数据类型取决于属性类型,方法名为
一个完整的类的封装示例(包含有参无参构造、getter和setter方法)
// User 封装
class User {
private String account;// 账户
private String password;// 密码
private String phone;// 电话
private String identityCard;// 身份证
private double balance;// 余额
public User() {
}
public User(String account, String password, String phone, String identityCard, double balance) {
this.account = account;
this.password = password;
this.phone = phone;
this.identityCard = identityCard;
this.balance = balance;
}
// 账户封装
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
// 密码封装
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
// 电话封装
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
// 身份证封装
public String getIdentityCard() {
return identityCard;
}
public void setIdentityCard(String identityCard) {
this.identityCard = identityCard;
}
// 余额封装
public Double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
二、继承
1. 继承概述
- 定义:继承表示类之间的一种 “is-a” 关系。
- 例如:狗是一种动物,A is a B。
- 语法:
class 子类类名 extends 父类类名 {}
- 子类可以直接使用父类中定义的属性和方法。
2. 继承的优势
- 继承体现了程序的复用性和可扩展性。
3. 方法的覆盖(重写 - override
)
- 定义:子类中定义与父类中相同的方法。
- 要求:
- 子类的方法名、形参列表、返回值类型必须与父类一致。
- 子类的访问修饰符与父类相同或更宽。
- 优先级:子类覆盖的方法优先于父类的方法。
- 注意:
- 如果子类方法名和形参列表与父类相同但返回值类型不同,则编译报错。
- 如果子类方法名相同但形参列表不同,则构成方法的重载,编译和运行均通过。
4. Java中的继承特点
- 一个父类可以有多个直接子类。
class Animal {} class Dog extends Animal {} class Cat extends Animal {}
- 一个子类只能有一个直接父类,但可以有多个间接父类(多级继承),Java 中的类是单继承的。
5. 访问修饰符
-
定义:访问修饰符表示一种访问权限。
-
Java 中的访问修饰符:
private
:私有的default
:默认的(无修饰符)protected
:受保护的public
:公开的
-
访问权限:
修饰符 本类 同包 非同包子类 其他 private
√ default
√ √ protected
√ √ √ public
√ √ √ √ -
继承性:
private
修饰的属性和方法不能被继承。default
修饰的属性和方法,同包子类允许继承。protected
修饰的属性和方法,同包和非同包子类都允许继承。public
修饰的属性和方法,所有子类都可以继承。
6. super
的应用
1. super.
用法
- 定义:在子类方法中,
super.
表示访问父类的属性或成员方法。 - 语法:
super.属性名; super.成员方法名(实参);
2. super()
用法
- 定义:在创建子类对象时,JVM 默认创建一个父类对象,并使用父类的无参数构造方法。
- 语法:
super(); super(实参);
- 注意:
super()
或super(实参)
应用在子类构造方法的第一行有效语句。- 如果子类构造方法的第一行没有指定
super()
或super(实参)
,则默认添加super()
。 - super本质上就是一个存储了当前对象的父类对象的地址值的特殊变量
三、多态
1. 多态概述
- 定义:父类型的引用可以存储不同子类型的对象。
- 语法:
父类类名 引用名 = new 子类类名();
- 使用:使用父类型的引用调用属性或方法时,只能调用父类中定义的属性和方法。
- 覆盖方法:如果子类覆盖了父类中的方法,则优先使用子类的覆盖方法。
2. 引用之间的转换
1. 父类型转子类型
- 语法:
子类类名 引用名 = (子类类名)父类型的引用名;
- 结果:
- 实际存储的对象类型和要转换的类型一致,编译和运行通过。
- 实际存储的对象类型和要转换的类型不一致,编译通过,运行报错
java.lang.ClassCastException
(类型转换异常)。
2. 子类型转父类型
- 语法:
父类型的引用名 = 子类型的引用名;
- 特点:无需强制类型转换,直接赋值,体现多态。
3. 无继承关系的类型转换
- 限制:没有继承关系的类型之间不能相互赋值,强制类型转换也不允许。
3. instanceof
避免类型转换异常
- 语法:
引用名 instanceof 类名
- 执行:判断引用中存储的实际对象类型是否兼容于后面的类型,兼容返回
true
,不兼容返回false
。 - 作用:在程序设计中避免类型转换异常。
示例代码
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
public class Test {
public static void main(String[] args) {
Animal a = null;
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
if (n % 2 == 0) {
a = new Dog();
} else {
a = new Cat();
}
if (a instanceof Cat) {
System.out.println("我是猫");
} else {
System.out.println("我不是猫");
}
}
}
4. 多态的应用
1. 多态在形式参数上的应用
- 本类型和所有子类型的对象、引用都可以作为实际参数传递。
2. 多态在返回值上的应用
- 本类型和所有子类型的对象、引用都可以作为返回值返回。
第九章:三个修饰符
一、abstract(抽象的)
1. abstract 修饰类
- 定义:被
abstract
修饰的类称为抽象类。 - 语法:
abstract class 类名 {}
- 特点:
- 抽象类只能声明引用,不能创建对象。
- 抽象类中可以定义属性、成员方法和构造方法。
2. abstract 修饰方法
- 定义:被
abstract
修饰的方法称为抽象方法。 - 语法:
访问修饰符 abstract 返回值类型 方法名(形参);
- 注意:访问修饰符和
abstract
的顺序可以互换。
- 注意:访问修饰符和
- 特点:
- 抽象方法只有声明部分,没有方法的实现。
- 抽象方法只能定义在抽象类中。
3. 抽象类与子类
- 语法:
class 子类名 extends 抽象类名 {}
- 特点:
- 子类如果不想成为抽象类,则必须覆盖父类中所有的抽象方法。
示例代码
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
void makeSound() {
System.out.println("汪汪汪_(:з」∠)_");
}
}
public class Test {
public static void main(String[] args) {
Animal myDog = new Dog();
myDog.makeSound(); // 输出: Woof
}
}
二、static(静态的)
1. static 修饰属性
- 定义:被
static
修饰的属性称为静态属性、静态变量或类变量。 - 语法:
static 数据类型 变量名; static 数据类型 变量名 = 值;
- 特点:
- 静态变量基于类存在,被所有对象共享,与创建对象的数量无关。
- 使用:
- 可以通过
对象.静态属性名
访问。 - 可以直接通过
类名.静态属性名
访问。
- 可以通过
2. static 修饰方法
- 定义:被
static
修饰的方法称为静态方法。 - 语法:
访问修饰符 static 返回值类型 方法名(形参) {}
- 使用:直接通过
类名.静态方法名(实参)
调用。 - 注意:
- 静态方法中只能访问本类的静态成员,不能直接访问本类的非静态成员。
- 静态方法中不能使用
this
和super
关键字。
3. static 修饰代码块
- 定义:被
static
修饰的代码称为静态代码块。 - 位置:定义在类内、方法外。
- 语法:
class 类名 { static { // 静态代码块内容 } }
- 作用:在类加载时,静态代码块按照属性定义的先后顺序完成对静态属性的初始化工作。
- 类加载:
- JVM 第一次使用一个类时,通过 classpath 找到类对应的
.class
文件,读取该类的包名、类名、父类、属性、构造方法等信息,并将信息保存到 JVM 内存中,一个类进行一次类加载。 - JVM 类加载的时机:
- 第一次访问该类的静态成员。
- 第一次创建该类对象时,先进行类加载,再完成对象的创建。
- 子类加载时,先导致其父类加载。
- JVM 第一次使用一个类时,通过 classpath 找到类对应的
示例代码
class MyClass {
static int staticVar = 10;
static {
System.out.println("静态代码块执行");
}
static void staticMethod() {
System.out.println("静态方法调用");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(MyClass.staticVar); // 输出: 10
MyClass.staticMethod(); // 输出: 静态方法调用
}
}
三、final(最终的、不可变的)
1. final 修饰变量
- 定义:被
final
修饰的变量在作用范围内是常量。 - 语法:
final 数据类型 变量名;
- 特点:
- 只允许一次赋值,不允许修改。
- 注意:引用一旦被
final
修饰,代表引用中存储的对象不能更换。
2. final 修饰方法
- 定义:被
final
修饰的方法允许子类继承,但不允许覆盖。
3. final 修饰类
- 定义:被
final
修饰的类不允许被继承,没有子类。例如:System
、Math
。
示例代码
final class Constants {
static final int MAX_VALUE = 100;
}
class Test {
public static void main(String[] args) {
System.out.println(Constants.MAX_VALUE); // 输出: 100
}
}
思考:System.out.println()
实现原理
- System:类
- out:静态属性,对象类型
- println():方法
为什么不效仿写一个呢?
public class PrintTest {
public static void main(String[] args) {
System.out.println("String Print Test");
Console.log.print("String Print Test");
}
}
class Print{
public void print(String x){
System.out.println(x);
}
}
class Console{
static Print log;
}
第十章:接口
一、了解接口
-
接口简介:
- 接口就像一份契约,规定了类应该实现的行为和方法。
- 接口定义了一组方法的签名,但不提供具体实现。
interface Animal { void makeSound(); // 抽象方法,无方法体 }
二、实现接口
-
实现接口:
- 使用
implements
关键字来实现接口。 - 实现类必须实现接口中定义的所有方法。
class Dog implements Animal { @Override public void makeSound() { System.out.println("Dog barks"); // 实现抽象方法 } }
- 使用
三、接口间关系
-
接口继承:
- 接口支持多继承,一个接口可以继承多个父接口。
- 类实现接口的同时可以继承父类,但接口实现必须在继承之后。
interface Behaviour extends Animal { void eat(); // 新接口继承已有接口 }
四、接口的特性
-
多态应用:
- 使用接口类型的引用可以指向不同实现类的对象,体现多态性。
- 只能调用接口中定义的方法和属性。
public class Main { public static void main(String[] args) { Animal animal = new Dog(); // 多态应用 animal.makeSound(); // 调用接口方法 } }
五、JDK新特性
-
JDK8.0+:
- 提供默认方法和静态方法,使接口更灵活。
interface Vehicle { default void start() { System.out.println("引擎启动了"); } }
-
JDK9.0+:
- 引入私有方法,用于接口内部代码重用。
interface Person { private void breathe() { System.out.println("人在呼吸"); } }
六、接口分类
-
常量接口:只包含静态常量,没有方法。
interface Constants { // 常量的命名规范要全大写 int MAX_VALUE = 100; // 静态常量 }
-
标记接口:没有方法或属性,仅用于标记类的特性。
interface Serializable {} // 标记接口
-
普通接口:包含至少一个抽象方法的接口。
interface Logger { void log(String message); // 抽象方法 }
-
函数式接口:只包含一个抽象方法,用于Lambda表达式。
interface Calculator { int calculate(int x, int y); // 抽象方法 }
综合示例
public class Test {
public static void main(String[] args) {
MyClass mc = new MyClass();
IA ia = mc; // 多态
ia.m1();
IB ib = mc;
ib.m2();
IC ic = mc;
ic.m1();
ic.m2();
ic.m3();
}
}
interface IA{
// 对于变量,如果什么都不写,Java会默认使用 public static final
int a = 10;
// 对于方法,如果什么都不写,Java会默认使用 public abstract
void m1();
}
interface IB{
void m2();
}
interface IC extends IA, IB{
void m3();
}
interface ID{
void m4();
}
class ClassA {
public void m5(){
System.out.println("m5 in Class A");
}
}
class MyClass extends ClassA implements IC, ID{
@Override
public void m1() {
System.out.println("m1 in MyClass");
}
@Override
public void m2() {
System.out.println("m2 in MyClass");
}
@Override
public void m3() {
System.out.println("m3 in MyClass");
}
@Override
public void m4() {
System.out.println("m4 in MyClass");
}
}
public class Test {
public static void main(String[] args) {
IA ia = new MyInterImpl();
ia.m1();
System.out.println(ia.m2(1000));
IA ia3 = new MyInterImpl3();
ia3.m1();
System.out.println(ia3.m2(1000));
}
}
interface IA{
void m1();
boolean m2(int num);
}
class MyClass implements IA{
@Override
public void m1() {
System.out.println("重写的方法m1");
}
@Override
public boolean m2(int num) {
if(num % 2 == 0){
return true;
}else{
return false;
}
}
}
class MyInterImpl implements IA{
@Override
public void m1(){
System.out.println("实现类");
}
@Override
public boolean m2(int num) {
if(num > 0){
return true;
}else{
return false;
}
}
}
class MyInterImpl3 implements IA{
@Override
public void m1() {
System.out.println("实现类3");
}
@Override
public boolean m2(int num) {
if(num > 100 && num % 2 != 0){
return true;
}else{
return false;
}
}
}
十一章:理解内部类
一、初识内部类
-
什么是内部类:
- 在一个类的内部定义的类称为内部类。
- 内部类具有独立的命名空间,可以访问外部类的所有成员。
class Outer { class Inner {} // 内部类定义 }
-
内部类分类:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
二、匿名内部类
-
匿名内部类特点:
- 匿名内部类继承一个类或实现一个接口。
- 只能创建一个对象,且类的定义与对象的创建同时完成。
public class Test2 { public static void main(String[] args) { IA ia = new IA() { // 匿名内部类 public void m1() { System.out.println("m1..."); } public int m2(int a) { int sum = 0; for (int i = 1; i <= a; i++) { sum += i; } return sum; } }; ia.m1(); int r = ia.m2(45); System.out.println("r=" + r); } } interface IA { void m1(); int m2(int a); }
三、Lambda表达式
-
Lambda表达式:
- 用于简化函数式接口的使用,只能应用在函数式接口上。
- 语法:
(参数) -> { 方法体 }
。
interface Calculator { int calculate(int x, int y); // 函数式接口 } public class Main { public static void main(String[] args) { Calculator add = (x, y) -> x + y; // Lambda表达式 int result = add.calculate(3, 5); System.out.println("Result: " + result); } }
-
Lambda细节:
- 当方法体只有一行语句时,大括号
{}
可省略。 - 参数类型可省略。
- 当方法体只有一行
return
语句时,大括号{}
和return
可一起省略。
- 当方法体只有一行语句时,大括号
综合示例
public class Test {
public static void main(String[] args) {
IB ib1 = () ->{
System.out.println("方法的实现");
};
ib1.m1();
// 当{}中有且只有一行语句时,{}可以省略
IB ib2 = ()-> System.out.println("方法的实现2");
ib2.m1();
IC ic1 = (int a) -> System.out.println("a=" + a);
ic1.m2(1);
// 小括号中的数据类型是可以省略的
IC ic2 = (x) -> System.out.println("x=" + x);
ic2.m2(5);
ID id1 = (x, y) -> {
return x + y;
};
System.out.println(id1.m3(3, 5));
// 只有一行语句且只有return语句,则return也可以省,而且要省需要一起省,不然就一个都不能省
ID id2 = (x, y) -> x + y;
System.out.println(id2.m3(1, 2));
}
}
interface IB{
void m1();
}
interface IC{
void m2(int a);
}
interface ID{
int m3(int a, int b);
}
public class Test {
public static void main(String[] args) {
IB ib1 = (a, b) -> a + b > 100 ? true : false;
System.out.println(ib1.selectResult(199, -100));
IB ib2 = (a, b) -> (a % 2 == 0 && b % 2 == 0) ? true : false;
System.out.println(ib2.selectResult(101, 102));
}
}
interface IB{
boolean selectResult(int a, int b);
}