1 java基础

1.1 java基础

1.1.1 java基础

java技术平台:JavaSE 标准版、JavaEE 企业版、JavaME 小型版
java主要特点:面向对象(OOP)、健壮性、跨平台性、解释性
JavaSE - 图1
JVM:Java Virtual Machine,java虚拟机,具有指令集并使用不同存储区域,负责执行指令,管理数据、内存、寄存器,包含JDK中。不同操作系统有不同的JVM,JVM屏蔽Java运行平台的差异,实现跨平台。
JRE:Java Runtime Environment,java运行环境,包含了JVM和java的核心类库。
JDK:Java Development Kit,java开发工具包,包含JRE和java开发工具(java,javac,javadoc,javap等)

1.1.2 面试题:JVM、JRE、JDK的区别

JavaSE - 图2

1.2 基础语法

1.2.1 数据类型

JavaSE - 图3

基本类型
基本类型分为4大类,共8种数据类型。
整数类型:byte、short、int、long
浮点类型:float、double
字符类型:char
布尔类型:boolean
其中整数类型、浮点类型和字符类型为为数值类型。

1.2.1.1 整数类型

整数类型 内存长度 取值范围
byte 1字节(8位) -128~127
short 2字节(16位) -215~215-1
int 4字节(32位) -231~231-1
long 8字节(64位) -263~263-1

注意:Java语言中,整数类型默认是int类型。long型需要在数值后面加L。例:
byte a = 16;
short b = 16;
int c = 16;
long d = 16L;

1.2.1.2 浮点类型

浮点类型分单精度浮点数(float)和双精度浮点数(double)。

浮点类 内存长度
float 4字节(32位)
double 8字节(64位)

注意:Java语言中,整数类型默认是double类型。float型需要在数值后面加F。例:

float money = 360.66F;
double money = 360.66;

1.2.1.3 数字标示方式

1.2.1.3.1 进制数字表示

二进制数:以0b或0B为前缀。例:0B11100。

八进制数:以0为前缀。例067。

十六进制数:以0x或0X为前缀。例:0x1C。

1.2.1.3.2 指数表示
进行数学计算时往往会用到指数表示的数值。如果采用十进制表示指数,需要使用大写或小写的e表示幂,e2表示102。

1.2.1.4 字符类型

Java中char声明字符类型,Java中的字符常量必须用单引号括起来的单个字符。例:

  1. char c = 'A';

Java字符采用双字节Unicode编码,占两个字节(16位),因而可用十六进制(无符号的)编码形式表示,它们的表现形式是\un,其中n为16位十六进制数,所以’A’字符也可以用Unicode编码’\u0041’表示。(Unicode字符列表:https://zh.wikipedia.org/wiki/Unicode)

注意:字符类型也属于数值类型,可以与int等数值类型进行计算或转换。char类型取值范围为0~216-1。

为表示一些特殊字符,前面加上反斜杠(\),称为转义字符。

转义字符 Unicode编码 说明
\t \u0009 水平字符tab
\n \u000a 换行
\r \u000d 回车
\“ \u0022 双引号
\‘ \u0027 单引号
\\ \u005c 反斜杠

1.2.1.5 布尔类型

布尔类型(boolean)只有2个值:true和false

1.2.2 基本数据类型转换

基本数据类型转换分为自动类型转换和强制类型转换。

1.2.2.1 自动类型转换

自动类型转换就是需要类型之间转换是自动的,不需要采取其他手段,总的原则是小范围数据类型可以自动转换为大范围数据类型,列类型转换顺序如下:

byte→short→int→long→float→double

char→int→long→float→double

自动类型转换不仅发生在赋值过程中,在进行数学计算时也会发生自动类型转换,在运算中往往是先将数据类型转换为同一类型,然后再进行计算。

byte\short op int →int

byte\short\int op →long

byte\short\int\long op float →float

byte\short\int\long\float op double→double

op表示运算符(加、减、乘等),箭头右边表示运算结果的数据类型

1.2.2.2 强制类型转换

高级数类型要转换为低级数据类型,需要用到强制类型转换。格式如下:

(类型名)表达式
例:
double money=9800;

float myMoney=(float)money;

注意:

1、强制类型转换可能会导致竖值溢出或精度下降,一般应尽量避免使用。

2、byte/short/char这三种类型都可以发生数学运算,例如加法“+”,这三种在运算时首先先提升成int类型,然后在进行运算。

3、boolean类型不能发生数据类型转换。

1.2.3 引用数据类型

Java中除8种基本类型外都是引用数据类型,包括字符串、数组、类、接口等。引用数据类型保存的是对象的地址值,而不是对象本身。

1.2.4 运算符

Java根据操作数个数可分为:单目运算符、双目运算符、三目运算符。根据功能可分为:算术运算符、关系运算符、逻辑运算符、位运算符、条件运算符、赋值运算符和其他。

1.2.3.1 常用运算符

Java根据操作数个数可分为:单目运算符、双目运算符、三目运算符。根据功能可分为:算术运算符、关系运算符、逻辑运算符、位运算符、条件运算符、赋值运算符和其他。

分类 运算符 描述
算术运算符 +、-、*、/ 加、减、乘、除
算术运算符 % 求余运算,一般要求两边操作数均为整型
算术运算符 ++、— 自增、自减
关系运算符 >、<、>=、<=、==、!= 关系运算符两边的数据类型应一致,一个关系表达式的结果类型为布尔型true\false
逻辑运算符 !(逻辑非)、&&(短路与)、||(短路或)、&(逻辑与)、|(逻辑或) 这些运算符要求的操作数与结果值都是布尔型
位运算 >>(有符号二进制右移一定位)、<<(有符号二进制左移一定位)、>>>(无符号二进制右移一定位)、&(按位与)、|(按位或)、^(按位异或)、~(按位取反)
条件运算符 ? : 唯一的三元运算符,格式为: 变量= <布尔表达式>?<表达式1>:<表达式2>
赋值运算符 =(语法格式:变量名=表达式;)、复合赋值运算符:+=、-=、*-、/=、%=、>>=、<<=、>>>=等 复合赋值运算符注意事项:1.只有变量才能使用赋值运算符;2.复合赋值运算符其中隐含了一个强制类型转换)
其他 分量运算符.、下标运算符[]、实例运算符instanceof

1.2.3.2 运算符优先级

运算符优先级大体顺序,从高到低是:算术运算符→位运算符→关系运算符→逻辑运算符→赋值运算符。

优先级 运算符
1 .(引用号)、小括号、中括号
2 ++、—、-(数值取反)、~(按位取反)、!(逻辑非)、类型转换小括号
3 *、/、%
4 +、-
5 <<、>>、>>>
6 >、<、<=、>=、instanceof
7 ==、!=
8 &(逻辑与、位与)
9 ^(位异或)
10 |(逻辑或、位或)
11 &&
12 ||
13 ?:
14 ->
15 =、*=、/=、%=、+=、-=、<<=、>>=、>>>=、&=、^=、|=

1.2.5 控制语句

分支语句:if、switch

循环语句:while、do while、for

跳转语句:break、continue、return、throw

1.2.5.1 分支语句

1. if语句
格式1(不带else):

  1. if(条件表达式){
  2. 语句体;
  3. }

格式2(带else):

  1. if(条件表达式){
  2. 语句体1;
  3. }else{
  4. 语句体2;
  5. }

格式2(嵌套):

  1. if(条件表达式1){
  2. 语句体1;
  3. }else if(条件表达式2){
  4. 语句体2;
  5. }else if(条件表达式3){
  6. ……
  7. }else if(条件表达式n){
  8. 语句体n;
  9. }else{
  10. 语句体n+1;
  11. }

2.switch语句
switch语句格式:

  1. switch (表达式) {
  2. case 1:{
  3. 语句组1
  4. }
  5. break;
  6. case 2:{
  7. 语句组2
  8. }
  9. break;
  10. case 3:{
  11. 语句组3
  12. }
  13. break;
  14. ...
  15. case 判断值n:{
  16. 语句组n
  17. }
  18. break;
  19. default:{
  20. 语句组n1
  21. }
  22. }

注意:

1、switch语句中“表达式”计算结果只能是int、byte、short和char类型,不能是long更不能其他的类型。

2、每个case后面只能跟一个int、byte、short和char类型的常量,default语句可以省略。

3、当程序执行到switch语句时,先计算条件表达式的,执行表达式结果匹配的语句组。没有只执行default。完成后不跳出switch,只有遇到break或执行完全部语句才跳出switch。

1.2.5.2 循环语句

1 while 语句
while语句是一种先判断的循环结构,格式如下:

  1. while (循环条件) {
  2. 语句组;
  3. }

2 do while 语句

do while语句是事后判断循环条件结构,语句格式如下:

  1. do {
  2. 语句组
  3. } while (循环条件);

注意: do-while循环没有初始化语句,循环次数是不可知的,不管循环条件是否满足,都会先执行一次循环体,然后再判断循环条件。

3 for语句

for语句是最常用的循环语句,格式如下:

  1. for (初始化; 循环条件; 迭代) {
  2. 语句组;
  3. }

注意: 初始化、循环条件以及迭代部分都可以为空语句(但分号不能省略),三者均为空的时候,相当于一个无限循环。

4 补充:
如果只遍历数组或者集合,可以用for each循环。for each循环不必按照for的标准套路编写代码,只需要提供一个集合就可以遍历。
例:

  1. public static void main(arg[] String){
  2. List<String> list = new ArrayList<String>();
  3. list.add("Google");
  4. list.add("Runoob");
  5. list.add("Taobao");
  6. System.out.println("----------方式1:普通for循环-----------");
  7. for(int i = 0; i < list.size(); i++){
  8. System.out.println(list.get(i));
  9. }
  10. System.out.println("----------方式3:For-Each 循环-----------");
  11. for(String str: list){
  12. System.out.println(str);
  13. }
  14. }

1.2.5.3 跳转语句

1 break语句
break语句可用于上一节介绍的while、repeat-while和for循环结构,它的作用是强行退出循环体,不再执行循环体中剩余的语句。

在循环体中使用break语句有两种方式:带有标签和不带标签。语法格式如下:

不带标签:break;

带标签:break label;

不带标签的break语句使程序跳出所在层的循环体,而带标签的break语句使程序跳出标签指示的循环体。

2 continue语句

continue语句用来结束本次循环,跳过循环体中尚未执行的语句,接着进行终止条件的判断,以决定
是否继续循环。对于for语句,在进行终止条件的判断前,还要先执行迭代语句。

在循环体中使用continue语句有两种方式可以带有标签,也可以不带标签。语法格式如下:

不带标签:continue

带标签:continue label

1.2.6 数组

数组具有三个基本特性:

  1. 一致性:同一个数组只能保存相同数据类型元素。
  2. 有序性:数组元素是有序的,通过下标访问。数组的下标从0开始。
  3. 不可变性:数组一旦初始化,则数组中元素的个数不可变。

数组可分为一维数组和多维数组。
数组声明需要用关键字new进行创建,会在堆内存分配空间。对数组进行赋值前数组内保存的是默认值,根据数组的数据类型不同,见下表:

数组类型 默认值
byte、short、int 0
long 0L
float 0.0F
double 0.0
char ‘\u0000’
boolean flase
引用类型(数组、字符串、接口等) null

1.2.6.1 一维数组

1 静态数组
声明格式:
元素数据类型[] 数组名 = new 元素数据类型[]{元素1、元素2……元素n};

也可写成:
元素数据类型[] 数组名 = new 元素数据类型[];

数组名={元素1,元素2……元素n};

也可简写为:

元素数据类型[] 数组名 = {元素1,元素2,……元素n};
通过下标进行赋值。
例:

  1. int[] num1 = new int[]{1,2,3,4,5};// 法1
  2. int[] num2 =new int[];//法2
  3. num2={1,2,34,5};
  4. int[] num3={1,2,34,5};//法3

2 动态数组
声明格式:

元素数据类型[] 数组名 = new 元素数据类型[数组长度];

例:

  1. int[] num = new int[10];

1.2.6.2 二维数组

声明格式:

元素数据类型[][] 数组名 = new 元素数据类型[第一维长度][第二维长度];

说明:声明时,可直接为每一维分配空间,也可从最高维开始为每一维分配空间。

例:

  1. //直接为每一维分配空间
  2. int[][] num = new int[2][3];
  3. //从最高维开始,分别为每一维分配空间
  4. int[][] num = new int[2][];
  5. num[0] = new int[3];
  6. num[1] = new int[5];

注意:在声明多维数组时,至少要给出最高维的大小。

数组赋值可声明后直接对每个元素进行赋值,也可在声明数组时同时进行。对每个数组的访问也是通过下标进行,多维数组的每一维下标取值都是从0开始。

例:

  1. int[][] num = new int[][]{{1,2,3},{4,5,6}};//声明时直接赋值,数组大小为int[2][3]
  2. int[][] num = new int[2][3];
  3. for(i =0;i<num.length;i++){
  4. for(j=0;j<num[i].length;j++){
  5. num[i][j]=i
  6. }
  7. }// 动态初始化,声明后对每个元素进行赋值。结果:num={{1,1,1},{2,2,2}}

补充: 多维数组可定义成不规则数组,即第二维空间大小不相同。例:

  1. int[][] num = int[3][];
  2. num[0] = new int[3];
  3. num[1] = new int[1];
  4. num[2] = new int [2];

示意图如下:

num[0][0] num[0][1] num[0][2]
num[1][0]
num[2][0] num[2][1]

1.2.7 字符串

JavaSE提供三个字符串类:String(不可变字符串),StringBuffer(可变字符串),StringBuilder(可变字符串)。

1.2.7.1 String类

String在java.lang包下,使用时可不需要导包。(java.lang包是唯一不需要导包操作的包)具体用法参考API文档。

1 String类特点:

1、字符串内容不可变。

2、正是因为字符串内容不可变,所以字符串是可以共享使用的。

3、字符串效果上相当于char[]数组,但是底层原理是byte[]字节数组。

2 String对象创建

1、public String();创建一个空的字符串,不含任何内容。

2、public String(String original);使用另一个字符串创建并初始化一个新的String,换句话说,新创建的字符串是该参数字符串的副本。original:一个String。

4、public String(char[] value);通过字符数组创建并初始化一个新的 String 对象。

5、String(char[] value, int offset,int count);通过字符数组的子数组创建并初始化一个新的 String对象;offset参数是子数组第一个字符的索引,count参数指定子数组的长度。

6、public String(byte[] value);通过字节数组创建并初始化一个新的String对象。

7、public String(StringBuffer buffer);使用可变字符串对象(StringBuffer)创建并初始化一个新的 String对象。buffer:一个StringBuffer。

8、public String(StringBuilder builder);使用可变字符串对象(StringBuilder)创建并初始化一个新的String 象。

9、public String(byte[] bytes);使用平台的默认字符集解码指定的byte数组,通过byte数组创建并初始化一个新的String 对象。builder:一个StringBuilder。
例:

  1. String s1 = new String();
  2. String s2 = new String("Hello World");
  3. String s3 = new String("\u0048\u0065\u006c\u006c\u006f\u0020\u0057");
  4. System.out.println("s2 = " + s2);
  5. System.out.println("s3 = " + s3);
  6. char chars[] = { 'a', 'b', 'c', 'd', 'e' };
  7. String s4 = new String(chars);// 通过字符数组创建字符串对象
  8. String s5 = new String(chars, 1, 4);// 通过子字符数组创建字符串对象
  9. System.out.println("s4 = " + s4);
  10. System.out.println("s5 = " + s5);
  11. byte bytes[] = { 97, 98, 99 };
  12. String s6 = new String(bytes);// 通过byte数组创建字符串对象
  13. System.out.println("s6 = " + s6);
  14. System.out.println("s6字符串长度 = " + s6.length());
  15. //输出结果
  16. s2 = Hello World
  17. s3 = Hello World
  18. s4 = abcde
  19. s5 = bcde
  20. s6 = abc
  21. s6字符串长度 = 3

3 字符串常量池
程序中直接写上双引号的字符串,就是在字符串常量池中。

1.2.7.2 面试题: String,StringBuilder,StringBuffer的区别

1、String 字符串常量,一经创建不可修改,对字符串的拼接操作都会创建新的对象,占用内存。StringBulider和StringBuffer是字符串变量,只需要创建一个对象,然后对引用的值进行修改即可,不会创建新的对象。
2、StringBuffer是线程安全,方法被synchronized关键字修饰;StringBuilder是线程不安全的。
3、在执行速度上,StringBuilder>StringBuffer>String
基于以上,对与操作少量数据选择String,对于多线程大量数据选择StringBuffer,对于单线程大量数据选择StringBuilder

2 面向对象

3 java常用类

3.1 java.util.Objects

结合源码理解java.util.Objects:https://blog.csdn.net/weixin_42391752/article/details/119454895

3.2 java.util.Arrays

结合源码理解java.utils.Arrays:https://blog.csdn.net/weixin_42391752/article/details/119455187

3.3 java.lang.Math

结合源码理解java.lang.Math:https://blog.csdn.net/weixin_42391752/article/details/119455268

3.4 日期类

3.4.1 java.util.Date

//获取当前系统时间,为不超过 year 8099 的毫秒表示的自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数
Date date = new Date();

3.4.2 java.text.SimpleDateFormat


//1. 创建SimpleDateFormat 对象,可以指定相应的格式
//2. 这里的格式使用的字母是规定好,不能乱写

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format = sdf.format(date); // format:将日期转换成指定格式的字符串

//1. 可以把一个格式化的String 转成对应的Date
//2. 得到Date 仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
//3. 在把String -> Date , 使用的sdf 格式需要和你给的String 的格式一样,否则会抛出转换异常

String s = "1996 年01 月01 日10:20:30 星期一";
Date parse = sdf.parse(s);

3.4.3 java.util.Calendar

//通过getInstance()方法创建日历类对象
Calendar c = Calendar.getInstance(); 

//2.获取日历对象的某个日历字段,Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println("年:" + c.get(Calendar.YEAR));
// 这里为什么要+ 1, 因为Calendar 返回月时候,是按照0 开始编号
System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:" + c.get(Calendar.HOUR));
System.out.println("分钟:" + c.get(Calendar.MINUTE));
System.out.println("秒:" + c.get(Calendar.SECOND));

3.4.4 java.time.LocalDate

//1. 使用now() 返回表示当前日期时间的对象
LocalDateTime ldt1 = LocalDateTime.now();//只包含日期,可以获取日期字段
LocalDate ldt2 = LocalDate.now();//只包含时间,可以获取时间字段
LocalTime ldt3 = LocalTime.now();//包含日期、时间,可以获取日期、时间字段

//2. 使用DateTimeFormatter 对象来进行格式化
// 创建DateTimeFormatter 对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt1);
System.out.println("格式化的日期=" + format);

3.3.5 java.time.Instant 时间戳

//1.通过静态方法now() 获取表示当前时间戳的对象
Instant now = Instant.now();
//2. 通过from 可以把Instant 转成Date
Date date = Date.from(now);
//3. 通过date 的toInstant() 可以把date 转成Instant 对象
Instant instant = date.toInstant();

4 集合框架

image.png

4.1 Collection

Collection:https://blog.csdn.net/weixin_42391752/article/details/119490283

4.2 Map

Map:https://blog.csdn.net/weixin_42391752/article/details/119550396
https://www.cnblogs.com/semi-sub/p/12953414.html
https://blog.csdn.net/fengxi_tan/article/details/106629280

4.3 面试题

java集合面试题:https://blog.csdn.net/u010775025/article/details/79315361

4.3.1 Java集合框架是什么?说出一些集合框架的优点?

每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector、Stack、HashTable和Array。随着集合的广泛使用,Java1.2提出了囊括所有集合接口、实现和算法的集合框架。在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了很久。它还包括在Java并发包中,阻塞接口以及它们的实现。集合框架的部分优点如下:<br />(1)使用核心集合类降低开发成本,而非实现我们自己的集合类。<br />(2)随着使用经过严格测试的集合框架类,代码质量会得到提高。<br />(3)通过使用JDK附带的集合类,可以降低代码维护成本。<br />(4)复用性和可操作性。

4.3.2 集合框架中的泛型有什么优点?

Java1.5引入了泛型,所有的集合接口和实现都大量地使用它。泛型允许我们为集合提供一个可以容纳的对象类型,因此,如果你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现ClassCastException,因为你将会在编译时得到报错信息。泛型也使得代码整洁,我们不需要使用显式转换和instanceOf操作符。它也给运行时带来好处,因为不会产生类型检查的字节码指令。

4.3.3 Java集合框架的基础接口有哪些?

Collection为集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。<br />    Set是一个不能包含重复元素的集合。这个接口对数学集合抽象进行建模,被用来代表集合,就如一副牌。<br />    List是一个有序集合,可以包含重复元素。你可以通过它的索引来访问任何元素。List更像长度动态变换的数组。<br />    Map是一个将key映射到value的对象.一个Map不能包含重复的key:每个key最多只能映射一个value。<br />    一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。

4.3.4 为何Collection不从Cloneable和Serializable接口继承?

Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。<br />    当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。<br />    在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。

4.3.5 为何Map接口不继承Collection接口?

尽管Map接口和它的实现也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map继承Collection毫无意义,反之亦然。<br />    如果Map继承Collection接口,那么元素去哪儿?Map包含key-value对,它提供抽取key或value列表集合的方法,但是它不适合“一组对象”规范。

4.3.6 Iterator是什么?

Iterator接口提供遍历任何Collection的接口。我们可以从一个Collection中使用迭代器方法来获取迭代器实例。迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者在迭代过程中移除元素。

4.3.7 Enumeration和Iterator接口的区别?

Enumeration的速度是Iterator的两倍,也使用更少的内存。Enumeration是非常基础的,也满足了基础的需要。但是,与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合。

迭代器取代了Java集合框架中的Enumeration。迭代器允许调用者从集合中移除元素,而Enumeration不能做到。为了使它的功能更加清晰,迭代器方法名已经经过改善。

4.3.8 为何没有像Iterator.add()这样的方法,向集合中添加元素?

语义不明,已知的是,Iterator的协议不能确保迭代的次序。然而要注意,ListIterator没有提供一个add操作,它要确保迭代的顺序。

4.3.9 为何迭代器没有一个方法可以直接获取下一个元素,而不需要移动游标?

它可以在当前Iterator的顶层实现,但是它用得很少,如果将它加到接口中,每个继承都要去实现它,这没有意义。

4.3.10 Iterater和ListIterator之间有什么区别?

(1)我们可以使用Iterator来遍历Set和List集合,而ListIterator只能遍历List。

(2)Iterator只可以向前遍历,而ListIterator可以双向遍历。

(3)ListIterator从Iterator接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。

4.3.11 遍历一个List有哪些不同的方式?

List<String> strList = new ArrayList<>();
//使用for-each循环
for(String obj : strList){
    System.out.println(obj);
}
//using iterator
Iterator<String> it = strList.iterator();
while(it.hasNext()){
    String obj = it.next();
    System.out.println(obj);
}
使用迭代器更加线程安全,因为它可以确保,在当前遍历的集合元素被更改的时候,它会抛出ConcurrentModificationException。

4.3.12 通过迭代器fail-fast属性,你明白了什么?

每次我们尝试获取下一个元素的时候,Iterator fail-fast属性检查当前集合结构里的任何改动。如果发现任何改动,它抛出ConcurrentModificationException。Collection中所有Iterator的实现都是按fail-fast来设计的(ConcurrentHashMap和CopyOnWriteArrayList这类并发集合类除外)。

4.3.13 fail-fast与fail-safe有什么区别?

Iterator的fail-fast属性与当前的集合共同起作用,因此它不会受到集合中任何改动的影响。Java.util包中的所有集合类都被设计为fail-fast的,而java.util.concurrent中的集合类都为fail-safe的。Fail-fast迭代器抛出ConcurrentModificationException,而fail-safe迭代器从不抛出ConcurrentModificationException。

4.3.14 在迭代一个集合的时候,如何避免ConcurrentModificationException?

在遍历一个集合的时候,我们可以使用并发集合类来避免ConcurrentModificationException,比如使用CopyOnWriteArrayList,而不是ArrayList。

4.3.15 为何Iterator接口没有具体的实现?

Iterator接口定义了遍历集合的方法,但它的实现则是集合实现类的责任。每个能够返回用于遍历的Iterator的集合类都有它自己的Iterator实现内部类。

这就允许集合类去选择迭代器是fail-fast还是fail-safe的。比如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的。

4.3.16 UnsupportedOperationException是什么?

UnsupportedOperationException是用于表明操作不支持的异常。在JDK类中已被大量运用,在集合框架java.util.Collections.UnmodifiableCollection将会在所有add和remove操作中抛出这个异常。

4.3.17 在Java中,HashMap是如何工作的?

HashMap在Map.Entry静态内部类实现中存储key-value对。HashMap使用哈希算法,在put和get方法中,它使用hashCode()和equals()方法。当我们通过传递key-value对调用put方法的时候,HashMap使用Key hashCode()和哈希算法来找出存储key-value对的索引。Entry存储在LinkedList中,所以如果存在entry,它使用equals()方法来检查传递的key是否已经存在,如果存在,它会覆盖value,如果不存在,它会创建一个新的entry然后保存。当我们通过传递key调用get方法时,它再次使用hashCode()来找到数组中的索引,然后使用equals()方法找出正确的Entry,然后返回它的值。下面的图片解释了详细内容。

其它关于HashMap比较重要的问题是容量、负荷系数和阀值调整。HashMap默认的初始容量是16,负荷系数是0.75。阀值是为负荷系数乘以容量,无论何时我们尝试添加一个entry,如果map的大小比阀值大的时候,HashMap会对map的内容进行重新哈希,且使用更大的容量。容量总是2的幂,所以如果你知道你需要存储大量的key-value对,比如缓存从数据库里面拉取的数据,使用正确的容量和负荷系数对HashMap进行初始化是个不错的做法。

4.3.18 hashCode()和equals()方法有何重要性?

HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。equals()和hashCode()的实现应该遵循以下规则:

(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。

(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。

4.3.19 我们能否使用任何类作为Map的key?

我们可以使用任何类作为Map的key,然而在使用它们之前,需要考虑以下几点:

(1)如果类重写了equals()方法,它也应该重写hashCode()方法。

(2)类的所有实例需要遵循与equals()和hashCode()相关的规则。请参考之前提到的这些规则。

(3)如果一个类没有使用equals(),你不应该在hashCode()中使用它。

(4)用户自定义key类的最佳实践是使之为不可变的,这样,hashCode()值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode()和equals()在未来不会改变,这样就会解决与可变相关的问题了。

比如,我有一个类MyKey,在HashMap中使用它。
//传递给MyKey的name参数被用于equals()和hashCode()中
MyKey key = new MyKey('Pankaj'); //assume hashCode=1234
myHashMap.put(key, 'Value');
// 以下的代码会改变key的hashCode()和equals()值
key.setName('Amit'); //assume new hashCode=7890
//下面会返回null,因为HashMap会尝试查找存储同样索引的key,而key已被改变了,匹配失败,返回null
myHashMap.get(new MyKey('Pankaj'));
那就是为何String和Integer被作为HashMap的key大量使用。

4.3.20 Map接口提供了哪些不同的集合视图?

Map接口提供三个集合视图:

(1)Set keyset():返回map中包含的所有key的一个Set视图。集合是受map支持的,map的变化会在集合中反映出来,反之亦然。当一个迭代器正在遍历一个集合时,若map被修改了(除迭代器自身的移除操作以外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

(2)Collection values():返回一个map中包含的所有value的一个Collection视图。这个collection受map支持的,map的变化会在collection中反映出来,反之亦然。当一个迭代器正在遍历一个collection时,若map被修改了(除迭代器自身的移除操作以外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

(3)Set> entrySet():返回一个map钟包含的所有映射的一个集合视图。这个集合受map支持的,map的变化会在collection中反映出来,反之亦然。当一个迭代器正在遍历一个集合时,若map被修改了(除迭代器自身的移除操作,以及对迭代器返回的entry进行setValue外),迭代器的结果会变为未定义。集合支持通过Iterator的Remove、Set.remove、removeAll、retainAll和clear操作进行元素移除,从map中移除对应的映射。它不支持add和addAll操作。

4.3.21 HashMap和HashTable有何不同?

(1)HashMap允许key和value为null,而HashTable不允许。

(2)HashTable是同步的,而HashMap不是。所以HashMap适合单线程环境,HashTable适合多线程环境。

(3)在Java1.4中引入了LinkedHashMap,HashMap的一个子类,假如你想要遍历顺序,你很容易从HashMap转向LinkedHashMap,但是HashTable不是这样的,它的顺序是不可预知的。

(4)HashMap提供对key的Set进行遍历,因此它是fail-fast的,但HashTable提供对key的Enumeration进行遍历,它不支持fail-fast。

(5)HashTable被认为是个遗留的类,如果你寻求在迭代的时候修改Map,你应该使用CocurrentHashMap。

4.3.22 如何决定选用HashMap还是TreeMap?

对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。

4.3.23 ArrayList和Vector有何异同点?

ArrayList和Vector在很多时候都很类似。

(1)两者都是基于索引的,内部由一个数组支持。

(2)两者维护插入的顺序,我们可以根据插入顺序来获取元素。

(3)ArrayList和Vector的迭代器实现都是fail-fast的。

(4)ArrayList和Vector两者允许null值,也可以使用索引值对元素进行随机访问。

以下是ArrayList和Vector的不同点。

(1)Vector是同步的,而ArrayList不是。然而,如果你寻求在迭代的时候对列表进行改变,你应该使用CopyOnWriteArrayList。

(2)ArrayList比Vector快,它因为有同步,不会过载。

(3)ArrayList更加通用,因为我们可以使用Collections工具类轻易地获取同步列表和只读列表。

4.3.24 Array和ArrayList有何区别?什么时候更适合用Array?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。

Array是指定大小的,而ArrayList大小是固定的。

Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。尽管ArrayList明显是更好的选择,但也有些时候Array比较好用。

(1)如果列表的大小已经指定,大部分情况下是存储和遍历它们。

(2)对于遍历基本数据类型,尽管Collections使用自动装箱来减轻编码任务,在指定大小的基本类型的列表上工作也会变得很慢。

(3)如果你要使用多维数组,使用[][]比List>更容易。

4.3.25 ArrayList和LinkedList有何区别?

ArrayList和LinkedList两者都实现了List接口,但是它们之间有些不同。

(1)ArrayList是由Array所支持的基于一个索引的数据结构,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList存储一系列的节点数据,每个节点都与前一个和下一个节点相连接。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。

(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。

(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。

4.3.26 哪些集合类提供对元素的随机访问?

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问。

4.3.27 EnumSet是什么?
java.util.EnumSet是使用枚举类型的集合实现。当集合创建时,枚举集合中的所有元素必须来自单个指定的枚举类型,可以是显示的或隐示的。EnumSet是不同步的,不允许值为null的元素。它也提供了一些有用的方法,比如copyOf(Collection c)、of(E first,E…rest)和complementOf(EnumSet s)。

4.3.28 哪些集合类是线程安全的?

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用。Java1.5并发API包括一些集合类,允许迭代时修改,因为它们都工作在集合的克隆上,所以它们在多线程环境中是安全的。

4.3.29 并发集合类是什么?

Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合。迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。一部分类为:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet。

4.3.30 BlockingQueue是什么?

Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。

4.3.31 队列和栈是什么,列出它们的区别?

栈和队列两者都被用来预存储数据。java.util.Queue是一个接口,它的实现类在Java并发包中。队列允许先进先出(FIFO)检索元素,但并非总是这样。Deque接口允许从两端检索元素。

栈与队列很相似,但它允许对元素进行后进先出(LIFO)进行检索。

Stack是一个扩展自Vector的类,而Queue是一个接口。

4.3.32 Collections类是什么?

Java.util.Collections是一个工具类仅包含静态方法,它们操作或返回集合。它包含操作集合的多态算法,返回一个由指定集合支持的新集合和其它一些内容。这个类包含集合框架算法的方法,比如折半搜索、排序、混编和逆序等。

4.3.33 Comparable和Comparator接口是什么?

如果我们想使用Array或Collection的排序方法时,需要在自定义类里实现Java提供Comparable接口。Comparable接口有compareTo(T OBJ)方法,它被排序方法所使用。我们应该重写这个方法,如果“this”对象比传递的对象参数更小、相等或更大时,它返回一个负整数、0或正整数。但是,在大多数实际情况下,我们想根据不同参数进行排序。比如,作为一个CEO,我想对雇员基于薪资进行排序,一个HR想基于年龄对他们进行排序。这就是我们需要使用Comparator接口的情景,因为Comparable.compareTo(Object o)方法实现只能基于一个字段进行排序,我们不能根据对象排序的需要选择字段。Comparator接口的compare(Object o1, Object o2)方法的实现需要传递两个对象参数,若第一个参数比第二个小,返回负整数;若第一个等于第二个,返回0;若第一个比第二个大,返回正整数。

4.3.34 Comparable和Comparator接口有何区别?

Comparable和Comparator接口被用来对对象集合或者数组进行排序。Comparable接口被用来提供对象的自然排序,我们可以使用它来提供基于单个逻辑的排序。

Comparator接口被用来提供不同的排序算法,我们可以选择需要使用的Comparator来对给定的对象集合进行排序。

4.3.35 我们如何对一组对象进行排序?

如果我们需要对一个对象数组进行排序,我们可以使用Arrays.sort()方法。如果我们需要排序一个对象列表,我们可以使用Collection.sort()方法。两个类都有用于自然排序(使用Comparable)或基于标准的排序(使用Comparator)的重载方法sort()。Collections内部使用数组排序方法,所有它们两者都有相同的性能,只是Collections需要花时间将列表转换为数组。

4.3.36 当一个集合被作为参数传递给一个函数时,如何才可以确保函数不能修改它?

在作为参数传递之前,我们可以使用Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,这将确保改变集合的任何操作都会抛出UnsupportedOperationException。

4.3.37 我们如何从给定集合那里创建一个synchronized的集合?

我们可以使用Collections.synchronizedCollection(Collection c)根据指定集合来获取一个synchronized(线程安全的)集合。

4.3.38 集合框架里实现的通用算法有哪些?

Java集合框架提供常用的算法实现,比如排序和搜索。Collections类包含这些方法实现。大部分算法是操作List的,但一部分对所有类型的集合都是可用的。部分算法有排序、搜索、混编、最大最小值。

4.3.39 大写的O是什么?举几个例子?

大写的O描述的是,就数据结构中的一系列元素而言,一个算法的性能。Collection类就是实际的数据结构,我们通常基于时间、内存和性能,使用大写的O来选择集合实现。比如:例子1:ArrayList的get(index i)是一个常量时间操作,它不依赖list中元素的数量。所以它的性能是O(1)。例子2:一个对于数组或列表的线性搜索的性能是O(n),因为我们需要遍历所有的元素来查找需要的元素。

4.3.40 与Java集合框架相关的有哪些最好的实践?

(1)根据需要选择正确的集合类型。比如,如果指定了大小,我们会选用Array而非ArrayList。如果我们想根据插入顺序遍历一个Map,我们需要使用TreeMap。如果我们不想重复,我们应该使用Set。

(2)一些集合类允许指定初始容量,所以如果我们能够估计到存储元素的数量,我们可以使用它,就避免了重新哈希或大小调整。

(3)基于接口编程,而非基于实现编程,它允许我们后来轻易地改变实现。

(4)总是使用类型安全的泛型,避免在运行时出现ClassCastException。

(5)使用JDK提供的不可变类作为Map的key,可以避免自己实现hashCode()和equals()。

(6)尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性。

5 I/O

IO:https://blog.csdn.net/weixin_42391752/article/details/119652034

6 多线程

多线程:https://blog.csdn.net/weixin_42391752/article/details/119656607

7 网络编程

8 注解

8.1 什么是注解

Annotation 是JDK5.0开始引入的技术。其作用是,可以对程序做出解释,可以被其他程序(如编译器)读取。Annotation的格式是以”@注解名”在代码中存在,也可以添加一些参数值。例:

@SuppresssWarnings(value="unchecked")

Annotation可以附加在package,class,method,field等上面。相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问。

8.2 常用内置注解

@Override

java.lang.Override,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明。

@Deprecated

java.lang.Deprecated,此注解可以用于修饰方法,属性,类,表示该方法、属性、类已经废弃(存在bug或有更好的选择),不建议适用。

@SuppressWarnings

java.lang.SuppressWarnings,此注解用来抑制编译时的警告信息。该注解需要添加一个参数才能正常适用。例

@SuppressWarnings("all");
@suppressWarnings("unchecked");
@suppressWarnings(value={"unchecked","deprecation"});

8.3 元注解

元注解是负责注解其他注解,java定义了4个标准的meta-annotation类型(元注解),用来提供对其他annotation类型作说明。这四个元注解为:

@Target

@Target,用于描述注解的使用范围(该注解可以用在什么地方)
( TYPE,FIELD,METHOD,PARAMETER, CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,TYPE_PARAMETER,TYPE_USE,MODULE)

@Retention

@Retention,表示需要在什么级别保存注释信息,用于描述注解的生命周期。(SOURCE

@Documented

@Documented,说明该注解被包含在javadoc中

@Inherited

@Inherited,说明子类可以继承父类中的该注解

8.4 自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口。

自定义注解格式:public @interface 注解名{定义的内容}
注解的参数:参数类型+参数名();
说明:
1、其中的每一个方法实际上是声明了一个配置参数;
2、方法的名称是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型,Class,String,enum);
3、可以通过default来声明参数的默认值、如果只有一个参数成员,一般参数名为value;
4、注解元素必须要有值,定义元注解是,经常使用空字符串,0作为默认值。
例:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(METHOD)
public @interface MyTest {
    String[] value() ;
}

9 反射

9.1 反射机制概述

9.1.1 理解动态语言与静态语言

动态语言:在运行时可以改变其结构的语言,通俗点将就是在运行是代码可以根据某些条件改变自身结构。主要动态语言:Object-C,C#,javaScript,PHP,Python等;
静态语言:运行时结构不可改变的语言就是静态语言。如java,C,C++。

9.1.2 Reflection

Reflection(反射)是java被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能够直接操作任意对象的内部属性和方法。

加载玩类后,在堆内存的方法区中产生一个Class类型的对象(一个类只有一个Class类对象),这个对象包含了完整的类的结构信息,可以通过这个对象看到类的结构。

9.1.2 主要API

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的属性
java.lang.reflect.Constructor:代表类的构造器

9.2理解Class类并获取Class实例

9.2.1 获取Class类的实例

1、若已知具体的类,通过类的class属性获取,最安全可靠,性能最高。例:

//类.class ;  类必须存在,不然编译报错

public class ClassLoadDemo {
    public static void main(String[] args) throws ClassNotFoundException { 
        // Class<?> aClass = Class.forName("com.java.practice.reflection.Teacher");
        //未创建Teacher类时编译报错:Cannot resolve symbol 'Teacher'
        Class<Teacher> teacherClass = Teacher.class;  
    }

通过对第三行打断点debug查看程序运行过程,可以看到运行到加载类:

public Class<?> loadClass(String name) throws ClassNotFoundException {  
        return loadClass(name, false);  //name:"com.java.practice.reflection.Teacher"
    }

2、已知某个类的实例,调用该实例的getClass()方法获取Class对象。例:

// 对象.getclass();  创建了对象,类已经加载到内存,通过getclass()方法会累该类的Class类对象。既然要先创建对象,那类必须存在。

public class ClassLoadDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // Class<?> aClass = Class.forName("com.java.practice.reflection.Teacher");
        // Class<Teacher> teacherClass = Teacher.class;
        //未创建Teacher类时编译报错:Cannot resolve symbol 'Teacher'
        Teacher teacher = new Teacher();
        Class<? extends Teacher> aClass = teacher.getClass();
    }
}    

在new Teacher()创建对象时就会进行类的加载
  public Class<?> loadClass(String name) throws ClassNotFoundException {  
        return loadClass(name, false);  //name:"com.java.practice.reflection.Teacher"
    }

3、已知一个类的全名,且在该类在类路径下,可以通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException。例:

// Class.forName(String className);  类可以不存在编译阶段也不会报错,动态加载。

public class ClassLoadDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //编译不会出错,但是运行时类不存在则会抛异常java.lang.ClassNotFoundException
        Class<?> aClass = Class.forName("com.java.practice.reflection.Teacher");   
    }
}


通过对第三行打断点debug查看程序运行过程,可以看到运行到加载类:

public Class<?> loadClass(String name) throws ClassNotFoundException {  
        return loadClass(name, false);  //name:"com.java.practice.reflection.Teacher"
    }

4、内置基本数据类型可以直接用类型.Type
5、使用ClassLoader进行加载

//通过类加载器ClassLoader得到Class对象

public class ClassLoadDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // Class<?> aClass = Class.forName("com.java.practice.reflection.Teacher");
        // Class<Teacher> teacherClass = Teacher.class;
        //Teacher teacher = new Teacher();
        //Class<? extends Teacher> aClass = teacher.getClass();

        //获取到一个类加载器ClassLoader
        ClassLoader classLoader = Student.class.getClassLoader();
        //编译不会出错,但是运行时类不存在则会抛异常java.lang.ClassNotFoundException
        Class<?> aClass = classLoader.loadClass("com.java.practice.reflection.Teacher");     
    }
}

加载类:
public Class<?> loadClass(String name) throws ClassNotFoundException {  
        return loadClass(name, false);  //name:"com.java.practice.reflection.Teacher"
}

9.3 类的加载与ClassLoader

  1. 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
  2. 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    1. 验证:确保加载的信息符合JVM规范,没有安全方面的问题;
    2. 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配;
    3. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  3. 初始化
    1. 执行类构造器,()方法的过程。类构造器()方法是有编译器自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生的。
    2. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
    3. 虚拟机会保证一个类()方法在多线程环境中被正确加锁合同步。

反射机制原理图.png

9.4 反射获取类结构(部分常用API)

java.lang.Class

方法 描述
public String getName(); 获取全类名
public String getSimpleName(); 获取简单类名
public Field[] getFields(); 获取public修饰的属性,包括本类和父类的
public Field[] getDeclaredFields(); 获取本类中所有属性
public Method[] getMethods(); 获取public修饰的方法,包括本类和父类
public Method[] getDeclaredMethods(); 获取本类所有方法
public Constructor<?>[] getConstructors(); 获取本类所有public修饰的构造器
public Constructor<?>[] getDeclaredConstructors(); 获取本类所有构造器
public ClassLoader getClassLoader(); 获取一个类加载器
public Package getPackage(); 一Package形式返回包信息

java.lang.reflect.Field

方法 描述
public int getModifiers(); 以int的方式返回修饰符,public : 1、default : 0、protected : 4、private : 2、static : 8、final : 16
public Class<?> getType(); 以Class形式返回类型
public String getName(); 返回属性名称

java.lang.reflect.Method

方法 描述
public Class<?> getReturnType(); 以Class形式返回返回值类型
public String getName(); 返回方法名
public Class<?>[] getParameterTypes(); 以Class[]形式返回参数类型数组

java.lang.reflect.Constructor

方法 描述
public String getName(); 返回构造器名称
public Class<?>[] getParameterTypes(); 以Class[]返回构造器参数类型数组

10 并发编程