-
Notifications
You must be signed in to change notification settings - Fork 1
/
search.xml
482 lines (482 loc) · 778 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Java-Basics]]></title>
<url>%2Funcategorized%2FJava-Basics%2F</url>
<content type="text"><![CDATA[自用 Java基础 笔记 Java路线第一阶段入门进阶 JAVA基本语法 OO的编程思想 集合 IO 异常 泛型 反射 多线程 函数式. 第二阶段Web基础和工具 前段基础三件套(html/css/js), jquery,ajax,jsp,cookie,session, http基础 servlet基础, Git,SVN等代码管理工具 第三阶段企业级应用框架培训 maven/gradle项目管理工具 Spring全家桶:Spring/Spring MVC/ SpringBoot(比较先进的培训有SpringBoot,而SSH基本不用报) 关系型数据库 MySQL,jdbc,MyBatis,Hibernate. 非关系数据库 Redis缓存 (也是区别重点) 模板技术: thymeleaf,freemarker 第四阶段高级应用框架培训 搜索引擎 elastic search RPC框架/微服务框架: Dubbo,Spring Cloud(区别重点) 中间件技术 RabbitMQ,RocketMQ,ActiveMQ,Kafka等. 虚拟化技术:Docker容器,k8s容器编排技术等 第五阶段高级话题 JVM优化和排错 GC分析 数据库高级优化等话题 基本概念Java语言的特点 面向对象 健壮性 跨平台性(JVM) Java两种核心机制 Java虚拟机(Java Virtual Machine) 可以理解为一个以字节码为机器指令的CPU,是解释型 垃圾回收机制(Garbage collection) 名词解释 JDK(J2SDK) & JRE SDK(Software Development kit 软件开发包) Java Runtime Environment (Java运行环境) ps: 开发需要JDK,用户只需JRE。 JDK = JRE + 开发工具集(例如Javac编译工具) JRE = JVM + Java SE标准类库 Java技术体系平台 Java SE(Java Standard Edition) 标准版 Java EE(Java Enterprise Edition)企业版 Java ME(Java Micro Edition)小型版 Java Card IDE (Integrated Development Environment) 集成开发环境 基本语法注释类型Comment 单行注释 // 多行注释 / / 文档注释 (Java特有) 1234/** @author 指定Java程序的作者 @version 指定源文件的版本 */ PS:文档注释的内容可以被javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。 12> javadoc -d myXXXdoc -author -version XXX.java> Java API 文档API (Application Programming Interface, 应用程序编程接口) 一个Java源文件只能有一个类声明public,该类名必须与文件名一致 程序入口是main() 输出语句:System.out.println() 输出数据再换行 / System.out.print() 编译会生成(多个)与源文件中类名相同的字节码文件。 关键字与保留字Java保留字: goto 、const 标识符各种变量、方法和类(凡是自己起名字的) 特点:字母,0-9,_ 或$组成,数字不开头 规范:(PS:Java 采用unicode字符集) 包名:多单词组成所有字母都小写:xxxyyyzz 类名、接口名:多单词所有单词首字母大写:XxxYyyZzz 变量名、方法名:第一个首字母小写其余首字母大写:xxxYyyZzz 常量名:所有字母大写,多单词下划线连接:XXX_YYY_ZZZ 变量是程序中最基本的储存单元。包括变量类型、变量名和储存的值。先声明后使用 数据类型: 基本数据类型(primitive type) 整数类型(byte 1, short 2, int 4, long 8) 浮点类型(float 4数值范围大必须末尾加f, double 8双精度) 字符类型(char 2 ‘ ‘) 布尔型(boolean) 引用数据类型(reference type) 类(class) 接口(interface) 数组(string[]) 基本数据类型转换(不包含boolean): 自动类型提升 byte、char、short互相 –> int –> long –> float –> double 强制类型转换 自动类型提升的逆运算,强制转换符() 1234567891011// 特殊情况1long l = 123123;//int//long ll = 123123123123123123;//float f = 12.3;//double false// 特殊情况2//整型常量默认为int,浮点型常量默认为doublebyte b = 12;byte b1 = b + 1;//int false//char c = '';//false 引用数据类型string string可以和8种基本数据类型进行运算,且只能进行连接运算: + 123456//String str1 = 123;//falseString str2 = 123 + "";//int num = str1;//false//int num = (int)str1;//falseint num = Integer.parseInt(str1); 进制 二进制(binary):以0B或0b开头,原码 补码(反码+1) 反码(符号位不变)计算机底层都以补码存储数据 十进制(decimal) 八进制(octal):以数字0开头 十六进制(hex):以0x或0X开头 运算符 算术运算符 (%结果与被模数符号相同)(++不改变本身数据 类型) 赋值运算符 (+=不改变本身数据 类型,带隐形制性转换) 比较运算符 (ps: instanceof是否是string) 逻辑运算符 (逻辑 & | ! ^ 短路 && ||) ps: & 与 && (| 与 ||类似)开发中推荐使用双 相同点:运算结果相同,当左边为true时都执行 不同点:当左边为false时,&继续执行&&短路不执行 位运算符 (<< (最高效2*8) >> (最高位0补0,1补1) >>>无符号右移(都用0补) & | ^(俩数交换) ~取反(补码各位取反))取决于数据类型 三元运算符 (? : ) ps:可以嵌套使用,凡是?:(运算效率高)都可以改写成if-else,反之不成立 [a:b] -> (int)(Math.random() * (b - a + 1) + a) 程序流程控制基本流程结构: 顺序结构 分支结构 if-else (万能) switch (执行效率更高,都可以转换成if-else,反之不成立) 循环结构 while do-while for foreace 引用数据类型变量:数组 初始化 123456789101112131415// 静态初始化int[] ids = new int[]{1,3,5,6};int[] ids = {1,3,5,6}; //类型判断int[][] ids1 = new int[][]{{3.4},{1,2,3},{5,6}};// 动态初始化int[] ids = new int[4];int[] ods = new int[3][4];String[][] arr = new String[1][2];String[] arr[] = new String[1][2];String[][] arr = new String[2][];//错误String[][] arr = new String[][2];// 数组一旦初始化完成,在内存中占有一连串地址,并且长度无法改变。 PS:默认初始化值:整型为0,浮点型0.0,char为0或‘\u000’非这个‘0’,boolean型false,引用类型为null 非“null”,多维数组外层为地址值,内层地址未指定时为null。 获取数组的长度:length属性 1System.out.println(name.length); 数组的遍历:for 123456789for (int i = 0; i < name.length; i++) { System.out.println(name[i]);}for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { System.out.println(arr[i][j]); }} 内存结构解析 栈stack:局部变量 (基本数据类型的变量,一个对象的引用,函数调用的现场保留) 堆heap:new出来的结构:对象、数组(构造器创建的对象) 方法区: 常量池 静态域 (直接的100,“hello“,常量) 1234String str = new String("hello");//str -> 栈 //new出来的对象 -> 堆 //"hello"字面量 -> 静态区s 数组中涉及的常见算法 1.数组元素的赋值(杨辉三角、回形数等)2.求数值型数组中元素的最大值、最小值、平均数、总和等3.数组的复制(需new)、反转、查找(线性查找、二分法查找(必须有序))4.数组元素的排序算法(加粗手写,斜体原理) ●选择排序:直接选择排序、堆排序●交换排序:冒泡排序O(n^2)、快速排序O(nlog2n)●插入排序:直接插入排序、折半插入排序、Shell排序(希尔)●归并排序●桶式排序●基数排序 Array工具类(操作数组的工具类Arrays.XXX)定义在java.util下 boolean equals(int[] a, int[] b) 判断两个数组是否相等 String toString(int[] a) 输出数组信息 void fill(int[] a, int val) 将指定值填充到数组之中 void sort(int[] a) 对数组进行排序 int binarySearch(int[] a, int key) 对排序后的数组进行二分法检索特定的值(注意是排序后的) 数组中常见异常 角标越界异常:ArrayIndexOutOfBoundsExcetion 空指针异常:NullPointerException *补充:eclipse快捷键 1.补全代码的声明:alt + / 2.快速修复: ctrl + 1 3.批量导包:ctrl + shift + o 4.使用单行注释:ctrl + / 5.使用多行注释: ctrl + shift + / 6.取消多行注释:ctrl + shift + \ 7.复制指定行的代码:ctrl + alt + down 或 ctrl + alt + up 8.删除指定行的代码:ctrl + d 9.上下移动代码:alt + up 或 alt + down 10.切换到下一行代码空位:shift + enter 11.切换到上一行代码空位:ctrl + shift + enter 12.如何查看源码:ctrl + 选中指定的结构 或 ctrl + shift + t 13.退回到前一个编辑的页面:alt + left 14.进入到下一个编辑的页面(针对于上面那条来说的):alt + right 15.光标选中指定的类,查看继承树结构:ctrl + t 16.复制代码: ctrl + c 17.撤销: ctrl + z 18.反撤销: ctrl + y 19.剪切:ctrl + x 20.粘贴:ctrl + v 21.保存: ctrl + s 22.全选:ctrl + a 23.格式化代码: ctrl + shift + f 24.选中数行,整体往后移动:tab 25.选中数行,整体往前移动:shift + tab 26.在当前类中,显示类结构,并支持搜索指定的方法、属性等:ctrl + o 27.批量修改指定的变量名、方法名、类名等:alt + shift + r 28.选中的结构的大小写的切换:变成大写: ctrl + shift + x 29.选中的结构的大小写的切换:变成小写:ctrl + shift + y 30.调出生成 getter/setter/构造器等结构: alt + shift + s 31.显示当前选择资源(工程 or 文件)的属性:alt + enter 32.快速查找:参照选中的 Word 快速定位到下一个 :ctrl + k 33.关闭当前窗口:ctrl + w 34.关闭所有的窗口:ctrl + shift + w 35.查看指定的结构使用过的地方:ctrl + alt + g 36.查找与替换:ctrl + f 37.最大化当前的 View:ctrl + m 38.直接定位到当前行的首位:home 39.直接定位到当前行的末位:end 面向对象Java类及类的成员: 属性、方法、构造器;代码块、内部类 类与对象面向对象程序设计的重点是类的设计,设计类就是设计类的成员 类的成员 属性:Field = 域、字段 = 成员变量 行为:Method =(成员)方法 = 函数 类和对象的使用创建类的对象 = 类的实例化 = 实例化类 调用对象的结构(属性、方法) 属性: object.field 方法: object.method() 对象的内存解析 堆(Heap)存放对象实例 栈(Stack)存储局部变量 方法区(Method Area)存储已被虚拟机加载的类信息、变量、静态变量、即时编译器编译后的代码等 理解万事万物皆对象121. 在Java语言范畴中,我们将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构。2. 涉及到Java与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。 PS:引用类型的变量,只可能储存两类值,null 或 地址值(含变量的类型) 匿名对象的使用,只能调用一次 属性属性(成员变量)VS 局部变量 相同点 定义变量格式相同,先声明后使用,且都有对应的作用域 不同点 声明的位置不同 属性:直接定义在类的{}中 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量 权限修饰符的不同 属性:可以在声明属性时,使用权限修饰符指明其权限 局部变量:不可以使用 默认初始化值的情况 属性:类的属性根据类型都有默认初始化值 局部变量:无初始化值 在内存中加载的位置 属性:堆空间(非static) 局部变量:栈空间 方法方法重载(overload)定义:在同一个类中允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。(跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系) “两同一不同”:同一个类,同一个方法名。参数列表不同。 可变个数的形参格式:数据类型 … 变量名, 必须申明在末尾,可传入参数个数为0个及以上 123456public void show(String[] strs){}public void show(String ... strs){ for(int i = 0; i < strs.length; i++) { System.out.println(strs[i]); }} 方法参数的值传递机制变量赋值: 基本数据类型:赋值的是变量所保存的数据值 引用数据类型:赋值变量所保存数据的地址值 类的成员之三:构造器(构造方法constructor)12Person p = new Person();//构建类的对象:new + 构造器 作用:创建对象;给对象进行初始化 格式:权限修饰符 + 类名(形参列表){} ps:构造器可重载,且一旦显示定义了类的构造器之后,系统不再提供默认空参构造器。(必须自己加,一个类中至少有一个构造器) 类的成员之四:代码块(初始化块)就是一对大括号,用来初始化类、对象,若有修饰,只能是static, 静态代码块 内部可以有输出语句,并随着类的加载而执行(不只是加载,最先甚至先于main方法),且只执行一次。 若多个静态代码块,按声明顺序依次执行,总优先于非静态代码块 静态代码块内只能静态的属性、方法,不能调用非静态的结构 作用:初始化类的信息(我一行写不下,应用场景如连接数据池) 非静态代码块 内部可以有输出语句,随着每次对象的创建而执行。 非静态代码块内既能静态的属性、方法,也能调用非静态的结构 调用顺序: 代码块的执行先于构造器,甚至先于main方法(由父及子,静态先行) Root的静态初始化块 Mid的静态初始化块 Leaf的静态初始化块 Root的普通初始化块 Root的无参数的构造器 Mid的普通初始化块 Mid的无参数的构造器 Mid的带参数构造器,其参数值:“” Leaf的普通初始化块 Leaf的构造器 属性赋值的相关问题:可以对属性进行赋值的位置: 默认初始化 显式初始化 构造器中初始化 有对象后,通过“对象.属性”或“对象.方法”的方法进行赋值 在代码块中进行赋值 属性赋值的先后顺序:1 -> 2 / 5 (看谁后写)-> 3 -> 4 类的成员之五:内部类类A声明在类B中,A为内部类 分类:成员内部类 (静态、非静态) VS 局部内部类(方法、代码块、构造器内) 成员内部类: 作为外部类的成员:调用外部类的结构、可以被static修饰、可以被四种权限修饰符修饰 作为一个类:内可以定义属性、方法、构造器等,可以被final修饰,可以被abstract修饰 相关使用细节: 12345678910111213141516//如何实例化成员内部类的对象://创建静态成员内部类对象Person.Dog dog = new Person.Dog();dog.show();//创建非静态成员内部类对象Person p = new Person();Person.Bird bird = p.new Person.Bird();bird.show();//如何在成员内部类中区分调用外部类的结构://方法的形参name//内部类的形参this.name//外部类的形参Person.this.name 局部内部类 的方法中,如果调用局部内部类所声明的方法中的局部变量, 要求此 局部变量 声明为 final 的(JDK8以后可以省略) 123456789101112131415161718192021//开发中局部内部类的使用://eg:返回一个实现了XXX接口的类的对象 public Comparable getComparable(){ //创建一个实现了Comparable接口的类:局部内部类 //方式一:// class MyComparable implements Comparable{// @Override// public int compareTo(Object o) {// return 0;// }// }// return new MyComparable(); //方式二: return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; } 面向对象的三个特征封装性、继承性、多态性、(抽象性) 封装与隐藏体现: 将类的属性XXX私有化private,提供公有化public方法来获取getXXX和设置属性setXXX的值。 不对外暴露的私有的方法 单例模式(将构造器私有化) 如果不希望 类 在包外被使用可以设置成缺省 目标:高内聚,低耦合PS:封装性的体现需要权限修饰符来体现。修饰 类 只能public与缺省 权限修饰符(从小到大) 修饰符 类内部 同一个包 不同包的子类 同一个工程 private √ (缺省) √ √ protect √ √ √ public √ √ √ √ 继承性inheritance好处: 减少了代码的多余,提高代码的复用性 便于功能的扩展 为之后多态性的使用,提供了前提 格式:1class A extends B{} A:子类、派生类、subclass B:父类、超类、superclass 体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。(private的属性也继承到了,只是因为封装性的影响不能直接调用) ps:Java只支持单继承和多继承,不允许多重继承,一个子类只能有一个父类。如果没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类,所有Java类都直接或间接继承了java.lang.Object类。 方法的重写(override/overwrite)定义:在子类中根据需要对父类中的方法进行改造。 应用:重写以后,当创建子类对象,调用同名方法时调用的是重写的方法。 规定:(建议:开发中直接从父类粘贴过来) 子类重写的方法名与形参列表与被重写一样 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符 ps:特殊情况,子类中不能重写父类中声明为private的方法。 返回类型: 父类被重写方法的返回类型为void,子类重写的方法只能返回void; 父类被重写方法的返回类型为基本类型,子类重写的方法只能返回相同的基本类型; 父类被重写方法的返回类型为A类型,子类重写的方法可以返回A类型或A的子类; 异常类型:子类重写的方法的异常类型不大于父类被重写的方法抛出的异常类型 特别注意:子类和父类的同名同参数的方法要么声明非static(考虑重写),要么都声明为static(不是重写,静态方法不能被覆盖) 区分重载和重写:①二者的概念:②重载和重写的具体规则③重敢:不表现为多态性重写:表现为多态性 多态性polymorphism定义:一个事物的多种形态 在Java中的体现:对象的多态性,父类的引用指向子类的对象 123Person p1 = new Man();Person p2 = new Woman();//子类的对象赋给父类的引用 多态的使用:在编译期,只能调用父类中声明的方法,在运行期,实际执行的是子类中的重写的方法。 虚拟方法调用(Virtual Method Invocation)当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 Java引用变量有两种类型:编译时类型 和 运行时类型。 调用方法时,编译看左边,执行看右边。–动态绑定 多态是运行时行为。重载是编译是就已经确定了,“早绑定”,”静态绑定”。 Bruce Eckel:”不要犯傻,如果它不是晚绑定,就不是多态。” 多态性的使用前提: 类的继承关系 方法的重写 ps: 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边) 1001 VS 1002 向下转型(使用强制类型转换符)编译时只能调用父类声明的属性和方法,如何调用子类特有的属性和方法? 使用强制类型转换。 使用强制转换时,可能出现ClassCastException的异常。 使用instanceof进行检测: 123if(a instanceof A) { } //判断对象是否是A的实例,如是返回true。 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。2.对于实例变量,则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。 其他关键字:this、super、static、final、abstract、interface、package、import与相关补充知识 this:理解为 当前对象,通常可以省略。可以调用:属性(用以区分局部变量和属性)、方法和构造器。 调用本类中指定的构造器 this(形参列表) 减少代码重复,且必须放在构造器内第一行,最多只能声明一个来调用其他构造器。 ps: import static导入指定类或接口中的静态结构:属性、方法 super:理解为 父类的,可以调用:属性、方法和构造器。 通常可以省略,当子类和父类中定义同名的属性时,需要显式的super 特殊情况, 当子类重写父类的方法时,需要调用父类中的方法时,需要显式的super super调用父类构造器:在子类构造器中显式的“super(参数列表)”,且必须声明在子类构造器中的首行 在类的构造器中,针对“super(参数列表)”和“this(参数列表)”只能二选一,构造器中没有,默认super(空参) 在类的多个构造器中,至少有一个类的构造器使用了super(参数列表),调用父类的构造器。 虽然创建子类时调用了父类的构造器,只是创建了一个对象。 static:某些特定的数据在内存中只有一份。eg:每个中国人都共享中国这个国籍。 修饰:属性、方法、代码块、内部类(注意构造器不行) 修饰属性:静态变量(类变量) 属性按是否有static修饰又分为:静态属性 VS 非静态属性(实例变量) 实例变量:当创建类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改某一个对象的静态属性时,不会导致其他对象中同样的属性值的修改。 静态属性:创建类的多个对象,每个对象都共享同一个静态属性。当修改某一个对象的静态属性时,会导致其他对象调用此静态变量时,属性值是修改的。 PS:静态变量(类变量)随类的加载而加载,且早于对象的加载。因此可以通过”类.静态变量“进行调用。由于类只会加载一次 ,则静态变量在内存中也只会存在一份。 举例:System.out / Math.PI 修饰方法:静态方法 可以通过”类.静态方法“进行调用 静态方法只能调用静态的方法或属性,非静态则都可以。 在静态方法中,不能使用this,super关键字 static应用场景: 属性是可以被多个对象共享,不会随着对象的不同而改变 操作静态属性的方法,设置成static;工具类中的方法,习惯上声明为static final:可以修饰的结构:类、方法、变量 修饰类:此类不能被其他类继承。eg:String类、System类、StringBuffer类。 修饰方法:此方法不能被重写。eg:Object类中的getClass()。 修饰变量:此“变量”被称为一个常量。 修饰属性,之后(必须初始化)可以考虑的赋值位置有:显式初始化、代码块中初始化、构造器中初始化、不可以在方法中赋值(因为未调用)。 修饰局部变量:常量不可修改,尤其修饰形参时,表明此形参是常量,方法内不能重新赋值。 static final 修饰属性:全局常量 abstract:抽象类与抽象方法修饰的结构:类、方法 修饰类:抽象类,不可实例化,类中一定要构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)开发中,都会提供抽象类的子类。让子类对象实例化,完成相关的。 修饰方法:抽象方法只有方法的声明,没有方法体。包含抽象方法的类一定是一个抽象类,抽象类中可以没有抽象方法。若子类重写了父类的所有的抽象方法后,此子类方可实例化。若未重写,则该子类也是一个抽象类,需要abstract修饰。(子类必须重写后才可实例化) PS:abstract不可以用来修饰属性、构造器等。abstract不能修饰私有方法、静态方法、final的类与方法。 抽象类的匿名子类 12345678910111213141516171819202122Worker worker = new Worker();method1(worker);//非匿名的类非匿名对象method1(new Worker());//非匿名的类匿名的对象Person p = new Person(){ @override public void eat() { } @override public void breath() { }}//匿名子类的对象:pmethod1(new Person(){ @override public void eat() { } @override public void breath() { }});//匿名子类的匿名对象 --- 省事 接口(interface) 与类并列的结构,一定程度解决类的单继承性的缺陷。本质是契约、规范、标准。(JDK7及以前)接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义。 继承是一个“是不是”的关系,而接口实现的是“能不能”的关系。(例如学生和运动员都能学习) 接口中的成员: JDK1.7及以前,只能定义全局变量和抽象方法(默认缺省也是) 全局变量:public static final的 抽象方法:public abstract的 JDK8,除了定义以上之外,还可以定义静态static方法、默认default方法。 接口中不能定义构造器!意味着接口不能实例化 Java开发中,接口通过让类去实现(implement)的方法来使用(面向接口编程),若实现类覆盖了接口的所有的抽象方法后,此实现类方可实例化。若未重写,则该实现类也是一个抽象类。 Java类可以实现多个接口(多实现),弥补类的单继承性的局限性。 12//先写extends再写implementsclass AA extends BB implements CC,DD,EE 接口与接口之间可以继承,而且可以多继承。 接口的具体使用,体现了多态性、 Java8中关于接口的改进 接口中定义的静态方法,只能通过接口来调用(像工具类) 通过实现类的对象,可以调用接口中的默认方法。 类优先原则:若子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法(属性bushi),那么子类在没有重写此方法的情况下,默认调用的是父类的同名同参数的方法。 接口冲突:若实现类实现了多个接口,而多个接口都定义了同名同参数的默认方法,在实现类没有重写的情况下,报错。因此实现类必须重写此方法。 如何在子类(实现类)的方法中调用父类、接口中被重写的(默认)方法: 1234method();//调用自己重写的方法super.method();//调用父类中声明的Interface1.super.method();//调用接口中的默认方法Interface1.method();//直接调用接口中的静态方法 接口的应用: 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问,另一个博文(中介,歌手经纪人)应用场景: 安全代理 远程代理 延迟加载 分类: 静态代理(静态定义代理类) 动态代理(动态生存代理类) 12345678910111213141516171819202122232425262728293031323334353637//接口的应用:代理模式 举例public class NetWorkTest { public static void main(String[] args) { Server server = new Server();// server.browse(); ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse(); }}interface NetWork{ public void browse();}//被代理类class Server implements NetWork{ @Override public void browse() { System.out.println("真实的服务器访问网络"); }}//代理类class ProxyServer implements NetWork{ private NetWork work; public ProxyServer(NetWork work){ this.work = work; } public void check(){ System.out.println("联网之前的检查工作"); } @Override public void browse() { check(); work.browse(); }} 工厂模式 Factory 创建者和调用者分离。 补充JavaBean是一种Java语言写成的可重用组件,符合以下标准的Java类: 类是公共的 有一个无参的公共的构造器 有属性,且有对应的get、set方法 Debug调试设置断点 操作 作用 step into 跳入(f5) 进入当前行所调用的方法中 step over 跳过(f6) 执行完当前行的语句,进入下一行 step return 跳回(f7) 执行完当前行所在的方法,进入下一行 drop to frame 回到当前行所在方法的第一行 resume恢复 执行完当前行所在断点的所有代码,进入 下一个断点,如果没有就结束 Terminate 终止 停止 JVM, 后面的程序不会再执行 JUnit单元测试方法 选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步 创建Java类 (要求:①此类是public ②此类提供公共空参构造器)进行单元测试 在此类中声明单元测试方法 要求权限为 public,没有返回值 且 没有形参 PS:需要声明注释@Test,并导入包import org.junit.Test; 左键双击 单元测试方法名,右键:run as - JUnit Test 123456789import org.junit.Test;public class JunitTest { @Test public void test1() { }} 若执行结果无异常为绿色,异常为红。 Object类:只定义了一个空参构造器 方法: clone() / getClass() / finalize() /hashCode() / wait() / notify() / notifyAll() / equals() / toString() == VS equals() == 是运算符,可以用于 基本数据 与 引用类型变量,前者比较保存的数据是否相同(不一定要类型相同,但必须一致,否则编译不通过),后者比较地址值是否相同,是否引用指向同一个对象 equals() 是一个方法,只能用于 引用数据类型变量 的比较,*object类中定义的equals() 和 == 的作用是相同的*(未重写)。像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后比较的是两个对象的实体内容是否相同。若自己定义的类也要有这样的功能,比较对象的实体内容,应该重写equals()方法。(equals()建议反着写,比如:”反着来”.equals(str) ,这样可以避免str可能是空指针的情况!) 12345678910111213141516//重写的原则,比较两个对象的实体内容是否相同(举例:name和age)@Overridepublic boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof XXX) { XXX xxx = (XXX)obj; return this.age == xxx.age && this.name.equals(xxx.name); } else { return false; }}//实际使用自动生成 和setter、getter一样 toString()方法 输出一个 引用变量 时,实际上输出的是对象的toString() 像String、Date、File、包装类等都重写了Object类中的toString()方法,返回“实体内容”信息 自定义类也可以重写该方法。 main()方法 作为程序的入口 也是一个普通的静态方法(只能调用静态属性方法,不然造类:通过实例化类对象调用普通属性与方法) 可以作为与控制台交互的方式(java xxxDemo “str”)因为有参数String[] args 包装类(Wrapper)的使用 针对八种基本数据类型定义相应的引用类型-包装类(封装类),具备类的特征,就可以调用类中的方法,实现真正的面向对象。 | 基本数据类型 | 包装类 || ———— | ————- || byte | Byte || short | Short || int | Integer || long | Long || float | Float || double | Double || boolean | Boolean || char | Character | Byte Short Integer Long Float Double 父类为Number 基本数据类型、包装类、String三者的相互转换 包装类 -> 基本数据类型:调用包装类的xxxValue() 基本数据类型 -> 包装类:调用包装类的构造器new() 基本数据类型、包装类 -> String类型:调用String重载的valueOf(Xxx xxx) 12345int num1 = 10;//方式一:连接运算String str1 = num1 + "";//方式二:调用String重载的valueOf(Xxx xxx)String str2 = String.valueOf(num1); String类型 -> 基本数据类型、包装类:调用包装类的parseXxx() 12String str3 = "1234";int num3 = Integer.parseInt(str3); 自动装箱与拆箱(JDK5.0以后) 1234int num1 = 10;Integer in1 = num1; //自动装箱int num3 = in1;//自动拆箱 1234567891011121314151617181920212223242526// 关于包装类的比较迷惑的问题Object o1 = true ? new Integer(1) : new Double(2.0);System.out.println(o1); //1.0 提升了!!Object o2;if (true) { o2 = new Integer(1);} else { new Double(2.0);}System.out.println(o1); //1Integer i = new Integer(1);Integer j = new Integer(1);System.out.println(i == j); //falseInteger m = 1;Integer n = 1;System.out.println(m == n); //true// Integer内部定义了IntegerCache结构,其中定义了Integer[]//保存了-128~127,如果使用自动装箱时,直接调用。//128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的,提高效率Integer x = 128;Integer y = 128;System.out.println(x == y); //false 设计模式是在大量实践中总结和理论化后优选的代码结构、编程风格以及解决问题的思考方式。“套路” 单例(Singleton)设计模式只能存在一个对象实例,好处减少了系统性能的开销(注意:学习时由static延申) 实现一:饿汉式 (线程安全) 私有化类的构造器 内部创建类的对象(private static) 提供公共的方法(public static),返回类的对象 1234567private Bank(){ }private static Bank instance = new Bank();public static Bank getInstance() { return instance;} 实现二:懒汉式 (延迟对象的创建) 私有化类的构造器 声明当前类对象(private static),没有初始化null 声明public、static的返回当前类对象的方法 12345678910private Order(){ }private static Order instance = null;public static Order getInstance() { if(instance == null) { instance = new Order(); } return instance;} 饿汉式 VS 懒汉式 区别:懒汉式好处延迟对象的创建,饿汉式坏处,对象加载时间太长,但其是线程安全的 使用场景: 网站的计数器 应用程序的日志应用 数据库连接池 读取配置文件的类 Application也是单例的典型应用 Windows的Task Manager(任务管理器) Windows中的Recycle Bin(回收站) 模板方法设计模式(TemplateMethod)抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展、改造,但子类总体上会保留抽象类的行为方式。 解决的问题: 当功能内部一部分实现是确定的,一部分实现是不确定的。这是可以把不确定部分暴露出来,让子类去实现。 在软件开发中实现一个算法时,整体步骤很确定、通用,这些步骤在父类中写好,但部分易变,可以将该部分抽象出来,供不同子类去实现。 MVC设计模式模型层 model:主要处理数据 数据对象封装: model.bean/domain 数据库操作类: model.dao 数据库: model.db 视图层 view: 显示数据 相关工具类: view.utils 自定义view: view.ui 控制层 controller: 处理业务逻辑 应用界面相关: controller.activity 存放fragment: controller.fragment 显示列表的适配器: controller.adapter 服务相关的: controller.service 抽取的基类: controller.base 异常处理概述与体系结构开发过程中的 语法错误 和 逻辑错误 不是异常。 执行过程中出现的异常分为两类: Error:Java虚拟机无法解决的严重问题。eg:StackOverflowError 栈溢出 和 OutOfMemoryError 堆溢出。一般不编写针对性的代码进行处理。 Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。又分为编译时异常(受检checked异常) VS 运行时异常(非受检unchecked异常,RuntimeException) 异常处理机制抓抛模型: “抛”:程序在正常执行中,一旦出现异常就会在代码处生成一个对应异常类的对象,并抛出。之后的代码不再执行。 关于异常对象的产生: 系统自动生成的异常对象 手动的生成一个异常对象,并抛出(throw) 12throw new RuntimeException("nnn");//自身并不输出nnn,被catch后.getMessage才有 ‘’抓“:可以理解为异常的处理方式,如下两种方式: ① try-catch-finally123456789101112try{ //可能会出现异常的代码} catch(异常类型1 变量名1) { //处理异常的方法1} catch(异常类型2 变量名2) { //处理异常的方法2}...finally{ //一定会执行的代码}// 类似直接上药 123456789101112131415161718@Testpublic void test1() { String str = "123"; str = "abc"; try { int num = Integer.parseInt(str); System.out.println("hello---1"); //未运行 } catch(NumberFormatException e) { System.out.println("数值转换异常啦。"); System.out.println(e.getMessage()); //e.printStackTrace(); } System.out.println(str + "hello");}//output:// 数值转换异常啦。// For input string: "abc"// abchello 常用异常对象处理的方式: ①String getMessage() ②printStackTrace() PS: catch中的异常类型 若无子类父类关系 ,无需考虑声明的先后顺序;若有,子类必须声明在父类的上面,否则报错(类似if和switch break)。 在try中声明的变量,在大括号外不能调用。try-catch-finally可以嵌套。 finally是可选的,其中声明的是一定会执行的代码,即便catch中又出现了异常,try或catch中有return语句等情况。 finally重要应用:像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,需要收到编写代码进行资源的释放,此时代码就需要编写在finally中。 体会: 使用try-catch-finally处理编译时异常,是使得程序在编译时不再报错,但在运行时仍可能报错。“延迟”到运行时 开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally,针对编译时异常,一定要考虑异常的处理。 ② throws + 异常类型(喊人通报,未解决)声明在方法的声明处,指明此方法执行时,可能回抛出的异常类型,一旦方法体执行时出现异常,仍会在异常处生成一个异常类的对象。此对象满足throws后的异常类型时,就会被抛出,之后的代码就不再执行。 如何选择 若父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着若子类重写的方法有异常必须使用try-catch-finally 执行的方法a中,先后调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws的方式处理(层层向上报),而执行的方法a可以考虑使用try-catch-finally 自定义异常类 继承现有的异常结构:RuntimeException、Exception 提供全局常量:serialVersionUID 提供重载的构造器 (记得要搭配throw手动抛出) 123456789101112public class MyException extends RuntimeException{ static final long serialVersionUID = -70348971766939L; public MyException() { super(); } public MyException(String message) { super(message); }} throw VS throws throw表示抛出一个异常类对象,生成异常对象的过程。声明在方法体内。 throws属于异常处理的一种方式,声明在方法的声明处。 程序、进程、线程基本概念 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期PS:程序是静态的,进程是动态的进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。(方法区和堆) 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。 何时需要多线程 程序需要同时执行两个或多个任务。 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。 需要一些后台运行的程序时。 多线程的创建方法一:继承于Thread类12345678910111213141516171819202122232425262728293031/** *多线程的创建,方法一:继承于Thread类 * 1.创建一个继承于Thread类的子类 * 2.重写Thread类的run() -> 将此线程执行的操作声明在run()中 * 3.创建Thread类的子类对象 * 4.通过此对象调用start(): ①启动当前线程;②调用当前线程的run() * * PS: 1.不能直接调用run()启动线程 * 2.要运行多个线程需要造多个对象(不可以让已经start()的线程去执行,会报IllegalThread) * * @author goodwell * @create 2019-09-25-9:39 */class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}public class ThreadTest{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); }} 创建Thread类的匿名子类的方法 12345678910new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}.start(); Thread类中的常用方法: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263/** * 测试Thread中的常用方法 * 1. start() 启动线程;调用当前线程的run() * 2. run() 通常需要重写Thread类中的方法,将创建的线程要执行的操作声明在此方法 * 3. currentThread() 静态方法,返回执行当前代码的线程 * 4. getName() 获取当前线程的名字 * 5. setName() 设置当前线程的名字 * 6. yield() 释放当前线程cpu的执行权 * 7. join() 在线程a中调用了线程b的join(),此时线程a进入阻塞状态,直到b完全执行完,a才结束阻塞状态。 * 8. sleep(long millitime) 让当前线程“睡眠”millitime 单位为ms,此时为阻塞状态 * 9. isAlive() 判断当前线程是否存活 * * @author goodwell * @create 2021-03-09 19:15 */class HelloThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + i); }// if (i % 20 == 0) {// yield();// } } } //直接构造器命名 public HelloThread(String name) { super(name); }}public class ThreadMethodTest { public static void main(String[] args) { HelloThread hiThread =new HelloThread("Thread:1");// hiThread.setName("线程一"); hiThread.start(); // 主线程命名 Thread.currentThread().setName("主线程"); for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } if (i == 0) { try { hiThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } }} 方法二:实现Runnable接口 创建一个实现了Runnable接口的类 实现类 去 实现Runnable中的抽象方法:run() 创建 实现类 的对象 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 通过Thread类的对象调用start() 12345678910111213141516171819202122232425/** * 创建多线程方法二 * * @author goodwell * @create 2019-09-25-16:13 */class MThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}public class ThreadTest1 { public static void main(String[] args) { MThread mThread = new MThread(); Thread t1 = new Thread(mThread); t1.start(); }} 两种创建方式的对比开发中,优先选择实现Runnable接口的方式 原因: 实现的方式没有类的单继承的局限性 实现的方式更适合处理多个线程有共享数据的情况 联系: 1public class Thread implements Runnable 相同点:都需要重写run(),将线程执行的逻辑声明在run()中 方法三:实现Callable接口 创建一个Callable的实现类 实现call方法,将线程需要执行的操作声明在call()中 创建callable实现类的对象 将此对象传递到FutureTask构造器中,创建FutureTask的对象 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() 获取Callable中call方法的返回值 使用Runnable VS Callable如何理解与使用Runnable相比, Callable功能更强大些 相比run()方法,可以有返回值 方法可以抛出异常,被外面操作捕获,得到异常信息 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果 Future接口 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 FutureTask 同时实现了 Runnable, Future 接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值 12345678910111213141516171819202122232425262728293031323334353637// 1. 创建一个Callable的实现类class NumThread implements Callable { //2. 实现call方法,将线程需要执行的操作声明在call()中 @Override public Object call() throws Exception{ int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); sum += i; } } return sum; }}public class CallTest { public static void main(String[] args) { // 3. 创建callable实现类的对象 NumThread numThread = new NumThread(); // 4. 将此对象传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); // 5. 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() new Thread(futureTask).start(); Object sum = null; try { // 6. get()获取Callable中call方法的返回值 sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }} 方法四:使用线程池背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前 创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 步骤: 提供指定线程数量的线程池 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 关闭连接池 好处: 提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止 1234567891011121314151617181920212223242526272829class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ": " + i); } } }}public class ThreadPool { public static void main(String[] args) { // 1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //↑是接口,↓是类 System.out.println(service.getClass());// ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; // 设置线程池的属性// service1.setCorePoolSize(15);// service1.setKeepAliveTime(); // 2. 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 service.execute(new NumberThread()); // 适用于Runnable// service.submit(Callable callable); // 适用于Callable // 3. 关闭连接池 service.shutdown(); }} 线程的生命周期 线程的同步举例问题:卖票过程中,出现了重票、错票 –> 出现了线程的安全问题,通过同步机制来解决 方式一:同步代码块123synchronized(同步监视器){ //需要同步的代码} 说明:操作共享数据的代码,及需要被同步的代码;共享数据,多个线程共同操作的变量;同步监视器,俗称:锁,任何一个类的对象都可以当锁,runnable实现类可以用this,或者用类(Xxxx.class)。(要求:多个线程必须要共同的一把锁) 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充同步监视器;在继承Thread类创建多线程的方式中,慎用this充当同步监视器考虑使用当前类作为锁Xxxx.class 同步的优缺点: 好处:解决了线程的安全问题 局限性:操作同步代码时,只能有一个线程参加,其他线程等等待,效率较低。 方法二:同步方法如操作共享数据的代码完整的声明在一个方法中。 总结: 同步方法仍然涉及到同步监视器,不需要显式的声明。 非静态的同步方法,同步监视器是:this;静态的同步方法,同步监视器是:当前类本身。 线程安全的饿汉式单例模式: 1234567891011121314151617181920class Bank{ private Bank(){}; private static Bank instance = null; private static Bank getInstance(){ //方式一:效率较差// synchronized (Bank.class) {// if (instance == null) {// instance = new Bank();// }// return instance;// } //方式二:效率更高 if (instance == null) { synchronized (Bank.class) { instance = new Bank(); } } return instance; }} 线程的死锁问题死锁的理解: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。 说明: 出现死锁后,不会出现异常、提示,只是所有的线程都处于阻塞状态,无法继续 使用同步的时候,要避免出现死锁。 方式三:lock锁 — JDK5.0新增 实例化ReentrantLock 调用lock 调用解锁方法:unlock 1234567891011121314151617181920212223242526272829303132class Window implements Runnable{ private int ticket = 100; //1.实例化Reentrantlock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { //2.调用lock lock.lock(); if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + i); ticket--; } else { break; } } finally { //3.调用解锁方法:unlock lock.unlock(); } } }} synchronized VS Lock 异同 相同:二者都可以解决线程安全问题 不同:synchronized机制在执行完相应的同步代码后,自动释放同步监视器;Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock()) 优先使用顺序:Lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外) 线程的通信相关方法: wait():一旦执行,当前线程就进入阻塞状态,并释放同步监视器 notify():唤醒被wait的一个线程,若有多个线程被wait,就唤醒优先级最高的 notifyAll():唤醒所有被wait的线程 说明: 此三个方法必须使用在同步代码块中 其调用者必须是 同步代码块中 的同步监视器,否则会出现IllegalMonitorStateException异常 都是定义在java.lang.Object类中 sleep() VS wait() 异同: 同:一旦执行,都可以是当前的线程进入阻塞状态 异: 方法声明的位置不同:Thread类中声明sleep,Object类中声明wait 调用的要求不同:sleep可以在任何场景下调用,wait必须使用在同步代码块中 是否释放同步监视器:若都使用在同步代码块或同步方法中,sleep不会释放锁,wait会释放锁 字符串相关的类String字符串是常量,用双引号引起来,他们的值在创建后就不能改变。 String对象的 字符串内容 是 存储在一个 字符数组final char[] value中。 特点 实现了 Serializable 接口,表示字符串支持序列化的; 实现了 Comparable 接口,表示String可以比较大小 String是一个final类,不可被继承,其代表不可变的字符序列。(不可变性) 体现: 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当对现有的字符串进行拼接时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当调用String的replace()修改字符串,也需要重新指定内存区域赋值。 12public final class String implements java.io.Serializable, Comparable<String>, CharSequence 通过字面量的方式(区别于new方式)给一个字符串赋值,此时的 字符串值 声明在字符串常量池中,字符串常量池 不会重复储存相同的 字符串。 实例化的方式 通过字面量定义 通过new + 构造器 面试题: 12345String s = new String("abc");//此方式创建对象,在内存中创建了几个对象? 俩:一个堆空间中new结构,另一个是char[]对应的常量池中的数据“abc”String s1 = new String("abc");String s2 = new String("abc");System.out.println(s1 == s2); //false// 先造了对象在堆中,对象的value指向常量池 123456789101112131415161718192021222324252627282930public class StringTest { @Test public void testString(){ String s1 = "hello"; String s2 = "goodwell"; String s3 = "hellogoodwell"; String s4 = "hello" + "goodwell"; String s5 = s1 + "goodwell"; String s6 = "hello" + s2; String s7 = s1 + s2;//1. System.out.println(s3 == s4);//true//2. System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s3 == s7);//false System.out.println(s5 == s6);//false System.out.println(s6 == s7);//false String s8 = s5.intern(); //返回值得到的s8使用的常量值已经存在的“hellogoodwell” //3. System.out.println(s3 == s8);//true final String s9 = "hello"; String s10 = s9 + "goodwell";//4. System.out.println(s3 == s10);//true } 结论: 常量与常量的 拼接结果 在常量池。且常量池中 不会存在相同内容的常量。 只要拼接的其中有一个是变量,结果就在堆中(类似new) 如果拼接的结果 调用intern()方法,返回值就在常量池中 final String(也在常量池中)和字面量连接,结果在常量池中 1234567891011121314151617public class StringTest { String str = new String("good"); char[] ch = {'t','e','s','t'}; public void change(String str, char ch[]) { str = "test ok"; ch[0] = 'b'; } public static void main(String[] args) { StringTest ex = new StringTest(); ex.change(ex.str, ex.ch); System.out.println(ex.str);//good //不可变性 System.out.println(ex.ch);//best }} 常用方法 int length() :返回字符串的长度: return value.length char charAt(int index): : 返回某索引处的字符return value[index] boolean isEmpty() :判断是否是空字符串:return value.length == 0 String toLowerCase() :使用默认语言环境,将 String 中的所有字符转换为小写 String toUpperCase() :使用默认语言环境,将 String 中的所有字符转换为大写 String trim(): :返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj): :比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString) :与equals方法类似,忽略大小写 String concat(String str) :将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString): :比较两个字符串的大小 String substring(int beginIndex): :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 String substring(int beginIndex, int endIndex) : :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。 boolean contains(CharSequence s) :当且仅当此字符串包含指定的 char 值序列时,返回 true int indexOf(String str): :返回指定子字符串在此字符串中第一次出现处的索引 int indexOf(String str, int fromIndex): :返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 int lastIndexOf(String str): :返回指定子字符串在此字符串中最右边出现处的索引 int lastIndexOf(String str, int fromIndex): :返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索注:indexOf和lastIndexOf方法如果未找到都是返回-1 boolean endsWith(String suffix): :测试此字符串是否以指定的后缀结束 boolean startsWith(String prefix): :测试此字符串是否以指定的前缀开始 boolean startsWith(String prefix, int toffset): :测试此字符串从指定索引开始的子字符串是否以指定前缀开始 替换 String replace(char oldChar, char newChar): :返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement): :使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 String replaceAll(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 String replaceFirst(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 匹配 boolean matches(String regex): :告知此字符串是否匹配给定的正则表达式。 切片 String[] split(String regex): :根据给定正则表达式的匹配拆分此字符串。 String[] split(String regex, int limit): :根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。 类型转换String 与 char[] 之间的转换: String –> char[] 调用String的toCharArray() 1char[] charArray = str1.toCharArray(); char[] –> String 调用String的构造器 1String str2 = new String(arr); String 与 byte[] 之间的转换: String –> byte[] 调用String的getBytes() 1byte[] bytes = str1.getBytes(); //使用默认的字符集进行编码 byte[] –> String 调用String的构造器 12String str2 = new String(bytes);//使用默认的字符集进行解码//说明:解码时,要求解码使用的字符集必须和编码时使用的字符集一致,否则出现乱码 StringBuffer类 java.lang.StringBuffer代表 可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。很多方法与String相同。作为参数传递时,方法内部可以改变值。 StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器: StringBuffer() :初始为 容量为16 的字符串缓冲区 StringBuffer(int size) :构造 指定容量的字符串缓冲区 StringBuffer(String str) :将内容初始化为指定字符串内容 StringBuffer 类的常用方法 StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接(啥都变成字符串,例如“null”) StringBuffer delete(int start,int end):删除指定位置的内容 StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把当前字符序列逆转 public int indexOf(String str) public String substring(int start,int end) public int length() public char charAt(int n ) public void setCharAt(int n ,char ch) ## StringBuilder类 StringBuilder和StringBuffer 非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样 对比String 、StringBuffer 、StringBuilder String(JDK1.0):不可变字符序列 StringBuffer(JDK1.0):可变字符序列、效率低、线程安全 StringBuilder(JDK 5.0):可变字符序列、效率高、线程不安全,底层都是使用char[] 存储 注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。 12345678String str = null;//System.out.println(str.length());//NullPointerExceptionStringBuffer sb = new StringBuffer();sb.append(str);System.out.println(sb.length());//4System.out.println(sb);//"null"//StringBuffer sb1 = new StringBuffer(str);//System.out.println(sb1);//NullPointerException 源码分析 12345678910111213String str = new String();// char[] value = new char[0];String str1 = new String("abn");// char[] value = new char[]{'a','b','n'};StringBuffer sb1 = new StringBuffer();// char[] value = new char[16];sb1.append('a');// value[0] = 'a';sb1.append('b');// value[1] = 'b';StringBuffer sb2 = new StringBuffer("abc");// char[] value = new char["abc".length + 16]{'a','b','c's};//问题一:System.out.println(sb2.length());// 3//问题二://扩容问题:若添加的数据底层数组装不下,那就需要扩容底层的数组;默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。 指导建议: 开发中使用,StringBuffer(int capacity) 或 StringBuilder(int capacity) 指定容量的 对比三者效率: StringBuilder > StringBuffer > String 总结: 增:append(xxx) 删:delete(int start, int end) 改:setCharAt(int n, char ch) / replace(int start, int end, String str) 查:charAt(int n) 插:insert(int offset, xxx) 长度:length() *遍历:for() + charAt() / toString() 时间相关的类JDk8之前的日期和时间的API①. java.lang.System类System.currentTimeMillis(): 返回当前时间与1970年1月1日0时0分0秒之间 以毫秒为单位的时间差,也称为时间戳 ②. java.util.Date (java.sql.Date继承前者)构造器: Date() 创建一个对应当前时间的Date对象 //Date(int year, int month, int day) 创建一个对应时间的Date对象 Date(long) 创建指定毫秒数的Date对象 方法: tiString() 显示当前年月日 getTime() 获取当前Date对象对应的时间戳 java.sql.Date 对应着数据库中的日期类型的变量 将java.util.Date对象转换成java.sql.Date对象 1java.sql.Date dateSql = new java.sql.Date(dateUtil.getTime()); 1234567891011121314151617181920212223242526272829303132333435363738394041424344public class TimeTest { //1.System类中的currentTimeMillis() @Test public void test11(){ long time = System.currentTimeMillis(); System.out.println(time); //1569513273409返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 } //2.java.util.Date类(java.sql.Date类)// ①两个构造器的使用// > Date() 创建一个当前时间的Date对象// > Date(long) 创建指定毫秒数的Date对象// ②两个方法的使用// > toString() 显示当前的年、月、日、时、分、秒// > getTime() 获取当前Date对象对应的毫秒数(时间戳)// ③java.sql.Date类对应着数据库中的日期类型的变量// > 如何实例化// > util.Date --getTime()-> sql.Date对象// >< sql.Date --> util.Date对象(不需要转,学生本身就是人) @Test public void test22(){ //构造器一:Date() 创建一个当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString());//Fri Sep 27 00:50:46 CST 2019 System.out.println(date1.getTime());//1569516806737 //构造器二:Date(long) 创建指定毫秒数的Date对象 Date date2 = new Date(1569516806737L); System.out.println(date2.toString());//Fri Sep 27 00:53:26 CST 2019 //创建java.sql.Date对象 java.sql.Date date3 = new java.sql.Date(1569516806737L); System.out.println(date3);//2019-09-27有无.toString()一样 //sql.Date --> util.Date对象 Date date8 = date3; System.out.println(date8.toString()); //util.Date --getTime()-> sql.Date对象 //情况1 Date date4 = new java.sql.Date(1569516806737L); java.sql.Date date5 = (java.sql.Date) date4; //情况2 Date date6 = new Date(); java.sql.Date date7 = new java.sql.Date(date6.getTime()); }} ③. SimpleDateFormat类java.text.SimpleDateFormat() 不与语言环境有关的方式来对Date类的格式化和解析的具体类 构造器: SimpleDateFormat() 默认的模式和语言环境创建对象 SimpleDateFormat(String Pattern) 用参数pattern指定的格式创建一个对象。 该对象可以 格式化: 日期 -> 文本 String format(Date date) 解析:文本 -> 日期 Date parse(String source) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class DateTimeTesr { /* SimpleDateFormat的使用:对日期Date类的格式化和解析 1.两个操作 - 格式化: 日期 ---> 字符串 - 解析: 字符串 ---> 日期 2.SimpleDateFormat的实例化 */ @Test public void testSimpleDateFormat(){ //实例化SimpleDateFormat:使用默认构造器 SimpleDateFormat sdf = new SimpleDateFormat(); //格式化: 日期 ---> 字符串 Date date = new Date(); System.out.println(date); String format = sdf.format(date); System.out.println(format); //解析: 字符串 ---> 日期 String str = "19-9-27 下午8:12"; Date date1 = null; try { date1 = sdf.parse(str); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date1); // 按照指定的方式格式化和解析,调用带参数的构造器// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa"); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //格式化 String format1 = sdf1.format(date); System.out.println(format1);//2019-09-27 08:22:08 //解析 Date date2 = null; try { date2 = sdf1.parse("2019-09-27 08:22:08"); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date2); }} 1234567891011121314151617public void test12() throws ParseException { /* 练习一: 字符串“2020-09-08”抓换成java.sql.Date 练习二: “三天打鱼两天晒网” 1990-01-01 xxxx-xx-xx 打鱼?晒网? */ String birth = "2020-09-08"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf1.parse(birth);// System.out.println(date); java.sql.Date birthDate = new java.sql.Date(date.getTime()); System.out.println(birthDate); //思路:总天数%5==? //方式一:( date2.getTime() - date1.getTime() ) / (1000 * 60 * 60 * 24) + 1 //方式二:时间分段整数年加闰年} ④. Calendar类是一个抽象基类,主要用于完成日期之间相互操作的功能。 1.实例化 方式一:创建其子类(GregorianCalendar)的对象 方式二:调用其静态方法getInstance() 2.常用方法 get() set() 可变性 add() getTime() 日历类 –> Date setTime() Date –> 日历类 注意: 获取月份时:一月是0 … 十二月是11 获取星期时:周日是1 … 周六是7 12345678910111213141516171819202122232425262728293031323334353637/*Calendar类(抽象类)的使用 */@Testpublic void testCalendar(){ // 1.实例化 // 方式一:创建其子类(GregorianCalendar)的对象 // 方式二:调用其静态方法getInstance() Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass()); // 2.常用方法 // get() int days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28 System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//271 // set() 可变性 calendar.set(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//22 // add() calendar.add(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//14 // getTime() 日历类 --> Date Date date = calendar.getTime(); System.out.println(date); // setTime() Date --> 日历类 Date date1 = new Date(); calendar.setTime(date1); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28} JDK 8中新日期时间APIjava.time1234567891011121314151617181920212223242526272829303132333435363738394041/*LocalDate LocalTime LocalDateTime 的使用 类似于Calendar() */@Testpublic void testTime(){ //1. now() 获取当前时间日期 LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate);//2019-09-28 System.out.println(localTime);//09:19:28.307 System.out.println(localDateTime);//2019-09-28T09:19:28.307 //2. of() 设置指定的年月日时分秒,没有偏移量 LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 10, 0, 0, 0); System.out.println(localDateTime1);//2020-10-10T00:00 //getXxx() 获取相关属性 System.out.println(localDateTime.getDayOfMonth());//28 System.out.println(localDateTime.getDayOfWeek());//SATURDAY System.out.println(localDateTime.getDayOfYear());//271 System.out.println(localDateTime.getHour());//9 System.out.println(localDateTime.getMonthValue());//9 //withXxx() 修改、设置 不同于Calendar() 体现不可变性 LocalDate localDate1 = localDate.withDayOfMonth(22); System.out.println(localDate);//2019-09-28 System.out.println(localDate1);//2019-09-22 //plusXxx() 增加 LocalDateTime localDateTime2 = localDateTime.plusMonths(4); System.out.println(localDateTime);//2019-09-28T09:29:43.638 System.out.println(localDateTime2);//2020-01-28T09:29:43.638 //minusXxx() 减去 LocalDateTime localDateTime3 = localDateTime.minusDays(3); System.out.println(localDateTime);//2019-09-28T09:32:06.256 System.out.println(localDateTime3);//2019-09-25T09:32:06.256} Instant瞬时 12345678910111213141516171819202122/*Instant 的使用类似于Date */@Test public void testInstant(){ //now() 获取本初子午线对应的标准时间 Instant instant = Instant.now(); System.out.println(instant); //2019-09-28T01:42:23.739Z //添加时间的偏移量 OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); //2019-09-28T09:42:23.739+08:00 //toEpochMilli() 获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 --> Date类的getTime() long milli = instant.toEpochMilli(); System.out.println(milli);//1569638022547 //ofEpochMilli() 通过给定的毫秒数,获取Instant实例 --> Date(long millis) Instant instant1 = Instant.ofEpochMilli(11111111L); System.out.println(instant1);//1970-01-01T03:05:11.111Z} java.time.format.DateTimeFormatter1234567891011121314151617181920212223242526272829303132333435363738394041/*DateTimeFormatter 格式化或解析日期、时间类似于SimpleDateFormat */@Testpublic void test3(){ //方式1:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; //格式化: 日期 --> 字符串 LocalDateTime localDateTime = LocalDateTime.now(); String str = formatter.format(localDateTime); System.out.println(localDateTime);//2019-09-28T14:49:57.682 System.out.println(str);//2019-09-28T14:49:57.682 //解析: 字符串 --> 日期 TemporalAccessor parse = formatter.parse("2019-09-28T14:49:57.682"); System.out.println(parse);//{},ISO resolved to 2019-09-28T14:49:57.682 //方式2:本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) //FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT ↑ DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); //格式化 String str1 = formatter1.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:33:41.802 System.out.println(str1);//2019年9月28日 下午03时33分41秒 //ofLocalizedDate() DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); //格式化 String str2 = formatter2.format(LocalDate.now()); System.out.println(str2);//2019年9月28日 星期六 //方式3:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”) DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); //格式化 String str3 = formatter3.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:38:38.889 System.out.println(str3);//2019-09-28 03:38:38 //解析 TemporalAccessor parse1 = formatter3.parse("2019-09-28 03:38:38"); System.out.println(parse1);//{SecondOfMinute=38, HourOfAmPm=3, MicroOfSecond=0, NanoOfSecond=0, MinuteOfHour=38} Java比较器对象数组的排序问题,涉及对象之间的比较。Java对象正常情况下,只能进行比较: == 或 != ,不能使用 > 或 < 。Java实现对象排序的方式有两种: 自然排序:java.lang.ComparableComparable接口的使用举例 自然排序 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个大小的方式 像String、包装类等重写了compareTo()方法以后,进行了从小到大的排序 重写compareTo(obj)的规则: 如果当前对象this大于形参对象obj,则返回正整数; 如果当前对象this小于形参对象obj,则返回负整数; 如果当前对象this等于形参对象obj,则返回零。 对于自定义类,若需要排序,可让自定义类实现Comparable接口,重写compareTo(obj)方法,指明如何排序 123456789101112131415161718@Testpublic void test1(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr); System.out.println(Arrays.toString(arr));}//Eg:商品价格从低到高:按价格排序class Goods implements Comparable {}@Overridepublic int compareTo(Objact o) { if(o instanceod Goods) { Goods goods = (Goods)o; return Double.compare(this.price,goods.price) } throw new RuntimeException("传入的数据类型不一致");} 定制排序:java.util.Comparator 背景: 当元素的类型没有实现Comparable接口 而又不方便修改代码;或者实现了Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来进行排序。 重写compare(Object o1, Object o2)方法,比较o1和o2的大小: 若方法返回正整数,表示o1大于o2; 返回0,表示相等; 返回负数,表示o1小于o2。 123456789101112131415161718@Testpublic void test2(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr,new Comparator(){ //按照字符串大到小 @Override public int compare(Object o1, Object o2) { if(o1 instanceof String && o2 instanceof String){ String s1 = (String) o1; String s2 = (String) o2; return -s1.compareTo(s2); }// return 0; throw new RuntimeException("输入的数据类型不一致"); } }); System.out.println(Arrays.toString(arr));} Comparable接口与Comparator的使用的对比: 前者一旦指定,保证Comparable接口实现类的对象在任何位置都可以比较大小; 后者属于临时性的比较。Arrays.sort(arr,new Comparator(){…..} 其他类System类 System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。 成员变量System类内部包含 in、out和err 三个成员变量,分别代表 标准输入流(键盘输入),标准输出流(显示器)和 标准错误输出流(显示器)。 成员方法 native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 使用该方法可以在图形界面编程中实现程序的退出功能等。 void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。 String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示: | 属性名 | 属性说明 || ————- | ————————– || java. version | Java运行时环境版本 || java. home | java安装目录操作系统的名称 || os.version | 操作系统的版本 || user.nane | 用户的账户名称 || user.home | 用户的主目录 || user.dir | 用户的当前工作目录 | 12345678910111213141516171819202122232425@Testpublic void test3(){ String javaVersion = System.getProperty("java.version"); System.out.println("java的version:" + javaVersion); String javaHome = System.getProperty("java.home"); System.out.println("java的home:" + javaHome); String osName = System.getProperty("os.name"); System.out.println("os的name:" + osName); String osVersion = System.getProperty("os.version"); System.out.println("os的version:" + osVersion); String userName = System.getProperty("user.name"); System.out.println("user的name:" + userName); String userHome = System.getProperty("user.home"); System.out.println("user的home:" + userHome); String userDir = System.getProperty("user.dir"); System.out.println("user的dir:" + userDir);}out:java的version:1.8.0_191java的home:C:\Program Files\Java\jdk1.8.0_191\jreos的name:Windows 10os的version:10.0user的name:goodwelluser的home:C:\Users\goodwelluser的dir:D:\Codes\IdeaProjects\JavaSenior\Day Math类java.lang.Math 提供了一系列静态方法用于 科学 计算。其 方法的参数和返回值类型一般为double 型。 abs 绝对值 acos,asin,atan,cos,sin,tan 三角函数 sqrt 平方根 pow(double a,doble b) a 的b 次幂 log 自然对数 exp e 为底指数 max(double a,double b) min(double a,double b) random() 返回0.0 到1.0 的随机数 long round(double a) double 型数据a 转换为long 型(四舍五入) toDegrees(double angrad) 弧度—> 角度 toRadians(double angdeg) 角度—>弧度 BigInteger与BigDecimalBigInteger类java.math包的 BigInteger 可以表示不可变的任意精度的整数。提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。 构造器 BigInteger(String val):根据字符串构建BigInteger对象 常用 方法 public BigInteger abs(): 返回此 BigInteger 的绝对值的 BigInteger。 BigInteger add(BigInteger val) : 返回其值为 (this + val) 的 BigInteger BigInteger subtract(BigInteger val) : 返回其值为 (this - val) 的 BigInteger BigInteger multiply(BigInteger val) : 返回其值为 (this * val) 的 BigInteger BigInteger divide(BigInteger val) : 返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。 BigInteger remainder(BigInteger val) : 返回其值为 (this % val) 的 BigInteger。 BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个BigInteger 的数组。 BigInteger pow(int exponent) : 返回其值为 (this exponent ) 的 BigInteger。 ### BigDecimal类 一般的Float类和Double类可以用来做科学计算或工程计算,但在 商业计算中,到 要求数字精度比较高,故用到java.math.BigDecimal类 。BigDecimal类支持不可变的、任意精度的有符号十进制定点数。 构造器 public BigDecimal(double val) public BigDecimal(String val) 常用方法 public BigDecimal add(BigDecimal augend) public BigDecimal subtract(BigDecimal subtrahend) public BigDecimal multiply(BigDecimal multiplicand) public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 123456789public void testBigInteger() { BigInteger bi = new BigInteger("12433241123"); BigDecimal bd = new BigDecimal("12435.351"); BigDecimal bd2 = new BigDecimal("11"); System.out.println(bi); // System.out.println(bd.divide(bd2)); System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));} 枚举类的使用含义: 类的对象只有有限个,确定的。(当需要定义一组常量时,强烈建议使用枚举类。若枚举类只有一个对象,则可作为单例模式的实现方式。) 如何定义: 方式一:jdk5.0之前,自定义枚举类 方式二:jdk5.0时,可使用enum关键字定义枚举类 123456789101112131415161718192021222324252627282930313233343536373839404142public class enumTest { public static void main(String[] args) { Season spring = Season.SPRING; System.out.println(spring);//Season{seasonName='春天', seasonDesc='春暖花开'} }}//自定义枚举类class Season { //1.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //3.提供当前枚举类的多个对象: public static final修饰 public static final Season SPRING = new Season("春天", "春暖花开"); public static final Season SUMMER = new Season("夏天", "夏日炎炎"); public static final Season AUTUMN = new Season("秋天", "秋高气爽"); public static final Season WINTER = new Season("冬天", "冬天雪地"); //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } //4.其他诉求2:提供toString() @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class enumTest1 { public static void main(String[] args) { Season1 summer = Season1.SUMMER; System.out.println(summer);//SUMMER System.out.println(Season1.class.getSuperclass());//class java.lang.Enum }}//使用enum关键字定义枚举类//说明:定义的枚举类默认继承于class java.lang.Enumenum Season1 { //1.提供当前枚举类的多个对象,多个对象之间用","隔开,末尾对象";"结束 SPRING("春天", "春暖花开"), SUMMER("夏天", "夏日炎炎"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "冬天雪地"); //2.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season1(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; }// //4.其他诉求2:提供toString()//// @Override// public String toString() {// return "Season1{" +// "seasonName='" + seasonName + '\'' +// ", seasonDesc='" + seasonDesc + '\'' +// '}';// }} Enum类的主要方法 values() 方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。 valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。 toString():返回当前枚举类对象常量的名称 123456789//values():Season1[] values = Season1.values();for (int i = 0; i < values.length; i++) { System.out.println(values[i]);}//SPRING SUMMER AUTUMN WINTER//valueOf(String objName):返回枚举类中对象名是objName的对象Season1 winter = Season1.valueOf("WINTER");System.out.println(winter);//WINTER 使用enum关键字定义的枚举类实现接口的情况 情况一:实现接口,在enum类中实现抽象方法; 情况二:让枚举类的对象分别实现接口中的抽象方法(每个都不一样) 123456SPRING("春天", "春暖花开"){ @Override public void show(){ System.out.println("春天在哪里"); }} 注解(Annotation)的使用框架 = 注解 + 反射 + 设计模式。 理解Annotation: jdk 5.0 新增的功能 Annotation 其实就是代码里的 特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。 Annotation的使用示例 示例一:生成文档相关的注解 示例二: 在编译时进行格式检查(JDK 内置的三个基本注解) @Override: 限定重写父类方法, 该注解只能用于方法 @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择 @SuppressWarnings: 抑制编译器警告 示例三: 跟踪 代码依赖性,实现替代配置文件功能 如何自定义注解 注解声明为:publi @interface XXX 内部定义成员,通常使用value表示 可以指定成员的默认值,使用default定义(String[] value default “hello”) 若自定义注解没有成员,表明是一个标识作用 PS: 若注解有成员,在使用注解时,需要指明成员的值 自定义注解必须配上注解的信息处理流程(使用反射)才有意义 自定义注解通常都会指明两个元注解:Retention、Target jdk 提供的4种元注解元注解:对现有的注解进行解释说明的 注解 Retention:指定所修饰的 Annotation 的生命周期:SOURCE \ CLASS(默认行为)\ RUNTIME只有声明为RUNTIME生命周期的注解,才能通过反射获得 Target:用于指定被修饰的 Annotation 能用于修饰那些程序元素 Documented:表示所修饰的注解在被javadoc解析时,保留下来 Inherited:被它修饰的 Annotation 将具有继承性 JDK8中 注解的新特性: 可重复注解 、 类型注解 可重复注解 ① 在MyAnnotation 上声明@Repeatable,成员值为 MyAnnotation.class ② MyAnnotationd Taget 和 Reten等元注解与MyAnnotation相同。 类型注解 ELementType. TYPE PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)ELementType.TYPE_USE 表示该注解能写在使用类型的任何语句中 集合概述 集合、数组都是对 多个数据 进行 存储操作 的结构,简称Java容器。 说明:此时的存储,主要指的是内存层面的存储,不涉及持久化的存储(.txt,.jpg,.avi,数据库中) 数组在存储多个数据方面的特点(缺点*): (*)一旦初始化后,长度就确定了 一旦定义好,元素的类型也确定了 (*)提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高 (*)获取数组中实际元素的个数的需求,没有现成的属性或方法可用 (*)存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足 Java集合 可以分为Collection 和 Map两种体系(接口) Collection接口:单列数据,定义了存取一些对象的方法的集合 List:元素有序、可重复的集合,“动态数组” Set:元素无序、不可重复的集合,“数学上的集合” Map接口:双列数据,保存具有映射关系”key-value对”的集合,“数学上的函数映射” 集合框架123456789|----Collection接口:单列集合,用来存储一个一个的对象 |----List接口:存储有序的、可重复的数据 --> “动态”数组 |----ArrayList、LinkedList、Vector |----Set接口:存储无序的、不可重复的数据 --> 数学中的“集合” |----HashSet、LinkedHashSet、TreeSet |----Map接口:双列接口,用来存储一对(key-value)数据 --> y = f(x) |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties Collection常用方法(通用) add(Object e): 将元素e添加到集合coll中 addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 size(): 获取添加的元素的个数 clear(): 清空集合元素 isEmpty(): 判断当前集合是否为空 12345678910111213141516171819202122232425262728@Test public void test1(){ Collection coll = new ArrayList(); //add(Object e): 将元素e添加到集合coll中、 coll.add("AA"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); //size(): 获取添加的元素的个数 System.out.println(coll.size());//4 //addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("cc"); coll.addAll(coll1); System.out.println(coll.size());//6 System.out.println(coll);//[AA, BB, 123, Thu Oct 31 12:03:02 CST 2019, 456, cc] //clear(): 清空集合元素 coll.clear(); //isEmpty(): 判断当前集合是否为空 System.out.println(coll.isEmpty());//true } contains(Object obj): 判断当前集合中是否包含obj PS:向Collection接口的实现类的对象中 添加数据obj时,要求obj所在类要重写equals()。不是判断地址,判断内容 123456789101112131415161718192021@Testpublic void test2(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); Person p = new Person("Jerry1", 20); coll.add(p); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //1.contains(Object obj): 判断当前集合中是否包含obj //我们在判断时会调用obj对象所在类的equals() boolean contains = coll.contains(123); System.out.println(contains);//true System.out.println(coll.contains(new String("Tom")));//true System.out.println(coll.contains(new Person("Jerry", 20)));//false System.out.println(coll.contains(p));//true //PS:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()。} containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。 123//2.containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));//false remove(Object obj): 从当前集合中移除obj元素 1234567891011121314151617@Testpublic void test3(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //3.remove(Object obj): 从当前集合中移除obj元素 coll.remove(123); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false] coll.remove(new Person("Jerry", 20)); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false]} removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素 12345//4.removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);//[com.good.java.Person@4ee285c6, Tom, false] retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 123456789101112131415@Testpublic void test4() { Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //5.retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 Collection coll1 = Arrays.asList(123,456,789); coll.retainAll(coll1); System.out.println(coll); //[123, 456]} equals(Object obj) 123456789 Collection coll1 = new ArrayList(); coll1.add(123); coll1.add(456);// coll1.add(new Person("Jerry", 20)); coll1.add(new String("Tom")); coll1.add(false); //6.equals(Object obj) System.out.println(coll.equals(coll1)); hashCode(): 返回当前对象的哈希值 123456789Collection coll = new ArrayList();coll.add(123);coll.add(456); coll.add(new Person("Jerry", 20));coll.add(new String("Tom"));coll.add(false);//7.hashCode(): 返回当前对象的哈希值System.out.println(coll.hashCode());//701070075 集合 —> 数组:toArray() /拓展:数组 —> 集合: 调用Arrays类的静态方法asList() 1234567891011121314Object[] arr = coll.toArray();for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]);}//拓展:数组 ---> 集合: 调用Arrays类的静态方法asList()List<String> list = Arrays.asList(new String[]{"aa","bb","cc"});System.out.println(list);//[aa, bb, cc]List arr1 = Arrays.asList(new int[]{123,456});System.out.println(arr1.size());//1List arr2 = Arrays.asList(new Integer[]{123,456});System.out.println(arr2.size());//2 iterator() 返回Iterator接口的实例,用于遍历集合元素,注意是一次性的 集合元素的遍历(迭代器接口Iterator) next() 判断是否还有下有一个元素 hasNext() ①指针下移 ②将下移以后集合位置上的元素返回 123456789101112131415161718192021222324252627@Testpublic void test1(){ Collection coll = new ArrayList(); coll.add("aa"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); Iterator iterator = coll.iterator(); //方式一:// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next()); //报异常NoSuchElementException// System.out.println(iterator.next()); //方式二: 不推荐// for (int i = 0; i < coll.size(); i++) {// System.out.println(iterator.next());// } //方式三: 推荐 while (iterator.hasNext()) { System.out.println(iterator.next()); }} remove() 删除集合中某数据(调用前需要先next()) 123456while (iterator.hasNext()) { Object obj = iterator.next(); if ("BB".equals(obj)) { iterator.remove(); }} foreach 循环遍历集合JDK5 新增了foreach 用于遍历数组和集合(内部任然调用迭代器) for(集合元素类型 局部变量:集合对象) 1234for (Object obj : coll) { System.out.println(obj);}// 注意是局部变量,不会改变 List接口替代数组,元素有序,且可重复 具体实现类:ArrayList、LinkedList、Vector 三者异同: 同:三个类都实现了List接口,存储数据的特点相同,元素有序,且可重复 异: ArrayList 作为List接口的主要实现类 ,线程不安全,效率高,底层用Object[]存储 LinkedList 底层用双向链表存储,对于频繁插入、删除操作,此类效率高 Vector 作为List的古老实现类,线程安全,效率低,底层用Object[]存储 Arraylist,的源码分析:JDK7情况下ArrayList list= new ArrayList(/(底层创建了长度是10850bc数 HelementData List. add (123); //eLementData【0】= new Integer(123); List.0(1):/0果此次的添加导致底层 eLementDat数组容量不够,则扩容默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中结论:建议开发中使用带参的构造器:ArrayList List= new Arraylist( unt capacity) JDK8中 ArrayList的变化:ArrayList list= new ArrayList()/)底层0 bject】 elementdata初始化为},并没有创建List.0d(123);//第次调用d()时,底层才创建了长度10的数组,并将数据123添加到 elemen后续的添加和扩容操作与jR7无异 总结:JDK7中的 Arraylist的对象的创建类似于单例的汉式,而8中的ryst的对象的创建类似于单例的像汉式,延迟了数组的创建,节省内存 List中的常用方法 void add (int index, Object ele):在 index位置插入eLle元素 boolean addAll (int index, Collection eles):从 index位置开始特eles中的所有元素添加进来 Object get ( int index):获取指定 index位置的元素 int indexOf (Object obj):返园obj在集合中首次出现的位置 int lastIndexOf (Object obj):返bj在当前集台中末次出现的位置 Object remove ( int index):移除指定inex位置的元素,并返回此元素。(区别于Collection的,eg:List. remove(2); list.remove(new Integer (2);) Object set ( int index, Object ele):设置指定 index位置的元素为ele List subList ( int fromIndex, int toIndex):返从 fromIndex到 toIndex位置的子集合 总结:常用方法 增:add(Object obj) 删:remove(int index) / remove(object obj) 改: set(int index, Object ele) 查:get(int index) 插:add(int index, Object ele) 长度:size() 遍历:①Iterate送代器 ②增强for循环 ③普通的循环 Set接口存储无序的、不可重复的数据 –> 数学中的“集合” |—-HashSet、LinkedHashSet、TreeSet 无序性:不等于随机性,添加的位置不同 不可重复性:保证添加的元素按照equals()判断时,不能返回true PS:Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。 要求: 向Set中添加的数据,其所在类一定要重写hashCode() 和 equals() 重写的方法必须保持一致性,相同的对象必须具有相等的散列码。(技巧:对象中用作equals方法比较的Field,都应该用来计算hashCode值) HashSet: 作为set接口的主要实现类;线程不安全的;可以存储null值 LinkedHashSet: 作为HashSet的子类,遍历内部数据时,可以按照添加的顺序遍历(原因:在添加数据的同时,每个数据还维护了俩应用,记录此数据前一个数据和后一个数据的地址)优点:对于频繁的遍历操作效率更高。 TreeSet: 可以按照添加对象的指定属性,进行排序 1234567891011121314151617181920212223242526272829@Testpublic void test2(){ //HashSet Set set = new HashSet(); set.add(123); set.add("AA"); set.add(false); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }// AA// false// 123 //LinkedHashSet LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(123); linkedHashSet.add("AA"); linkedHashSet.add(false); Iterator iterator1 = linkedHashSet.iterator(); while (iterator1.hasNext()) { System.out.println(iterator1.next()); }// 123// AA// false} TreeSet:向其中添加数据,要求是相同的对象。比较是否添加的对象相同,此处不使用equals,可以分别实现Comparable和Comparator实现自然和定制排序。 自然排序使用的是compareTo返回0,必须重写compareTo 定制排序使用的是compare返回0,必须重写compare 123456789101112@Testpublic void test2(){ TreeSet set = new TreeSet(); set.add(123); set.add(34); set.add(577); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }} Map接口双列接口,用来存储一对(key-value)数据 –> y = f(x) |—-HashMap :作为Map的主要实现类;线程不安全,效率高,能存储null 的key 和value,底层:数组+链表(JDK7)+红黑树(JDK8) |—-LinkedHashMap :保证在遍历map元素时,可以按照添加的顺序实现遍历,对于频繁的遍历操作,此类执行效率高于HashMap|—-TreeMap : 保证按照添加的 key-value对 进行排序,实现排序遍历,按照key自然排序或定制排序,底层使用红黑树。|—-Hashtable : 作为古老的实现类,线程安全,效率低,不能存储null 的key 和value |—-Properties : 常用来处理配置文件,key和value都是String类型、 Map结构的理解: Map中的 key:无序的、不可重复的,使用 Set 存储所有的key —–> key所在的类要重写equals和 hashCode Map中的 value:无序的、可重复的,使用 Collection存储所有的 value —–> value所在的类要重写equals 一个键值对:key- value构成了一个 Entry对象 Map中的 entry:无序的、不可重复的,使用 Set 存所有entry HashMap的底层实现原理 HashMap的底层实现原理?d7为例说明HashMap map new HashMap():在实例化以后,底层创建了长度是16的一维数组 Entry【 table..可能已经执行过多次put map. put(key1, vaLue1)首先,调用key1所在类的 hashcode()计算key1哈希值,此哈希信经过某种算法计算以后,得到在 Entry数组中的存放位置。如果此位置上的数据为空,此时的ey1- value1添加成功情况如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:如果Rey1的哈希值与已经存在的数据的哈希值都不相同,此的key1- value1添加成功。—)况2如果Rey1的哈希值和已经存在的某一个数据(Rey2-vaue2)的哈希值相同,继续比较:调用key1所在类的 equals(key2如果 equals()返aLse:此的key1-vae1添加成功。—情况3如果 equaLs()返回true:使用vLue1营换 value2补充:关于情况2和情况3:此的key1- value1和原来的数据以链表的方式存储 在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来 jdk8相较于jdk7在底层实现方面的不同1. new Hash№p():底层没有创建一个长度为6的数组2.j如k8底层的数组是:Mode【】,非 Entry【3.营次调用put()方法的,底层创建长度为16的数组4.jk7底居结构只有:数组+链表。dR8中底居结构:数组+链表+红黑树当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64的此时此索引位置上的所有数据改为体用红黑树存储。 Map接口的常用方法添加、删除、修改操作 Object put(Object key, Object value):将指定key-value添加到或修改)当前map对象中 void putALL(Map m):将m中的所有key-value对存放到当前map中 Object remove(Object key):移除指定key的key- value对,并返value void clear():清空当前map中的所有数据 元素查询的操作: Object get(Object key):获取指定key对应的value boolean containsKey (Object key):是否包含指定的key boolean containsValue(Object value):是否包含指定的value int size():返回map中 key- value对的个数 boolean isEmpty():判断当前map是否为空 booLean equals(Object obj):判断当前map和参数对象obj是否相等 元视图操作的方法: Set keySet():返回所有key构成的Set集合 Collection values():返园所有 value构成的 Collection集合 Set entrySet():返园所有key-value对构成的Set集台 123456789101112131415161718192021222324252627282930313233343536@Test public void test5(){ Map map = new HashMap(); map.put("AA",123); map.put(45,123); map.put("BB",56); //遍历所有的key集:keySet() Set set = map.keySet(); Iterator iterator = set.iterator(); While(iterator.hasNext()) { System.out.println(iterator,next()); } //遍历所有的value集:values() Collection values = map.values(); for(object obj : values) { System.out.println(obj); } //遍历所有的key-value //方式一 entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); While(iterator1.hasNext()) { Object obj = iterator1.next(); //entrySet:集合中的元素都是entry Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "----" + entry.getValue()); } //方式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); While(iterator2.hasNext()){ object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "=====" + value); }} 总结:常用方法:添加:put(Object key, Object value)删除:remove(Object key)修改:put(Object key, Object value)查询:get(Object key)长度:size()遍历:keySet() / values() / entrySet() 向 TreeMap 中添加key- value,要求 key必须是由同一个类创建的对象,因为要按照key进行排序:自然排序、定制排序 123456789// Properties:常用来处理配置文件。key和value都是 String类型public static void main(String[] args) throws Exception { Properties pros = new Properties(); FileInputstream fis = new FileInputstream(jdbc.properties); pros.load(fis); //加载流对应的文件 String name = pros.getProperty("name"); String password = pros.getProperty("password1"); System.out.println("name =" + name + "password =" password);} Collections工具类 操作Set、List和Map等集合的工具类 ● Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。 ●排序操作:(均为 static方法) reverse(List):反转List中元素的顺序 shuffle(List):对List集合元素进行随机排 sort(List):根据元素的自然顺序对指定List集合元素按升序排序 sort(List, Comparator):根据指定的 Comparator产生的顺序对List集合元素进行排序 swap(List,int,int):将指定List集合中的i 处元素和j 处元素进行交换 查找、替换 Object max( Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max( Collection, Comparator):根据 Comparator指定的顺序,返回给定集合中的最大元素 Object min( Collection)Object min(Collection, Comparator) int frequency(collection, Object):返回指定集合中指定元素的出现次数 void copy( List dest. List src):将src中的内容复制到dest中 boolean replaceAll( List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值 1234567891011121314151617@Testpublic void test2{ List list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(-97); // 报异常:IndexOutOfBounds Exception(" Source does not fit in dest") // List dest = new ArrayList; // Collections.copy(dest, List); // 正确的: List dest = Arrays.asList(new Object(list.size()); System.out.println(dest.size()); // List.sizeof Collections.copy(dest, list); System.out.println(dest);} Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成 线程同步 的集合,从而可以解决多线程并发访问集合时的线程安全。 泛型Generic标签 概念集合容器类在 设计阶段/声明阶段 不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为 Object, JDK1.5之后使用泛型来解决。 因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。 Collection,List, ArrayList这个就是类型参数,即泛型。(不能是基础类型) 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中 某个属性的类型 或者是 某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。 12345678910111213141516171819202122232425262728293031323334//在集合中使用泛型之前的情况@Test public void test1{ ArrayList list = new ArrayList(); //需求:存放学生的成绩 list.add(78); list.add(76); //问题一:类型不安全 List.add("Tom"); for(object score : list){ //问题二:强转时,可能出现 CLassCastException int stuScore = (Integer) score; System.out.println(stuScore); }//在集合中使用泛型的情况public void test2{ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); //编译时,就会进行类型检查,保证数据的安全 List.add("Tom"); for(Integer score : list) { //避免了强转操作 int stuScore = score; System.out.println(stuScore); } //方式二: Iterator<Integer> iterator = list.iterator(); While(iterator.hasNext()) { int stuScore = iterator.next(); System.out.println(stuScore); }} 123456789101112131415//在集合中使用泛型的情况:以 HashMap 为例public void test3(){ Map<String,Integer> map = new HashMap<String, Integer>(); map.put("Jerry", 87); map.put("Jack", 67); // map.put(123,"ABC");报错 //泛型的放套 Set<Map Entry<string, Integer>> entry = map.entryset(); Iterator<Map Entry<String, Integer>> iterator = entry.iterator(); While(iterator.hasNext()){ Map Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----" + value);} 在集合中使用泛型总结 ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。 ② 在实例化 集合类 时,可以指明具体的泛型类型 ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。 比如:add(E e) —> 实例化以后:add(Integer e) ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换 ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。 自定义泛型结构泛型类、泛型接口12345//子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再要指明泛型public class SubOrder1<T> extends order<T> // Suborder1<T>:仍然是泛型类public class subOrder extends order<Integer> // SubOrder:不是泛型类 PS: 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。 比如 <E1, E2, E3> 泛型类的 构造器 如下: public GenericClass(){}而下面是错误的:public Generic Class(){} 实例化后,操作原来泛型位置的结构 必须与指定的泛型类型一致 泛型不同的引用不能相互赋值 尽管在编译时 ArrayList和 ArrayList是两种类型,但是,在运行时只有一个 ArrayList被加载到JVM中 泛型如果不指定,将被搽除,泛型对应的类型均按照Object处理,但不等价于Object。 经验:泛型要使用一路都用。要不用,一路都不要用。 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。 jdk1.7,泛型的简化操作:ArrayList fist = new Array List<>();; 泛型的指定中不能使用基本数据类型,可以使用包装类替换。 在 类/接口 上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。 异常类不能是泛型的 不能使用new E。但是可以:E elements = (E) new Object()参考:ArrayList 源码中声明:Object[] elementData,而非泛型参数类型数组。 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型: 子类 不保留 父类的泛型:按需实现没有类型 擦除 具体类型 子类 保留 父类的泛型:泛型子类 全部保留 / 部分保留 结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型 泛型方法在方法中出现了泛型的结构,泛型参数 与 类的泛型参数没有任何关系。换句话说,泛型方法所属的类是不是泛型类都没有关系 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的,并非在实例化类时确定。 1234567public static <E> List<E> copyFromArrayToList(E[] arr) { ArrayList<E> list = new ArrayList<E>(); for(E e : arr) { list.add(e); } return list} 泛型在继承方面的体现虽然类A是类B的父类,但是G 和 G 二者不具备子父类关系,二者是并列关系。 补充:类A是类B的父类,A 是B 的父类 通配符: ?类A是类B的父类,G 和G 是没有关系的,二者共同的父类是:G<?> 对于G<?> 就不能向其内部添加数据,除了添加null之外 允许读取数据,读取的数据类型为Object。 有限制条件的通配符 ? extends A: ? super A: 123456789101112131415161718192021222324252627282930313233343536373839/*有限制条件的通配符的使用。 ? extends A: 上限<= G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类 ? super A: 下限>= G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类 */ @Test public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4;// list1 = list5;// list2 = list3; list2 = list4; list2 = list5; //读取数据: list1 = list3; Person p = list1.get(0); //编译不通过 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////编译不通过// Person obj = list2.get(0); //写入数据: //编译不通过// list1.add(new Student()); //编译通过 list2.add(new Person()); list2.add(new Student()); }} IOFile类java.io.File类,文件和文件目录路径的抽象表示形式,与平台无关 File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹) File类声明在java.io包下 File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法, 并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”终点”. 创建File类的实例 File(String filePath) File(String parentPath,String childPath) File(File parentFile,String childPath) PS: 相对路径:相较于某个路径下,指明的路径。绝对路径:包含盘符在内的文件或文件目录的路径 .路径分隔符windows:\unix:/ 12345678910111213141516@Testpublic void test1(){ //构造器1 File file1 = new File("hello.txt");//相对于当前module File file2 = new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt"); System.out.println(file1); System.out.println(file2); //构造器2: File file3 = new File("D:\\workspace_idea1","JavaSenior"); System.out.println(file3); //构造器3: File file4 = new File(file3,"hi.txt"); System.out.println(file4);} 常用方法 public String getAbsolutePath():获取绝对路径 public String getPath() :获取路径 public String getName() :获取名称 public String getParent():获取上层文件目录路径。若无,返回null public long length() :获取文件长度(即:字节数)。不能获取目录的长度。 public long lastModified() :获取最后一次的修改时间,毫秒值 如下的两个方法适用于文件目录: public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组 12345678910111213141516171819202122232425262728293031323334@Testpublic void test2(){ File file1 = new File("hello.txt"); File file2 = new File("d:\\io\\hi.txt"); System.out.println(file1.getAbsolutePath()); System.out.println(file1.getPath()); System.out.println(file1.getName()); System.out.println(file1.getParent()); System.out.println(file1.length()); System.out.println(new Date(file1.lastModified())); System.out.println(); System.out.println(file2.getAbsolutePath()); System.out.println(file2.getPath()); System.out.println(file2.getName()); System.out.println(file2.getParent()); System.out.println(file2.length()); System.out.println(file2.lastModified());}@Testpublic void test3(){ File file = new File("D:\\workspace_idea1\\JavaSenior"); String[] list = file.list(); for(String s : list){ System.out.println(s); } System.out.println(); File[] files = file.listFiles(); for(File f : files){ System.out.println(f); }} public boolean renameTo(File dest):把文件重命名为指定的文件路径比如:file1.renameTo(file2)为例:要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。 12345678@Testpublic void test4(){ File file1 = new File("hello.txt"); File file2 = new File("D:\\io\\hi.txt"); boolean renameTo = file2.renameTo(file1); System.out.println(renameTo);} public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏 1234567891011121314151617181920212223@Testpublic void test5(){ File file1 = new File("hello.txt"); file1 = new File("hello1.txt"); System.out.println(file1.isDirectory()); System.out.println(file1.isFile()); System.out.println(file1.exists()); System.out.println(file1.canRead()); System.out.println(file1.canWrite()); System.out.println(file1.isHidden()); System.out.println(); File file2 = new File("d:\\io"); file2 = new File("d:\\io1"); System.out.println(file2.isDirectory()); System.out.println(file2.isFile()); System.out.println(file2.exists()); System.out.println(file2.canRead()); System.out.println(file2.canWrite()); System.out.println(file2.isHidden());} 创建 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建 删除 public boolean delete():删除文件或者文件夹删除注意事项:**Java中的删除不走回收站**。 123456789101112131415161718192021222324252627282930313233@Testpublic void test6() throws IOException { File file1 = new File("hi.txt"); if(!file1.exists()){ //文件的创建 file1.createNewFile(); System.out.println("创建成功"); }else{//文件存在 file1.delete(); System.out.println("删除成功"); }}@Testpublic void test7(){ //文件目录的创建 File file1 = new File("d:\\io\\io1\\io3"); boolean mkdir = file1.mkdir(); if(mkdir){ System.out.println("创建成功1"); } File file2 = new File("d:\\io\\io1\\io4"); boolean mkdir1 = file2.mkdirs(); if(mkdir1){ System.out.println("创建成功2"); } //要想删除成功,io4文件目录下不能有子目录或文件 File file3 = new File("D:\\io\\io1\\io4"); file3 = new File("D:\\io\\io1"); System.out.println(file3.delete()); //false} IO流处理设备之间的数据传输,对于数据的输入输出操作以“流Stream”的方式进行。 流的分类 按操作数据单位不同分为:字节流(8bit),字符流(16bit) 按数据流的流向不同分为:输入流,输出流 按流的角色的不同分为:节点流,处理流 抽象基类 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer PS: Java的O流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀 流的体系结构 抽象基类 节点流(或文件流) 缓冲流(处理流的一种) InputStream FileInputStream (read(byte[] buffer)) BufferedInputStream (read(byte[] buffer)) OutputStream FileOutputStream (write(byte[] buffer,0,len) BufferedOutputStream (write(byte[] buffer,0,len) / flush() Reader FileReader (read(char[] cbuf)) BufferedReader (read(char[] cbuf) / readLine()) Writer FileWriter (write(char[] cbuf,0,len) BufferedWriter (write(char[] cbuf,0,len) / flush() 节点流字符流1.read() 从内存中写出数据到硬盘的文件里。 File类的实例化 FileReader流的实例化 读入的操作 资源的关闭 说明点: read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理 读入的文件一定要存在,否则就会报FileNotFoundException。 123456789101112131415161718192021222324252627282930313233343536373839404142@Testpublic void testFileReader(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt");//相较于当前Module //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的一个字符。如果达到文件末尾,返回-1 //方式一:// int data = fr.read();// while(data != -1){// System.out.print((char)data);// data = fr.read();// } //方式二:语法上针对于方式一的修改 int data; while((data = fr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { //4.流的关闭操作// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// } //或 if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } //对read()操作升级:使用read的重载方法 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void testFileReader1() { FileReader fr = null; try { //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //方式一: //错误的写法// for(int i = 0;i < cbuf.length;i++){// System.out.print(cbuf[i]);// } //heloworld123ld //正确的写法// for(int i = 0;i < len;i++){// System.out.print(cbuf[i]);// } //方式二: //错误的写法,对应着方式一的错误的写法// String str = new String(cbuf);// System.out.print(str); //正确的写法 String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 2.write() 从内存中写出数据到硬盘的文件里。 说明: 输出操作,对应的File可以不存在的。并不会报异常 File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。File对应的硬盘中的文件如果存在:如果流使用的构造器是:**FileWriter(file,false)** / **FileWriter(file)**:**对原有文件的覆盖** 如果流使用的构造器是:**FileWriter(file,true)**:**不会对原有文件覆盖,而是在原有文件基础上追加内容** 123456789101112131415161718192021222324252627@Testpublic void testFileWriter() { FileWriter fw = null; try { //1.提供File类的对象,指明写出到的文件 File file = new File("hello1.txt"); //2.提供FileWriter的对象,用于数据的写出 fw = new FileWriter(file,false); //3.写出的操作 fw.write("I have a dream!\n"); fw.write("you need to have a dream!"); } catch (IOException e) { e.printStackTrace(); } finally { //4.流资源的关闭 if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } }} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556@Testpublic void testFileReaderFileWriter() { FileReader fr = null; FileWriter fw = null; try { //1.创建File类的对象,指明读入和写出的文件 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); //不能使用字符流来处理图片等字节数据// File srcFile = new File("爱情与友情.jpg");// File destFile = new File("爱情与友情1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); //3.数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while((len = fr.read(cbuf)) != -1){ //每次写出len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关闭流资源 //方式一:// try {// if(fw != null)// fw.close();// } catch (IOException e) {// e.printStackTrace();// }finally{// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// }// } //方式二: try { if(fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 字节流结论: 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,…),使用字节流处理 12345678910111213141516171819202122232425262728293031//使用字节流FileInputStream处理文本文件,可能出现乱码。@Testpublic void testFileInputStream() { FileInputStream fis = null; try { //1. 造文件 File file = new File("hello.txt"); //2.造流 fis = new FileInputStream(file); //3.读数据 byte[] buffer = new byte[5]; int len;//记录每次读取的字节的个数 while((len = fis.read(buffer)) != -1){ String str = new String(buffer,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ //4.关闭资源 try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现对图片的复制操作 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687@Testpublic void testFileInputOutputStream() { FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情2.jpg"); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[5]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}//指定路径下文件的复制public void copyFile(String srcPath,String destPath){ FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File(srcPath); File destFile = new File(destPath); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void testCopyFile(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\02-视频.avi"; // String srcPath = "hello.txt"; // String destPath = "hello3.txt"; copyFile(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618} 缓冲流处理流,就是“套接”在已有的流的基础上。处理流之一:缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 作用:提高流的读取、写入的速度(内部提供了一个缓冲区) 实现非文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void BufferedStreamTest() throws FileNotFoundException { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情3.jpg"); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[10]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); // bos.flush();//刷新缓冲区 } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }} 实现文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void testBufferedReaderBufferedWriter(){ BufferedReader br = null; BufferedWriter bw = null; try { //创建文件和相应的流 br = new BufferedReader(new FileReader(new File("dbcp.txt"))); bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"))); //读写操作 //方式一:使用char[]数组 // char[] cbuf = new char[1024]; // int len; // while((len = br.read(cbuf)) != -1){ // bw.write(cbuf,0,len); // // bw.flush(); // } //方式二:使用String String data; while((data = br.readLine()) != null){ //方法一: // bw.write(data + "\n");//data中不包含换行符 //方法二: bw.write(data);//data中不包含换行符 bw.newLine();//提供换行的操作 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现文件复制的方法 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }}@Testpublic void testCopyFileWithBuffered(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\03-视频.avi"; copyFileWithBuffered(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618 - 176} 转换流处理流之二:转换流 转换流:属于字符流 InputStreamReader:将一个字节的输入流转换为字符的输入流 OutputStreamWriter:将一个字符的输出流转换为字节的输出流 作用:提供字节流与字符流之间的转换 解码:字节、字节数组 —>字符数组、字符串 编码:字符数组、字符串 —> 字节、字节数组 字符集ASCII:美国标准信息交换码。 用一个字节的7位可以表示。ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。GB2312:中国的中文编码表。最多两个字节编码所有字符GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546/* 此时处理异常的话,仍然应该使用try-catch-finally InputStreamReader的使用,实现字节的输入流到字符的输入流的转换 */@Testpublic void test1() throws IOException { FileInputStream fis = new FileInputStream("dbcp.txt"); // InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集 //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集 InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ String str = new String(cbuf,0,len); System.out.print(str); } isr.close();}/* 此时处理异常的话,仍然应该使用try-catch-finally 综合使用InputStreamReader和OutputStreamWriter */@Testpublic void test2() throws Exception { //1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.读写过程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.关闭资源 isr.close(); osw.close();} 其他流 标准的输入、输出流 打印流 数据流 标准的输入、输出流 System.in:标准的输入流,默认从键盘输入 System.out:标准的输出流,默认从控制台输出 System类的 setIn(InputStream is) / setOut(PrintStream ps) 方式重新指定输入和输出的流。 练习: 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作, 直至当输入“e”或者“exit”时,退出程序。 方法一:使用Scanner实现,调用next()返回一个字符串 方法二:使用System.in实现。System.in ---> 转换流 ---> BufferedReader的readLine() 12345678910111213141516171819202122232425262728public static void main(String[] args) { BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("请输入字符串:"); String data = br.readLine(); if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 打印流PrintStream 和 PrintWriter 实现将基本数据类型的 数据格式 转化 为字符串输出,提供了一系列重载的print() 和 println()练习: 123456789101112131415161718192021222324@Testpublic void test2() { PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区) ps = new PrintStream(fos, true); if (ps != null) {// 把标准输出流(控制台输出)改成文件 System.setOut(ps); } for (int i = 0; i <= 255; i++) { // 输出ASCII字符 System.out.print((char) i); if (i % 50 == 0) { // 每50个数据一行 System.out.println(); // 换行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } }} 数据流DataInputStream 和 DataOutputStream作用:用于读取或写出基本数据类型的变量或字符串 练习:将内存中的字符串、基本数据类型的变量写出到文件中。 注意:处理异常的话,仍然应该使用try-catch-finally. 1234567891011121314@Testpublic void test3() throws IOException { //1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("刘建辰"); dos.flush();//刷新操作,将内存中的数据写入文件 dos.writeInt(23); dos.flush(); dos.writeBoolean(true); dos.flush(); //3. dos.close();} 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。 注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致! 1234567891011121314@Testpublic void test4() throws IOException { //1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); //2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); //3. dis.close();} 对象流 ObjectInputStream 和 ObjectOutputStream 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。 要想一个java对象是可序列化的,需要满足相应的要求。见Person.java 1234567891011/** * Person需要满足如下的要求,方可序列化 * 1.需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化) * * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量 */public class Person implements Serializable{ public static final long serialVersionUID = 475463534532L;... 序列化机制: 对象序列化机制允许把内存中的Java对象 转换成 平台无关的 二进制流,从而允许把这种 二进制流 持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/* 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去 使用ObjectOutputStream实现 */@Testpublic void testObjectOutputStream(){ ObjectOutputStream oos = null; try { //1. oos = new ObjectOutputStream(new FileOutputStream("object.dat")); //2. oos.writeObject(new String("我爱北京天安门")); oos.flush();//刷新操作 oos.writeObject(new Person("王铭",23)); oos.flush(); oos.writeObject(new Person("张学良",23,1001,new Account(5000))); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(oos != null){ //3. try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } }}/* 反序列化:将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream来实现 */@Testpublic void testObjectInputStream(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dat")); Object obj = ois.readObject(); String str = (String) obj; Person p = (Person) ois.readObject(); Person p1 = (Person) ois.readObject(); System.out.println(str); System.out.println(p); System.out.println(p1); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } }} 随机存取文件流RandomAccessFile 直接继承于java.lang.Object类,实现了DataInput 和 DataOutput接口 既可以作为一个输入流,又可以作为一个输出流 作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。 如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖,不是覆盖文件) 可以通过相关的操作,实现RandomAccessFile “插入”数据的效果 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void test1() { RandomAccessFile raf1 = null; RandomAccessFile raf2 = null; try { //1. raf1 = new RandomAccessFile(new File("爱情与友情.jpg"),"r"); raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"),"rw"); //2. byte[] buffer = new byte[1024]; int len; while((len = raf1.read(buffer)) != -1){ raf2.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //3. if(raf1 != null){ try { raf1.close(); } catch (IOException e) { e.printStackTrace(); } } if(raf2 != null){ try { raf2.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void test2() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 raf1.write("xyz".getBytes());// raf1.close();} 使用RandomAccessFile实现数据的插入效果 12345678910111213141516171819202122@Testpublic void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 //保存指针3后面的所有数据到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //调回指针,写入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //将StringBuilder中的数据写入到文件中 raf1.write(builder.toString().getBytes()); raf1.close(); //思考:将StringBuilder替换为ByteArrayOutputStream} 网络编程概述网络编程中有两个主要的问题: 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用 找到主机后如何可靠高效地进行数据传输 网络编程中的两个要素: 对应问题一:IP和端口号 对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层) 通信要素一: IP和端口号 IP:唯一的标识 Internet 上的计算机(通信实体) 在Java中使用InetAddress类代表IP IP分类:IPv4 和 IPv6 ; 万维网 和 局域网 域名: www.baidu.com www.mi.com www.sina.com www.jd.com 本地回路地址:127.0.0.1 对应着:localhost 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost() 两个常用方法:getHostName() / getHostAddress() 端口号:正在计算机上运行的进程。 要求:不同的进程有不同的端口号 范围:被规定为一个 16 位的整数 0~65535。 端口号与IP地址的组合得出一个网络套接字:Socket 1234567891011121314151617181920212223242526public static void main(String[] args) { try { //File file = new File("hello.txt"); InetAddress inet1 = InetAddress.getByName("192.168.10.14"); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("www.atguigu.com"); System.out.println(inet2); InetAddress inet3 = InetAddress.getByName("127.0.0.1"); System.out.println(inet3); //获取本地ip InetAddress inet4 = InetAddress.getLocalHost(); System.out.println(inet4); //getHostName() System.out.println(inet2.getHostName()); //getHostAddress() System.out.println(inet2.getHostAddress()); } catch (UnknownHostException e) { e.printStackTrace(); }} 实现TCP的网络编程 例题3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。 并关闭相应的连接。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869@Testpublic void client() throws IOException { //1. Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090); //2. OutputStream os = socket.getOutputStream(); //3. FileInputStream fis = new FileInputStream(new File("beauty.jpg")); //4. byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //关闭数据的输出 socket.shutdownOutput(); //5.接收来自于服务器端的数据,并显示到控制台上 InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bufferr = new byte[20]; int len1; while((len1 = is.read(buffer)) != -1){ baos.write(buffer,0,len1); } System.out.println(baos.toString()); //6. fis.close(); os.close(); socket.close(); baos.close();}/* 这里涉及到的异常,应该使用try-catch-finally处理 */@Testpublic void server() throws IOException { //1. ServerSocket ss = new ServerSocket(9090); //2. Socket socket = ss.accept(); //3. InputStream is = socket.getInputStream(); //4. FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg")); //5. byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("图片传输完成"); //6.服务器端给予客户端反馈 OutputStream os = socket.getOutputStream(); os.write("你好,美女,照片我已收到,非常漂亮!".getBytes()); //7. fos.close(); is.close(); socket.close(); ss.close(); os.close();} UDPd协议的网络编程 123456789101112131415161718192021222324252627282930313233//发送端@Testpublic void sender() throws IOException { DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式发送的导弹"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090); socket.send(packet); socket.close();}//接收端@Testpublic void receiver() throws IOException { DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); socket.close();} URL网络编程 URL:统一资源定位符,对应着互联网的某一资源地址 格式: http://localhost:8080/examples/beauty.jpg?username=Tom 协议 主机名 端口号 资源地址 参数列表 12345678910111213141516171819public static void main(String[] args) { try { URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom"); // public String getProtocol( ) 获取该URL的协议名 System.out.println(url.getProtocol()); // public String getHost( ) 获取该URL的主机名 System.out.println(url.getHost()); // public String getPort( ) 获取该URL的端口号 System.out.println(url.getPort()); // public String getPath( ) 获取该URL的文件路径 System.out.println(url.getPath()); // public String getFile( ) 获取该URL的文件名 System.out.println(url.getFile()); // public String getQuery( ) 获取该URL的查询名 System.out.println(url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445public static void main(String[] args) { HttpURLConnection urlConnection = null; InputStream is = null; FileOutputStream fos = null; try { URL url = new URL("http://localhost:8080/examples/beauty.jpg"); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); is = urlConnection.getInputStream(); fos = new FileOutputStream("day10\\beauty3.jpg"); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("下载完成"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(urlConnection != null){ urlConnection.disconnect(); } }} *idea快捷键: 重构一切:Ctrl+Shift+Alt+T 智能补全:Ctrl+Shift+Space 选你所想:Ctrl+W 自我修复:Alt+Enter 自动完成:Ctrl+Shift+Enter 可以新建类、方法等任何东西、get/set、toString方法: alt+insert 自动new完整对象: Ctrl+Alt+V,可以引入变量 自动选中模块代码:Ctrl+W 移动到前/后方法:Alt+Forward/Backward 删除行:Ctrl+Y、复制:Ctrl+D 切换vim模式:Ctrl+; 高亮错误或警告快速定位:F2或shift+F2 打开类或资源:Ctrl+N/Ctrl+Shift+N 弹出框中搜索任何东西,包括类、资源、配置项、方法:Shift+Shift 查看当前类的所有方法:Ctrl+F12 找到类或方法使用的地方:,Alt+F7 格式化import列表:Ctrl+Alt+O,格式化代码:Ctrl+Alt+L 查看项目结构选中类:Alt+1,查看搜索窗口:Alt+3,查看运行调试Alt+4/5 打开最近打开或编辑过的文件列表:Ctrl+E 运行程序:Alt+Shift+F10,启动调试:Shift+F9,停止:Ctrl+F2。 调试:F7/F8/F9分别对应Step into,Step over,Continue 上/下移一行:Alt+Shift+Up/Down 反射概述反射的特征:动态性 疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?建议:直接new的方式。什么时候会使用:反射的方式。疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?不矛盾。 java.lang.Class类的理解 类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件 加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此 运行时类,就作为Class的一个实例。 换句话说,Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。 获取Class的实例的方式 调用运行时类的属性:.class 通过运行时类的对象,调用getClass() 调用Class的静态方法:forName(String classPath) 使用类的加载器:ClassLoader (了解) 1234567891011121314151617181920212223242526//(前三种方式需要掌握)@Testpublic void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class clazz1 = Person.class; System.out.println(clazz1); //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); //方式三:调用Class的静态方法:forName(String classPath) Class clazz3 = Class.forName("com.atguigu.java.Person"); // clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); //方式四:使用类的加载器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); System.out.println(clazz1 == clazz4);} Class实例可以是哪些结构的说明: 1234567891011121314151617181920//万事万物皆对象?对象.xxx,File,URL,反射,前端、数据库操作@Testpublic void test4(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要数组的元素类型与维度一样,就是同一个Class System.out.println(c10 == c11);} 了解类的加载器 12345678910111213141516@Testpublic void test1(){ //对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //调用系统类加载器的getParent():获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //调用扩展类加载器的getParent():无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);} 使用 ClassLoader加载配置文件 123456789101112131415161718192021//Properties:用来读取配置文件。@Testpublic void test2() throws Exception { Properties pros = new Properties(); //此时的文件默认在当前的module下。 //读取配置文件的方式一: // FileInputStream fis = new FileInputStream("jdbc.properties"); // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); // pros.load(fis); //读取配置文件的方式二:使用ClassLoader //配置文件默认识别为:当前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password);} 通过反射 创建运行时类的对象 newInstance(): 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。要想此方法正常的创建运行时类的对象,要求: 运行时类必须提供空参的构造器 空参的构造器的访问权限得够。通常,设置为public。 在javabean中要求提供一个public的空参构造器。原因:1.便于通过反射,创建运行时类的对象2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器 1234567@Testpublic void test1() throws IllegalAccessException, InstantiationException { Class<Person> clazz = Person.class; Person obj = clazz.newInstance(); System.out.println(obj);} 体会反射的动态性 123456789101112131415161718192021222324252627282930313233@Testpublic void test2(){ for(int i = 0;i < 100;i++){ int num = new Random().nextInt(3);//0,1,2 String classPath = ""; switch(num){ case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.lang.Object"; break; case 2: classPath = "com.atguigu.java.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } }}/* 创建一个指定类的对象。 classPath:指定类的全类名 */public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance();} 获取运行时类的完整结构属性结构 getFields(): 获取当前运行时类及其父类中声明为public访问权限的属性 getDeclaredFields(): 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) 12345678910111213141516@Testpublic void test1(){ Class clazz = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); } System.out.println(); //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); }} 权限修饰符 数据类型 变量名 1234567891011121314151617@Testpublic void test2(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName() + "\t"); //3.变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); }} 方法结构 getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法 getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) 123456789101112131415@Testpublic void test1(){ Class clazz = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = clazz.getMethods(); for(Method m : methods){ System.out.println(m); } System.out.println(); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); }} 权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) throws XxxException{} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748@Testpublic void test2(){ Class clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ //1.获取方法声明的注解 Annotation[] annos = m.getAnnotations(); for(Annotation a : annos){ System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers()) + "\t"); //3.返回值类型 System.out.print(m.getReturnType().getName() + "\t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //5.形参列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for(int i = 0;i < parameterTypes.length;i++){ if(i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + " args_" + i + ","); } } System.out.print(")"); //6.抛出的异常 Class[] exceptionTypes = m.getExceptionTypes(); if(exceptionTypes.length > 0){ System.out.print("throws "); for(int i = 0;i < exceptionTypes.length;i++){ if(i == exceptionTypes.length - 1){ System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName() + ","); } } System.out.println(); }} 构造器结构 getConstructors(): 获取当前运行时类中声明为public的构造器 getDeclaredConstructors(): 获取当前运行时类中声明的所有的构造器 1234567891011121314151617@Testpublic void test1(){ Class clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } System.out.println(); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); }} 其他结构获取运行时类的父类 1234567@Testpublic void test2(){ Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass);} 获取运行时类的带泛型的父类 1234567@Testpublic void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass);} 获取运行时类的带泛型的父类的泛型 1234567891011@Testpublic void test4(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; //获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); // System.out.println(actualTypeArguments[0].getTypeName()); System.out.println(((Class)actualTypeArguments[0]).getName());} 获取运行时类实现的接口 123456789101112131415@Testpublic void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); }} 获取运行时类所在的包 1234567@Testpublic void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack);} 获取运行时类声明的注解 123456789@Testpublic void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos); }} 调用运行时类的指定结构属性、方法、构造器 12345678910111213141516171819202122// 不需要掌握@Testpublic void testField() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //获取指定的属性:要求运行时类中属性声明为public //通常不采用此方法 Field id = clazz.getField("id"); /* 设置当前属性的值 set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 */ id.set(p,1001); /* 获取当前属性的值 get():参数1:获取哪个对象的当前属性值 */ int pId = (int) id.get(p); System.out.println(pId);} 操作指定的属性1234567891011121314151617181920/* 如何操作运行时类中的指定的属性 -- 需要掌握 */@Testpublic void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(p,"Tom"); System.out.println(name.get(p));} 指定的方法123456789101112131415161718192021222324252627282930313233/* 如何操作运行时类中的指定的方法 -- 需要掌握 */@Testpublic void testMethod() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); /* 1.获取指定的某个方法 getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); /* 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 invoke()的返回值即为对应类中调用的方法的返回值。 */ Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); System.out.println(returnValue); System.out.println("*************如何调用静态方法*****************"); // private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null // Object returnVal = showDesc.invoke(null); Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal);//null} 指定的构造器123456789101112131415161718192021/* 如何调用运行时类中的指定的构造器 */@Testpublic void testConstructor() throws Exception { Class clazz = Person.class; //private Person(String name) /* 1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表 */ Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); //3.调用此构造器创建运行时类的对象 Person per = (Person) constructor.newInstance("Tom"); System.out.println(per);} 应用:动态代理 静态代理举例 特点:代理类和被代理类在编译期间,就确定下来了。 12345678910111213141516171819202122232425262728293031323334353637383940interface ClothFactory{ void produceCloth();}//代理类class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); }}//被代理类class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); }}public class StaticProxyTest { public static void main(String[] args) { //创建被代理类的对象 ClothFactory nike = new NikeClothFactory(); //创建代理类的对象 ClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); }} 动态代理的举例 要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879interface Human{ String getBelief(); void eat(String food);}//被代理类class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃" + food); }}class HumanUtil{ public void method1(){ System.out.println("====================通用方法一===================="); } public void method2(){ System.out.println("====================通用方法二===================="); }}/*要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 */class ProxyFactory{ //调用此方法,返回一个代理类的对象。解决问题一 public static Object getProxyInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); }}class MyInvocationHandler implements InvocationHandler{ private Object obj;//需要使用被代理类的对象进行赋值 public void bind(Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { HumanUtil util = new HumanUtil(); util.method1(); //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj,args); util.method2(); //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; }}public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); //proxyInstance:代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("四川麻辣烫"); System.out.println("*****************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); }} 新特性Java8其他新特性Lambda表达式123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173/** * Lambda表达式的使用 * * 1.举例: (o1,o2) -> Integer.compare(o1,o2); * 2.格式: * -> :lambda操作符 或 箭头操作符 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表) * ->右边:lambda体 (其实就是重写的抽象方法的方法体) * * 3. Lambda表达式的使用:(分为6种情况介绍) * * 总结: * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字 * * 4.Lambda表达式的本质:作为函数式接口的实例 * * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, * 这样做可以检查它是否是一个函数式接口。 * * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。 * * @author shkstart * @create 2019 上午 11:40 */public class LambdaTest1 { //语法格式一:无参,无返回值 @Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱北京天安门"); } }; r1.run(); System.out.println("***********************"); Runnable r2 = () -> { System.out.println("我爱北京故宫"); }; r2.run(); } //语法格式二:Lambda 需要一个参数,但是没有返回值。 @Test public void test2(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("谎言和誓言的区别是什么?"); System.out.println("*******************"); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” @Test public void test3(){ Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } @Test public void test4(){ ArrayList<String> list = new ArrayList<>();//类型推断 int[] arr = {1,2,3};//类型推断 } //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 @Test public void test5(){ Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 @Test public void test6(){ Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12,21)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12,6)); } //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 @Test public void test7(){ Comparator<Integer> com1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(com1.compare(12,6)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2); System.out.println(com2.compare(12,21)); } @Test public void test8(){ Consumer<String> con1 = s -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*****************************"); Consumer<String> con2 = s -> System.out.println(s); con2.accept("一个是听得人当真了,一个是说的人当真了"); }} 函数式 Functional接囗1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/** * java内置的4大核心函数式接口 * * 消费型接口 Consumer<T> void accept(T t) * 供给型接口 Supplier<T> T get() * 函数型接口 Function<T,R> R apply(T t) * 断定型接口 Predicate<T> boolean test(T t) * * * @author shkstart * @create 2019 下午 2:29 */public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble); } }); System.out.println("********************"); happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money)); } public void happyTime(double money, Consumer<Double> con){ con.accept(money); } @Test public void test2(){ List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京"); List<String> filterStrs = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("京"); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list,s -> s.contains("京")); System.out.println(filterStrs1); } //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定 public List<String> filterString(List<String> list, Predicate<String> pre){ ArrayList<String> filterList = new ArrayList<>(); for(String s : list){ if(pre.test(s)){ filterList.add(s); } } return filterList; }} 方法引用与构造器引用123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135/** * 方法引用的使用 * * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! * * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以 * 方法引用,也是函数式接口的实例。 * * 3. 使用格式: 类(或对象) :: 方法名 * * 4. 具体分为如下的三种情况: * 情况1 对象 :: 非静态方法 * 情况2 类 :: 静态方法 * * 情况3 类 :: 非静态方法 * * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的 * 形参列表和返回值类型相同!(针对于情况1和情况2) * * Created by shkstart. */public class MethodRefTest { // 情况一:对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) @Test public void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); } // 情况三:类 :: 实例方法 (有难度) // Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); @Test public void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778/** * 一、构造器引用 * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。 * 抽象方法的返回值类型即为构造器所属的类的类型 * * 二、数组引用 * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。 * * Created by shkstart */public class ConstructorRefTest { //构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } //数组引用 //Function中的R apply(T t) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }} 强大的 Stream API 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677/** * 1. Stream关注的是对数据的运算,与CPU打交道 * 集合关注的是数据的存储,与内存打交道 * * 2. * ①Stream 自己不会存储元素。 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 * * 3.Stream 执行流程 * ① Stream的实例化 * ② 一系列的中间操作(过滤、映射、...) * ③ 终止操作 * * 4.说明: * 4.1 一个中间操作链,对数据源的数据进行处理 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用 * * * 测试Stream的实例化 * * @author shkstart * @create 2019 下午 4:25 */public class StreamAPITest { //创建 Stream方式一:通过集合 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// default Stream<E> stream() : 返回一个顺序流 Stream<Employee> stream = employees.stream();// default Stream<E> parallelStream() : 返回一个并行流 Stream<Employee> parallelStream = employees.parallelStream(); } //创建 Stream方式二:通过数组 @Test public void test2(){ int[] arr = new int[]{1,2,3,4,5,6}; //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流 IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001,"Tom"); Employee e2 = new Employee(1002,"Jerry"); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } //创建 Stream方式三:通过Stream的of() @Test public void test3(){ Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); } //创建 Stream方式四:创建无限流 @Test public void test4(){// 迭代// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) //遍历前10个偶数 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);// 生成// public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119/** * 测试Stream的中间操作 * * @author shkstart * @create 2019 下午 4:42 */public class StreamAPITest1 { //1-筛选与切片 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees();// filter(Predicate p)——接收 Lambda , 从流中排除某些元素。 Stream<Employee> stream = list.stream(); //练习:查询员工表中薪资大于7000的员工信息 stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println();// limit(n)——截断流,使其元素不超过给定数量。 list.stream().limit(3).forEach(System.out::println); System.out.println();// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); System.out.println();// distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",41,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000));// System.out.println(list); list.stream().distinct().forEach(System.out::println); } //映射 @Test public void test2(){// map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);// 练习1:获取员工姓名长度大于3的员工的姓名。 List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3).forEach(System.out::println); System.out.println(); //练习2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println();// flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构成的集合转换为对应的Stream的实例 public static Stream<Character> fromStringToStream(String str){//aa ArrayList<Character> list = new ArrayList<>(); for(Character c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test3(){ ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6);// list1.add(list2); list1.addAll(list2); System.out.println(list1); } //3-排序 @Test public void test4(){// sorted()——自然排序 List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7); list.stream().sorted().forEach(System.out::println); //抛异常,原因:Employee没有实现Comparable接口// List<Employee> employees = EmployeeData.getEmployees();// employees.stream().sorted().forEach(System.out::println);// sorted(Comparator com)——定制排序 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if(ageValue != 0){ return ageValue; }else{ return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495/** * 测试Stream的终止操作 * * @author shkstart * @create 2019 下午 6:37 */public class StreamAPITest2 { //1-匹配与查找 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// allMatch(Predicate p)——检查是否匹配所有元素。// 练习:是否所有的员工的年龄都大于18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch);// anyMatch(Predicate p)——检查是否至少匹配一个元素。// 练习:是否存在员工的工资大于 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch);// noneMatch(Predicate p)——检查是否没有匹配的元素。// 练习:是否存在员工姓“雷” boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch);// findFirst——返回第一个元素 Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee);// findAny——返回当前流中的任意元素 Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2(){ List<Employee> employees = EmployeeData.getEmployees(); // count——返回流中元素的总个数 long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count);// max(Comparator c)——返回流中最大值// 练习:返回最高的工资: Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary);// min(Comparator c)——返回流中最小值// 练习:返回最低工资的员工 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println();// forEach(Consumer c)——内部迭代 employees.stream().forEach(System.out::println); //使用集合的遍历操作 employees.forEach(System.out::println); } //2-归约 @Test public void test3(){// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T// 练习1:计算1-10的自然数的和 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum);// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>// 练习2:计算公司所有员工工资的总和 List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);// Optional<Double> sumMoney = salaryStream.reduce(Double::sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } //3-收集 @Test public void test4(){// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法// 练习1:查找工资大于6000的员工,结果返回为一个List或Set List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); }} Optional类123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100/** * Optional类:为了在程序中避免出现空指针异常而创建的。 * * 常用的方法:ofNullable(T t) * orElse(T t) * * @author shkstart * @create 2019 下午 7:24 */public class OptionalTest {/*Optional.of(T t) : 创建一个 Optional 实例,t必须非空;Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t):t可以为null */ @Test public void test1(){ Girl girl = new Girl();// girl = null; //of(T t):保证t是非空的 Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2(){ Girl girl = new Girl();// girl = null; //ofNullable(T t):t可以为null Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); //orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t. //如果内部的t是空的,则返回orElse()方法中的参数t1. Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖")); System.out.println(girl1); } public String getGirlName(Boy boy){ return boy.getGirl().getName(); } @Test public void test3(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName(boy); System.out.println(girlName); } //优化以后的getGirlName(): public String getGirlName1(Boy boy){ if(boy != null){ Girl girl = boy.getGirl(); if(girl != null){ return girl.getName(); } } return null; } @Test public void test4(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName1(boy); System.out.println(girlName); } //使用Optional类的getGirlName(): public String getGirlName2(Boy boy){ Optional<Boy> boyOptional = Optional.ofNullable(boy); //此时的boy1一定非空 Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); //girl1一定非空 Girl girl1 = girlOptional.orElse(new Girl("古力娜扎")); return girl1.getName(); } @Test public void test5(){ Boy boy = null; boy = new Boy(); boy = new Boy(new Girl("苍老师")); String girlName = getGirlName2(boy); System.out.println(girlName); }}]]></content>
</entry>
<entry>
<title><![CDATA[Java-summary2]]></title>
<url>%2FJava%2F%E5%B0%8F%E7%BB%93%2FJava-summary2%2F</url>
<content type="text"><![CDATA[自用Java小结(Ⅱ)回顾一下之前学的 泛型Generic标签 概念集合容器类在 设计阶段/声明阶段 不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为 Object, JDK1.5之后使用泛型来解决。 因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。 Collection,List, ArrayList这个就是类型参数,即泛型。(不能是基础类型) 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中 某个属性的类型 或者是 某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。 12345678910111213141516171819202122232425262728293031323334//在集合中使用泛型之前的情况@Test public void test1{ ArrayList list = new ArrayList(); //需求:存放学生的成绩 list.add(78); list.add(76); //问题一:类型不安全 List.add("Tom"); for(object score : list){ //问题二:强转时,可能出现 CLassCastException int stuScore = (Integer) score; System.out.println(stuScore); }//在集合中使用泛型的情况public void test2{ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); //编译时,就会进行类型检查,保证数据的安全 List.add("Tom"); for(Integer score : list) { //避免了强转操作 int stuScore = score; System.out.println(stuScore); } //方式二: Iterator<Integer> iterator = list.iterator(); While(iterator.hasNext()) { int stuScore = iterator.next(); System.out.println(stuScore); }} 123456789101112131415//在集合中使用泛型的情况:以 HashMap 为例public void test3(){ Map<String,Integer> map = new HashMap<String, Integer>(); map.put("Jerry", 87); map.put("Jack", 67); // map.put(123,"ABC");报错 //泛型的放套 Set<Map Entry<string, Integer>> entry = map.entryset(); Iterator<Map Entry<String, Integer>> iterator = entry.iterator(); While(iterator.hasNext()){ Map Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----" + value);} 在集合中使用泛型总结 ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。 ② 在实例化 集合类 时,可以指明具体的泛型类型 ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。 比如:add(E e) —> 实例化以后:add(Integer e) ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换 ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。 自定义泛型结构泛型类、泛型接口12345//子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再要指明泛型public class SubOrder1<T> extends order<T> // Suborder1<T>:仍然是泛型类public class subOrder extends order<Integer> // SubOrder:不是泛型类 PS: 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。 比如 <E1, E2, E3> 泛型类的 构造器 如下: public GenericClass(){}而下面是错误的:public Generic Class(){} 实例化后,操作原来泛型位置的结构 必须与指定的泛型类型一致 泛型不同的引用不能相互赋值 尽管在编译时 ArrayList和 ArrayList是两种类型,但是,在运行时只有一个 ArrayList被加载到JVM中 泛型如果不指定,将被搽除,泛型对应的类型均按照Object处理,但不等价于Object。 经验:泛型要使用一路都用。要不用,一路都不要用。 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。 jdk1.7,泛型的简化操作:ArrayList fist = new Array List<>();; 泛型的指定中不能使用基本数据类型,可以使用包装类替换。 在 类/接口 上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。 异常类不能是泛型的 不能使用new E。但是可以:E elements = (E) new Object()参考:ArrayList 源码中声明:Object[] elementData,而非泛型参数类型数组。 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型: 子类 不保留 父类的泛型:按需实现没有类型 擦除 具体类型 子类 保留 父类的泛型:泛型子类 全部保留 / 部分保留 结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型 泛型方法在方法中出现了泛型的结构,泛型参数 与 类的泛型参数没有任何关系。换句话说,泛型方法所属的类是不是泛型类都没有关系 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的,并非在实例化类时确定。 1234567public static <E> List<E> copyFromArrayToList(E[] arr) { ArrayList<E> list = new ArrayList<E>(); for(E e : arr) { list.add(e); } return list} 泛型在继承方面的体现虽然类A是类B的父类,但是G 和 G 二者不具备子父类关系,二者是并列关系。 补充:类A是类B的父类,A 是B 的父类 通配符: ?类A是类B的父类,G 和G 是没有关系的,二者共同的父类是:G<?> 对于G<?> 就不能向其内部添加数据,除了添加null之外 允许读取数据,读取的数据类型为Object。 有限制条件的通配符 ? extends A: ? super A: 123456789101112131415161718192021222324252627282930313233343536373839/*有限制条件的通配符的使用。 ? extends A: 上限<= G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类 ? super A: 下限>= G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类 */ @Test public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4;// list1 = list5;// list2 = list3; list2 = list4; list2 = list5; //读取数据: list1 = list3; Person p = list1.get(0); //编译不通过 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////编译不通过// Person obj = list2.get(0); //写入数据: //编译不通过// list1.add(new Student()); //编译通过 list2.add(new Person()); list2.add(new Student()); }} IOFile类java.io.File类,文件和文件目录路径的抽象表示形式,与平台无关 File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹) File类声明在java.io包下 File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法, 并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”终点”. 创建File类的实例 File(String filePath) File(String parentPath,String childPath) File(File parentFile,String childPath) PS: 相对路径:相较于某个路径下,指明的路径。绝对路径:包含盘符在内的文件或文件目录的路径 .路径分隔符windows:\unix:/ 12345678910111213141516@Testpublic void test1(){ //构造器1 File file1 = new File("hello.txt");//相对于当前module File file2 = new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt"); System.out.println(file1); System.out.println(file2); //构造器2: File file3 = new File("D:\\workspace_idea1","JavaSenior"); System.out.println(file3); //构造器3: File file4 = new File(file3,"hi.txt"); System.out.println(file4);} 常用方法 public String getAbsolutePath():获取绝对路径 public String getPath() :获取路径 public String getName() :获取名称 public String getParent():获取上层文件目录路径。若无,返回null public long length() :获取文件长度(即:字节数)。不能获取目录的长度。 public long lastModified() :获取最后一次的修改时间,毫秒值 如下的两个方法适用于文件目录: public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组 12345678910111213141516171819202122232425262728293031323334@Testpublic void test2(){ File file1 = new File("hello.txt"); File file2 = new File("d:\\io\\hi.txt"); System.out.println(file1.getAbsolutePath()); System.out.println(file1.getPath()); System.out.println(file1.getName()); System.out.println(file1.getParent()); System.out.println(file1.length()); System.out.println(new Date(file1.lastModified())); System.out.println(); System.out.println(file2.getAbsolutePath()); System.out.println(file2.getPath()); System.out.println(file2.getName()); System.out.println(file2.getParent()); System.out.println(file2.length()); System.out.println(file2.lastModified());}@Testpublic void test3(){ File file = new File("D:\\workspace_idea1\\JavaSenior"); String[] list = file.list(); for(String s : list){ System.out.println(s); } System.out.println(); File[] files = file.listFiles(); for(File f : files){ System.out.println(f); }} public boolean renameTo(File dest):把文件重命名为指定的文件路径比如:file1.renameTo(file2)为例:要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。 12345678@Testpublic void test4(){ File file1 = new File("hello.txt"); File file2 = new File("D:\\io\\hi.txt"); boolean renameTo = file2.renameTo(file1); System.out.println(renameTo);} public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏 1234567891011121314151617181920212223@Testpublic void test5(){ File file1 = new File("hello.txt"); file1 = new File("hello1.txt"); System.out.println(file1.isDirectory()); System.out.println(file1.isFile()); System.out.println(file1.exists()); System.out.println(file1.canRead()); System.out.println(file1.canWrite()); System.out.println(file1.isHidden()); System.out.println(); File file2 = new File("d:\\io"); file2 = new File("d:\\io1"); System.out.println(file2.isDirectory()); System.out.println(file2.isFile()); System.out.println(file2.exists()); System.out.println(file2.canRead()); System.out.println(file2.canWrite()); System.out.println(file2.isHidden());} 创建 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建 删除 public boolean delete():删除文件或者文件夹删除注意事项:**Java中的删除不走回收站**。 123456789101112131415161718192021222324252627282930313233@Testpublic void test6() throws IOException { File file1 = new File("hi.txt"); if(!file1.exists()){ //文件的创建 file1.createNewFile(); System.out.println("创建成功"); }else{//文件存在 file1.delete(); System.out.println("删除成功"); }}@Testpublic void test7(){ //文件目录的创建 File file1 = new File("d:\\io\\io1\\io3"); boolean mkdir = file1.mkdir(); if(mkdir){ System.out.println("创建成功1"); } File file2 = new File("d:\\io\\io1\\io4"); boolean mkdir1 = file2.mkdirs(); if(mkdir1){ System.out.println("创建成功2"); } //要想删除成功,io4文件目录下不能有子目录或文件 File file3 = new File("D:\\io\\io1\\io4"); file3 = new File("D:\\io\\io1"); System.out.println(file3.delete()); //false} IO流处理设备之间的数据传输,对于数据的输入输出操作以“流Stream”的方式进行。 流的分类 按操作数据单位不同分为:字节流(8bit),字符流(16bit) 按数据流的流向不同分为:输入流,输出流 按流的角色的不同分为:节点流,处理流 抽象基类 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer PS: Java的O流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀 流的体系结构 抽象基类 节点流(或文件流) 缓冲流(处理流的一种) InputStream FileInputStream (read(byte[] buffer)) BufferedInputStream (read(byte[] buffer)) OutputStream FileOutputStream (write(byte[] buffer,0,len) BufferedOutputStream (write(byte[] buffer,0,len) / flush() Reader FileReader (read(char[] cbuf)) BufferedReader (read(char[] cbuf) / readLine()) Writer FileWriter (write(char[] cbuf,0,len) BufferedWriter (write(char[] cbuf,0,len) / flush() 节点流字符流1.read() 从内存中写出数据到硬盘的文件里。 File类的实例化 FileReader流的实例化 读入的操作 资源的关闭 说明点: read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理 读入的文件一定要存在,否则就会报FileNotFoundException。 123456789101112131415161718192021222324252627282930313233343536373839404142@Testpublic void testFileReader(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt");//相较于当前Module //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的一个字符。如果达到文件末尾,返回-1 //方式一:// int data = fr.read();// while(data != -1){// System.out.print((char)data);// data = fr.read();// } //方式二:语法上针对于方式一的修改 int data; while((data = fr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { //4.流的关闭操作// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// } //或 if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } //对read()操作升级:使用read的重载方法 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void testFileReader1() { FileReader fr = null; try { //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //方式一: //错误的写法// for(int i = 0;i < cbuf.length;i++){// System.out.print(cbuf[i]);// } //heloworld123ld //正确的写法// for(int i = 0;i < len;i++){// System.out.print(cbuf[i]);// } //方式二: //错误的写法,对应着方式一的错误的写法// String str = new String(cbuf);// System.out.print(str); //正确的写法 String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 2.write() 从内存中写出数据到硬盘的文件里。 说明: 输出操作,对应的File可以不存在的。并不会报异常 File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。File对应的硬盘中的文件如果存在:如果流使用的构造器是:**FileWriter(file,false)** / **FileWriter(file)**:**对原有文件的覆盖** 如果流使用的构造器是:**FileWriter(file,true)**:**不会对原有文件覆盖,而是在原有文件基础上追加内容** 123456789101112131415161718192021222324252627@Testpublic void testFileWriter() { FileWriter fw = null; try { //1.提供File类的对象,指明写出到的文件 File file = new File("hello1.txt"); //2.提供FileWriter的对象,用于数据的写出 fw = new FileWriter(file,false); //3.写出的操作 fw.write("I have a dream!\n"); fw.write("you need to have a dream!"); } catch (IOException e) { e.printStackTrace(); } finally { //4.流资源的关闭 if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } }} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556@Testpublic void testFileReaderFileWriter() { FileReader fr = null; FileWriter fw = null; try { //1.创建File类的对象,指明读入和写出的文件 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); //不能使用字符流来处理图片等字节数据// File srcFile = new File("爱情与友情.jpg");// File destFile = new File("爱情与友情1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); //3.数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while((len = fr.read(cbuf)) != -1){ //每次写出len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关闭流资源 //方式一:// try {// if(fw != null)// fw.close();// } catch (IOException e) {// e.printStackTrace();// }finally{// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// }// } //方式二: try { if(fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 字节流结论: 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,…),使用字节流处理 12345678910111213141516171819202122232425262728293031//使用字节流FileInputStream处理文本文件,可能出现乱码。@Testpublic void testFileInputStream() { FileInputStream fis = null; try { //1. 造文件 File file = new File("hello.txt"); //2.造流 fis = new FileInputStream(file); //3.读数据 byte[] buffer = new byte[5]; int len;//记录每次读取的字节的个数 while((len = fis.read(buffer)) != -1){ String str = new String(buffer,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ //4.关闭资源 try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现对图片的复制操作 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687@Testpublic void testFileInputOutputStream() { FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情2.jpg"); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[5]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}//指定路径下文件的复制public void copyFile(String srcPath,String destPath){ FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File(srcPath); File destFile = new File(destPath); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void testCopyFile(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\02-视频.avi"; // String srcPath = "hello.txt"; // String destPath = "hello3.txt"; copyFile(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618} 缓冲流处理流,就是“套接”在已有的流的基础上。处理流之一:缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 作用:提高流的读取、写入的速度(内部提供了一个缓冲区) 实现非文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void BufferedStreamTest() throws FileNotFoundException { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情3.jpg"); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[10]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); // bos.flush();//刷新缓冲区 } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }} 实现文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void testBufferedReaderBufferedWriter(){ BufferedReader br = null; BufferedWriter bw = null; try { //创建文件和相应的流 br = new BufferedReader(new FileReader(new File("dbcp.txt"))); bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"))); //读写操作 //方式一:使用char[]数组 // char[] cbuf = new char[1024]; // int len; // while((len = br.read(cbuf)) != -1){ // bw.write(cbuf,0,len); // // bw.flush(); // } //方式二:使用String String data; while((data = br.readLine()) != null){ //方法一: // bw.write(data + "\n");//data中不包含换行符 //方法二: bw.write(data);//data中不包含换行符 bw.newLine();//提供换行的操作 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现文件复制的方法 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }}@Testpublic void testCopyFileWithBuffered(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\03-视频.avi"; copyFileWithBuffered(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618 - 176} 转换流处理流之二:转换流 转换流:属于字符流 InputStreamReader:将一个字节的输入流转换为字符的输入流 OutputStreamWriter:将一个字符的输出流转换为字节的输出流 作用:提供字节流与字符流之间的转换 解码:字节、字节数组 —>字符数组、字符串 编码:字符数组、字符串 —> 字节、字节数组 字符集ASCII:美国标准信息交换码。 用一个字节的7位可以表示。ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。GB2312:中国的中文编码表。最多两个字节编码所有字符GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546/* 此时处理异常的话,仍然应该使用try-catch-finally InputStreamReader的使用,实现字节的输入流到字符的输入流的转换 */@Testpublic void test1() throws IOException { FileInputStream fis = new FileInputStream("dbcp.txt"); // InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集 //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集 InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ String str = new String(cbuf,0,len); System.out.print(str); } isr.close();}/* 此时处理异常的话,仍然应该使用try-catch-finally 综合使用InputStreamReader和OutputStreamWriter */@Testpublic void test2() throws Exception { //1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.读写过程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.关闭资源 isr.close(); osw.close();} 其他流 标准的输入、输出流 打印流 数据流 标准的输入、输出流 System.in:标准的输入流,默认从键盘输入 System.out:标准的输出流,默认从控制台输出 System类的 setIn(InputStream is) / setOut(PrintStream ps) 方式重新指定输入和输出的流。 练习: 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作, 直至当输入“e”或者“exit”时,退出程序。 方法一:使用Scanner实现,调用next()返回一个字符串 方法二:使用System.in实现。System.in ---> 转换流 ---> BufferedReader的readLine() 12345678910111213141516171819202122232425262728public static void main(String[] args) { BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("请输入字符串:"); String data = br.readLine(); if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 打印流PrintStream 和 PrintWriter 实现将基本数据类型的 数据格式 转化 为字符串输出,提供了一系列重载的print() 和 println()练习: 123456789101112131415161718192021222324@Testpublic void test2() { PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区) ps = new PrintStream(fos, true); if (ps != null) {// 把标准输出流(控制台输出)改成文件 System.setOut(ps); } for (int i = 0; i <= 255; i++) { // 输出ASCII字符 System.out.print((char) i); if (i % 50 == 0) { // 每50个数据一行 System.out.println(); // 换行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } }} 数据流DataInputStream 和 DataOutputStream作用:用于读取或写出基本数据类型的变量或字符串 练习:将内存中的字符串、基本数据类型的变量写出到文件中。 注意:处理异常的话,仍然应该使用try-catch-finally. 1234567891011121314@Testpublic void test3() throws IOException { //1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("刘建辰"); dos.flush();//刷新操作,将内存中的数据写入文件 dos.writeInt(23); dos.flush(); dos.writeBoolean(true); dos.flush(); //3. dos.close();} 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。 注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致! 1234567891011121314@Testpublic void test4() throws IOException { //1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); //2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); //3. dis.close();} 对象流 ObjectInputStream 和 ObjectOutputStream 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。 要想一个java对象是可序列化的,需要满足相应的要求。见Person.java 1234567891011/** * Person需要满足如下的要求,方可序列化 * 1.需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化) * * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量 */public class Person implements Serializable{ public static final long serialVersionUID = 475463534532L;... 序列化机制: 对象序列化机制允许把内存中的Java对象 转换成 平台无关的 二进制流,从而允许把这种 二进制流 持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/* 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去 使用ObjectOutputStream实现 */@Testpublic void testObjectOutputStream(){ ObjectOutputStream oos = null; try { //1. oos = new ObjectOutputStream(new FileOutputStream("object.dat")); //2. oos.writeObject(new String("我爱北京天安门")); oos.flush();//刷新操作 oos.writeObject(new Person("王铭",23)); oos.flush(); oos.writeObject(new Person("张学良",23,1001,new Account(5000))); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(oos != null){ //3. try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } }}/* 反序列化:将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream来实现 */@Testpublic void testObjectInputStream(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dat")); Object obj = ois.readObject(); String str = (String) obj; Person p = (Person) ois.readObject(); Person p1 = (Person) ois.readObject(); System.out.println(str); System.out.println(p); System.out.println(p1); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } }} 随机存取文件流RandomAccessFile 直接继承于java.lang.Object类,实现了DataInput 和 DataOutput接口 既可以作为一个输入流,又可以作为一个输出流 作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。 如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖,不是覆盖文件) 可以通过相关的操作,实现RandomAccessFile “插入”数据的效果 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void test1() { RandomAccessFile raf1 = null; RandomAccessFile raf2 = null; try { //1. raf1 = new RandomAccessFile(new File("爱情与友情.jpg"),"r"); raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"),"rw"); //2. byte[] buffer = new byte[1024]; int len; while((len = raf1.read(buffer)) != -1){ raf2.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //3. if(raf1 != null){ try { raf1.close(); } catch (IOException e) { e.printStackTrace(); } } if(raf2 != null){ try { raf2.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void test2() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 raf1.write("xyz".getBytes());// raf1.close();} 使用RandomAccessFile实现数据的插入效果 12345678910111213141516171819202122@Testpublic void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 //保存指针3后面的所有数据到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //调回指针,写入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //将StringBuilder中的数据写入到文件中 raf1.write(builder.toString().getBytes()); raf1.close(); //思考:将StringBuilder替换为ByteArrayOutputStream} 网络编程概述网络编程中有两个主要的问题: 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用 找到主机后如何可靠高效地进行数据传输 网络编程中的两个要素: 对应问题一:IP和端口号 对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层) 通信要素一: IP和端口号 IP:唯一的标识 Internet 上的计算机(通信实体) 在Java中使用InetAddress类代表IP IP分类:IPv4 和 IPv6 ; 万维网 和 局域网 域名: www.baidu.com www.mi.com www.sina.com www.jd.com 本地回路地址:127.0.0.1 对应着:localhost 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost() 两个常用方法:getHostName() / getHostAddress() 端口号:正在计算机上运行的进程。 要求:不同的进程有不同的端口号 范围:被规定为一个 16 位的整数 0~65535。 端口号与IP地址的组合得出一个网络套接字:Socket 1234567891011121314151617181920212223242526public static void main(String[] args) { try { //File file = new File("hello.txt"); InetAddress inet1 = InetAddress.getByName("192.168.10.14"); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("www.atguigu.com"); System.out.println(inet2); InetAddress inet3 = InetAddress.getByName("127.0.0.1"); System.out.println(inet3); //获取本地ip InetAddress inet4 = InetAddress.getLocalHost(); System.out.println(inet4); //getHostName() System.out.println(inet2.getHostName()); //getHostAddress() System.out.println(inet2.getHostAddress()); } catch (UnknownHostException e) { e.printStackTrace(); }} 实现TCP的网络编程 例题3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。 并关闭相应的连接。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869@Testpublic void client() throws IOException { //1. Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090); //2. OutputStream os = socket.getOutputStream(); //3. FileInputStream fis = new FileInputStream(new File("beauty.jpg")); //4. byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //关闭数据的输出 socket.shutdownOutput(); //5.接收来自于服务器端的数据,并显示到控制台上 InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bufferr = new byte[20]; int len1; while((len1 = is.read(buffer)) != -1){ baos.write(buffer,0,len1); } System.out.println(baos.toString()); //6. fis.close(); os.close(); socket.close(); baos.close();}/* 这里涉及到的异常,应该使用try-catch-finally处理 */@Testpublic void server() throws IOException { //1. ServerSocket ss = new ServerSocket(9090); //2. Socket socket = ss.accept(); //3. InputStream is = socket.getInputStream(); //4. FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg")); //5. byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("图片传输完成"); //6.服务器端给予客户端反馈 OutputStream os = socket.getOutputStream(); os.write("你好,美女,照片我已收到,非常漂亮!".getBytes()); //7. fos.close(); is.close(); socket.close(); ss.close(); os.close();} UDPd协议的网络编程 123456789101112131415161718192021222324252627282930313233//发送端@Testpublic void sender() throws IOException { DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式发送的导弹"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090); socket.send(packet); socket.close();}//接收端@Testpublic void receiver() throws IOException { DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); socket.close();} URL网络编程 URL:统一资源定位符,对应着互联网的某一资源地址 格式: http://localhost:8080/examples/beauty.jpg?username=Tom 协议 主机名 端口号 资源地址 参数列表 12345678910111213141516171819public static void main(String[] args) { try { URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom"); // public String getProtocol( ) 获取该URL的协议名 System.out.println(url.getProtocol()); // public String getHost( ) 获取该URL的主机名 System.out.println(url.getHost()); // public String getPort( ) 获取该URL的端口号 System.out.println(url.getPort()); // public String getPath( ) 获取该URL的文件路径 System.out.println(url.getPath()); // public String getFile( ) 获取该URL的文件名 System.out.println(url.getFile()); // public String getQuery( ) 获取该URL的查询名 System.out.println(url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445public static void main(String[] args) { HttpURLConnection urlConnection = null; InputStream is = null; FileOutputStream fos = null; try { URL url = new URL("http://localhost:8080/examples/beauty.jpg"); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); is = urlConnection.getInputStream(); fos = new FileOutputStream("day10\\beauty3.jpg"); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("下载完成"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(urlConnection != null){ urlConnection.disconnect(); } }} *idea快捷键: 重构一切:Ctrl+Shift+Alt+T 智能补全:Ctrl+Shift+Space 选你所想:Ctrl+W 自我修复:Alt+Enter 自动完成:Ctrl+Shift+Enter 可以新建类、方法等任何东西、get/set、toString方法: alt+insert 自动new完整对象: Ctrl+Alt+V,可以引入变量 自动选中模块代码:Ctrl+W 移动到前/后方法:Alt+Forward/Backward 删除行:Ctrl+Y、复制:Ctrl+D 切换vim模式:Ctrl+; 高亮错误或警告快速定位:F2或shift+F2 打开类或资源:Ctrl+N/Ctrl+Shift+N 弹出框中搜索任何东西,包括类、资源、配置项、方法:Shift+Shift 查看当前类的所有方法:Ctrl+F12 找到类或方法使用的地方:,Alt+F7 格式化import列表:Ctrl+Alt+O,格式化代码:Ctrl+Alt+L 查看项目结构选中类:Alt+1,查看搜索窗口:Alt+3,查看运行调试Alt+4/5 打开最近打开或编辑过的文件列表:Ctrl+E 运行程序:Alt+Shift+F10,启动调试:Shift+F9,停止:Ctrl+F2。 调试:F7/F8/F9分别对应Step into,Step over,Continue 上/下移一行:Alt+Shift+Up/Down 反射概述反射的特征:动态性 疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?建议:直接new的方式。什么时候会使用:反射的方式。疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?不矛盾。 java.lang.Class类的理解 类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件 加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此 运行时类,就作为Class的一个实例。 换句话说,Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。 获取Class的实例的方式 调用运行时类的属性:.class 通过运行时类的对象,调用getClass() 调用Class的静态方法:forName(String classPath) 使用类的加载器:ClassLoader (了解) 1234567891011121314151617181920212223242526//(前三种方式需要掌握)@Testpublic void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class clazz1 = Person.class; System.out.println(clazz1); //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); //方式三:调用Class的静态方法:forName(String classPath) Class clazz3 = Class.forName("com.atguigu.java.Person"); // clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); //方式四:使用类的加载器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); System.out.println(clazz1 == clazz4);} Class实例可以是哪些结构的说明: 1234567891011121314151617181920//万事万物皆对象?对象.xxx,File,URL,反射,前端、数据库操作@Testpublic void test4(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要数组的元素类型与维度一样,就是同一个Class System.out.println(c10 == c11);} 了解类的加载器 12345678910111213141516@Testpublic void test1(){ //对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //调用系统类加载器的getParent():获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //调用扩展类加载器的getParent():无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);} 使用 ClassLoader加载配置文件 123456789101112131415161718192021//Properties:用来读取配置文件。@Testpublic void test2() throws Exception { Properties pros = new Properties(); //此时的文件默认在当前的module下。 //读取配置文件的方式一: // FileInputStream fis = new FileInputStream("jdbc.properties"); // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); // pros.load(fis); //读取配置文件的方式二:使用ClassLoader //配置文件默认识别为:当前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password);} 通过反射 创建运行时类的对象 newInstance(): 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。要想此方法正常的创建运行时类的对象,要求: 运行时类必须提供空参的构造器 空参的构造器的访问权限得够。通常,设置为public。 在javabean中要求提供一个public的空参构造器。原因:1.便于通过反射,创建运行时类的对象2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器 1234567@Testpublic void test1() throws IllegalAccessException, InstantiationException { Class<Person> clazz = Person.class; Person obj = clazz.newInstance(); System.out.println(obj);} 体会反射的动态性 123456789101112131415161718192021222324252627282930313233@Testpublic void test2(){ for(int i = 0;i < 100;i++){ int num = new Random().nextInt(3);//0,1,2 String classPath = ""; switch(num){ case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.lang.Object"; break; case 2: classPath = "com.atguigu.java.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } }}/* 创建一个指定类的对象。 classPath:指定类的全类名 */public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance();} 获取运行时类的完整结构属性结构 getFields(): 获取当前运行时类及其父类中声明为public访问权限的属性 getDeclaredFields(): 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) 12345678910111213141516@Testpublic void test1(){ Class clazz = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); } System.out.println(); //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); }} 权限修饰符 数据类型 变量名 1234567891011121314151617@Testpublic void test2(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName() + "\t"); //3.变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); }} 方法结构 getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法 getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) 123456789101112131415@Testpublic void test1(){ Class clazz = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = clazz.getMethods(); for(Method m : methods){ System.out.println(m); } System.out.println(); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); }} 权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) throws XxxException{} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748@Testpublic void test2(){ Class clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ //1.获取方法声明的注解 Annotation[] annos = m.getAnnotations(); for(Annotation a : annos){ System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers()) + "\t"); //3.返回值类型 System.out.print(m.getReturnType().getName() + "\t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //5.形参列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for(int i = 0;i < parameterTypes.length;i++){ if(i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + " args_" + i + ","); } } System.out.print(")"); //6.抛出的异常 Class[] exceptionTypes = m.getExceptionTypes(); if(exceptionTypes.length > 0){ System.out.print("throws "); for(int i = 0;i < exceptionTypes.length;i++){ if(i == exceptionTypes.length - 1){ System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName() + ","); } } System.out.println(); }} 构造器结构 getConstructors(): 获取当前运行时类中声明为public的构造器 getDeclaredConstructors(): 获取当前运行时类中声明的所有的构造器 1234567891011121314151617@Testpublic void test1(){ Class clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } System.out.println(); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); }} 其他结构获取运行时类的父类 1234567@Testpublic void test2(){ Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass);} 获取运行时类的带泛型的父类 1234567@Testpublic void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass);} 获取运行时类的带泛型的父类的泛型 1234567891011@Testpublic void test4(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; //获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); // System.out.println(actualTypeArguments[0].getTypeName()); System.out.println(((Class)actualTypeArguments[0]).getName());} 获取运行时类实现的接口 123456789101112131415@Testpublic void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); }} 获取运行时类所在的包 1234567@Testpublic void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack);} 获取运行时类声明的注解 123456789@Testpublic void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos); }} 调用运行时类的指定结构属性、方法、构造器 12345678910111213141516171819202122// 不需要掌握@Testpublic void testField() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //获取指定的属性:要求运行时类中属性声明为public //通常不采用此方法 Field id = clazz.getField("id"); /* 设置当前属性的值 set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 */ id.set(p,1001); /* 获取当前属性的值 get():参数1:获取哪个对象的当前属性值 */ int pId = (int) id.get(p); System.out.println(pId);} 操作指定的属性1234567891011121314151617181920/* 如何操作运行时类中的指定的属性 -- 需要掌握 */@Testpublic void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(p,"Tom"); System.out.println(name.get(p));} 指定的方法123456789101112131415161718192021222324252627282930313233/* 如何操作运行时类中的指定的方法 -- 需要掌握 */@Testpublic void testMethod() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); /* 1.获取指定的某个方法 getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); /* 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 invoke()的返回值即为对应类中调用的方法的返回值。 */ Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); System.out.println(returnValue); System.out.println("*************如何调用静态方法*****************"); // private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null // Object returnVal = showDesc.invoke(null); Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal);//null} 指定的构造器123456789101112131415161718192021/* 如何调用运行时类中的指定的构造器 */@Testpublic void testConstructor() throws Exception { Class clazz = Person.class; //private Person(String name) /* 1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表 */ Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); //3.调用此构造器创建运行时类的对象 Person per = (Person) constructor.newInstance("Tom"); System.out.println(per);} 应用:动态代理 静态代理举例 特点:代理类和被代理类在编译期间,就确定下来了。 12345678910111213141516171819202122232425262728293031323334353637383940interface ClothFactory{ void produceCloth();}//代理类class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); }}//被代理类class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); }}public class StaticProxyTest { public static void main(String[] args) { //创建被代理类的对象 ClothFactory nike = new NikeClothFactory(); //创建代理类的对象 ClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); }} 动态代理的举例 要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879interface Human{ String getBelief(); void eat(String food);}//被代理类class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃" + food); }}class HumanUtil{ public void method1(){ System.out.println("====================通用方法一===================="); } public void method2(){ System.out.println("====================通用方法二===================="); }}/*要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 */class ProxyFactory{ //调用此方法,返回一个代理类的对象。解决问题一 public static Object getProxyInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); }}class MyInvocationHandler implements InvocationHandler{ private Object obj;//需要使用被代理类的对象进行赋值 public void bind(Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { HumanUtil util = new HumanUtil(); util.method1(); //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj,args); util.method2(); //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; }}public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); //proxyInstance:代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("四川麻辣烫"); System.out.println("*****************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); }} 新特性Java8其他新特性Lambda表达式123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173/** * Lambda表达式的使用 * * 1.举例: (o1,o2) -> Integer.compare(o1,o2); * 2.格式: * -> :lambda操作符 或 箭头操作符 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表) * ->右边:lambda体 (其实就是重写的抽象方法的方法体) * * 3. Lambda表达式的使用:(分为6种情况介绍) * * 总结: * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字 * * 4.Lambda表达式的本质:作为函数式接口的实例 * * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, * 这样做可以检查它是否是一个函数式接口。 * * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。 * * @author shkstart * @create 2019 上午 11:40 */public class LambdaTest1 { //语法格式一:无参,无返回值 @Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱北京天安门"); } }; r1.run(); System.out.println("***********************"); Runnable r2 = () -> { System.out.println("我爱北京故宫"); }; r2.run(); } //语法格式二:Lambda 需要一个参数,但是没有返回值。 @Test public void test2(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("谎言和誓言的区别是什么?"); System.out.println("*******************"); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” @Test public void test3(){ Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } @Test public void test4(){ ArrayList<String> list = new ArrayList<>();//类型推断 int[] arr = {1,2,3};//类型推断 } //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 @Test public void test5(){ Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 @Test public void test6(){ Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12,21)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12,6)); } //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 @Test public void test7(){ Comparator<Integer> com1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(com1.compare(12,6)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2); System.out.println(com2.compare(12,21)); } @Test public void test8(){ Consumer<String> con1 = s -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*****************************"); Consumer<String> con2 = s -> System.out.println(s); con2.accept("一个是听得人当真了,一个是说的人当真了"); }} 函数式 Functional接囗1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/** * java内置的4大核心函数式接口 * * 消费型接口 Consumer<T> void accept(T t) * 供给型接口 Supplier<T> T get() * 函数型接口 Function<T,R> R apply(T t) * 断定型接口 Predicate<T> boolean test(T t) * * * @author shkstart * @create 2019 下午 2:29 */public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble); } }); System.out.println("********************"); happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money)); } public void happyTime(double money, Consumer<Double> con){ con.accept(money); } @Test public void test2(){ List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京"); List<String> filterStrs = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("京"); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list,s -> s.contains("京")); System.out.println(filterStrs1); } //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定 public List<String> filterString(List<String> list, Predicate<String> pre){ ArrayList<String> filterList = new ArrayList<>(); for(String s : list){ if(pre.test(s)){ filterList.add(s); } } return filterList; }} 方法引用与构造器引用123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135/** * 方法引用的使用 * * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! * * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以 * 方法引用,也是函数式接口的实例。 * * 3. 使用格式: 类(或对象) :: 方法名 * * 4. 具体分为如下的三种情况: * 情况1 对象 :: 非静态方法 * 情况2 类 :: 静态方法 * * 情况3 类 :: 非静态方法 * * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的 * 形参列表和返回值类型相同!(针对于情况1和情况2) * * Created by shkstart. */public class MethodRefTest { // 情况一:对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) @Test public void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); } // 情况三:类 :: 实例方法 (有难度) // Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); @Test public void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778/** * 一、构造器引用 * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。 * 抽象方法的返回值类型即为构造器所属的类的类型 * * 二、数组引用 * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。 * * Created by shkstart */public class ConstructorRefTest { //构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } //数组引用 //Function中的R apply(T t) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }} 强大的 Stream API 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677/** * 1. Stream关注的是对数据的运算,与CPU打交道 * 集合关注的是数据的存储,与内存打交道 * * 2. * ①Stream 自己不会存储元素。 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 * * 3.Stream 执行流程 * ① Stream的实例化 * ② 一系列的中间操作(过滤、映射、...) * ③ 终止操作 * * 4.说明: * 4.1 一个中间操作链,对数据源的数据进行处理 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用 * * * 测试Stream的实例化 * * @author shkstart * @create 2019 下午 4:25 */public class StreamAPITest { //创建 Stream方式一:通过集合 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// default Stream<E> stream() : 返回一个顺序流 Stream<Employee> stream = employees.stream();// default Stream<E> parallelStream() : 返回一个并行流 Stream<Employee> parallelStream = employees.parallelStream(); } //创建 Stream方式二:通过数组 @Test public void test2(){ int[] arr = new int[]{1,2,3,4,5,6}; //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流 IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001,"Tom"); Employee e2 = new Employee(1002,"Jerry"); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } //创建 Stream方式三:通过Stream的of() @Test public void test3(){ Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); } //创建 Stream方式四:创建无限流 @Test public void test4(){// 迭代// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) //遍历前10个偶数 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);// 生成// public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120/** * 测试Stream的中间操作 * * @author shkstart * @create 2019 下午 4:42 */public class StreamAPITest1 { //1-筛选与切片 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees();// filter(Predicate p)——接收 Lambda , 从流中排除某些元素。 Stream<Employee> stream = list.stream(); //练习:查询员工表中薪资大于7000的员工信息 stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println();// limit(n)——截断流,使其元素不超过给定数量。 list.stream().limit(3).forEach(System.out::println); System.out.println();// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); System.out.println();// distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",41,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000));// System.out.println(list); list.stream().distinct().forEach(System.out::println); } //映射 @Test public void test2(){// map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);// 练习1:获取员工姓名长度大于3的员工的姓名。 List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3).forEach(System.out::println); System.out.println(); //练习2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println();// flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构成的集合转换为对应的Stream的实例 public static Stream<Character> fromStringToStream(String str){//aa ArrayList<Character> list = new ArrayList<>(); for(Character c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test3(){ ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6);// list1.add(list2); list1.addAll(list2); System.out.println(list1); } //3-排序 @Test public void test4(){// sorted()——自然排序 List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7); list.stream().sorted().forEach(System.out::println); //抛异常,原因:Employee没有实现Comparable接口// List<Employee> employees = EmployeeData.getEmployees();// employees.stream().sorted().forEach(System.out::println);// sorted(Comparator com)——定制排序 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if(ageValue != 0){ return ageValue; }else{ return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495/** * 测试Stream的终止操作 * * @author shkstart * @create 2019 下午 6:37 */public class StreamAPITest2 { //1-匹配与查找 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// allMatch(Predicate p)——检查是否匹配所有元素。// 练习:是否所有的员工的年龄都大于18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch);// anyMatch(Predicate p)——检查是否至少匹配一个元素。// 练习:是否存在员工的工资大于 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch);// noneMatch(Predicate p)——检查是否没有匹配的元素。// 练习:是否存在员工姓“雷” boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch);// findFirst——返回第一个元素 Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee);// findAny——返回当前流中的任意元素 Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2(){ List<Employee> employees = EmployeeData.getEmployees(); // count——返回流中元素的总个数 long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count);// max(Comparator c)——返回流中最大值// 练习:返回最高的工资: Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary);// min(Comparator c)——返回流中最小值// 练习:返回最低工资的员工 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println();// forEach(Consumer c)——内部迭代 employees.stream().forEach(System.out::println); //使用集合的遍历操作 employees.forEach(System.out::println); } //2-归约 @Test public void test3(){// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T// 练习1:计算1-10的自然数的和 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum);// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>// 练习2:计算公司所有员工工资的总和 List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);// Optional<Double> sumMoney = salaryStream.reduce(Double::sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } //3-收集 @Test public void test4(){// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法// 练习1:查找工资大于6000的员工,结果返回为一个List或Set List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); }} Optional类123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100/** * Optional类:为了在程序中避免出现空指针异常而创建的。 * * 常用的方法:ofNullable(T t) * orElse(T t) * * @author shkstart * @create 2019 下午 7:24 */public class OptionalTest {/*Optional.of(T t) : 创建一个 Optional 实例,t必须非空;Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t):t可以为null */ @Test public void test1(){ Girl girl = new Girl();// girl = null; //of(T t):保证t是非空的 Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2(){ Girl girl = new Girl();// girl = null; //ofNullable(T t):t可以为null Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); //orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t. //如果内部的t是空的,则返回orElse()方法中的参数t1. Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖")); System.out.println(girl1); } public String getGirlName(Boy boy){ return boy.getGirl().getName(); } @Test public void test3(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName(boy); System.out.println(girlName); } //优化以后的getGirlName(): public String getGirlName1(Boy boy){ if(boy != null){ Girl girl = boy.getGirl(); if(girl != null){ return girl.getName(); } } return null; } @Test public void test4(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName1(boy); System.out.println(girlName); } //使用Optional类的getGirlName(): public String getGirlName2(Boy boy){ Optional<Boy> boyOptional = Optional.ofNullable(boy); //此时的boy1一定非空 Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); //girl1一定非空 Girl girl1 = girlOptional.orElse(new Girl("古力娜扎")); return girl1.getName(); } @Test public void test5(){ Boy boy = null; boy = new Boy(); boy = new Boy(new Girl("苍老师")); String girlName = getGirlName2(boy); System.out.println(girlName); }}]]></content>
<categories>
<category>Java</category>
<category>小结</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-day11]]></title>
<url>%2FJava%2F%E6%96%B0%E7%89%B9%E6%80%A7%2FJava-day11%2F</url>
<content type="text"><![CDATA[自用Java笔记,主要Java8及之后新特性!奋斗ing 新特性Java8其他新特性Lambda表达式123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173/** * Lambda表达式的使用 * * 1.举例: (o1,o2) -> Integer.compare(o1,o2); * 2.格式: * -> :lambda操作符 或 箭头操作符 * ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表) * ->右边:lambda体 (其实就是重写的抽象方法的方法体) * * 3. Lambda表达式的使用:(分为6种情况介绍) * * 总结: * ->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略 * ->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字 * * 4.Lambda表达式的本质:作为函数式接口的实例 * * 5. 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解, * 这样做可以检查它是否是一个函数式接口。 * * 6. 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。 * * @author shkstart * @create 2019 上午 11:40 */public class LambdaTest1 { //语法格式一:无参,无返回值 @Test public void test1(){ Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱北京天安门"); } }; r1.run(); System.out.println("***********************"); Runnable r2 = () -> { System.out.println("我爱北京故宫"); }; r2.run(); } //语法格式二:Lambda 需要一个参数,但是没有返回值。 @Test public void test2(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("谎言和誓言的区别是什么?"); System.out.println("*******************"); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” @Test public void test3(){ Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } @Test public void test4(){ ArrayList<String> list = new ArrayList<>();//类型推断 int[] arr = {1,2,3};//类型推断 } //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略 @Test public void test5(){ Consumer<String> con1 = (s) -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*******************"); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("一个是听得人当真了,一个是说的人当真了"); } //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值 @Test public void test6(){ Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12,21)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12,6)); } //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略 @Test public void test7(){ Comparator<Integer> com1 = (o1,o2) -> { return o1.compareTo(o2); }; System.out.println(com1.compare(12,6)); System.out.println("*****************************"); Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2); System.out.println(com2.compare(12,21)); } @Test public void test8(){ Consumer<String> con1 = s -> { System.out.println(s); }; con1.accept("一个是听得人当真了,一个是说的人当真了"); System.out.println("*****************************"); Consumer<String> con2 = s -> System.out.println(s); con2.accept("一个是听得人当真了,一个是说的人当真了"); }} 函数式 Functional接囗1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/** * java内置的4大核心函数式接口 * * 消费型接口 Consumer<T> void accept(T t) * 供给型接口 Supplier<T> T get() * 函数型接口 Function<T,R> R apply(T t) * 断定型接口 Predicate<T> boolean test(T t) * * * @author shkstart * @create 2019 下午 2:29 */public class LambdaTest2 { @Test public void test1(){ happyTime(500, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println("学习太累了,去天上人间买了瓶矿泉水,价格为:" + aDouble); } }); System.out.println("********************"); happyTime(400,money -> System.out.println("学习太累了,去天上人间喝了口水,价格为:" + money)); } public void happyTime(double money, Consumer<Double> con){ con.accept(money); } @Test public void test2(){ List<String> list = Arrays.asList("北京","南京","天津","东京","西京","普京"); List<String> filterStrs = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("京"); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list,s -> s.contains("京")); System.out.println(filterStrs1); } //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定 public List<String> filterString(List<String> list, Predicate<String> pre){ ArrayList<String> filterList = new ArrayList<>(); for(String s : list){ if(pre.test(s)){ filterList.add(s); } } return filterList; }} 方法引用与构造器引用123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135/** * 方法引用的使用 * * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! * * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以 * 方法引用,也是函数式接口的实例。 * * 3. 使用格式: 类(或对象) :: 方法名 * * 4. 具体分为如下的三种情况: * 情况1 对象 :: 非静态方法 * 情况2 类 :: 静态方法 * * 情况3 类 :: 非静态方法 * * 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的 * 形参列表和返回值类型相同!(针对于情况1和情况2) * * Created by shkstart. */public class MethodRefTest { // 情况一:对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) @Test public void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); } // 情况三:类 :: 实例方法 (有难度) // Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); @Test public void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879/** * 一、构造器引用 * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。 * 抽象方法的返回值类型即为构造器所属的类的类型 * * 二、数组引用 * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。 * * Created by shkstart */public class ConstructorRefTest { //构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } //数组引用 //Function中的R apply(T t) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }} 强大的 Stream API 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778/** * 1. Stream关注的是对数据的运算,与CPU打交道 * 集合关注的是数据的存储,与内存打交道 * * 2. * ①Stream 自己不会存储元素。 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 * * 3.Stream 执行流程 * ① Stream的实例化 * ② 一系列的中间操作(过滤、映射、...) * ③ 终止操作 * * 4.说明: * 4.1 一个中间操作链,对数据源的数据进行处理 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用 * * * 测试Stream的实例化 * * @author shkstart * @create 2019 下午 4:25 */public class StreamAPITest { //创建 Stream方式一:通过集合 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// default Stream<E> stream() : 返回一个顺序流 Stream<Employee> stream = employees.stream();// default Stream<E> parallelStream() : 返回一个并行流 Stream<Employee> parallelStream = employees.parallelStream(); } //创建 Stream方式二:通过数组 @Test public void test2(){ int[] arr = new int[]{1,2,3,4,5,6}; //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流 IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001,"Tom"); Employee e2 = new Employee(1002,"Jerry"); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); } //创建 Stream方式三:通过Stream的of() @Test public void test3(){ Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); } //创建 Stream方式四:创建无限流 @Test public void test4(){// 迭代// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) //遍历前10个偶数 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);// 生成// public static<T> Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121/** * 测试Stream的中间操作 * * @author shkstart * @create 2019 下午 4:42 */public class StreamAPITest1 { //1-筛选与切片 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees();// filter(Predicate p)——接收 Lambda , 从流中排除某些元素。 Stream<Employee> stream = list.stream(); //练习:查询员工表中薪资大于7000的员工信息 stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println();// limit(n)——截断流,使其元素不超过给定数量。 list.stream().limit(3).forEach(System.out::println); System.out.println();// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 list.stream().skip(3).forEach(System.out::println); System.out.println();// distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",41,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000)); list.add(new Employee(1010,"刘强东",40,8000));// System.out.println(list); list.stream().distinct().forEach(System.out::println); } //映射 @Test public void test2(){// map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);// 练习1:获取员工姓名长度大于3的员工的姓名。 List<Employee> employees = EmployeeData.getEmployees(); Stream<String> namesStream = employees.stream().map(Employee::getName); namesStream.filter(name -> name.length() > 3).forEach(System.out::println); System.out.println(); //练习2: Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringToStream); streamStream.forEach(s ->{ s.forEach(System.out::println); }); System.out.println();// flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构成的集合转换为对应的Stream的实例 public static Stream<Character> fromStringToStream(String str){//aa ArrayList<Character> list = new ArrayList<>(); for(Character c : str.toCharArray()){ list.add(c); } return list.stream(); } @Test public void test3(){ ArrayList list1 = new ArrayList(); list1.add(1); list1.add(2); list1.add(3); ArrayList list2 = new ArrayList(); list2.add(4); list2.add(5); list2.add(6);// list1.add(list2); list1.addAll(list2); System.out.println(list1); } //3-排序 @Test public void test4(){// sorted()——自然排序 List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7); list.stream().sorted().forEach(System.out::println); //抛异常,原因:Employee没有实现Comparable接口// List<Employee> employees = EmployeeData.getEmployees();// employees.stream().sorted().forEach(System.out::println);// sorted(Comparator com)——定制排序 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if(ageValue != 0){ return ageValue; }else{ return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596/** * 测试Stream的终止操作 * * @author shkstart * @create 2019 下午 6:37 */public class StreamAPITest2 { //1-匹配与查找 @Test public void test1(){ List<Employee> employees = EmployeeData.getEmployees();// allMatch(Predicate p)——检查是否匹配所有元素。// 练习:是否所有的员工的年龄都大于18 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println(allMatch);// anyMatch(Predicate p)——检查是否至少匹配一个元素。// 练习:是否存在员工的工资大于 10000 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch);// noneMatch(Predicate p)——检查是否没有匹配的元素。// 练习:是否存在员工姓“雷” boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch);// findFirst——返回第一个元素 Optional<Employee> employee = employees.stream().findFirst(); System.out.println(employee);// findAny——返回当前流中的任意元素 Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1); } @Test public void test2(){ List<Employee> employees = EmployeeData.getEmployees(); // count——返回流中元素的总个数 long count = employees.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count);// max(Comparator c)——返回流中最大值// 练习:返回最高的工资: Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary);// min(Comparator c)——返回流中最小值// 练习:返回最低工资的员工 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee); System.out.println();// forEach(Consumer c)——内部迭代 employees.stream().forEach(System.out::println); //使用集合的遍历操作 employees.forEach(System.out::println); } //2-归约 @Test public void test3(){// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T// 练习1:计算1-10的自然数的和 List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum);// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>// 练习2:计算公司所有员工工资的总和 List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);// Optional<Double> sumMoney = salaryStream.reduce(Double::sum); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get()); } //3-收集 @Test public void test4(){// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法// 练习1:查找工资大于6000的员工,结果返回为一个List或Set List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); employeeSet.forEach(System.out::println); }} Optional类123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100/** * Optional类:为了在程序中避免出现空指针异常而创建的。 * * 常用的方法:ofNullable(T t) * orElse(T t) * * @author shkstart * @create 2019 下午 7:24 */public class OptionalTest {/*Optional.of(T t) : 创建一个 Optional 实例,t必须非空;Optional.empty() : 创建一个空的 Optional 实例Optional.ofNullable(T t):t可以为null */ @Test public void test1(){ Girl girl = new Girl();// girl = null; //of(T t):保证t是非空的 Optional<Girl> optionalGirl = Optional.of(girl); } @Test public void test2(){ Girl girl = new Girl();// girl = null; //ofNullable(T t):t可以为null Optional<Girl> optionalGirl = Optional.ofNullable(girl); System.out.println(optionalGirl); //orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t. //如果内部的t是空的,则返回orElse()方法中的参数t1. Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖")); System.out.println(girl1); } public String getGirlName(Boy boy){ return boy.getGirl().getName(); } @Test public void test3(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName(boy); System.out.println(girlName); } //优化以后的getGirlName(): public String getGirlName1(Boy boy){ if(boy != null){ Girl girl = boy.getGirl(); if(girl != null){ return girl.getName(); } } return null; } @Test public void test4(){ Boy boy = new Boy(); boy = null; String girlName = getGirlName1(boy); System.out.println(girlName); } //使用Optional类的getGirlName(): public String getGirlName2(Boy boy){ Optional<Boy> boyOptional = Optional.ofNullable(boy); //此时的boy1一定非空 Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl); //girl1一定非空 Girl girl1 = girlOptional.orElse(new Girl("古力娜扎")); return girl1.getName(); } @Test public void test5(){ Boy boy = null; boy = new Boy(); boy = new Boy(new Girl("苍老师")); String girlName = getGirlName2(boy); System.out.println(girlName); }}]]></content>
<categories>
<category>Java</category>
<category>新特性</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-day10]]></title>
<url>%2FJava%2F%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B%E4%B8%8E%E5%8F%8D%E5%B0%84%2FJava-day10%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅹ),主要记录网络编程与反射!奋斗ing 网络编程概述网络编程中有两个主要的问题: 如何准确地定位网络上一台或多台主机;定位主机上的特定的应用 找到主机后如何可靠高效地进行数据传输 网络编程中的两个要素: 对应问题一:IP和端口号 对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层) 通信要素一: IP和端口号 IP:唯一的标识 Internet 上的计算机(通信实体) 在Java中使用InetAddress类代表IP IP分类:IPv4 和 IPv6 ; 万维网 和 局域网 域名: www.baidu.com www.mi.com www.sina.com www.jd.com 本地回路地址:127.0.0.1 对应着:localhost 如何实例化InetAddress:两个方法:getByName(String host) 、 getLocalHost() 两个常用方法:getHostName() / getHostAddress() 端口号:正在计算机上运行的进程。 要求:不同的进程有不同的端口号 范围:被规定为一个 16 位的整数 0~65535。 端口号与IP地址的组合得出一个网络套接字:Socket 1234567891011121314151617181920212223242526public static void main(String[] args) { try { //File file = new File("hello.txt"); InetAddress inet1 = InetAddress.getByName("192.168.10.14"); System.out.println(inet1); InetAddress inet2 = InetAddress.getByName("www.atguigu.com"); System.out.println(inet2); InetAddress inet3 = InetAddress.getByName("127.0.0.1"); System.out.println(inet3); //获取本地ip InetAddress inet4 = InetAddress.getLocalHost(); System.out.println(inet4); //getHostName() System.out.println(inet2.getHostName()); //getHostAddress() System.out.println(inet2.getHostAddress()); } catch (UnknownHostException e) { e.printStackTrace(); }} 实现TCP的网络编程 例题3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。 并关闭相应的连接。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869@Testpublic void client() throws IOException { //1. Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9090); //2. OutputStream os = socket.getOutputStream(); //3. FileInputStream fis = new FileInputStream(new File("beauty.jpg")); //4. byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ os.write(buffer,0,len); } //关闭数据的输出 socket.shutdownOutput(); //5.接收来自于服务器端的数据,并显示到控制台上 InputStream is = socket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bufferr = new byte[20]; int len1; while((len1 = is.read(buffer)) != -1){ baos.write(buffer,0,len1); } System.out.println(baos.toString()); //6. fis.close(); os.close(); socket.close(); baos.close();}/* 这里涉及到的异常,应该使用try-catch-finally处理 */@Testpublic void server() throws IOException { //1. ServerSocket ss = new ServerSocket(9090); //2. Socket socket = ss.accept(); //3. InputStream is = socket.getInputStream(); //4. FileOutputStream fos = new FileOutputStream(new File("beauty2.jpg")); //5. byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("图片传输完成"); //6.服务器端给予客户端反馈 OutputStream os = socket.getOutputStream(); os.write("你好,美女,照片我已收到,非常漂亮!".getBytes()); //7. fos.close(); is.close(); socket.close(); ss.close(); os.close();} UDPd协议的网络编程 123456789101112131415161718192021222324252627282930313233//发送端@Testpublic void sender() throws IOException { DatagramSocket socket = new DatagramSocket(); String str = "我是UDP方式发送的导弹"; byte[] data = str.getBytes(); InetAddress inet = InetAddress.getLocalHost(); DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090); socket.send(packet); socket.close();}//接收端@Testpublic void receiver() throws IOException { DatagramSocket socket = new DatagramSocket(9090); byte[] buffer = new byte[100]; DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length); socket.receive(packet); System.out.println(new String(packet.getData(),0,packet.getLength())); socket.close();} URL网络编程 URL:统一资源定位符,对应着互联网的某一资源地址 格式: http://localhost:8080/examples/beauty.jpg?username=Tom 协议 主机名 端口号 资源地址 参数列表 12345678910111213141516171819public static void main(String[] args) { try { URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom"); // public String getProtocol( ) 获取该URL的协议名 System.out.println(url.getProtocol()); // public String getHost( ) 获取该URL的主机名 System.out.println(url.getHost()); // public String getPort( ) 获取该URL的端口号 System.out.println(url.getPort()); // public String getPath( ) 获取该URL的文件路径 System.out.println(url.getPath()); // public String getFile( ) 获取该URL的文件名 System.out.println(url.getFile()); // public String getQuery( ) 获取该URL的查询名 System.out.println(url.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445public static void main(String[] args) { HttpURLConnection urlConnection = null; InputStream is = null; FileOutputStream fos = null; try { URL url = new URL("http://localhost:8080/examples/beauty.jpg"); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); is = urlConnection.getInputStream(); fos = new FileOutputStream("day10\\beauty3.jpg"); byte[] buffer = new byte[1024]; int len; while((len = is.read(buffer)) != -1){ fos.write(buffer,0,len); } System.out.println("下载完成"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(is != null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(urlConnection != null){ urlConnection.disconnect(); } }} *idea快捷键: 重构一切:Ctrl+Shift+Alt+T 智能补全:Ctrl+Shift+Space 选你所想:Ctrl+W 自我修复:Alt+Enter 自动完成:Ctrl+Shift+Enter 可以新建类、方法等任何东西、get/set、toString方法: alt+insert 自动new完整对象: Ctrl+Alt+V,可以引入变量 自动选中模块代码:Ctrl+W 移动到前/后方法:Alt+Forward/Backward 删除行:Ctrl+Y、复制:Ctrl+D 切换vim模式:Ctrl+; 高亮错误或警告快速定位:F2或shift+F2 打开类或资源:Ctrl+N/Ctrl+Shift+N 弹出框中搜索任何东西,包括类、资源、配置项、方法:Shift+Shift 查看当前类的所有方法:Ctrl+F12 找到类或方法使用的地方:,Alt+F7 格式化import列表:Ctrl+Alt+O,格式化代码:Ctrl+Alt+L 查看项目结构选中类:Alt+1,查看搜索窗口:Alt+3,查看运行调试Alt+4/5 打开最近打开或编辑过的文件列表:Ctrl+E 运行程序:Alt+Shift+F10,启动调试:Shift+F9,停止:Ctrl+F2。 调试:F7/F8/F9分别对应Step into,Step over,Continue 上/下移一行:Alt+Shift+Up/Down 反射概述反射的特征:动态性 疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?建议:直接new的方式。什么时候会使用:反射的方式。疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?不矛盾。 java.lang.Class类的理解 类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件 加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此 运行时类,就作为Class的一个实例。 换句话说,Class的实例就对应着一个运行时类。 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。 获取Class的实例的方式 调用运行时类的属性:.class 通过运行时类的对象,调用getClass() 调用Class的静态方法:forName(String classPath) 使用类的加载器:ClassLoader (了解) 1234567891011121314151617181920212223242526//(前三种方式需要掌握)@Testpublic void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class clazz1 = Person.class; System.out.println(clazz1); //方式二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); //方式三:调用Class的静态方法:forName(String classPath) Class clazz3 = Class.forName("com.atguigu.java.Person"); // clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); System.out.println(clazz1 == clazz2); System.out.println(clazz1 == clazz3); //方式四:使用类的加载器:ClassLoader (了解) ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4); System.out.println(clazz1 == clazz4);} Class实例可以是哪些结构的说明: 1234567891011121314151617181920//万事万物皆对象?对象.xxx,File,URL,反射,前端、数据库操作@Testpublic void test4(){ Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class; Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // 只要数组的元素类型与维度一样,就是同一个Class System.out.println(c10 == c11);} 了解类的加载器 12345678910111213141516@Testpublic void test1(){ //对于自定义类,使用系统类加载器进行加载 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); //调用系统类加载器的getParent():获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //调用扩展类加载器的getParent():无法获取引导类加载器 //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2); ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);} 使用 ClassLoader加载配置文件 123456789101112131415161718192021//Properties:用来读取配置文件。@Testpublic void test2() throws Exception { Properties pros = new Properties(); //此时的文件默认在当前的module下。 //读取配置文件的方式一: // FileInputStream fis = new FileInputStream("jdbc.properties"); // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); // pros.load(fis); //读取配置文件的方式二:使用ClassLoader //配置文件默认识别为:当前module的src下 ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("jdbc1.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password);} 通过反射 创建运行时类的对象 newInstance(): 调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。要想此方法正常的创建运行时类的对象,要求: 运行时类必须提供空参的构造器 空参的构造器的访问权限得够。通常,设置为public。 在javabean中要求提供一个public的空参构造器。原因:1.便于通过反射,创建运行时类的对象2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器 1234567@Testpublic void test1() throws IllegalAccessException, InstantiationException { Class<Person> clazz = Person.class; Person obj = clazz.newInstance(); System.out.println(obj);} 体会反射的动态性 123456789101112131415161718192021222324252627282930313233@Testpublic void test2(){ for(int i = 0;i < 100;i++){ int num = new Random().nextInt(3);//0,1,2 String classPath = ""; switch(num){ case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.lang.Object"; break; case 2: classPath = "com.atguigu.java.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); } }}/* 创建一个指定类的对象。 classPath:指定类的全类名 */public Object getInstance(String classPath) throws Exception { Class clazz = Class.forName(classPath); return clazz.newInstance();} 获取运行时类的完整结构属性结构 getFields(): 获取当前运行时类及其父类中声明为public访问权限的属性 getDeclaredFields(): 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) 12345678910111213141516@Testpublic void test1(){ Class clazz = Person.class; //获取属性结构 //getFields():获取当前运行时类及其父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for(Field f : fields){ System.out.println(f); } System.out.println(); //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ System.out.println(f); }} 权限修饰符 数据类型 变量名 1234567891011121314151617@Testpublic void test2(){ Class clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for(Field f : declaredFields){ //1.权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); //2.数据类型 Class type = f.getType(); System.out.print(type.getName() + "\t"); //3.变量名 String fName = f.getName(); System.out.print(fName); System.out.println(); }} 方法结构 getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法 getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) 123456789101112131415@Testpublic void test1(){ Class clazz = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法 Method[] methods = clazz.getMethods(); for(Method m : methods){ System.out.println(m); } System.out.println(); //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ System.out.println(m); }} 权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) throws XxxException{} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748@Testpublic void test2(){ Class clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for(Method m : declaredMethods){ //1.获取方法声明的注解 Annotation[] annos = m.getAnnotations(); for(Annotation a : annos){ System.out.println(a); } //2.权限修饰符 System.out.print(Modifier.toString(m.getModifiers()) + "\t"); //3.返回值类型 System.out.print(m.getReturnType().getName() + "\t"); //4.方法名 System.out.print(m.getName()); System.out.print("("); //5.形参列表 Class[] parameterTypes = m.getParameterTypes(); if(!(parameterTypes == null && parameterTypes.length == 0)){ for(int i = 0;i < parameterTypes.length;i++){ if(i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + " args_" + i); break; } System.out.print(parameterTypes[i].getName() + " args_" + i + ","); } } System.out.print(")"); //6.抛出的异常 Class[] exceptionTypes = m.getExceptionTypes(); if(exceptionTypes.length > 0){ System.out.print("throws "); for(int i = 0;i < exceptionTypes.length;i++){ if(i == exceptionTypes.length - 1){ System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName() + ","); } } System.out.println(); }} 构造器结构 getConstructors(): 获取当前运行时类中声明为public的构造器 getDeclaredConstructors(): 获取当前运行时类中声明的所有的构造器 1234567891011121314151617@Testpublic void test1(){ Class clazz = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor[] constructors = clazz.getConstructors(); for(Constructor c : constructors){ System.out.println(c); } System.out.println(); //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器 Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for(Constructor c : declaredConstructors){ System.out.println(c); }} 其他结构获取运行时类的父类 1234567@Testpublic void test2(){ Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass);} 获取运行时类的带泛型的父类 1234567@Testpublic void test3(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass);} 获取运行时类的带泛型的父类的泛型 1234567891011@Testpublic void test4(){ Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) genericSuperclass; //获取泛型类型 Type[] actualTypeArguments = paramType.getActualTypeArguments(); // System.out.println(actualTypeArguments[0].getTypeName()); System.out.println(((Class)actualTypeArguments[0]).getName());} 获取运行时类实现的接口 123456789101112131415@Testpublic void test5(){ Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for(Class c : interfaces){ System.out.println(c); } System.out.println(); //获取运行时类的父类实现的接口 Class[] interfaces1 = clazz.getSuperclass().getInterfaces(); for(Class c : interfaces1){ System.out.println(c); }} 获取运行时类所在的包 1234567@Testpublic void test6(){ Class clazz = Person.class; Package pack = clazz.getPackage(); System.out.println(pack);} 获取运行时类声明的注解 123456789@Testpublic void test7(){ Class clazz = Person.class; Annotation[] annotations = clazz.getAnnotations(); for(Annotation annos : annotations){ System.out.println(annos); }} 调用运行时类的指定结构属性、方法、构造器 12345678910111213141516171819202122// 不需要掌握@Testpublic void testField() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //获取指定的属性:要求运行时类中属性声明为public //通常不采用此方法 Field id = clazz.getField("id"); /* 设置当前属性的值 set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少 */ id.set(p,1001); /* 获取当前属性的值 get():参数1:获取哪个对象的当前属性值 */ int pId = (int) id.get(p); System.out.println(pId);} 操作指定的属性1234567891011121314151617181920/* 如何操作运行时类中的指定的属性 -- 需要掌握 */@Testpublic void testField1() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性 Field name = clazz.getDeclaredField("name"); //2.保证当前属性是可访问的 name.setAccessible(true); //3.获取、设置指定对象的此属性值 name.set(p,"Tom"); System.out.println(name.get(p));} 指定的方法123456789101112131415161718192021222324252627282930313233/* 如何操作运行时类中的指定的方法 -- 需要掌握 */@Testpublic void testMethod() throws Exception { Class clazz = Person.class; //创建运行时类的对象 Person p = (Person) clazz.newInstance(); /* 1.获取指定的某个方法 getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表 */ Method show = clazz.getDeclaredMethod("show", String.class); //2.保证当前方法是可访问的 show.setAccessible(true); /* 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参 invoke()的返回值即为对应类中调用的方法的返回值。 */ Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); System.out.println(returnValue); System.out.println("*************如何调用静态方法*****************"); // private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //如果调用的运行时类中的方法没有返回值,则此invoke()返回null // Object returnVal = showDesc.invoke(null); Object returnVal = showDesc.invoke(Person.class); System.out.println(returnVal);//null} 指定的构造器123456789101112131415161718192021/* 如何调用运行时类中的指定的构造器 */@Testpublic void testConstructor() throws Exception { Class clazz = Person.class; //private Person(String name) /* 1.获取指定的构造器 getDeclaredConstructor():参数:指明构造器的参数列表 */ Constructor constructor = clazz.getDeclaredConstructor(String.class); //2.保证此构造器是可访问的 constructor.setAccessible(true); //3.调用此构造器创建运行时类的对象 Person per = (Person) constructor.newInstance("Tom"); System.out.println(per);} 应用:动态代理 静态代理举例 特点:代理类和被代理类在编译期间,就确定下来了。 12345678910111213141516171819202122232425262728293031323334353637383940interface ClothFactory{ void produceCloth();}//代理类class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//用被代理类对象进行实例化 public ProxyClothFactory(ClothFactory factory){ this.factory = factory; } @Override public void produceCloth() { System.out.println("代理工厂做一些准备工作"); factory.produceCloth(); System.out.println("代理工厂做一些后续的收尾工作"); }}//被代理类class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); }}public class StaticProxyTest { public static void main(String[] args) { //创建被代理类的对象 ClothFactory nike = new NikeClothFactory(); //创建代理类的对象 ClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); }} 动态代理的举例 要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081interface Human{ String getBelief(); void eat(String food);}//被代理类class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("我喜欢吃" + food); }}class HumanUtil{ public void method1(){ System.out.println("====================通用方法一===================="); } public void method2(){ System.out.println("====================通用方法二===================="); }}/*要想实现动态代理,需要解决的问题?问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。 */class ProxyFactory{ //调用此方法,返回一个代理类的对象。解决问题一 public static Object getProxyInstance(Object obj){//obj:被代理类的对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); }}class MyInvocationHandler implements InvocationHandler{ private Object obj;//需要使用被代理类的对象进行赋值 public void bind(Object obj){ this.obj = obj; } //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke() //将被代理类要执行的方法a的功能就声明在invoke()中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { HumanUtil util = new HumanUtil(); util.method1(); //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 //obj:被代理类的对象 Object returnValue = method.invoke(obj,args); util.method2(); //上述方法的返回值就作为当前类中的invoke()的返回值。 return returnValue; }}public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); //proxyInstance:代理类的对象 Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法 String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("四川麻辣烫"); System.out.println("*****************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); }}]]></content>
<categories>
<category>Java</category>
<category>网络编程与反射</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-day9]]></title>
<url>%2FJava%2F%E6%B3%9B%E5%9E%8B%2FJava-day9%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅸ),主要记录Java泛型与IO流!奋斗ing 泛型Generic标签 概念集合容器类在 设计阶段/声明阶段 不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为 Object, JDK1.5之后使用泛型来解决。 因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。 Collection,List, ArrayList这个就是类型参数,即泛型。(不能是基础类型) 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中 某个属性的类型 或者是 某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。 12345678910111213141516171819202122232425262728293031323334//在集合中使用泛型之前的情况@Test public void test1{ ArrayList list = new ArrayList(); //需求:存放学生的成绩 list.add(78); list.add(76); //问题一:类型不安全 List.add("Tom"); for(object score : list){ //问题二:强转时,可能出现 CLassCastException int stuScore = (Integer) score; System.out.println(stuScore); }//在集合中使用泛型的情况public void test2{ ArrayList<Integer> list = new ArrayList<Integer>(); list.add(78); list.add(76); //编译时,就会进行类型检查,保证数据的安全 List.add("Tom"); for(Integer score : list) { //避免了强转操作 int stuScore = score; System.out.println(stuScore); } //方式二: Iterator<Integer> iterator = list.iterator(); While(iterator.hasNext()) { int stuScore = iterator.next(); System.out.println(stuScore); }} 123456789101112131415//在集合中使用泛型的情况:以 HashMap 为例public void test3(){ Map<String,Integer> map = new HashMap<String, Integer>(); map.put("Jerry", 87); map.put("Jack", 67); // map.put(123,"ABC");报错 //泛型的放套 Set<Map Entry<string, Integer>> entry = map.entryset(); Iterator<Map Entry<String, Integer>> iterator = entry.iterator(); While(iterator.hasNext()){ Map Entry<String, Integer> e = iterator.next(); String key = e.getKey(); Integer value = e.getValue(); System.out.println(key + "----" + value);} 在集合中使用泛型总结 ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。 ② 在实例化 集合类 时,可以指明具体的泛型类型 ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。 比如:add(E e) —> 实例化以后:add(Integer e) ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换 ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。 自定义泛型结构泛型类、泛型接口12345//子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再要指明泛型public class SubOrder1<T> extends order<T> // Suborder1<T>:仍然是泛型类public class subOrder extends order<Integer> // SubOrder:不是泛型类 PS: 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。 比如 <E1, E2, E3> 泛型类的 构造器 如下: public GenericClass(){}而下面是错误的:public Generic Class(){} 实例化后,操作原来泛型位置的结构 必须与指定的泛型类型一致 泛型不同的引用不能相互赋值 尽管在编译时 ArrayList和 ArrayList是两种类型,但是,在运行时只有一个 ArrayList被加载到JVM中 泛型如果不指定,将被搽除,泛型对应的类型均按照Object处理,但不等价于Object。 经验:泛型要使用一路都用。要不用,一路都不要用。 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。 jdk1.7,泛型的简化操作:ArrayList fist = new Array List<>();; 泛型的指定中不能使用基本数据类型,可以使用包装类替换。 在 类/接口 上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。 异常类不能是泛型的 不能使用new E。但是可以:E elements = (E) new Object()参考:ArrayList 源码中声明:Object[] elementData,而非泛型参数类型数组。 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型: 子类 不保留 父类的泛型:按需实现没有类型 擦除 具体类型 子类 保留 父类的泛型:泛型子类 全部保留 / 部分保留 结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型 泛型方法在方法中出现了泛型的结构,泛型参数 与 类的泛型参数没有任何关系。换句话说,泛型方法所属的类是不是泛型类都没有关系 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的,并非在实例化类时确定。 1234567public static <E> List<E> copyFromArrayToList(E[] arr) { ArrayList<E> list = new ArrayList<E>(); for(E e : arr) { list.add(e); } return list} 泛型在继承方面的体现虽然类A是类B的父类,但是G 和 G 二者不具备子父类关系,二者是并列关系。 补充:类A是类B的父类,A 是B 的父类 通配符: ?类A是类B的父类,G 和G 是没有关系的,二者共同的父类是:G<?> 对于G<?> 就不能向其内部添加数据,除了添加null之外 允许读取数据,读取的数据类型为Object。 有限制条件的通配符 ? extends A: ? super A: 123456789101112131415161718192021222324252627282930313233343536373839/*有限制条件的通配符的使用。 ? extends A: 上限<= G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类 ? super A: 下限>= G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类 */ @Test public void test4(){ List<? extends Person> list1 = null; List<? super Person> list2 = null; List<Student> list3 = new ArrayList<Student>(); List<Person> list4 = new ArrayList<Person>(); List<Object> list5 = new ArrayList<Object>(); list1 = list3; list1 = list4;// list1 = list5;// list2 = list3; list2 = list4; list2 = list5; //读取数据: list1 = list3; Person p = list1.get(0); //编译不通过 //Student s = list1.get(0); list2 = list4; Object obj = list2.get(0); ////编译不通过// Person obj = list2.get(0); //写入数据: //编译不通过// list1.add(new Student()); //编译通过 list2.add(new Person()); list2.add(new Student()); }} IOFile类java.io.File类,文件和文件目录路径的抽象表示形式,与平台无关 File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹) File类声明在java.io包下 File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法, 并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”终点”. 创建File类的实例 File(String filePath) File(String parentPath,String childPath) File(File parentFile,String childPath) PS: 相对路径:相较于某个路径下,指明的路径。绝对路径:包含盘符在内的文件或文件目录的路径 .路径分隔符 windows:\ unix:/12345678910111213141516@Testpublic void test1(){ //构造器1 File file1 = new File("hello.txt");//相对于当前module File file2 = new File("D:\\workspace_idea1\\JavaSenior\\day08\\he.txt"); System.out.println(file1); System.out.println(file2); //构造器2: File file3 = new File("D:\\workspace_idea1","JavaSenior"); System.out.println(file3); //构造器3: File file4 = new File(file3,"hi.txt"); System.out.println(file4);} 常用方法 public String getAbsolutePath():获取绝对路径 public String getPath() :获取路径 public String getName() :获取名称 public String getParent():获取上层文件目录路径。若无,返回null public long length() :获取文件长度(即:字节数)。不能获取目录的长度。 public long lastModified() :获取最后一次的修改时间,毫秒值 如下的两个方法适用于文件目录: public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组 12345678910111213141516171819202122232425262728293031323334@Testpublic void test2(){ File file1 = new File("hello.txt"); File file2 = new File("d:\\io\\hi.txt"); System.out.println(file1.getAbsolutePath()); System.out.println(file1.getPath()); System.out.println(file1.getName()); System.out.println(file1.getParent()); System.out.println(file1.length()); System.out.println(new Date(file1.lastModified())); System.out.println(); System.out.println(file2.getAbsolutePath()); System.out.println(file2.getPath()); System.out.println(file2.getName()); System.out.println(file2.getParent()); System.out.println(file2.length()); System.out.println(file2.lastModified());}@Testpublic void test3(){ File file = new File("D:\\workspace_idea1\\JavaSenior"); String[] list = file.list(); for(String s : list){ System.out.println(s); } System.out.println(); File[] files = file.listFiles(); for(File f : files){ System.out.println(f); }} public boolean renameTo(File dest):把文件重命名为指定的文件路径比如:file1.renameTo(file2)为例:要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。 12345678@Testpublic void test4(){ File file1 = new File("hello.txt"); File file2 = new File("D:\\io\\hi.txt"); boolean renameTo = file2.renameTo(file1); System.out.println(renameTo);} public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏 1234567891011121314151617181920212223@Testpublic void test5(){ File file1 = new File("hello.txt"); file1 = new File("hello1.txt"); System.out.println(file1.isDirectory()); System.out.println(file1.isFile()); System.out.println(file1.exists()); System.out.println(file1.canRead()); System.out.println(file1.canWrite()); System.out.println(file1.isHidden()); System.out.println(); File file2 = new File("d:\\io"); file2 = new File("d:\\io1"); System.out.println(file2.isDirectory()); System.out.println(file2.isFile()); System.out.println(file2.exists()); System.out.println(file2.canRead()); System.out.println(file2.canWrite()); System.out.println(file2.isHidden());} 创建 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建 删除 public boolean delete():删除文件或者文件夹删除注意事项:**Java中的删除不走回收站**。 123456789101112131415161718192021222324252627282930313233@Testpublic void test6() throws IOException { File file1 = new File("hi.txt"); if(!file1.exists()){ //文件的创建 file1.createNewFile(); System.out.println("创建成功"); }else{//文件存在 file1.delete(); System.out.println("删除成功"); }}@Testpublic void test7(){ //文件目录的创建 File file1 = new File("d:\\io\\io1\\io3"); boolean mkdir = file1.mkdir(); if(mkdir){ System.out.println("创建成功1"); } File file2 = new File("d:\\io\\io1\\io4"); boolean mkdir1 = file2.mkdirs(); if(mkdir1){ System.out.println("创建成功2"); } //要想删除成功,io4文件目录下不能有子目录或文件 File file3 = new File("D:\\io\\io1\\io4"); file3 = new File("D:\\io\\io1"); System.out.println(file3.delete()); //false} IO流处理设备之间的数据传输,对于数据的输入输出操作以“流Stream”的方式进行。 流的分类 按操作数据单位不同分为:字节流(8bit),字符流(16bit) 按数据流的流向不同分为:输入流,输出流 按流的角色的不同分为:节点流,处理流 抽象基类 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer PS: Java的O流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀 流的体系结构 抽象基类 节点流(或文件流) 缓冲流(处理流的一种) InputStream FileInputStream (read(byte[] buffer)) BufferedInputStream (read(byte[] buffer)) OutputStream FileOutputStream (write(byte[] buffer,0,len) BufferedOutputStream (write(byte[] buffer,0,len) / flush() Reader FileReader (read(char[] cbuf)) BufferedReader (read(char[] cbuf) / readLine()) Writer FileWriter (write(char[] cbuf,0,len) BufferedWriter (write(char[] cbuf,0,len) / flush() 节点流字符流1.read() 从内存中写出数据到硬盘的文件里。 File类的实例化 FileReader流的实例化 读入的操作 资源的关闭 说明点: read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理 读入的文件一定要存在,否则就会报FileNotFoundException。 123456789101112131415161718192021222324252627282930313233343536373839404142@Testpublic void testFileReader(){ FileReader fr = null; try { //1.实例化File类的对象,指明要操作的文件 File file = new File("hello.txt");//相较于当前Module //2.提供具体的流 fr = new FileReader(file); //3.数据的读入 //read():返回读入的一个字符。如果达到文件末尾,返回-1 //方式一:// int data = fr.read();// while(data != -1){// System.out.print((char)data);// data = fr.read();// } //方式二:语法上针对于方式一的修改 int data; while((data = fr.read()) != -1){ System.out.print((char)data); } } catch (IOException e) { e.printStackTrace(); } finally { //4.流的关闭操作// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// } //或 if(fr != null){ try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } //对read()操作升级:使用read的重载方法 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void testFileReader1() { FileReader fr = null; try { //1.File类的实例化 File file = new File("hello.txt"); //2.FileReader流的实例化 fr = new FileReader(file); //3.读入的操作 //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1 char[] cbuf = new char[5]; int len; while((len = fr.read(cbuf)) != -1){ //方式一: //错误的写法// for(int i = 0;i < cbuf.length;i++){// System.out.print(cbuf[i]);// } //heloworld123ld //正确的写法// for(int i = 0;i < len;i++){// System.out.print(cbuf[i]);// } //方式二: //错误的写法,对应着方式一的错误的写法// String str = new String(cbuf);// System.out.print(str); //正确的写法 String str = new String(cbuf,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fr != null){ //4.资源的关闭 try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 2.write() 从内存中写出数据到硬盘的文件里。 说明: 输出操作,对应的File可以不存在的。并不会报异常 File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。File对应的硬盘中的文件如果存在:如果流使用的构造器是:**FileWriter(file,false)** / **FileWriter(file)**:**对原有文件的覆盖** 如果流使用的构造器是:**FileWriter(file,true)**:**不会对原有文件覆盖,而是在原有文件基础上追加内容** 123456789101112131415161718192021222324252627@Testpublic void testFileWriter() { FileWriter fw = null; try { //1.提供File类的对象,指明写出到的文件 File file = new File("hello1.txt"); //2.提供FileWriter的对象,用于数据的写出 fw = new FileWriter(file,false); //3.写出的操作 fw.write("I have a dream!\n"); fw.write("you need to have a dream!"); } catch (IOException e) { e.printStackTrace(); } finally { //4.流资源的关闭 if(fw != null){ try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } }} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556@Testpublic void testFileReaderFileWriter() { FileReader fr = null; FileWriter fw = null; try { //1.创建File类的对象,指明读入和写出的文件 File srcFile = new File("hello.txt"); File destFile = new File("hello2.txt"); //不能使用字符流来处理图片等字节数据// File srcFile = new File("爱情与友情.jpg");// File destFile = new File("爱情与友情1.jpg"); //2.创建输入流和输出流的对象 fr = new FileReader(srcFile); fw = new FileWriter(destFile); //3.数据的读入和写出操作 char[] cbuf = new char[5]; int len;//记录每次读入到cbuf数组中的字符的个数 while((len = fr.read(cbuf)) != -1){ //每次写出len个字符 fw.write(cbuf,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.关闭流资源 //方式一:// try {// if(fw != null)// fw.close();// } catch (IOException e) {// e.printStackTrace();// }finally{// try {// if(fr != null)// fr.close();// } catch (IOException e) {// e.printStackTrace();// }// } //方式二: try { if(fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } try { if(fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } }} 字节流结论: 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,…),使用字节流处理 12345678910111213141516171819202122232425262728293031//使用字节流FileInputStream处理文本文件,可能出现乱码。@Testpublic void testFileInputStream() { FileInputStream fis = null; try { //1. 造文件 File file = new File("hello.txt"); //2.造流 fis = new FileInputStream(file); //3.读数据 byte[] buffer = new byte[5]; int len;//记录每次读取的字节的个数 while((len = fis.read(buffer)) != -1){ String str = new String(buffer,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ //4.关闭资源 try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现对图片的复制操作 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687@Testpublic void testFileInputOutputStream() { FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情2.jpg"); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[5]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}//指定路径下文件的复制public void copyFile(String srcPath,String destPath){ FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File(srcPath); File destFile = new File(destPath); fis = new FileInputStream(srcFile); fos = new FileOutputStream(destFile); //复制的过程 byte[] buffer = new byte[1024]; int len; while((len = fis.read(buffer)) != -1){ fos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void testCopyFile(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\02-视频.avi"; // String srcPath = "hello.txt"; // String destPath = "hello3.txt"; copyFile(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618} 缓冲流处理流,就是“套接”在已有的流的基础上。处理流之一:缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 作用:提高流的读取、写入的速度(内部提供了一个缓冲区) 实现非文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void BufferedStreamTest() throws FileNotFoundException { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File("爱情与友情.jpg"); File destFile = new File("爱情与友情3.jpg"); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[10]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); // bos.flush();//刷新缓冲区 } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }} 实现文本文件的复制 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Testpublic void testBufferedReaderBufferedWriter(){ BufferedReader br = null; BufferedWriter bw = null; try { //创建文件和相应的流 br = new BufferedReader(new FileReader(new File("dbcp.txt"))); bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt"))); //读写操作 //方式一:使用char[]数组 // char[] cbuf = new char[1024]; // int len; // while((len = br.read(cbuf)) != -1){ // bw.write(cbuf,0,len); // // bw.flush(); // } //方式二:使用String String data; while((data = br.readLine()) != null){ //方法一: // bw.write(data + "\n");//data中不包含换行符 //方法二: bw.write(data);//data中不包含换行符 bw.newLine();//提供换行的操作 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 if(bw != null){ try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if(br != null){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 实现文件复制的方法 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); }}@Testpublic void testCopyFileWithBuffered(){ long start = System.currentTimeMillis(); String srcPath = "C:\\Users\\Administrator\\Desktop\\01-视频.avi"; String destPath = "C:\\Users\\Administrator\\Desktop\\03-视频.avi"; copyFileWithBuffered(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制操作花费的时间为:" + (end - start));//618 - 176} 转换流处理流之二:转换流 转换流:属于字符流 InputStreamReader:将一个字节的输入流转换为字符的输入流 OutputStreamWriter:将一个字符的输出流转换为字节的输出流 作用:提供字节流与字符流之间的转换 解码:字节、字节数组 —>字符数组、字符串 编码:字符数组、字符串 —> 字节、字节数组 字符集 ASCII:美国标准信息交换码。 用一个字节的7位可以表示。 ISO8859-1:拉丁码表。欧洲码表 用一个字节的8位表示。 GB2312:中国的中文编码表。最多两个字节编码所有字符 GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码 Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。 UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546/* 此时处理异常的话,仍然应该使用try-catch-finally InputStreamReader的使用,实现字节的输入流到字符的输入流的转换 */@Testpublic void test1() throws IOException { FileInputStream fis = new FileInputStream("dbcp.txt"); // InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集 //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集 InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ String str = new String(cbuf,0,len); System.out.print(str); } isr.close();}/* 此时处理异常的话,仍然应该使用try-catch-finally 综合使用InputStreamReader和OutputStreamWriter */@Testpublic void test2() throws Exception { //1.造文件、造流 File file1 = new File("dbcp.txt"); File file2 = new File("dbcp_gbk.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); InputStreamReader isr = new InputStreamReader(fis,"utf-8"); OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); //2.读写过程 char[] cbuf = new char[20]; int len; while((len = isr.read(cbuf)) != -1){ osw.write(cbuf,0,len); } //3.关闭资源 isr.close(); osw.close();} 其他流 标准的输入、输出流 打印流 数据流 标准的输入、输出流 System.in:标准的输入流,默认从键盘输入 System.out:标准的输出流,默认从控制台输出 System类的 setIn(InputStream is) / setOut(PrintStream ps) 方式重新指定输入和输出的流。 练习: 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作, 直至当输入“e”或者“exit”时,退出程序。 方法一:使用Scanner实现,调用next()返回一个字符串 方法二:使用System.in实现。System.in ---> 转换流 ---> BufferedReader的readLine() 12345678910111213141516171819202122232425262728public static void main(String[] args) { BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.println("请输入字符串:"); String data = br.readLine(); if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println(upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } }} 打印流PrintStream 和 PrintWriter 实现将基本数据类型的 数据格式 转化 为字符串输出,提供了一系列重载的print() 和 println()练习: 123456789101112131415161718192021222324@Testpublic void test2() { PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt")); // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区) ps = new PrintStream(fos, true); if (ps != null) {// 把标准输出流(控制台输出)改成文件 System.setOut(ps); } for (int i = 0; i <= 255; i++) { // 输出ASCII字符 System.out.print((char) i); if (i % 50 == 0) { // 每50个数据一行 System.out.println(); // 换行 } } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (ps != null) { ps.close(); } }} 数据流DataInputStream 和 DataOutputStream作用:用于读取或写出基本数据类型的变量或字符串 练习:将内存中的字符串、基本数据类型的变量写出到文件中。 注意:处理异常的话,仍然应该使用try-catch-finally. 1234567891011121314@Testpublic void test3() throws IOException { //1. DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt")); //2. dos.writeUTF("刘建辰"); dos.flush();//刷新操作,将内存中的数据写入文件 dos.writeInt(23); dos.flush(); dos.writeBoolean(true); dos.flush(); //3. dos.close();} 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。 注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致! 1234567891011121314@Testpublic void test4() throws IOException { //1. DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); //2. String name = dis.readUTF(); int age = dis.readInt(); boolean isMale = dis.readBoolean(); System.out.println("name = " + name); System.out.println("age = " + age); System.out.println("isMale = " + isMale); //3. dis.close();} 对象流 ObjectInputStream 和 ObjectOutputStream 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。 要想一个java对象是可序列化的,需要满足相应的要求。见Person.java 1234567891011/** * Person需要满足如下的要求,方可序列化 * 1.需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化) * * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量 */public class Person implements Serializable{ public static final long serialVersionUID = 475463534532L;... 序列化机制: 对象序列化机制允许把内存中的Java对象 转换成 平台无关的 二进制流,从而允许把这种 二进制流 持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768/* 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去 使用ObjectOutputStream实现 */@Testpublic void testObjectOutputStream(){ ObjectOutputStream oos = null; try { //1. oos = new ObjectOutputStream(new FileOutputStream("object.dat")); //2. oos.writeObject(new String("我爱北京天安门")); oos.flush();//刷新操作 oos.writeObject(new Person("王铭",23)); oos.flush(); oos.writeObject(new Person("张学良",23,1001,new Account(5000))); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(oos != null){ //3. try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } }}/* 反序列化:将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream来实现 */@Testpublic void testObjectInputStream(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dat")); Object obj = ois.readObject(); String str = (String) obj; Person p = (Person) ois.readObject(); Person p1 = (Person) ois.readObject(); System.out.println(str); System.out.println(p); System.out.println(p1); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } }} 随机存取文件流RandomAccessFile 直接继承于java.lang.Object类,实现了DataInput 和 DataOutput接口 既可以作为一个输入流,又可以作为一个输出流 作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。 如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖,不是覆盖文件) 可以通过相关的操作,实现RandomAccessFile “插入”数据的效果 123456789101112131415161718192021222324252627282930313233343536373839404142434445@Testpublic void test1() { RandomAccessFile raf1 = null; RandomAccessFile raf2 = null; try { //1. raf1 = new RandomAccessFile(new File("爱情与友情.jpg"),"r"); raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"),"rw"); //2. byte[] buffer = new byte[1024]; int len; while((len = raf1.read(buffer)) != -1){ raf2.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //3. if(raf1 != null){ try { raf1.close(); } catch (IOException e) { e.printStackTrace(); } } if(raf2 != null){ try { raf2.close(); } catch (IOException e) { e.printStackTrace(); } } }}@Testpublic void test2() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 raf1.write("xyz".getBytes());// raf1.close();} 使用RandomAccessFile实现数据的插入效果 12345678910111213141516171819202122@Testpublic void test3() throws IOException { RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw"); raf1.seek(3);//将指针调到角标为3的位置 //保存指针3后面的所有数据到StringBuilder中 StringBuilder builder = new StringBuilder((int) new File("hello.txt").length()); byte[] buffer = new byte[20]; int len; while((len = raf1.read(buffer)) != -1){ builder.append(new String(buffer,0,len)) ; } //调回指针,写入“xyz” raf1.seek(3); raf1.write("xyz".getBytes()); //将StringBuilder中的数据写入到文件中 raf1.write(builder.toString().getBytes()); raf1.close(); //思考:将StringBuilder替换为ByteArrayOutputStream}]]></content>
<categories>
<category>Java</category>
<category>泛型</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java-summary1]]></title>
<url>%2FJava%2F%E5%B0%8F%E7%BB%93%2FJava-summary1%2F</url>
<content type="text"><![CDATA[自用Java小结(Ⅰ)回顾一下之前学的 Java路线 CodeSheep来告诉你编程培训班到底培训什么内容和具体学习路线! 摘自CodeSheep的B站视频 第一阶段入门进阶 JAVA基本语法 OO的编程思想 集合 IO 异常 泛型 反射 多线程 函数式. 第二阶段Web基础和工具 前段基础三件套(html/css/js), jquery,ajax,jsp,cookie,session, http基础 servlet基础, Git,SVN等代码管理工具 第三阶段企业级应用框架培训 maven/gradle项目管理工具 Spring全家桶:Spring/Spring MVC/ SpringBoot(比较先进的培训有SpringBoot,而SSH基本不用报) 关系型数据库 MySQL,jdbc,MyBatis,Hibernate. 非关系数据库 Redis缓存 (也是区别重点) 模板技术: thymeleaf,freemarker 第四阶段高级应用框架培训 搜索引擎 elastic search RPC框架/微服务框架: Dubbo,Spring Cloud(区别重点) 中间件技术 RabbitMQ,RocketMQ,ActiveMQ,Kafka等. 虚拟化技术:Docker容器,k8s容器编排技术等 第五阶段高级话题 JVM优化和排错 GC分析 数据库高级优化等话题 基本概念Java语言的特点 面向对象 健壮性 跨平台性(JVM) Java两种核心机制 Java虚拟机(Java Virtual Machine) 可以理解为一个以字节码为机器指令的CPU,是解释型 垃圾回收机制(Garbage collection) 名词解释 JDK(J2SDK) & JRE SDK(Software Development kit 软件开发包) Java Runtime Environment (Java运行环境) ps: 开发需要JDK,用户只需JRE。 JDK = JRE + 开发工具集(例如Javac编译工具) JRE = JVM + Java SE标准类库 Java技术体系平台 Java SE(Java Standard Edition) 标准版 Java EE(Java Enterprise Edition)企业版 Java ME(Java Micro Edition)小型版 Java Card IDE (Integrated Development Environment) 集成开发环境 基本语法注释类型Comment 单行注释 // 多行注释 / / 文档注释 (Java特有) 1234/** @author 指定Java程序的作者 @version 指定源文件的版本 */ PS:文档注释的内容可以被javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。 12> javadoc -d myXXXdoc -author -version XXX.java> Java API 文档API (Application Programming Interface, 应用程序编程接口) 一个Java源文件只能有一个类声明public,该类名必须与文件名一致 程序入口是main() 输出语句:System.out.println() 输出数据再换行 / System.out.print() 编译会生成(多个)与源文件中类名相同的字节码文件。 关键字与保留字Java保留字: goto 、const 标识符各种变量、方法和类(凡是自己起名字的) 特点:字母,0-9,_ 或$组成,数字不开头 规范:(PS:Java 采用unicode字符集) 包名:多单词组成所有字母都小写:xxxyyyzz 类名、接口名:多单词所有单词首字母大写:XxxYyyZzz 变量名、方法名:第一个首字母小写其余首字母大写:xxxYyyZzz 常量名:所有字母大写,多单词下划线连接:XXX_YYY_ZZZ 变量是程序中最基本的储存单元。包括变量类型、变量名和储存的值。先声明后使用 数据类型: 基本数据类型(primitive type) 整数类型(byte 1, short 2, int 4, long 8) 浮点类型(float 4数值范围大必须末尾加f, double 8双精度) 字符类型(char 2 ‘ ‘) 布尔型(boolean) 引用数据类型(reference type) 类(class) 接口(interface) 数组(string[]) 基本数据类型转换(不包含boolean): 自动类型提升 byte、char、short互相 –> int –> long –> float –> double 强制类型转换 自动类型提升的逆运算,强制转换符() 1234567891011// 特殊情况1long l = 123123;//int//long ll = 123123123123123123;//float f = 12.3;//double false// 特殊情况2//整型常量默认为int,浮点型常量默认为doublebyte b = 12;byte b1 = b + 1;//int false//char c = '';//false 引用数据类型string string可以和8种基本数据类型进行运算,且只能进行连接运算: + 123456//String str1 = 123;//falseString str2 = 123 + "";//int num = str1;//false//int num = (int)str1;//falseint num = Integer.parseInt(str1); 进制 二进制(binary):以0B或0b开头,原码 补码(反码+1) 反码(符号位不变)计算机底层都以补码存储数据 十进制(decimal) 八进制(octal):以数字0开头 十六进制(hex):以0x或0X开头 运算符 算术运算符 (%结果与被模数符号相同)(++不改变本身数据 类型) 赋值运算符 (+=不改变本身数据 类型,带隐形制性转换) 比较运算符 (ps: instanceof是否是string) 逻辑运算符 (逻辑 & | ! ^ 短路 && ||) ps: & 与 && (| 与 ||类似)开发中推荐使用双 相同点:运算结果相同,当左边为true时都执行 不同点:当左边为false时,&继续执行&&短路不执行 位运算符 (<< (最高效2*8) >> (最高位0补0,1补1) >>>无符号右移(都用0补) & | ^(俩数交换) ~取反(补码各位取反))取决于数据类型 三元运算符 (? : ) ps:可以嵌套使用,凡是?:(运算效率高)都可以改写成if-else,反之不成立 [a:b] -> (int)(Math.random() * (b - a + 1) + a) 程序流程控制基本流程结构: 顺序结构 分支结构 if-else (万能) switch (执行效率更高,都可以转换成if-else,反之不成立) 循环结构 while do-while for foreace 引用数据类型变量:数组 初始化 123456789101112131415// 静态初始化int[] ids = new int[]{1,3,5,6};int[] ids = {1,3,5,6}; //类型判断int[][] ids1 = new int[][]{{3.4},{1,2,3},{5,6}};// 动态初始化int[] ids = new int[4];int[] ods = new int[3][4];String[][] arr = new String[1][2];String[] arr[] = new String[1][2];String[][] arr = new String[2][];//错误String[][] arr = new String[][2];// 数组一旦初始化完成,在内存中占有一连串地址,并且长度无法改变。 PS:默认初始化值:整型为0,浮点型0.0,char为0或‘\u000’非这个‘0’,boolean型false,引用类型为null 非“null”,多维数组外层为地址值,内层地址未指定时为null。 获取数组的长度:length属性 1System.out.println(name.length); 数组的遍历:for 123456789for (int i = 0; i < name.length; i++) { System.out.println(name[i]);}for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { System.out.println(arr[i][j]); }} 内存结构解析 栈stack:局部变量 (基本数据类型的变量,一个对象的引用,函数调用的现场保留) 堆heap:new出来的结构:对象、数组(构造器创建的对象) 方法区: 常量池 静态域 (直接的100,“hello“,常量) 1234String str = new String("hello");//str -> 栈 //new出来的对象 -> 堆 //"hello"字面量 -> 静态区s 数组中涉及的常见算法 1.数组元素的赋值(杨辉三角、回形数等)2.求数值型数组中元素的最大值、最小值、平均数、总和等3.数组的复制(需new)、反转、查找(线性查找、二分法查找(必须有序))4.数组元素的排序算法(加粗手写,斜体原理) ●选择排序:直接选择排序、堆排序●交换排序:冒泡排序O(n^2)、快速排序O(nlog2n)●插入排序:直接插入排序、折半插入排序、Shell排序(希尔)●归并排序●桶式排序●基数排序 Array工具类(操作数组的工具类Arrays.XXX)定义在java.util下 boolean equals(int[] a, int[] b) 判断两个数组是否相等 String toString(int[] a) 输出数组信息 void fill(int[] a, int val) 将指定值填充到数组之中 void sort(int[] a) 对数组进行排序 int binarySearch(int[] a, int key) 对排序后的数组进行二分法检索特定的值(注意是排序后的) 数组中常见异常 角标越界异常:ArrayIndexOutOfBoundsExcetion 空指针异常:NullPointerException *补充:eclipse快捷键 1.补全代码的声明:alt + / 2.快速修复: ctrl + 1 3.批量导包:ctrl + shift + o 4.使用单行注释:ctrl + / 5.使用多行注释: ctrl + shift + / 6.取消多行注释:ctrl + shift + \ 7.复制指定行的代码:ctrl + alt + down 或 ctrl + alt + up 8.删除指定行的代码:ctrl + d 9.上下移动代码:alt + up 或 alt + down 10.切换到下一行代码空位:shift + enter 11.切换到上一行代码空位:ctrl + shift + enter 12.如何查看源码:ctrl + 选中指定的结构 或 ctrl + shift + t 13.退回到前一个编辑的页面:alt + left 14.进入到下一个编辑的页面(针对于上面那条来说的):alt + right 15.光标选中指定的类,查看继承树结构:ctrl + t 16.复制代码: ctrl + c 17.撤销: ctrl + z 18.反撤销: ctrl + y 19.剪切:ctrl + x 20.粘贴:ctrl + v 21.保存: ctrl + s 22.全选:ctrl + a 23.格式化代码: ctrl + shift + f 24.选中数行,整体往后移动:tab 25.选中数行,整体往前移动:shift + tab 26.在当前类中,显示类结构,并支持搜索指定的方法、属性等:ctrl + o 27.批量修改指定的变量名、方法名、类名等:alt + shift + r 28.选中的结构的大小写的切换:变成大写: ctrl + shift + x 29.选中的结构的大小写的切换:变成小写:ctrl + shift + y 30.调出生成 getter/setter/构造器等结构: alt + shift + s 31.显示当前选择资源(工程 or 文件)的属性:alt + enter 32.快速查找:参照选中的 Word 快速定位到下一个 :ctrl + k 33.关闭当前窗口:ctrl + w 34.关闭所有的窗口:ctrl + shift + w 35.查看指定的结构使用过的地方:ctrl + alt + g 36.查找与替换:ctrl + f 37.最大化当前的 View:ctrl + m 38.直接定位到当前行的首位:home 39.直接定位到当前行的末位:end 面向对象Java类及类的成员: 属性、方法、构造器;代码块、内部类 类与对象面向对象程序设计的重点是类的设计,设计类就是设计类的成员 类的成员 属性:Field = 域、字段 = 成员变量 行为:Method =(成员)方法 = 函数 类和对象的使用创建类的对象 = 类的实例化 = 实例化类 调用对象的结构(属性、方法) 属性: object.field 方法: object.method() 对象的内存解析 堆(Heap)存放对象实例 栈(Stack)存储局部变量 方法区(Method Area)存储已被虚拟机加载的类信息、变量、静态变量、即时编译器编译后的代码等 理解万事万物皆对象121. 在Java语言范畴中,我们将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构。2. 涉及到Java与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。 PS:引用类型的变量,只可能储存两类值,null 或 地址值(含变量的类型) 匿名对象的使用,只能调用一次 属性属性(成员变量)VS 局部变量 相同点 定义变量格式相同,先声明后使用,且都有对应的作用域 不同点 声明的位置不同 属性:直接定义在类的{}中 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量 权限修饰符的不同 属性:可以在声明属性时,使用权限修饰符指明其权限 局部变量:不可以使用 默认初始化值的情况 属性:类的属性根据类型都有默认初始化值 局部变量:无初始化值 在内存中加载的位置 属性:堆空间(非static) 局部变量:栈空间 方法方法重载(overload)定义:在同一个类中允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。(跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系) “两同一不同”:同一个类,同一个方法名。参数列表不同。 可变个数的形参格式:数据类型 … 变量名, 必须申明在末尾,可传入参数个数为0个及以上 123456public void show(String[] strs){}public void show(String ... strs){ for(int i = 0; i < strs.length; i++) { System.out.println(strs[i]); }} 方法参数的值传递机制变量赋值: 基本数据类型:赋值的是变量所保存的数据值 引用数据类型:赋值变量所保存数据的地址值 类的成员之三:构造器(构造方法constructor)12Person p = new Person();//构建类的对象:new + 构造器 作用:创建对象;给对象进行初始化 格式:权限修饰符 + 类名(形参列表){} ps:构造器可重载,且一旦显示定义了类的构造器之后,系统不再提供默认空参构造器。(必须自己加,一个类中至少有一个构造器) 类的成员之四:代码块(初始化块)就是一对大括号,用来初始化类、对象,若有修饰,只能是static, 静态代码块 内部可以有输出语句,并随着类的加载而执行(不只是加载,最先甚至先于main方法),且只执行一次。 若多个静态代码块,按声明顺序依次执行,总优先于非静态代码块 静态代码块内只能静态的属性、方法,不能调用非静态的结构 作用:初始化类的信息(我一行写不下,应用场景如连接数据池) 非静态代码块 内部可以有输出语句,随着每次对象的创建而执行。 非静态代码块内既能静态的属性、方法,也能调用非静态的结构 调用顺序: 代码块的执行先于构造器,甚至先于main方法(由父及子,静态先行) Root的静态初始化块 Mid的静态初始化块 Leaf的静态初始化块 Root的普通初始化块 Root的无参数的构造器 Mid的普通初始化块 Mid的无参数的构造器 Mid的带参数构造器,其参数值:“” Leaf的普通初始化块 Leaf的构造器 属性赋值的相关问题:可以对属性进行赋值的位置: 默认初始化 显式初始化 构造器中初始化 有对象后,通过“对象.属性”或“对象.方法”的方法进行赋值 在代码块中进行赋值 属性赋值的先后顺序:1 -> 2 / 5 (看谁后写)-> 3 -> 4 类的成员之五:内部类类A声明在类B中,A为内部类 分类:成员内部类 (静态、非静态) VS 局部内部类(方法、代码块、构造器内) 成员内部类: 作为外部类的成员:调用外部类的结构、可以被static修饰、可以被四种权限修饰符修饰 作为一个类:内可以定义属性、方法、构造器等,可以被final修饰,可以被abstract修饰 相关使用细节: 12345678910111213141516//如何实例化成员内部类的对象://创建静态成员内部类对象Person.Dog dog = new Person.Dog();dog.show();//创建非静态成员内部类对象Person p = new Person();Person.Bird bird = p.new Person.Bird();bird.show();//如何在成员内部类中区分调用外部类的结构://方法的形参name//内部类的形参this.name//外部类的形参Person.this.name 局部内部类 的方法中,如果调用局部内部类所声明的方法中的局部变量, 要求此 局部变量 声明为 final 的(JDK8以后可以省略) 123456789101112131415161718192021//开发中局部内部类的使用://eg:返回一个实现了XXX接口的类的对象 public Comparable getComparable(){ //创建一个实现了Comparable接口的类:局部内部类 //方式一:// class MyComparable implements Comparable{// @Override// public int compareTo(Object o) {// return 0;// }// }// return new MyComparable(); //方式二: return new Comparable(){ @Override public int compareTo(Object o) { return 0; } }; } 面向对象的三个特征封装性、继承性、多态性、(抽象性) 封装与隐藏体现: 将类的属性XXX私有化private,提供公有化public方法来获取getXXX和设置属性setXXX的值。 不对外暴露的私有的方法 单例模式(将构造器私有化) 如果不希望 类 在包外被使用可以设置成缺省 目标:高内聚,低耦合PS:封装性的体现需要权限修饰符来体现。修饰 类 只能public与缺省 权限修饰符(从小到大) 修饰符 类内部 同一个包 不同包的子类 同一个工程 private √ (缺省) √ √ protect √ √ √ public √ √ √ √ 继承性inheritance好处: 减少了代码的多余,提高代码的复用性 便于功能的扩展 为之后多态性的使用,提供了前提 格式:1class A extends B{} A:子类、派生类、subclass B:父类、超类、superclass 体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。(private的属性也继承到了,只是因为封装性的影响不能直接调用) ps:Java只支持单继承和多继承,不允许多重继承,一个子类只能有一个父类。如果没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类,所有Java类都直接或间接继承了java.lang.Object类。 方法的重写(override/overwrite)定义:在子类中根据需要对父类中的方法进行改造。 应用:重写以后,当创建子类对象,调用同名方法时调用的是重写的方法。 规定:(建议:开发中直接从父类粘贴过来) 子类重写的方法名与形参列表与被重写一样 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符 ps:特殊情况,子类中不能重写父类中声明为private的方法。 返回类型: 父类被重写方法的返回类型为void,子类重写的方法只能返回void; 父类被重写方法的返回类型为基本类型,子类重写的方法只能返回相同的基本类型; 父类被重写方法的返回类型为A类型,子类重写的方法可以返回A类型或A的子类; 异常类型:子类重写的方法的异常类型不大于父类被重写的方法抛出的异常类型 特别注意:子类和父类的同名同参数的方法要么声明非static(考虑重写),要么都声明为static(不是重写,静态方法不能被覆盖) 区分重载和重写:①二者的概念:②重载和重写的具体规则③重敢:不表现为多态性重写:表现为多态性 多态性polymorphism定义:一个事物的多种形态 在Java中的体现:对象的多态性,父类的引用指向子类的对象 123Person p1 = new Man();Person p2 = new Woman();//子类的对象赋给父类的引用 多态的使用:在编译期,只能调用父类中声明的方法,在运行期,实际执行的是子类中的重写的方法。 虚拟方法调用(Virtual Method Invocation)当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 Java引用变量有两种类型:编译时类型 和 运行时类型。 调用方法时,编译看左边,执行看右边。–动态绑定 多态是运行时行为。重载是编译是就已经确定了,“早绑定”,”静态绑定”。 Bruce Eckel:”不要犯傻,如果它不是晚绑定,就不是多态。” 多态性的使用前提: 类的继承关系 方法的重写 ps: 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边) 1001 VS 1002 向下转型(使用强制类型转换符)编译时只能调用父类声明的属性和方法,如何调用子类特有的属性和方法? 使用强制类型转换。 使用强制转换时,可能出现ClassCastException的异常。 使用instanceof进行检测: 123if(a instanceof A) { } //判断对象是否是A的实例,如是返回true。 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。2.对于实例变量,则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。 其他关键字:this、super、static、final、abstract、interface、package、import与相关补充知识 this:理解为 当前对象,通常可以省略。可以调用:属性(用以区分局部变量和属性)、方法和构造器。 调用本类中指定的构造器 this(形参列表) 减少代码重复,且必须放在构造器内第一行,最多只能声明一个来调用其他构造器。 ps: import static导入指定类或接口中的静态结构:属性、方法 super:理解为 父类的,可以调用:属性、方法和构造器。 通常可以省略,当子类和父类中定义同名的属性时,需要显式的super 特殊情况, 当子类重写父类的方法时,需要调用父类中的方法时,需要显式的super super调用父类构造器:在子类构造器中显式的“super(参数列表)”,且必须声明在子类构造器中的首行 在类的构造器中,针对“super(参数列表)”和“this(参数列表)”只能二选一,构造器中没有,默认super(空参) 在类的多个构造器中,至少有一个类的构造器使用了super(参数列表),调用父类的构造器。 虽然创建子类时调用了父类的构造器,只是创建了一个对象。 static:某些特定的数据在内存中只有一份。eg:每个中国人都共享中国这个国籍。 修饰:属性、方法、代码块、内部类(注意构造器不行) 修饰属性:静态变量(类变量) 属性按是否有static修饰又分为:静态属性 VS 非静态属性(实例变量) 实例变量:当创建类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改某一个对象的静态属性时,不会导致其他对象中同样的属性值的修改。 静态属性:创建类的多个对象,每个对象都共享同一个静态属性。当修改某一个对象的静态属性时,会导致其他对象调用此静态变量时,属性值是修改的。 PS:静态变量(类变量)随类的加载而加载,且早于对象的加载。因此可以通过”类.静态变量“进行调用。由于类只会加载一次 ,则静态变量在内存中也只会存在一份。 举例:System.out / Math.PI 修饰方法:静态方法 可以通过”类.静态方法“进行调用 静态方法只能调用静态的方法或属性,非静态则都可以。 在静态方法中,不能使用this,super关键字 static应用场景: 属性是可以被多个对象共享,不会随着对象的不同而改变 操作静态属性的方法,设置成static;工具类中的方法,习惯上声明为static final:可以修饰的结构:类、方法、变量 修饰类:此类不能被其他类继承。eg:String类、System类、StringBuffer类。 修饰方法:此方法不能被重写。eg:Object类中的getClass()。 修饰变量:此“变量”被称为一个常量。 修饰属性,之后(必须初始化)可以考虑的赋值位置有:显式初始化、代码块中初始化、构造器中初始化、不可以在方法中赋值(因为未调用)。 修饰局部变量:常量不可修改,尤其修饰形参时,表明此形参是常量,方法内不能重新赋值。 static final 修饰属性:全局常量 abstract:抽象类与抽象方法修饰的结构:类、方法 修饰类:抽象类,不可实例化,类中一定要构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)开发中,都会提供抽象类的子类。让子类对象实例化,完成相关的。 修饰方法:抽象方法只有方法的声明,没有方法体。包含抽象方法的类一定是一个抽象类,抽象类中可以没有抽象方法。若子类重写了父类的所有的抽象方法后,此子类方可实例化。若未重写,则该子类也是一个抽象类,需要abstract修饰。(子类必须重写后才可实例化) PS:abstract不可以用来修饰属性、构造器等。abstract不能修饰私有方法、静态方法、final的类与方法。 抽象类的匿名子类 12345678910111213141516171819202122Worker worker = new Worker();method1(worker);//非匿名的类非匿名对象method1(new Worker());//非匿名的类匿名的对象Person p = new Person(){ @override public void eat() { } @override public void breath() { }}//匿名子类的对象:pmethod1(new Person(){ @override public void eat() { } @override public void breath() { }});//匿名子类的匿名对象 --- 省事 接口(interface) 与类并列的结构,一定程度解决类的单继承性的缺陷。本质是契约、规范、标准。(JDK7及以前)接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义。 继承是一个“是不是”的关系,而接口实现的是“能不能”的关系。(例如学生和运动员都能学习) 接口中的成员: JDK1.7及以前,只能定义全局变量和抽象方法(默认缺省也是) 全局变量:public static final的 抽象方法:public abstract的 JDK8,除了定义以上之外,还可以定义静态static方法、默认default方法。 接口中不能定义构造器!意味着接口不能实例化 Java开发中,接口通过让类去实现(implement)的方法来使用(面向接口编程),若实现类覆盖了接口的所有的抽象方法后,此实现类方可实例化。若未重写,则该实现类也是一个抽象类。 Java类可以实现多个接口(多实现),弥补类的单继承性的局限性。 12//先写extends再写implementsclass AA extends BB implements CC,DD,EE 接口与接口之间可以继承,而且可以多继承。 接口的具体使用,体现了多态性、 Java8中关于接口的改进 接口中定义的静态方法,只能通过接口来调用(像工具类) 通过实现类的对象,可以调用接口中的默认方法。 类优先原则:若子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法(属性bushi),那么子类在没有重写此方法的情况下,默认调用的是父类的同名同参数的方法。 接口冲突:若实现类实现了多个接口,而多个接口都定义了同名同参数的默认方法,在实现类没有重写的情况下,报错。因此实现类必须重写此方法。 如何在子类(实现类)的方法中调用父类、接口中被重写的(默认)方法: 1234method();//调用自己重写的方法super.method();//调用父类中声明的Interface1.super.method();//调用接口中的默认方法Interface1.method();//直接调用接口中的静态方法 接口的应用: 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问,另一个博文(中介,歌手经纪人)应用场景: 安全代理 远程代理 延迟加载 分类: 静态代理(静态定义代理类) 动态代理(动态生存代理类) 12345678910111213141516171819202122232425262728293031323334353637//接口的应用:代理模式 举例public class NetWorkTest { public static void main(String[] args) { Server server = new Server();// server.browse(); ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse(); }}interface NetWork{ public void browse();}//被代理类class Server implements NetWork{ @Override public void browse() { System.out.println("真实的服务器访问网络"); }}//代理类class ProxyServer implements NetWork{ private NetWork work; public ProxyServer(NetWork work){ this.work = work; } public void check(){ System.out.println("联网之前的检查工作"); } @Override public void browse() { check(); work.browse(); }} 工厂模式 Factory 创建者和调用者分离。 补充JavaBean是一种Java语言写成的可重用组件,符合以下标准的Java类: 类是公共的 有一个无参的公共的构造器 有属性,且有对应的get、set方法 Debug调试设置断点 操作 作用 step into 跳入(f5) 进入当前行所调用的方法中 step over 跳过(f6) 执行完当前行的语句,进入下一行 step return 跳回(f7) 执行完当前行所在的方法,进入下一行 drop to frame 回到当前行所在方法的第一行 resume恢复 执行完当前行所在断点的所有代码,进入 下一个断点,如果没有就结束 Terminate 终止 停止 JVM, 后面的程序不会再执行 JUnit单元测试方法 选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步 创建Java类 (要求:①此类是public ②此类提供公共空参构造器)进行单元测试 在此类中声明单元测试方法 要求权限为 public,没有返回值 且 没有形参 PS:需要声明注释@Test,并导入包import org.junit.Test; 左键双击 单元测试方法名,右键:run as - JUnit Test 123456789import org.junit.Test;public class JunitTest { @Test public void test1() { }} 若执行结果无异常为绿色,异常为红。 Object类:只定义了一个空参构造器 方法: clone() / getClass() / finalize() /hashCode() / wait() / notify() / notifyAll() / equals() / toString() == VS equals() == 是运算符,可以用于 基本数据 与 引用类型变量,前者比较保存的数据是否相同(不一定要类型相同,但必须一致,否则编译不通过),后者比较地址值是否相同,是否引用指向同一个对象 equals() 是一个方法,只能用于 引用数据类型变量 的比较,*object类中定义的equals() 和 == 的作用是相同的*(未重写)。像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后比较的是两个对象的实体内容是否相同。若自己定义的类也要有这样的功能,比较对象的实体内容,应该重写equals()方法。(equals()建议反着写,比如:”反着来”.equals(str) ,这样可以避免str可能是空指针的情况!) 12345678910111213141516//重写的原则,比较两个对象的实体内容是否相同(举例:name和age)@Overridepublic boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof XXX) { XXX xxx = (XXX)obj; return this.age == xxx.age && this.name.equals(xxx.name); } else { return false; }}//实际使用自动生成 和setter、getter一样 toString()方法 输出一个 引用变量 时,实际上输出的是对象的toString() 像String、Date、File、包装类等都重写了Object类中的toString()方法,返回“实体内容”信息 自定义类也可以重写该方法。 main()方法 作为程序的入口 也是一个普通的静态方法(只能调用静态属性方法,不然造类:通过实例化类对象调用普通属性与方法) 可以作为与控制台交互的方式(java xxxDemo “str”)因为有参数String[] args 包装类(Wrapper)的使用 针对八种基本数据类型定义相应的引用类型-包装类(封装类),具备类的特征,就可以调用类中的方法,实现真正的面向对象。 | 基本数据类型 | 包装类 || ———— | ————- || byte | Byte || short | Short || int | Integer || long | Long || float | Float || double | Double || boolean | Boolean || char | Character | Byte Short Integer Long Float Double 父类为Number 基本数据类型、包装类、String三者的相互转换 包装类 -> 基本数据类型:调用包装类的xxxValue() 基本数据类型 -> 包装类:调用包装类的构造器new() 基本数据类型、包装类 -> String类型:调用String重载的valueOf(Xxx xxx) 12345int num1 = 10;//方式一:连接运算String str1 = num1 + "";//方式二:调用String重载的valueOf(Xxx xxx)String str2 = String.valueOf(num1); String类型 -> 基本数据类型、包装类:调用包装类的parseXxx() 12String str3 = "1234";int num3 = Integer.parseInt(str3); 自动装箱与拆箱(JDK5.0以后) 1234int num1 = 10;Integer in1 = num1; //自动装箱int num3 = in1;//自动拆箱 1234567891011121314151617181920212223242526// 关于包装类的比较迷惑的问题Object o1 = true ? new Integer(1) : new Double(2.0);System.out.println(o1); //1.0 提升了!!Object o2;if (true) { o2 = new Integer(1);} else { new Double(2.0);}System.out.println(o1); //1Integer i = new Integer(1);Integer j = new Integer(1);System.out.println(i == j); //falseInteger m = 1;Integer n = 1;System.out.println(m == n); //true// Integer内部定义了IntegerCache结构,其中定义了Integer[]//保存了-128~127,如果使用自动装箱时,直接调用。//128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的,提高效率Integer x = 128;Integer y = 128;System.out.println(x == y); //false 设计模式是在大量实践中总结和理论化后优选的代码结构、编程风格以及解决问题的思考方式。“套路” 单例(Singleton)设计模式只能存在一个对象实例,好处减少了系统性能的开销(注意:学习时由static延申) 实现一:饿汉式 (线程安全) 私有化类的构造器 内部创建类的对象(private static) 提供公共的方法(public static),返回类的对象 1234567private Bank(){ }private static Bank instance = new Bank();public static Bank getInstance() { return instance;} 实现二:懒汉式 (延迟对象的创建) 私有化类的构造器 声明当前类对象(private static),没有初始化null 声明public、static的返回当前类对象的方法 12345678910private Order(){ }private static Order instance = null;public static Order getInstance() { if(instance == null) { instance = new Order(); } return instance;} 饿汉式 VS 懒汉式 区别:懒汉式好处延迟对象的创建,饿汉式坏处,对象加载时间太长,但其是线程安全的 使用场景: 网站的计数器 应用程序的日志应用 数据库连接池 读取配置文件的类 Application也是单例的典型应用 Windows的Task Manager(任务管理器) Windows中的Recycle Bin(回收站) 模板方法设计模式(TemplateMethod)抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展、改造,但子类总体上会保留抽象类的行为方式。 解决的问题: 当功能内部一部分实现是确定的,一部分实现是不确定的。这是可以把不确定部分暴露出来,让子类去实现。 在软件开发中实现一个算法时,整体步骤很确定、通用,这些步骤在父类中写好,但部分易变,可以将该部分抽象出来,供不同子类去实现。 MVC设计模式模型层 model:主要处理数据 数据对象封装: model.bean/domain 数据库操作类: model.dao 数据库: model.db 视图层 view: 显示数据 相关工具类: view.utils 自定义view: view.ui 控制层 controller: 处理业务逻辑 应用界面相关: controller.activity 存放fragment: controller.fragment 显示列表的适配器: controller.adapter 服务相关的: controller.service 抽取的基类: controller.base 异常处理概述与体系结构开发过程中的 语法错误 和 逻辑错误 不是异常。 执行过程中出现的异常分为两类: Error:Java虚拟机无法解决的严重问题。eg:StackOverflowError 栈溢出 和 OutOfMemoryError 堆溢出。一般不编写针对性的代码进行处理。 Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。又分为编译时异常(受检checked异常) VS 运行时异常(非受检unchecked异常,RuntimeException) 异常处理机制抓抛模型: “抛”:程序在正常执行中,一旦出现异常就会在代码处生成一个对应异常类的对象,并抛出。之后的代码不再执行。 关于异常对象的产生: 系统自动生成的异常对象 手动的生成一个异常对象,并抛出(throw) 12throw new RuntimeException("nnn");//自身并不输出nnn,被catch后.getMessage才有 ‘’抓“:可以理解为异常的处理方式,如下两种方式: ① try-catch-finally123456789101112try{ //可能会出现异常的代码} catch(异常类型1 变量名1) { //处理异常的方法1} catch(异常类型2 变量名2) { //处理异常的方法2}...finally{ //一定会执行的代码}// 类似直接上药 123456789101112131415161718@Testpublic void test1() { String str = "123"; str = "abc"; try { int num = Integer.parseInt(str); System.out.println("hello---1"); //未运行 } catch(NumberFormatException e) { System.out.println("数值转换异常啦。"); System.out.println(e.getMessage()); //e.printStackTrace(); } System.out.println(str + "hello");}//output:// 数值转换异常啦。// For input string: "abc"// abchello 常用异常对象处理的方式: ①String getMessage() ②printStackTrace() PS: catch中的异常类型 若无子类父类关系 ,无需考虑声明的先后顺序;若有,子类必须声明在父类的上面,否则报错(类似if和switch break)。 在try中声明的变量,在大括号外不能调用。try-catch-finally可以嵌套。 finally是可选的,其中声明的是一定会执行的代码,即便catch中又出现了异常,try或catch中有return语句等情况。 finally重要应用:像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,需要收到编写代码进行资源的释放,此时代码就需要编写在finally中。 体会: 使用try-catch-finally处理编译时异常,是使得程序在编译时不再报错,但在运行时仍可能报错。“延迟”到运行时 开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally,针对编译时异常,一定要考虑异常的处理。 ② throws + 异常类型(喊人通报,未解决)声明在方法的声明处,指明此方法执行时,可能回抛出的异常类型,一旦方法体执行时出现异常,仍会在异常处生成一个异常类的对象。此对象满足throws后的异常类型时,就会被抛出,之后的代码就不再执行。 如何选择 若父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着若子类重写的方法有异常必须使用try-catch-finally 执行的方法a中,先后调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws的方式处理(层层向上报),而执行的方法a可以考虑使用try-catch-finally 自定义异常类 继承现有的异常结构:RuntimeException、Exception 提供全局常量:serialVersionUID 提供重载的构造器 (记得要搭配throw手动抛出) 123456789101112public class MyException extends RuntimeException{ static final long serialVersionUID = -70348971766939L; public MyException() { super(); } public MyException(String message) { super(message); }} throw VS throws throw表示抛出一个异常类对象,生成异常对象的过程。声明在方法体内。 throws属于异常处理的一种方式,声明在方法的声明处。 程序、进程、线程基本概念 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期PS:程序是静态的,进程是动态的进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。(方法区和堆) 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。 何时需要多线程 程序需要同时执行两个或多个任务。 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。 需要一些后台运行的程序时。 多线程的创建方法一:继承于Thread类12345678910111213141516171819202122232425262728293031/** *多线程的创建,方法一:继承于Thread类 * 1.创建一个继承于Thread类的子类 * 2.重写Thread类的run() -> 将此线程执行的操作声明在run()中 * 3.创建Thread类的子类对象 * 4.通过此对象调用start(): ①启动当前线程;②调用当前线程的run() * * PS: 1.不能直接调用run()启动线程 * 2.要运行多个线程需要造多个对象(不可以让已经start()的线程去执行,会报IllegalThread) * * @author goodwell * @create 2019-09-25-9:39 */class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}public class ThreadTest{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); }} 创建Thread类的匿名子类的方法 12345678910new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}.start(); Thread类中的常用方法: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263/** * 测试Thread中的常用方法 * 1. start() 启动线程;调用当前线程的run() * 2. run() 通常需要重写Thread类中的方法,将创建的线程要执行的操作声明在此方法 * 3. currentThread() 静态方法,返回执行当前代码的线程 * 4. getName() 获取当前线程的名字 * 5. setName() 设置当前线程的名字 * 6. yield() 释放当前线程cpu的执行权 * 7. join() 在线程a中调用了线程b的join(),此时线程a进入阻塞状态,直到b完全执行完,a才结束阻塞状态。 * 8. sleep(long millitime) 让当前线程“睡眠”millitime 单位为ms,此时为阻塞状态 * 9. isAlive() 判断当前线程是否存活 * * @author goodwell * @create 2021-03-09 19:15 */class HelloThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + i); }// if (i % 20 == 0) {// yield();// } } } //直接构造器命名 public HelloThread(String name) { super(name); }}public class ThreadMethodTest { public static void main(String[] args) { HelloThread hiThread =new HelloThread("Thread:1");// hiThread.setName("线程一"); hiThread.start(); // 主线程命名 Thread.currentThread().setName("主线程"); for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } if (i == 0) { try { hiThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } }} 方法二:实现Runnable接口 创建一个实现了Runnable接口的类 实现类 去 实现Runnable中的抽象方法:run() 创建 实现类 的对象 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 通过Thread类的对象调用start() 12345678910111213141516171819202122232425/** * 创建多线程方法二 * * @author goodwell * @create 2019-09-25-16:13 */class MThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}public class ThreadTest1 { public static void main(String[] args) { MThread mThread = new MThread(); Thread t1 = new Thread(mThread); t1.start(); }} 两种创建方式的对比开发中,优先选择实现Runnable接口的方式 原因: 实现的方式没有类的单继承的局限性 实现的方式更适合处理多个线程有共享数据的情况 联系: 1public class Thread implements Runnable 相同点:都需要重写run(),将线程执行的逻辑声明在run()中 方法三:实现Callable接口 创建一个Callable的实现类 实现call方法,将线程需要执行的操作声明在call()中 创建callable实现类的对象 将此对象传递到FutureTask构造器中,创建FutureTask的对象 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() 获取Callable中call方法的返回值 使用Runnable VS Callable如何理解与使用Runnable相比, Callable功能更强大些 相比run()方法,可以有返回值 方法可以抛出异常,被外面操作捕获,得到异常信息 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果 Future接口 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 FutureTask 同时实现了 Runnable, Future 接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值 12345678910111213141516171819202122232425262728293031323334353637// 1. 创建一个Callable的实现类class NumThread implements Callable { //2. 实现call方法,将线程需要执行的操作声明在call()中 @Override public Object call() throws Exception{ int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); sum += i; } } return sum; }}public class CallTest { public static void main(String[] args) { // 3. 创建callable实现类的对象 NumThread numThread = new NumThread(); // 4. 将此对象传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); // 5. 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() new Thread(futureTask).start(); Object sum = null; try { // 6. get()获取Callable中call方法的返回值 sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }} 方法四:使用线程池背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前 创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 步骤: 提供指定线程数量的线程池 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 关闭连接池 好处: 提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止 1234567891011121314151617181920212223242526272829class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ": " + i); } } }}public class ThreadPool { public static void main(String[] args) { // 1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //↑是接口,↓是类 System.out.println(service.getClass());// ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; // 设置线程池的属性// service1.setCorePoolSize(15);// service1.setKeepAliveTime(); // 2. 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 service.execute(new NumberThread()); // 适用于Runnable// service.submit(Callable callable); // 适用于Callable // 3. 关闭连接池 service.shutdown(); }} 线程的生命周期 线程的同步举例问题:卖票过程中,出现了重票、错票 –> 出现了线程的安全问题,通过同步机制来解决 方式一:同步代码块123synchronized(同步监视器){ //需要同步的代码} 说明:操作共享数据的代码,及需要被同步的代码;共享数据,多个线程共同操作的变量;同步监视器,俗称:锁,任何一个类的对象都可以当锁,runnable实现类可以用this,或者用类(Xxxx.class)。(要求:多个线程必须要共同的一把锁) 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充同步监视器;在继承Thread类创建多线程的方式中,慎用this充当同步监视器考虑使用当前类作为锁Xxxx.class 同步的优缺点: 好处:解决了线程的安全问题 局限性:操作同步代码时,只能有一个线程参加,其他线程等等待,效率较低。 方法二:同步方法如操作共享数据的代码完整的声明在一个方法中。 总结: 同步方法仍然涉及到同步监视器,不需要显式的声明。 非静态的同步方法,同步监视器是:this;静态的同步方法,同步监视器是:当前类本身。 线程安全的饿汉式单例模式: 1234567891011121314151617181920class Bank{ private Bank(){}; private static Bank instance = null; private static Bank getInstance(){ //方式一:效率较差// synchronized (Bank.class) {// if (instance == null) {// instance = new Bank();// }// return instance;// } //方式二:效率更高 if (instance == null) { synchronized (Bank.class) { instance = new Bank(); } } return instance; }} 线程的死锁问题死锁的理解: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。 说明: 出现死锁后,不会出现异常、提示,只是所有的线程都处于阻塞状态,无法继续 使用同步的时候,要避免出现死锁。 方式三:lock锁 — JDK5.0新增 实例化ReentrantLock 调用lock 调用解锁方法:unlock 1234567891011121314151617181920212223242526272829303132class Window implements Runnable{ private int ticket = 100; //1.实例化Reentrantlock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { //2.调用lock lock.lock(); if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + i); ticket--; } else { break; } } finally { //3.调用解锁方法:unlock lock.unlock(); } } }} synchronized VS Lock 异同 相同:二者都可以解决线程安全问题 不同:synchronized机制在执行完相应的同步代码后,自动释放同步监视器;Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock()) 优先使用顺序:Lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外) 线程的通信相关方法: wait():一旦执行,当前线程就进入阻塞状态,并释放同步监视器 notify():唤醒被wait的一个线程,若有多个线程被wait,就唤醒优先级最高的 notifyAll():唤醒所有被wait的线程 说明: 此三个方法必须使用在同步代码块中 其调用者必须是 同步代码块中 的同步监视器,否则会出现IllegalMonitorStateException异常 都是定义在java.lang.Object类中 sleep() VS wait() 异同: 同:一旦执行,都可以是当前的线程进入阻塞状态 异: 方法声明的位置不同:Thread类中声明sleep,Object类中声明wait 调用的要求不同:sleep可以在任何场景下调用,wait必须使用在同步代码块中 是否释放同步监视器:若都使用在同步代码块或同步方法中,sleep不会释放锁,wait会释放锁 字符串相关的类String字符串是常量,用双引号引起来,他们的值在创建后就不能改变。 String对象的 字符串内容 是 存储在一个 字符数组final char[] value中。 特点 实现了 Serializable 接口,表示字符串支持序列化的; 实现了 Comparable 接口,表示String可以比较大小 String是一个final类,不可被继承,其代表不可变的字符序列。(不可变性) 体现: 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当对现有的字符串进行拼接时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当调用String的replace()修改字符串,也需要重新指定内存区域赋值。 12public final class String implements java.io.Serializable, Comparable<String>, CharSequence 通过字面量的方式(区别于new方式)给一个字符串赋值,此时的 字符串值 声明在字符串常量池中,字符串常量池 不会重复储存相同的 字符串。 实例化的方式 通过字面量定义 通过new + 构造器 面试题: 12345String s = new String("abc");//此方式创建对象,在内存中创建了几个对象? 俩:一个堆空间中new结构,另一个是char[]对应的常量池中的数据“abc”String s1 = new String("abc");String s2 = new String("abc");System.out.println(s1 == s2); //false// 先造了对象在堆中,对象的value指向常量池 123456789101112131415161718192021222324252627282930public class StringTest { @Test public void testString(){ String s1 = "hello"; String s2 = "goodwell"; String s3 = "hellogoodwell"; String s4 = "hello" + "goodwell"; String s5 = s1 + "goodwell"; String s6 = "hello" + s2; String s7 = s1 + s2;//1. System.out.println(s3 == s4);//true//2. System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s3 == s7);//false System.out.println(s5 == s6);//false System.out.println(s6 == s7);//false String s8 = s5.intern(); //返回值得到的s8使用的常量值已经存在的“hellogoodwell” //3. System.out.println(s3 == s8);//true final String s9 = "hello"; String s10 = s9 + "goodwell";//4. System.out.println(s3 == s10);//true } 结论: 常量与常量的 拼接结果 在常量池。且常量池中 不会存在相同内容的常量。 只要拼接的其中有一个是变量,结果就在堆中(类似new) 如果拼接的结果 调用intern()方法,返回值就在常量池中 final String(也在常量池中)和字面量连接,结果在常量池中 1234567891011121314151617public class StringTest { String str = new String("good"); char[] ch = {'t','e','s','t'}; public void change(String str, char ch[]) { str = "test ok"; ch[0] = 'b'; } public static void main(String[] args) { StringTest ex = new StringTest(); ex.change(ex.str, ex.ch); System.out.println(ex.str);//good //不可变性 System.out.println(ex.ch);//best }} 常用方法 int length() :返回字符串的长度: return value.length char charAt(int index): : 返回某索引处的字符return value[index] boolean isEmpty() :判断是否是空字符串:return value.length == 0 String toLowerCase() :使用默认语言环境,将 String 中的所有字符转换为小写 String toUpperCase() :使用默认语言环境,将 String 中的所有字符转换为大写 String trim(): :返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj): :比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString) :与equals方法类似,忽略大小写 String concat(String str) :将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString): :比较两个字符串的大小 String substring(int beginIndex): :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 String substring(int beginIndex, int endIndex) : :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。 boolean contains(CharSequence s) :当且仅当此字符串包含指定的 char 值序列时,返回 true int indexOf(String str): :返回指定子字符串在此字符串中第一次出现处的索引 int indexOf(String str, int fromIndex): :返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 int lastIndexOf(String str): :返回指定子字符串在此字符串中最右边出现处的索引 int lastIndexOf(String str, int fromIndex): :返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索注:indexOf和lastIndexOf方法如果未找到都是返回-1 boolean endsWith(String suffix): :测试此字符串是否以指定的后缀结束 boolean startsWith(String prefix): :测试此字符串是否以指定的前缀开始 boolean startsWith(String prefix, int toffset): :测试此字符串从指定索引开始的子字符串是否以指定前缀开始 替换 String replace(char oldChar, char newChar): :返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement): :使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 String replaceAll(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 String replaceFirst(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 匹配 boolean matches(String regex): :告知此字符串是否匹配给定的正则表达式。 切片 String[] split(String regex): :根据给定正则表达式的匹配拆分此字符串。 String[] split(String regex, int limit): :根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。 类型转换String 与 char[] 之间的转换: String –> char[] 调用String的toCharArray() 1char[] charArray = str1.toCharArray(); char[] –> String 调用String的构造器 1String str2 = new String(arr); String 与 byte[] 之间的转换: String –> byte[] 调用String的getBytes() 1byte[] bytes = str1.getBytes(); //使用默认的字符集进行编码 byte[] –> String 调用String的构造器 12String str2 = new String(bytes);//使用默认的字符集进行解码//说明:解码时,要求解码使用的字符集必须和编码时使用的字符集一致,否则出现乱码 StringBuffer类 java.lang.StringBuffer代表 可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。很多方法与String相同。作为参数传递时,方法内部可以改变值。 StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器: StringBuffer() :初始为 容量为16 的字符串缓冲区 StringBuffer(int size) :构造 指定容量的字符串缓冲区 StringBuffer(String str) :将内容初始化为指定字符串内容 StringBuffer 类的常用方法 StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接(啥都变成字符串,例如“null”) StringBuffer delete(int start,int end):删除指定位置的内容 StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把当前字符序列逆转 public int indexOf(String str) public String substring(int start,int end) public int length() public char charAt(int n ) public void setCharAt(int n ,char ch) ## StringBuilder类 StringBuilder和StringBuffer 非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样 对比String 、StringBuffer 、StringBuilder String(JDK1.0):不可变字符序列 StringBuffer(JDK1.0):可变字符序列、效率低、线程安全 StringBuilder(JDK 5.0):可变字符序列、效率高、线程不安全,底层都是使用char[] 存储 注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。 12345678String str = null;//System.out.println(str.length());//NullPointerExceptionStringBuffer sb = new StringBuffer();sb.append(str);System.out.println(sb.length());//4System.out.println(sb);//"null"//StringBuffer sb1 = new StringBuffer(str);//System.out.println(sb1);//NullPointerException 源码分析 12345678910111213String str = new String();// char[] value = new char[0];String str1 = new String("abn");// char[] value = new char[]{'a','b','n'};StringBuffer sb1 = new StringBuffer();// char[] value = new char[16];sb1.append('a');// value[0] = 'a';sb1.append('b');// value[1] = 'b';StringBuffer sb2 = new StringBuffer("abc");// char[] value = new char["abc".length + 16]{'a','b','c's};//问题一:System.out.println(sb2.length());// 3//问题二://扩容问题:若添加的数据底层数组装不下,那就需要扩容底层的数组;默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。 指导建议: 开发中使用,StringBuffer(int capacity) 或 StringBuilder(int capacity) 指定容量的 对比三者效率: StringBuilder > StringBuffer > String 总结: 增:append(xxx) 删:delete(int start, int end) 改:setCharAt(int n, char ch) / replace(int start, int end, String str) 查:charAt(int n) 插:insert(int offset, xxx) 长度:length() *遍历:for() + charAt() / toString() 时间相关的类JDk8之前的日期和时间的API①. java.lang.System类System.currentTimeMillis(): 返回当前时间与1970年1月1日0时0分0秒之间 以毫秒为单位的时间差,也称为时间戳 ②. java.util.Date (java.sql.Date继承前者)构造器: Date() 创建一个对应当前时间的Date对象 //Date(int year, int month, int day) 创建一个对应时间的Date对象 Date(long) 创建指定毫秒数的Date对象 方法: tiString() 显示当前年月日 getTime() 获取当前Date对象对应的时间戳 java.sql.Date 对应着数据库中的日期类型的变量 将java.util.Date对象转换成java.sql.Date对象 1java.sql.Date dateSql = new java.sql.Date(dateUtil.getTime()); 1234567891011121314151617181920212223242526272829303132333435363738394041424344public class TimeTest { //1.System类中的currentTimeMillis() @Test public void test11(){ long time = System.currentTimeMillis(); System.out.println(time); //1569513273409返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 } //2.java.util.Date类(java.sql.Date类)// ①两个构造器的使用// > Date() 创建一个当前时间的Date对象// > Date(long) 创建指定毫秒数的Date对象// ②两个方法的使用// > toString() 显示当前的年、月、日、时、分、秒// > getTime() 获取当前Date对象对应的毫秒数(时间戳)// ③java.sql.Date类对应着数据库中的日期类型的变量// > 如何实例化// > util.Date --getTime()-> sql.Date对象// >< sql.Date --> util.Date对象(不需要转,学生本身就是人) @Test public void test22(){ //构造器一:Date() 创建一个当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString());//Fri Sep 27 00:50:46 CST 2019 System.out.println(date1.getTime());//1569516806737 //构造器二:Date(long) 创建指定毫秒数的Date对象 Date date2 = new Date(1569516806737L); System.out.println(date2.toString());//Fri Sep 27 00:53:26 CST 2019 //创建java.sql.Date对象 java.sql.Date date3 = new java.sql.Date(1569516806737L); System.out.println(date3);//2019-09-27有无.toString()一样 //sql.Date --> util.Date对象 Date date8 = date3; System.out.println(date8.toString()); //util.Date --getTime()-> sql.Date对象 //情况1 Date date4 = new java.sql.Date(1569516806737L); java.sql.Date date5 = (java.sql.Date) date4; //情况2 Date date6 = new Date(); java.sql.Date date7 = new java.sql.Date(date6.getTime()); }} ③. SimpleDateFormat类java.text.SimpleDateFormat() 不与语言环境有关的方式来对Date类的格式化和解析的具体类 构造器: SimpleDateFormat() 默认的模式和语言环境创建对象 SimpleDateFormat(String Pattern) 用参数pattern指定的格式创建一个对象。 该对象可以 格式化: 日期 -> 文本 String format(Date date) 解析:文本 -> 日期 Date parse(String source) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class DateTimeTesr { /* SimpleDateFormat的使用:对日期Date类的格式化和解析 1.两个操作 - 格式化: 日期 ---> 字符串 - 解析: 字符串 ---> 日期 2.SimpleDateFormat的实例化 */ @Test public void testSimpleDateFormat(){ //实例化SimpleDateFormat:使用默认构造器 SimpleDateFormat sdf = new SimpleDateFormat(); //格式化: 日期 ---> 字符串 Date date = new Date(); System.out.println(date); String format = sdf.format(date); System.out.println(format); //解析: 字符串 ---> 日期 String str = "19-9-27 下午8:12"; Date date1 = null; try { date1 = sdf.parse(str); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date1); // 按照指定的方式格式化和解析,调用带参数的构造器// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa"); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //格式化 String format1 = sdf1.format(date); System.out.println(format1);//2019-09-27 08:22:08 //解析 Date date2 = null; try { date2 = sdf1.parse("2019-09-27 08:22:08"); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date2); }} 1234567891011121314151617public void test12() throws ParseException { /* 练习一: 字符串“2020-09-08”抓换成java.sql.Date 练习二: “三天打鱼两天晒网” 1990-01-01 xxxx-xx-xx 打鱼?晒网? */ String birth = "2020-09-08"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf1.parse(birth);// System.out.println(date); java.sql.Date birthDate = new java.sql.Date(date.getTime()); System.out.println(birthDate); //思路:总天数%5==? //方式一:( date2.getTime() - date1.getTime() ) / (1000 * 60 * 60 * 24) + 1 //方式二:时间分段整数年加闰年} ④. Calendar类是一个抽象基类,主要用于完成日期之间相互操作的功能。 1.实例化 方式一:创建其子类(GregorianCalendar)的对象 方式二:调用其静态方法getInstance() 2.常用方法 get() set() 可变性 add() getTime() 日历类 –> Date setTime() Date –> 日历类 注意: 获取月份时:一月是0 … 十二月是11 获取星期时:周日是1 … 周六是7 12345678910111213141516171819202122232425262728293031323334353637/*Calendar类(抽象类)的使用 */@Testpublic void testCalendar(){ // 1.实例化 // 方式一:创建其子类(GregorianCalendar)的对象 // 方式二:调用其静态方法getInstance() Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass()); // 2.常用方法 // get() int days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28 System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//271 // set() 可变性 calendar.set(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//22 // add() calendar.add(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//14 // getTime() 日历类 --> Date Date date = calendar.getTime(); System.out.println(date); // setTime() Date --> 日历类 Date date1 = new Date(); calendar.setTime(date1); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28} JDK 8中新日期时间APIjava.time1234567891011121314151617181920212223242526272829303132333435363738394041/*LocalDate LocalTime LocalDateTime 的使用 类似于Calendar() */@Testpublic void testTime(){ //1. now() 获取当前时间日期 LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate);//2019-09-28 System.out.println(localTime);//09:19:28.307 System.out.println(localDateTime);//2019-09-28T09:19:28.307 //2. of() 设置指定的年月日时分秒,没有偏移量 LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 10, 0, 0, 0); System.out.println(localDateTime1);//2020-10-10T00:00 //getXxx() 获取相关属性 System.out.println(localDateTime.getDayOfMonth());//28 System.out.println(localDateTime.getDayOfWeek());//SATURDAY System.out.println(localDateTime.getDayOfYear());//271 System.out.println(localDateTime.getHour());//9 System.out.println(localDateTime.getMonthValue());//9 //withXxx() 修改、设置 不同于Calendar() 体现不可变性 LocalDate localDate1 = localDate.withDayOfMonth(22); System.out.println(localDate);//2019-09-28 System.out.println(localDate1);//2019-09-22 //plusXxx() 增加 LocalDateTime localDateTime2 = localDateTime.plusMonths(4); System.out.println(localDateTime);//2019-09-28T09:29:43.638 System.out.println(localDateTime2);//2020-01-28T09:29:43.638 //minusXxx() 减去 LocalDateTime localDateTime3 = localDateTime.minusDays(3); System.out.println(localDateTime);//2019-09-28T09:32:06.256 System.out.println(localDateTime3);//2019-09-25T09:32:06.256} Instant瞬时 12345678910111213141516171819202122/*Instant 的使用类似于Date */@Test public void testInstant(){ //now() 获取本初子午线对应的标准时间 Instant instant = Instant.now(); System.out.println(instant); //2019-09-28T01:42:23.739Z //添加时间的偏移量 OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); //2019-09-28T09:42:23.739+08:00 //toEpochMilli() 获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 --> Date类的getTime() long milli = instant.toEpochMilli(); System.out.println(milli);//1569638022547 //ofEpochMilli() 通过给定的毫秒数,获取Instant实例 --> Date(long millis) Instant instant1 = Instant.ofEpochMilli(11111111L); System.out.println(instant1);//1970-01-01T03:05:11.111Z} java.time.format.DateTimeFormatter1234567891011121314151617181920212223242526272829303132333435363738394041/*DateTimeFormatter 格式化或解析日期、时间类似于SimpleDateFormat */@Testpublic void test3(){ //方式1:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; //格式化: 日期 --> 字符串 LocalDateTime localDateTime = LocalDateTime.now(); String str = formatter.format(localDateTime); System.out.println(localDateTime);//2019-09-28T14:49:57.682 System.out.println(str);//2019-09-28T14:49:57.682 //解析: 字符串 --> 日期 TemporalAccessor parse = formatter.parse("2019-09-28T14:49:57.682"); System.out.println(parse);//{},ISO resolved to 2019-09-28T14:49:57.682 //方式2:本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) //FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT ↑ DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); //格式化 String str1 = formatter1.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:33:41.802 System.out.println(str1);//2019年9月28日 下午03时33分41秒 //ofLocalizedDate() DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); //格式化 String str2 = formatter2.format(LocalDate.now()); System.out.println(str2);//2019年9月28日 星期六 //方式3:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”) DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); //格式化 String str3 = formatter3.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:38:38.889 System.out.println(str3);//2019-09-28 03:38:38 //解析 TemporalAccessor parse1 = formatter3.parse("2019-09-28 03:38:38"); System.out.println(parse1);//{SecondOfMinute=38, HourOfAmPm=3, MicroOfSecond=0, NanoOfSecond=0, MinuteOfHour=38} Java比较器对象数组的排序问题,涉及对象之间的比较。Java对象正常情况下,只能进行比较: == 或 != ,不能使用 > 或 < 。Java实现对象排序的方式有两种: 自然排序:java.lang.ComparableComparable接口的使用举例 自然排序 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个大小的方式 像String、包装类等重写了compareTo()方法以后,进行了从小到大的排序 重写compareTo(obj)的规则: 如果当前对象this大于形参对象obj,则返回正整数; 如果当前对象this小于形参对象obj,则返回负整数; 如果当前对象this等于形参对象obj,则返回零。 对于自定义类,若需要排序,可让自定义类实现Comparable接口,重写compareTo(obj)方法,指明如何排序 123456789101112131415161718@Testpublic void test1(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr); System.out.println(Arrays.toString(arr));}//Eg:商品价格从低到高:按价格排序class Goods implements Comparable {}@Overridepublic int compareTo(Objact o) { if(o instanceod Goods) { Goods goods = (Goods)o; return Double.compare(this.price,goods.price) } throw new RuntimeException("传入的数据类型不一致");} 定制排序:java.util.Comparator 背景: 当元素的类型没有实现Comparable接口 而又不方便修改代码;或者实现了Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来进行排序。 重写compare(Object o1, Object o2)方法,比较o1和o2的大小: 若方法返回正整数,表示o1大于o2; 返回0,表示相等; 返回负数,表示o1小于o2。 123456789101112131415161718@Testpublic void test2(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr,new Comparator(){ //按照字符串大到小 @Override public int compare(Object o1, Object o2) { if(o1 instanceof String && o2 instanceof String){ String s1 = (String) o1; String s2 = (String) o2; return -s1.compareTo(s2); }// return 0; throw new RuntimeException("输入的数据类型不一致"); } }); System.out.println(Arrays.toString(arr));} Comparable接口与Comparator的使用的对比: 前者一旦指定,保证Comparable接口实现类的对象在任何位置都可以比较大小; 后者属于临时性的比较。Arrays.sort(arr,new Comparator(){…..} 其他类System类 System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。 成员变量System类内部包含 in、out和err 三个成员变量,分别代表 标准输入流(键盘输入),标准输出流(显示器)和 标准错误输出流(显示器)。 成员方法 native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 使用该方法可以在图形界面编程中实现程序的退出功能等。 void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。 String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示: |属性名 |属性说明|| —- | —- ||java. version |Java运行时环境版本||java. home |java安装目录操作系统的名称||os.version |操作系统的版本||user.nane |用户的账户名称||user.home |用户的主目录||user.dir |用户的当前工作目录| 12345678910111213141516171819202122232425@Testpublic void test3(){ String javaVersion = System.getProperty("java.version"); System.out.println("java的version:" + javaVersion); String javaHome = System.getProperty("java.home"); System.out.println("java的home:" + javaHome); String osName = System.getProperty("os.name"); System.out.println("os的name:" + osName); String osVersion = System.getProperty("os.version"); System.out.println("os的version:" + osVersion); String userName = System.getProperty("user.name"); System.out.println("user的name:" + userName); String userHome = System.getProperty("user.home"); System.out.println("user的home:" + userHome); String userDir = System.getProperty("user.dir"); System.out.println("user的dir:" + userDir);}out:java的version:1.8.0_191java的home:C:\Program Files\Java\jdk1.8.0_191\jreos的name:Windows 10os的version:10.0user的name:goodwelluser的home:C:\Users\goodwelluser的dir:D:\Codes\IdeaProjects\JavaSenior\Day Math类java.lang.Math 提供了一系列静态方法用于 科学 计算。其 方法的参数和返回值类型一般为double 型。 abs 绝对值 acos,asin,atan,cos,sin,tan 三角函数 sqrt 平方根 pow(double a,doble b) a 的b 次幂 log 自然对数 exp e 为底指数 max(double a,double b) min(double a,double b) random() 返回0.0 到1.0 的随机数 long round(double a) double 型数据a 转换为long 型(四舍五入) toDegrees(double angrad) 弧度—> 角度 toRadians(double angdeg) 角度—>弧度 BigInteger与BigDecimalBigInteger类java.math包的 BigInteger 可以表示不可变的任意精度的整数。提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。 构造器 BigInteger(String val):根据字符串构建BigInteger对象 常用 方法 public BigInteger abs(): 返回此 BigInteger 的绝对值的 BigInteger。 BigInteger add(BigInteger val) : 返回其值为 (this + val) 的 BigInteger BigInteger subtract(BigInteger val) : 返回其值为 (this - val) 的 BigInteger BigInteger multiply(BigInteger val) : 返回其值为 (this * val) 的 BigInteger BigInteger divide(BigInteger val) : 返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。 BigInteger remainder(BigInteger val) : 返回其值为 (this % val) 的 BigInteger。 BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个BigInteger 的数组。 BigInteger pow(int exponent) : 返回其值为 (this exponent ) 的 BigInteger。 ### BigDecimal类 一般的Float类和Double类可以用来做科学计算或工程计算,但在 商业计算中,到 要求数字精度比较高,故用到java.math.BigDecimal类 。BigDecimal类支持不可变的、任意精度的有符号十进制定点数。 构造器 public BigDecimal(double val) public BigDecimal(String val) 常用方法 public BigDecimal add(BigDecimal augend) public BigDecimal subtract(BigDecimal subtrahend) public BigDecimal multiply(BigDecimal multiplicand) public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 123456789public void testBigInteger() { BigInteger bi = new BigInteger("12433241123"); BigDecimal bd = new BigDecimal("12435.351"); BigDecimal bd2 = new BigDecimal("11"); System.out.println(bi); // System.out.println(bd.divide(bd2)); System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));} 枚举类的使用含义: 类的对象只有有限个,确定的。(当需要定义一组常量时,强烈建议使用枚举类。若枚举类只有一个对象,则可作为单例模式的实现方式。) 如何定义: 方式一:jdk5.0之前,自定义枚举类 方式二:jdk5.0时,可使用enum关键字定义枚举类 123456789101112131415161718192021222324252627282930313233343536373839404142public class enumTest { public static void main(String[] args) { Season spring = Season.SPRING; System.out.println(spring);//Season{seasonName='春天', seasonDesc='春暖花开'} }}//自定义枚举类class Season { //1.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //3.提供当前枚举类的多个对象: public static final修饰 public static final Season SPRING = new Season("春天", "春暖花开"); public static final Season SUMMER = new Season("夏天", "夏日炎炎"); public static final Season AUTUMN = new Season("秋天", "秋高气爽"); public static final Season WINTER = new Season("冬天", "冬天雪地"); //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } //4.其他诉求2:提供toString() @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class enumTest1 { public static void main(String[] args) { Season1 summer = Season1.SUMMER; System.out.println(summer);//SUMMER System.out.println(Season1.class.getSuperclass());//class java.lang.Enum }}//使用enum关键字定义枚举类//说明:定义的枚举类默认继承于class java.lang.Enumenum Season1 { //1.提供当前枚举类的多个对象,多个对象之间用","隔开,末尾对象";"结束 SPRING("春天", "春暖花开"), SUMMER("夏天", "夏日炎炎"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "冬天雪地"); //2.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season1(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; }// //4.其他诉求2:提供toString()//// @Override// public String toString() {// return "Season1{" +// "seasonName='" + seasonName + '\'' +// ", seasonDesc='" + seasonDesc + '\'' +// '}';// }} Enum类的主要方法 values() 方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。 valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。 toString():返回当前枚举类对象常量的名称 123456789//values():Season1[] values = Season1.values();for (int i = 0; i < values.length; i++) { System.out.println(values[i]);}//SPRING SUMMER AUTUMN WINTER//valueOf(String objName):返回枚举类中对象名是objName的对象Season1 winter = Season1.valueOf("WINTER");System.out.println(winter);//WINTER 使用enum关键字定义的枚举类实现接口的情况 情况一:实现接口,在enum类中实现抽象方法; 情况二:让枚举类的对象分别实现接口中的抽象方法(每个都不一样) 123456SPRING("春天", "春暖花开"){ @Override public void show(){ System.out.println("春天在哪里"); }} 注解(Annotation)的使用框架 = 注解 + 反射 + 设计模式。 理解Annotation: jdk 5.0 新增的功能 Annotation 其实就是代码里的 特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。 Annotation的使用示例 示例一:生成文档相关的注解 示例二: 在编译时进行格式检查(JDK 内置的三个基本注解) @Override: 限定重写父类方法, 该注解只能用于方法 @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择 @SuppressWarnings: 抑制编译器警告 示例三: 跟踪 代码依赖性,实现替代配置文件功能 如何自定义注解 注解声明为:publi @interface XXX 内部定义成员,通常使用value表示 可以指定成员的默认值,使用default定义(String[] value default “hello”) 若自定义注解没有成员,表明是一个标识作用 PS: 若注解有成员,在使用注解时,需要指明成员的值 自定义注解必须配上注解的信息处理流程(使用反射)才有意义 自定义注解通常都会指明两个元注解:Retention、Target jdk 提供的4种元注解元注解:对现有的注解进行解释说明的 注解 Retention:指定所修饰的 Annotation 的生命周期:SOURCE \ CLASS(默认行为)\ RUNTIME只有声明为RUNTIME生命周期的注解,才能通过反射获得 Target:用于指定被修饰的 Annotation 能用于修饰那些程序元素 Documented:表示所修饰的注解在被javadoc解析时,保留下来 Inherited:被它修饰的 Annotation 将具有继承性 JDK8中 注解的新特性: 可重复注解 、 类型注解 可重复注解 ① 在MyAnnotation 上声明@Repeatable,成员值为 MyAnnotation.class ② MyAnnotationd Taget 和 Reten等元注解与MyAnnotation相同。 类型注解 ELementType. TYPE PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)ELementType.TYPE_USE 表示该注解能写在使用类型的任何语句中 集合概述 集合、数组都是对 多个数据 进行 存储操作 的结构,简称Java容器。 说明:此时的存储,主要指的是内存层面的存储,不涉及持久化的存储(.txt,.jpg,.avi,数据库中) 数组在存储多个数据方面的特点(缺点*): (*)一旦初始化后,长度就确定了 一旦定义好,元素的类型也确定了 (*)提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高 (*)获取数组中实际元素的个数的需求,没有现成的属性或方法可用 (*)存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足 Java集合 可以分为Collection 和 Map两种体系(接口) Collection接口:单列数据,定义了存取一些对象的方法的集合 List:元素有序、可重复的集合,“动态数组” Set:元素无序、不可重复的集合,“数学上的集合” Map接口:双列数据,保存具有映射关系”key-value对”的集合,“数学上的函数映射” 集合框架123456789|----Collection接口:单列集合,用来存储一个一个的对象 |----List接口:存储有序的、可重复的数据 --> “动态”数组 |----ArrayList、LinkedList、Vector |----Set接口:存储无序的、不可重复的数据 --> 数学中的“集合” |----HashSet、LinkedHashSet、TreeSet |----Map接口:双列接口,用来存储一对(key-value)数据 --> y = f(x) |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties Collection常用方法(通用) add(Object e): 将元素e添加到集合coll中 addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 size(): 获取添加的元素的个数 clear(): 清空集合元素 isEmpty(): 判断当前集合是否为空 12345678910111213141516171819202122232425262728@Test public void test1(){ Collection coll = new ArrayList(); //add(Object e): 将元素e添加到集合coll中、 coll.add("AA"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); //size(): 获取添加的元素的个数 System.out.println(coll.size());//4 //addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("cc"); coll.addAll(coll1); System.out.println(coll.size());//6 System.out.println(coll);//[AA, BB, 123, Thu Oct 31 12:03:02 CST 2019, 456, cc] //clear(): 清空集合元素 coll.clear(); //isEmpty(): 判断当前集合是否为空 System.out.println(coll.isEmpty());//true } contains(Object obj): 判断当前集合中是否包含obj PS:向Collection接口的实现类的对象中 添加数据obj时,要求obj所在类要重写equals()。不是判断地址,判断内容 123456789101112131415161718192021@Testpublic void test2(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); Person p = new Person("Jerry1", 20); coll.add(p); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //1.contains(Object obj): 判断当前集合中是否包含obj //我们在判断时会调用obj对象所在类的equals() boolean contains = coll.contains(123); System.out.println(contains);//true System.out.println(coll.contains(new String("Tom")));//true System.out.println(coll.contains(new Person("Jerry", 20)));//false System.out.println(coll.contains(p));//true //PS:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()。} containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。 123//2.containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));//false remove(Object obj): 从当前集合中移除obj元素 1234567891011121314151617@Testpublic void test3(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //3.remove(Object obj): 从当前集合中移除obj元素 coll.remove(123); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false] coll.remove(new Person("Jerry", 20)); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false]} removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素 12345//4.removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);//[com.good.java.Person@4ee285c6, Tom, false] retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 123456789101112131415@Testpublic void test4() { Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //5.retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 Collection coll1 = Arrays.asList(123,456,789); coll.retainAll(coll1); System.out.println(coll); //[123, 456]} equals(Object obj) 123456789 Collection coll1 = new ArrayList(); coll1.add(123); coll1.add(456);// coll1.add(new Person("Jerry", 20)); coll1.add(new String("Tom")); coll1.add(false); //6.equals(Object obj) System.out.println(coll.equals(coll1)); hashCode(): 返回当前对象的哈希值 123456789Collection coll = new ArrayList();coll.add(123);coll.add(456); coll.add(new Person("Jerry", 20));coll.add(new String("Tom"));coll.add(false);//7.hashCode(): 返回当前对象的哈希值System.out.println(coll.hashCode());//701070075 集合 —> 数组:toArray() /拓展:数组 —> 集合: 调用Arrays类的静态方法asList() 1234567891011121314Object[] arr = coll.toArray();for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]);}//拓展:数组 ---> 集合: 调用Arrays类的静态方法asList()List<String> list = Arrays.asList(new String[]{"aa","bb","cc"});System.out.println(list);//[aa, bb, cc]List arr1 = Arrays.asList(new int[]{123,456});System.out.println(arr1.size());//1List arr2 = Arrays.asList(new Integer[]{123,456});System.out.println(arr2.size());//2 iterator() 返回Iterator接口的实例,用于遍历集合元素,注意是一次性的 集合元素的遍历(迭代器接口Iterator) next() 判断是否还有下有一个元素 hasNext() ①指针下移 ②将下移以后集合位置上的元素返回 123456789101112131415161718192021222324252627@Testpublic void test1(){ Collection coll = new ArrayList(); coll.add("aa"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); Iterator iterator = coll.iterator(); //方式一:// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next()); //报异常NoSuchElementException// System.out.println(iterator.next()); //方式二: 不推荐// for (int i = 0; i < coll.size(); i++) {// System.out.println(iterator.next());// } //方式三: 推荐 while (iterator.hasNext()) { System.out.println(iterator.next()); }} remove() 删除集合中某数据(调用前需要先next()) 123456while (iterator.hasNext()) { Object obj = iterator.next(); if ("BB".equals(obj)) { iterator.remove(); }} foreach 循环遍历集合JDK5 新增了foreach 用于遍历数组和集合(内部任然调用迭代器) for(集合元素类型 局部变量:集合对象) 1234for (Object obj : coll) { System.out.println(obj);}// 注意是局部变量,不会改变 List接口替代数组,元素有序,且可重复 具体实现类:ArrayList、LinkedList、Vector 三者异同: 同:三个类都实现了List接口,存储数据的特点相同,元素有序,且可重复 异: ArrayList 作为List接口的主要实现类 ,线程不安全,效率高,底层用Object[]存储 LinkedList 底层用双向链表存储,对于频繁插入、删除操作,此类效率高 Vector 作为List的古老实现类,线程安全,效率低,底层用Object[]存储 Arraylist,的源码分析:JDK7情况下ArrayList list= new ArrayList(/(底层创建了长度是10850bc数 HelementData List. add (123); //eLementData【0】= new Integer(123); List.0(1):/0果此次的添加导致底层 eLementDat数组容量不够,则扩容默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中结论:建议开发中使用带参的构造器:ArrayList List= new Arraylist( unt capacity) JDK8中 ArrayList的变化:ArrayList list= new ArrayList()/)底层0 bject】 elementdata初始化为},并没有创建List.0d(123);//第次调用d()时,底层才创建了长度10的数组,并将数据123添加到 elemen后续的添加和扩容操作与jR7无异 总结:JDK7中的 Arraylist的对象的创建类似于单例的汉式,而8中的ryst的对象的创建类似于单例的像汉式,延迟了数组的创建,节省内存 List中的常用方法 void add (int index, Object ele):在 index位置插入eLle元素 boolean addAll (int index, Collection eles):从 index位置开始特eles中的所有元素添加进来 Object get ( int index):获取指定 index位置的元素 int indexOf (Object obj):返园obj在集合中首次出现的位置 int lastIndexOf (Object obj):返bj在当前集台中末次出现的位置 Object remove ( int index):移除指定inex位置的元素,并返回此元素。(区别于Collection的,eg:List. remove(2); list.remove(new Integer (2);) Object set ( int index, Object ele):设置指定 index位置的元素为ele List subList ( int fromIndex, int toIndex):返从 fromIndex到 toIndex位置的子集合 总结:常用方法 增:add(Object obj) 删:remove(int index) / remove(object obj) 改: set(int index, Object ele) 查:get(int index) 插:add(int index, Object ele) 长度:size() 遍历:①Iterate送代器 ②增强for循环 ③普通的循环 Set接口存储无序的、不可重复的数据 –> 数学中的“集合” |—-HashSet、LinkedHashSet、TreeSet 无序性:不等于随机性,添加的位置不同 不可重复性:保证添加的元素按照equals()判断时,不能返回true PS:Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。 要求: 向Set中添加的数据,其所在类一定要重写hashCode() 和 equals() 重写的方法必须保持一致性,相同的对象必须具有相等的散列码。(技巧:对象中用作equals方法比较的Field,都应该用来计算hashCode值) HashSet: 作为set接口的主要实现类;线程不安全的;可以存储null值 LinkedHashSet: 作为HashSet的子类,遍历内部数据时,可以按照添加的顺序遍历(原因:在添加数据的同时,每个数据还维护了俩应用,记录此数据前一个数据和后一个数据的地址)优点:对于频繁的遍历操作效率更高。 TreeSet: 可以按照添加对象的指定属性,进行排序 1234567891011121314151617181920212223242526272829@Testpublic void test2(){ //HashSet Set set = new HashSet(); set.add(123); set.add("AA"); set.add(false); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }// AA// false// 123 //LinkedHashSet LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(123); linkedHashSet.add("AA"); linkedHashSet.add(false); Iterator iterator1 = linkedHashSet.iterator(); while (iterator1.hasNext()) { System.out.println(iterator1.next()); }// 123// AA// false} TreeSet:向其中添加数据,要求是相同的对象。比较是否添加的对象相同,此处不使用equals,可以分别实现Comparable和Comparator实现自然和定制排序。 自然排序使用的是compareTo返回0,必须重写compareTo 定制排序使用的是compare返回0,必须重写compare 123456789101112@Testpublic void test2(){ TreeSet set = new TreeSet(); set.add(123); set.add(34); set.add(577); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }} Map接口双列接口,用来存储一对(key-value)数据 –> y = f(x) |—-HashMap :作为Map的主要实现类;线程不安全,效率高,能存储null 的key 和value,底层:数组+链表(JDK7)+红黑树(JDK8) |—-LinkedHashMap :保证在遍历map元素时,可以按照添加的顺序实现遍历,对于频繁的遍历操作,此类执行效率高于HashMap|—-TreeMap : 保证按照添加的 key-value对 进行排序,实现排序遍历,按照key自然排序或定制排序,底层使用红黑树。|—-Hashtable : 作为古老的实现类,线程安全,效率低,不能存储null 的key 和value |—-Properties : 常用来处理配置文件,key和value都是String类型、 Map结构的理解: Map中的 key:无序的、不可重复的,使用 Set 存储所有的key —–> key所在的类要重写equals和 hashCode Map中的 value:无序的、可重复的,使用 Collection存储所有的 value —–> value所在的类要重写equals 一个键值对:key- value构成了一个 Entry对象 Map中的 entry:无序的、不可重复的,使用 Set 存所有entry HashMap的底层实现原理 HashMap的底层实现原理?d7为例说明HashMap map new HashMap():在实例化以后,底层创建了长度是16的一维数组 Entry【 table..可能已经执行过多次put map. put(key1, vaLue1)首先,调用key1所在类的 hashcode()计算key1哈希值,此哈希信经过某种算法计算以后,得到在 Entry数组中的存放位置。如果此位置上的数据为空,此时的ey1- value1添加成功情况如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:如果Rey1的哈希值与已经存在的数据的哈希值都不相同,此的key1- value1添加成功。—)况2如果Rey1的哈希值和已经存在的某一个数据(Rey2-vaue2)的哈希值相同,继续比较:调用key1所在类的 equals(key2如果 equals()返aLse:此的key1-vae1添加成功。—情况3如果 equaLs()返回true:使用vLue1营换 value2补充:关于情况2和情况3:此的key1- value1和原来的数据以链表的方式存储 在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来 jdk8相较于jdk7在底层实现方面的不同1. new Hash№p():底层没有创建一个长度为6的数组2.j如k8底层的数组是:Mode【】,非 Entry【3.营次调用put()方法的,底层创建长度为16的数组4.jk7底居结构只有:数组+链表。dR8中底居结构:数组+链表+红黑树当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64的此时此索引位置上的所有数据改为体用红黑树存储。 Map接口的常用方法添加、删除、修改操作 Object put(Object key, Object value):将指定key-value添加到或修改)当前map对象中 void putALL(Map m):将m中的所有key-value对存放到当前map中 Object remove(Object key):移除指定key的key- value对,并返value void clear():清空当前map中的所有数据 元素查询的操作: Object get(Object key):获取指定key对应的value boolean containsKey (Object key):是否包含指定的key boolean containsValue(Object value):是否包含指定的value int size():返回map中 key- value对的个数 boolean isEmpty():判断当前map是否为空 booLean equals(Object obj):判断当前map和参数对象obj是否相等 元视图操作的方法: Set keySet():返回所有key构成的Set集合 Collection values():返园所有 value构成的 Collection集合 Set entrySet():返园所有key-value对构成的Set集台 123456789101112131415161718192021222324252627282930313233343536@Test public void test5(){ Map map = new HashMap(); map.put("AA",123); map.put(45,123); map.put("BB",56); //遍历所有的key集:keySet() Set set = map.keySet(); Iterator iterator = set.iterator(); While(iterator.hasNext()) { System.out.println(iterator,next()); } //遍历所有的value集:values() Collection values = map.values(); for(object obj : values) { System.out.println(obj); } //遍历所有的key-value //方式一 entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); While(iterator1.hasNext()) { Object obj = iterator1.next(); //entrySet:集合中的元素都是entry Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "----" + entry.getValue()); } //方式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); While(iterator2.hasNext()){ object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "=====" + value); }} 总结:常用方法:添加:put(Object key, Object value)删除:remove(Object key)修改:put(Object key, Object value)查询:get(Object key)长度:size()遍历:keySet() / values() / entrySet() 向 TreeMap 中添加key- value,要求 key必须是由同一个类创建的对象,因为要按照key进行排序:自然排序、定制排序 123456789// Properties:常用来处理配置文件。key和value都是 String类型public static void main(String[] args) throws Exception { Properties pros = new Properties(); FileInputstream fis = new FileInputstream(jdbc.properties); pros.load(fis); //加载流对应的文件 String name = pros.getProperty("name"); String password = pros.getProperty("password1"); System.out.println("name =" + name + "password =" password);} Collections工具类 操作Set、List和Map等集合的工具类 ● Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。 ●排序操作:(均为 static方法) reverse(List):反转List中元素的顺序 shuffle(List):对List集合元素进行随机排 sort(List):根据元素的自然顺序对指定List集合元素按升序排序 sort(List, Comparator):根据指定的 Comparator产生的顺序对List集合元素进行排序 swap(List,int,int):将指定List集合中的i 处元素和j 处元素进行交换 查找、替换 Object max( Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max( Collection, Comparator):根据 Comparator指定的顺序,返回给定集合中的最大元素 Object min( Collection)Object min(Collection, Comparator) int frequency(collection, Object):返回指定集合中指定元素的出现次数 void copy( List dest. List src):将src中的内容复制到dest中 boolean replaceAll( List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值 1234567891011121314151617@Testpublic void test2{ List list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(-97); // 报异常:IndexOutOfBounds Exception(" Source does not fit in dest") // List dest = new ArrayList; // Collections.copy(dest, List); // 正确的: List dest = Arrays.asList(new Object(list.size()); System.out.println(dest.size()); // List.sizeof Collections.copy(dest, list); System.out.println(dest);} Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成 线程同步 的集合,从而可以解决多线程并发访问集合时的线程安全。]]></content>
<categories>
<category>Java</category>
<category>小结</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MySQL初识]]></title>
<url>%2F%E6%95%B0%E6%8D%AE%E5%BA%93%2FMySQL%2FMySQL%E5%88%9D%E8%AF%86%2F</url>
<content type="text"><![CDATA[特别基础的MySQL语法 数据库介绍数据库的重要性: 实现数据持久化 使用完整的管理系统统一管理,易于查询 相关术语DB:数据库(database)存储数据的“仓库”,保存了一系列有组织的数据。 DBMS:数据库管理系统(Database Management System)数据库是通过DBMS创建何操作的容器。 SQL:结构化查询语言(Structure Query Language)专门用来与数据库通信的语言。 SQL的优点: 不是某个特定数据库供应商特有的语言,几乎多有的DBMS都支持SQL。 简单易学 灵活,可以进行非常复杂和高级的数据库操作。 数据库的特点: 将数据放在表中,表再放在库中 每个数据库中可以有多张表。每个表的表名标识自己,具有唯一性 表具有一些特性,特性定义了数据再表中如何存储,类似与“类”的设计 表由列(字段)组成。所有表都是由一个或者多个列组成,类似“属性” 表中的数据按照行来存储,每行类似“对象” DBMS分为两类: 基于共享文件系统(Access) 基于C/S客户机-服务器(MySQL,Oracle,SqlServer) 关系型数据库MySQL介绍MySQL服务启动退出 计算机-右击管理-服务 通过管理员身份运行 12net start 服务名net stop 服务名 MySQL登录登出12mysql [-h localhost -P 3306] -u root -pexit或者ctrl+c 常见命令:( Commands end with ; or \g.) 查看当前所有数据库 1show databases; 打开指定的库 1use 库名; 查看当前库中的所有表 1show tables; 查看其它库中的所有表 1show tables from 库名; 创建表 1234create table 表名( 列名 列类型; ...) 查看表格式 1desc 表名; 查看服务器的版本 12341. 登录mysql服务端select version();2. 未登录服务端mysql --version 或 -V MySQL的语法规范 不区分大小写,但建议关键字大写,表名、列名小写 每条命令最好用分号结尾 根据需要进行缩进或者换行 注释 单行注释:#注释文字 或者 – 注释文字 多行注释:/注释文字/ SQL的语言分类 DQL(Data Query Language):数据查询语言select DML(Data Manipulate Language):数据操作语言insert 、update、delete DDL(Data Define Languge):数据定义语言create、drop、alter TCL(Transaction Control Language):事务控制语言commit、rollback DQL语言基础查询格式: 12select 查询列表from 表 特点: 查询列表可以是:表中的字段、常量值、表达式、函数 查询的结果是一个虚拟的表格 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162#1. 查询单个字段SELECT `first_name` FROM `employees`;#2. 查询多个字段SELECT `first_name`,`last_name` FROM `employees`;#3. 查询所有字段SELECT * FROM `employees`;SELECT `first_name`, `last_name`, `email`, `phone_number`, `job_id`, `salary`, `commission_pct`, `manager_id`, `department_id`, `hiredate` FROM `employees` ; #4. 查询常量SELECT 100;###字符型和日期型需要用单引号引起来,数值型不需要SELECT 'goodwell';#5. 查询函数SELECT VERSION();#6. 查询表达式SELECT 100/22;#7. 起别名###①as###②空格SELECT `last_name` AS 姓 FROM `employees`;SELECT `first_name` 名 FROM `employees`;#8. 去重SELECT DISTINCT `job_id` FROM`employees`;#9. +###作用:做加法运算SELECT 23+23; #46SELECT 数值+数值; 直接运算SELECT 'a'+2; #2SELECT '11'+11; #22SELECT 字符+数值;先试图将字符转换成数值,如果转换成功,则继续运算;否则转换成0,再做运算SELECT NULL+1; #nullSELECT NULL+值;结果都为NULL10、【补充】CONCAT函数###功能:拼接字符SELECT CONCAT(字符1,字符2,字符3,...);11、【补充】IFNULL函数###功能:判断某字段或表达式是否为null,如果为null 返回指定的值,否则返回原本的值SELECT IFNULL(commission_pct,0) FROM employees;12、【补充】ISNULL函数###功能:判断某字段或表达式是否为null,如果是,则返回1,否则返回0 条件查询格式: 123select 查询列表3from 表名1where 筛选条件2; 执行顺序:1->2->3 分类: 按条件表达式筛选(> < = != <> >= <=) 按逻辑表达式筛选(&& || !and or not用于连接条件表达式) 模糊查询(like, between and, in, is null) 12345678910111213141516171819202122232425262728#案例1.查询工资>12000的员工信息SELECT * FROM `employees` WHERE `salary` > 12000 ; #案例2.查询部门编号不等于90号的员工名和部门编号SELECT `last_name`, `department_id` FROM `employees` WHERE `department_id` <> 90 ; #案例3.查询工资在10000到20000之间的员工名、工资以及奖金SELECT `last_name`, `salary`, `commission_pct` FROM `employees` WHERE `salary` <= 20000 AND `salary` >= 10000 ;#案例4.查询部门编号不是在90到110之间,或者工资高于15000的员工信息 *模糊查询 like(一般与通配符搭配使用) 通配符: % 任意多个字符 _ 任意单个字符 转义 escape 12345678910111213141516#案例1.查询员工名中第三个字符为n,第五个字符为l的员工名和工资SELECT `last_name`, `salary`FROM `employees`WHERE `last_name` LIKE '__n_l%'; #案例2.查询员工名中第二个字符为_的员工名SELECT `last_name`FROM `employees`WHERE `last_name` LIKE '_$_%' ESCAPE '$'; between and ①使用可以提高语言的简洁度 ②包含临界值(完全等价>=x and <=y) ③两个临界值不能调换顺序 12345678#案例.查询工资在10000到20000之间的员工名、工资以及奖金SELECT `last_name`, `salary`, `commission_pct` FROM `employees` WHERE `salary` between 10000 and 20000 ; in(判断某字段的值是否属于in列表中的某一项) 特点: ①相比于or提高了语句简洁度 ②in列表中的值类型必须一致或兼容 ③in中不支持通配符 12345678#案例.查询员工的工种编号是IT_PROG、AD_vp、AD_PRES中的一个的员工名和工种编号SELECT `last_name`, `job_id`FROM `employees`WHERE `job_id` IN('IT_PROG','AD_vp','AD_PRES'); is null/is not null =或<>不能用于判断null,所以需要用is null/is not null 12345678#案例.查询没有奖金的员工名和奖金率SELECT `last_name`, `commission_pct`FROM `employees`WHERE `commission_pct` IS NULL; **安全等于 <=> 1234567891011121314151617#案例1.查询没有奖金的员工名和奖金率SELECT `last_name`, `commission_pct`FROM `employees`WHERE `commission_pct` <=>NULL;#案例2.查询奖金为12000的员工名和奖金SELECT `last_name`, `salary`FROM `employees`WHERE `salary` <=> 12000; 区别: IS NULL仅仅可以判断NULL值,可读性较高,建议使用 <=>既可以判断NULL又可以判断数值 排序查询语法: 1234select 查询列表from 表[where 筛选条件]order by 排序列表 [asc | desc] 特点: 缺省asc升序 order by子句中支持单个字段、多个字段、表达式、函数、别名 order by子句一般放在查询语句的最后面,limit子句除外 123456789101112131415161718192021222324252627282930313233343536373839#1. 案例:查询员工信息,要求工资从高到低排序;SELECT * FROM `employees` ORDER BY salary DESC ;#2. 案例:查询部门编号>=90的员工信息,按入职时间的先后顺序进行排序【添加筛选条件】SELECT * FROM employees WHERE department_id >= 90 ORDER BY `hiredate` ASC ;#3. 案例:按年薪的高低显示员工的信息和年薪【按表达式排序】SELECT *, (IFNULL(commission_pct, 0) + 1) * salary * 12 年薪 FROM employees ORDER BY 年薪 DESC ;#4. 案例:按年薪的高低显示员工的信息和年薪【按别名排序】#5. 案例:按姓名的长度显示员工的姓名和工资【按函数排序】SELECT last_name, salary FROM employees ORDER BY LENGTH(last_name) DESC ;#6. 案例:查询员工信息,要求先按工资升序,再按员工编号降序【按多个字段排序】SELECT * FROM employees ORDER BY salary, employee_id DESC ; 常见函数概念:类似于Java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名。 好处: 隐藏了实现细节 提高了代码的重用性 调用: 叫什么(函数名) 干什么(函数功能) 分类: 单行函数(如concat、length、ifnull等) 分组函数(做统计使用,又称为统计函数、聚合函数、组函数)(多对一) 单行函数 ①字符函数 1234567891011121314151617181920212223242526272829303132333435363738394041424344#1.length 获取参数值的字节个数(汉字为3) SELECT LENGTH('john') ; #4SELECT LENGTH('goodwell古德'); #14#2.concat 拼接字符串SELECT CONCAT(last_name,' ',first_name) 姓名 FROM employees;#3.upper、lowerSELECT UPPER('john'); #JOHNSELECT LOWER('John'); #john#4.substr、substring###PS:索引从1开始###截取从指定索引处后面所有字符SELECT SUBSTR('goodwell古德',2) out_put; #oodwell古德###截取从指定索引处指定字符长度的字符SELECT SUBSTR('goodwell古德',2,3) out_put; #ood#案例:姓名中首字母大写,其他字符小写然后用_拼接,显示出来SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) out_put FROM employees;#5.instr 返回子串第一次出现的索引,如果找不到返回0SELECT INSTR('goodwell','o') out_put; #2#6.trimSELECT LENGTH(TRIM(' goodwell ')) AS out_put; #8SELECT TRIM('aa' FROM 'aaaaagoodaaaawellaaaaaaaa'); #agoodaaaawell#7.lpad 用指定的字符实现左填充成指定长度SELECT LPAD('goodwell',8,'aa'); #goodwellSELECT LPAD('goodwell',6,'aa'); #goodweSELECT LPAD('goodwell',11,'aa'); #aaagoodwell#8.lpad 用指定的字符实现右填充成指定长度SELECT RPAD('goodwell',8,'aa'); #goodwellSELECT RPAD('goodwell',6,'aa'); #goodweSELECT RPAD('goodwell',11,'aa'); #goodwellaaa#9.replace 替换SELECT REPLACE('goodgoodwell','good','best'); #bestbestwell ②数学函数 12345678910111213141516171819202122#round 四舍五入SELECT ROUND(1.45); #1SELECT ROUND(1.5); #2#ceil 向上取整,返回>=该参数的的最小整数SELECT CEIL(1.001); #2SELECT CEIL(1.000); #1#floor 向下取整,返回<=该参数的的最大整数SELECT FLOOR(-1.002); #-2SELECT FLOOR(-1.000); #-1#truncate 截断(小数点后)SELECT TRUNCATE(1.03722,2); #1.03#mod 取余/* mod(a,b): a-a/b*b mod(-10,-3): -10- (-10)/(-3)*(-3) =-1*/#rand 获取随机数,返回0-1之间的小数 ③日期函数 123456789101112131415161718192021222324252627282930#now 返回当前系统日期+时间 select now() ; #2020-08-14 01:11:05#curdate 只返回当前日期select curdate(); #2020-08-14#curtime 只返回当前时间select curtime(); #01:11:54#获取指定的部分年、月、日、小时、分钟、秒SELECT YEAR(NOW()); #2020SELECT YEAR('2020-01-25'); #2020SELECT month(NOW()); #8SELECT monthname(NOW()); #August#datediff查询两个日期相差天数select datediff('2020-1-25','2020-1-27'); #-2#str_to_date 将日期格式的字符转换成指定格式的日期select str_to_date('3-9-1998','%m-%d-%Y'); #1998-03-09select str_to_date('3-9-98','%c-%d-%y'); #1998-03-09#date_format 将日期转换成字符select date_format('1998-3-9','%y年%m月%d日'); #98年03月09日#案例:查询有奖金的员工名和入职日期(xx月/xx日 xx年)select last_name 员工名,date_format(hiredate,'%m/%d %y') 入职日期from employeeswhere commission_pct is not null; 序号 格式符 功能 1 %Y 四位的年份 2 %y 2位的年份 3 %m 月份(01,02) 4 %c 月份(1,2) 5 %d 日(01,02) 6 %H 小时(24h) 7 %h 小时(12h) 8 %i 分钟(00,01) 9 %s 秒(00,01) ④其他函数 12345SELECT VERSION(); #8.0.13SELECT DATABASE(); #myemployeesSELECT USER(); #root@localhostSELECT Password('');SELECT md5(''); ⑤流程控制函数 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657#1.if函数:类似if elseSELECT IF(2 < 4, '是', '否') ;SELECT last_name,commission_pct,IF(commission_pct IS NULL,'无','有') 备注FROM employees;#2.case函数使用1: 类似switch case/* case 要判断的字段或表达式 when 常量1 then 要显示的值或语句; when 常量2 then 要显示的值或语句; ... else 要显示的值或语句; end*/#案例:查询员工工资,要求部门号30显示的工资为1.1倍,部门号40显示的工资为1.2倍,部门号50显示的工资为1.3倍,其他部门,显示的工资为原工资SELECT salary 原始工资, department_id, CASE department_id WHEN 30 THEN salary * 1.1 WHEN 40 THEN salary * 1.2 WHEN 50 THEN salary * 1.3 ELSE salary END AS 工资 FROM employees ; #3.case函数的使用2:类似多重if/* case when 条件1 then 要显示的值或语句; when 条件2 then 要显示的值或语句; ... else 要显示的值或语句; end*/#案例:查询员工工资,如果工资>20000,显示A;如果工资>15000,显示B;如果工资>10000,显示C;否则显示D。SELECT salary 原始工资, department_id, CASE WHEN salary>20000 THEN 'A' WHEN salary>15000 THEN 'B' WHEN salary>10000 THEN 'C' ELSE 'D' END AS 等级FROM employees ; 分组函数 分类: sum 求和 avg 平均值 max 最大值 min 最小值 count 计数 特点: sum、avg一般用于处理数值型 max、min、count可以处理任何类型 以上都会忽略null值 可以和distinct搭配实现去重的效果 1select count(distinct salary) from employees; count特殊性(一般使用count(*)用作统计行数) 效率: MYISAM存储引擎下,count(*)的效率高INNODB存储引擎下,count(*)和count(1)的效率差不多,比count(*)要高一些 和分组函数一同查询的字段要求是group by后的字段(多对一了) 分组查询语法: 12345select 分组函数,列(要求出现在group by的后面)from 表[where 筛选条件]group by 分组的列表[order by 子句] 注意:查询列表必须是分组函数和group by后出现的字段 特点: 1、分组查询中的筛选条件分为两类 筛选时刻 数据源 位置 关键字 分组前 原始表 group by子句前 where 分组后 分组后的结果集 group by子句后 having ①分组函数做条件肯定是放在having子句 ②能用分组前筛选的,优先考虑使用分组前筛选 2、group by语句支持单个或者多个字段,(多个字段无顺序要求)还有表达式和函数。 3、也可以添加排序,放在最后。 12345678910#简单的分组查询#案例1:查询每个工种的最高工资SELECT MAX(salary),job_idFROM employeesGROUP BY job_id;#案例2:查询每个位置上的部门个数SELECT COUNT(*),location_idFROM departmentsGROUP BY location_id; 123456789101112#添加分组前的筛选条件#案例1:查询邮箱中包含a字符的,每个部门的平均工资SELECT AVG(salary),department_idFROM employeesWHERE email LIKE '%a%'GROUP BY department_id;#案例2:查询有奖金的每个领导手下员工的最高工资SELECT MAX(salary),manager_idFROM employeesWHERE commission_pct IS NOT NULLGROUP BY manager_id; 1234567891011121314151617181920#添加分组后的筛选条件#案例1:查询哪个部门的员工个数>2SELECT COUNT(*),department_idFROM employeesGROUP BY department_idHAVING COUNT(*)>2;#案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资SELECT job_id,MAX(salary)FROM employeesWHERE commission_pct IS NOT NULLGROUP BY job_idHAVING MAX(salary)>12000;#案例3:查询领导编号>102的每个领导手下的最低工资>5000的领导编号是哪个,以及其最低工资SELECT manager_id,MIN(salary)FROM employeesWHERE manager_id>102GROUP BY manager_idHAVING MIN(salary)>5000 12345678910111213141516171819#按表达式或函数分组#案例:按员工姓名的长度分组,查询每一组的员工个数,筛选员工个数>5的有哪些SELECT LENGTH(last_name),COUNT(*)FROM employeesGROUP BY LENGTH(last_name)HAVING COUNT(*)>5;#按多个字段分组#案例:查询每个部门每个工种的员工的平均工资SELECT AVG(salary),department_id,job_idFROM employeesGROUP BY department_id,job_id;#添加排序#案例:查询每个部门每个工种的员工的平均工资,并按照薪资排序SELECT AVG(salary),department_id,job_idFROM employeesGROUP BY department_id,job_idORDER BY AVG(salary) DESC; 连接查询含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询 笛卡尔乘积现象:表1有m行,表2有n行,结果=m*n行 发生原因:没有有效的连接条件 如何避免:添加有效的连接条件 分类: 按年代分类 sql92标准:仅仅支持内连接 sql99标准【推荐】:内连接+外连接(左外和右外)+交叉连接 按功能分类: 内连接:等值连接 非等值连接 自连接 外连接: 左外连接 右外连接 全外连接 交叉连接 sql92标准 等值连接 特点: ①多表等值连接的结果为多表的交集部分②n表连接,至少需要n-1个连接条件③多表的顺序没有要求④一般需要为表起别名(起别名后只能使用别名)⑤可以搭配前面介绍的所有子句使用,比如排序、分组、筛选 非等值连接(不等于) 自连接(同一张表,一张表作几张表用,起不同别名) 12345678910#案例:查询员工名和上级的名称SELECT e.employee_id, e.last_name, m.employee_id, m.last_name FROM employees e, employees m WHERE e.manager_id = m.employee_id sql99标准 语法: 12345678select 查询列表from 表1 别名 [连接类型] join 表2 别名on 连接条件[where 筛选条件][group by 分组][having 分组后筛选条件][order by 排序列表] 分类: 内连接: inner 外连接:[outer] 左外:left [outer] 右外:right [outer] 全外:full [outer] 交叉连接: cross 1、内连接:交集 特点: ①添加排序、分组、筛选 ② Inner可以省略 ③筛选条件放在 where后面,连接条件放在on后面,提高分离性,便于阅读 ④ Inner join连接和sq192语法中的等值连接效果是一样的,都是查询多表的交集 2、外连接:一个表中有,一个表中无 特点: ①外连接的查询结果为主表中的所有记录。 如果从表中有和它匹配的值,则显示匹配的值;若没有则显示null。 外连接查询结果=内连接查询结果+主表中有而从表中没有的记录 ②左外连接,left join左边的是主表 右外连接,right join右边的是主表 ③左外和右外交换两个表的顺序,可以实现相同的效果。 ④全外连接:内连接结果+表1中有但表2中无+表2中有而表1无 12345678910111213#案例1:查询哪个部门没有员工#左外SELECT d.department_nameFROM departments dLEFT OUTER JOIN employees eON e.`department_id`=d.`department_id`WHERE e.`department_id` IS NULL;#右外SELECT d.department_nameFROM employees eRIGHT OUTER JOIN departments dON e.`department_id`=d.`department_id`WHERE e.`department_id` IS NULL; 3、交叉连接:笛卡尔乘积(eg:表1有11,表2有哦4行,则交叉连接结果为44行) sql92 VS. sql99 功能:sql99支持的较多 可读性:sq199实现连接条件和筛选条件的分离,可读性较高 12345678910111213141516171819202122#查询编号>3的女神的男朋友信息,如果有则列出详细,如果没有,用nu11填充USE girls;SELECT b.*,bo.*FROM beauty bLEFT JOIN boys boON b.boyfriend_id=bo.idWHERE b.id>3;#查询哪个城市没有部门USE `myemployees`;SELECT l.`city`FROM locations lLEFT JOIN departments dON d.`location_id`= l.`location_id`WHERE d.`department_id` IS NULL;#查询部门名为SAL或IT的员工信息SELECT e.*,d.`department_name`FROM employees eJOIN departments dON d.`department_id`=e.`department_id`WHERE d.`department_name` IN('SAL','IT'); 子查询含义:出现在其他语句中的select语句,称为子查询或内查询;外部的查询语句成为主查询或者外查询。 分类: 按子查询出现的位置: select后面: 仅仅支持标量子查询 from后面: 支持表子查询 where或having后面: 标量子查询 列子查询 行子查询 exists后面(相关子查询): 表子查询 按结果集的行列数不同: 标量子查询(结果集只有一行一列) 列子查询(结果集只有一列多行) 行子查询(结果集只有一行多列) 表子查询(结果集一般为多行多列) 1、where或having后面 特点:①子查询放在小括号内②子查询一般放在条件的右侧③标量子查询,一般搭配着单行操作符使用<>=<==<>列子查询,一般搭配着多行操作符使用in、any/some、all④子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果 标量子查询 返回标量(单行单列),搭配单行操作符<、>=、<=、=、<> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657#案例1:谁的工资比Abe1高?SELECT * FROM employees WHERE salary > (SELECT salary FROM employees WHERE last_name = 'Abel') ; #案例2:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资SELECT last_name, job_id, salary FROM employees WHERE job_id = ( SELECT job_id FROM employees WHERE employee_id = 141 ) AND salary > ( SELECT salary FROM employees WHERE employee_id = 143 ) ;#案例3:返回公司工资最少的员工的1ast name, Job id和sa1axySELECT last_name,job_id,salaryFROM employeesWHERE salary=( SELECT MIN(salary) FROM employees);#案例4:查询最低工资大于50号部门最低工资的部门和其最低工资SELECT department_id, MIN(salary) FROM employees GROUP BY department_id HAVING MIN(salary) > (SELECT MIN(salary) FROM employees WHERE department_id = 50) ; #非法使用子查询情况:查询结果多行用于一行的(多对一);子查询结果无 列子查询(多行子查询) 返回多行,使用多行比较操作符 IN / NOT IN :等于列表中的任意一个 ANY / SOME :和子查询返回的某个值比较 ALL :和子查询返回的所有值比较 1234567891011121314151617181920212223242526272829#案例1:返回1ocation id是1400或1700的部门中的所有员工姓名SELECT last_name FROM employees WHERE department_id IN (SELECT DISTINCT department_id FROM departments WHERE location_id IN (1400, 1700)) ;#案例2:返回其它部门中比job_id为'IT_PROG'部门任一工资低的员工的员工号、姓名、 job id以及sa1arySELECT employee_id,last_name,job_id,salaryFROM employeesWHERE salary < (SELECT MAX(salary)FROM employeesWHERE job_id='IT_PROG') AND job_id<>'IT_PROG';#案例3:返回其它部门中比job_id为'IT_PROG'部门所有工资低的员工的员工号、姓名、 job id以及sa1arySELECT employee_id,last_name,job_id,salaryFROM employeesWHERE salary < (SELECT MIN(salary)FROM employeesWHERE job_id='IT_PROG'); 行子查询(一行多列或者多行多列) 123456789101112131415161718#案例:查询员工编号最小并且工资最高的员工信息#普通SELECT *FROM employeesWHERE employee_id=( SELECT MIN(employee_id) FROM employees) AND salary=( SELECT MAX(salary) FROM employees)#一行多列SELECT *FROM employeesWHERE (employee_id,salary)=( SELECT MIN(employee_id) ,MAX(salary) FROM employees) 2、select后面(仅仅支持标量查询) 123456789101112131415161718192021222324252627282930313233#????案例:查询每个部门的员工个数###错误的方法:(不知道咋整)SELECT d.*,COUNT(*)FROM departments dLEFT JOIN employees eON d.`department_id`=e.`department_id`GROUP BY d.department_id###改进后容易错的方法:(不知道咋整)SELECT d.*,COUNT(e.`employee_id`)FROM departments dLEFT JOIN employees eON d.`department_id`=e.`department_id`GROUP BY d.department_id###select子查询方法SELECT d.*,( SELECT COUNT(*) FROM employees e WHERE d.`department_id`=e.`department_id`) 个数FROM departments d;#案例2:查询员工号=102的部门名SELECT department_nameFROM departmentsWHERE department_id=( SELECT department_id FROM employees WHERE employee_id=102);###select那种子查询真变扭,不是很会 3、from后面 将子查询结果充当一张表,要求必须起别名 4、exists后面(相关子查询) ps:感觉像个监测是否存在的布尔型函数,误 语法: 123exists(完整的语句)#结果为1 或者 0#前面记得加where 能有exists的都能用in解决 123456789101112131415#案例1:查询有员工的部门名SELECT department_nameFROM departments dWHERE EXISTS( SELECT * FROM employees e WHERE d.`department_id`=e.`department_id`);###(记得加where)用inSELECT department_nameFROM departments dWHERE d.`department_id` IN( SELECT e.`department_id` FROM employees e); 分页查询应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求 语法: 123456789101112select 查询列表from 表[Join type] join 表2[on 连接条件][where 筛选条件][group by 分组字段][having 分组后的筛选][order by 排序的字段]limit [offset,] size;# offset 要显示条目的起始素引(起始索引从0开始)缺省为0开始# size要显示的条目个数 特点: limit放在查询语句最后,也是最后在执行 公式 要显示的页数 page,煤业的条目数 size limit (page-1)*size,size; 12345678910111213141516171819202122#案例1:查询前五条员工信息SELECT * FROM employees LIMIT 0, 5 ;#案例2:查询第1条一第25条SELECT * FROM employees LIMIT 10, 15 ;#案例3:有奖金的员工信息,并且工资较高的前10名显示出来SELECT * FROM employees WHERE commission_pct IS NOT NULL ORDER BY salary * (1+ IFNULL(commission_pct, 0)) DESC LIMIT 10 ; 联合查询union:将多条查询语句的结果合并成一个结果 语法: 12345查询语句1union查询语句2union... 应用场景: 要查询的结果来自多个表,且多个表之间没有直接的连接关系,但查询的信息一致时。 特点: 要求多条查询语句查询的列数一致 多条查询语句查询的顺序是一致的 union会去重,不需要去重时使用UNION all 查询总结DML语言数据操作语言: 插入:insert 修改:update 删除:delete 插入语法: 12#方式一:insert into 表名(列名,...) values(值1,...),values(值2,...); 特点: 插入的值类型必须与列的类型一致或者兼容 不可以为null的列必须插入值,可以为null的列可以填null或者在列名列表中不写对应的列名。 列的顺序可以调换,但是必须一一对应 列数与值的个数必须一致 列名缺省代表默认列顺序与表的顺序一致 123#方式二:insert into 表名set 列名=值,列名=值,...; 区别: 方式一支持插入多行,二不支持 方式一支持子查询,二不支持 修改语法: 12345678910111213141516171819#1.修改单表的记录update 表名 #1set 列名=新值,列=新值,... #3where 筛选条件; #2#2.修改夺标的记录#sql92语法update 表1 别名, 表2 别名set 列=值where 连接条件and 筛选条件;#sql99语法update 表1 别名[连接类型] join 表2 别名on 连接条件set 列=值,...where 筛选条件; 删除语法: 123456789101112131415161718192021##方式一:delete#1.单表的删除delete from 表名 where 筛选条件#2.多表的删除#sql92语法delete 表1的表名,表2的别名from 表1 别名,表2 别名where 连接条件and 筛选条件;#sql99语法delete 表1的表名,表2的别名from 表1 别名[连接类型] join 表2 别名on 连接条件where 筛选条件;##方式二:truncatetruncate table 表名; 区别:delete VS. truncate delete可以加 where条件, truncate不能加 truncate删除,效率高一丢丢 假如要删除的表中有自增长列,如果用delete删除后,再插入数据,自增长列的值从断点开始,而 truncate删除后,再插入数据,自增长列的值从1开始 truncate删除没有返回值,delete删除有返回值 truncate删除不能回滚,delete删除可以回滚 DDL语言数据定义语言:库和表的管理 创建:create 修改:alter 删除:drop 库的管理#1.库的创建 语法: 1create database [if not exists] 库名; #2.库的修改 12#更改库的字符集alter database 库名 character set 字符集; #3.库的删除 123drop database [if exists] 库名;#desc databases; 表的管理#1.表的创建 语法: 123456create table [if not exists] 表名( 列名 列的类型 [长度 约束], 列名 列的类型 [长度 约束], ... 列名 列的类型 [长度 约束]); #2.表的修改 12345678910111213141516alter table 表名 [类型关键词] column 列名 [列类型 约束];#①修改列名alter table 表名 change column 列名 新列名 [新类型];#②修改列的类型或约束alter table 表名 modify column 列名 新类型;#③添加新列alter table 表名 add column 新列名 新类型 [first | after 字段名];#④删除列alter table 表名 drop column [if exists] 列名;#⑤修改表名alter table 表名 rename to 新表名; #3.表的删除 123drop table [if exists] 表名;#show tables; 通用写法: 12345drop database if exists 旧库名;create database 新库名;drop table if exists 旧表名;create table 新表名(); #4. 表的复制 123456789101112131415161718#1.仅仅复制表的结构create table 新表名 like 源表名;#2.复制表的结构和数据create table 新表名 select * from 源表名;#3.复制表的部分数据create table 新表名 select 列名 from 源表名where 筛选条件;#4.仅仅复制表的某些字段(不包含数据)create table 新表名 select 列名 from 源表名where 0; 数据类型 数值型: 整型 小数: 定点数 浮点数 字符型: 较短的文本:char、 varchar 较长的文本:text、blob(较长的二进制数据) 日期型 选择原则:所选择的类型越简单越好,能保存数值的类型越小越好。 1、整型: tinyint smallint mediumint int/integer bigint 1 2 3 4 8 特点: 缺省有符号,若设置无符号须在int后加unsigned关键字 若插入数值超出整型范围,会报out of range异常,并且插入临界值 若不设置长度,会有默认的长度。长度代表显示的最大宽度,若不够0在左边填充,但必须搭配关键字zerofill,且会默认成unsigned 2、小数 分类: 浮点型:float(M,D) 4byte; double(M,D) 8byte 定点型:dec(M,D) / decimal(M,D) 特点: 1. M整数+小数的位数;D代表效数部位。超出范围则插入临界值。 2. M和D都可以省略,但如果是decimal默认(M,D)为(10,0);如果是float和double,则会根据插入的数值的精度来决定精度. 3. 定点型的精确度较高,如果要求插入数值的精度较高如货币运算等则考虑使用 3、字符型(串数据) 较短的文本:char、 varchar较长的文本;text、blob(较长的二进制数据);bit(M)位类型;binary和varbinary包含二进制字符串;Enum类型,要求插入的值必须属于列表中指定的值之一;set类型保存集合,类型一次可以选取多个成员,而Enum只能选一个。 写法 M的意思 特点 空间的耗费 效率 char(M) 最大的字符数,缺省为1 固定长度的字符 比较耗费 高 varchar(M) 最大的字符数,不可省略 可变长度的字符 比较节省 低 4、日期型 分类: date只保存日期 time只保存时间 year只保存年 datetime保存日期+时间 timestamp保存日期+时间 特点: 字节 范围 时区等的影响 datetime 8 1000-9999 不受 timestamp 4 1970-2038 受 常见的约束含义:一种限制,用于限制表中的数据,为了保证表中的数据的准确性和可靠性。 分类:六大约束 NOT NULL:非空,用于保证该字段的值不能为空,比如姓名、学号等 DEFAULT:默认,用于保证该字段有默认值,比如性别 PRIMARY KEY:主键,用于保证该字段的值具有唯一性,并且非空,比如学号、员工编号等 UNIQUE:唯一,用于保证该字段的值具有唯一性,可以为空,比如座位号 CHECK:检查约束【mysql中不支持】,比如年龄、性别 FOREIGN KEY:外键,用于限制两个表的关系,用于保证该字段的值必须来自于主表的关联列的值,在从表添加外键约束,用于引用主表中某列的值,比如学生表的专业编号,员工表的部门编号,员工表的工种编号 主键 VS. 唯一 唯一性 支持组合键 存在个数 是否允许为空 主键 √ √(不推荐) 至多1个 不允许 唯一 √ √(不推荐) 可以有很多个 可以(null只能出现一次) 外键: 要求在从表设置外键关系 从表的外键列的类型和主表的关联列的类型要求一致或兼容,名称无要求 主表的关联列必须是一个key(一般是主键或唯一) 插入数据时,先插入主表,再插入从表删除数据时,先删除从表,再删除主表 可以通过以下两种方式来删除主表的记录 12345#方式一:级联删除(删除从表内容,主表也删除)ALTER TABLE stuinfo ADD CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE CASCADE;#方式二:级联置空(删除从表内容,主表相关内容置空)ALTER TABLE stuinfo ADD CONSTRAINT fk_stu_major FOREIGN KEY(majorid) REFERENCES major(id) ON DELETE SET NULL; 格式: 12345678910111213141516171819create table 表名( 字段名 字段类型 not null, #非空 字段名 字段类型 primary key, #主键 字段名 字段类型 unique, #唯一 字段名 字段类型 default 值, #默认 constraint 约束名 foreign key(字段名) references 主表(被引用列))#通用的写法:CREATE TABLE IF not EXISTS stuinfo( id INT PRIMARY KEY, stuname VARCHAR(20) NOT NULL, sex CHAR(1), age INT DEFAULT 18, seat INT UNIQUE, majoris INT, CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id)); 约束的添加分类: 列级约束:六大约束语法上都支持,但外键约束没有效果 表级约束:除了非空、默认,其他的都支持 区别 位置 支持的约束类型 是否可以起约束名 列级约束 列的后面 语法都支持,但外键没有效果 不可以 表级约束 所有列的下面 默认和非空不支持,其他支持 可以(主键没有效果) 添加约束的时机: 创建表时 修改表时 一、创建表时添加约束 1.添加列级约束语法:直接在字段名和类型后面追加约束类型即可,只支持:默认、非空、主键、唯一键 123456789USE students;CREATE TABLE stuinfo( Id INT PRIMARY KEY, #主键 stuName vARCHAR(20) NOT NULL, #非空 gender CHAR(1) CHECK(gender=男 OR gender=女!), #检查 seat INT UNIQUE, #唯一 age INT DEFAULT 18, #默认约束 majorId INT REFERENCES major(Id) #外键(无用)); 12###PS:查看索引(包括主键、外键和唯一)SHOW INDEX FROM 表名 2.添加表级约束 语法:在各个字段的最下面添加 constraint 约束名 约束类型(字段名) 123456789101112DROP TABLE IF EXISTS stuinfo;CREATE TABLE stuinfo( id INT, stuname VARCHAR(20), gender CHAR(1), seat INT, age INT, majoris INT, CONSTRAINT pk PRIMARY KEY(id), #主键 CONSTRAINT uq UNIQUE(seat), #唯一键 CONSTRAINT CK CHECK(gender='男' OR gender='女'), #检查 constraint fk stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id) #外键); 二、修改表时添加约束 1、添加列级约束 1alter table 表名 modify column 字段名 字段类型 新约束; 2、添加表级约束 1alter table 表名 add [constraint 约束名] 约束类型(字段名) [REFERENCES 被引用列]; 1234567891011121314151617181920212223242526272829303132DROP TABLE IF EXISTS stuinfo;CREATE TABLE stuinfo( id INT, stuname VARCHAR(20), gender CHAR(1), seat INT, age INT, majorid INT);#1.添加非空约束ALTER TABLE stuinfo MODIFY COLUMN stuname VARCHAR(20) NOT NULL;#2.添加默认约束ALTER TABLE stuinfo MODIFY COlUMN age INT DEFAULT 18;#3.添加主键##①列级约束ALTER TABLE stuinfo MODIFY column id INT PRIMARY KEY;##②表级约束ALTER TABLE stuinfo ADD PRIMARY KEY(id);#4.添加唯一##①列级约束ALTER TABLE stuinfo MODIFY COLUMN seat INT UNIQUE;##②表级约束ALTER TABLE stuinfo ADD UNIQUE(seat);#5.添加外键ALTER TABLE stuinfo ADD CONSTRAINT fk_stuinfo_major FOREIGN KEY(majorid) REFERENCES major(id);###非空和默认只有alter modify;外键只有add 三、修改表时删除约束 12345678910111213141516#1.删除非空约束ALTER TABLE stuinfo MODIFY COlUMN stuname VARCHAR(20) NULL;#2.删除默认约束ALTER TABLE stuinfo MODIFY Column age INT;#3.删除主键ALTER TABLE stuinfo DROP PRIMARY KEY;#4.删除唯一ALTER TABLE stuinfo drop index 索引名;#5.删除外键ALTER TABLE stuinfo DROP FOREIGN KEY fk_stuinfo_major;###非空和默认只有alter modify;其他用drop 标识列 (又称为自增长列) 含义:可以不用手动的插入值,系统提供默认的序列值 特点:1、标识列不一定和主键搭配,但要求是一个key2、一个表至多一个标识列3、标识列的类型只能是数值型4、标识列可以通过 SET auto_increment_increment=3;设置步长。可以通过手动插入值,设置起始值。 一、创建表时设置标识列 123456789/*create table 表( 字段名 字段类型 约束 auto_increment);*/DROP TABLE IF EXISTS tab_identity;CREATE TABLE tab_identity( Id INT PRIMARY KEY AUTO_INCREMENT NAME VARCHAR(20)); 二、修改表时设置标识列 123#alter table 表 modify column 字段名 字段类型 约束 auto_incrementALTER TABlE tab_identity MOdIFY column id INT PRIMARY KEY AUTO_increment; 三、修改表时删除标识列 123#alter table 表 modify column 字段名 字段类型 约束;ALTER TABLE tab_identity MODIFY column id INT; TCL语言transaction control language 事务控制语言 事务事务:一个或一组sql语句组成一个执行单位,这个执行单位要么全部执行,要么全部不执行。(事务:事务由单独单元的一个或多个QL语句组成,在这个单元中,每个MSQL语句是相互依赖的。而整个单独单元作为一个不可分割的整体,如果单元中某条SQL语句且执行失败或产生错误整个单元将会回滚。所有受到影响的数据捋返回到事物开始以前的状态;如果单元中的所有SQL语句均执行成功,则事物被顺利执行。) 存储引擎:数据用各种不同的技术存储在文件(或内存)中。在MySQL中用的最多的存储引擎有:innodb,myisam,memory等,其中innodb支持事务,另外两种不支持。可以通过show engines来查看MySQL支持的存储引擎。 事务的ACID(acid)属性 原子性(Atomicity) 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么不发生。 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另一个一致性状态 隔离性(Isolation) 一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰 持久性(Durability) 事务一旦提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响 事务的创建 隐式事务:事务没有明显的开启和结束的标记,比如 insert、 update、de1ete语句 显式事务:事务具有明显的开启和结束的标记,前提:必须先设置自动提交功能为禁用set autocommit=0; 12345678910111213141516171819#步骤1:开启事务set autocommit=0; start transaction; #可选的#步骤2:编写事务中的sq1语句(select insert update delete)步骤3:结束事务commit; #提交事务rollback; #回滚事务##演示事务的使用步骤#开启事务SEt autocommit=0;START TRANSACTION;#编写一组事务的语句UPDATE account set ba1ance=500 WHERE username='张无忌';UPDATE account set balance 1500 WHERE username='赵敏';#结束事务commit; 事务的并发问题: 多个事务 同时 操作 同一个 数据库的相同数据时。 对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题: 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的(一个事物读取了其他事务还没有提交的“更新”数据) 不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了(一个事务多次读取,结果不一致) 幻读:对于两个事务T1,T2,T1从一个表中读取了一个字段然后T2在该表中插入了一些新的行。之后,如果T1再次读取同一个表,就会多出几行(一个事物读取了其他事务还没有提交的“插入”数据) 一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度隔离级别越高,数据一致性就越好,但并发性越弱。 事务的隔离级别: 事务的隔离级别: 脏读 不可重复读 幻读 read uncommitted √ √ √ read committed × √ √ repeatable read × × √ serializable × × × MySQL中默认repeatable read Oracle默认read committed READ UNCOMMITTED(读未提交数据) 允许事务读取未被其他事物提交的变更脏读,不可重复读和幻读的问题都会出现 READ COMMITED(读已提交数据) 只允许事务读取已经被其它事务提交的变更可以避免脏读,但不可重复读和幻读问题仍然可能出现 REPEATABLE READ(可重复读) 确保事务可以多次从一个字段中读取相同的值在这个事务持续期问,禁止其他事物对这个字段进行更新可以避免脏读和不可重复读,但幻读的问题仍然存在 SERIALIZABLE(串行化) 确保事务可以从一个表中读取相同的行在这个事务持续期间,禁止其他事务对该表执行插入,更新和删除操作所有并发问题都可以避免,但性能十分低下 12345#查看隔离级别select @@tx_isolation;#设置隔离级别set session|global transacion isolation level 等级名称 PS:每启动一个MySQL程序,就会获得一个单独的数据库连接,每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。 12345678910111213#2.演示事务对于 delete和 truncate的处理的区别SeT autocommit=0;START TRANSACTION; DELETE FROM account;ROLLBACK;#3,演示 savepoint的使用SET autocommit=0;START TRANSACTION;DELETE FROM account WHERE id=25;SAVEPOINT a; #设置保存点DELETE FROM account WhErE id=28;ROLLBACK to a; #回滚到保存点 其他视图含义:虚拟表,和普通表一样使用,通过表动态生成的数据(只保存了sql逻辑,不保存查询结果),临时性的。 1234567create view v1asselect stuname,majornamefrom stuinfo sinner join major m on s.majorid = m.id;select * from v1 where stuname like '张%'; 使用场景: 多个地方用到相同的查询结构 该查询结果使用的sql语句较复杂 好处: 重用sql语句 简化复杂的sql操作,不必知道它的查询细节 保护基表的数据,提高安全性 视图 VS. 表 关键字 物理空间 使用 create view 只保存了sql逻辑 一般不能增删改 create table 保存了数据 增删改查 创建视图: 1234#语法create view 视图名as查询语句; 修改视图: 123456789#方式一create or replace view 视图名as查询语句;#方式二alter view 视图名as查询语句; 删除视图: 1drop view 视图名,视图名,...; 查看视图: 123desc 视图名;show create view 视图名; 视图的更新 1#正常的增删改 视图的可更新性和视图中查询的定义有关系,以下类型的视图是不能更新的。(视图更新表也会更新) 包含以下关键字的sql语句:分组函数、 distinct、 group by、 having、 union或者 union all 常量视图 Select包含子查询 join from一个不能更新的视图 where子句的子查询引用了fom子句中的表 变量 系统变量 全局变量 会话变量 自定义变量 用户变量 局部变量 PS:如果是全局级别,则需要加 GLOBA,如果是会话级别,则需要加 SESSION,如果不写,则默认 SESSION #一、系统变量 说明:变量由系统提供的,不是用户定义,属于服务器层面使用的语法: 123456789101112131415#1、查看所有的系统变量show global | [session] variables#2、查看满足条件的部分系统变量show global | [session] variab1es like '%char%';#3、查看指定的某个系统变量的值select @@g1oba1 | [session].系统变量名;#4、为某个系统变量赋值##方式一:set global | [session] 系统变量名=值;##方式二:set @@global | [session].系统变量名=值 全局变量作用域:服务器每次启动将为所有的全局变量赋初始值,针对于所有的会话(连接)有效,但不能跨重启。 会话变量作用域:仅仅针对于当前会话(连接)有效 #二、自定义变量 说明:变量是用户自定义的,不是由系统 使用步骤: 声明 赋值 使用(查看、比较、运算等) 用户变量作用域:针对于当前会话(连接),同于会话变量的作用域。应用在任何地方,也就是 begin end里面或 begin end外面 1234567891011121314151617#赋值的操作符:=或:=#①声明并初始化SET 用户变量名=值;SET 用户变量名:=值;SELECT 用户变量名:=值;#②赋值(更新用户变量的值)##方式一:通过SET或 SELECT SET 用户变量名=值;SET 用户变量名:=值;SELECT 用户变量名:=值;##方式二:通过 SELECT INTO SELECT 字段 INTO @变量名FROM 表;#③使用(查看用户变量的值)SELECT 用户变量名; 局部变量 作用域:仅仅在定义它的 begin end中有效,而且只能在 begin end中的第一句话 123456789101112131415#①声明DECLARE 变量名 类型;DECLARE 变量名 类型 DEFAULT 值;#②赋值##方式一:通过SET 或 SELECTSET 局部变量名=值;SET 局部变量名:=值;SELECT @局部变量名:=值;##方式二:通过 SELECT INTO SELECT 字段 into 局部变量名FROM 表;#③使用SELECT 局部变量名; 用户变量 VS. 局部变量 作用域 定义和使用的位置 语法 用户变量 当前会话 会话中的任何地方 必须加@符号,不用限定类型 局部变量 BEGIN END中 只能在 BEGIN END中,且为第一句话 一般不用加@符号,需要限定类型 12345678910111213141516#案例:声明两个变量并赋初值,求和并打印#1、用户变量set @m=1;set @n=2;set @sum = @m + @n;select @sum;#2、局部变量begindeclare m int default 1;declare n int default 2;declare sum int;set sum=m+n;select sum;...end 存储过程和函数类似于java中的方法 好处: 提高代码的重用性 简化操作 存储过程 含义:一组预先编译好的sα语句的集合,理解成批处理语句 1、提高代码的重用性 2、简化操作 3、减少了编译次数并且减少了和数据库服务器的连接次数,提高了效率 #一、创建语法 1234create procedure 存储过程名(参数列表)begin 存储过程体#(一组合法的SQL语句)end PS: 1、参数列表包含三部分: 参数模式 参数名 参数类型 eg: IN stuname varchar(20) 参数模式: IN:该参数可以作为输入,也就是该参数需要调用方传入值 OUT:该参数可以作为输出,也就是该参数可以作为返回值 INOUT:该参数既可以作为输入又可以作为输出,也就是该参数既需要传入值,又可以返回值 2、如果存储过程体仅仅只有一句话, BEGIN END可以省略。存储过程体中的每条s语句的结尾要求必须加分号存储过程的结尾可以使用 DELIMITER重新设置 语法 DELIMITER 结束标记 DELIMITER $ #二、调用语法 1call 存储过程名(实参列表); 12345678910#1.空参列表#案例:插入到admin表中五条记录delimiter $create procedure myp1()begin insert into admin(username,password) values('goodwell','0000'),values('goodwell','0000'),values('goodwell','0000'),values('goodwell','0000'),values('goodwell','0000')end $#调用call myp1()$ 123456789101112131415161718192021222324#2.创建带in模式参数的存储过程#案例1:创建存储过程实现根据女神名,查询对应的男神信息create procedure myp2(in beautyName varcher(20))begin select bo.* from boys bo right join beauty b on bo.id = b.boyfriend_id where b.name=beautyName;end $#调用CALL myp2('gowd')$#案例2:创建存储过程实现,用户是否登录成功create procedure myp3(in username varcher(20),in passsword varchar(20))begin declare result int default 0; select count(*) int result from admin where username=admin.username and password=admin.password; select if(result>0,'yes','no');end $ 1234567891011121314#3、创建带out模式的存储过程#案例1:根据女神名,返回对应的男神名create procedure myp5(in beautyName varcher(20),out boyName varchar(20))begin select bo.boyName into boyName from boys bo right join beauty b on bo.id = b.boyfriend_id where b.name=beautyName;end $#调用call myp5('godw',@bName) $select @bName 1234567#4.创建带 anout模式参数的存储过程#案例1:传入a和b两个值,最终a和b都翻倍并返回create procedure myp5(inout a int,inout b int)begin set a=a*2; set b=b*2;end $ #三、删除存储过程语法 1drop procedure 存储过程名 #四、查看存储过程的信息 123desc 存储过程名#×show create procedure 存储过程名; 函数 与存储过程的区别: 存储过程:可以有0个或者多个返回,适合做批量插入、更新 函数:有且只有一个返回,适合做处理数据后返回一个结果 #一、创建语法 1234create function 函数名(参数列表) returns 返回类型begin 函数体end 注意:1.参数列表包含两部分: 参数名 参数类型2.函数体: 肯定会有 return语句,如果没有会报错 如果 return语句没有放在函数体的最后也不报错,但不建议return值3.函数体中仅有一句话,则可以省略 begin end4.使用delimiter语句设置结束标记delimiter $ #二、调用语法 1select 函数名(参数); 1234567891011#1.无参有返回#案例:返回公司的员工个数CREATE FUNCTION myf1() RETURNS INT BEGIN DECLARE C INT DEFAULT 0;#定义变量 SELECT COUNT(*) into c #赋值 FROM employees; RETURN C;END $ SELECT myf1() $ 123456789101112131415161718192021222324#2.有参有返回#案例1:根据员工名,返回它的工资CREATE FUNCTION myf2(empName VARCHAR(20)) RETURNS DOUBLEBEGIN sET8sa1=0;#定义用户变量 SELECT Salary INTO sa1 #赋值 FROM employees WHERE last_name=empName; return sa1;END $ SELECT myf2()#案例2:根据部门名,返回该部门的平均工资CREATE FUNCTION myf3(deptName VARCHAR(20)) RETURNS DOUBLE BEGIN DECLARE sal DOUBlE; SELECT AVG(salary) INTo sal FROM employees e JOIN departments d on e.department_id= d.department_id WHERE d.department_name=deptName; RETURN sal;END $SELECT myf3('IT') $ #三、查看函数 1SHOW CREATE FUNCTION 函数名; #四、删除函数 1DROP FUNCTION 函数名; 流程控制结构 顺序结构:程序从上往下依次执行 分支结构:程序从两条或多条路径中选择一条去执行 循环结构:程序在满足一定条件的基础上,重复执行一段代码 #一、分支结构 #1.if 函数功能:实现简单的双分支 语法: 1if(表达式1,表达式2,表达式3); 执行顺序:如果表达式1成立,则if函数返回表达式2的值,否则返回表达式3的值 作用:任何地方 #2.case结构 情况1:类似于java中的 switch语句,一般用于实现等值判断 语法: 12345CASE 变量表达式/字段WHEN 要判断的值 THEN 返回的值1或语句1;WHEN 要判断的值 THEN 返回的值2或语句2;ELSE 要返回的值n或语句n;END CASE; 情况2:类似于java中的多重if语句,一般用于实现区间判断 语法: 12345CASE WHEN 要判断的条件 THEN 返回的值1或语句1;WHEN 要判断的条件 THEN 返回的值2或语句2;ELSE 要返回的值n或语句n;END CASE; 特点: 可以作为表达式,嵌套在其他语句中使用,可以放在任何地方, BEGIN END中或 BEGIN END的外面 可以作为独立的语句去使用,只能放在 BEGIN END中 如果WHEN中的值满足或条件成立,则执行对应的THEN后面的语句,并且结束 CASE;如果都不满足,则执行ELSE中的语句或值 ELSE可以省略,如果ELSE省略了,并且所有WEN条件都不满足,则返回NULL 12345678910#案例创建存储过程,根据传入的成绩,来显示等级,比如传入的成绩:90-100,显示λ,80-90,显示B,60-80,显示c,否则,显示D CREATE PROCEDURE test_case(IN score INT)BEGIN CASE When score>=90 And score<=100 THEN SELECT 'A'; WheN score>=80 THEN SELECT 'B'; WHEN score>=60 THEN SElECT 'C'; ELSE SELECT 'D'; END CASE END $ CALI test case(95) $ #3.if结构 功能:实现多重分支 语法: 1234if 条件1 then 语句1;e1se if 条件2 then 语句2;[e1se 语句n;]end if; 应用在 begin end中 12345678#案例1:根据传入的成绩,来显示等级,比如传入的成绩:90-100,返回A,80-90,返回B,60-80,返回c,否则,返回D CREATE FUNCTION test_if(score INT) RETURNS CHAR BEGIN IF score>=90 AND score<=100 THEN RETURN 'A'; ELSE IF score>=80 THEN RETURN 'B'; ELSE IF score>=60 THEN RETURN 'C'; ELSE RETURN 'D'; end if;END $ #二、循环结构分类:while、loop、 repeat iterate类似于 continue,结束本次循环,继续下一次 leave类似于 break,跳出,结束当前所在的循环 #1.while 语法: 12345678[标签:] while 循环条件 do 循环体;end while [标签];/*联想:while(循环条件){ 循环体;}*/ #2.loop 语法: 123[标签:] loop 循环体;end loop [标签]; 可以用来模拟简单的死循环 #3.repeat 语法: 123456[标签:] repeat 循环体;until 结束循环的条件end repeat [标签];##类似do-while 123456789101112#没有添加循环控制语句普案例:#批量插入,根据次数插入到 admin表中多条记录CREATE PROCEDURE pro_whilel(IN insertcount INT)BEGIN DECLARE i INT DEFAULT 1; WHILE i<=insertcount Do INSERT INTo admin(username, password) VALUES (concat('Rose',1),666); SET i=i+1; END WHILE;END $ CALL pro_whilel(100) $ 12345678910111213#2.添加leave语句#案例:批量插入,根据次数插入到 admin表中多条记录,如果次数>20则停止CREATE PROCEDURE test_whilel(IN insertcount INT)BEGIN DECLARE i INT DEFAULT 1; a:WHILE i<=insertcount Do INSERT INTO admin(username, password) VALUES (CONCAT('xiaohua',1),0000) IF i>=20 THEN LEAVE a; END IF; SET i=i+1; END WHILE a;END $CALL test_whilel(100) $ 12345678910111213#3.添加 iterate语句#案例:批量插入,根据次数插入到 admin表中多条记录,只插入偶数次CREATE PROCEDURE test_whilel(IN insertcount INT)BEGIN DECLARE i INT DEFAULT 1; a:WHILE i<=insertcount Do INSERT INTO admin(username, password) VALUES (CONCAT('xiaohua',1),0000) SET i=i+1; IF mod(i,2) THEN iterate a; END IF; END WHILE a;END $CALL test_whilel(100) $ 对比: ①这三种循环都可以省略名称,但如果循环中添加了循环控制语句( leave或 iterate)则必须添加名称。 ②loop一般用于实现简单的死循环;while先判断后执行;repeat先执行后判断,无条件至少执行一次。]]></content>
<categories>
<category>数据库</category>
<category>MySQL</category>
</categories>
<tags>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MySQL语法提纲]]></title>
<url>%2F%E6%95%B0%E6%8D%AE%E5%BA%93%2FMySQL%2FMySQL%E8%AF%AD%E6%B3%95%E6%8F%90%E7%BA%B2%2F</url>
<content type="text"><![CDATA[尚硅谷MySQL核心技术配套笔记md ##本单元目标 一、为什么要学习数据库 二、数据库的相关概念 DBMS、DB、SQL 三、数据库存储数据的特点 四、初始MySQL MySQL产品的介绍 MySQL产品的安装 ★ MySQL服务的启动和停止 ★ MySQL服务的登录和退出 ★ MySQL的常见命令和语法规范 五、DQL语言的学习 ★ 基础查询 ★ 条件查询 ★ 排序查询 ★ 常见函数 ★ 分组函数 ★ 分组查询 ★ 连接查询 ★ 子查询 √ 分页查询 ★ union联合查询 √ 六、DML语言的学习 ★ 插入语句 修改语句 删除语句 七、DDL语言的学习 库和表的管理 √ 常见数据类型介绍 √ 常见约束 √ 八、TCL语言的学习 事务和事务处理 九、视图的讲解 √ 十、变量 十一、存储过程和函数 十二、流程控制结构 ##数据库的好处 1.持久化数据到本地 2.可以实现结构化查询,方便管理 ##数据库相关概念 1、DB:数据库,保存一组有组织的数据的容器 2、DBMS:数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据 3、SQL:结构化查询语言,用于和DBMS通信的语言 ##数据库存储数据的特点 1、将数据放到表中,表再放到库中 2、一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。 3、表具有一些特性,这些特性定义了数据在表中如何存储,类似java中 “类”的设计。 4、表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java 中的”属性” 5、表中的数据是按行存储的,每一行类似于java中的“对象”。 ##MySQL产品的介绍和安装 ###MySQL服务的启动和停止 方式一:计算机——右击管理——服务 方式二:通过管理员身份运行 net start 服务名(启动服务) net stop 服务名(停止服务) ###MySQL服务的登录和退出 方式一:通过mysql自带的客户端 只限于root用户 方式二:通过windows自带的客户端 登录: mysql 【-h主机名 -P端口号 】-u用户名 -p密码 退出: exit或ctrl+C ###MySQL的常见命令 1.查看当前所有的数据库 show databases; 2.打开指定的库 use 库名 3.查看当前库的所有表 show tables; 4.查看其它库的所有表 show tables from 库名; 5.创建表 create table 表名( 列名 列类型, 列名 列类型, 。。。 ); 6.查看表结构 desc 表名; 7.查看服务器的版本 方式一:登录到mysql服务端 select version(); 方式二:没有登录到mysql服务端 mysql --version 或 mysql --V ###MySQL的语法规范 1.不区分大小写,但建议关键字大写,表名、列名小写 2.每条命令最好用分号结尾 3.每条命令根据需要,可以进行缩进 或换行 4.注释 单行注释:#注释文字 单行注释:– 注释文字 多行注释:/ 注释文字 / ###SQL的语言分类 DQL(Data Query Language):数据查询语言 select DML(Data Manipulate Language):数据操作语言 insert 、update、delete DDL(Data Define Languge):数据定义语言 create、drop、alter TCL(Transaction Control Language):事务控制语言 commit、rollback ###SQL的常见命令 show databases; 查看所有的数据库 use 库名; 打开指定 的库 show tables ; 显示库中的所有表 show tables from 库名;显示指定库中的所有表 create table 表名( 字段名 字段类型, 字段名 字段类型 ); 创建表 desc 表名; 查看指定表的结构 select * from 表名;显示表中的所有数据 ##DQL语言的学习 ###进阶1:基础查询 语法: SELECT 要查询的东西 【FROM 表名】; 类似于Java中 :System.out.println(要打印的东西); 特点: ①通过select查询完的结果 ,是一个虚拟的表格,不是真实存在 ② 要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数 ###进阶2:条件查询 条件查询:根据条件过滤原始表的数据,查询到想要的数据 语法: select 要查询的字段|表达式|常量值|函数 from 表 where 条件 ; 分类: 一、条件表达式 示例:salary>10000 条件运算符: > < >= <= = != <> 二、逻辑表达式 示例:salary>10000 && salary<20000 逻辑运算符: and(&&):两个条件如果同时成立,结果为true,否则为false or(||):两个条件只要有一个成立,结果为true,否则为false not(!):如果条件成立,则not后为false,否则为true 三、模糊查询 示例:last_name like 'a%' ###进阶3:排序查询 语法: select 要查询的东西 from 表 where 条件 order by 排序的字段|表达式|函数|别名 【asc|desc】 ###进阶4:常见函数 一、单行函数 1、字符函数 concat拼接 substr截取子串 upper转换成大写 lower转换成小写 trim去前后指定的空格和字符 ltrim去左边空格 rtrim去右边空格 replace替换 lpad左填充 rpad右填充 instr返回子串第一次出现的索引 length 获取字节个数 2、数学函数 round 四舍五入 rand 随机数 floor向下取整 ceil向上取整 mod取余 truncate截断 3、日期函数 now当前系统日期+时间 curdate当前系统日期 curtime当前系统时间 str_to_date 将字符转换成日期 date_format将日期转换成字符 4、流程控制函数 if 处理双分支 case语句 处理多分支 情况1:处理等值判断 情况2:处理条件判断 5、其他函数 version版本 database当前库 user当前连接用户 二、分组函数 sum 求和 max 最大值 min 最小值 avg 平均值 count 计数 特点: 1、以上五个分组函数都忽略null值,除了count(*) 2、sum和avg一般用于处理数值型 max、min、count可以处理任何数据类型 3、都可以搭配distinct使用,用于统计去重后的结果 4、count的参数可以支持: 字段、*、常量值,一般放1 建议使用 count(*) ##进阶5:分组查询 语法: select 查询的字段,分组函数 from 表 group by 分组的字段 特点: 1、可以按单个字段分组 2、和分组函数一同查询的字段最好是分组后的字段 3、分组筛选 针对的表 位置 关键字 分组前筛选: 原始表 group by的前面 where 分组后筛选: 分组后的结果集 group by的后面 having 4、可以按多个字段分组,字段之间用逗号隔开 5、可以支持排序 6、having后可以支持别名 ##进阶6:多表连接查询 笛卡尔乘积:如果连接条件省略或无效则会出现 解决办法:添加上连接条件 一、传统模式下的连接 :等值连接——非等值连接 1.等值连接的结果 = 多个表的交集 2.n表连接,至少需要n-1个连接条件 3.多个表不分主次,没有顺序要求 4.一般为表起别名,提高阅读性和性能 二、sql99语法:通过join关键字实现连接 含义:1999年推出的sql语法 支持: 等值连接、非等值连接 (内连接) 外连接 交叉连接 语法: select 字段,... from 表1 【inner|left outer|right outer|cross】join 表2 on 连接条件 【inner|left outer|right outer|cross】join 表3 on 连接条件 【where 筛选条件】 【group by 分组字段】 【having 分组后的筛选条件】 【order by 排序的字段或表达式】 好处:语句上,连接条件和筛选条件实现了分离,简洁明了! 三、自连接 案例:查询员工名和直接上级的名称 sql99 SELECT e.last_name,m.last_name FROM employees e JOIN employees m ON e.`manager_id`=m.`employee_id`; sql92 SELECT e.last_name,m.last_name FROM employees e,employees m WHERE e.`manager_id`=m.`employee_id`; ##进阶7:子查询 含义: 一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询 在外面的查询语句,称为主查询或外查询 特点: 1、子查询都放在小括号内 2、子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧 3、子查询优先于主查询执行,主查询使用了子查询的执行结果 4、子查询根据查询结果的行数不同分为以下两类: ① 单行子查询 结果集只有一行 一般搭配单行操作符使用:> < = <> >= <= 非法使用子查询的情况: a、子查询的结果为一组值 b、子查询的结果为空 ② 多行子查询 结果集有多行 一般搭配多行操作符使用:any、all、in、not in in: 属于子查询结果中的任意一个就行 any和all往往可以用其他查询代替 ##进阶8:分页查询 应用场景: 实际的web项目中需要根据用户的需求提交对应的分页查询的sql语句 语法: select 字段|表达式,... from 表 【where 条件】 【group by 分组字段】 【having 条件】 【order by 排序的字段】 limit 【起始的条目索引,】条目数; 特点: 1.起始条目索引从0开始 2.limit子句放在查询语句的最后 3.公式:select * from 表 limit (page-1)*sizePerPage,sizePerPage 假如: 每页显示条目数sizePerPage 要显示的页数 page ##进阶9:联合查询 引入: union 联合、合并 语法: select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】 select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】 select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】 ..... select 字段|常量|表达式|函数 【from 表】 【where 条件】 特点: 1、多条查询语句的查询的列数必须是一致的 2、多条查询语句的查询的列的类型几乎相同 3、union代表去重,union all代表不去重 ##DML语言 ###插入 语法: insert into 表名(字段名,…) values(值1,…); 特点: 1、字段类型和值类型一致或兼容,而且一一对应 2、可以为空的字段,可以不用插入值,或用null填充 3、不可以为空的字段,必须插入值 4、字段个数和值的个数必须一致 5、字段可以省略,但默认所有字段,并且顺序和表中的存储顺序一致 ###修改 修改单表语法: update 表名 set 字段=新值,字段=新值 【where 条件】 修改多表语法: update 表1 别名1,表2 别名2 set 字段=新值,字段=新值 where 连接条件 and 筛选条件 ###删除 方式1:delete语句 单表的删除: ★ delete from 表名 【where 筛选条件】 多表的删除: delete 别名1,别名2 from 表1 别名1,表2 别名2 where 连接条件 and 筛选条件; 方式2:truncate语句 truncate table 表名 两种方式的区别【面试题】 #1.truncate不能加where条件,而delete可以加where条件 #2.truncate的效率高一丢丢 #3.truncate 删除带自增长的列的表后,如果再插入数据,数据从1开始 #delete 删除带自增长列的表后,如果再插入数据,数据从上一次的断点处开始 #4.truncate删除不能回滚,delete删除可以回滚 ##DDL语句 ###库和表的管理库的管理: 一、创建库 create database 库名 二、删除库 drop database 库名 表的管理: #1.创建表 CREATE TABLE IF NOT EXISTS stuinfo( stuId INT, stuName VARCHAR(20), gender CHAR, bornDate DATETIME ); DESC studentinfo; #2.修改表 alter 语法:ALTER TABLE 表名 ADD|MODIFY|DROP|CHANGE COLUMN 字段名 【字段类型】; #①修改字段名 ALTER TABLE studentinfo CHANGE COLUMN sex gender CHAR; #②修改表名 ALTER TABLE stuinfo RENAME [TO] studentinfo; #③修改字段类型和列级约束 ALTER TABLE studentinfo MODIFY COLUMN borndate DATE ; #④添加字段 ALTER TABLE studentinfo ADD COLUMN email VARCHAR(20) first; #⑤删除字段 ALTER TABLE studentinfo DROP COLUMN email; #3.删除表 DROP TABLE [IF EXISTS] studentinfo; ###常见类型 整型: 小数: 浮点型 定点型 字符型: 日期型: Blob类型: ###常见约束 NOT NULL DEFAULT UNIQUE CHECK PRIMARY KEY FOREIGN KEY ##数据库事务 ###含义 通过一组逻辑操作单元(一组DML——sql语句),将数据从一种状态切换到另外一种状态 ###特点 (ACID) 原子性:要么都执行,要么都回滚 一致性:保证数据的状态操作前和操作后保持一致 隔离性:多个事务同时操作相同数据库的同一个数据时,一个事务的执行不受另外一个事务的干扰 持久性:一个事务一旦提交,则数据将持久化到本地,除非其他事务对其进行修改 相关步骤: 1、开启事务 2、编写事务的一组逻辑操作单元(多条sql语句) 3、提交事务或回滚事务 ###事务的分类: 隐式事务,没有明显的开启和结束事务的标志 比如 insert、update、delete语句本身就是一个事务 显式事务,具有明显的开启和结束事务的标志 1、开启事务 取消自动提交事务的功能 2、编写事务的一组逻辑操作单元(多条sql语句) insert update delete 3、提交事务或回滚事务 ###使用到的关键字 set autocommit=0; start transaction; commit; rollback; savepoint 断点 commit to 断点 rollback to 断点 ###事务的隔离级别: 事务并发问题如何发生? 当多个事务同时操作同一个数据库的相同数据时 事务的并发问题有哪些? 脏读:一个事务读取到了另外一个事务未提交的数据 不可重复读:同一个事务中,多次读取到的数据不一致 幻读:一个事务读取数据时,另外一个事务进行更新,导致第一个事务读取到了没有更新的数据 如何避免事务的并发问题? 通过设置事务的隔离级别 1、READ UNCOMMITTED 2、READ COMMITTED 可以避免脏读 3、REPEATABLE READ 可以避免脏读、不可重复读和一部分幻读 4、SERIALIZABLE可以避免脏读、不可重复读和幻读 设置隔离级别: set session|global transaction isolation level 隔离级别名; 查看隔离级别: select @@tx_isolation; ##视图含义:理解成一张虚拟的表 视图和表的区别: 使用方式 占用物理空间 视图 完全相同 不占用,仅仅保存的是sql逻辑 表 完全相同 占用 视图的好处: 1、sql语句提高重用性,效率高 2、和表实现了分离,提高了安全性 ###视图的创建 语法: CREATE VIEW 视图名 AS 查询语句; ###视图的增删改查 1、查看视图的数据 ★ SELECT * FROM my_v4; SELECT * FROM my_v1 WHERE last_name='Partners'; 2、插入视图的数据 INSERT INTO my_v4(last_name,department_id) VALUES('虚竹',90); 3、修改视图的数据 UPDATE my_v4 SET last_name ='梦姑' WHERE last_name='虚竹'; 4、删除视图的数据 DELETE FROM my_v4; ###某些视图不能更新 包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all 常量视图 Select中包含子查询 join from一个不能更新的视图 where子句的子查询引用了from子句中的表 ###视图逻辑的更新 #方式一: CREATE OR REPLACE VIEW test_v7 AS SELECT last_name FROM employees WHERE employee_id>100; #方式二: ALTER VIEW test_v7 AS SELECT employee_id FROM employees; SELECT * FROM test_v7; ###视图的删除 DROP VIEW test_v1,test_v2,test_v3; ###视图结构的查看 DESC test_v7; SHOW CREATE VIEW test_v7; ##存储过程 含义:一组经过预先编译的sql语句的集合好处: 1、提高了sql语句的重用性,减少了开发程序员的压力 2、提高了效率 3、减少了传输次数 分类: 1、无返回无参 2、仅仅带in类型,无返回有参 3、仅仅带out类型,有返回无参 4、既带in又带out,有返回有参 5、带inout,有返回有参 注意:in、out、inout都可以在一个存储过程中带多个 ###创建存储过程语法: create procedure 存储过程名(in|out|inout 参数名 参数类型,...) begin 存储过程体 end 类似于方法: 修饰符 返回类型 方法名(参数类型 参数名,...){ 方法体; } 注意 1、需要设置新的结束标记 delimiter 新的结束标记 示例: delimiter $ CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名 参数类型,...) BEGIN sql语句1; sql语句2; END $ 2、存储过程体中可以有多条sql语句,如果仅仅一条sql语句,则可以省略begin end 3、参数前面的符号的意思 in:该参数只能作为输入 (该参数不能做返回值) out:该参数只能作为输出(该参数只能做返回值) inout:既能做输入又能做输出 #调用存储过程 call 存储过程名(实参列表) ##函数 ###创建函数 学过的函数:LENGTH、SUBSTR、CONCAT等语法: CREATE FUNCTION 函数名(参数名 参数类型,...) RETURNS 返回类型 BEGIN 函数体 END ###调用函数 SELECT 函数名(实参列表) ###函数和存储过程的区别 关键字 调用语法 返回值 应用场景 函数 FUNCTION SELECT 函数() 只能是一个 一般用于查询结果为一个值并返回时,当有返回值而且仅仅一个 存储过程 PROCEDURE CALL 存储过程() 可以有0个或多个 一般用于更新 ##流程控制结构 ###系统变量一、全局变量 作用域:针对于所有会话(连接)有效,但不能跨重启 查看所有全局变量 SHOW GLOBAL VARIABLES; 查看满足条件的部分系统变量 SHOW GLOBAL VARIABLES LIKE '%char%'; 查看指定的系统变量的值 SELECT @@global.autocommit; 为某个系统变量赋值 SET @@global.autocommit=0; SET GLOBAL autocommit=0; 二、会话变量 作用域:针对于当前会话(连接)有效 查看所有会话变量 SHOW SESSION VARIABLES; 查看满足条件的部分会话变量 SHOW SESSION VARIABLES LIKE '%char%'; 查看指定的会话变量的值 SELECT @@autocommit; SELECT @@session.tx_isolation; 为某个会话变量赋值 SET @@session.tx_isolation='read-uncommitted'; SET SESSION tx_isolation='read-committed'; ###自定义变量一、用户变量 声明并初始化: SET @变量名=值; SET @变量名:=值; SELECT @变量名:=值; 赋值: 方式一:一般用于赋简单的值 SET 变量名=值; SET 变量名:=值; SELECT 变量名:=值; 方式二:一般用于赋表 中的字段值 SELECT 字段名或表达式 INTO 变量 FROM 表; 使用: select @变量名; 二、局部变量 声明: declare 变量名 类型 【default 值】; 赋值: 方式一:一般用于赋简单的值 SET 变量名=值; SET 变量名:=值; SELECT 变量名:=值; 方式二:一般用于赋表 中的字段值 SELECT 字段名或表达式 INTO 变量 FROM 表; 使用: select 变量名 二者的区别: 作用域 定义位置 语法 用户变量 当前会话 会话的任何地方 加@符号,不用指定类型局部变量 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指定类型 ###分支一、if函数 语法:if(条件,值1,值2) 特点:可以用在任何位置 二、case语句 语法: 情况一:类似于switch case 表达式 when 值1 then 结果1或语句1(如果是语句,需要加分号) when 值2 then 结果2或语句2(如果是语句,需要加分号) ... else 结果n或语句n(如果是语句,需要加分号) end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要) 情况二:类似于多重if case when 条件1 then 结果1或语句1(如果是语句,需要加分号) when 条件2 then 结果2或语句2(如果是语句,需要加分号) ... else 结果n或语句n(如果是语句,需要加分号) end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要) 特点: 可以用在任何位置 三、if elseif语句 语法: if 情况1 then 语句1; elseif 情况2 then 语句2; ... else 语句n; end if; 特点: 只能用在begin end中!!!!!!!!!!!!!!! 三者比较: 应用场合 if函数 简单双分支 case结构 等值判断 的多分支 if结构 区间判断 的多分支 ###循环 语法: 【标签:】WHILE 循环条件 DO 循环体 END WHILE 【标签】; 特点: 只能放在BEGIN END里面 如果要搭配leave跳转语句,需要使用标签,否则可以不用标签 leave类似于java中的break语句,跳出所在循环!!!]]></content>
<categories>
<category>数据库</category>
<category>MySQL</category>
</categories>
<tags>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day8]]></title>
<url>%2FJava%2F%E9%9B%86%E5%90%88%2FJava-day8%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅷ),主要记录Java集合!奋斗ing 集合概述 集合、数组都是对 多个数据 进行 存储操作 的结构,简称Java容器。 说明:此时的存储,主要指的是内存层面的存储,不涉及持久化的存储(.txt,.jpg,.avi,数据库中) 数组在存储多个数据方面的特点(缺点*): (*)一旦初始化后,长度就确定了 一旦定义好,元素的类型也确定了 (*)提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高 (*)获取数组中实际元素的个数的需求,没有现成的属性或方法可用 (*)存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足 Java集合 可以分为Collection 和 Map两种体系(接口) Collection接口:单列数据,定义了存取一些对象的方法的集合 List:元素有序、可重复的集合,“动态数组” Set:元素无序、不可重复的集合,“数学上的集合” Map接口:双列数据,保存具有映射关系”key-value对”的集合,“数学上的函数映射” 集合框架123456789|----Collection接口:单列集合,用来存储一个一个的对象 |----List接口:存储有序的、可重复的数据 --> “动态”数组 |----ArrayList、LinkedList、Vector |----Set接口:存储无序的、不可重复的数据 --> 数学中的“集合” |----HashSet、LinkedHashSet、TreeSet |----Map接口:双列接口,用来存储一对(key-value)数据 --> y = f(x) |----HashMap、LinkedHashMap、TreeMap、Hashtable、Properties Collection常用方法(通用) add(Object e): 将元素e添加到集合coll中 addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 size(): 获取添加的元素的个数 clear(): 清空集合元素 isEmpty(): 判断当前集合是否为空 12345678910111213141516171819202122232425262728@Test public void test1(){ Collection coll = new ArrayList(); //add(Object e): 将元素e添加到集合coll中、 coll.add("AA"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); //size(): 获取添加的元素的个数 System.out.println(coll.size());//4 //addAll(Collection coll1): 将coll1集合中的元素添加到当前的集合中 Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("cc"); coll.addAll(coll1); System.out.println(coll.size());//6 System.out.println(coll);//[AA, BB, 123, Thu Oct 31 12:03:02 CST 2019, 456, cc] //clear(): 清空集合元素 coll.clear(); //isEmpty(): 判断当前集合是否为空 System.out.println(coll.isEmpty());//true } contains(Object obj): 判断当前集合中是否包含obj PS:向Collection接口的实现类的对象中 添加数据obj时,要求obj所在类要重写equals()。不是判断地址,判断内容 123456789101112131415161718192021@Testpublic void test2(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); Person p = new Person("Jerry1", 20); coll.add(p); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //1.contains(Object obj): 判断当前集合中是否包含obj //我们在判断时会调用obj对象所在类的equals() boolean contains = coll.contains(123); System.out.println(contains);//true System.out.println(coll.contains(new String("Tom")));//true System.out.println(coll.contains(new Person("Jerry", 20)));//false System.out.println(coll.contains(p));//true //PS:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()。} containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。 123//2.containsAll(Collection coll1): 判断形参coll1中的所有元素都存在于当前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));//false remove(Object obj): 从当前集合中移除obj元素 1234567891011121314151617@Testpublic void test3(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //3.remove(Object obj): 从当前集合中移除obj元素 coll.remove(123); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false] coll.remove(new Person("Jerry", 20)); System.out.println(coll); //[456, com.good.java.Person@4ee285c6, Tom, false]} removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素 12345//4.removeAll(Collection coll1): 差集,从当前集合中移除coll1中所有的元素Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);//[com.good.java.Person@4ee285c6, Tom, false] retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 123456789101112131415@Testpublic void test4() { Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry", 20)); coll.add(new String("Tom")); coll.add(false); //5.retainAll(Collection coll1): 交集,获取当前集合和coll1集合的交集 Collection coll1 = Arrays.asList(123,456,789); coll.retainAll(coll1); System.out.println(coll); //[123, 456]} equals(Object obj) 123456789 Collection coll1 = new ArrayList(); coll1.add(123); coll1.add(456);// coll1.add(new Person("Jerry", 20)); coll1.add(new String("Tom")); coll1.add(false); //6.equals(Object obj) System.out.println(coll.equals(coll1)); hashCode(): 返回当前对象的哈希值 123456789Collection coll = new ArrayList();coll.add(123);coll.add(456); coll.add(new Person("Jerry", 20));coll.add(new String("Tom"));coll.add(false);//7.hashCode(): 返回当前对象的哈希值System.out.println(coll.hashCode());//701070075 集合 —> 数组:toArray() /拓展:数组 —> 集合: 调用Arrays类的静态方法asList() 1234567891011121314Object[] arr = coll.toArray();for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]);}//拓展:数组 ---> 集合: 调用Arrays类的静态方法asList()List<String> list = Arrays.asList(new String[]{"aa","bb","cc"});System.out.println(list);//[aa, bb, cc]List arr1 = Arrays.asList(new int[]{123,456});System.out.println(arr1.size());//1List arr2 = Arrays.asList(new Integer[]{123,456});System.out.println(arr2.size());//2 iterator() 返回Iterator接口的实例,用于遍历集合元素,注意是一次性的 集合元素的遍历(迭代器接口Iterator) next() 判断是否还有下有一个元素 hasNext() ①指针下移 ②将下移以后集合位置上的元素返回 123456789101112131415161718192021222324252627@Testpublic void test1(){ Collection coll = new ArrayList(); coll.add("aa"); coll.add("BB"); coll.add(123); //自动装箱 coll.add(new Date()); Iterator iterator = coll.iterator(); //方式一:// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next());// System.out.println(iterator.next()); //报异常NoSuchElementException// System.out.println(iterator.next()); //方式二: 不推荐// for (int i = 0; i < coll.size(); i++) {// System.out.println(iterator.next());// } //方式三: 推荐 while (iterator.hasNext()) { System.out.println(iterator.next()); }} remove() 删除集合中某数据(调用前需要先next()) 123456while (iterator.hasNext()) { Object obj = iterator.next(); if ("BB".equals(obj)) { iterator.remove(); }} foreach 循环遍历集合JDK5 新增了foreach 用于遍历数组和集合(内部任然调用迭代器) for(集合元素类型 局部变量:集合对象) 1234for (Object obj : coll) { System.out.println(obj);}// 注意是局部变量,不会改变 List接口替代数组,元素有序,且可重复 具体实现类:ArrayList、LinkedList、Vector 三者异同: 同:三个类都实现了List接口,存储数据的特点相同,元素有序,且可重复 异: ArrayList 作为List接口的主要实现类 ,线程不安全,效率高,底层用Object[]存储 LinkedList 底层用双向链表存储,对于频繁插入、删除操作,此类效率高 Vector 作为List的古老实现类,线程安全,效率低,底层用Object[]存储 Arraylist,的源码分析:JDK7情况下ArrayList list= new ArrayList(/(底层创建了长度是10850bc数 HelementData List. add (123); //eLementData【0】= new Integer(123); List.0(1):/0果此次的添加导致底层 eLementDat数组容量不够,则扩容默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中结论:建议开发中使用带参的构造器:ArrayList List= new Arraylist( unt capacity) JDK8中 ArrayList的变化:ArrayList list= new ArrayList()/)底层0 bject】 elementdata初始化为},并没有创建List.0d(123);//第次调用d()时,底层才创建了长度10的数组,并将数据123添加到 elemen后续的添加和扩容操作与jR7无异 总结:JDK7中的 Arraylist的对象的创建类似于单例的汉式,而8中的ryst的对象的创建类似于单例的像汉式,延迟了数组的创建,节省内存 List中的常用方法 void add (int index, Object ele):在 index位置插入eLle元素 boolean addAll (int index, Collection eles):从 index位置开始特eles中的所有元素添加进来 Object get ( int index):获取指定 index位置的元素 int indexOf (Object obj):返园obj在集合中首次出现的位置 int lastIndexOf (Object obj):返bj在当前集台中末次出现的位置 Object remove ( int index):移除指定inex位置的元素,并返回此元素。(区别于Collection的,eg:List. remove(2); list.remove(new Integer (2);) Object set ( int index, Object ele):设置指定 index位置的元素为ele List subList ( int fromIndex, int toIndex):返从 fromIndex到 toIndex位置的子集合 总结:常用方法 增:add(Object obj) 删:remove(int index) / remove(object obj) 改: set(int index, Object ele) 查:get(int index) 插:add(int index, Object ele) 长度:size() 遍历:①Iterate送代器 ②增强for循环 ③普通的循环 Set接口存储无序的、不可重复的数据 –> 数学中的“集合” |—-HashSet、LinkedHashSet、TreeSet 无序性:不等于随机性,添加的位置不同 不可重复性:保证添加的元素按照equals()判断时,不能返回true PS:Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。 要求: 向Set中添加的数据,其所在类一定要重写hashCode() 和 equals() 重写的方法必须保持一致性,相同的对象必须具有相等的散列码。(技巧:对象中用作equals方法比较的Field,都应该用来计算hashCode值) HashSet: 作为set接口的主要实现类;线程不安全的;可以存储null值 LinkedHashSet: 作为HashSet的子类,遍历内部数据时,可以按照添加的顺序遍历(原因:在添加数据的同时,每个数据还维护了俩应用,记录此数据前一个数据和后一个数据的地址)优点:对于频繁的遍历操作效率更高。 TreeSet: 可以按照添加对象的指定属性,进行排序 1234567891011121314151617181920212223242526272829@Testpublic void test2(){ //HashSet Set set = new HashSet(); set.add(123); set.add("AA"); set.add(false); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }// AA// false// 123 //LinkedHashSet LinkedHashSet linkedHashSet = new LinkedHashSet(); linkedHashSet.add(123); linkedHashSet.add("AA"); linkedHashSet.add(false); Iterator iterator1 = linkedHashSet.iterator(); while (iterator1.hasNext()) { System.out.println(iterator1.next()); }// 123// AA// false} TreeSet:向其中添加数据,要求是相同的对象。比较是否添加的对象相同,此处不使用equals,可以分别实现Comparable和Comparator实现自然和定制排序。 自然排序使用的是compareTo返回0,必须重写compareTo 定制排序使用的是compare返回0,必须重写compare 123456789101112@Testpublic void test2(){ TreeSet set = new TreeSet(); set.add(123); set.add(34); set.add(577); Iterator iterator = set.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); }} Map接口双列接口,用来存储一对(key-value)数据 –> y = f(x) |—-HashMap :作为Map的主要实现类;线程不安全,效率高,能存储null 的key 和value,底层:数组+链表(JDK7)+红黑树(JDK8) |—-LinkedHashMap :保证在遍历map元素时,可以按照添加的顺序实现遍历,对于频繁的遍历操作,此类执行效率高于HashMap|—-TreeMap : 保证按照添加的 key-value对 进行排序,实现排序遍历,按照key自然排序或定制排序,底层使用红黑树。|—-Hashtable : 作为古老的实现类,线程安全,效率低,不能存储null 的key 和value |—-Properties : 常用来处理配置文件,key和value都是String类型、 Map结构的理解: Map中的 key:无序的、不可重复的,使用 Set 存储所有的key —–> key所在的类要重写equals和 hashCode Map中的 value:无序的、可重复的,使用 Collection存储所有的 value —–> value所在的类要重写equals 一个键值对:key- value构成了一个 Entry对象 Map中的 entry:无序的、不可重复的,使用 Set 存所有entry HashMap的底层实现原理 HashMap的底层实现原理?d7为例说明HashMap map new HashMap():在实例化以后,底层创建了长度是16的一维数组 Entry【 table..可能已经执行过多次put map. put(key1, vaLue1)首先,调用key1所在类的 hashcode()计算key1哈希值,此哈希信经过某种算法计算以后,得到在 Entry数组中的存放位置。如果此位置上的数据为空,此时的ey1- value1添加成功情况如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:如果Rey1的哈希值与已经存在的数据的哈希值都不相同,此的key1- value1添加成功。—)况2如果Rey1的哈希值和已经存在的某一个数据(Rey2-vaue2)的哈希值相同,继续比较:调用key1所在类的 equals(key2如果 equals()返aLse:此的key1-vae1添加成功。—情况3如果 equaLs()返回true:使用vLue1营换 value2补充:关于情况2和情况3:此的key1- value1和原来的数据以链表的方式存储 在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来 jdk8相较于jdk7在底层实现方面的不同1. new Hash№p():底层没有创建一个长度为6的数组2.j如k8底层的数组是:Mode【】,非 Entry【3.营次调用put()方法的,底层创建长度为16的数组4.jk7底居结构只有:数组+链表。dR8中底居结构:数组+链表+红黑树当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64的此时此索引位置上的所有数据改为体用红黑树存储。 Map接口的常用方法添加、删除、修改操作 Object put(Object key, Object value):将指定key-value添加到或修改)当前map对象中 void putALL(Map m):将m中的所有key-value对存放到当前map中 Object remove(Object key):移除指定key的key- value对,并返value void clear():清空当前map中的所有数据 元素查询的操作: Object get(Object key):获取指定key对应的value boolean containsKey (Object key):是否包含指定的key boolean containsValue(Object value):是否包含指定的value int size():返回map中 key- value对的个数 boolean isEmpty():判断当前map是否为空 booLean equals(Object obj):判断当前map和参数对象obj是否相等 元视图操作的方法: Set keySet():返回所有key构成的Set集合 Collection values():返园所有 value构成的 Collection集合 Set entrySet():返园所有key-value对构成的Set集台 123456789101112131415161718192021222324252627282930313233343536@Test public void test5(){ Map map = new HashMap(); map.put("AA",123); map.put(45,123); map.put("BB",56); //遍历所有的key集:keySet() Set set = map.keySet(); Iterator iterator = set.iterator(); While(iterator.hasNext()) { System.out.println(iterator,next()); } //遍历所有的value集:values() Collection values = map.values(); for(object obj : values) { System.out.println(obj); } //遍历所有的key-value //方式一 entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); While(iterator1.hasNext()) { Object obj = iterator1.next(); //entrySet:集合中的元素都是entry Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "----" + entry.getValue()); } //方式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); While(iterator2.hasNext()){ object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "=====" + value); }} 总结:常用方法:添加:put(Object key, Object value)删除:remove(Object key)修改:put(Object key, Object value)查询:get(Object key)长度:size()遍历:keySet() / values() / entrySet() 向 TreeMap 中添加key- value,要求 key必须是由同一个类创建的对象,因为要按照key进行排序:自然排序、定制排序 123456789// Properties:常用来处理配置文件。key和value都是 String类型public static void main(String[] args) throws Exception { Properties pros = new Properties(); FileInputstream fis = new FileInputstream(jdbc.properties); pros.load(fis); //加载流对应的文件 String name = pros.getProperty("name"); String password = pros.getProperty("password1"); System.out.println("name =" + name + "password =" password);} Collections工具类 操作Set、List和Map等集合的工具类 ● Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。 ●排序操作:(均为 static方法) reverse(List):反转List中元素的顺序 shuffle(List):对List集合元素进行随机排 sort(List):根据元素的自然顺序对指定List集合元素按升序排序 sort(List, Comparator):根据指定的 Comparator产生的顺序对List集合元素进行排序 swap(List,int,int):将指定List集合中的i 处元素和j 处元素进行交换 查找、替换 Object max( Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max( Collection, Comparator):根据 Comparator指定的顺序,返回给定集合中的最大元素 Object min( Collection)Object min(Collection, Comparator) int frequency(collection, Object):返回指定集合中指定元素的出现次数 void copy( List dest. List src):将src中的内容复制到dest中 boolean replaceAll( List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值 1234567891011121314151617@Testpublic void test2{ List list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(-97); // 报异常:IndexOutOfBounds Exception(" Source does not fit in dest") // List dest = new ArrayList; // Collections.copy(dest, List); // 正确的: List dest = Arrays.asList(new Object(list.size()); System.out.println(dest.size()); // List.sizeof Collections.copy(dest, list); System.out.println(dest);} Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成 线程同步 的集合,从而可以解决多线程并发访问集合时的线程安全。]]></content>
<categories>
<category>Java</category>
<category>集合</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day7]]></title>
<url>%2FJava%2F%E6%9E%9A%E4%B8%BE%E7%B1%BB%2FJava-day7%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅶ),主要记录Java枚举类与注解!奋斗ing 枚举类的使用含义: 类的对象只有有限个,确定的。(当需要定义一组常量时,强烈建议使用枚举类。若枚举类只有一个对象,则可作为单例模式的实现方式。) 如何定义: 方式一:jdk5.0之前,自定义枚举类 方式二:jdk5.0时,可使用enum关键字定义枚举类 123456789101112131415161718192021222324252627282930313233343536373839404142public class enumTest { public static void main(String[] args) { Season spring = Season.SPRING; System.out.println(spring);//Season{seasonName='春天', seasonDesc='春暖花开'} }}//自定义枚举类class Season { //1.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //3.提供当前枚举类的多个对象: public static final修饰 public static final Season SPRING = new Season("春天", "春暖花开"); public static final Season SUMMER = new Season("夏天", "夏日炎炎"); public static final Season AUTUMN = new Season("秋天", "秋高气爽"); public static final Season WINTER = new Season("冬天", "冬天雪地"); //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; } //4.其他诉求2:提供toString() @Override public String toString() { return "Season{" + "seasonName='" + seasonName + '\'' + ", seasonDesc='" + seasonDesc + '\'' + '}'; }} 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class enumTest1 { public static void main(String[] args) { Season1 summer = Season1.SUMMER; System.out.println(summer);//SUMMER System.out.println(Season1.class.getSuperclass());//class java.lang.Enum }}//使用enum关键字定义枚举类//说明:定义的枚举类默认继承于class java.lang.Enumenum Season1 { //1.提供当前枚举类的多个对象,多个对象之间用","隔开,末尾对象";"结束 SPRING("春天", "春暖花开"), SUMMER("夏天", "夏日炎炎"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "冬天雪地"); //2.声明season对象的属性: private final修饰 private final String seasonName; private final String seasonDesc; //2.私有化类的构造器,并给对象属性赋值 private Season1(String seasonName, String seasonDesc) { this.seasonDesc = seasonDesc; this.seasonName = seasonName; } //4.其他诉求1:获取枚举类对象的属性 public String getSeasonName() { return seasonName; } public String getSeasonDesc() { return seasonDesc; }// //4.其他诉求2:提供toString()//// @Override// public String toString() {// return "Season1{" +// "seasonName='" + seasonName + '\'' +// ", seasonDesc='" + seasonDesc + '\'' +// '}';// }} Enum类的主要方法 values() 方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。 valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。 toString():返回当前枚举类对象常量的名称 123456789//values():Season1[] values = Season1.values();for (int i = 0; i < values.length; i++) { System.out.println(values[i]);}//SPRING SUMMER AUTUMN WINTER//valueOf(String objName):返回枚举类中对象名是objName的对象Season1 winter = Season1.valueOf("WINTER");System.out.println(winter);//WINTER 使用enum关键字定义的枚举类实现接口的情况 情况一:实现接口,在enum类中实现抽象方法; 情况二:让枚举类的对象分别实现接口中的抽象方法(每个都不一样) 123456SPRING("春天", "春暖花开"){ @Override public void show(){ System.out.println("春天在哪里"); }} 注解(Annotation)的使用框架 = 注解 + 反射 + 设计模式。 理解Annotation: jdk 5.0 新增的功能 Annotation 其实就是代码里的 特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。 Annotation的使用示例 示例一:生成文档相关的注解 示例二: 在编译时进行格式检查(JDK 内置的三个基本注解) @Override: 限定重写父类方法, 该注解只能用于方法 @Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择 @SuppressWarnings: 抑制编译器警告 示例三: 跟踪 代码依赖性,实现替代配置文件功能 如何自定义注解 注解声明为:publi @interface XXX 内部定义成员,通常使用value表示 可以指定成员的默认值,使用default定义(String[] value default “hello”) 若自定义注解没有成员,表明是一个标识作用 PS: 若注解有成员,在使用注解时,需要指明成员的值 自定义注解必须配上注解的信息处理流程(使用反射)才有意义 自定义注解通常都会指明两个元注解:Retention、Target jdk 提供的4种元注解元注解:对现有的注解进行解释说明的 注解 Retention:指定所修饰的 Annotation 的生命周期:SOURCE \ CLASS(默认行为)\ RUNTIME只有声明为RUNTIME生命周期的注解,才能通过反射获得 Target:用于指定被修饰的 Annotation 能用于修饰那些程序元素 Documented:表示所修饰的注解在被javadoc解析时,保留下来 Inherited:被它修饰的 Annotation 将具有继承性 JDK8中 注解的新特性: 可重复注解 、 类型注解 可重复注解 ① 在MyAnnotation 上声明@Repeatable,成员值为 MyAnnotation.class ② MyAnnotationd Taget 和 Reten等元注解与MyAnnotation相同。 类型注解 ELementType. TYPE PARAMETER 表示该注解能写在类型变量的声明语句中(如:泛型声明)ELementType.TYPE_USE 表示该注解能写在使用类型的任何语句中]]></content>
<categories>
<category>Java</category>
<category>枚举类</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day6]]></title>
<url>%2FJava%2F%E5%B8%B8%E7%94%A8%E7%B1%BB%2FJava-day6%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅵ),主要记录Java常用类!奋斗ing 字符串相关的类String字符串是常量,用双引号引起来,他们的值在创建后就不能改变。 String对象的 字符串内容 是 存储在一个 字符数组final char[] value中。 特点 实现了 Serializable 接口,表示字符串支持序列化的; 实现了 Comparable 接口,表示String可以比较大小 String是一个final类,不可被继承,其代表不可变的字符序列。(不可变性) 体现: 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当对现有的字符串进行拼接时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值; 当调用String的replace()修改字符串,也需要重新指定内存区域赋值。 12public final class String implements java.io.Serializable, Comparable<String>, CharSequence 通过字面量的方式(区别于new方式)给一个字符串赋值,此时的 字符串值 声明在字符串常量池中,字符串常量池 不会重复储存相同的 字符串。 实例化的方式 通过字面量定义 通过new + 构造器 面试题: 12345String s = new String("abc");//此方式创建对象,在内存中创建了几个对象? 俩:一个堆空间中new结构,另一个是char[]对应的常量池中的数据“abc”String s1 = new String("abc");String s2 = new String("abc");System.out.println(s1 == s2); //false// 先造了对象在堆中,对象的value指向常量池 123456789101112131415161718192021222324252627282930public class StringTest { @Test public void testString(){ String s1 = "hello"; String s2 = "goodwell"; String s3 = "hellogoodwell"; String s4 = "hello" + "goodwell"; String s5 = s1 + "goodwell"; String s6 = "hello" + s2; String s7 = s1 + s2;//1. System.out.println(s3 == s4);//true//2. System.out.println(s3 == s5);//false System.out.println(s3 == s6);//false System.out.println(s3 == s7);//false System.out.println(s5 == s6);//false System.out.println(s6 == s7);//false String s8 = s5.intern(); //返回值得到的s8使用的常量值已经存在的“hellogoodwell” //3. System.out.println(s3 == s8);//true final String s9 = "hello"; String s10 = s9 + "goodwell";//4. System.out.println(s3 == s10);//true } 结论: 常量与常量的 拼接结果 在常量池。且常量池中 不会存在相同内容的常量。 只要拼接的其中有一个是变量,结果就在堆中(类似new) 如果拼接的结果 调用intern()方法,返回值就在常量池中 final String(也在常量池中)和字面量连接,结果在常量池中 1234567891011121314151617public class StringTest { String str = new String("good"); char[] ch = {'t','e','s','t'}; public void change(String str, char ch[]) { str = "test ok"; ch[0] = 'b'; } public static void main(String[] args) { StringTest ex = new StringTest(); ex.change(ex.str, ex.ch); System.out.println(ex.str);//good //不可变性 System.out.println(ex.ch);//best }} 常用方法 int length() :返回字符串的长度: return value.length char charAt(int index): : 返回某索引处的字符return value[index] boolean isEmpty() :判断是否是空字符串:return value.length == 0 String toLowerCase() :使用默认语言环境,将 String 中的所有字符转换为小写 String toUpperCase() :使用默认语言环境,将 String 中的所有字符转换为大写 String trim(): :返回字符串的副本,忽略前导空白和尾部空白 boolean equals(Object obj): :比较字符串的内容是否相同 boolean equalsIgnoreCase(String anotherString) :与equals方法类似,忽略大小写 String concat(String str) :将指定字符串连接到此字符串的结尾。 等价于用“+” int compareTo(String anotherString): :比较两个字符串的大小 String substring(int beginIndex): :返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。 String substring(int beginIndex, int endIndex) : :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。 boolean contains(CharSequence s) :当且仅当此字符串包含指定的 char 值序列时,返回 true int indexOf(String str): :返回指定子字符串在此字符串中第一次出现处的索引 int indexOf(String str, int fromIndex): :返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始 int lastIndexOf(String str): :返回指定子字符串在此字符串中最右边出现处的索引 int lastIndexOf(String str, int fromIndex): :返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索注:indexOf和lastIndexOf方法如果未找到都是返回-1 boolean endsWith(String suffix): :测试此字符串是否以指定的后缀结束 boolean startsWith(String prefix): :测试此字符串是否以指定的前缀开始 boolean startsWith(String prefix, int toffset): :测试此字符串从指定索引开始的子字符串是否以指定前缀开始 替换 String replace(char oldChar, char newChar): :返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 String replace(CharSequence target, CharSequence replacement): :使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 String replaceAll(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 String replaceFirst(String regex, String replacement) : : 使 用 给 定 的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 匹配 boolean matches(String regex): :告知此字符串是否匹配给定的正则表达式。 切片 String[] split(String regex): :根据给定正则表达式的匹配拆分此字符串。 String[] split(String regex, int limit): :根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。 类型转换String 与 char[] 之间的转换: String –> char[] 调用String的toCharArray() 1char[] charArray = str1.toCharArray(); char[] –> String 调用String的构造器 1String str2 = new String(arr); String 与 byte[] 之间的转换: String –> byte[] 调用String的getBytes() 1byte[] bytes = str1.getBytes(); //使用默认的字符集进行编码 byte[] –> String 调用String的构造器 12String str2 = new String(bytes);//使用默认的字符集进行解码//说明:解码时,要求解码使用的字符集必须和编码时使用的字符集一致,否则出现乱码 StringBuffer类 java.lang.StringBuffer代表 可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。很多方法与String相同。作为参数传递时,方法内部可以改变值。 StringBuffer类不同于String,其对象必须使用构造器生成。有三个构造器: StringBuffer() :初始为 容量为16 的字符串缓冲区 StringBuffer(int size) :构造 指定容量的字符串缓冲区 StringBuffer(String str) :将内容初始化为指定字符串内容 StringBuffer 类的常用方法 StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接(啥都变成字符串,例如“null”) StringBuffer delete(int start,int end):删除指定位置的内容 StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str StringBuffer insert(int offset, xxx):在指定位置插入xxx StringBuffer reverse() :把当前字符序列逆转 public int indexOf(String str) public String substring(int start,int end) public int length() public char charAt(int n ) public void setCharAt(int n ,char ch) ## StringBuilder类 StringBuilder和StringBuffer 非常类似,均代表可变的字符序列,而且提供相关功能的方法也一样 对比String 、StringBuffer 、StringBuilder String(JDK1.0):不可变字符序列 StringBuffer(JDK1.0):可变字符序列、效率低、线程安全 StringBuilder(JDK 5.0):可变字符序列、效率高、线程不安全,底层都是使用char[] 存储 注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。 12345678String str = null;//System.out.println(str.length());//NullPointerExceptionStringBuffer sb = new StringBuffer();sb.append(str);System.out.println(sb.length());//4System.out.println(sb);//"null"//StringBuffer sb1 = new StringBuffer(str);//System.out.println(sb1);//NullPointerException 源码分析 12345678910111213String str = new String();// char[] value = new char[0];String str1 = new String("abn");// char[] value = new char[]{'a','b','n'};StringBuffer sb1 = new StringBuffer();// char[] value = new char[16];sb1.append('a');// value[0] = 'a';sb1.append('b');// value[1] = 'b';StringBuffer sb2 = new StringBuffer("abc");// char[] value = new char["abc".length + 16]{'a','b','c's};//问题一:System.out.println(sb2.length());// 3//问题二://扩容问题:若添加的数据底层数组装不下,那就需要扩容底层的数组;默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。 指导建议: 开发中使用,StringBuffer(int capacity) 或 StringBuilder(int capacity) 指定容量的 对比三者效率: StringBuilder > StringBuffer > String 总结: 增:append(xxx) 删:delete(int start, int end) 改:setCharAt(int n, char ch) / replace(int start, int end, String str) 查:charAt(int n) 插:insert(int offset, xxx) 长度:length() *遍历:for() + charAt() / toString() 时间相关的类JDk8之前的日期和时间的API①. java.lang.System类System.currentTimeMillis(): 返回当前时间与1970年1月1日0时0分0秒之间 以毫秒为单位的时间差,也称为时间戳 ②. java.util.Date (java.sql.Date继承前者)构造器: Date() 创建一个对应当前时间的Date对象 //Date(int year, int month, int day) 创建一个对应时间的Date对象 Date(long) 创建指定毫秒数的Date对象 方法: tiString() 显示当前年月日 getTime() 获取当前Date对象对应的时间戳 java.sql.Date 对应着数据库中的日期类型的变量 将java.util.Date对象转换成java.sql.Date对象 1java.sql.Date dateSql = new java.sql.Date(dateUtil.getTime()); 1234567891011121314151617181920212223242526272829303132333435363738394041424344public class TimeTest { //1.System类中的currentTimeMillis() @Test public void test11(){ long time = System.currentTimeMillis(); System.out.println(time); //1569513273409返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差 } //2.java.util.Date类(java.sql.Date类)// ①两个构造器的使用// > Date() 创建一个当前时间的Date对象// > Date(long) 创建指定毫秒数的Date对象// ②两个方法的使用// > toString() 显示当前的年、月、日、时、分、秒// > getTime() 获取当前Date对象对应的毫秒数(时间戳)// ③java.sql.Date类对应着数据库中的日期类型的变量// > 如何实例化// > util.Date --getTime()-> sql.Date对象// >< sql.Date --> util.Date对象(不需要转,学生本身就是人) @Test public void test22(){ //构造器一:Date() 创建一个当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString());//Fri Sep 27 00:50:46 CST 2019 System.out.println(date1.getTime());//1569516806737 //构造器二:Date(long) 创建指定毫秒数的Date对象 Date date2 = new Date(1569516806737L); System.out.println(date2.toString());//Fri Sep 27 00:53:26 CST 2019 //创建java.sql.Date对象 java.sql.Date date3 = new java.sql.Date(1569516806737L); System.out.println(date3);//2019-09-27有无.toString()一样 //sql.Date --> util.Date对象 Date date8 = date3; System.out.println(date8.toString()); //util.Date --getTime()-> sql.Date对象 //情况1 Date date4 = new java.sql.Date(1569516806737L); java.sql.Date date5 = (java.sql.Date) date4; //情况2 Date date6 = new Date(); java.sql.Date date7 = new java.sql.Date(date6.getTime()); }} ③. SimpleDateFormat类java.text.SimpleDateFormat() 不与语言环境有关的方式来对Date类的格式化和解析的具体类 构造器: SimpleDateFormat() 默认的模式和语言环境创建对象 SimpleDateFormat(String Pattern) 用参数pattern指定的格式创建一个对象。 该对象可以 格式化: 日期 -> 文本 String format(Date date) 解析:文本 -> 日期 Date parse(String source) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546public class DateTimeTesr { /* SimpleDateFormat的使用:对日期Date类的格式化和解析 1.两个操作 - 格式化: 日期 ---> 字符串 - 解析: 字符串 ---> 日期 2.SimpleDateFormat的实例化 */ @Test public void testSimpleDateFormat(){ //实例化SimpleDateFormat:使用默认构造器 SimpleDateFormat sdf = new SimpleDateFormat(); //格式化: 日期 ---> 字符串 Date date = new Date(); System.out.println(date); String format = sdf.format(date); System.out.println(format); //解析: 字符串 ---> 日期 String str = "19-9-27 下午8:12"; Date date1 = null; try { date1 = sdf.parse(str); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date1); // 按照指定的方式格式化和解析,调用带参数的构造器// SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyy.MMMMM.dd GGG hh:mm aaa"); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //格式化 String format1 = sdf1.format(date); System.out.println(format1);//2019-09-27 08:22:08 //解析 Date date2 = null; try { date2 = sdf1.parse("2019-09-27 08:22:08"); } catch (ParseException e) { e.printStackTrace(); } System.out.println(date2); }} 1234567891011121314151617public void test12() throws ParseException { /* 练习一: 字符串“2020-09-08”抓换成java.sql.Date 练习二: “三天打鱼两天晒网” 1990-01-01 xxxx-xx-xx 打鱼?晒网? */ String birth = "2020-09-08"; SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf1.parse(birth);// System.out.println(date); java.sql.Date birthDate = new java.sql.Date(date.getTime()); System.out.println(birthDate); //思路:总天数%5==? //方式一:( date2.getTime() - date1.getTime() ) / (1000 * 60 * 60 * 24) + 1 //方式二:时间分段整数年加闰年} ④. Calendar类是一个抽象基类,主要用于完成日期之间相互操作的功能。 1.实例化 方式一:创建其子类(GregorianCalendar)的对象 方式二:调用其静态方法getInstance() 2.常用方法 get() set() 可变性 add() getTime() 日历类 –> Date setTime() Date –> 日历类 注意: 获取月份时:一月是0 … 十二月是11 获取星期时:周日是1 … 周六是7 12345678910111213141516171819202122232425262728293031323334353637/*Calendar类(抽象类)的使用 */@Testpublic void testCalendar(){ // 1.实例化 // 方式一:创建其子类(GregorianCalendar)的对象 // 方式二:调用其静态方法getInstance() Calendar calendar = Calendar.getInstance(); System.out.println(calendar.getClass()); // 2.常用方法 // get() int days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28 System.out.println(calendar.get(Calendar.DAY_OF_YEAR));//271 // set() 可变性 calendar.set(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//22 // add() calendar.add(Calendar.DAY_OF_MONTH,22); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//14 // getTime() 日历类 --> Date Date date = calendar.getTime(); System.out.println(date); // setTime() Date --> 日历类 Date date1 = new Date(); calendar.setTime(date1); days = calendar.get(Calendar.DAY_OF_MONTH); System.out.println(days);//28} JDK 8中新日期时间APIjava.time1234567891011121314151617181920212223242526272829303132333435363738394041/*LocalDate LocalTime LocalDateTime 的使用 类似于Calendar() */@Testpublic void testTime(){ //1. now() 获取当前时间日期 LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); LocalDateTime localDateTime = LocalDateTime.now(); System.out.println(localDate);//2019-09-28 System.out.println(localTime);//09:19:28.307 System.out.println(localDateTime);//2019-09-28T09:19:28.307 //2. of() 设置指定的年月日时分秒,没有偏移量 LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 10, 0, 0, 0); System.out.println(localDateTime1);//2020-10-10T00:00 //getXxx() 获取相关属性 System.out.println(localDateTime.getDayOfMonth());//28 System.out.println(localDateTime.getDayOfWeek());//SATURDAY System.out.println(localDateTime.getDayOfYear());//271 System.out.println(localDateTime.getHour());//9 System.out.println(localDateTime.getMonthValue());//9 //withXxx() 修改、设置 不同于Calendar() 体现不可变性 LocalDate localDate1 = localDate.withDayOfMonth(22); System.out.println(localDate);//2019-09-28 System.out.println(localDate1);//2019-09-22 //plusXxx() 增加 LocalDateTime localDateTime2 = localDateTime.plusMonths(4); System.out.println(localDateTime);//2019-09-28T09:29:43.638 System.out.println(localDateTime2);//2020-01-28T09:29:43.638 //minusXxx() 减去 LocalDateTime localDateTime3 = localDateTime.minusDays(3); System.out.println(localDateTime);//2019-09-28T09:32:06.256 System.out.println(localDateTime3);//2019-09-25T09:32:06.256} Instant瞬时 12345678910111213141516171819202122/*Instant 的使用类似于Date */@Test public void testInstant(){ //now() 获取本初子午线对应的标准时间 Instant instant = Instant.now(); System.out.println(instant); //2019-09-28T01:42:23.739Z //添加时间的偏移量 OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8)); System.out.println(offsetDateTime); //2019-09-28T09:42:23.739+08:00 //toEpochMilli() 获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数 --> Date类的getTime() long milli = instant.toEpochMilli(); System.out.println(milli);//1569638022547 //ofEpochMilli() 通过给定的毫秒数,获取Instant实例 --> Date(long millis) Instant instant1 = Instant.ofEpochMilli(11111111L); System.out.println(instant1);//1970-01-01T03:05:11.111Z} java.time.format.DateTimeFormatter1234567891011121314151617181920212223242526272829303132333435363738394041/*DateTimeFormatter 格式化或解析日期、时间类似于SimpleDateFormat */@Testpublic void test3(){ //方式1:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; //格式化: 日期 --> 字符串 LocalDateTime localDateTime = LocalDateTime.now(); String str = formatter.format(localDateTime); System.out.println(localDateTime);//2019-09-28T14:49:57.682 System.out.println(str);//2019-09-28T14:49:57.682 //解析: 字符串 --> 日期 TemporalAccessor parse = formatter.parse("2019-09-28T14:49:57.682"); System.out.println(parse);//{},ISO resolved to 2019-09-28T14:49:57.682 //方式2:本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) //FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT ↑ DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG); //格式化 String str1 = formatter1.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:33:41.802 System.out.println(str1);//2019年9月28日 下午03时33分41秒 //ofLocalizedDate() DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); //格式化 String str2 = formatter2.format(LocalDate.now()); System.out.println(str2);//2019年9月28日 星期六 //方式3:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”) DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); //格式化 String str3 = formatter3.format(localDateTime); System.out.println(localDateTime);//2019-09-28T15:38:38.889 System.out.println(str3);//2019-09-28 03:38:38 //解析 TemporalAccessor parse1 = formatter3.parse("2019-09-28 03:38:38"); System.out.println(parse1);//{SecondOfMinute=38, HourOfAmPm=3, MicroOfSecond=0, NanoOfSecond=0, MinuteOfHour=38} Java比较器对象数组的排序问题,涉及对象之间的比较。Java对象正常情况下,只能进行比较: == 或 != ,不能使用 > 或 < 。Java实现对象排序的方式有两种: 自然排序:java.lang.ComparableComparable接口的使用举例 自然排序 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个大小的方式 像String、包装类等重写了compareTo()方法以后,进行了从小到大的排序 重写compareTo(obj)的规则: 如果当前对象this大于形参对象obj,则返回正整数; 如果当前对象this小于形参对象obj,则返回负整数; 如果当前对象this等于形参对象obj,则返回零。 对于自定义类,若需要排序,可让自定义类实现Comparable接口,重写compareTo(obj)方法,指明如何排序 123456789101112131415161718@Testpublic void test1(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr); System.out.println(Arrays.toString(arr));}//Eg:商品价格从低到高:按价格排序class Goods implements Comparable {}@Overridepublic int compareTo(Objact o) { if(o instanceod Goods) { Goods goods = (Goods)o; return Double.compare(this.price,goods.price) } throw new RuntimeException("传入的数据类型不一致");} 定制排序:java.util.Comparator 背景: 当元素的类型没有实现Comparable接口 而又不方便修改代码;或者实现了Comparable接口的排序规则不适合当前的操作,那么可以考虑使用Comparator的对象来进行排序。 重写compare(Object o1, Object o2)方法,比较o1和o2的大小: 若方法返回正整数,表示o1大于o2; 返回0,表示相等; 返回负数,表示o1小于o2。 123456789101112131415161718@Testpublic void test2(){ String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"}; Arrays.sort(arr,new Comparator(){ //按照字符串大到小 @Override public int compare(Object o1, Object o2) { if(o1 instanceof String && o2 instanceof String){ String s1 = (String) o1; String s2 = (String) o2; return -s1.compareTo(s2); }// return 0; throw new RuntimeException("输入的数据类型不一致"); } }); System.out.println(Arrays.toString(arr));} Comparable接口与Comparator的使用的对比: 前者一旦指定,保证Comparable接口实现类的对象在任何位置都可以比较大小; 后者属于临时性的比较。Arrays.sort(arr,new Comparator(){…..} 其他类System类 System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。 由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用。 成员变量System类内部包含 in、out和err 三个成员变量,分别代表 标准输入流(键盘输入),标准输出流(显示器)和 标准错误输出流(显示器)。 成员方法 native long currentTimeMillis():该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。 void exit(int status):该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表异常退出。 使用该方法可以在图形界面编程中实现程序的退出功能等。 void gc():该方法的作用是请求系统进行垃圾回收。至于系统是否立刻回收,则取决于系统中垃圾回收算法的实现以及系统执行时的情况。 String getProperty(String key):该方法的作用是获得系统中属性名为key的属性对应的值。系统中常见的属性名以及属性的作用如下表所示: | 属性名 | 属性说明 || ————- | ————————– || java. version | Java运行时环境版本 || java. home | java安装目录操作系统的名称 || os.version | 操作系统的版本 || user.nane | 用户的账户名称 || user.home | 用户的主目录 || user.dir | 用户的当前工作目录 | 12345678910111213141516171819202122232425@Testpublic void test3(){ String javaVersion = System.getProperty("java.version"); System.out.println("java的version:" + javaVersion); String javaHome = System.getProperty("java.home"); System.out.println("java的home:" + javaHome); String osName = System.getProperty("os.name"); System.out.println("os的name:" + osName); String osVersion = System.getProperty("os.version"); System.out.println("os的version:" + osVersion); String userName = System.getProperty("user.name"); System.out.println("user的name:" + userName); String userHome = System.getProperty("user.home"); System.out.println("user的home:" + userHome); String userDir = System.getProperty("user.dir"); System.out.println("user的dir:" + userDir);}out:java的version:1.8.0_191java的home:C:\Program Files\Java\jdk1.8.0_191\jreos的name:Windows 10os的version:10.0user的name:goodwelluser的home:C:\Users\goodwelluser的dir:D:\Codes\IdeaProjects\JavaSenior\Day Math类java.lang.Math 提供了一系列静态方法用于 科学 计算。其 方法的参数和返回值类型一般为double 型。 abs 绝对值 acos,asin,atan,cos,sin,tan 三角函数 sqrt 平方根 pow(double a,doble b) a 的b 次幂 log 自然对数 exp e 为底指数 max(double a,double b) min(double a,double b) random() 返回0.0 到1.0 的随机数 long round(double a) double 型数据a 转换为long 型(四舍五入) toDegrees(double angrad) 弧度—> 角度 toRadians(double angdeg) 角度—>弧度 BigInteger与BigDecimalBigInteger类java.math包的 BigInteger 可以表示不可变的任意精度的整数。提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。 构造器 BigInteger(String val):根据字符串构建BigInteger对象 常用 方法 public BigInteger abs(): 返回此 BigInteger 的绝对值的 BigInteger。 BigInteger add(BigInteger val) : 返回其值为 (this + val) 的 BigInteger BigInteger subtract(BigInteger val) : 返回其值为 (this - val) 的 BigInteger BigInteger multiply(BigInteger val) : 返回其值为 (this * val) 的 BigInteger BigInteger divide(BigInteger val) : 返回其值为 (this / val) 的 BigInteger。整数相除只保留整数部分。 BigInteger remainder(BigInteger val) : 返回其值为 (this % val) 的 BigInteger。 BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个BigInteger 的数组。 BigInteger pow(int exponent) : 返回其值为 (this exponent ) 的 BigInteger。 ### BigDecimal类 一般的Float类和Double类可以用来做科学计算或工程计算,但在 商业计算中,到 要求数字精度比较高,故用到java.math.BigDecimal类 。BigDecimal类支持不可变的、任意精度的有符号十进制定点数。 构造器 public BigDecimal(double val) public BigDecimal(String val) 常用方法 public BigDecimal add(BigDecimal augend) public BigDecimal subtract(BigDecimal subtrahend) public BigDecimal multiply(BigDecimal multiplicand) public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 123456789public void testBigInteger() { BigInteger bi = new BigInteger("12433241123"); BigDecimal bd = new BigDecimal("12435.351"); BigDecimal bd2 = new BigDecimal("11"); System.out.println(bi); // System.out.println(bd.divide(bd2)); System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP)); System.out.println(bd.divide(bd2, 15, BigDecimal.ROUND_HALF_UP));} ##]]></content>
<categories>
<category>Java</category>
<category>常用类</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day5]]></title>
<url>%2FJava%2F%E5%A4%9A%E7%BA%BF%E7%A8%8B%2FJava-day5%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅴ),主要记录IDEA的使用与Java多线程!奋斗ing 程序、进程、线程基本概念 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期PS:程序是静态的,进程是动态的进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。 何时需要多线程 程序需要同时执行两个或多个任务。 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。 需要一些后台运行的程序时。 多线程的创建方法一:继承于Thread类12345678910111213141516171819202122232425262728293031/** *多线程的创建,方法一:继承于Thread类 * 1.创建一个继承于Thread类的子类 * 2.重写Thread类的run() -> 将此线程执行的操作声明在run()中 * 3.创建Thread类的子类对象 * 4.通过此对象调用start(): ①启动当前线程;②调用当前线程的run() * * PS: 1.不能直接调用run()启动线程 * 2.要运行多个线程需要造多个对象 * * @author goodwell * @create 2019-09-25-9:39 */class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(System.out.println(Thread.currentThread().getName() + ":" + i);); } } }}public class ThreadTest{ public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); }} 创建Thread类的匿名子类的方法 12345678910new Thread(){ @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}.start(); Thread类中的常用方法: 方法二:实现Runnable接口 创建一个实现了Runnable接口的类 实现类去实现Runnable中的抽象方法:run() 创建实现类的对象 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 通过Thread类的对象调用start() 1234567891011121314151617181920212223242526/** * 创建多线程方法二 * * @author goodwell * @create 2019-09-25-16:13 */class MThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }}public class ThreadTest1 { public static void main(String[] args) { MThread mThread = new MThread(); Thread t1 = new Thread(mThread); t1.start(); }} 两种创建方式的对比开发中,优先选择实现Runnable接口的方式 原因: 实现的方式没有类的单继承的局限性 实现的方式更适合处理多个线程有共享数据的情况 联系: 1public class Thread implements Runnable 相同点:都需要重写run(),将线程执行的逻辑声明在run()中 方法三:实现Callable接口 创建一个Callable的实现类 实现call方法,将线程需要执行的操作声明在call()中 创建callable实现类的对象 将此对象传递到FutureTask构造器中,创建FutureTask的对象 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() 获取Callable中call方法的返回值 使用Runnable VS Callable如何理解与使用Runnable相比, Callable功能更强大些 相比run()方法,可以有返回值 方法可以抛出异常,被外面操作捕获,得到异常信息 支持泛型的返回值 需要借助FutureTask类,比如获取返回结果 Future接口 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值 12345678910111213141516171819202122232425262728293031323334353637// 1. 创建一个Callable的实现类class NumThread implements Callable { //2. 实现call方法,将线程需要执行的操作声明在call()中 @Override public Object call() throws Exception{ int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); sum += i; } } return sum; }}public class CallTest { public static void main(String[] args) { // 3. 创建callable实现类的对象 NumThread numThread = new NumThread(); // 4. 将此对象传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); // 5. 将FutureTask的对象作为参数传给Thread的构造器,创建Thread对象,并调用start() new Thread(futureTask).start(); Object sum = null; try { // 6. 获取Callable中call方法的返回值 sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }} 方法四:使用线程池背景: 经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前 创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 步骤: 提供指定线程数量的线程池 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 关闭连接池 好处: 提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建) 便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止 1234567891011121314151617181920212223242526272829class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { if(i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ": " + i); } } }}public class ThreadPool { public static void main(String[] args) { // 1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); //↑是接口,↓是类 System.out.println(service.getClass());// ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; // 设置线程池的属性// service1.setCorePoolSize(15);// service1.setKeepAliveTime(); // 2. 执行指定的线程操作,需要提供实现Runnable、Callable接口实现类的对象 service.execute(new NumberThread()); // 适用于Runnable// service.submit(Callable callable); // 适用于Callable // 3. 关闭连接池 service.shutdown(); }} 线程的生命周期 线程的同步举例问题:卖票过程中,出现了重票、错票 –> 出现了线程的安全问题,通过同步机制来解决 方式一:同步代码块123synchronized(同步监视器){ //需要同步的代码} 说明:操作共享数据的代码,及需要被同步的代码;共享数据,多个线程共同操作的变量;同步监视器,俗称:锁,任何一个类的对象都可以当锁。(要求:多个线程必须要共同的一把锁) 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充同步监视器;在继承Thread类创建多线程的方式中,慎用this充当同步监视器考虑使用当前类作为锁Xxxx.class 同步的优缺点: 好处:解决了线程的安全问题 局限性:操作同步代码时,只能有一个线程参见,其他线程等等待,效率较低。 方法二:同步方法如操作共享数据的代码完整的声明在一个方法中。 总结: 同步方法仍然涉及到同步监视器,不需要显式的声明。 非静态的同步方法,同步监视器是:this;静态的同步方法,同步监视器是:当前类本身。 线程安全的饿汉式单例模式: 1234567891011121314151617181920class Bank{ private Bank(){}; private static Bank instance = null; private static Bank getInstance(){ //方式一:效率较差// synchronized (Bank.class) {// if (instance == null) {// instance = new Bank();// }// return instance;// } //方式二:效率更高 if (instance == null) { synchronized (Bank.class) { instance = new Bank(); } } return instance; }} 线程的死锁问题死锁的理解: 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。 说明: 出现死锁后,不会出现异常、提示,只是所有的线程都处于阻塞状态,无法继续 使用同步的时候,要避免出现死锁。 方式三:lock锁 — JDK5.0新增 实例化Reentranlock 调用lock 调用解锁方法:unlock 1234567891011121314151617181920212223242526272829303132class Window implements Runnable{ private int ticket = 100; //1.实例化Reentranlock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while (true) { try { //2.调用lock lock.lock(); if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + i); ticket--; } else { break; } } finally { //3.调用解锁方法:unlock lock.unlock(); } } }} synchronized VS Lock 异同 相同:二者都可以解决线程安全问题 不同:synchronized机制在执行完相应的同步代码后,自动释放同步监视器;Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock()) 优先使用顺序:Lock -> 同步代码块(已经进入了方法体,分配了相应资源) -> 同步方法(在方法体之外) 线程的通信相关方法: wait():一旦执行,当前线程就进入阻塞状态,并释放同步监视器 notify():唤醒被wait的一个线程,若有多个线程被wait,就唤醒优先级最高的 notifyAll():唤醒所有被wait的线程 说明: 此三个方法必须使用在同步代码块中 其调用者必须是同步代码块中的同步监视器,否则会出现IllegalMonitorStateException异常 都是定义在java.lang.Object类中 sleep() VS wait() 异同: 同:一旦执行,都可以是当前的线程进入阻塞状态 异: 方法声明的位置不同:Thread类中声明sleep,Object类中声明wait 调用的要求不同:sleep可以在任何场景下调用,wait必须使用在同步代码块中 是否释放同步监视器:若都使用在同步代码块或同步方法中,sleep不会释放锁,wait会释放锁]]></content>
<categories>
<category>Java</category>
<category>多线程</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day4]]></title>
<url>%2FJava%2F%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%2FJava-day4%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅳ),主要记录Java异常处理!奋斗ing 异常概述与异常体系结构开发过程中的语法错误和逻辑错误不是异常。 执行过程中出现的异常分为两类: Error:Java虚拟机无法解决的严重问题。eg:StackOverflowError栈溢出和OutOfMemoryError堆溢出。一般不编写针对性的代码进行处理。 Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。又分为编译时异常(受检checked异常) VS 运行时异常(非受检unchecked异常) 异常处理机制关于异常对象的产生: 系统自动生成的异常对象 手动的生成一个异常对象,并抛出(throw) 抓抛模型: “抛”:程序在正常执行中,一旦出现异常就会在代码处生成一个对应异常类的对象,并抛出。之后的代码不再执行。 ‘’抓“:可以理解为异常的处理方式,如下两种方式: try-catch-finally1234567891011try{ //可能会出现异常的代码} catch(异常类型1 变量名1) { //处理异常的方法1} catch(异常类型2 变量名2) { //处理异常的方法2}...finally{ //一定会执行的代码} PS: catch中的异常类型若五子类父类关系 ,无需考虑声明的先后顺序;若有,子类必须声明在父类的上面,否则报错。 在try中声明的变量,在大括号外不能调用。try-catch-finally可以嵌套。 finally是可选的,其中声明的是一定会执行的代码,即便catch中又出现了异常,try或catch中有return语句等情况。 finally重要应用:像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,需要收到编写代码进行资源的释放,此时代码就需要编写在finally中。 常用异常对象处理的方式:①String getMessage() ②printStackTrace() 体会: 使用try-catch-finally处理编译时异常,是使得程序在编译时不再报错,但在运行时仍可能报错。“延迟”、 开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally,针对编译时异常,一定要考虑异常的处理。 throws + 异常类型声明在方法的声明处,指明此方法执行时,可能回抛出的异常类型,一旦方法体执行时出现异常,仍会在异常处生成一个异常类的对象。此对象满足throws后的异常 如何选择 若父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着若子类重写的方法有异常必须使用try-catch-finally 执行的方法a中,先后调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws的方式处理,而执行的方法a可以考虑使用try-catch-finally 自定义异常类 继承现有的异常结构:RuntimeException、Exception 提供全局常量:serialVersionUID 提供重载的构造器 throw VS throws throw表示抛出一个异常类对象,生成异常对象的过程。声明在方法体内。 throws属于异常处理的一种方式,声明在方法的声明处。]]></content>
<categories>
<category>Java</category>
<category>异常处理</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day3]]></title>
<url>%2FJava%2F%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%2FJava-day3%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅲ),主要记录Java面向对象!奋斗ing 面向对象Java类及类的成员:属性、方法、构造器;代码块、内部类类与对象类的成员 属性:Field = 域、字段 = 成员变量 行为:Method =(成员)方法 = 函数 对象的内存解析 堆(Heap)存放对象实例 栈(Stack)存储局部变量 方法区(Method Area)存储已被虚拟机加载的类信息、变量、静态变量、即时编译器编译后的代码等 理解万事万物皆对象 在Java语言范畴中,我们将功能,结构等封装到类中,通过类的实例化,来调用具体的功能结构。 涉及到Java与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。 PS:引用类型的变量,只可能储存两类值,null 或 地址值(含变量的类型) 匿名对象的使用,只能调用一次 属性属性(成员变量)VS 局部变量 相同点 定义变量格式相同,先声明后使用,且都有对应的作用域 不同点 声明的位置不同 属性:直接定义在类的{}中 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量 权限修饰符的不同 属性:可以在声明属性时,使用权限修饰符指明其权限 局部变量:不可以使用 默认初始化值的情况 属性:类的属性根据类型都有默认初始化只 局部变量:无初始化值 在内存中加载的位置 属性:堆空间(非static) 局部变量:栈空间 方法方法重载(overload)定义:在同一个类中允许存在一个以上的同名方法,只要他们的参数个数或者类型不同即可。 “两同一不同”:同一个类,同一个方法名。参数列表不同。 可变个数的形参格式:数据类型 … 变量名, 必须申明在末尾,可传入参数个数为0个及以上 方法参数的值传递机制变量赋值: 基本数据类型:赋值的是变量所保存的数据值 引用数据类型:赋值变量所保存数据的地址值 面向对象的三个特征:封装性、继承性、多态性、(抽象性)封装与隐藏体现: 将类的属性XXX私有化private,提供公有化public方法来获取getXXX和设置属性setXXX的值。 不对外暴露的私有的方法 单例模式(将构造器私有化) 如果不希望类在包外被使用可以设置成缺省 目标:高内聚,低耦合PS:封装性的体现需要权限修饰符来体现。修饰类只能public与缺省 权限修饰符(从小到大) 修饰符 类内部 同一个包 不同包的子类 同一个工程 private √ (缺省) √ √ protect √ √ √ public √ √ √ √ 继承性inheritance好处: 减少了代码的多余,提高代码的复用性 便于功能的扩展 为之后多态性的使用,提供了前提 格式:1class A extends B{} A:子类、派生类、subclass B:父类、超类、superclass ps:Java只支持单继承和多继承,不允许多重继承,一个子类只能有一个父类。如果没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类。 方法的重写(override/overwrite)定义:在子类中根据需要对父类中的方法进行改造。 应用:重写以后,当创建子类对象,调用同名方法时调用的是重写的方法。 规定: 子类重写的方法名与形参列表与被重写一样 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符 ps:特殊情况,子类中不能重写父类中声明为private的方法。 返回类型: 父类被重写方法的返回类型为void,子类重写的方法只能返回void; 父类被重写方法的返回类型为基本类型,子类重写的方法只能返回相同的基本类型; 父类被重写方法的返回类型为A类型,子类重写的方法可以返回A类型或A的子类; 异常类型:子类重写的方法的异常类型不大于父类被重写的方法抛出的异常类型 特别注意:子类和父类的同名同参数的方法要么声明非static(考虑重写),要么都声明为static(不是重写) 类的成员之三:构造器(构造方法constructor)作用:创建对象;给对象进行初始化 格式:权限修饰符 + 类名(形参列表){} ps:构造器可重载,且一旦显示定义了类的构造器之后,系统不再提供默认空参构造器。 多态性polymorphism定义:一个事物的多种形态,在Java中的体现:对象的多态性,父类的引用指向子类的对象 多态的使用:在编译期,只能调用父类中声明的方法,在运行期,实际执行的是子类中的重写的方法。 虚拟方法调用(Virtual Method Invocation)Java引用变量有两种类型:编译时类型和运行时类型。 调用方法时,编译看左边,执行看右边。–动态绑定,多态是运行时行为。重载是编译是就已经确定了,“早绑定”,”静态绑定”。 Bruce Eckel:”不要犯傻,如果它不是晚绑定,就不是多态。” 多态性的使用前提: 类的继承关系 方法的重写 ps: 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边) 向下转型编译时只能调用父类声明的属性和方法,如何调用子类特有的属性和方法? 使用强制类型转换。使用强制转换时,可能出现ClassCastException的异常。 使用instanceof进行检测: 1a instanceof A //判断duixa是否是A的实例,如是返回true。 其他关键字:this、super、static、final、abstract、interface、package、import与相关补充知识重要关键字this:理解为 当前对象,通常可以省略。可以调用:属性、方法和构造器。 调用本类中指定的构造器 this(形参列表) ps: import static导入指定类或接口中的静态结构:属性、方法 super:理解为 父类的,可以调用:属性、方法和构造器。 通常可以省略,当子类和父类中定义同名的属性时,需要显式的super 特殊情况, 当子类重写父类的方法时,需要调用父类中的方法时,需要显式的super super调用父类构造器:在子类构造器中显式的“super(参数列表)”,且必须声明在子类构造器中的首行 在类的构造器中,针对“super(参数列表)”和“this(参数列表)”只能二选一,构造器中没有,默认super(空参) 在类的多个构造器中,至少有一个类的构造器使用了super(参数列表),调用父类的构造器。 static:某些特定的数据在内存中只有一份。eg:每个中国人都共享中国这个国籍。 修饰:属性、方法、代码块、内部类 修饰属性:静态变量 属性按是否有static修饰又分为:静态属性 VS 非静态属性(实例变量) 实例变量:当创建类的多个对象,每个对象都独立拥有一套类中的非静态属性,当修改某一个对象的静态属性时,不会导致其他对象中同样的属性值的修改。 静态属性:创建类的多个对象,每个对象都共享同一个静态属性。当修改某一个对象的静态属性时,会导致其他对象调用此静态变量时,属性值是修改的。 PS:静态变量随类的加载而加载,且早于对象的加载。因此可以通过”类.静态变量“进行调用。由于类只会加载一次 ,则静态变量在内存中也只会存在一份。 修饰方法:静态方法 可以通过”类.静态方法“进行调用 静态方法只能调用静态的方法或属性,非静态则都可以。 在静态方法中,不能使用this,super关键字 static应用场景: 属性是可以被多个对象共享,不会随着对象的不同而改变 操作静态属性的方法,设置成static;工具类中的方法,习惯上声明为static final:可以修饰的结构:类、方法、变量 修饰类:此类不能被其他类继承。eg:String类、System类、StringBuffer类。 修饰方法:此方法不能被重写。eg:Object类中的getClass()。 修饰变量:此“变量”被称为一个常量。 修饰属性,可以考虑的赋值位置有显式初始化、代码块中初始化、构造器中初始化、不可以在方法中赋值。 修饰局部变量:尤其修饰形参时,表明此形参是常量。 static final 修饰属性:全局常量 abstract:抽象类与抽象方法修饰的结构:类、方法 修饰类:抽象类,不可实例化,类中一定要构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)开发中,都会提供抽象类的子类。让子类对象实例化,完成相关的。 修饰方法:抽象方法只有方法的声明,没有方法体。包含抽象方法的类一定是一个抽象类,抽象类中可以没有抽象方法。若子类重写了父类的所有的抽象方法后,此子类方可实例化。若未重写,则该子类也是一个抽象类,需要abstract修饰。 PS:abstract不可以用来修饰属性、构造器等。abstract不能修饰私有方法、静态方法、final的类与方法。 抽象类的匿名子类 12345678910111213141516171819202122Worker worker = new Worker();method1(worker);//非匿名的类非匿名对象method1(new Worker());//非匿名的类匿名的对象Person p = new Person(){ @override public void eat() { } @override public void breath() { }}//匿名子类的对象:pmethod1(new Worker(){ @override public void eat() { } @override public void breath() { }});//匿名子类的匿名对象 接口(interface) 与类并列的结构,一定程度解决类的单继承性的缺陷。本质是契约、规范、标准。(JDK7及以前)接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义。 继承是一个“是不是”的关系,而接口实现的是“能不能”的关系。 接口中的成员: JDK1.7及以前,只能定义全局变量和抽象方法 全局变量:public static final的 抽象方法:public abstract的 JDK8,出来定义以上之外,还可以定义静态方法、默认方法。 接口中不能定义构造器!意味着接口不能实例化 Java开发中,接口通过让类去实现(implement)的方法来使用(面向接口编程),若实现类覆盖了接口的所有的抽象方法后,此实现类方可实例化。若未重写,则该实现类也是一个抽象类。 Java类可以实现多个接口,弥补类的单继承性的局限性。 12//先写extends再写implementsclass AA extends BB implements CC,DD,EE 接口与接口之间可以继承,而且可以多继承。 接口的具体使用,体现了多态性、 接口的应用: 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问,另一个博文(中介)应用场景: 安全代理 远程代理 延迟加载 分类: 静态代理(静态定义代理类) 动态代理(动态生存代理类) 工厂模式 Java8中关于接口的改进 接口中定义的静态方法,只能通过接口来调用(像工具类) 通过实现类的对象,可以调用接口中的默认方法。 类优先原则:若子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类的同名同参数的方法。、 接口冲突:若实现类实现了多个接口,而多个接口都定义了同名同参数的默认方法,在实现类没有重写的情况下,报错。因此实现类必须重写此方法。 如何在子类(实现类)的方法中调用父类、接口中被重写的方法: 123method();//调用自己重写的方法super.method();//调用父类中声明的Interface1.super.method();//调用接口中的默认方法 补充类的成员之四:代码块(初始化块)就是一对大括号,用来初始化类、对象,若有修饰,只能是static 静态代码块 内部可以有输出语句,并随着类的加载而执行,且只执行一次。 若多个静态代码块,按声明顺序依次执行,总优先于非静态代码块 静态代码块内只能静态的属性、方法,不能调用非静态的结构 作用:初始化类的信息 非静态代码块 内部可以有输出语句,随着每次对象的创建而执行。 非静态代码块内既能静态的属性、方法,也能调用非静态的结构 属性赋值的相关问题:可以对属性进行赋值的位置: 默认初始化 显式初始化 构造器中初始化 有对象后,通过“对象.属性”或“对象.方法”的方法进行赋值 在代码块中进行赋值 属性赋值的先后顺序:1 -> 2 / 5 -> 3 -> 4 类的成员之五:内部类类A声明在类B中,A为内部类 分类:成员内部类 (静态、非静态) VS 局部内部类(方法、代码块、构造器内) 成员内部类: 作为外部类的成员:调用外部类的结构、可以被static修饰、可以被四种权限修饰符修饰 作为一个类:内可以定义属性、方法、构造器等,可以被final修饰,可以被abstract修饰 相关使用细节: 12345678910111213141516171819//如何实例化成员内部类的对象://创建静态成员内部类对象Person.Dog dog = new Person.Dog();dog.show();//创建非静态成员内部类对象Person p = new Person();Person.Bird bird = p.new Person.Bird();bird.show();//如何在成员内部类中区分调用外部类的结构://方法的形参name//内部类的形参this.name//外部类的形参Person.this.name//开发中局部内部类的使用://eg:返回一个实现了XXX接口的类的对象 JavaBean符合以下标准的Java类: 类是公共的 有一个无参的公共的构造器 有属性,且有对应的get、set方法 JUnit单元测试方法 选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步 创建Java类(要求:①此类是public ②此类提供公共空参构造器)进行单元测试 在此类中声明单元测试方法:要求权限为public,没有返回值且没有形参 PS:需要声明注释@Test,并导入包import org.junit.Test; 左键双击单元测试方法名,右键:run as - JUnit Test 若执行结果无异常为绿色,异常为红。 == VS equals() == 是运算符,可以用于基本数据与引用类型变量,前者比较保存的数据是否相同(不一定要类型相同,但必须一致),后者比较地址值是否相同,是否引用指向同一个对象 equals() 是一个方法,只能用于引用数据类型变量的比较,object类中定义的equals() 和 == 的作用是相同的。像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后比较的是两个对象的实体内容是否相同。若自己定义的类也要有这样的功能,比较对象的实体内容,应该重写equals()方法。 toString()方法 输出一个引用变量时,实际上输出的是对象的toString() 像String、Date、File、包装类等都重写了Object类中的toString()方法,返回“实体内容”信息 自定义类也可以重写该方法。 main()方法 作为程序的入口 也是一个普通的静态方法(通过实例化类对象调用普通属性与方法) 可以作为与控制台交互的方式(java xxxDemo “str”) 包装类(Wrapper)的使用 针对八种基本数据类型定义相应的引用类型-包装类(封装类),有了类的特点,就可以调用类中的方法,实现真正的面向对象。 | 基本数据类型 | 包装类 || ———— | ————- || byte | Byte || short | Short || int | Integer || long | Long || float | Float || double | Double || boolean | Boolean || char | Character | Byte Short Integer Long Float Double 父类为Number 基本数据类型、包装类、String三者的相互转换 包装类 -> 基本数据类型:调用包装类的xxxValue() 基本数据类型 -> 包装类:调用包装类的构造器 基本数据类型、包装类 -> String类型:调用String重载的valueOf(Xxx xxx) 12345int num1 = 10;//方式一:连接运算String str1 = num1 + "";//方式二:调用String重载的valueOf(Xxx xxx)String str2 = String.valueOf(num1); String类型 -> 基本数据类型、包装类:调用包装类的parseXxx() 12String str3 = "1234";int num3 = Integer.parseInt(str3); 自动装箱与拆箱(JDK5.0以后) 1234int num1 = 10;Integer in1 = num1; //自动装箱int num3 = in1;//自动拆箱 12345678910111213141516Object o1 = true ? new Integer(1) : new Double(2.0);System.out.println(o1);Integer i = new Integer(1);Integer j = new Integer(1);System.out.println(i == j);//falseInteger m = 1;Integer n = 1;System.out.println(m == n);//true// Integer内部定义了IntegerCache结构,其中定义了Integer[]//保存了-128~127,如果使用自动装箱时,直接调用。Integer x = 128;Integer y = 128;System.out.println(x == y);//false 设计模式单例(Singleton)设计模式只能存在一个对象实例,好处减少了系统性能的开销 实现一:饿汉式 私有化类的构造器 内部创建类的对象(private static) 提供公共的方法(static),返回类的对象 1234567private Bank(){ }private static Bank instance - new Bank();public static Bank getInstance() { return instance;} 实现二:懒汉式 私有化类的构造器 声明当前类对象(static),没有初始化 声明public、static的返回当前类对象的方法 12345678910private Order(){ }private static Order instance - null;public static Order getInstance() { if(instance == null) { instance = new Order(); } return instance;} 饿汉式 VS 懒汉式 区别:懒汉式好处延迟对象的创建,饿汉式坏处,对象加载时间太长,但其是线程安全的 使用场景: 网站的计算器、 应用程序的日志应用 数据库连接池 读取配置文件的类 Application也是单例的典型应用 Windows的Task Manager(任务管理器) Windows中的Recycle Bin(回收站) 模板方法设计模式(TemplateMethod)抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展、改造,但子类总体上会保留抽象类的行为方式。 解决的问题: 当功能内部一部分实现是确定的,一部分实现是不确定的。这是可以把不确定部分暴露出来,让子类去实现。 在软件开发中实现一个算法时,整体步骤很确定、通用,这些步骤在父类中写好,但部分易变,可以将该部分抽象出来,供不同子类去实现。 MVC设计模式模型层 model:主要处理数据 数据对象封装: model.bean/domain 数据库操作类: model.dao 数据库: model.db 视图层 view: 显示数据 相关工具类: view.utils 自定义view: view.ui 控制层 controller: 处理业务逻辑 应用界面相关: controller.activity 存放fragment: controller.fragment 显示列表的适配器: controller.adapter 服务相关的: controller.service 抽取的基类: controller.base]]></content>
<categories>
<category>Java</category>
<category>面向对象</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_day2]]></title>
<url>%2FJava%2F%E6%A6%82%E5%BF%B5%E4%B8%8E%E8%AF%AD%E6%B3%95%2FJava-day2%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅱ),记录Java基本概念与语法!奋斗ing 基本概念Java语言的特点 面向对象 健壮性 跨平台性 Java两种核心机制 Java虚拟机(Java Virtual Machine) 可以理解为一个以字节码为机器指令的CPU,是解释型 垃圾回收机制(Garbage collection) 名词解释 JDK(J2SDK) & JRE SDK(Software Development kit 软件开发包) Java Runtime Environment(Java运行环境) ps: 开发需要JDK,用户只需JRE。 JDK = JRE + 开发工具集(例如Javac编译工具) JRE = JVM + Java SE标准类库 Java技术体系平台 Java SE(Java Standard Edition) 标准版 Java EE(Java Enterprise Edition)企业版 Java ME(Java Micro Edition)小型版 Java Card 基本语法复习注释类型 单行注释 多行注释 文档注释 1234/** @author 指定Java程序的作者 @version 指定源文件的版本 */ PS:文档注释的内容可以被javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。 12> javadoc -d myXXXdoc -author -version XXX.java> Java API 文档API (Application Programming Interface, 应用程序编程接口) 一个Java源文件只能有一个类声明public 程序入口是main() 输出语句:System.out.println() 输出数据再换行 / System.out.print() 编译会生成(多个)与源文件中类名相同的字节码文件。 关键字与保留字Java保留字: goto 、const 标识符各种变量、方法和类(凡是自己起名字的) 特点:字母,0-9,_ 或$组成,数字不开头 规范:(PS:Java 采用unicode字符集) 包名:多单词组成所有字母都小写:xxxyyyzz 类名、接口名:多单词所有单词首字母大写:XxxYyyZzz 变量名、方法名:第一个首字母小写其余首字母大写:xxxYyyZzz 常量名:所有字母大写,多单词下划线连接:XXX_YYY_ZZZ 变量数据类型: 基本数据类型(primitive type) 整数类型(byte, short, int, long) 浮点类型(float, double) 字符类型(char) 布尔型(boolean) 引用数据类型(reference type) 类(class) 接口(interface) 数组(string[]) 基本数据类型转换(不包含boolean): 自动类型提升 byte、char、short –> int –> long –> float –> double 强制类型转换 自动类型提升的逆运算,强制转换符() 1234567891011// 特殊情况1long l = 123123;//int//long ll = 123123123123123123;//float f = 12.3;//int false// 特殊情况2//整型常量默认为int,浮点型常量默认为doublebyte b = 12;byte b1 = b + 1;//int false//char c = '';//false 引用数据类型string string可以和8种基本数据类型进行运算,且只能进行连接运算: + 123456//String str1 = 123;//falseString str2 = 123 + "";//int num = str1;//false//int num = (int)str1;//falseint num = Integer.parseInt(str1); 进制 二进制(binary):以0B或0b开头,原码补码反码 十进制(decimal) 八进制(octal):以数字0开头 十六进制(hex):以0x或0X开头 运算符 算术运算符 赋值运算符 比较运算符 (ps: instanceof是否是string) 逻辑运算符 (逻辑& | ! ^ 短路&& ||) ps: & 与 && (| 与 ||类似)开发中推荐使用双 相同点:运算结果相同,当左边为true时都执行 不同点:当左边为false时,&继续执行&&短路不执行 位运算符 (<< >>(最高效2*8) >>>无符号右移(都用0补) & | ^(俩数交换) ~取反(补码各位取反))取决于数据类型 三元运算符 (? : ) ps:可以嵌套使用,凡是?:(运算效率高)都可以改写成if-else,反之不成立 程序流程控制基本流程结构: 顺序结构 分支结构 if-else switch 循环结构 while do-while for foreace 引用数据类型变量:数组 初始化 1234567// 静态初始化int[] ids = new int[]{1,3,5,6};// 动态初始化int[] ids = new int[4];int[] ods = new int[3][4];// 数组一旦初始化完成,在内存中占有一连串地址,并且长度无法改变。 默认初始化值:整型为0,浮点型0.0,char为0或‘\u000’非‘0’,boolean型false,引用类型为null 内存结构解析 栈stack:局部变量 堆heap:new出来的结构:对象、数组 方法区 常量池 静态域 Array工具类 boolean equals(int[] a, int[] b) String toString(int[] a) void fill(int[] a, int val) void sort(int[] a) int binarySearch(int[] a, int key) 数组中常见异常 角标越界异常:ArrayIndexOutOfBoundsExcetion 空指针异常:NullPointerException]]></content>
<categories>
<category>Java</category>
<category>概念与语法</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[技术学习方法]]></title>
<url>%2Funcategorized%2F%E6%8A%80%E6%9C%AF%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[徒弟:师傅这就是绝世武功秘籍吗?师傅:不,这只是绝世武功的目录! 来自 codesheep的视频分享 说在前面:方法因人而异,仅供参考 心法阶段一:认知了解阶段(第一印象很重要) 1.该技术的概念和方向2.该技术解决了什么问题3.同类技术有哪些4.该技术的主要组成部分5.该技术为什么出现 阶段二:学习语法,用法途径进阶1.视频教程(效率低)2.快速上手视频3.入门博客4.权威书籍,官方文档 阶段三:局部练习,小型实战,搭建环境(记录总结,写博客写博客写博客!!!) 阶段四:上手实际项目或开源项目(先打牢基础) 阶段五:造轮子,撸源码(终极目标) 秘籍基础知识编程语言:java python c基本语法基本网络知识:tcp/ip http/https-—————————————— 工具方面操作系统:linux (CentOS\Ubuntu\Fe..)代码管理:svn/git持续集成(CI/CD):jenkinsjava的项目管理工具:maven/gradle-—————————————– 框架方面ssh (spring+structs+hibernate)(已过时)ssm:spring +springmvc+mybatis(流行)spring boot-—————————————– 各种中间件MQ 消息队列RPC 通讯框架 gRPC thrift dubbo springcloudelasticsearch 收索引擎 数据库-———————————————— 数据库SQL:mysql/postgre sqlNosql:redis memcached mongodb elasticsearch-————————————————- 架构方面分布式/微服务架构spring clouddubborpc通信-———————————————– 虚拟化/容器化的技术Docker 容器化k8s kubernetes-————————————————- 关注源码/性能jdk源码以及部分设计思想Spring 源码JVM细节与排错高并发/高可用]]></content>
</entry>
<entry>
<title><![CDATA[Java_day1]]></title>
<url>%2FJava%2F%E4%BD%93%E7%B3%BB%2FJava-day1%2F</url>
<content type="text"><![CDATA[自用Java笔记(Ⅰ),记录Java体系与培训内容大纲和具体学习路线!奋斗ing Java体系 JDK初步 Java Web编程 J2EE J2ME 移动增值 Java培训 CodeSheep来告诉你编程培训班到底培训什么内容和具体学习路线! 摘自CodeSheep的B站视频 第一阶段入门进阶 JAVA基本语法 OO的编程思想 集合 IO 异常 泛型 反射 多线程 函数式. 第二阶段Web基础和工具 前段基础三件套(html/css/js), jquery,ajax,jsp,cookie,session, http基础 servlet基础, Git,SVN等代码管理工具 第三阶段企业级应用框架培训 maven/gradle项目管理工具 Spring全家桶:Spring/Spring MVC/ SpringBoot(比较先进的培训有SpringBoot,而SSH基本不用报) 关系型数据库 MySQL,jdbc,MyBatis,Hibernate. 非关系数据库 Redis缓存 (也是区别重点) 模板技术: thymeleaf,freemarker 第四阶段高级应用框架培训 搜索引擎 elastic search RPC框架/微服务框架: Dubbo,Spring Cloud(区别重点) 中间件技术 RabbitMQ,RocketMQ,ActiveMQ,Kafka等. 虚拟化技术:Docker容器,k8s容器编排技术等 第五阶段高级话题 JVM优化和排错 GC分析 数据库高级优化等话题]]></content>
<categories>
<category>Java</category>
<category>体系</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java_training]]></title>
<url>%2Funcategorized%2FJava-training%2F</url>
<content type="text"><![CDATA[CodeSheep来告诉你编程培训班到底培训什么内容和具体学习路线! 摘自CodeSheep的B站视频 第一阶段入门进阶 JAVA基本语法 OO的编程思想 集合 IO 异常 泛型 反射 多线程 函数式. 第二阶段Web基础和工具 前段基础三件套(html/css/js), jquery,ajax,jsp,cookie,session, http基础 servlet基础, Git,SVN等代码管理工具 第三阶段企业级应用框架培训 maven/gradle项目管理工具 Spring全家桶:Spring/Spring MVC/ SpringBoot(比较先进的培训有SpringBoot,而SSH基本不用报) 关系型数据库 MySQL,jdbc,MyBatis,Hibernate. 非关系数据库 Redis缓存 (也是区别重点) 模板技术: thymeleaf,freemarker 第四阶段高级应用框架培训 搜索引擎 elastic search RPC框架/微服务框架: Dubbo,Spring Cloud(区别重点) 中间件技术 RabbitMQ,RocketMQ,ActiveMQ,Kafka等. 虚拟化技术:Docker容器,k8s容器编排技术等 第五阶段高级话题 JVM优化和排错 GC分析 数据库高级优化等话题]]></content>
</entry>
<entry>
<title><![CDATA[Data_visualization_in_CSU]]></title>
<url>%2F%E5%AE%9E%E4%B9%A0%2F%E6%95%B0%E6%8D%AE%E5%8F%AF%E8%A7%86%E5%88%86%E6%9E%90%2FDaysInCSU%2F</url>
<content type="text"><![CDATA[记录在中南大学数据可视分析小组待的那几天,感谢老师与学长们的帮助! DAY1在中南大学数据可视分析小组待的第一天(2019-07-09) 前后台数据交换使用Ajax技术实现数据的传递,一个标准的用于在前台声明数据传递的代码格式如下: 1234567891011121314151617181920var formData = new FormData();//声明一个存放数据的对象formData.append("id","requestPlus");//用于前后台交互的IDformData.append("csrfmiddlewaretoken", token);//安全传输formData.append("data",data);//将数据添加到fromData对象中$.ajaxSetup({data: {csrfmiddlewaretoken: '{{ csrf_token }}' },});$.ajax({ url:"/", type:"POST", data:formData,//注意这里为之前声明的数据 processData: false, contentType: false, success:function(data){ jsonData = $.parseJSON(data);//将后台传回来的json格式转换}, error:function(data){}}); 在上面代码中,首先声明了一个用于存放需要进行前后台数据交互的数据对象formData,然后声明一个前后台用于识别当前调用哪个方法的唯一ID,这个ID在后台中将会被识别并执行相应方法。接下来,依次使用append方法将需要传入后台的数据添加到这个对象中,如上面代码中示例的输入参数data,用双引号声明的是参数名称,可以自定义,接下来按照上面代码的格式定义ajax的传输,在success后的function里包含的data即为后台成功执行后返回到前台的json格式的数据,我们需要使用parseJSON方法进行强转。 要注意的是,ajax的使用需要引用jQurey包,所以需要在html文件引用。 前台的部分写完后即可以进行后台的编写,下面一段代码解释了后台如何接收前台数据,进行数据格式的转换,调用相应算法并返回到前台的过程。 12345678910111213if request.is_ajax() and request.POST['id'] == 'requestPlus': //根据ID调用对应方法 data = request.POST.get("data") //根据数据的名称获取到对应的数据 data = data.encode("utf-8")//将获得数据转换成utf-8格式,python3中不需要 data = int(data)//将获得数据转换成int格式 c = plus(a,b)//调用相应的算法 result = { 'c' : c//返回结果 } return HttpResponse(json.dumps(result))//将结果返回并转换成json格式 def plus(data)://加法算法 data += 1 return data 到这里,我们完成了后台关于数据交互功能的编写. 在数据可视化的项目中,用户在前端通过种种交互,设定了某种目标,前台将这一数据传递到后台,后台调用相应的算法进行计算返回,前台获得经过计算的数据之后进行绘制从而实现交互的功能。 疑惑公用的static 文件这么处理 DAY2在中南大学数据可视分析小组待的第二天(2019-07-10) 未解决Ajax后台传回前台数据 csv数据传入Django 数据表如何联动 Django导入csv123456789101112import csv with open('import.csv') as csvfile: reader = csv.DictReader(csvfile) for row in reader: # The header row values become your keys suite_name = row['SuiteName'] test_case = row['Test Case'] # etc.... new_revo = Revo(SuiteName=suite_name, TestCase=test_case,...) new_revo.save() 可视化:最重要的是 D3点击事件对一个被选择的元素,添加监听操作,代码如下: 123.on("click", function(){ } ) 常用的事件(event)有: click : 鼠标单击某元素时,相当于 mousedown 和 mouseup 组合在一起 mouseover : 鼠标移到某元素上 mouseout : 鼠标从某元素移开 mousemove : 鼠标被移动 mousedown : 鼠标按钮被按下 mouseup : 鼠标按钮被松开 dblclick : 鼠标双击 AJAX特点 异步交互 局部刷新 Ajax+Django 前后端数据交互笔记 data:JSON.stringify(data_list) DAY3在中南大学数据可视分析小组待的第三天(2019-07-11) 《精通D3.js》吕之华著 可视化最重要的不在编程,技术需要就去找就行。 地图的数据数据获取:Natural Earth主页 从shp文件中提取需要的地理信息,并保存为JSON格式。 工具: ogr2ogr: 可提取shp文件的地理信息,以及转换为JSON格式。命令行操作。 ogr2gui: 基于ogr2ogr开发的图形化软件,下载地址 shibai 由于 GeoJSON 文件中的地图数据,都是经度和纬度的信息。它们都是三维的,而要在网页上显示的是二维的,所以要设定一个投影函数来转换经度纬度。 投影函数1234var projection = d3.geoMercator() .center([107, 31]) .scale(850) .translate([width/2, height/2]); 可以参考: https://github.com/mbostock/d3/wiki/Geo-Projections 第 2 行:center() 设定地图的中心位置,[107,31] 指的是经度和纬度。 第 3 行:scale() 设定放大的比例。 第 4 行:translate() 设定平移。 地理路径生成器为了根据地图的地理数据生成 SVG 中 path 元素的路径值,需要用到 d3.geo.path(),我称它为地理路径生成器。 12var path = d3.geoPath() .projection(projection); projection() 是设定生成器的投影函数,把上面定义的投影传入即可。以后,当使用此生成器计算路径时,会自己加入投影的影响。 向服务器请求文件并绘制地图12345678910111213141516171819202122232425d3.json("china.json", function(error, root) { if (error) return console.error(error); console.log(root.features); svg.selectAll("path") .data( root.features ) .enter() .append("path") .attr("stroke","#000") .attr("stroke-width",1) .attr("fill", function(d,i){ return color(i); }) .attr("d", path ) //使用地理路径生成器 .on("mouseover",function(d,i){ d3.select(this) .attr("fill","yellow"); }) .on("mouseout",function(d,i){ d3.select(this) .attr("fill",color(i)); });}); 接下来,就是给 svg 中添加 path 元素。本例中,每一个 path 表示一个省。要注意 attr(“d”,path) 这一行代码,它相当于: 123.attr("d",funtion(d){ return path(d);}) 文献计算机视觉领域世界三大顶级会议分别为CVPR、ICCV和ECCV。 三大期刊三大会议《nature》《science》《cell》 先去浙大可视小组的论文集里找,然后去scholar.google.cn找 近3年关于“动态图”(英文:dynamic diagram 或者dynamic graph) 动态图综述 DAY4在中南大学数据可视分析小组待的第四天(2019-07-12) dynamic diagram / dynamic graph bootstrap4 这两天相关安排: 快速了解可视化需要的相关技术方法,不用全很熟,搞清楚框架和流程,到时候用到谁再细化学习; 搜集动态图可视化和张量分解在动态图可视化中应用相关的文献; 讨论动态图中运用张量分解进行可视分析的思路和存在的问题。 Note矩阵分解有三个很明显的用途: 降维处理 缺失数据填补(或者说成“稀疏数据填补”) 隐性关系挖掘, 张量(tensor) 阶 实例 0 纯量(只有方向) 1 向量(大小和方向) 2 矩阵(数据表) 3 3阶张量(数据立方) n rank:number of dimensions shape: number of rows and columns type: data type of tensor’s elements 延伸阅读:Tucker分解与CP分解的比较前面,我们提到了CP分解可认为是Tucker分解的特例,那么,如何理解两者之间的异同呢?我们先写出两种分解的数学表达式。 Tucker分解: CP分解: 其中,张量的大小为,在Tucker分解中,核心张量的大小为,矩阵、、的大小分别是、、;在CP分解中,矩阵、、大小分别为、、,运算符号“”表示外积(outer product),如向量,向量,则。 张量在位置索引上对应的元素为 Tucker分解: CP分解: 从这两个数学表达式不难看出,CP分解中构成的向量替换了Tucker分解中构成的核心张量(如图1所示),即CP分解是Tucker分解的特例,CP分解过程相对于Tucker分解更为简便,但CP分解中的选取会遇到很多复杂的问题,如张量的秩的求解是一个NP-hard问题等,这里不做深入讨论。 图1 CP分解过程(图片来源:Low-Rank Tensor Networks for Dimensionality …&filter=sc_long_sign&tn=SE_xueshusource_2kduw22v&sc_vurl=http%3A%2F%2Farxiv.org%2Fpdf%2F1609.00893&ie=utf-8&sc_us=5709486074605317721)) 今天相关安排:1、搜集动态图可视化和张量分解在动态图可视化中应用相关的文献;2、讨论动态图中运用张量分解进行可视分析的思路和存在的问题。 明天安排:1、开始尝试设计时空数据可视化界面,先把几个视图设计好(可以参考其他的做时空数据可视化的页面布局)。 DAY5在中南大学数据可视分析小组待的第五天(2019-07-13) 设计]]></content>
<categories>
<category>实习</category>
<category>数据可视分析</category>
</categories>
<tags>
<tag>数据可视分析</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Django初识]]></title>
<url>%2Fpython%2FDjango%2FDjango%E5%88%9D%E8%AF%86%2F</url>
<content type="text"><![CDATA[以一个极简的博客网站为例,领略Diango。 本笔记提炼自慕课网教程 项目代码托管于我的Github 创建项目步骤在工作目录打开命令行,输入 123django-admin.py startproject project_nameeg: django-admin.py startproject myblog 项目目录结构树manage.py ./myblog –_init_.py –settings.py –urls.py –wsgi.py 介绍 文件 作用 urls.py 用于配置URL wsgi.py WSGI(Python Web Server Gateway Interface) settings.py settings.py 配置文件 init\.py python 声明模块文件(为空) 创建应用步骤在项目 manage.py 同级目录下打开命令行中,输入 1python manage.py startapp blog 添加应用名到 settings.py 中的 INSTALLED_APPS 里 应用目录结构树./migrations –_init_.py _init_.py admin.py apps.py models.py tests.py views.py 介绍 文件 作用 migrations: 数据迁移模块(自动生成) admin.py: 应用的后台管理系统配置 apps.py: 应用的一些配置 models.py: 数据模块,使用ORM test.py: 自动测试模块 views.py: 执行响应的代码所在模块,代码逻辑处理的主要地点 创建第一个页面(响应) 编辑blog.view 每一个响应对应一个函数,函数必须返回一个响应 函数必须接收一个参数,一般约定为request 每一个响应函数对应一个URL 1234567# view.pyfrom django.shortcuts import renderfrom django.http import HttpResponse# Create your views here.def index(request): return HttpResponse('hello,goodwell') 编辑urls.py配置URL url函数放在urlpatterns列表中 包含三个参数: URL,对应方法,名称 关于页面聚合的改进:包含其他URL 在app目录下创建urls.py文件 1234567from django.urls import pathfrom . import viewsurlpatterns = [ path('', views.index),] 在根urls.py 中引入 include,将第二个参数改为include(‘blog.urls’) //其为总路径 12345678from django.contrib import adminfrom django.urls import path, includeurlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls')),] 模板如下 views.py文件用于编写前后台的交互,以及需要的python算法: 一个标准的views.py文件可以按照下面的模板来写: 1234567891011from django.shortcuts import renderfrom django.shortcuts import render_to_response, RequestContextfrom django.http import HttpResponseimport json# Create your views here.def home(request): if request.method == 'POST': #add funtions here return HttpResponse(json.dumps({'message': "error"})) return render_to_response('home.html',{'handler': []}, context_instance=RequestContext(request)) url文件修改成如下代码: 123456789from django.conf.urls import patterns, include, urlfrom django.conf import settingsfrom django.contrib import adminfrom django.conf.urls.static import staticurlpatterns = [url(r'^$', '**testApp.views.home**', name='home'), url(r'^admin/', include(admin.site.urls)),] static文件 修改setting文件,建立static文件夹。 打开settings.py文件,在 1BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)) 后面加上如下的代码,确定静态文件的位置。 12345678#Absolute path to the directory static files should be collected to.#Don't put anything in this directory yourself; store your static files#in apps' "static/" subdirectories and in STATICFILES_DIRS.#Example: "/var/www/example.com/static/"STATIC_ROOT = ''#URL prefix for static files.#Example: "http://example.com/static/", "http://static.example.com/"STATIC_URL = '/static/' 接下来在项目文件夹中建立一个static文件夹用于存放项目中的css,js以及其他文件。 加载静态文件 在html文件的中添加如下代码用于加载静态文件: 12{% load staticfiles %}<script> token= '{{ csrf_token }}';</script> 在html文件中引入css文件和script文件的方法如下代码:12<link rel="stylesheet" type="text/css" href="{%static 'css/scatter.css'%}"><script src = "{%static 'lib/d3.min.js' %}"></script> TemplatesHTML文件,使用了Django模板语言(Django Template Language, DTL),可以使用第三方模板 步骤 在app的根目录下创建 Templates 的目录 在该目录下创建HTML文件 在views.py中返回render() //渲染 DTLrender() 函数三个参数 request ‘index.html’ dict类型参数 该字典是后台传递到模板的参数,键为参数名 在模板中使用 来直接使用 注意事项查找Templa按照INSTALLED_APPS 中添加的顺序查找 不同app下Templates 目录下的同名 .html文件会造成冲突 解决方法:在app的Templates 的目录创建app名的目录,将HTML放在其中 Models通常,一个Models对应数据库的一张数据表,Django中Models以类的形式表现,包含一些基本字段和数据的一些行为。(ORM) 生成数据表步骤 在应用根目录下的models.py引入models模板,创建类,继承models.Model,该类即是一张数据表。 在类中创建字段 生成数据表:命令行进入manage.py 同级目录,执行 123python manage.py makemigrations app名(可选)python manage.py migrate 字段创建字段即类中的属性(变量),eg: 1234567# models.pyfrom django.db import modelsclass Article(models.Model): title = models.CharField(max_length=32, default='Title') content = models.TextField(null=True) 数据库查看自动在app/migrations/目录下生成移植文件,执行 1python manage.py sqlmigrate 应用名 文件id 查看SQL语句,默认sqlite3 的数据库在项目根目录下db.sqlite3 查看并编辑db.sqlite3,使用第三方软件: SQLite Expert Personal (免费,轻量级) 页面呈现数据后台步骤: views.py 中import models 1234567891011# views.pyfrom django.shortcuts import renderfrom django.http import HttpResponse# Create your views here.from . import modelsdef index(request): article = models.Article.objects.get(pk=1) return render(request, 'blog/index.html', {'article': article}) 前端步骤: 模板可以直接使用对象以及对象的“.” 操作 12<h1>{{ article.title }}</h1><h3>{{ article.content }}</h3> Admin是Django自带的一个功能强大的自动化数据管理界面(后台),被授权的用户可以直接在Admin中管理数据库,提供了定制功能。 配置 创建用户(超级用户): 命令行输入 1python manage.py createsuperuser Admin入口: localhost:8000/admin/ 修改settings.py 中LANGUAGE_CODE = ‘zh-Hans’ 配置应用: 在应用下admin,py 中引入自身的models模块(或里面的模型类) 编辑admin.py: admin.site.register(models.Article) 123456# admin.pyfrom django.contrib import adminfrom .models import Articleadmin.site.register(Article) 修改数据默认显示名称 在Article类下添加一个方法,python3选择__str__(self) // 注意双下划线,然后return self.title 12345678910# models.pyfrom django.db import modelsclass Article(models.Model): title = models.CharField(max_length=32, default='Title') content = models.TextField(null=True) def __str__(self): return self.title 补充内容Template过滤器123{{ value | filter }}eg:可叠加:{{ list_nums | length }} Django Shell自动引入项目环境,命令行输入 1python manage.py shell 可以来进行调试,可以用于测试未知的方法 12from blog.models import ArticleArticle.objects.all() Admin增强 创建admin配置类 123class ArticleAdmin(admin.ModelAdmin)注册: admin.site.register(Article, ArticleAdmin) 显示其他字段 1list_display = ('title', 'content') 过滤器 1list_filter = ('pub_time',)]]></content>
<categories>
<category>python</category>
<category>Django</category>
</categories>
<tags>
<tag>Django python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[D3初识]]></title>
<url>%2FJavaScript%2FD3-js%2FD3%E5%88%9D%E8%AF%86%2F</url>
<content type="text"><![CDATA[以一个简单的可视化分析系统为例,领略D3.js 本文主要为记录D3.js笔记。 根据刘晖老师发的文档介绍的技术,通过查阅资料实现一个类似的简单的系统界面。 笔记、项目代码托管于我的Github 选择元素和绑定数据选择元素在 D3 中,用于选择元素的函数有两个: d3.select():是选择所有指定元素的第一个 d3.selectAll():是选择指定元素的全部 这两个函数返回的结果称为选择集。 例如,选择集的常见用法如下。 12345var body = d3.select("body"); //选择文档中的body元素var p1 = body.select("p"); //选择body中的第一个p元素var p = body.selectAll("p"); //选择body中的所有p元素var svg = body.select("svg"); //选择body中的svg元素var rects = svg.selectAll("rect"); //选择svg中所有的svg元素 选择集和绑定数据通常是一起使用的。 绑定数据D3 有一个很独特的功能:能将数据绑定到 DOM 上,也就是绑定到文档上。 D3 中是通过以下两个函数来绑定数据的: datum():绑定一个数据到选择集上 data():绑定一个数组到选择集上,数组的各项值分别与选择集的各元素绑定 相对而言,data() 比较常用。 假设现在有三个段落元素如下。 123<p>Apple</p><p>Pear</p><p>Banana</p> $$ datum()$$ 假设有一字符串 China,要将此字符串分别与三个段落元素绑定,代码如下: 12345678910var str = "China";var body = d3.select("body");var p = body.selectAll("p");p.datum(str);p.text(function(d, i){return "第 "+ i + " 个元素绑定的数据是 " + d;}); 绑定数据后,使用此数据来修改三个段落元素的内容,其结果如下: 123第 0 个元素绑定的数据是 China第 1 个元素绑定的数据是 China第 2 个元素绑定的数据是 China 在上面的代码中,用到了一个无名函数 function(d, i)。当选择集需要使用被绑定的数据时,常需要这么使用。其包含两个参数,其中: d 代表数据,也就是与某元素绑定的数据。 i 代表索引,代表数据的索引号,从 0 开始。例如,上述例子中:第 0 个元素 apple 绑定的数据是 China。 $$ data()$$ 有一个数组,接下来要分别将数组的各元素绑定到三个段落元素上。 1var dataset = ["I like dog","I like cat","I like snake"]; 绑定之后,其对应关系的要求为: Apple 与 I like dog 绑定 Pear 与 I like cat 绑定 Banana 与 I like snake 绑定 调用 data() 绑定数据,并替换三个段落元素的字符串为被绑定的字符串,代码如下: 1234567var body = d3.select("body");var p = body.selectAll("p");p.data(dataset) .text(function(d, i){return d;}); 这段代码也用到了一个无名函数 function(d, i),其对应的情况如下: 当 i == 0 时, d 为 I like dog。 当 i == 1 时, d 为 I like cat。 当 i == 2 时, d 为 I like snake。 此时,三个段落元素与数组 dataset 的三个字符串是一一对应的,因此,在函数 function(d, i) 直接 return d 即可。结果自然是三个段落的文字分别变成了数组的三个字符串。 123I like dogI like catI like snake 插入元素插入元素涉及的函数有两个: append():在选择集末尾插入元素 insert():在选择集前面插入元素 假设有三个段落元素,与上文相同。$$ append()$$ 12body.append("p").text("append p element"); 在 body 的末尾添加一个 p 元素,结果为: 1234ApplePearBananaappend p element $$insert()$$ 在 body 中 id 为 myid 的元素前添加一个段落元素。 12body.insert("p","#myid").text("insert p element"); 已经指定了 Pear 段落的 id 为 myid,因此结果如下。 1234Appleinsert p elementPearBanana 删除元素删除一个元素时,对于选择的元素,使用 remove 即可,例如: 12var p = body.select("#myid");p.remove(); 如此即可删除指定 id 的段落元素. 比例尺的使用将某一区域的值映射到另一区域,其大小关系不变,这就是比例尺(Scale)。 D3 中的比例尺,也有定义域和值域,分别被称为 domain 和 range。开发者需要指定 domain 和 range 的范围,如此即可得到一个计算关系。 线性比例尺线性比例尺,能将一个连续的区间,映射到另一区间。要解决柱形图宽度的问题,就需要线性比例尺。假设有以下数组: 1var dataset = [1.2, 2.3, 0.9, 1.5, 3.3]; 现有要求如下:将 dataset 中最小的值,映射成 0;将最大的值,映射成 300。代码如下: 12345678var min = d3.min(dataset);var max = d3.max(dataset);var linear = d3.scale.linear().domain([min, max]).range([0, 300]);linear(0.9); //返回 0linear(2.3); //返回 175linear(3.3); //返回 300 其中,) d3.scale.linear() 返回一个线性比例尺。domain() 和 range() 分别设定比例尺的定义域和值域。在这里还用到了两个函数,它们经常与比例尺一起出现: d3.max() d3.min() 这两个函数能够求数组的最大值和最小值,是 D3 提供的。有一点请大家记住:d3.scale.linear() 的返回值,是可以当做函数来使用的。因此,才有这样的用法:linear(0.9)。 序数比例尺有时候,定义域和值域不一定是连续的。例如,有两个数组: 12var index = [0, 1, 2, 3, 4];var color = ["red", "blue", "green", "yellow", "black"]; 我们希望 0 对应颜色 red,1 对应 blue,依次类推。但是,这些值都是离散的,线性比例尺不适合,需要用到序数比例尺。 123456var ordinal = d3.scale.ordinal().domain(index).range(color);ordinal(0); //返回 redordinal(2); //返回 greenordinal(4); //返回 black 用法与线性比例尺是类似的。 给柱形图添加比例尺在上一章的基础上,修改一下数组,再定义一个线性比例尺。 1234var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];var linear = d3.scale.linear().domain([0, d3.max(dataset)]).range([0, 250]); 其后,按照上一章的方法添加矩形,在给矩形设置宽度的时候,应用比例尺。 1234567891011121314var rectHeight = 25; //每个矩形所占的像素高度(包括空白)svg.selectAll("rect").data(dataset).enter().append("rect").attr("x",20).attr("y",function(d,i){return i * rectHeight;}).attr("width",function(d){return linear(d); //在这里用比例尺}).attr("height",rectHeight-2).attr("fill","steelblue"); 如此一来,所有的数值,都按照同一个线性比例尺的关系来计算宽度,因此数值之间的大小关系不变。 数据切片]]></content>
<categories>
<category>JavaScript</category>
<category>D3.js</category>
</categories>
<tags>
<tag>D3.js JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[常用软件记录]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%2F%E5%B8%B8%E7%94%A8%E8%BD%AF%E4%BB%B6%2F%E5%B8%B8%E7%94%A8%E8%BD%AF%E4%BB%B6%E8%AE%B0%E5%BD%95%2F</url>
<content type="text"><![CDATA[win10 超好用软件推荐(上)本文所有应用程序都可在官网上下载,如若无法下载可以联系博主,可以提供最新的安装程序及相关文件(你懂的)。 (都是自己一个一个用过以后觉得相对好用的应用软件) Listary文件搜索与应用程序启动,“快准狠” Listary – A truly amazing and polished search utility that is somewhere between a launcher and a file manager, both but neither. Listary lives next to your major file management apps and makes managing their lists and finding files a breeze. Really something special and worth your download. Listary下载 PS:同类型还有Wox,有点小bug,个人还是觉得Listary好用。两者似乎都是基于everything,是真的很强,秒杀win10自带搜索框,不喜欢花里胡哨的强烈推荐这个。 Bandizip压缩解压缩软件,比其他压缩软件真的好太多,不论是速度还是压缩度 主要功能 全能的压缩/解压/浏览/编辑软件 可提取30多种格式,包括RAR/RAR5/7Z/ZIP等 包含密码压缩和分卷压缩功能 支持多核高速压缩 测试文件完整性以确定压缩包是否损坏 支持修改代码页改 可集成至资源管理器右键菜单 Bandizip下载 Honeyview绿色轻量级图像浏览器,“强大的漫画阅读器,速度惊人的图像浏览器”,同时支持不解压浏览ZIP、RAR和7z压缩包中的图片。软件功能强大,看图快速,支持几乎所有图片格式的浏览,包括GIF动图,甚至RAW文件、PSD文件(Photoshop专用格式),功能强大,还免费。和Bandizip是同一家开发的。 支持的格式 图片格式: BMP, JPG, GIF, PNG, PSD, DDS, JXR, WebP, J2K, JP2, TGA, TIFF, PCX, PGM, PNM, PPM, and BPG Raw 图片格式: DNG, CR2, CRW, NEF, NRW, ORF, RW2, PEF, SR2, and RAF 动画格式: Animated GIF, Animated WebP, Animated BPG, and Animated PNG 无需解压,直接预览图像: ZIP, RAR, 7Z, LZH, TAR, CBR, and CBZ Honeyview下载 HEIF-Utility图片查看与格式转换,“麻雀虽小五脏俱全”,可在 Windows 上查看/转换 Apple HEIF 图片(当时下载此软件的主要原因,后来见识到它的强大)。 功能 / Features 1.查看由 Apple 设备生成的 HEIF 图片 / View Apple devices-generated HEIF images 2.查看图片 EXIF 信息 / View image EXIF 3.将 Apple HEIF 图片转换为 JPEG 图片 / Convert Apple HEIF images to JPEG images 4.批量转换 / Batch Conversion HEIF-Utility下载(若网页打不开,可直接在GitHub上下载,附其GitHub地址) IDM下载神器,震惊到了! Internet Download Manager: the fastest download accelerator Internet Download Manager是一个用于Windows系统的下载管理器。 IDM可以让用户自动下载某些类型的文件,它可将文件划分为多个下载点以更快下载,并列出最近的下载,方便访问文件。相对于其它下载管理器,它具有独特的动态文件分割技术。 维基百科 IDM下载(若网页打不开,可联系博主) PotPlayer多功能视频播放器,“不多说好用就是好用” Multifunctional Media Player特点 捆绑了多媒体文件的解码器,不需要另外安装解码器也可以播放。 硬件加速,支持DxVA(DirectX Video Acceleration)。 提供更多的字幕输出选项。 支持播放50MB以下的ZIP/RAR压缩包内的多媒体内容。 维基百科 PotPlayer下载 Sci-Hub论文文献下载神器,“传奇的创作背景,科研人的福音” 点开这篇知乎文章看看一个神奇的免费下载文献的网站(使用技巧) typoraMarkdown编辑、阅读器,“本片博文就是用这个码出来的” a markdown editor, markdown reader. Typora是一款由Abner Lee开发的轻量级Markdown编辑器,适用于OS X、Windows和Linux三种操作系统,是一款免费软件。与其他Markdown编辑器不同的是,Typora没有采用源代码和预览双栏显示的方式,而是采用所见即所得的编辑方式,实现了即时预览的功能,但也可切换至源代码编辑模式。 维基百科 typora下载 八爪鱼采集器抓取一些简单的网络数据可以直接用这个,很给力。 八爪鱼网页数据采集器,是一款使用简单、功能强大的网络爬虫工具,完全可视化操作,无需编写代码,内置海量模板,支持任意网络数据抓取。来自他们的广告词 八爪鱼采集器下载 Mamsds桌面倒计时倒计时软件,小巧可以设置多种格式,“就是我想要的那种桌面倒计时”]]></content>
<categories>
<category>计算机</category>
<category>常用软件</category>
</categories>
<tags>
<tag>计算机</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点汇总]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B9%E6%B1%87%E6%80%BB%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(共八章)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(此为汇总版,单独章节版见 计算机网络考点目录下 其他博文) 绪论计算机网络发展的四个阶段-P1 计算机网络的形成与发展 互联网的形成与发展 移动互联网的形成与发展 物联网的形成与发展 ARPANET研究的主要问题-P2三网融合的概念-P10实质上是计算机网络、电信通信网与电视传输网技术的融合、业务的融合。 计算机网络发展的三条主线-P12 从 ARPANET 到 Internet 从无线分组网到无线自组网、无线传感器网络 网络安全技术 计算机网络的定义-P14计算机网络是 “以相互共享资源的方式互联起来的自治计算机系统的集合”。 按覆盖范围分类,计算机网络的类型-P17 广域网(WAN) 城域网(MAN) 局域网(LAN) 个人区域网(PAN) 人体区域网(BAN) ISP 和 ICP 的概念-P26 ISP (Internet service provider)Internet服务提供者 ICP (Internet content provider)Internet内容提供商 常见的计算机网络拓扑类型及其特点-P29 星状 环状 总线型 树状 网状 常见的数据交换方式及其特点-P30 基本分为两大类: 线路交换 与 存储转发交换 存储转发交换又分为两类: 报文(存储转发)交换 与 分组(存储转发)交换 分组交换分为两类: 数据报交换 与 虚电路交换 面向连接和无连接服务的比较-P41网络协议及其三要素-P42 为网络数据交换制定的通信规则、约定与标准称为“网络协议” 三要素: 语义(要做什么)、语法(要怎么做)、时序(做的顺序) OSI 七层模型的层次结构-P47物理层、数据链路层、网络层、传输层、会话层、表示层和应用层 TCP/IP 参考模型的层次结构-P51 应用层 传输层 互联网络层 主机-网络层 主要的网络协议标准化组织-P53 国际电信联盟(ITU) 国际标准化组织(ISO) 电子工业协会(EIA) 电气电子工程师(IEEE) 物理层物理层的基本功能-P58 向数据链路层提供比特流传输服务。 根据所使用传输介质的不同,制定相应的物理层协议,规定数据信号编码方式、传输速率,以及相关的通信参数。 屏蔽物理层所采用的传输介质,通信设备与通信技术的差异性。 信息、数据、信号间的关系-P62信 息 -> 数 据(eg:01串) -> 信 号(eg:光电信号) 串行通信和并行通信-P64 串行通信:一个字符的二进制代码按由低位到高位的顺序依次发送。 并行通信:一个字符的8位二进制代码同时通过8条并行的通信信道发送,每次发送一个字符代码。 单工、半双工、双工通信-P64 单工:信号只能向一个方向传输,任何时候都不能改变信号的传送方向。 半双工:信号可以双向传送,但是必须交替进行,一个时间只能向一个方向传送。 双工:信号可以同时双向传送。 同步技术-P65 位同步 字符同步 常用的传输介质-P66 双绞线 同轴电缆 光纤 无线 卫星通信 制作双绞线时直通线和交叉线的线序-实验册 标准[568A]:绿白-1,绿-2,橙白-3,蓝-4,蓝白-5,橙-6,棕白-7,棕-8; 标准[568B]:橙白-1,橙-2,绿白-3,蓝-4,蓝白-5,绿-6,棕白-7,棕-8 直通线:两头都是[568A]或者两头都是[568B],(两头相同,用于连接电脑和交换机和交换机之间的连接) 交叉线:一头[568A]一头[568B] ,(两头不同,用于两台计算机直连或者两个Hub 和 uplink口相连) 光纤通信的基本原理-P67光纤通过内部的全反射来传输一束通过编码的光信号。 单模光纤与多模光纤的区别-P68多模光纤:光信号与光纤轴成多个可分辨角度的多路光载波传输。(反射多次,粗,我们用的,光源为发光二极管) 单模光纤:光信号仅与光纤轴成单个可分辨角度的多路光载波传输。(近似直射,细,适合远距离传输,光源为激光二极管,贵) 模拟数据的编码方法-P75 振幅键控 移频键控 移相键控 波特率的概念-P76波特率(调制速率)描述的是码元传输的速率(所以又被称为码元传输速率),单位是波特(Baud)等价于,码元/s。 PS:码元个数也可以被称为脉冲个数或者信号变化的次数 波特率与比特率的关系-P76比特率:单位时间内数字通信系统所传输的 二进制码元 个数,单位bps等价于bit/s。 $$S = B log_2k$$比特率 = 波特率 X 单个调制状态对应二进制位数k* PS:在二进制码元情况下,比特率在数值上等于波特率。eg:一个码元携 n bit信息量,则 M Baud的码元传输速率 <=> M × n bit/sPS: 相数 <=> 进制数 非归零编码-P77曼彻斯特编码-P77均见图2-22,比较简单但是重要 脉冲编码调制(PCM)的过程-P78是模拟数据数字化的主要方法。 采样 量化 编码 香农定理的基本内容-P81PS:信息论里面也讲了 香农公式:$$C_{max} = W × log_2(1+S/N)$$ 信噪比$$(dB)= 10log_{10}(S/N)$$ W为信道带宽,所以要提高最大数据传输速率,就应该设法提高传输线路的带宽或提高所传信号的信噪比。 多路复用技术的分类及各自特点-P82 时分多路复用(TDM) 频分多路复用(FDM) 波分多路复用(WDM) 码分多路复用(码分多址CDMA)(与正交频分复用) ADSL的特点-P89 ADSL在电话线上同时提供电话与Internet接入服务 ADSL提供的非对称带宽特性 ADSL结构 HFC的特点-P91 HFC下行信道与上行信道频段划分有多种方案(既有带宽相同的对称结构又有非对称结构) 用户端的电视机与计算机分别接到线缆调制解调器(下行有线电视信道传输的电视节目,下行数据信道传输数据,上行数据信道传输的数据传输到头端) HFC的头端又被称为电缆调制解调器终端系统。 小区光纤结点将光纤干线和同轴电缆相互连接。 HFC对上行信道与下行信道的管理是不相同的。 移动通信的接入Internet的基本原理-P97移动通信系统由移动终端、接入网与核心交换网三部分组成。 工作原理示意图见图2-43 数据链路层物理线路的噪声类别-P101 类别 产生原因 引起 特点 热噪声 传播介质导体的电子热运动 随机差错 时刻存在,幅度较小,强度与频率无关,频谱很宽 冲击噪声 外界电磁干扰 传输差错 幅度大,持续时间长 误码率的定义-P102是指二进制比特在数据传输系统中被传错的概率,它在数值上近似等于$$P_e = N_e / N$$N为传输的二进制比特总数,$$N_e$$是被传错的比特数。 CRC的工作原理-P103(此处出计算题)见P104举例 链路层的主要功能-P106 数据链路管理 帧同步 流量控制 差错控制 透明传输 寻址 HDLC的帧结构-P113见图3-10 PPP的帧结构-P116见图3-13 介质访问控制(MAC)层“冲突”的概念-P122如果有两个或两个以上的主机同时在一条共享介质发送数据,那么多路的信号就会出现相互干扰,造成接收主机无法正确接受任何一台主机发送的数据,这种现象称为“冲突”。 CSMA/CD 的基本工作原理-P132CSMA/CD 协议的基本思想是发送前侦听边发送边做听一旦出现碰撞马上停止发送。 CSMA/CD工作流程“先听后发,边听边发,冲突停发,随机重发”,用于总线型或半双工网络环境,应用于有线连接的局域网。 物理地址的概念-P14148位地址,前24位为公司标识,后24位位网卡厂家自行分配,具有唯一性。 VLAN的概念-P146虚拟局域网技术 10M\100M 自动协商功能的原理-P152不同的组网设备-P166 中继器: 集线器: 网桥: 交换机: 路由器: 无线信道划分的基本方法-P170 2.4GHz频段的信道划分方法 5GHz频段的信道划分方法 CSMA/CA 的基本工作原理CSMA/CA 协议的基本思想是在发送数据时先广播告知其他结点,让其他统点在某段时间内不要发送数据,以免出现磁撞。 CSMA/CD 与 CSMA/CA 的比较从应用场景看: CSMA/CD:有线局域网 CSMA/CA:无线局域网 从命名上看: CD(Colllision Detection)碰撞检测 CA(Colllision Avoidance)碰撞避免 区别: 1) CSMA/CD可以检测冲突,但无法避免: CSMA/CA 发送包的同时不能检测到信道上有无冲突,本结点处没有冲突并不意味着在接收结点处就没有冲突,只能尽量避免。2)传输介质不同。CSMA/CD 用于总线形以太网,CSMA/CA 用于无线局域网802.11a/b/g/n等。3)检测方式不同。CSMA/CD 通过电缆中的电压变化来检测:而 CSMA/CA 采用能量检测、载波检测和能量载波混合检测三种检测信道空闲的方式。4)在本结点处有(无)冲突,并不一定意味着在接收结点处就有(无) 冲突。 网络层IPV4分组头部格式-P214见图5-3 标准IP地址的分类和表示方法-P223两级IP地址(网络号+主机号) 分类 最大可用网络数 第一个可用网络号 最后一个 每个网络最大主机数 A类 2^7 - 2 1 126 2^24 - 2 B类 2^14 - 1 128.1 191.255 2^16 - 2 C类 2^21 - 1 192.0.1 223.255.255 2^8 - 2 D类 E类 特殊的IP地址 6种特殊地址 网络号 主机号 源地址或目的地址 网络地址 特定的 全0 都不是 直接广播地址 特定的 全1 目的地址 受限广播地址 全1 全1 目的地址 此网络上的这个主机 全0 全0 原地址或默认目的地址 此网络上的特定主机 全0 特定的 目的地址 环回地址 127 不是全0或全1 源地址或目的地址 子网掩码的概念及划分子网的方法-P225-227重点 CIDR的概念-P228无类别域间路由 NAT的概念-P230网络地址转换 路由表生成的基本过程-P236见图5-18 路由选择协议的类别-P239 内部网关协议(IGP) 外部网关协议(EGP) RIP协议和OSPF协议的比较-P240 主要特点 RIP(路由信息协议) OSPF(最短路径优先) 网关协议 内部 内部 路由表内容 目的网络,下一跳,距离 目的网络,完整路径 最优通路依据 跳数 费用 算法 距离-向量协议 链路状态协议 传送方式 传输层UDP IP数据报 其他 简单、效率低、跳数为16不可达;(好消息传得快,坏消息传得慢) 效率高,路由器频繁交换信息,难维持一致性;规模大,同一度量为可达性 ICMP协议及其应用-P255网际控制报文协议:提高IP数据报交付成功的机会,ICMP协议让主机或路由器报告差错和异常情况。 ICMP报文分为:ICMP差错报告报文 和 ICMP询问报文 两个常见的应用: 分组网间探测PING(用来测试两台主机之间的连通性) Traceroute(UNIX中的名字,在Windows是tracert)(可以用来跟踪分组经过的路由) ARP协议的概念-P270地址解析协议:完成IP地址到MAC地址的映射。 (每台主机都设有一个ARP高速缓存,用来存放本局域网上各个主机和路由器的IP地址到MAC地址的映射表,称ARP表。使用ARP来动态维护此ARP表。) IPV6协议的基本概念-P281IPV6地址的表示-P283冒号十六进制表示法:IPv6的128位地址按每16位划分一个位段,每个位段被转换成一个4位的十六进制数,并用冒号隔开。 传输层端口号及其分类-P296 端口号的数值取 0 ~ 65535 之间的整数 分三类:熟知端口号(0-1023)、注册端口号(1024-49151)和临时端口号(49152-65535) 常见应用的端口号-P297 端口号 服务进程 说明 20 FTP 文件传输协议(数据连接) 21 FTP 文件传输协议(控制连接) 23 TELNET 网络虚拟终端协议 25 SMTP 简单邮件传输协议 80 HTTP 超文本传输协议 179 BGP 边界路由协议 53 DNS 域名服务 67/68 DHCP 动态主机配置协议 69 TFTP 简单文件传送协议 161/162 SNMP 简单网络管理协议 520 RIP 路由信息协议 套接字的概念-P296用于标识一个进程,由IP地址与对应的端口号(IP地址:端口号)组成。 TCP协议和UDP协议的比较 传输层提供两种类型的服务: 无连接服务 和 面向连接服务 相应实现分别为 用户数据报协议(UDP) 和 传输控制协议(TCP) 对应向上提供 不可靠的逻辑信道 和 全双工的可靠的逻辑信道 UDP TCP 传送数据前无需建立连接,数据到达后也无需确认 面向连接,不提供广播或多播服务 无可靠交付 可靠交付 报文头部短,传输开销少,时延较短 报文段头部长,传输开销大 “三次握手”的过程-P307即TCP连接的建立。应该是重点吧,重在理解,见图6-15上半截。 流量控制和拥塞控制的比较-P317 流量控制解决的是发送方和接收方速率不匹配的问题,发送方发送过快接收方就来不及接收和处理。采用的机制是滑动窗口的机制。 拥塞控制解决的是避免网络资源被耗尽的问题,通过大家自律的采取避让的措施,来避免网络有限资源被耗尽。 流量控制是端到端的控制,例如A通过网络给B发数据,A发送的太快导致B没法接收(B缓冲窗口过小或者处理过慢),这时候的控制就是流量控制,原理是通过滑动窗口的大小改变来实现。 拥塞控制是A与B之间的网络发生堵塞导致传输过慢或者丢包,来不及传输。防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至于过载。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络性能有关的所有因素。 应用层C/S 模式和 P2P 模式的概念-P328 C/S(Client/Server)客户/服务器 P2P对等网络 DNS 的功能-P337域名解析:将域名转换为对应的IP地址。 域名的表示方法-P335四级域名.三级域名.二级域名.顶级域名 eg:it.nankai.edu.cn TELNET 的功能-P342解决异构计算机系统互连中存在的问题。(又被称为网络虚拟终端协议、终端仿真协议或者远程终端协议) E-MAIL 的功能-P342是—种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。 E-MAIL 的基本工作原理-P342见图7-13 web 应用的应用层协议 HTTP-P347超文本传送协议 URL 的概念及表示方法-P347由协议类型、主机名和路径及文件名三个部分组成。 eg: http://www.nankai.edu.cn/index.html 搜索引擎的组成-P355 搜索器 索引器 检索器 用户接口 DHCP 的功能-P362可以为主机自动分配IP地址及其他一些重要的参数 SNMP 的功能-P367SNMP(简单网络管理协议)主要解决三个问题:管理信息结构、管理信息库与SNMP规则。网络管理目的是使网络资源能得到有效的利用,网络出现故障时能及时报告和处理,以保证网络能够正常、高效地运行。 FTP 的应用层协议-P371网络安全安全攻击的类型-P388 主动攻击 被动攻击 网络安全服务的类型-P389 认证 访问控制 数据机密性 数据完整性 防抵赖 对称密码体系及 DES-P397非对称密码体系及 RSA-P398数字签名的主要功能-P402可以验证数据在传输过程中是否被篡改,同时能够确认发送者的身份,防止信息交互中抵赖现象的发生。 IPSec 协议的概念-P403为解决IP协议的安全性问题。 SSL 协议的概念-P405安全套接层(Secure socket layer)使用非对称加密技术,保护信息传输的秘密性和完整性。 DoS 攻击的概念-P411拒绝服务(denial of service)攻击主要是通过消耗网络系统有限的、不可恢复的资源,从而使合法用户应该获得的服务质量下降或受到拒绝。本质是延长正常网络应用服务的等待时间,或者使合法用户的请求受到拒绝。 入侵检测系统的概念-P412是对计算机和网络资源的恶意使用行为进行识别的系统。目的是检测和发现可能存在的攻击行为(包括来自系统外部的入侵行为和来自内部用户的非授权行为),并采取相应的防护手段。 防火墙的概念-P415是在网络之间执行控制策略的系统,它包括硬件和软件。目的是保护内部网络资源不被外部非授权用户使用,防止内部受到外部非法用户的攻击。防火墙安装位置为内部与外部网络之间。]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点5]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B95%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(第七章 应用层 与 第八章 网络安全)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(另七章见 计算机网络考点目录下 其他博文) 应用层C/S 模式和 P2P 模式的概念-P328 C/S(Client/Server)客户/服务器 P2P对等网络 DNS 的功能-P337域名解析:将域名转换为对应的IP地址。 域名的表示方法-P335四级域名.三级域名.二级域名.顶级域名 eg:it.nankai.edu.cn TELNET 的功能-P342解决异构计算机系统互连中存在的问题。(又被称为网络虚拟终端协议、终端仿真协议或者远程终端协议) E-MAIL 的功能-P342是—种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。 E-MAIL 的基本工作原理-P342见图7-13 web 应用的应用层协议 HTTP-P347超文本传送协议 URL 的概念及表示方法-P347由协议类型、主机名和路径及文件名三个部分组成。 eg: http://www.nankai.edu.cn/index.html 搜索引擎的组成-P355 搜索器 索引器 检索器 用户接口 DHCP 的功能-P362可以为主机自动分配IP地址及其他一些重要的参数 SNMP 的功能-P367SNMP(简单网络管理协议)主要解决三个问题:管理信息结构、管理信息库与SNMP规则。网络管理目的是使网络资源能得到有效的利用,网络出现故障时能及时报告和处理,以保证网络能够正常、高效地运行。 FTP 的应用层协议-P371网络安全安全攻击的类型-P388 主动攻击 被动攻击 网络安全服务的类型-P389 认证 访问控制 数据机密性 数据完整性 防抵赖 对称密码体系及 DES-P397非对称密码体系及 RSA-P398数字签名的主要功能-P402可以验证数据在传输过程中是否被篡改,同时能够确认发送者的身份,防止信息交互中抵赖现象的发生。 IPSec 协议的概念-P403为解决IP协议的安全性问题。 SSL 协议的概念-P405安全套接层(Secure socket layer)使用非对称加密技术,保护信息传输的秘密性和完整性。 DoS 攻击的概念-P411拒绝服务(denial of service)攻击主要是通过消耗网络系统有限的、不可恢复的资源,从而使合法用户应该获得的服务质量下降或受到拒绝。本质是延长正常网络应用服务的等待时间,或者使合法用户的请求受到拒绝。 入侵检测系统的概念-P412是对计算机和网络资源的恶意使用行为进行识别的系统。目的是检测和发现可能存在的攻击行为(包括来自系统外部的入侵行为和来自内部用户的非授权行为),并采取相应的防护手段。 防火墙的概念-P415是在网络之间执行控制策略的系统,它包括硬件和软件。目的是保护内部网络资源不被外部非授权用户使用,防止内部受到外部非法用户的攻击。防火墙安装位置为内部与外部网络之间。]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点4]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B94%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(第五章 网络层 与 第六章 传输层)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(另七章见 计算机网络考点目录下 其他博文) 网络层IPV4分组头部格式-P214见图5-3 标准IP地址的分类和表示方法-P223两级IP地址(网络号+主机号) 分类 最大可用网络数 第一个可用网络号 最后一个 每个网络最大主机数 A类 2^7 - 2 1 126 2^24 - 2 B类 2^14 - 1 128.1 191.255 2^16 - 2 C类 2^21 - 1 192.0.1 223.255.255 2^8 - 2 D类 E类 特殊的IP地址 6种特殊地址 网络号 主机号 源地址或目的地址 网络地址 特定的 全0 都不是 直接广播地址 特定的 全1 目的地址 受限广播地址 全1 全1 目的地址 此网络上的这个主机 全0 全0 原地址或默认目的地址 此网络上的特定主机 全0 特定的 目的地址 环回地址 127 不是全0或全1 源地址或目的地址 子网掩码的概念及划分子网的方法-P225-227重点 CIDR的概念-P228无类别域间路由 NAT的概念-P230网络地址转换 路由表生成的基本过程-P236见图5-18 路由选择协议的类别-P239 内部网关协议(IGP) 外部网关协议(EGP) RIP协议和OSPF协议的比较-P240 主要特点 RIP(路由信息协议) OSPF(最短路径优先) 网关协议 内部 内部 路由表内容 目的网络,下一跳,距离 目的网络,完整路径 最优通路依据 跳数 费用 算法 距离-向量协议 链路状态协议 传送方式 传输层UDP IP数据报 其他 简单、效率低、跳数为16不可达;(好消息传得快,坏消息传得慢) 效率高,路由器频繁交换信息,难维持一致性;规模大,同一度量为可达性 ICMP协议及其应用-P255网际控制报文协议:提高IP数据报交付成功的机会,ICMP协议让主机或路由器报告差错和异常情况。 ICMP报文分为:ICMP差错报告报文 和 ICMP询问报文 两个常见的应用: 分组网间探测PING(用来测试两台主机之间的连通性) Traceroute(UNIX中的名字,在Windows是tracert)(可以用来跟踪分组经过的路由) ARP协议的概念-P270地址解析协议:完成IP地址到MAC地址的映射。 (每台主机都设有一个ARP高速缓存,用来存放本局域网上各个主机和路由器的IP地址到MAC地址的映射表,称ARP表。使用ARP来动态维护此ARP表。) IPV6协议的基本概念-P281IPV6地址的表示-P283冒号十六进制表示法:IPv6的128位地址按每16位划分一个位段,每个位段被转换成一个4位的十六进制数,并用冒号隔开。 传输层端口号及其分类-P296 端口号的数值取 0 ~ 65535 之间的整数 分三类:熟知端口号(0-1023)、注册端口号(1024-49151)和临时端口号(49152-65535) 常见应用的端口号-P297 端口号 服务进程 说明 20 FTP 文件传输协议(数据连接) 21 FTP 文件传输协议(控制连接) 23 TELNET 网络虚拟终端协议 25 SMTP 简单邮件传输协议 80 HTTP 超文本传输协议 179 BGP 边界路由协议 53 DNS 域名服务 67/68 DHCP 动态主机配置协议 69 TFTP 简单文件传送协议 161/162 SNMP 简单网络管理协议 520 RIP 路由信息协议 套接字的概念-P296用于标识一个进程,由IP地址与对应的端口号(IP地址:端口号)组成。 TCP协议和UDP协议的比较 传输层提供两种类型的服务: 无连接服务 和 面向连接服务 相应实现分别为 用户数据报协议(UDP) 和 传输控制协议(TCP) 对应向上提供 不可靠的逻辑信道 和 全双工的可靠的逻辑信道 UDP TCP 传送数据前无需建立连接,数据到达后也无需确认 面向连接,不提供广播或多播服务 无可靠交付 可靠交付 报文头部短,传输开销少,时延较短 报文段头部长,传输开销大 “三次握手”的过程-P307即TCP连接的建立。应该是重点吧,重在理解,见图6-15上半截。 流量控制和拥塞控制的比较-P317 流量控制解决的是发送方和接收方速率不匹配的问题,发送方发送过快接收方就来不及接收和处理。采用的机制是滑动窗口的机制。 拥塞控制解决的是避免网络资源被耗尽的问题,通过大家自律的采取避让的措施,来避免网络有限资源被耗尽。 流量控制是端到端的控制,例如A通过网络给B发数据,A发送的太快导致B没法接收(B缓冲窗口过小或者处理过慢),这时候的控制就是流量控制,原理是通过滑动窗口的大小改变来实现。 拥塞控制是A与B之间的网络发生堵塞导致传输过慢或者丢包,来不及传输。防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不至于过载。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络性能有关的所有因素。]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点3]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B93%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(第三章 数据链路层 与 第四章 介质访问控制(MAC)层)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(另七章见 计算机网络考点目录下 其他博文) 数据链路层物理线路的噪声类别-P101 类别 产生原因 引起 特点 热噪声 传播介质导体的电子热运动 随机差错 时刻存在,幅度较小,强度与频率无关,频谱很宽 冲击噪声 外界电磁干扰 传输差错 幅度大,持续时间长 误码率的定义-P102是指二进制比特在数据传输系统中被传错的概率,它在数值上近似等于$$P_e = N_e / N$$N为传输的二进制比特总数,$$N_e$$是被传错的比特数。 CRC的工作原理-P103(此处出计算题)见P104举例 链路层的主要功能-P106 数据链路管理 帧同步 流量控制 差错控制 透明传输 寻址 HDLC的帧结构-P113见图3-10 PPP的帧结构-P116见图3-13 介质访问控制(MAC)层“冲突”的概念-P122如果有两个或两个以上的主机同时在一条共享介质发送数据,那么多路的信号就会出现相互干扰,造成接收主机无法正确接受任何一台主机发送的数据,这种现象称为“冲突”。 CSMA/CD 的基本工作原理-P132CSMA/CD 协议的基本思想是发送前侦听边发送边做听一旦出现碰撞马上停止发送。 CSMA/CD工作流程“先听后发,边听边发,冲突停发,随机重发”,用于总线型或半双工网络环境,应用于有线连接的局域网。 物理地址的概念-P14148位地址,前24位为公司标识,后24位位网卡厂家自行分配,具有唯一性。 VLAN的概念-P146虚拟局域网技术 10M\100M 自动协商功能的原理-P152不同的组网设备-P166 中继器: 集线器: 网桥: 交换机: 路由器: 无线信道划分的基本方法-P170 2.4GHz频段的信道划分方法 5GHz频段的信道划分方法 CSMA/CA 的基本工作原理CSMA/CA 协议的基本思想是在发送数据时先广播告知其他结点,让其他统点在某段时间内不要发送数据,以免出现磁撞。 CSMA/CD 与 CSMA/CA 的比较从应用场景看: CSMA/CD:有线局域网 CSMA/CA:无线局域网 从命名上看: CD(Colllision Detection)碰撞检测 CA(Colllision Avoidance)碰撞避免 区别: 1) CSMA/CD可以检测冲突,但无法避免: CSMA/CA 发送包的同时不能检测到信道上有无冲突,本结点处没有冲突并不意味着在接收结点处就没有冲突,只能尽量避免。2)传输介质不同。CSMA/CD 用于总线形以太网,CSMA/CA 用于无线局域网802.11a/b/g/n等。3)检测方式不同。CSMA/CD 通过电缆中的电压变化来检测:而 CSMA/CA 采用能量检测、载波检测和能量载波混合检测三种检测信道空闲的方式。4)在本结点处有(无)冲突,并不一定意味着在接收结点处就有(无) 冲突。]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点2]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B92%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(第二章 物理层)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(另七章见 计算机网络考点目录下 其他博文) 物理层物理层的基本功能-P58 向数据链路层提供比特流传输服务。 根据所使用传输介质的不同,制定相应的物理层协议,规定数据信号编码方式、传输速率,以及相关的通信参数。 屏蔽物理层所采用的传输介质,通信设备与通信技术的差异性。 信息、数据、信号间的关系-P62信 息 -> 数 据(eg:01串) -> 信 号(eg:光电信号) 串行通信和并行通信-P64 串行通信:一个字符的二进制代码按由低位到高位的顺序依次发送。 并行通信:一个字符的8位二进制代码同时通过8条并行的通信信道发送,每次发送一个字符代码。 单工、半双工、双工通信-P64 单工:信号只能向一个方向传输,任何时候都不能改变信号的传送方向。 半双工:信号可以双向传送,但是必须交替进行,一个时间只能向一个方向传送。 双工:信号可以同时双向传送。 同步技术-P65 位同步 字符同步 常用的传输介质-P66 双绞线 同轴电缆 光纤 无线 卫星通信 制作双绞线时直通线和交叉线的线序-实验册 标准[568A]:绿白-1,绿-2,橙白-3,蓝-4,蓝白-5,橙-6,棕白-7,棕-8; 标准[568B]:橙白-1,橙-2,绿白-3,蓝-4,蓝白-5,绿-6,棕白-7,棕-8 直通线:两头都是[568A]或者两头都是[568B],(两头相同,用于连接电脑和交换机和交换机之间的连接) 交叉线:一头[568A]一头[568B] ,(两头不同,用于两台计算机直连或者两个Hub 和 uplink口相连) 光纤通信的基本原理-P67光纤通过内部的全反射来传输一束通过编码的光信号。 单模光纤与多模光纤的区别-P68多模光纤:光信号与光纤轴成多个可分辨角度的多路光载波传输。(反射多次,粗,我们用的,光源为发光二极管) 单模光纤:光信号仅与光纤轴成单个可分辨角度的多路光载波传输。(近似直射,细,适合远距离传输,光源为激光二极管,贵) 模拟数据的编码方法-P75 振幅键控 移频键控 移相键控 波特率的概念-P76波特率(调制速率)描述的是码元传输的速率(所以又被称为码元传输速率),单位是波特(Baud)等价于,码元/s。 PS:码元个数也可以被称为脉冲个数或者信号变化的次数 波特率与比特率的关系-P76比特率:单位时间内数字通信系统所传输的 二进制码元 个数,单位bps等价于bit/s。 $$S = B log_2k$$比特率 = 波特率 X 单个调制状态对应二进制位数k* PS:在二进制码元情况下,比特率在数值上等于波特率。eg:一个码元携 n bit信息量,则 M Baud的码元传输速率 <=> M × n bit/sPS: 相数 <=> 进制数 非归零编码-P77曼彻斯特编码-P77均见图2-22,比较简单但是重要 脉冲编码调制(PCM)的过程-P78是模拟数据数字化的主要方法。 采样 量化 编码 香农定理的基本内容-P81PS:信息论里面也讲了 香农公式:$$C_{max} = W × log_2(1+S/N)$$ 信噪比$$(dB)= 10log_{10}(S/N)$$ W为信道带宽,所以要提高最大数据传输速率,就应该设法提高传输线路的带宽或提高所传信号的信噪比。 多路复用技术的分类及各自特点-P82 时分多路复用(TDM) 频分多路复用(FDM) 波分多路复用(WDM) 码分多路复用(码分多址CDMA)(与正交频分复用) ADSL的特点-P89 ADSL在电话线上同时提供电话与Internet接入服务 ADSL提供的非对称带宽特性 ADSL结构 HFC的特点-P91 HFC下行信道与上行信道频段划分有多种方案(既有带宽相同的对称结构又有非对称结构) 用户端的电视机与计算机分别接到线缆调制解调器(下行有线电视信道传输的电视节目,下行数据信道传输数据,上行数据信道传输的数据传输到头端) HFC的头端又被称为电缆调制解调器终端系统。 小区光纤结点将光纤干线和同轴电缆相互连接。 HFC对上行信道与下行信道的管理是不相同的。 移动通信的接入Internet的基本原理-P97移动通信系统由移动终端、接入网与核心交换网三部分组成。 工作原理示意图见图2-43]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计网考点1]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%2F%E8%80%83%E7%82%B9%2F%E8%AE%A1%E7%BD%91%E8%80%83%E7%82%B91%2F</url>
<content type="text"><![CDATA[本文列出老师指明的计算机网络(第一章 绪论)考点索引,简要概念及所在教材页数。教材:吴功宜,清华大学出版社,《计算机网络(第四版)》 内部资料,仅供参考,请君雅正。(另七章见 计算机网络考点目录下 其他博文) 绪论计算机网络发展的四个阶段-P1 计算机网络的形成与发展 互联网的形成与发展 移动互联网的形成与发展 物联网的形成与发展 ARPANET研究的主要问题-P2三网融合的概念-P10实质上是计算机网络、电信通信网与电视传输网技术的融合、业务的融合。 计算机网络发展的三条主线-P12 从 ARPANET 到 Internet 从无线分组网到无线自组网、无线传感器网络 网络安全技术 计算机网络的定义-P14计算机网络是 “以相互共享资源的方式互联起来的自治计算机系统的集合”。 按覆盖范围分类,计算机网络的类型-P17 广域网(WAN) 城域网(MAN) 局域网(LAN) 个人区域网(PAN) 人体区域网(BAN) ISP 和 ICP 的概念-P26 ISP (Internet service provider)Internet服务提供者 ICP (Internet content provider)Internet内容提供商 常见的计算机网络拓扑类型及其特点-P29 星状 环状 总线型 树状 网状 常见的数据交换方式及其特点-P30 基本分为两大类: 线路交换 与 存储转发交换 存储转发交换又分为两类: 报文(存储转发)交换 与 分组(存储转发)交换 分组交换分为两类: 数据报交换 与 虚电路交换 面向连接和无连接服务的比较-P41网络协议及其三要素-P42 为网络数据交换制定的通信规则、约定与标准称为“网络协议” 三要素: 语义(要做什么)、语法(要怎么做)、时序(做的顺序) OSI 七层模型的层次结构-P47物理层、数据链路层、网络层、传输层、会话层、表示层和应用层 TCP/IP 参考模型的层次结构-P51 应用层 传输层 互联网络层 主机-网络层 主要的网络协议标准化组织-P53 国际电信联盟(ITU) 国际标准化组织(ISO) 电子工业协会(EIA) 电气电子工程师(IEEE)]]></content>
<categories>
<category>计算机网络</category>
<category>考点</category>
</categories>
<tags>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Matlab常用工具箱]]></title>
<url>%2F%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1%2FMatlab%E5%B7%A5%E5%85%B7%E7%AE%B1%2FMatlab%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%E7%AE%B1%2F</url>
<content type="text"><![CDATA[工具箱我的版本是MATLB R2017a,主要介绍曲线拟合与优化工具箱 Curve fitting –> 曲线拟合曲线函数选项Exponential:指数逼近,有2种类型, aexp(bx) 、 aexp(bx) + cexp(dx)Fourier:傅立叶逼近,有7种类型,基础型是 a0 + a1cos(xw) + b1sin(xw)Gaussian:高斯逼近,有8种类型,基础型是 a1exp(c1-((x-b1)/c1)^2)Interpolant:插值逼近,有4种类型,linear、nearest neighbor、cubic spline、shape-preservingPolynomial:多形式逼近,有9种类型,linear ~、quadratic ~、cubic ~、4-9th degree ~Power:幂逼近,有2种类型,ax^b 、ax^b + cRational:有理数逼近,分子、分母共有的类型是linear ~、quadratic ~、cubic ~、4-5th degree ~;此外,分子还包括constant型Smoothing Spline:平滑逼近Sum of Sin Functions:正弦曲线逼近,有8种类型,基础型是 a1sin(b1x + c1)Weibull:只有一种,abx^(b-1)exp(-a*x^b) 拟合效果评价除了直观的观察图像和散点的拟合程度外,matlab还提供了几个评价参数 The sum of squares due to error (SSE) R-square Adjusted R-square Root mean squared error (RMSE) sse 这个统计量测量的是拟合值与实际值的总偏差和。它也被称为残差的平方求和 值越小,拟合程度越好。R-square由三个公式计算得来,衡量了拟合在解释数据变化方面的成功程度 越接近1,表示模型在方差中所占的比例更大(with a value closer to 1 indicating that a greater proportion of variance is accounted for by the model) Adjusted R-square调整后的R-平方统计量可以接受任何小于或等于1的值,而接近1的值表示更好的拟合。当模型包含无助于预测响应的项时,可能会出现负值。 RMSE由以下两个公式计算得出 与sse一样,RMSE值越小,拟合程度越好 Optimization –> 优化 利用Matlab的优化工具箱,可以求解线性规划、非线性规划和多目标规划问题。具体而言,包括线性、非线性最小化,最大最小化,二次规划,半无限问题,线性、非线性方程(组)的求解,线性、非线性的最小二乘问题。另外,该工具箱还提供了线性、非线性最小化,方程求解,曲线拟合,二次规划等问题中大型课题的求解方法,为优化方法在工程中的实际应用提供了更方便快捷的途径。 优化工具箱中的函数 优化工具箱中的函数包括下面几类: 1.最小化函数 表1 最小化函数表 函 数 描 述 fgoalattain 多目标达到问题 fminbnd 有边界的标量非线性最小化 fmincon 有约束的非线性最小化 fminimax 最大最小化 fminsearch, fminunc 无约束非线性最小化 fseminf 半无限问题 linprog 线性课题 quadprog 二次课题 2.方程求解函数 表2 方程求解函数表 函 数 描 述 / 线性方程求解 fsolve 非线性方程求解 fzero 标量非线性方程求解 3.最小二乘(曲线拟合)函数 表3 最小二乘函数表 函 数 描 述 / 线性最小二乘 lsqlin 有约束线性最小二乘 lsqcurvefit 非线性曲线拟合 lsqnonlin 非线性最小二乘 lsqnonneg 非负线性最小二乘 4.实用函数 表4 实用函数表 函 数 描 述 optimset 设置参数 optimget 5.大型方法的演示函数 表5 大型方法的演示函数表 函 数 描 述 circustent 马戏团帐篷问题—二次课题 molecule 用无约束非线性最小化进行分子组成求解 optdeblur 用有边界线性最小二乘法进行图形处理 6.中型方法的演示函数 表6 中型方法的演示函数表 函 数 描 述 bandemo 香蕉函数的最小化 dfildemo 过滤器设计的有限精度 goaldemo 目标达到举例 optdemo 演示过程菜单 tutdemo 教程演示 参数设置 利用optimset函数,可以创建和编辑参数结构;利用optimget函数,可以获得options优化参数。 ● optimget函数 功能:获得options优化参数。 语法: val = optimget(options,’param’) val = optimget(options,’param’,default) 描述: val = optimget(options,’param’) 返回优化参数options中指定的参数的值。只需要用参数开头的字母来定义参数就行了。 val = optimget(options,’param’,default) 若options结构参数中没有定义指定参数,则返回缺省值。注意,这种形式的函数主要用于其它优化函数。 举例: 1. 下面的命令行将显示优化参数options返回到my_options结构中: val = optimget(my_options,’Display’) 2. 下面的命令行返回显示优化参数options到my_options结构中(就象前面的例子一样),但如果显示参数没有定义,则返回值’final’: optnew = optimget(my_options,’Display’,’final’); 参见: optimset ● optimset函数 功能:创建或编辑优化选项参数结构。 语法: options = optimset(‘param1’,value1,’param2’,value2,…) optimset options = optimset options = optimset(optimfun) options = optimset(oldopts,’param1’,value1,…) options = optimset(oldopts,newopts) 描述: options = optimset(‘param1’,value1,’param2’,value2,…) 创建一个称为options的优化选项参数,其中指定的参数具有指定值。所有未指定的参数都设置为空矩阵[](将参数设置为[]表示当options传递给优化函数时给参数赋缺省值)。赋值时只要输入参数前面的字母就行了。 optimset函数没有输入输出变量时,将显示一张完整的带有有效值的参数列表。 options = optimset (with no input arguments) 创建一个选项结构options,其中所有的元素被设置为[]。 options = optimset(optimfun) 创建一个含有所有参数名和与优化函数optimfun相关的缺省值的选项结构options。 options = optimset(oldopts,’param1’,value1,…) 创建一个oldopts的拷贝,用指定的数值修改参数。 options = optimset(oldopts,newopts) 将已经存在的选项结构oldopts与新的选项结构newopts进行合并。newopts参数中的所有元素将覆盖oldopts参数中的所有对应元素。 举例: 1.下面的语句创建一个称为options的优化选项结构,其中显示参数设为’iter’,TolFun参数设置为1e-8: options = optimset(‘Display’,’iter’,’TolFun’,1e-8) 2.下面的语句创建一个称为options的优化结构的拷贝,改变TolX参数的值,将新值保存到optnew参数中: optnew = optimset(options,’TolX’,1e-4); 3.下面的语句返回options优化结构,其中包含所有的参数名和与fminbnd函数相关的缺省值: options = optimset(‘fminbnd’) 4.若只希望看到fminbnd函数的缺省值,只需要简单地键入下面的语句就行了: optimset fminbnd 或者输入下面的命令,其效果与上面的相同: optimset(‘fminbnd’) 参见: optimget 模型输入时需要注意的问题使用优化工具箱时,由于优化函数要求目标函数和约束条件满足一定的格式,所以需要用户在进行模型输入时注意以下几个问题: 1.目标函数最小化 优化函数fminbnd、fminsearch、fminunc、fmincon、fgoalattain、fminmax和lsqnonlin都要求目标函数最小化,如果优化问题要求目标函数最大化,可以通过使该目标函数的负值最小化即-f(x)最小化来实现。近似地,对于quadprog函数提供-H和-f,对于linprog函数提供-f。 2.约束非正 优化工具箱要求非线性不等式约束的形式为Ci(x)≤0,通过对不等式取负可以达到使大于零的约束形式变为小于零的不等式约束形式的目的,如Ci(x)≥0形式的约束等价于- Ci(x)≤0;Ci(x)≥b形式的约束等价于- Ci(x)+b≤0。 3.避免使用全局变量 @(函数句柄)函数 MATLAB6.0中可以用@函数进行函数调用。@函数返回指定MATLAB函数的句柄,其调用格式为: handle = @function 利用@函数进行函数调用有下面几点好处: ● 用句柄将一个函数传递给另一个函数; ● 减少定义函数的文件个数; ● 改进重复操作; ● 保证函数计算的可靠性。 下面的例子为humps函数创建一个函数句柄,并将它指定为fhandle变量。 fhandle = @humps; 同样传递句柄给另一个函数,也将传递所有变量。本例将刚刚创建的函数句柄传递给fminbnd函数,然后在区间[0.3,1]上进行最小化。 x = fminbnd (@humps, 0.3, 1) x = 0.6370]]></content>
<categories>
<category>数学建模</category>
<category>Matlab工具箱</category>
</categories>
<tags>
<tag>数学建模</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Matlab工具箱简介]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%2FMatlab%2FMatlab%E5%B7%A5%E5%85%B7%E7%AE%B1%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[Toolbox工具箱数学、统计与优化1 Symbolic Math Toolbox 符号数学工具箱 Symbolic Math Toolbox™ 提供用于求解和推演符号运算表达式以及执行可变精度算术的函数。您可以通过分析执行微分、积分、化简、转换以及方程求解。另外,还可以利用符号运算表达式为 MATLAB®、Simulink® 和 Simscape™ 生成代码。 Symbolic Math Toolbox 包含 MuPAD® 语言,并已针对符号运算表达式的处理和执行进行优化。该工具箱备有 MuPAD 函数库,其中包括普通数学领域的微积分和线性代数,以及专业领域的数论和组合论。此外,还可以使用 MuPAD 语言编写自定义的符号函数和符号库。MuPAD 记事本支持使用嵌入式文本、图形和数学排版格式来记录符号运算推导。您可以采用 HTML 或 PDF 的格式分享带注释的推导。 2 Partial Differential Euqation Toolbox 偏微分方程工具箱 偏微分方程工具箱™提供了用于在2D,3D求解偏微分方程(PDE)以及一次使用有限元分析。它可以让你指定和网格二维和三维几何形状和制定边界条件和公式。你能解决静态,时域,频域和特征值问题在几何领域。功能进行后处理和绘图效果使您能够直观地探索解决方案。 你可以用偏微分方程工具箱,以解决从标准问题,如扩散,传热学,结构力学,静电,静磁学,和AC电源电磁学,以及自定义,偏微分方程的耦合系统偏微分方程。 3 Statistics Toolbox 统计学工具箱 Statistics and Machine Learning Toolbox 提供运用统计与机器学习来描述、分析数据和对数据建模的函数和应用程序。您可以使用用于探查数据分析的描述性统计和绘图,使用概率分布拟合数据,生成用于 Monte Carlo 仿真的随机数,以及执行假设检验。回归和分类算法用于依据数据执行推理并构建预测模型。 对于分析多维数据,Statistics and Machine Learning Toolbox 可让您通过序列特征选择、逐步回归、主成份分析、规则化和其他降维方法确定影响您的模型的主要变量或特征。该工具箱提供了受监督和不受监督机器学习算法,包括支持向量机 (SVM)、促进式 (boosted) 和袋装 (bagged) 决策树、k-最近邻、k-均值、k-中心点、分层聚类、高斯混合模型和隐马尔可夫模型。 4 Curve Fitting Toolbox 曲线拟合工具箱 Curve Fitting Toolbox™ 提供了用于拟合曲线和曲面数据的应用程序和函数。使用该工具箱可以执行探索性数据分析,预处理和后处理数据,比较候选模型,删除偏值。您可以使用随带的线性和非线性模型库进行回归分析,也可以指定您自行定义的方程式。该库提供了优化的解算参数和起始条件,以提高拟合质量。该工具箱还提供非参数建模方法,比如样条、插值和平滑。 在创建一个拟合之后,您可以运用多种后处理方法进行绘图、插值和外推,估计置信区间,计算积分和导数。 5 Optimization Toolbox 优化工具箱 Optimization Toolbox™ 提供了寻找最小化或最大化目标并同时满足限制条件的函数。工具箱中包括了线性规划、混合整型线性规划、二次规划、非线性优化、非线性最小二乘的求解器。您可以使用这些求解器寻找连续与离散优化问题的解决方案、执行折衷分析、以及将优化的方法结合到其算法和应用程序中。 6 Global Optimization Toolbox 全局优化工具箱 Global Optimization Toolbox 所提供的方法可为包含多个极大值或极小值的问题搜索全局解。它包含全局搜索、多初始点、模式搜索、遗传算法和模拟退火求解器。对于目标函数或约束函数连续、不连续、随机、导数不存在以及包含未确定参数的仿真模型或黑箱函数的优化问题,都可使用这些求解器来求解。 遗传算法和模式搜索求解器都支持算法定制。你可以修改初始种群和适应度尺度变换选项、定义亲本选配、交叉和变异函数,创建自定义的遗传算法。还可通过定义轮询、搜索和其它函数来自定义模式搜索。 7 Neural Network Toolbox 神经网络工具箱 神经网络工具箱™提供的功能和应用服务建模是不容易建模与封闭形式方程复杂的非线性系统。神经网络工具箱支持监督学习与前馈,径向基和动态网络。它也支持无监督学习与自组织地图和有竞争力的层。有了你可以设计,培训,可视化和模拟神经网络工具箱。可以使用神经网络工具箱等应用数据拟合,模式识别,聚类,时间序列预测,和动态系统建模和控制。 要加快培养和处理大型数据集,您可以在多核处理器,GPU和计算机集群使用并行计算工具箱™分发计算和数据。 8 Model-Based Calibration Toolbox 基于模型矫正工具箱 基于模型矫正工具箱™提供的应用程序和设计工具优化校准复杂的发动机和动力总成子系统。您可以定义最佳的测试计划,自动适应的统计模型,并生成校准和查找表的高自由度复杂的引擎,将使用传统的方法,否则需要详尽的测试。校准可以在各个工作点或以上驱动周期进行优化,以确定发动机的燃油经济性,性能和排放的最佳平衡。使用的应用程序或MATLAB®功能,可以自动校准过程相似类型的发动机。 与基于模型的标定工具箱创建的模型可以导出到Simulink®的支持控制设计,硬件在环测试,和整个动力总成设计团队的动力系统模拟活动。校准表可以导出到ETAS INCA和ATI愿景。 信号处理与通信9 Signal Processing Toolbox 信号处理工具箱 Signal Processing Toolbox™ 提供了用来生成、测量、变换、过滤和可视化信号的函数和应用程序。该工具箱包括用于重新采样、平滑和同步信号、设计和分析滤波器、估算功率谱以及测量峰值、带宽和失真的算法。该工具箱还包括参数化和线性预测建模算法。您可以使用 Signal Processing Toolbox 分析和比较时域、频域和时频域中的信号,识别规律和趋势,提取特征,开发和验证自定义算法,从而洞察您的数据。 10 DSP System Toolbox DSP系统工具箱 DSP System Toolbox 提供用于 MATLAB® 和 Simulink® 中流信号处理的算法、滤波器、设计工具和应用程序。这些功能以 MATLAB 函数、MATLAB 系统对象和 Simulink 模块的形式提供。您可以为音频、通信、医疗以及其他实时信号处理和物联网 (IoT) 应用场合创建并测试系统。 使用 DSP System Toolbox 可以设计和分析 FIR、IIR、多速率、多级和自适应滤波器。您可以从音频设备、文件和网络传输信号流来支持系统开发和验证。示波器、频谱分析仪和逻辑分析仪工具可用于对流信号进行动态可视化和测量。对于桌面原型建立并部署至嵌入式处理器(包括ARM® Cortex®),该系统工具箱支持 C/C++ 代码生成和定点建模,还支持为 FFT 和 IFFT 等算法生成 HDL 代码。 11 Communications System Toolbox 通信系统工具箱 Communications System Toolbox 提供用于在 MATLAB® 与 Simulink® 中对通信系统进行分析、设计、端到端仿真和验证的算法和应用程序。工具箱算法(包括信道编码、调制、MIMO 和 OFDM)使您可以组建系统的物理层模型。您可以仿真模型以测量性能。 该系统工具箱提供星座图和眼图、误码率以及其他分析工具和示波器以验证设计。这些工具可用于分析信号,实现信道特征可视化和获取误差矢量幅度 (EVM) 等性能指标。信道和 RF 损伤模型和补偿算法(包括载波和符号定时同步器)使您可以对链路级设计规范进行真实建模并补偿信道衰落效应。 通过使用 Communications System Toolbox 硬件支持包,您可以将发射机和接收机模型连接到外部无线电设备并使用无线测试验证设计。该系统工具箱支持定点运算和 C 或 HDL 代码生成。 算法可作为 MATLAB 函数、系统对象和 Simulink 模块。 12 Wavelet Toolbox 小波工具箱 小波工具箱™提供的功能和开发基于小波变换的算法进行分析,综合,去噪和压缩的信号和图像的应用程序。 该工具箱让您探索小波特性和应用,如语音和音频处理,图像与视频处理,生物医学成像和1-D以及通信和地球物理学2-D应用。 13 Fixed-Point Toolbox 定点运算工具箱 Fixed-Point Designer 提供了使用 MATLAB® 代码、Simulink® 模型和 Stateflow® 图开发定点算法的数据类型与工具。它会自动建议定点数据类型和字长等属性。您还可以手动指定取整模式和溢出操作等属性。您可以执行加速的比特级一致性仿真来观察有限范围与精度的影响。 Fixed-Point Designer 使您能够将浮点算法转换为定点算法。您可以创建符合您的数值精度要求和目标硬件约束的定点数据类型并进行优化。您可以仿真您的设计并分析结果以获得取值范围信息。Fixed-Point Designer 使用此信息来建议适用于您的定点算法的字长和定标,并让您能够将定点结果与浮点基线进行比较。 Fixed-Point Designer 支持 C、HDL 和 PLC 代码生成。 14 RF Toolbox 射频工具箱 射频工具箱™提供功能和用于设计,建模,分析和可视化射频(RF)元件的网络的应用程序。您可以使用射频工具箱软件工作在无线通信,雷达和信号完整性的项目。 15 Phased Array System Toolbox 相控阵系统工具箱 相控阵系统工具箱™提供的算法和应用的传感器阵列系统的设计,仿真和分析,雷达,声纳,无线通信,医学成像应用。该系统工具箱包括脉冲和连续波形和波束形成的信号处理算法,匹配滤波,到货即损(DOA)估计的方向和目标探测。它还包括发射机和接收机,传播,目标,干扰器,和杂波模型。 该系统工具箱可以让你模拟地面,空中,或舰载多功能雷达系统的动力学与移动目标和平台。可以设计端至端的相控阵系统和分析下使用合成或获得的数据不同的情况的性能。该工具箱的应用程序,让你探索传感器阵列和波形的特点,进行链路预算分析。在副产物实施例提供了一个起点实现自定义相控阵系统。 工具箱算法可作为MATLAB®系统对象™和Simulink®的块。 控制系统设计与分析16 Control system Toolbox 控制系统工具箱 Control System Toolbox™ 为系统地分析、设计和调节线性控制系统提供行业标准算法和应用程序。您可以将您的系统指定为传递函数、状态空间、零极点增益 或频率响应 模型。通过应用程序和函数(如阶跃响应图和波特图),您可以实现时域和频域中系统行为的可视化效果。可以使用自动 PID 控制器调节、波特回路整形、根轨迹方法、LQR/LQG 设计及其他交互式和自动化方法来调节补偿器参数。您可以通过校验上 升时间、超调量、稳定时间、增益和相位裕度及其他要求来验证您的设计。 17 System Indentification Toolbox 系统辨识工具箱 18 Fuzzy Logic Toolbox 模糊逻辑工具箱 19 Robust Control Toolbox 鲁棒控制工具箱 20 Model Predictive Control Toolbox模型预测控制工具箱 21 Aerospace Toolbox 航空航天工具箱 图像处理与计算机视觉22 Image Processing Toolbox 图像处理工具箱 Image Processing Toolbox™ 提供了一套全方位的参照标准算法、函数和应用程序,用于进行图像处理、分析、可视化和算法开发。您可进行图像分析、图像分割、图像增强、 降噪、几何变换和图像配准。工具箱中许多函数均支持多核处理器、GPU 和 C 代码生成。 Image Processing Toolbox 支持一组多样化的图像类型,包括高动态范围、千兆像素 分辨率、嵌入式 ICC 剖面图和层析成像。可视化函数和应用程序可用于探查图像与视频、检查像素区域、调节颜色与对比度、创建轮廓或柱状图以及操作感兴趣区域 (ROI)。工具箱支持用于处理、显示和浏览大图像的工作流程。 23 Computer Vision System Toolbox 计算机视觉工具箱 计算机视觉系统工具箱™提供的算法,功能和应用程序的设计和模拟计算机视觉和视频处理系统。您可以执行特征检测,提取和匹配;物体检测和跟踪;运动估计;和视频处理。对于3-D计算机视觉系统工具箱支持摄像机标定,立体视觉,三维重建和三维点云处理。随着基于机器学习的框架,你可以训练目标检测,物体识别和图像检索系统。算法可作为MATLAB®功能,系统对象™和Simulink®的块。 快速原型和嵌入式系统设计,系统工具箱支持定点运算和C代码生成。 24 Image Acquisition Toolbox 图像采集工具箱 图像采集工具箱™技术可以采集从相机和图像采集卡的图像和视频直接进入MATLAB®和Simulink®的。可以自动检测硬件和配置硬件属性。先进的工作流程,让你触发采集,同时处理的非循环,执行后台采集,并在多个多设备同步采样。随着多个硬件厂商和行业标准的支持,可以使用成像设备,从便宜的网络摄像头,以满足低光,高速和其它具有挑战性的需求的高端的科学和工业设备。 25 Mapping Toolbox 地图工具箱 地图工具箱™提供算法,功能,和用于分析的地理数据,并在MATLAB®创建地图显示的应用程序。您可以从多种文件格式和网络地图服务器导入矢量和栅格数据。该工具箱,您可以子集,并使用自定义修剪,插值,重采样数据,坐标变换和其他技术。地理空间数据可以从一个单一的地图显示多个来源的基础地图图层合并。您可以在文件格式,如shape文件,GeoTIFF文件和KML导出数据。通过将映射函数到MATLAB程序,可以自动在你的地理信息工作流程频繁的任务。 测试与测量26 Data Acquisition Toolbox 数据采集工具箱 数据采集工具箱™提供了用于连接MATLAB®数据采集硬件。该工具箱支持多种数据采集硬件,包括USB,PCI,PCI-EXPRESS®,PXI和PXI-Express设备,从美国国家仪器,测量计算,研华,数据转换,和其他供应商。 随着工具箱,您可以配置数据采集硬件和数据读入MATLAB和Simulink®中进行实时分析。您还可以发送出的数据通过模拟和数据采集硬件提供的数字输出通道。该工具箱的数据采集软件包括用于控制模拟输入,模拟输出,计数器/定时器和DAQ设备的数字I / O子系统的功能。您可以访问特定于设备的功能和同步多个设备采集的数据。 当你得到它或将它保存为后期处理,你可以分析数据。您还可以自动检测并进行迭代更新,根据分析结果测试设置。包括在工具箱中的Simulink模块让你直接传输实时数据到Simulink模型,使您能够验证和确认您的模型对现场测得的数据为您的设计验证过程的一部分。 27 Instrument Control Toolbox 仪表控制工具箱 28 Image Acquisition Toolbox 图像采集工具箱 图像采集工具箱™技术可以采集从相机和图像采集卡的图像和视频直接进入MATLAB®和Simulink®的。可以自动检测硬件和配置硬件属性。先进的工作流程,让你触发采集,同时处理的非循环,执行后台采集,并在多个多设备同步采样。随着多个硬件厂商和行业标准的支持,可以使用成像设备,从便宜的网络摄像头,以满足低光,高速和其它具有挑战性的需求的高端的科学和工业设备。 29 OPC Toolbox OPC开发工具 30 Vehicle Network Toolbox 车载网络工具箱 计算金融31 Financial Toolbox 金融工具箱 32 Econometrics Toolbox 计算经济学工具箱 33 Datafeed Toolbox 数据输入工具箱 34 Fixed-Income Toolbox 固定收益工具箱 35 Financial Derivatives Toolbox 衍生金融工具箱 计算生物33 Bioinformatics Toolbox 生物信息工具箱 34 SimBiology 生物学工具箱 并行计算35 Parallel Computing Toolbox 并行计算工具箱 36 MATLAB Distributed Computing Server MATLAB分布式计算服务器 数据库访问与报告37 Database Toolbox 数据库工具箱 数据库工具箱™提供了一个应用程序和功能的交换关系数据库和MATLAB®之间的数据。您可以使用SQL命令来读取和写入数据,或使用数据库资源管理器应用程序与数据库进行交互,而无需使用SQL。 该工具箱支持ODBC兼容和JDBC兼容数据库,包括的Oracle®,SAS®,MySQL®,访问Sybase®,Microsoft®SQLServer®上,微软Access®的,和PostgreSQL®。您可以应用简单和高级的条件,从MATLAB数据库查询。该工具箱允许您访问在一个单一的MATLAB进程同时进行多个数据库,能够实现大型数据集分段导入。 38 MATLAB Report Generator MATLAB报告生成 MATLAB代码生成39 MATLAB Coder MATLAB代码生成 MATLAB Coder™ 可从 MATLAB® 代码生成可读且可移植的 C 和 C++ 代码。它支持大多数 MATLAB 语言和广泛的工具箱。您可以将生成的代码作为源代码、静态库或动态库集成到您的项目。还可以在 MATLAB 环境中使用生成的代码,加快 MATLAB 代码的计算量密集部分的速度。使用 MATLAB Coder,您可以将现有 C 代码合并到 MATLAB 算法和生成的代码中。 通过联合使用 MATLAB Coder 和 Embedded Coder®,您可以进一步优化代码的效率和自定义生成的代码。然后可以使用软件在环 (SIL) 和处理器在环 (PIL) 执行程序验证生成代码的数字行为。 40 Filter Design HDL Coder 滤波器设计HDL代码生成 滤波器设计HDL编码器™产品增加了硬件实现能力MATLAB®。它可以让你产生有效的,综合的,而便携VHDL和Verilog代码,其设计与DSP系统工具箱™软件,用于ASIC或FPGA实现定点滤波器。它还可以自动快速仿真,测试和验证所生成的代码创建VHDL和Verilog测试平台。 MATLAB应用发布41 MATLAB Compiler MATLAB编译器 混合编程 42 MATLAB Builder NE for Microsoft.Net Framework 43 MATLAB Builder JA for Java Language 44 MATLAB Builder EX for Microsoft Excel 45 Spreadsheet Link EX for Microsoft Excel Simulink模块信号处理与通信1 DSP System Toolbox DSP系统工具箱 2 Communications System Toolbox 通信系统工具箱 3 Computer Vision System Toolbox 计算机视觉工具箱 4 SimRF RF 模块集功能 控制系统设计与分析5 Simulink Control Design Simulink 控制器设计 6 Simulink Design Optimization Simulink 设计优化 7 Aerospace Blockset 航空航天模块 物理建模8 Simscape 物理模型仿真模块组 9 SimMechanics 机构动态仿真模块组 10 SimDriveline 传动系统系统仿真模块组 11 SimHydraulics 液压仿真模块组 12 SimRF RF仿真模块组 13 SimElectronics 电子仿真模块组 14 SimPowerSystems 动力系统仿真模块组 基于事件的建模15 Stateflow 16 SimEvents 快速原型和硬件再回路仿真17 xPC Target 18 xPC Target Embedded Option 19 Real-Time Windows Target 仿真绘图与报告20 Simulink 3D Animation 21 Gauges Blockset 22 Simulink Report Generator 验证、确认和测试23 Simulink Verfication and Validation 24 Simulink Design Verifier 25 System Test 26 EDA Simulator Link 27 Simulink Code Inspector 定点建模28 Simulink Fixed Point 代码生成29 Simulink Coder 30 Embedded Coder 31 Simulink HDL Coder 32 Simulink PLC Coder 33 Do Qualification Kit for DO-178 34 IEC Certification Kit for ISO 26262 and IEC 61508]]></content>
<categories>
<category>计算机</category>
<category>Matlab</category>
</categories>
<tags>
<tag>数学建模,计算机</tag>
</tags>
</entry>
<entry>
<title><![CDATA[键盘快捷键2]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%2F%E5%BF%AB%E6%8D%B7%E9%94%AE%2F%E9%94%AE%E7%9B%98%E5%BF%AB%E6%8D%B7%E9%94%AE2%2F</url>
<content type="text"><![CDATA[计算机软件内的一些常用快捷键(按使用场景分类) Case 1、跨软件多窗口高效工作:Ctlr+← 实现两个窗口对比(跨软件文字对比、校验时非常有用,大家可以试试)Ctrl+↓ 缩小窗口/向下还原窗口 Case 2、单窗口工作切换/老板来查岗/保护隐私:Alt + Tab 快速切换多窗口alt + space+n 最小化窗口Win+D 显示桌面Win+M(最小化所有窗口)Win+L 锁定电脑 Case 3、代替鼠标,减少操作时间:Win+E 打开文件管理器/我的电脑Alt + F4 关闭窗口Ctrl+ Home 当前窗口或文档的最上端。Ctrl+ End 当前窗口或文档的最下端。Pageup 向上翻页键Pagedown 向下翻页键 Case4、好用的系统操作设置快捷键:Win+X:系统设置菜单(功能多强大自己试试看)Win+Q/S 调用Cortana搜索应用、文档、网页(你还在桌面上找软件打开吗?/你还愁找不到最近使用的文档吗/你能不能通过搜索词一步到位搜索到相应的网页)Win+T 任务栏上固定的应用之间相互切换。Win键+1/2/…N:依据顺序打开任务栏中固定的应用。Win+Tab 显示你打开的所有窗口,你要哪个点哪个。Ctrl + Shift + n 创建新文件夹F2 文件夹重命名Ctrl + Shift +Esc 打开任务管理器。(电脑不听话了) Case5、你想要几个工作环境吗?一个工作、一个游戏、一个看片?自己安排,那也许你会用到下面的快捷键:Win键+Ctrl+D:创建一个新的虚拟桌面Win键+Ctrl+F4:关闭虚拟桌面Win键+Ctrl+左/右:切换虚拟桌面 Case6、通用编辑快捷键:Ctrl+ A 全选。Ctrl+ C 复制Ctrl+ X 剪切Ctrl+ V 粘贴Ctrl+ Z 撤消Ctrl+Y恢复撤销内容Ctrl+ F 寻找Ctrl+ S 保存 Case7、浏览网页好用快捷键:F5 刷新Ctlr+W 关闭当前网页选项卡Ctrl+shift+T 恢复误关网页Ctrl+/- 网页放大缩小 其他快捷键(了解一下):Win+W 打开windows10新增windows ink工作区:便签、草图板、屏幕草图、最近使用的应用等Win+R 调用“运行”对话框Win+I 调用“windows设置”窗口Win+O 启用和关闭Win+U 调用“辅助工具管理器”Win+P 投影屏幕Win+A 调用操作中心Win+F 调用反馈中心Win+G 游戏录制工具栏,有时可以用作屏幕录制Win+H 共享Win+K 外置显示器和音频设备管理Win+B 显示隐藏的图标Win+N 调用便签]]></content>
<categories>
<category>计算机</category>
<category>快捷键</category>
</categories>
<tags>
<tag>计算机</tag>
</tags>
</entry>
<entry>
<title><![CDATA[word快捷键]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%2F%E5%BF%AB%E6%8D%B7%E9%94%AE%2Fword%E5%BF%AB%E6%8D%B7%E9%94%AE%2F</url>
<content type="text"><![CDATA[WORD全套快捷键小技巧 1、CTRL O 打开 2、CTRL P 打印 3、CTRL A 全选 4、CTRL [/] 对文字进行大小设置(在选中目标情况下) 5、CTRL D 字体设置(在选中目标情况下) 6、CTRL G/H 查找/替换; 7、CTRL N 全文删除; 8、CTRL M 左边距(在选中目标情况下); 9、CTRL U 绘制下划线(在选中目标情况下); 10、CTRL B 加粗文字(在选中目标情况下); 11、CTRL I 倾斜文字(在选中目标情况下); 12、CTRL Q 两边对齐(无首行缩进),(在选中目标情况下)或将光标放置目标文 字的段尾,亦可操作 13、CTRL J 两端对齐(操作同上) 14、CTRL E 居中(操作同上) 15、 CTRL R 右对齐(操作同上) 16、 CTRL K 插入超链接 17、CTRL T/Y 可进行首行缩进(将光标移到需做此操作的段尾,或将此段选中进行操作 18、Ctrl A(或Ctrl 小键盘上数字5):选中全文。 19、Ctrl B:给选中的文字加粗(再按一次,取消加粗)。 20、Ctrl C:将选中的文字复制到剪 贴板中。 21、Ctrl D:打开“字体”对话框,快速完成字体的各种设置。 22、Ctrl E:使光标所在行的文本居中。 23、Ctrl F:打开“查找与替换”对话框,并定位在“查找”标签上。 24、Ctrl G:打开“查找与替换”对话框,并定位在“定位”标签上。 25、Ctrl H:打开“查找与替换”对话框,并定位在“替换”标签上。 26、Ctrl I:使选中的文字倾斜(再按一次,取消倾斜)。 27、Ctrl K:打开“插入超链接”对话框。 28、Ctrl Shift L:给光标所在行的文本加上“项目符号”。 29、Ctrl M:同时增加首行和悬挂缩进。 30、Ctrl Shift M:同时减少首行和悬挂缩进。 31、Ctrl N:新建一个空文档。 32、 Ctrl O(或Ctrl F12):打开“打开”对话框。 33、Ctrl P(或Ctrl Shift F12):打开“打印”对话框。 34、Ctrl R:使光标所在行的文本右对齐。 35、Ctrl S:为新文档打开“另保存为”对话框,或对当前文档进行保存。 36、Ctrl T:增加首行缩进。 37、Ctrl Shift T:减少首行缩进。 38、Ctrl U:给选中的文字加上下划线(再按一次,去年下划线)。 39、Ctrl V:将剪贴板中的文本或图片粘贴到光标处。若剪贴板中有多个内容,则将最后一条内容粘贴到光标处。 40、 Ctrl X:将选中的文字剪切到剪贴板中。 41、Ctrl Z:撤销刚才进行的操作(可以多次使用)。 42、 Ctrl 0:将选中的文本每段前增加12磅的间距。 43、Ctrl 1:若选中的文本行距不是“单倍行距”,则将其快速设置为 “单倍行距”。 44、Ctrl 2: 将选中的文本行距设置“两倍行距”。 45、Ctrl 5:将选中的文本行距设置为“1.5倍行距”。 46、Ctrl F2:快速执行“打印预览”功能。 47、Ctrl F4:关闭当前文档。 48、Ctrl F5:使窗口还原到最大化之前的状态。 49、Ctrl Shift F5:打开“书签”对话框。 50、Ctrl Shift F8:激活列选择功能,即通常所说的选择竖块文本(再按一次或按ESC键,取消该功能)。 51、 Ctrl F9:在光标处插入一域记号“{}”(注意:直接输入的一对大括号不能作为域记号)。 52、Ctrl F5:使窗口还原到最大化之前的状态(再按一次,就会使窗口再次最大化)。 53、Ctrl Deltet:删除光标后面的一个英文单词或一个中文词语(可反复使用)。 54、Ctrl Enter:将光标后面的内容 快速移到下一页。 55、Ctrl End:快速将光标移到文末。 56、Ctrl Home(或Ctrl Page Up):快速将光标移到文首。 57、Ctrl Insert Insert(即按两下Insert键):快速打开或更改“任务窗格”到“剪贴板”状态。58、Ctrl ~:打开中文输入法的“在线造词”功能。59、Ctrl :快速切换到下标输入状态(再按一次恢复到正常状态)。60、Ctrl Shift :快速切换到上标输入状态(再按一次恢复到正常状态)。61、Ctrl →:按英文单词或中文词语的间隔向后移动光标。62、Ctrl ←:按英文单词或中文词语的间隔向前移动光标。63、Ctrl J:文本两端对齐64、Ctrl L:文本左对齐65、Ctrl Q:段落重置66、Ctrl W:关闭当前文档67、Ctrl Y:重复上一操作(部分操作不适用-68、winkey d : “老板键”可以将桌面上的所有窗口瞬间最小化,再次按下恢复桌面。69、winkey f : 不用再去移动鼠标点“开始→搜索→文件和文件夹”了,在任何状态下,只要一按winkeyf就会弹出搜索窗口。]]></content>
<categories>
<category>计算机</category>
<category>快捷键</category>
</categories>
<tags>
<tag>计算机</tag>
</tags>
</entry>
<entry>
<title><![CDATA[键盘快捷键]]></title>
<url>%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%2F%E5%BF%AB%E6%8D%B7%E9%94%AE%2F%E9%94%AE%E7%9B%98%E5%BF%AB%E6%8D%B7%E9%94%AE%2F</url>
<content type="text"><![CDATA[计算机软件内的一些常用快捷键(按按键分类) Ctrl键功能 1、Ctrl S 保存 2、Ctrl W 关闭程序 3、Ctrl N 新建 4、Ctrl O 打开 5、Ctrl Z 撤销 6、Ctrl F 查找 7、Ctrl X 剪切 8、Ctrl C 复制 9、Ctrl V 粘贴 10、Ctrl A 全选 11、Ctrl [ 缩小文字 12、Ctrl ] 放大文字 13、Ctrl B 粗体 14、Ctrl I 斜体 15、Ctrl U 下划线 16、Ctrl Shift 输入法切换 17、Ctrl 空格 中英文切换 18、Ctrl 回车 QQ号中发送信息 19、Ctrl Home 光标快速移到文件头 20、Ctrl End 光标快速移到文件尾 21、Ctrl Esc 显示开始菜单 22、Ctrl Shift < 快速缩小文字 23、Ctrl Shift > 快速放大文字 24、Ctrl F5 在IE中强行刷新 25、Ctrl 拖动文件 复制文件 26、Ctrl Backspace 启动\关闭输入法拖动文件时按住Ctrl Shift 创建快捷方式 Alt功能 1、Alt 空格 C 关闭窗口 2、Alt 空格 N 最小化当前窗口 3、Alt 空格 R 恢复最小化窗口 4、Alt 空格 X 最大化当前窗口 5、Alt 空格 M 移动窗口 6、Alt 空格 S 改变窗口大小 7、Alt Tab 两个程序交换 8、Alt 255 QQ号中输入无名人 9、Alt F 打开文件菜单 10、Alt V 打开视图菜单 11、Alt E 打开编辑菜单 12、Alt I 打开插入菜单 13、Alt O 打开格式菜单 14、Alt T 打开工具菜单 15、Alt A 打开表格菜单 16、Alt W 打开窗口菜单 17、Alt H 打开帮助菜单 18、Alt 回车 查看文件属性 19、Alt 双击文件 查看文件属性 20、Alt X 关闭C语言 Shift快捷键 1、 Shift 空格 半\全角切换 2、Shift Delete 永久删除所选项,而不将它放到“回收站”中。 3、拖动某一项时按 CTRL 复制所选项。 4、拖动某一项时按 CTRL SHIFT创建所选项目的快捷键。]]></content>
<categories>
<category>计算机</category>
<category>快捷键</category>
</categories>
<tags>
<tag>计算机</tag>
</tags>
</entry>
<entry>
<title><![CDATA[LaTeX公式编辑]]></title>
<url>%2F%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1%2FLaTeX%2FLaTeX%E5%85%AC%E5%BC%8F%E7%BC%96%E8%BE%91%2F</url>
<content type="text"><![CDATA[$\LaTeX$学习数学公式编辑$\LaTeX$ 数学模式 行内数学公式-inline 使用 美元符号引出\$ … $ 使用命令 \( 和 \) 使用环境\begin{math}…\end{math} 行间数学公式-display 使用 两个美元符号引出\$\$ … \$$ 使用命令 \[和 \] 使用环境\begin{displaymath}…\end{displaymath} 带自动编号的数学公式 单行\begin{equation}…\end{equation} 多行\begin{gather}…\end{gather} 分行multiline 组合cases(带编号numcases) ps:\text{},*,align环境居中&,&,\\ 数学结构 上下标 普通的用^和_ 多数数学算子中的上下标行间出现在正上、下方,行内不变 eg: $$\sum_{i=0}^n A_i$$ 上下标互不影响 上下划线与花括号 \overline和\underline 带箭头的\overleftarrow和单个字母的向量\vec eg: $\overleftarrow{a+b}$,$\vec a$ 分式 \frac和/的区别(\dfrac和\tfrac) $\frac{a}{b}$,$a/b$ 根式 \sqrt[] 矩阵 使用矩阵环境]]></content>
<categories>
<category>数学建模</category>
<category>LaTeX</category>
</categories>
<tags>
<tag>数学建模</tag>
</tags>
</entry>
<entry>
<title><![CDATA[文档编写笔记]]></title>
<url>%2F%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1%2F%E6%96%87%E6%A1%A3%E7%BC%96%E5%86%99%2F%E6%96%87%E6%A1%A3%E7%BC%96%E5%86%99%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[注意事项 论文任何地方都不可以出现中文; 标点符号后要有一个空格; 论文内部除解题部分以外都要细致认真的写。论文可以没有详细的解题步骤,但一定要有解决方案的构想和结果; 要详细讨论模型的设计方案及模型可能遇到的情况; 要有误差分析、稳定性分析; 不知道模型缺点的就不写,乱写一通说明对题目理解不深刻; 能搜出很多参考文献的关键词很有可能并非出题者所想(不要走题); 有时候数据出现一些问题不一定是数据不够,可能是数据精度不够或者噪声较多;(尽量不要写数据量不大作为缺点); 摘要不要引用文献; 论文内公式要与文字高度相同 ; 表格要尽量美观(显眼); 要么使用三线表格(如下)要么使用中规中矩的表格 表格要有标题,数居中放置; 符号说明一般使用三线表格文字左对齐; 图; 要有图例; 放在一起做对比的两张图颜色、大小、刻度要保持一致; 不可以截图,会被判定为作弊。]]></content>
<categories>
<category>数学建模</category>
<category>文档编写</category>
</categories>
<tags>
<tag>数学建模</tag>
</tags>
</entry>
<entry>
<title><![CDATA[建模算法分类]]></title>
<url>%2F%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1%2F%E7%AE%97%E6%B3%95%2F%E5%BB%BA%E6%A8%A1%E7%AE%97%E6%B3%95%E5%88%86%E7%B1%BB%2F</url>
<content type="text"><![CDATA[算法分类美赛培训第一天 下午 关于数学建模中算法的简单分类 一、预测1. 灰色预测样本点6-15个 数据呈指数或曲线形式 2. 微分方程预测无法找到原始数据的关系,但可以找到原始数据变化速度的关系,通过公式推导转化为原始数据的关系 3. 回归分析预测求一个因变量与若干自变量之间的关系,若自变量变化因变量如何变化自变量之间协方差比较小,最好趋近于0,自变量相关性小;样本点个数n>3k+1,k为自变量个数因变量要符合正态分布 4. 马尔可夫预测一个序列之间没有信息的传递,前后没有联系,数据之间随机性强,相互不影响 (每天的温度没有直接联系,预测温度高低的概率)只能得到概率 5. 时间序列预测与马尔可夫预测互补,至少两个点需要信息传递,ARMA模型、周期模型、季节模型等 6. 小波分析海量数据 无规律 将波分离,分离处出周期数据、规律性数据 可以做出时间序列做不出的数据,应用范围较广(高大上) 7. 神经网络预测大量数据 不需模型 只需输入输出 黑箱处理(检验、对照分析) 二、评价与决策1. 模糊综合评价评价一个对象 2. 主成分分析评价多个对象的水平并排序,指标见关联性很强 3. 数据包络分析优化问题,对各省发展状况进行评判 4. 秩和比综合评价同主成分分析,但指标间关联性不强 5. TOPSIS6. 投影寻踪综合评价法柔和多种算法(遗传算法、最优化理论等) 三、分析1. 方差分析看几类数据之间有无差异,差异性影响 2. 协方差分析有几个因素,只考虑一个因素对问题的影响,忽略其他因素 四、分类与判别1. 距离聚类(系统距离) 2. 关联性聚类(Q聚类 R聚类) 3. 层次聚类4. 密度聚类5. 其他聚类6. 贝叶斯判别(统计判别方法) 7. 费舍尔判别(训练样本较多) 8. 模糊识别(分好类的数据点较少) 五、关联与因果六、优化与控制1. 线性规划、整数规划、0-1规划(有约束、确定的目标) 2. 非线性规划与智能优化算法3. 多目标规划和目标规划(柔性约束、目标含糊、超过) 七、动态规划1. 网络优化(多因素交错复杂) 2. 排队论与计算机仿真3. 模糊规划(范围约束) 4. 灰色规划(难)]]></content>
<categories>
<category>数学建模</category>
<category>算法</category>
</categories>
<tags>
<tag>数学建模</tag>
</tags>
</entry>
<entry>
<title><![CDATA[美赛事项]]></title>
<url>%2F%E6%95%B0%E5%AD%A6%E5%BB%BA%E6%A8%A1%2F%E7%BE%8E%E8%B5%9B%2F%E7%BE%8E%E8%B5%9B%E4%BA%8B%E9%A1%B9%2F</url>
<content type="text"><![CDATA[美国大学生数学建模竞赛美赛培训第一天 上午 关于美赛的一些小提示和注意事项 一、注意事项1.报名缴费报名必须visa和master卡 2.赛题分为六道题-MCM:ABC-ICM:DEF 二、论文要求-1. 摘要要求高 -2. 结构,格式 -3. 图表,公式 -4. 做一个框图 -5. 简洁性和结构性 -6. 主要结论体现主旨和结果 三、大致模块-1. 适当简化或重述问题(一页)可以添加图片形象描述题目 -2. 明确假设5-6,阐述变量8-9,猜想 -3. 模型 建立,分析 -4. 测试 -5. 总结模型或方法优缺点 四、论文格式 字体至少12号,不能出现汉字(图表中务必注意) 页顶,都需要控制号和页码 五、经验 30%参考文献 -CNKI翻译 -读秀 30%靠建模资源 30%论文写作 10% 三个层次 搬运工 泥瓦匠 设计师]]></content>
<categories>
<category>数学建模</category>
<category>美赛</category>
</categories>
<tags>
<tag>数学建模</tag>
</tags>
</entry>
<entry>
<title><![CDATA[爬虫分析 Java 岗位招聘情况]]></title>
<url>%2FSpider%2Fanalysis%2F%E7%88%AC%E8%99%AB%E5%88%86%E6%9E%90-Java-%E5%B2%97%E4%BD%8D%E6%8B%9B%E8%81%98%E6%83%85%E5%86%B5%2F</url>
<content type="text"><![CDATA[智联招聘Java 岗位招聘信息为了知道现在的 Java 开发的岗位招聘,公司们需要什么样的人才?要有什么样的技能?以及对应的市场如何? 趁着这次课程设计,我有了一个“大”胆的想法。 利用Jsoup与gson库编写Java代码,爬取了智联招聘上 Java开发 关键字的招聘岗位近 90×625 = 56250 条招聘信息,选取 top5786 条招聘岗位带 Java 关键字的招聘信息进行数据分析。 主要对抓取到的以下16个方面数据进行分析: 信息发布 企业信息 招聘信息 招聘发布时间 企业名称 工作地点 招聘结束时间 企业类型 工作类型 最新更新时间 公司规模 工作职位 搜索标签 企业信息介绍 薪资 职业类型 公司福利 学历要求 工作经验要求 1详细信息页面、企业名称、企业类型、公司规模、企业信息介绍、工作地点、工作类型、工作职位、搜索标签、薪资、学历要求、工作经验要求、职业类型、公司福利 岗位分布先上两个图,把全部招聘工作地数据标记在符号地图上的效果如下,圆饼大小表示数量: 筛选出发布招聘岗位最多的地区,如下: 整理出来的表格: 地区 招聘数 北京 995 上海 486 深圳 340 南京 290 广州 241 成都 224 杭州 202 武汉 194 郑州 191 大连 184 济南 183 西安 169 长沙 164 5K多条招聘信息中出现超过150次的地区:显然北京995 是最多的,接近1K,上海 486 排在第二,深圳340排在第三,两者都超过了 300,北上广深,江浙沪都十分多。因为我是南方人(去过的最北方就是长沙),对北方不是很熟悉,大连,济南,西安都超过了150。当然数据只是针对这前 5786 条数据而言!见微知著总体上也差不多。 职位情况然后对职位进行分析,这个问题让我头可疼可疼了呢,因为没有一个规范,导致招聘信息上的职位写的是各式各样,举个例子吧。Java 高级开发工程师和高级 Java 开发工程师是一个意思吧。Java 开发工程师,Java开发工程师,JAVA 工程师这三是一个概念吧。有的甚至写着 JAVA 攻城师???以为自己萌萌哒??? 这样统计起来也比较麻烦,找不到好办法,只能先进行词频统计,然后剩下的再进行手动归类。最终被我归为 20 个类别。 通过词云可以看出企业提供的职位大多为JAVA开发工程师。 岗位要求提取了所有的职位要求,进行分词统计,清理没意义的词,统一英文字符,如 Java 和 java 不区分大小。 提取前 50 个中文词汇以及出现次数 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758开发,2100熟悉,1842经验,1268工程师,897工作,895职位,842能力,806优先,755描述,753软件,682技术,677负责,660职能,658类别,654设计,562系统,559要求,549相关,542使用,529良好,521以上,502框架,492数据库,491项目,429团队,424了解,398数据,387产品,352熟练,349精通,331公司,321任职,313进行,311编程,309平台,306学习,301语言,300具备,300岗位职责,296沟通,292代码,284互联网,280具有,269参与,263分析,262维护,253优化,239编写,235爬虫,233学历,230文档,226合作,219软件开发,218高级,213常用,212测试,205需求,205完成,200 这个词频排序挺有趣的,要来好好研究一下 (开发,2100)(熟悉,1842)(经验,1268),这是最多的三个。熟悉其实也就是相当于有经验了。表示程度上的词也是频频出现(熟悉,1842)(良好,521)(熟练,349)(精通,331)。其实我一直不太理解精通这个词,什么程度上的熟练才能称为精通。个人感觉应该对精通这个词怀有敬畏之心。(团队,424)(参与,263)(合作,219)(沟通,292)(协作,108),这个更多的是强调团队开发,参与到团队开发以及合作的重要性,毕竟现在的项目的规模基本上已经大到不是一个人就能完成的。顺便提一下 Git 出现了刚好 100 次。(分析,262)(维护,253)(优化,239),这三个词应该就是对个人能力的综合描述了,具有分析问题的能力,维护和优化项目的能力,一个成熟的项目后期的维护和优化是很重要的。 看看没排进 top50 的其他词汇(专业,199)(架构,173)(研发,170),这几个词看起来就厉害了,毕竟架构这个不是随便就能搞搞。(爬虫,233)(抓取,140)(爬取,28)(正则表达式,31),刚需刚需!!!(独立,153)(责任心,123)(强烈,69)(踏实,26)(抗压,17)(认真,26)(热爱,53)(意识,78)(逻辑,58)这应该都是所要求的品质和能力了吧。(数据结构,106)(算法,198)(设计模式,33)回忆起了被《数据结构》和《算法导论》统治的恐惧吗?(分布式,97)(分布式系统,29)(分布式计算,7)用心感受一下就行了。(开源,112),(Github,29)这个也可以看出公司对开源的这方面的重视了,这是一种学习能力的体现,也是对程序和代码的热爱。了解这个也能大概证明自己不是一个只会闭门造车的人。 薪酬情况 可以看出工作类型为全职的薪资水平相较于其他三种高。 学历要求 可以看出这些公司招聘主要是面向本科与大专学历的求职人员。 其他 工作经验与职位的词云。 博主正在努力更新中,未完待续>>> 总结详细请跳转网页全国Java开发招聘信息.]]></content>
<categories>
<category>Spider</category>
<category>analysis</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[employ_info]]></title>
<url>%2FJava%2FSpider%2Femploy-info%2F</url>
<content type="text"><![CDATA[爬虫分析 Java 岗位招聘情况现在的 Java 开发的岗位招聘,公司们需要什么样的人才?要有什么样的技能?以及对应的市场如何? 所以,我有了一个“大”胆的想法。 利用Jsoup与gson库编写Java代码,爬取了智联招聘上 Java开发 关键字的招聘岗位近 90×625 = 56250 条招聘信息,选取 top5786 条招聘岗位带 Java 关键字的招聘信息进行数据分析。主要对抓取到的以下16个方面信息进行分析: 信息发布 企业信息 招聘信息 招聘发布时间 企业名称 工作地点 招聘结束时间 企业类型 工作类型 最新更新时间 公司规模 工作职位 搜索标签 企业信息介绍 薪资 职业类型 公司福利 学历要求 工作经验要求 详细信息页面、企业名称、企业类型、公司规模、企业信息介绍、工作地点、工作类型、工作职位、搜索标签、薪资、学历要求、工作经验要求、职业类型、公司福利 岗位分布全部数据标记在符号地图上的效果如下,圆饼大小表示数量: 筛选出发布招聘岗位最多的地区,如下: 整理出来的表格如下: 地区 招聘数 北京 995 上海 486 深圳 340 南京 290 广州 241 成都 224 杭州 202 武汉 194 郑州 191 大连 184 济南 183 西安 169 长沙 164 5K多条招聘信息中出现超过150次的地区:显然北京995 是最多的,接近1K,上海 486 排在第二,深圳340排在第三,两者都超过了 300,北上广深,江浙沪都十分多。因为我是南方人(去过的最北方就是长沙),对北方不是很熟悉,大连,济南,西安都超过了150。当然数据只是针对这前 5786 条数据而言!见微知著总体上也差不多。 职位要求提取了所有的职位要求,进行分词统计,清理没意义的词,统一英文字符,如 Java 和 java 不区分大小。 提取前 50 个中文词汇以及出现次数 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758开发,2100熟悉,1842经验,1268工程师,897工作,895职位,842能力,806优先,755描述,753软件,682技术,677负责,660职能,658类别,654设计,562系统,559要求,549相关,542使用,529良好,521以上,502框架,492数据库,491项目,429团队,424了解,398数据,387产品,352熟练,349精通,331公司,321任职,313进行,311编程,309平台,306学习,301语言,300具备,300岗位职责,296沟通,292代码,284互联网,280具有,269参与,263分析,262维护,253优化,239编写,235爬虫,233学历,230文档,226合作,219软件开发,218高级,213常用,212测试,205需求,205完成,200 这个词频排序挺有趣的,要来好好研究一下 (开发,2100)(熟悉,1842)(经验,1268),这是最多的三个。熟悉其实也就是相当于有经验了。表示程度上的词也是频频出现(熟悉,1842)(良好,521)(熟练,349)(精通,331)。其实我一直不太理解精通这个词,什么程度上的熟练才能称为精通。个人感觉应该对精通这个词怀有敬畏之心。(团队,424)(参与,263)(合作,219)(沟通,292)(协作,108),这个更多的是强调团队开发,参与到团队开发以及合作的重要性,毕竟现在的项目的规模基本上已经大到不是一个人就能完成的。顺便提一下 Git 出现了刚好 100 次。(分析,262)(维护,253)(优化,239),这三个词应该就是对个人能力的综合描述了,具有分析问题的能力,维护和优化项目的能力,一个成熟的项目后期的维护和优化是很重要的。 看看没排进 top50 的其他词汇(专业,199)(架构,173)(研发,170),这几个词看起来就厉害了,毕竟架构这个不是随便就能搞搞。(爬虫,233)(抓取,140)(爬取,28)(正则表达式,31),刚需刚需!!!(独立,153)(责任心,123)(强烈,69)(踏实,26)(抗压,17)(认真,26)(热爱,53)(意识,78)(逻辑,58)这应该都是所要求的品质和能力了吧。(数据结构,106)(算法,198)(设计模式,33)回忆起了被《数据结构》和《算法导论》统治的恐惧吗?(分布式,97)(分布式系统,29)(分布式计算,7)用心感受一下就行了。(开源,112),(Github,29)这个也可以看出公司对开源的这方面的重视了,这是一种学习能力的体现,也是对程序和代码的热爱。了解这个也能大概证明自己不是一个只会闭门造车的人。 薪酬情况 学历情况 可以看出这些公司招聘主要是面向本科与大专学历的求职人员。 职位情况然后对职位进行分析,这个问题让我头可疼可疼了呢,因为没有一个规范,导致招聘信息上的职位写的是各式各样,举个例子吧。Java 高级开发工程师和高级 Java 开发工程师是一个意思吧。Java 开发工程师,Java开发工程师,JAVA 工程师这三是一个概念吧。有的甚至写着 JAVA 攻城师???以为自己萌萌哒??? 这样统计起来也比较麻烦,找不到好办法,只能先进行词频统计,然后剩下的再进行手动归类。最终被我归为 20 个类别。 企业提供的职位大多为JAVA开发工程师 工作经验与职位的词云。 总结详细请跳转网页全国Java开发招聘信息.]]></content>
<categories>
<category>Java</category>
<category>Spider</category>
</categories>
<tags>
<tag>Spider</tag>
</tags>
</entry>
<entry>
<title><![CDATA[goodwell的第一篇博文]]></title>
<url>%2F%E5%9B%9E%E6%94%B6%E7%AB%99%2Ftest1%2Fgoodwell%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%8D%9A%E6%96%87%2F</url>
<content type="text"><![CDATA[Markdown语法1.标题的设置(几级标题就使用几个“#”) 标题1: 啦啦啦标题2:嘻嘻嘻2.段落和换行段落:使用空行隔开自动换行:结尾使用两个 空格 3.简单字体设置(插件快捷键)粗体:在 ** ** 之间输入(Ctrl+B)eg:这很 粗斜体:在 _ _ 或者 * * 之间输入(Ctrl+I)eg:这很 _斜_删除线:在 ~~ ~~ 之间输入(Alt+S)eg:这很 多余PS: 支持嵌套使用这很粗、但是斜、所以很多余 4.引用使用“>”即可 test 5.代码单行:在 ` ` 之间输入command多行:在 \之间输入12 code block` 6.列表有序:number+dot(半角)eg:1.啦啦啦2.嘻嘻嘻3.嘿嘿嘿无序:单个 +或-或*-啦啦啦-嘻嘻嘻-嘿嘿嘿 7.链接小括号(链接),中括号(带title属性)eg:This is an example link.(https://goodwell42.github.io)eg:This is an example link. 8.分割线使用 *** 或 --- 9.页眉分割线10.插入图片![title](图片的外链地址)eg: 11.插入音乐关忆北 12.LaTeX公式编辑]]></content>
<categories>
<category>回收站</category>
<category>test1</category>
</categories>
<tags>
<tag>test</tag>
</tags>
</entry>
<entry>
<title><![CDATA[RTFSC启示]]></title>
<url>%2Funcategorized%2FRTFSC%E5%90%AF%E7%A4%BA%2F</url>
<content type="text"><![CDATA[123456789101112131415- RTFSC(Read the fucking source code) - RTFM(Read the fucking manual) - UTFH (“Use The Fucking Help”) - STFW (“Search The Fucking Web”) - STFG (“Search The Fucking Google” or “Search The Fantastic Google”) - GIYF (“Google Is Your Friend”) - JFGI (“Just Fucking Google It”) - UTSL (“Use The Source Luke”—alternately, RTFS) - RTFA (“Read The Fucking Article”—common on news forums such as Fark.com[3] and Slashdot) - RTFE (“Read The Fucking Email”) - RTFC (“Read The Fucking Code,” or “Reboot The Fucking Computer”) - RTFQ (“Read The Fucking Question”) - LMGTFY (“Let Me Google That For You”) - WIDGI (“When In Doubt Google It” - Also occasionally ‘WIDGIT’) - FIOTI (“Find It On The Internet”)]]></content>
<tags>
<tag>杂谈</tag>
</tags>
</entry>
<entry>
<title><![CDATA[goodwellTest]]></title>
<url>%2F%E5%9B%9E%E6%94%B6%E7%AB%99%2Ftest1%2FgoodwellTest%2F</url>
<content type="text"><![CDATA[博文发表测试 ##Markdown语法 ###1.标题的设置(几级标题就使用几个“#”) #标题1: 啦啦啦 ##标题2:嘻嘻嘻 ###2.段落和换行段落:使用空行隔开自动换行:结尾使用两个 空格 ###3.简单字体设置(插件快捷键)粗体:在 ** ** 之间输入(Ctrl+B)eg:这很 粗斜体:在 _ _ 或者 * * 之间输入(Ctrl+I)eg:这很 _斜_删除线:在 ~~ ~~ 之间输入(Alt+S)eg:这很 多余PS: 支持嵌套使用这很粗、但是斜、所以很多余 ###4.引用使用“>”即可 test ###5.代码单行:在 ` ` 之间输入command多行:在 \之间输入12 code block` ###6.列表有序:number+dot(半角)eg:1.啦啦啦2.嘻嘻嘻3.嘿嘿嘿无序:单个 +或-或*-啦啦啦-嘻嘻嘻-嘿嘿嘿 ###7.链接小括号(链接),中括号(带title属性)eg:This is an example link.(https://goodwell42.github.io)eg:This is an example link. ###8.分割线使用 *** 或 --- ###9.页眉分割线 ###10.插入图片![title](图片的外链地址)eg: ###11.插入音乐关忆北 ###11.LaTeX公式编辑]]></content>
<categories>
<category>回收站</category>
<category>test1</category>
</categories>
<tags>
<tag>test</tag>
</tags>
</entry>
</search>