仅作笔记📒, 尚不完善, 持续更新中…
一、Java概述
1.1 Java语言发展史
语言: 人与人交流沟通的表达方式
计算机语言: 人与计算机之间进行信息交流沟通的一种特殊语言
Java语言是美国Sun公司(Stanford University Network)在1995年推出的计算机语言
Java之父: 詹姆斯·高斯林(James Gosling)
2009年,Sun公司被甲骨文公司收购,所以我们现在访问oracle官网即可:https://www.oracle.com
1.2 Java语言跨平台原理
Java程序并非是直接运行的,Java编译器将Java源程序编译成与平台无关的字节码文件(class文件),然后由Java虚拟机(JVM)对字节码文件解释执行。所以在不同的操作系统下,只需安装不同的Java虚拟机即可实现Java程序的跨平台。
1.3 JRE和JDK
- JVM(Java Virtual Machine) Java虚拟机
- JRE(Java Runtime Environment) Java运行环境,包含了JVM和Java的核心类库(Java API)
- JDK(Java Development Kit)称为Java开发工具,包含了JRE和开发工具
注:我们只需安装JDK即可,它包含了Java的运行环境和虚拟机。
1.4 JDK的下载和安装
通过官方网站获取JDK https://www.oracle.com
JDK安装目录介绍
目录名称 | 说明 |
---|---|
bin | 该路径下存放了JDK的各种工具命令。javac和java就放在这个目录。 |
conf | 该路径下存放了JDK的相关配置文件。 |
include | 该路径下存放了一些平台特定的头文件。 |
jmods | 该路径下存放了JDK的各种模块。 |
legal | 该路径下存放了JDK各模块的授权文档。 |
lib | 该路径下存放了JDK工具的一些补充JAR包。 |
1.5 第一个演示程序
1.5.1 编写HelloWorld
vim HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
1.5.2 编译和运行
编译Java文件:
javac 文件名.java
执行Java程序:
java 类名
javac HelloWorld.java
java HelloWorld
二、Java基础语法
2.1 注释
注释是对代码的解释和说明文字,可以提高程序的可读性,因此在程序中添加必要的注释文字十分重要。Java中的注释分为三种
2.1.1 单行注释
单行注释的格式是使用 //
,从 //
开始至本行结尾的文字将作为注释文字。
// 这是单行注释文字
2.1.2 多行注释
多行注释的格式是使用/* 和 */将一段较长的注释括起来。
/*
这是多行注释文字
这是多行注释文字
这是多行注释文字
*/
注意:多行注释不能嵌套使用。
2.1.3 文档注释
文档注释以 /**
开始,以 */
结束。
/**
* 这是文档注释
*/
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
2.2 关键字
在Java中,所有的关键字都是由保留字的特殊版本定义的,这些关键字不能用作标识符,但是它们在Java语言中有特殊的含义。以下是Java中的所有关键字:
- 访问控制:
private
protected
public
- 类、方法和变量修饰符:
abstract
class
extends
final
implements
interface
native
new
static
strictfp
synchronized
transient
volatile
- 程序控制:
break
continue
return
do
else
for
if
instanceof
switch
while
- 错误处理:
catch
finally
throw
throws
try
- 包相关:
import
package
- 基本类型:
byte
short
int
long
float
double
char
boolean
null
true
false
- 变量引用:
super
this
void
- 保留字:
goto
const(在Java中不是关键字,但是为保留字)
这些是Java中所有的关键字。注意,Java区分大小写,所以 public
和 Public
是不同的。
2.3 常量
常量: 在程序运行过程中,其值不可以发生改变的量。
Java中的常量分类
- 字符串常量: 用双引号括起来的零到多个字符,例如
""
"a"
"abc"
"中国"
等 - 整数常量: 例如:
-10
0
88
等 - 小数常量: 例如:
-5.5
1.0
88.88
等 - 字符常量: 用单引号括起来的一个字符,例如:
'a'
'5'
'B'
'中'
等 - 布尔常量: 表示真假,只有两个值
true
和false
- 空常量: 一个特殊的值,空值,值为
null
@Slf4j
public class Constants {
public static void main(String[] args) {
// 字符串常量
System.out.println("我是一个好孩子");
// 整数常量
System.out.println(10);
// 小数常量
System.out.println(-5.5);
// 字符常量
System.out.println('S');
// 布尔常量
System.out.println(true);
// 空常量
log.info(null);
}
}
2.4 数据类型
Java是一个强类型语言,Java中的数据必须明确数据类型。在Java中的数据类型包括基本数据类型和引用数据类型两种。
Java中的基本数据类型
数据类型 | 关键字 | 内存占用 | 取值范围 |
---|---|---|---|
整数类型 | byte | 1 | -128~127 |
short | 2 | -32768~32767 | |
int(默认) | 4 | -2的31次方到2的31次方-1 | |
long | 8 | -2的63次方到2的63次方-1 | |
浮点类型 | float | 4 | 负数: -3.402823E+38到-1.401298E-45 正数: 1.401298E-45到3.402823E+38 |
double(默认) | 8 | 负数: -1.797693E+308到-4.9000000E-324 正数: 4.9000000E-324到1.797693E+308 | |
字符类型 | char | 2 | 0~65535 |
布尔类型 | boolean | 1 | true、false |
注: e+38表示是乘以10的38次方,同样,e-45表示乘以10的负45次方。 在Java中整数默认是int类型,浮点数默认是double类型。
2.5 变量
变量: 在程序运行过程中,其值可以发生改变的量。
变量的定义格式
// 声明变量并赋值
数据类型 变量名 = 初始化值;
// 先声明,后赋值(使用前赋值即可)
数据类型 变量名;
变量名 = 初始化值;
还可以在同一行定义多个同一种数据类型的变量,中间使用逗号隔开。但不建议使用这种方式,降低程序的可读性。
int a = 10, b = 20;
变量的使用: 通过变量名访问即可。
System.out.println(a);
2.6 标识符
标识符是用户编程时使用的名字,用于给类、方法、变量、常量等命名。
Java中标识符的组成规则
- 由字母、数字、下划线
"_"
、美元符号 “$” 组成,且不能以数字开头。 - 不能使用Java中关键字作为标识符。
- 标识符大小写敏感 (区分大小写)。
Java中标识符的命名约定
-
小驼峰式命名: 首字母小写,从第二个单词开始每个单词的首字母大写。
变量名(例:
empList
)、方法名(例:searchOrder
)。 -
大驼峰式命名: 每个单词的首字母都大写。
类名。例:
MongoTemplate
另外,标识符的命名最好可以做到见名知意
2.7 类型转换
在Java中,一些数据类型之间是可以相互转换的。分为两种情况: 自动类型转换
和 强制类型转换
。
自动类型转换
把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量。
这种转换方式是自动的,直接书写即可。例如:
double num = 10; // 将int类型的10直接赋值给double类型
System.out.println(num);// 输出10.0
强制类型转换
把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量。
强制类型转换格式: 目标数据类型 变量名 = (目标数据类型)值或者变量; 例如:
double num1 = 5.5;
int num2 = (int) num1; // 将double类型的num1强制转换为int类型
System.out.println(num2); // 输出5(小数位直接舍弃, 会造成精度丢失)
注: boolean类型不能与其他基本数据类型相互转换。
2.8 运算符
运算符: 对常量或者变量进行操作的符号
表达式: 用运算符把常量或者变量连接起来符合Java语法的式子就可以称为表达式。
不同运算符连接的表达式体现的是不同类型的表达式。
例如: +
是运算符,并且是算术运算符。a + b
是表达式,由于 +
是算术运算符,所以这个表达式叫算术表达式。
2.8.1 算术运算符
加法(
+
)、减法(-
)、乘法(*
)、除法(/
)、取余/取模(%
)注: 整数操作只能得到整数,要想得到小数,必须有浮点数参与运算。有字符串出现,
+
实为拼接。
2.8.2 赋值运算符
赋值运算符的作用是将一个表达式的值赋给左边,左边必须是可修改的,不能是常量。
赋值(
=
)、加后赋值(+=
)、减后赋值(-=
)、乘后赋值(*=
)、除后赋值(/=
)、取余后赋值(%=
)注: 扩展运算符具有隐式类型转换功效 (
+=
、-=
、*=
、/=
、%=
)
short s = 10;
s = s + 10; // 此行代码报出,因为运算中s提升为int类型,运算结果int赋值给short可能损失精度
s += 10; // 此行代码没有问题,隐含了强制类型转换,相当于 s = (short) (s + 10);
2.8.3 自增自减运算符
自增(
++
)、自减(--
)注: 放变量前后均可。单独使用,无论前后结果一样。参与操作时放前先自操作,放后后自操作。
int x = 10;
int y = x++ + x++ + x++;
System.out.println(y); // y的值是多少? 10 + 11 + 12 = 33
2.8.4 关系运算符
小于(
<
)、小于等于(<=
)、大于(>
)、大于等于(>=
)、等于(=
)、不等于(!=
)注: 关系运算符的结果都是boolean类型,要么是true,要么是false。
int a = 10;
int b = 20;
System.out.println(a == b); // false
System.out.println(a != b); // true
System.out.println(a > b); // false
System.out.println(a >= b); // false
System.out.println(a < b); // true
System.out.println(a <= b); // true
// 关系运算的结果肯定是boolean类型,所以也可以将运算结果赋值给boolean类型的变量 boolean flag = a > b;
System.out.println(flag); // 输出false
2.8.5 逻辑运算符
逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。
逻辑与(
&
)、逻辑或(|
)、逻辑异或(^
)、逻辑非(!
)、短路与(&&
)、短路或(||
)
2.8.6 三元运算符
三元运算符语法格式: 关系表达式 ? 表达式1 : 表达式2;
释义: 问号前面的位置是判断的条件,判断结果为boolean型,为true时调用表达式1,为false时调用表达式2。
其逻辑为: 如果条件表达式成立或者满足则执行表达式1,否则执行第二个。
2.9 数据输入
2.9.1 导包
import java.util.Scanner;
2.9.2 创建Scanner对象
Scanner sc = new Scanner(System.in); // 创建Scanner对象, sc表示变量名, 其他均不可变
2.9.3 接收数据
int i = sc.nextInt(); // 表示将键盘录入的值作为int数返回
2.10 流程控制语句
在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的。所以,我们必须清楚每条语句的执行流程。而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能。
2.10.1 流程控制语句分类
- 顺序结构
- 分支结构(
if
,switch
) - 循环结构(
for
,while
,do...while
)
2.10.1 顺序结构
顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。
顺序结构执行流程图
![](https://i-blog.csdnimg.cn/blog_migrate/452e0f73aab4eaa079c94141a25de2ab.png)
2.10.2 分支结构
2.10.2.1 if语句
语句格式一(if
)
// 格式:
if (关系表达式) {
语句体;
}
执行流程
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体
③如果关系表达式的值为false就不执行语句体
④继续执行后面的语句内容
![](https://i-blog.csdnimg.cn/blog_migrate/6f13c9c7abc1394dfd1546c5d0b837f7.png)
练习: 大于某值否
public class CompareTest {
public static void main(String[] args) {
System.out.println("开始");
// 定义两个变量
int a = 10;
int b = 20;
b = 5;
// 需求:判断a是否大于b,如果是,在控制台输出:a的值大于b,否则,在控制台输出:a的值不大于b
if (a > b) {
System.out.println("a的值大于b");
} else {
System.out.println("a的值不大于b");
}
System.out.println("结束");
}
}
语句格式二(if-else
)
// 格式:
if (关系表达式) {
语句体1;
} else {
语句体2;
}
执行流程:
①首先计算关系表达式的值
②如果关系表达式的值为true就执行语句体1
③如果关系表达式的值为false就执行语句体2
④继续执行后面的语句内容
![](https://i-blog.csdnimg.cn/blog_migrate/96c783e359805b56439c36b833efee0e.png)
练习: 奇偶数判断
import java.util.Scanner;
public class CardinalEvenTest {
public static void main(String[] args) {
// 为了体现任意给出一个整数,采用键盘录入一个数据。(导包,创建对象,接收数据)
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int number = sc.nextInt();
// 判断整数是偶数还是奇数要分两种情况进行判断,使用if..else结构
// 判断是否偶数需要使用取余运算符实现该功能 number % 2 == 0
// 根据判定情况,在控制台输出对应的内容
if (number % 2 == 0) {
System.out.println(number + "是偶数");
} else {
System.out.println(number + "是奇数");
}
}
}
语句格式三(if else if else
)
格式:
if (关系表达式1) {
语句体1;
} else if (关系表达式2) {
语句体2;
}
...
else {
语句体n+1;
}
执行流程:
①首先计算关系表达式1的值
②如果值为true就执行语句体1; 如果值为false就计算关系表达式2的值
③如果值为true就执行语句体2; 如果值为false就计算关系表达式3的值
④…
⑤如果没有任何关系表达式为true,就执行语句体n+1。
![](https://i-blog.csdnimg.cn/blog_migrate/492260ea87baba60b5001e9913d9690a.png)
练习:
-
键盘录入一个星期数(1,2,…7),输出对应的星期一,星期二,…星期日
import java.util.Scanner; public class WeekTest { public static void main(String[] args) { System.out.println("开始"); // 需求:键盘录入一个星期数(1,2,...7),输出对应的星期一,星期二,...星期日 Scanner sc = new Scanner(System.in); System.out.println("请输入一个星期数(1-7):"); int week = sc.nextInt(); if (week == 1) { System.out.println("星期一"); } else if (week == 2) { System.out.println("星期二"); } else if (week == 3) { System.out.println("星期三"); } else if (week == 4) { System.out.println("星期四"); } else if (week == 5) { System.out.println("星期五"); } else if (week == 6) { System.out.println("星期六"); } else { System.out.println("星期日"); } System.out.println("结束"); } }
-
小明快要期末考试了,小明爸爸对他说,会根据他不同的考试成绩,送他不同的礼物,假如你可以控制小明的得分,请用程序实现小明到底该获得什么样的礼物,并在控制台输出。
import java.util.Scanner; public class GiftTest { public static void main(String[] args) { // 小明的考试成绩未知,可以使用键盘录入的方式获取值 Scanner sc = new Scanner(System.in); System.out.println("请输入一个分数:"); int score = sc.nextInt(); // 由于奖励种类较多,属于多种判断,采用if...else...if格式实现 // 为每种判断设置对应的条件 // 为每种判断设置对应的奖励 // 数据测试:正确数据,边界数据,错误数据 if (score > 100 || score < 0) { System.out.println("你输入的分数有误"); } else if (score >= 95) { System.out.println("山地自行车一辆"); } else if (score >= 90) { System.out.println("游乐场玩一次"); } else if (score >= 80) { System.out.println("变形金刚玩具一个"); } else { System.out.println("胖揍一顿"); } } }
2.10.2.2 switch语句
格式
switch (表达式) {
case 1:
语句体1;
break;
case 2:
语句体2;
break;
...
default:
语句体n+1;
break;
}
- 执行流程:
- 首先计算出表达式的值
- 其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结束。
- 最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。
练习: 一年有12个月,分属于春夏秋冬4个季节,键盘录入一个月份,请用程序实现判断该月份属于哪个季节,并输出。(case穿透
)
import java.util.Scanner;
public class QuarterTest {
public static void main(String[] args) {
// 键盘录入月份数据,使用变量接收
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个月份:");
int month = sc.nextInt();
// case穿透
switch (month) {
case 1:
case 2:
case 12:
System.out.println("冬季");
break;
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
default:
System.out.println("你输入的月份有误");
}
}
}
注: 如果switch中的case,没有对应break的话,则会出现case穿透的现象。
2.10.3 循环结构
循环: 循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段被重复执行的代码被称为循环体语句, 当反复执行这个循环体时,需要在合适的时候把循环判断条件修改为false,从而结束循环,否则循环将一直执行下去,形成死循环。
2.10.3.1 for循环
格式
for (初始化语句;条件判断语句;条件控制语句) {
循环体语句;
}
- 格式解释
- 初始化语句: 用于表示循环开启时的起始状态,简单说就是循环开始的时候什么样
- 条件判断语句: 用于表示循环反复执行的条件,简单说就是判断循环是否能一直执行下去
- 循环体语句: 用于表示循环反复执行的内容,简单说就是循环反复执行的事情
- 条件控制语句: 用于表示循环执行中每次变化的内容,简单说就是控制循环是否能执行下去
- 执行流程
- ①执行初始化语句
- ②执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- ③执行循环体语句
- ④执行条件控制语句
- ⑤回到②继续
练习:
- 在控制台输出15和51的数据
- 求1~5之间的数据和,并把求和结果在控制台输出
- 求1~100之间的偶数和,并把求和结果在控制台输出
- 在控制台输出所有的
"水仙花数"(三位数,个位、十位、百位的数字立方和等于原数)
- 统计
"水仙花数"
一共有多少个,并在控制台输出个数
2.10.3.2 while循环
格式
初始化语句;
while (条件判断语句) {
循环体语句;
条件控制语句;
}
- 执行流程
- ①执行初始化语句
- ②执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- ③执行循环体语句
- ④执行条件控制语句
- ⑤回到②继续
练习: 世界最高山峰是珠穆朗玛峰(8844.43米=8844430毫米),假如我有一张足够大的纸,它的厚度是0.1毫米。请问,我折叠多少次,可以折成珠穆朗玛峰的高度?
2.10.3.3 do…while循环
格式
初始化语句;
do {
循环体语句;
条件控制语句;
}while(条件判断语句);
- 执行流程:
- ①执行初始化语句
- ②执行循环体语句
- ③执行条件控制语句
- ④执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- ⑤回到②继续
2.10.3.4 三种循环的区别
-
三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
- do…while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)
-
for循环和while的区别
- 条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了
- 条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构中,在while循环结束后,该变量还可以继续使用
-
死循环(无限循环)的三种格式
for(;;){}
while(true){}
do {} while(true);
2.10.3.5 跳出循环
- 跳转控制语句(break)
- 跳出循环,结束循环
- 跳转控制语句(continue)
- 跳过本次循环,继续下次循环
注意: continue只能在循环中进行使用!
2.11 Random随机数
Random类似Scanner,也是Java提供好的API,内部提供了产生随机数的功能
使用步骤
- 导包
import java.util.Random;
- 创建对象
Random r = new Random();
- 产生随机数
int num = r.nextInt(10);
注: 10代表的是一个范围,如果括号写10,产生的随机数就是09,括号写20,参数的随机数则是019
练习: 程序自动生成一个1-100之间的数字,使用程序实现猜出这个数字是多少?
当猜错的时候根据不同情况给出相应的提示
- 如果猜的数字比真实数字大,提示你猜的数据大了
- 如果猜的数字比真实数字小,提示你猜的数据小了
- 如果猜的数字与真实数字相等,提示恭喜你猜中了
import java.util.Random;
import java.util.Scanner;
public class RandomTest {
public static void main(String[] args) {
// 要完成猜数字的游戏,首先需要有一个要猜的数字,使用随机数生成该数字,范围1到100
Random r = new Random();
int number = r.nextInt(100) + 1;
while (true) {
// 使用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的数字:");
int guessNumber = sc.nextInt();
// 比较输入的数字和系统产生的数据,需要使用分支语句。
// 这里使用if..else..if..格式,根据不同情况进行猜测结果显示
if (guessNumber > number) {
System.out.println("你猜的数字" + guessNumber + "大了");
} else if (guessNumber < number) {
System.out.println("你猜的数字" + guessNumber + "小了");
} else {
System.out.println("恭喜你猜中了");
break;
}
}
}
}
2.12 数组
数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致。
2.12.1 定义
格式一
数据类型[] 数组名
示例
int[] arr;
double[] arr;
char[] arr;
格式二
数据类型 数组名[]
示例
int arr[];
double arr[];
char arr[];
2.12.2 初始化
动态初始化
数组动态初始化就是只给定数组的长度,由系统给出默认初始化值
格式
数据类型[] 数组名 = new 数据类型[数组长度];
示例
int[] arr = new int[3];
静态初始化
格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,...};
简化
数据类型[] 数组名 = {元素1,元素2,...};
2.12.3 数组元素访问
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。
这个自动编号称为数组索引(index),可以通过数组的索引访问到数组中的元素。
访问格式 数组名[索引];
注: ArrayIndexOutOfBoundsException
、NullPointerException
2.12.4 数组遍历
练习: 数组最值(找出最大值)
2.13 方法
2.13.1 方法定义
方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
- 注意:
- 方法必须先创建才可以使用,该过程成为方法定义
- 方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程成为方法调用
无参无返方法的定义和调用
格式 public static void 方法名 () { 方法体;}
调用 方法名();
有参无返方法的定义和调用
格式 public static void 方法名 (参数1, ...) { 方法体;}
调用 方法名(参数值1, ...);
无参有返方法的定义和调用
格式 public static 数据类型 方法名() { return 数据 ;}
调用 数据类型 变量名 = 方法名();
有参有返方法的定义和调用
格式 public static 数据类型 方法名(参数1, ...) { return 数据 ;}
调用 数据类型 变量名 = 方法名(参数值1, ...);
注: 方法不能嵌套; 了解方法的形参和实参
2.13.2 方法重载
重载发生在同一个类中,方法名相同参数列表不同,这里的参数列表可以使参数的顺序、类型、个数不同,与方法的修饰符和返回值类型无关,这种现象称为方法的重载;
2.14 内部类
在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
格式
/**
* 格式:
* class 外部类名{
* 修饰符 class 内部类名{ }
* }
*/
class Outer {
// 内部类
public class Inner {
}
// 静态内部类
public static class StaticInner {
}
}
注: 内部类可以直接访问外部类的成员,包括私有;外部类要访问内部类的成员,必须创建对象
2.14.1 成员内部类
-
成员内部类的定义位置
- 在类中方法,跟成员变量是一个位置
-
外界创建成员内部类格式
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 例:
Outer.Inner oi = new Outer().new Inner();
-
使用方案
- 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
2.14.2 局部内部类
-
成员内部类的定义位置
- 局部内部类是在方法中定义的类
-
外界创建成员内部类格式
- 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
2.14.3 匿名内部类
- 匿名内部类使用前提
- 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
- 格式
new 类名() { 重写方法 } new 接口名() { 重写方法 }
- 本质
- 是一个继承了该类或者实现了该接口的子类匿名对象
注: 可直接调用方法 (new 类名() { 重写方法 }.方法名();
),可通过多态的形式接收。
2.15 包装类
-
基本类型包装类的作用
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
常用的操作之一: 用于基本数据类型与字符串之间的转换
-
基本类型对应的包装类
基本数据类型 对应的包装类 byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean
注: 除int、char外其余包装类均为基本数据类型首字母大写。
自动拆装箱
自动装箱: 把基本数据类型转换为对应的包装类类型
自动拆箱: 把包装类类型转换为对应的基本数据类型
示例
Integer i = 100; // 自动装箱
i+=200; // i=i+200; i+200自动拆箱;i=i+200;是自动装箱
2.16 异常
异常的概述: 异常就是程序出现了不正常的情况
异常体系结构
Error: 一般为底层的不可恢复的类,一般此类错误都比较严重,JVM将终止其运行的线程;
Exception: 程序本身可以捕获并且可以预处理的异常,例如捕获或者抛出;
- RuntimeException:运行时异常;例: NullPointerException、ArrayIndexOutOfBoundsException
- CheckException:已检查异常,编译阶段必须处理;例: IOException、InterruptedException
2.16.1 异常捕获
-
格式
try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; } finally { 无论是否出现异常都会执行 }
-
执行流程
- 程序从 try 里面的代码开始执行
- 出现异常,就会跳转到对应的 catch 里面去执行
- 执行完毕之后,程序还可以继续往下执行
2.16.2 Throws方式处理异常(抛出)
-
格式
public void 方法() throws 异常类名 { }
-
注意事项
- 这个throws格式是跟在方法的括号后面的
- 编译时异常必须要进行处理,两种处理方案:try…catch …或者 throws,如果采用 throws 这种方案, 将来谁调用谁处理
- 运行时异常可以不处理,出现问题后,需要我们回来修改代码
2.16.3 Throws和Throw的区别
Throws | throw |
---|---|
用在方法声明后面,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示抛出异常,由方法的调用者处理 | 表示抛出异常,由方法体内的语句处理 |
表示出现异常的一种可能性,并不一定会发生这些异常 | 执行throw一定抛出某种异常 |
2.16.4 自定义异常
@NoArgsConstructor
@Setter
@Getter
public class ExtException extends RuntimeException {
protected String code; // 异常对应的返回码
protected String msg; // 异常对应的描述信息
protected Object data; // 异常对应的异常数据
protected ExtException(String msg) {
super(msg);
msg = msg;
}
protected ExtException(String code, String massage) {
this.code = code;
this.msg = massage;
}
protected ExtException(ExtExceptionInfo exceptionInfo) {
this.code = exceptionInfo.getCode();
this.msg = exceptionInfo.getMsg();
}
}
2.17 常用API
自行搜索,略
- String
- Math
- Random
- System
- Object
- Arrays
- Integer、Long、Double、Character等包装类
- Date
- SimpleDateFormat
- Calendar
- Comparable
- Comparator
注: 冒泡
2.18 泛型
- 泛型概述
是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
-
泛型定义格式
- <类型>: 指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
-
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
2.18.1 泛型类
格式
修饰符 class 类名<类型> {}
2.18.2 泛型方法
格式
修饰符 <类型> 返回值类型 方法名(类型 变量名) {}
2.18.3 泛型接口
格式
修饰符 interface 接口名<类型> {}
2.18.4 类型通配符
作用: 为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符的分类
- 类型通配符: <?>
- List<?>: 表示元素类型未知的List,它的元素可以匹配任何的类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
- 类型通配符上限: <? extends 类型>
- List<? extends Number>: 它表示的类型是Number或者其子类型
- 类型通配符下限: <? super 类型>
- List<? super Number>: 它表示的类型是Number或者其父类型
- 类型通配符: <?>
2.19 可变参数
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
格式
修饰符 返回值类型 方法名(数据类型... 变量名) {}
- 可变参数的注意事项
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
- 应用
- Arrays工具类中有一个静态方法:
- public static List asList(T… a): 返回由指定数组支持的固定大小的列表
- 返回的集合不能做增删操作,可以做修改操作
- List接口中有一个静态方法:
- public static List of(E… elements): 返回包含任意数量元素的不可变列表
- 返回的集合不能做增删改操作
- Set接口中有一个静态方法:
- public static Set of(E… elements) : 返回一个包含任意数量元素的不可变集合
- 在给元素的时候,不能给重复的元素
- 返回的集合不能做增删操作,没有修改的方法
- Arrays工具类中有一个静态方法:
三、面向对象
3.1 类和对象
客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。
类是对现实生活中一类具有共同属性和行为的事物的抽象;类是对象的数据类型,类是具有相同属性和行为的一组对象的集合;简单来说类就是对现实事物的一种描述。
问: 什么是面向对象?
答: 面向对象是相较于面向过程来讲的。面向过程讲的是当完成一件事需要多步,每一步都需要我们亲力亲为来完成,对应到代码中就是一块代码的功能逻辑需要多步来完成,每一步都需要我们开发者来实现,这个就叫做面向过程。而面向对象讲的是我需要完成某件事,这件事也是需要多个繁杂的步骤来实现,恰巧某个类中有方法已经实现了这个功能来完成这件事,我们可以通过此类来创建出一个该类的对象,调用其方法即可,并不需要独自实现里面繁杂的细节步骤,这个就叫做面向过程编程,操作的是对象。面向对象有三大特性,封装、继承和多态,封装指的是私有化属性提供公有的访问方式,隐藏内部实现细节,减少代码冗余,提高代码复用性;继承指的是父类无法满足子类的功能需求,子类需要对父类进行扩展增强,可以使用extends关键字,继承后可拥有父类的属性和方法,并可以添加自己的属性以及对父类方法进行重写以扩展功能;多态指的是不同对象对同一方法的不同表现形式,例如Animal动物,狗Dog和猫Cat都extends Animal,Animal有叫的方法bark,通过Animal a1 = new Dog(); Animal a2 = new Cat(); new出来的两个对象,a1调用bark得到的是汪汪汪,a2调用bark得到的是喵喵喵,不同对象(狗和猫)对统一方法(bark)的不同表现形式(汪汪汪和喵喵喵),这就是多态,通过父类引用指向子类对象来实现。以上就是我对面向对象的理解。
3.1.1 类的定义
public class 类名 {
// 成员变量
变量1的数据类型 变量1;
变量2的数据类型 变量2; ...
// 成员方法
方法1;
方法2;
}
3.1.2 对象的使用
创建对象的格式: 类名 对象名 = new 类名();
调用成员的格式:
- 对象名.成员变量;
- 对象名.成员方法();
3.1.3 成员变量和局部变量
- 类中位置不同: 成员变量(类中方法外)局部变量(方法内部或方法声明上)
- 内存中位置不同: 成员变量(堆内存)局部变量(栈内存)
- 生命周期不同: 成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而 存在,随着方法的调用完毕而消失)
- 初始化值不同: 成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
3.1.4 构造方法
构造方法是一种特殊的方法
格式
public class 类名{
修饰符 类名(参数) {
}
}
功能: 主要是完成对象数据的初始化;作用: 创建对象 Student stu = new Student();
注: 如果没有定义构造方法,系统将给出一个默认的无参数构造方法;如果定义了构造方法,系统将不再提供默认的构造方法
3.1.5 this和super
问: this和super的区别
答: 1、属性的区别,this访问本类中的属性,没有则访问父类中的属性,super访问父类中的属性;2、方法的区别: this访问本类方法,没有则访问父类中的方法,super访问父类中的方法;3、构造的区别: this调用本类构造,必须放在构造方法的首行;super调用父类构造,必须放在子类构造方法首行。4、其他区别,this表示当前对象。super不能表示当前对象。this和super不能用于static修饰的变量,方法,代码块;因为this和super都是指的是对象(实例)。
![](https://i-blog.csdnimg.cn/blog_migrate/ab5595e34113149975b823f18cbd1680.png)
注: ①在对拥有父类的子类进行初始化时,父类的构造方法也会执行,且优先于子类的构造函数执行;因为每一个子类的构造函数中的首行都有一条默认的隐式语句 super();
②不能既调 this();
又调 super();
是因为会导致父类构造执行两遍,编译不通过。
3.1.6 修饰符
package
、import
、权限修饰符(private、default、protected、public)
、final
、static(类共享、可通过类名调用)
问: 谈谈你对final关键字的认识。
答: final是java中的一个关键字,被final修饰的类不能被继承,被final修饰的方法不能被重写,被final修饰的变量是常量。除以上三点外,如果某个内部类被final修饰,那么它里面的所有方法会被加上隐式的final修饰;被final修饰的方法还能提升执行效率,原因是因为代码会嵌套执行,代码过长则提升不了多少,现在也不靠这些提升代码效率。
3.2 封装
私有化属性,隐藏内部实现细节,提供公共的访问方式。
提高了安全性、提高了代码复用性。
修饰符: default
、private
、protected
、public
, 修饰范围略
this关键字: this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
3.3 继承
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
Java中类只支持单继承,不支持多继承
格式(通过 extends
关键字实现)
class 子类 extends 父类 {}
注: ①继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。②提高了代码的复用性、可维护性。③继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性。④使用场景: 需要考虑类与类之间是否存在 is..a
的关系,不能盲目使用继承。谁是谁的一种,例如: 老师和学生是人的一种,那人就是父类,学生和老师就是子类
3.3.1 方法的重写
重写描述的是在父子类中,方法名和参数列表完全相同,并且重写方法的权限修饰符要大于等于被重写方法的权限修饰符、返回值类型要兼容被重写方法的返回值类型、抛出的异常范围不能比被重写方法抛出的异常范围大。父类的私有方法或被final修饰的方法不能被重写。
3.4 多态
多态指的是不同对象对同一方法的不同表现形式,例如Animal动物,狗Dog和猫Cat都extends Animal,Animal有叫的方法bark,通过Animal a1 = new Dog(); Animal a2 = new Cat(); new出来的两个对象,a1调用bark得到的是汪汪汪,a2调用bark得到的是喵喵喵,不同对象(狗和猫)对统一方法(bark)的不同表现形式(汪汪汪和喵喵喵),这就是多态,通过父类引用指向子类对象来实现。
四、Collection集合
集合类的特点: 提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变
集合类的体系图
4.1 Collection: 单列集合
-
Collection是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
-
JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
-
常用方法
方法名 说明 boolean add(E e) 添加元素 boolean remove(Object o) 从集合中移除指定的元素 void clear() 清空集合中的元素 boolean contains(Object o) 判断集合中是否存在指定的元素 boolean isEmpty() 判断集合是否为空 int size() 集合的长度,也就是集合中元素的个数 boolean addAll(Collection<? extends E> c); 添加目标集合中的所有元素到本集合
注: List和Set异同
4.2 Map: 双列集合
特点:
- 键值对映射关系
- 一个键对应一个值
- 键不能重复,值可以重复
- 元素存取无序
常用方法
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
五、文件操作
-
File类介绍
- 它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的
-
常用方法
方法名 说明 构造方法 File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例 创建方法 public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 public boolean mkdir() 创建由此抽象路径名命名的目录 public boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 判断方法 public boolean isDirectory() 测试此抽象路径名表示的File是否为目录 public boolean isFile() 测试此抽象路径名表示的File是否为文件 public boolean exists() 测试此抽象路径名表示的File是否存在 获取方法 public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串 public String getPath() 将此抽象路径名转换为路径名字符串 public String getName() 返回由此抽象路径名表示的文件或目录的名称 public String[] list() 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组 删除方法 public boolean delete() 删除由此抽象路径名表示的文件或目录
5.1 IO流
字节流
字符流
六、多线程
问: 什么是线程,线程和进程的区别是什么?
答: 根本区别为进程操作系统进行资源分配的最小单元,而线程是操作系统进行运算调度的最小单元。从属关系不同,一个进程可以包含多个线程,一个线程属于某个进程。开销不同,进程创建和销毁的开销要远远大于线程的创建和销毁。拥有的资源不同,进程拥有操作系统为它分配的全部内存和资源,而线程要和它所属进程的其他线程共享进程里的内存和资源。控制和影响能力不同,子进程无法影响父进程,子线程可以影响父线程,如果主线程发生异常还会影响它所属进程的其他线程。CPU利用率不同,进程因为上下文切换开销大所以利用率不高,线程上下文切换快CPU利用率高。操纵者不同,进程往往是操作系统来调用,而线程的操纵者一般是开发人员。
问: 并发和并行的区别?
答: 并发是一段时间内做多件事,并行是同一时刻做多件事。例如单核CPU的电脑上开多个应用程序,看似同一时刻执行,实际上是同一时间段来回切换执行,这种称为并发。多核CPU电脑上开多个应用程序,A程序在 1号核运行,B程序在 2号核运行,这种的称为并行。
Thrad类方法介绍
方法名 | 说明 |
---|---|
run和start | |
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,Java虚拟机会调用run方法() |
设置和获取线程名称 | |
void setName(String name) | 将此线程的名称更改为等于参数name |
String getName() | 返回此线程的名称 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
优先级相关方法 | |
final int getPriority() | 将此线程的名称更改为等于参数name |
final void setPriority(int newPriority) | 更改此线程的优先级 线程默认优先级是5;线程优先级的范围是:1~10 |
线程控制相关方法 | |
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
6.0 线程的生命周期
线程一共有五种状态,线程在各种状态之间转换。
6.1 实现多线程方式一: 继承Thread类
-
实现步骤
- 定义一个类MyThread继承Thread类
- 在MyThread类中重写run()方法
- 创建MyThread类的对象
- 启动线程
-
问: 为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
-
run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用。start():启动线程;然后由JVM调用此线程的run()方法
6.2 实现多线程方式二: 实现Runnable接口
- 实现步骤
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
- 相比继承Thread类,实现Runnable接口的好处
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
6.3 实现多线程方式三: 实现Callable接口
6.4 实现多线程方式四: 线程池
七、网络编程
7.1 概述
- 计算机网络
- 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
- 网络编程
- 在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
7.2 网络编程三要素
- IP地址
- 要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数 据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识
- 端口
- 网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序 了。也就是应用程序的标识
- 协议
- 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定 的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
八、反射 & 自定义注解&模块化
8.1 反射
是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。 由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。
-
获取Class类对象的三种方式
- 类名.class属性
- 对象名.getClass()方法
- Class.forName(全类名)方法
-
反射获取构造方法
方法名 说明 Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组 Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组 Constructor getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法对象 Constructor getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造方法对象 -
Constructor类用于创建对象的方法
方法名 说明 T newInstance(Object...initargs) 根据指定的构造方法创建对象 -
Class类获取成员变量对象的方法
方法名 说明 Field[] getFields() 返回所有公共成员变量对象的数组 Field[] getDeclaredFields() 返回所有成员变量对象的数组 Field getField(String name) 返回单个公共成员变量对象 Field getDeclaredField(String name) 返回单个成员变量对象 -
Field类用于给成员变量赋值的方法
方法名 说明 void set(Object obj,Object value) 给obj对象的成员变量赋值为value -
Class类获取成员方法对象的方法
方法名 说明 Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的 Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的 Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共成员方法对象 Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象 -
Method类用于执行方法的方法
方法名 说明 Object invoke(Object obj,Object... args) 调用obj对象的成员方法,参数是args,返回值是Object类型
8.2 自定义注解
Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能,注解相关类都包含在java.lang.annotation包中。JDK基本注解例如 @Override(重写)、@SuppressWarnings(value = "unchecked")(抑制编辑器警告)
在Java中,自定义注解(Annotation)是通过@interface
关键字来定义的。
自定义注解的定义示例如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 元注解 @Target 指定注解可以应用的程序元素类型
@Target(ElementType.METHOD)
// 元注解 @Retention 指定注解的保留策略,这里是源码中保留,编译后不保留
@Retention(RetentionPolicy.SOURCE)
public @interface CustomAnnotation {
// 注解的属性
String value() default "default";
int number() default 42;
}
使用自定义注解的示例如下:
public class Example {
// 使用自定义注解,并设置属性值
@CustomAnnotation(value = "ExampleMethod", number = 24)
public void exampleMethod() {
// 方法实现
}
}
注意:
- 注解属性的定义类似于方法声明,但不包括方法体。
- 注解的属性可以有默认值。
- 注解可以没有属性,仅用作标识(标记📌Annotation)。有属性的叫做元数据Annotation。
JDK元注解
- @Retention: 定义注解的保留策略,策略如下
- RetentionPolicy.SOURCE // 注解仅存在于源码中,在class字节码文件中不包含
- RetentionPolicy.CLASS // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
- RetentionPolicy.RUNTIME // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
- @Target: 指定被修饰的Annotation可以放置的位置(被修饰的目标,可指定多个),位置/目标如下
- ElementType.TYPE // 接口、类
- ElementType.FIELD // 属性
- ElementType.METHOD // 方法
- ElementType.PARAMETER // 方法参数
- ElementType.CONSTRUCTOR // 构造函数
- ElementType.LOCAL_VARIABLE // 局部变量
- ElementType.ANNOTATION_TYPE // 注解
- ElementType.PACKAGE // 包
- @Inherited: 指定被修饰的Annotation将具有继承性
- @Documented: 指定被修饰的该Annotation可以被javadoc工具提取成文档.
注: 具体可结合AOP使用。
8.3 模块化
Java语言随着这些年的发展已经成为了一门影响深远的编程语言,无数平台,系统都采用Java语言编写。但是,伴随着发展,Java也越来越庞大,逐渐发展成为一门 “臃肿” 的语言。而且,无论是运行一个大型的软件系统,还是运行一个小的程序,即使程序只需要使用Java的部分核心功能, JVM也要加载整个JRE环境。 为了给Java “瘦身”,让 Java实现轻量化,Java 9正式的推出了模块化系统。Java被拆分为N多个模块,并允许Java程序可以根据需要选择加载程序必须的Java模块,这样就可以让Java以轻量化的方式来运行
其实,Java 7的时候已经提出了模块化的概念,但由于其过于复杂,Java 7,Java 8都一直未能真正推出,直到Java 9才真正成熟起来。对于Java语言来说,模块化系统是一次真正的自我革新,这种革新使得 “古老而庞大” 的Java语言重新焕发年轻的活力
8.3.1 基本使用
one_module
public class Boss {
public void payoff() {
System.out.println("今天咱们发工资!");
}
}
public class Employee {
public void work() {
System.out.println("赶紧工作");
}
}
module-info
module onemodule {
exports com.zn.opit.onemodule.pkg1;
exports com.zn.opit.onemodule.pkg2;
}
two_module
public class Test {
public static void main(String[] args) {
Employee employee = new Employee();
employee.work();
}
}
module-info
module towmodule {
requires onemodule;
}
8.3.2 模块服务
one-module
新增MyService、OneServiceImpl、TwoServiceImpl
module-info
新增 service
import com.zn.opit.onemodule.service.MyService;
import com.zn.opit.onemodule.service.impl.OneServiceImpl;
module onemodule {
exports com.zn.opit.onemodule.pkg1;
exports com.zn.opit.onemodule.pkg2;
exports com.zn.opit.onemodule.service;
provides MyService with OneServiceImpl;
}
two-module
新增Test2
public class Test2 {
public static void main(String[] args) {
// 加载服务
ServiceLoader<MyService> myServices = ServiceLoader.load(MyService.class);
// 遍历服务
for (MyService myService : myServices) {
myService.serviceMethod();
}
}
}
module-info
新增service
module towmodule {
uses com.zn.opit.onemodule.service.MyService;
requires onemodule;
}