├── 1.1认识JAVA.md ├── 1.2原生数据类型.md ├── 1.3运算符(Operator).md ├── 1.4流程控制语句.md ├── 1.5命名规则.md ├── 2.1面向对象.md ├── 2.2面向对象之封装.md ├── 2.3面向对象之继承.md ├── 2.4面向对象之多态.md ├── 2.5接口的多继承.md ├── 2.6类.md ├── 3.1Java中的关键字.md ├── 3.2导包.md ├── 3.3访问修饰符.md ├── 3.4递归(Recursion).md ├── 3.5类型转换.md ├── 3.6回调.md ├── 4.1Object类.md ├── 4.2String类.md ├── 4.3StringBuffer类.md ├── 4.4包装类.md ├── 5.1数组(Array).md ├── 5.2数组之排序.md ├── 5.3集合简介.md ├── 5.4集合之List.md ├── 5.5集合之Map.md ├── 5.6集合之Set.md ├── 5.7集合总结.md ├── 6.1泛型.md ├── 6.2枚举类型.md ├── 6.3静态导入.md ├── 6.4反射.md ├── 6.5JAVA注解(Annotation).md ├── 6.6通过JUnit深入理解反射与注解的使用方式和与场景.md ├── 6.7异常(Exception).md ├── 6.8内部类.md ├── 7.1图形化界面GUI.md ├── 8.1IO(输入输出).md ├── 9.1序列化.md ├── 9.2线程.md ├── 9.3JVM之加载器.md ├── JAVA8 ├── API解析 │ └── Object.md ├── JAVA知识点论述 │ ├── 接口与抽象类的区别.md │ ├── 继承之private方法属性探索.md │ └── 继承之static方法属性探索.md └── README.md ├── Java核心卷 ├── README.md ├── 第一章JAVA程序设计概述 │ └── 第一章JAVA程序设计概述.md ├── 第七章异常、断言和日志 │ ├── 7.1处理错误.md │ ├── 7.2捕获异常.md │ ├── 7.3使用异常机制的技巧.md │ ├── 7.4使用断言.md │ ├── 7.5记录日志.md │ └── img │ │ └── 01.png ├── 第三章JAVA的基本程序设计结构 │ ├── 3.1一个简单的JAVA应用程序.md │ ├── JAVA基础语法.md │ └── img │ │ ├── 001.png │ │ ├── 002.png │ │ └── 003.png ├── 第九章集合 │ ├── 9.1Java集合框架.md │ ├── 9.2具体的集合.md │ ├── 9.3映射.md │ ├── 9.4视图与包装器.md │ ├── 9.5算法.md │ ├── 9.6遗留的集合.md │ └── img │ │ ├── 01.png │ │ ├── 9-1.png │ │ ├── 9-10.png │ │ ├── 9-11.png │ │ ├── 9-12.png │ │ ├── 9-2.png │ │ ├── 9-3.png │ │ ├── 9-4.png │ │ ├── 9-5.png │ │ ├── 9-6.png │ │ ├── 9-7.png │ │ ├── 9-8.png │ │ ├── 9-9.png │ │ ├── 9.png │ │ ├── table9-2.png │ │ └── table9-3.png ├── 第二章JAVA程序设计环境 │ └── 第二章JAVA程序设计环境.md ├── 第五章继承 │ ├── 5.1类、超类和继承.md │ ├── 5.2Object所有类的超类.md │ ├── 5.3泛型数组列表.md │ ├── 5.4对象包装器与自动装箱.md │ ├── 5.5参数数量可变的方法.md │ ├── 5.6枚举类.md │ ├── 5.7反射.md │ ├── 5.8继承的设计技巧.md │ └── img │ │ └── 001.png ├── 第八章泛型程序设计 │ ├── 8.1为什么要使用泛型程序设计.md │ ├── 8.2定义简单泛型.md │ ├── 8.3泛型方法.md │ ├── 8.4类型变量的限定.md │ ├── 8.5泛型代码和虚拟机.md │ ├── 8.6约束与局限性.md │ ├── 8.7泛型类型的继承规则.md │ ├── 8.8通配符类型.md │ ├── 8.9反射和泛型.md │ └── img │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 8-3.png │ │ └── 8-4.png ├── 第六章接口、lambda表达式与内部类 │ ├── 6.1接口.md │ ├── 6.2接口示例.md │ ├── 6.3lambda表达式.md │ ├── 6.4内部类.md │ ├── 6.5代理.md │ └── img │ │ ├── 01.png │ │ ├── 02.png │ │ ├── 03.png │ │ ├── 04.png │ │ ├── 05.png │ │ └── 06.png ├── 第十四章并发 │ ├── 14.1什么是线程.md │ ├── 14.2中断线程.md │ ├── 14.3线程状态.md │ ├── 14.4线程属性.md │ ├── 14.5同步.md │ ├── 14.6阻塞队列.md │ └── img │ │ ├── 001.png │ │ ├── 14-3.png │ │ ├── 14-4.png │ │ └── table14-1.png └── 第四章对象与类 │ ├── 4.10类设计技巧.md │ ├── 4.1面向对象程序设计概述.md │ ├── 4.2使用预定义类.md │ ├── 4.3用户自定义类.md │ ├── 4.4静态域与静态方法.md │ ├── 4.5方法参数.md │ ├── 4.6对象构造.md │ ├── 4.7包.md │ ├── 4.8类路径.md │ ├── 4.9文档注释.md │ └── img │ ├── 001.png │ ├── 002.png │ ├── 003.png │ ├── 004.png │ ├── 005.png │ ├── 006.png │ └── 007.png ├── README.md ├── Sort.java └── img ├── 5-1.png ├── Exception.png ├── HashMap的内存实现布局.png ├── JAVA层次图.png ├── synchronized.png ├── 数组1.png ├── 数组2.png ├── 迭代器.png └── 集合.png /1.1认识JAVA.md: -------------------------------------------------------------------------------- 1 | # 1.1 认识JAVA 2 | 3 |
4 | 5 | * **JAVA分三类** 6 | 1. Java SE:Java Standard Edition(J2SE) 7 | 2. Java ME: Java Mobile Edition(J2ME) 8 | 3. Java EE:Java Enterprise Edition(J2EE) 9 | 10 | * **JDK和Jre** 11 | 1. JDK:Java Development Kit (Java开发必备) 12 | 2. JRE:Java Runtime Environment (Java执行环境) 13 | 3. 配置环境 14 | 1. 下载JDK 15 | 2. 安装JDK 16 | 3. 设定环境变量(可以是用户变量,也可以是系统变量),指向JDK安装目录中的bin目录 17 | 4. 通过运行,输入cmd打开命令行窗口,输入java –version,显示出Java版本信息 18 | * **编写Java程序** 19 | 1. 用windows记事本来编写Java程序 20 | 2. 用Editplus,UltraEdit等高级文本编辑工具编写 21 | 3. 用专业的IDE(Integrated Development Environment)编写 22 | 23 | 注:所有的Java代码,其后缀都是以java结尾。 24 | * **Java程序的执行过程(以Test.java为例)** 25 | 1. **编译** 编译命令:javac Test.java 26 | 2. **执行** 执行命令:java Test(注意,Test后面没有.class) 27 | 28 | class文件是字节码文件,程序最终执行的就是这个字节码(bytecode)文件 29 | * Java与JVM 30 | 1. Java是跨平台的语言,真正执行的不是二进制代码,而是字节码。 31 | 2. JVM(Java Virtual Machine,Java虚拟机) 32 | 3. Java是跨平台的,而JVM不是跨平台的(JVM是由C语言编写的) 33 | 4. Java之所以能够做到跨平台,本质原因在于JVM不是跨平台的。 34 | * JAVA的注解 35 | 1. 单行注释:以`//`开头,`//`后面的所有内容均被当做注释处理。 36 | 2. 多行注释:以`/*开头,以*/结束`,中间的所有内容均被当做注释处理。(注:多行注释不可嵌套) 37 | 3.文档注释:以`/ **开头,以* /结束` 。 38 | * 栈与堆 39 | 1. 栈(`stack`):存放原生数据类型(先进后出,后进先出) 40 | 2. 堆(`heap`):存放对象 41 | * IDE(`Integrated Development Environment`)集成开发环境 42 | 1. `NetBeans http://netbeans.org/` 43 | 2. `JBuilder` 44 | 3. `Intellij IDEA` 45 | 4. `Eclipse`(日蚀、月蚀) 46 | -------------------------------------------------------------------------------- /1.2原生数据类型.md: -------------------------------------------------------------------------------- 1 | # 1.2 原生数据类型 2 | 3 | ### 一、Java 中的数据类型分为两大类: 4 | * 1.原生数据类型 (`Primitive Data Type`) 5 | * 2.引用类型(对象类型) (`Reference Type`) 6 | ### 二、变量与常量 7 | * 1.所谓常量,就是值不会变化的量。 8 | * 2.所谓变量,就是值可以变化 的量。 9 | ### 三、变量的定义与赋值 10 | * 1.定义:变量类型 变量名;`int a;` 11 | * 2.赋值:变量名 = 变量值; `a = 2;` 12 | 13 | >注:" = "表示赋值,将等号右边的值赋给了左边的变量。 Java 中使用" == "表示相等,等价于数学中的" = "。 14 | 15 | * 3.综合变量定义与赋值: 16 | ```java 17 | 变量类型 变量名; 18 | 变量名 = 变量值; 19 | eg: 20 | int a; 21 | a = 1; 22 | ``` 23 | * 可以将上面两个步骤合二为一: 24 | ```java 25 | 变量类型 变量名 = 变量值; 26 | int a = 1; 27 | ``` 28 | ### 四、变量名 29 | * 在 Java 中,变量名以下划线、字母、$符号开头,并且后跟下划线、 字母、$符号以及数字。 30 | * 总之,Java 中的变量名不能以数字开头。 31 | ### 五、原生数据类型(八种) 32 | 1. 整型:使用 `int` 表示。 (32 位) 33 | 2. 字节型:使用 `byte` 表示。(表示-128~127 之间的 256 个整数, 8 位)。 34 | 3. 短整型:使用 `short` 表示。 (16 位) 35 | 4. 长整型:使用 `long` 表示。(64 位) 36 | 5. 单精度浮点型:使用 `float` 表示。所谓浮点型,指的就是小数,也叫做实数,比如 1.2。 37 | 6. 双精度浮点型:使用 `double` 表示。双精度浮点型表示的数据范围要比单精度浮点型大。 38 | 7. 字符型:使用 `char` 表示(`char` 是 `character` 的缩写)。所谓字符,就是单个的字符表示, 比如字母 a 39 | 或者中文张,外面用单引号包围上。比如 `char a = ‘B’; char b = ‘张’;` 40 | 8. 布尔类型,使用 `boolean` 表示。布尔类型只有两种可能值,分别是 `true` 与 `false`。 41 | ### 六、关于计算机系统中的数据表示 42 | 1. 位:`bit`(只有 0,1 两种状态),是计算机系统中的最小数据表示单位。 43 | 2. 字节:`byte`, 44 | ```` 45 | 1 byte = 8 bit。 46 | 1 KB = 1024 Byte (1Kg = 1000g,与计算机系统不同) 47 | 1 MB = 1024 KB 48 | 1 GB = 1024 MB 49 | ```` 50 | ### 七、注释。 51 | 注释是给人看的,不是给计算机看的。Java 中共有 3 种类型的注释: 52 | 1. 单行注释:以`//`开头,`//`后面的所有内容均被当作注释处理。 53 | 2. 多行注释:以`/*开头,以*/结束`,中间的所有内容均被当作注释处理。多行注释来源于 C/C++。关于多行注释,需要注意的是,多行注释不能嵌套。 54 | 3. 另一种多行注释。用于产生 `Java Doc` 帮助文档。 55 | ### 八、`double`与`float`的辨别 56 | 1. Java 中的所有浮点类型默认情况下都是 `double`。不能将 `double` 类型的值赋给 `float` 类型的变量,即便该 `double` 类型的值处于`float`类型的范围内也是不可以的。总之,能否成功赋值取决于等号右边的值类型与等号左边的变量类型是否一致。 57 | 2. 将 `double` 类型的值赋给 `float` 类型的变量 58 | 1. 强制类型转换,将 `double` 类 型的值强制转换为 `float` 类型。 59 | 2. 使用 `java` 预言的支持。 60 | ### 九、强制类型转换 61 | ```java 62 | 类型 变量名 = (类型)变量值; 63 | ``` 64 | ### 十、不同类型变量之间的赋值 65 | ```java 66 | 如下代码无法通过编译: 67 | int a = 1; 68 | short b = a; 69 | 70 | a 是 int 类型,b 是 short 类型,int 类型表示的数据范围要比 short 类型大, 71 | 不能将表示范围大的值赋给表示范围小的变量。 72 | ``` 73 | 74 | ```java 75 | 如下代码可以通过编译: 76 | short a = 1; 77 | int b = a; 78 | a 是 short 类型,b 是 int 类型,int 类型表示的数据范围要比 short 类型大, 79 | 可以将表示范围小的值赋给表示范围大的变量。 80 | ``` 81 | * 1.可以将表示范围小的值赋给表示范围大的变量。`byte->short->int->long->float->double ` 82 | * 2.不能直接将表示范围大的值赋 给表示范围小的变量,只能通过强制类型转换实现。 83 | ### 十一、变量的声明与赋值 84 | ```java 85 | 变量在使用前必须要赋值; 86 | 87 | 变量必须要声明其类型方可使用; 88 | 89 | 变量在使用前必须要定义,并且只能定义一次。 90 | ``` 91 | * 1.成员变量:声明时可不赋初值,在使用时,编译器会自动为其赋默认值 92 | * 2.局部变量:使用前必须赋初值 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
默认初始值byte、short、int、long0
float、double0.0
char\u0000
booleanfalse
引用类型null
117 |
118 | 119 | ### 十二、常量 120 | 121 | * 1.命名规则:所有单词的字母均大写,若有多个单词,那么用下环线连接即可 122 | ```java 123 | public static final int AGE_OF_PERSON = 20; 124 | ``` 125 | * 2.在JAVA中声明`final`常量时通常都会加上`static`关键字,这样对象的每一个实例都会访问唯一 一份常量值。 126 | -------------------------------------------------------------------------------- /1.3运算符(Operator).md: -------------------------------------------------------------------------------- 1 | # 1.3 运算符(Operator) 2 | 3 | ### 一、运算结果类型介绍 4 | 5 | * 当有若干个变量参与运算时,结果类型取决于这些变量中表示范围最大的那个变量类型。 比如,参与运算的变量中,有整型 int,有双精度浮点型 double,有短整型 short,那么 最后的结果类型就是 double。 6 | 7 | >eg 8 | ```java 9 | int a = 1; 10 | int b = 2; 11 | double c = (double)a / b; 12 | ``` 13 | 14 | 15 | * 上面的代码中,a 与 b 都是整型,但是通过(double)a 这种转换将 a 转换为一个匿名的变量,该变量的类型是 double, 16 | * 但是要注意: a 本身依旧是 int 类型,而不是 double 类型, 17 | * 本例中,(double)a / b 就是 double 类型除以 int 类型,结果自然是 double 类型。 18 | 19 | ### 二、取模运算符:使用%表示。 (取模的结果符号永远与被除数的符号相同) 20 | 21 | >eg1 22 | ```java 23 | int a = 5; 24 | int b = 3; 25 | int c = a % b; 26 | ``` 27 | * 上面代码的运行结果是 2,因为 5 除以 3 结果是 1 余 2。 **取模的规律:取模的结果符号永远与被除数的符号相同** 28 | 29 | >eg2 30 | ```java 31 | int a = 5; 32 | int b = -3; 33 | int c = a % b; 34 | ``` 35 | 36 | * 这段代码,被除数是 5,那么取模的结果是 2 37 | 38 | >eg3 39 | ```java 40 | int a = -5; 41 | int b = 3; 42 | int c = a % b; 43 | ``` 44 | * 这段代码,被除数是-5,那么取模的结果是-2。 45 | 46 | ### 三、关系运算符(关系运算的结果是个 boolean 值。) 47 | 48 | * 大于(>)、小于(<)、 49 | 50 | * 等于(==)、不等于(!=)、 51 | 52 | * 大于等于(>=)、小于等 于(<=)。 53 | 54 | ### 四、逻辑运算符(返回一个 boolean 值) 55 | 56 | * 逻辑与:使用`&&`表示,逻辑与是个双目运算符(即有两个操作数的运算符),只有当两个操作数都为真的时候,结果才为真;其余情况结果均为假。逻辑与表示**并且**的意思。 57 | 58 | * 短路特性:如果第一个操作数为 false,那么结果肯定就是 false,所以在这种情况下, 将不会执行逻辑与后面的运算了,即发生了短路。 59 | 60 | * 逻辑或:使用`||`表示,逻辑或也是个双目运算符,只有当两个操作数都为假的时候, 结果才为假;其余情况结果均为真。逻辑或表示**或者**的意思。 61 | 62 | * 短路特性:如果第一个操作数为 true,那么结果肯定就是 true,所在在这种情况下, 将不会执行逻辑或后面的运算了,即发生了短路。 63 | 64 | ### 五、变量的自增与自减运算 65 | 66 | * int b = a++,作用是将 a 的值先赋给 b,然后再让 a 自增 67 | 68 | * int b = ++a,作用是将 a 的值先自增 1,然后将自增后的结果赋给 b。 69 | 70 | ### 六、条件运算符(三元表达式) 71 | 72 | * type d = a ? b : c; 73 | 74 | >eg 75 | ```java 76 | int d = 2 < 1 ? 3 : 4; 77 | ``` 78 | -------------------------------------------------------------------------------- /1.4流程控制语句.md: -------------------------------------------------------------------------------- 1 | # 1.4 流程控制语句 2 | 3 | ### 一、if语句 4 | 5 | * if 6 | ```java 7 | if(布尔表达式) { 8 | //待执行的代码 9 | } 10 | ``` 11 | * if、else 12 | ```java 13 | if(布尔表达式){ 14 | //待执行的代码 15 | } else { 16 | //待执行的代码 17 | } 18 | ``` 19 | 20 | * if、else if、else 21 | ```java 22 | if(布尔表达式) { 23 | 24 | //待执行的代码 25 | } else if(布尔表达式) { 26 | 27 | //待执行的代码 28 | } else if(布尔表达式) { 29 | 30 | //待执行的代码 31 | } else { 32 | 33 | //待执行的代码 34 | } 35 | ``` 36 | 37 | ### 二、switch 语句 38 | ```java 39 | switch(变量) //此处的变量类型:byte、short、int、char、枚举常量 { 40 | case 常量 1: 41 | //待执行的代码 42 | break; 43 | case 常量 2: 44 | //待执行的代码 45 | break; 46 | case 常量 3: 47 | //待执行的代码 48 | break; 49 | default: 50 | //待执行的代码 51 | } 52 | ``` 53 | 54 | * 虽然 case 语句中的 break 是可选的,但在绝大多数情况下,如果没有 break,程序的逻 辑就会发生错误,因此,通常情况下都需要加上 break。 55 | 56 | 57 | ### 三、循环语句(Java 中的循环控制语句一共有 3 种,分别是 while,do… while 以及 for 循环) 58 | 59 | * 1、while 循环: 60 | ```java 61 | while(布尔表达式) { 62 | //待执行的代码 63 | } 64 | ``` 65 | 66 | * 2、do…while 循环: 67 | ```java 68 | do { 69 | //待执行的代码 70 | } while(布尔表达式); 71 | ``` 72 | * 3、for 循环(使用最多的一种循环): 73 | ```java 74 | for(变量初始化; 条件判断; 步进) { 75 | //待执行的代码 76 | } 77 | ``` 78 | * for 循环的执行过程: 79 | 80 | * 1) 执行变量初始化。 81 | 82 | * 2) 执行条件判断。如果条件判断结果为假,那么退出 for 循环,开始执行循环后面的 代码;如果条件判断为真,执行 for 循环里面的代码。 83 | 84 | * 3) 执行步进。 85 | 86 | * 4) 重复步骤 2。 87 | 88 | * 4、for each循环(增强的for循环) 89 | 90 | >eg 91 | ```java 92 | for(int x : a){ 93 | System.out.println(x); 94 | } 95 | ``` 96 | 97 | ### 四、总结 98 | 99 | * while 与 do…while 之间的区别: 100 | 101 | * 1)while:如果布尔表达式的第一次判断就为 false,那么 while 循环一次也不执行。 102 | * 2)do......while:如果布尔表达式的第一次判断就为 false,do…while 循环则会执行一次。 103 | * 3)如果布尔表达式第一次判断为 true, 那么 while 循环与 do…while 循环等价。 104 | 105 | * break与continue: 106 | 107 | * break 语句:经常用在循环语句中,用于跳出整个循环,执行循环后面的代码。 108 | * continue 语句:经常用在循环语句中,用于跳出当前的这个循环(或者是跳出本次循环) 109 | , 开始下一次循环的执行。 110 | * break 与 continue 可以搭配标签使用,在实际开发中,根本没有人会将 break 与 continue 搭配标签来使用。 111 | 112 | * for each与for 113 | 114 | * 当遍历数组或集合时,若需要访问集合或数组的下标,那么最好使用旧式的方式来实现循环或遍历,而不要使用增强的for循环,因为它丢失了下标信息。 115 | 116 | -------------------------------------------------------------------------------- /1.5命名规则.md: -------------------------------------------------------------------------------- 1 | # 1.5命名规则 2 | 3 | * 1、类:首字母大写,如果一个类名由多个单词构成,那么每个单词的首字母都大写, 中间不使用任何的连接符。比如 Person 类,MemberTest 类。 4 | 5 | * 2、方法:首字母小写。如果一个方法由多个单词构成,那么第一个单词的所有字母全 都小写,从第二个单词开始,每个单词的首字母大写。比如 add,addThreeInt。 6 | 7 | * 3、属性:命名约定与方法相同。比如 age,ageOfPerson。 8 | 9 | * 4、包:将公司域名反转作为包名、对于包名 每个字母都需要小写 10 | 11 | >eg: 12 | ```java 13 | com.edu.bean 14 | ``` 15 | * 5、常亮::所有单词的字母都是大写,如果有多个单词, 那么使用下划线连接即可。 16 | 17 | >eg: 18 | ```java 19 | public static final int AGE_0F_PERSON = 20; 20 | ``` 21 | -------------------------------------------------------------------------------- /2.1面向对象.md: -------------------------------------------------------------------------------- 1 | # 2.1 面向对象 2 | 3 | ### 一、面向对象程序设计:OOP 4 | 5 | * Object Oriented Programming(简称:OOP):面向对象的程序设计 6 | * Object Oriented Design,(简称:OOD):面向对象设计 7 | * 两个重要的概念:类(class)与对象(object) 。 8 | 9 | ### 二、类(属性、方法) 10 | 11 | * 类是一种抽象的概念,类中包含了数据(通常使用名词来表示)与对数据的操纵(通常使用动词来表示)。比如说人就是一种抽象的概念,人具有姓名、年龄、身高等数据, 还有吃饭、跑步等操纵数据的动作。 12 | 13 | * 属性:数据,数据在类中称作属性(Property 或者 Attribute)或者叫成员变量(Member variable)。 14 | 15 | * 方法:对数据的操纵,这种操纵在类中称作方法(method)。 16 | 17 | ### 三、对象: 18 | 19 | * 对象是一种具体的概念,是类的一种具体表示方式。比如说人是一个类,而张三、 李四、王五等具体的人就是对象。 20 | 21 | ### 四、面向对象程序设计的三大基本特征: 22 | 23 | * 继承(Inheritence) 24 | 25 | * 封装(Encapsulation) 26 | 27 | * 多态 (Polymorphism) 28 | 29 | ### 五、修饰符 30 | 31 | * public: 用public修饰的类、类属变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问; 32 | 33 | * protected: 用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问,**protected重点突出继承**; 34 | 35 | * default: 如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。**default重点突出包**; 36 | 37 | * private: 用private修饰的类、类属变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。 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 |
访问级别访问控制修饰符同类同包不同类(不含子类)同包子类不同包不同类(不含子类)不同包子类
公开public
受保护protected--√(注意)
默认没有访问控制修饰符----
私有private----------
86 | 87 | 88 | ### 六、JAVA文件规范 89 | 90 | * 如果Java源文件中定义了多个类,那么,这些类中**最多只有一个类是public的**,换而言之,定义的多个类,可以都不是public的。 91 | 92 | * 若一个类是public的,那么main方法一定要放在public的这个类中。 93 | 94 | * 若没有public的类,那么main方法放在哪个类中都可以。 95 | 96 | -------------------------------------------------------------------------------- /2.3面向对象之继承.md: -------------------------------------------------------------------------------- 1 | # 2.3面向对象之继承 2 | 3 | ### 一、继承(Inheritence): 4 | 5 | * 1、Java 是单继承的,意味着一个类只能从另一个类继承(被继承的类叫做父类【基类,base class】, 继承的类叫做子类) 6 | 7 | * 2、Java 中的继承使用 extends 关键字。 8 | 9 | ### 二、生成子类对象的过程 10 | 11 | * 1、当生成子类对象时,Java 默认首先调用父类的不带参数的构造方法 12 | 13 | * 2、然后执行该构造方法,生成父类的对象 14 | 15 | * 3、再调用子类的构造方法,生成子类的对象 16 | 17 | * 注:要想生成子类的对象,首先需要生成父类的对象,没有父类对象就没有子类对象。比如说:没有父亲,就没有孩子 18 | 19 | ### 三、super 关键字 20 | 21 | * 1、super 表示对父类对象的引用(this表示对当前自己那个类对象的引用) 22 | 23 | * 2、 如果子类使用 super()显式调用父类的某个构造方法,那么在执行的时候就会寻找与 super()所对应的构造方法而不会再去寻找父类的不带参数的构造方法。 24 | 25 | * 3、与 this 一样,super 也必须**要作为构造方法的第一条执行语句**,前面不能有其他可执行语句。 26 | 27 | ### 四、关于继承的三点认识 28 | 29 | * 父类有的,子类也有 30 | 31 | * 父类没有的,子类可以增加 32 | 33 | * 父类有的,子类可以改变 34 | 35 | ### 五、 关于继承的注意事项 36 | 37 | * 构造方法不能被继承 38 | 39 | * 方法和属性可以被继承 40 | 41 | * 子类的构造方法隐式地调用父类的不带参数的构造方法 42 | 43 | * 当父类没有不带参数的构造方法时,子类需要使用 super 来显式地调用父类的构造方法,super 指的是对父类的引用 44 | 45 | * super 关键字必须是构造方法中的第一行语句。 46 | 47 | ### 六、方法重写(Override)又叫做覆写: 48 | 49 | * 子类与父类的方法**返回类型一样、方法名称一样,参数一样**,这样我们说子类与父类的方法构成了重写关系。 50 | 51 | * 方法重写与方法重载之间的关系: 52 | 53 | * 重载(Overload)发生在同一个类内部的两个或多个方法。<平行关系、不可决定多态、不是面向对象的特征> 54 | 55 | * 重写(Override)发生在父类与子类之间。 <层次关系、可决定多态> 56 | 57 | ### 七、继承的一些要点 58 | 59 | * 在普通方法中使用super关键字 60 | 61 | * 当两个方法形成重写关系时,可以在子类方法中通过super.run()形式调用父类的 run()方法,其中 super.run()不必放在第一行语句,因此此时父类对象已经构造完毕,先调用父类的 run()方法还是先调用子类的 run()方法是根据程序的逻辑决定的。 62 | 63 | * 在定义一个类的时候,如果没有显式指定该类的父类,那么该类就会继承于 java.lang.Object 类( JDK 提供的一个类,Object类是 Java 中所有类的直接或间接父类)。 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 | -------------------------------------------------------------------------------- /2.4面向对象之多态.md: -------------------------------------------------------------------------------- 1 | # 2.4面向对象之多态 2 | 3 | ### 一、多态(Polymorphism) 4 | 5 | * 子类就是父类(玫瑰是花,男人是人),因此多态的意思就是:**父类型的引用可以指向子类的对象**。 6 | 7 | >eg 8 | ```java 9 | People people = new Man(); 10 | ``` 11 | 12 | * 当使用多态方式调用方法时,首先检查父类中是否有 sing()方法,如果没有则编译错误;如果有,再去调用子类的 sing()方法。 13 | 14 | >eg 15 | ```java 16 | Parent p = new Child(); 17 | p.sing(); 18 | ``` 19 | 20 | ### 二、强制类型转换(向上类型转换、向下类型转换) 21 | 22 | * 向上类型转换(upcast):比如说将 Cat 类型转换为 Animal 类型,即将**子类型转换为父类型**。对于向上类型转换,不需要显式指定。 23 | 24 | >eg 25 | ```java 26 | Animal animal = new Cat(); 27 | animal.sing(); 28 | ``` 29 | 30 | * 向下类型转换(downcast):比如将 Animal 类型转换为 Cat 类型。即将**父类型转换为子类型**。对于向下类型转换,必须要显式指定(必须要使用强制类型 转换)。 31 | 32 | >eg 33 | ```java 34 | Animal animal = new Cat(); 35 | Cat cat = (Cat) animal; 36 | cat.sing(); 37 | ``` 38 | 39 | ### 三、抽象类、抽象方法 40 | 41 | * 抽象类(abstract class): 使用了 abstract 关键字所修饰的 类叫做抽象类。抽象类无法实例化,也就是说,不能 new 出来一个抽象类的对象(实例)。 42 | 43 | >eg 44 | ```java 45 | abstract class Test{ 46 | 47 | } 48 | ``` 49 | 50 | * 抽象方法(abstract method): 使用 abstract 关键字所修饰的方法叫做抽象方法。抽象方法需要定义在抽象类中。相对于抽象方法,之前所定义的方法叫做具体方法(**有声明,有实现**)。 <抽象方法默认是public的> 51 | 52 | >eg 53 | ```java 54 | public abstract void testMethod(); 55 | ``` 56 | 57 | * 抽象类与抽象方法 58 | 59 | * 如果一个类包含了抽象方法,那么这个类一定是抽象类。 60 | 61 | * 如果某个类是抽象类,那么该类可以包含具体方法(有声明、有实现)。 62 | 63 | * 如果一个类中包含了抽象方法,那么这个类一定要声明成 abstract class,也就是说,该类一定是抽象类;反之,如果某个类是抽象类,那么该类既可以包含抽象方法,也可以包含具体方法。 64 | 65 | * 总结: 66 | 67 | * 1>可以为空 68 | 69 | * 2>可以全为具体方法 70 | 71 | * 3>可以全为抽象方法 72 | 73 | * 4>可以有具体方法,也可以有抽象方法 74 | 75 | * 无论何种情况,只要一个类是抽象类,那么这个类就无法实例化。 76 | 77 | * 继承抽象类 78 | 79 | * 在子类继承父类(父类是个抽象类)的情况下,那么该子类必须要实现父类 中所定义的所有抽象方法;否则,该子类需要声明成一个 abstract class。 80 | 81 | 82 | ### 四、接口(interface) 83 | 84 | * 1、接口简介 85 | 86 | * 接口的地位等同于 class,接口中的所有方法都是抽象方法。 87 | 88 | * 在声明接口中的方法时,可以使用 abstract 关键字,也可以不使用。通常情况下,都会省略掉 abstract 关键字。 89 | 90 | * 可以将接口看作是特殊的抽象类(抽象类中可以有具体方法,也可以有抽象方法,而**接口中只能有抽象方法,不能有具体方法**)。 91 | 92 | * 2、实现接口(implements) 93 | 94 | * 类可以实现接口。实现使用关键字 implements 表示,代表了某个类实现了某个接口。 95 | 96 | * 一个类实现了某个接口,那么该类必须要实现接口中声明的所有方法。如果该类是个抽象类,那么就无需实现接口中的方法了。 97 | 98 | * Java 是单继承的,也就是说某个类只能有唯一 一个父类;一个类可以实现多 个接口,多个接口之间使用逗号分隔。 99 | 100 | * 一个类**可以同时实现接口和继承父类** 101 | 102 | * 3、接口中的方法与属性 103 | 104 | * 接口中声明的方法都是抽象方法,接口中的方法都是public的。 105 | 106 | * 接口中的成员变量都是public、final、static的(可省略这三个关键字)、定义时须赋初值。 107 | 108 | * 接口中成员变量的初始化 109 | * 因为是static和fianl的,所以不能是空final,但可以被非常量表达式初始化 110 | ```java 111 | interface aaa{ 112 | Random rand = new Random(); 113 | int randomInt = rand.nextInt(10); 114 | long randomLong = rand.nextLong()*10; 115 | …… 116 | } 117 | 在类被第一次加载是初始化 118 | ``` 119 | 120 | 121 | ### 五、多态之总结 122 | 123 | * 所谓多态,就是父类型的引用可以指向子类型的对象; 124 | 125 | * 所谓多态,就是接口类型的引用可以指向实现该接口的类的实例; 126 | 127 | * 关于接口与实现接口的类之间的强制类型转换方式与父类和子类之间的强制类型转换方式完全一样。 128 | 129 | * 一个类不能即是final,又是abstract的: 130 | 131 | * abstract的主要目的是定义一种约定、让子类去实现这种约定。 132 | 133 | * final表示该类不能被继承 134 | 135 | * abstract希望该类可以被继承,而final说明该类不能被继承,两者矛盾,因此一个类不能即是final的、又是abstract的。 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /2.5接口的多继承.md: -------------------------------------------------------------------------------- 1 | # 2.5 接口的多继承 2 | 3 | * 三个接口 4 | ```java 5 | package com.edu.testjava.testinterface; 6 | 7 | /** 8 | * @Author: 王仁洪 9 | * @Date: 2018/12/1 20:24 10 | */ 11 | public interface I1 { 12 | void fun1(); 13 | void fun2(); 14 | } 15 | ``` 16 | 17 | ```java 18 | package com.edu.testjava.testinterface; 19 | 20 | /** 21 | * @Author: 王仁洪 22 | * @Date: 2018/12/1 20:25 23 | */ 24 | public interface I2 { 25 | void fun3(); 26 | void fun4(); 27 | } 28 | ``` 29 | 30 | ```java 31 | package com.edu.testjava.testinterface; 32 | 33 | /** 34 | * @Author: 王仁洪 35 | * @Date: 2018/12/1 20:25 36 | */ 37 | public interface I3 { 38 | void fun5(); 39 | void fun6(); 40 | } 41 | ``` 42 | 43 | * 接口的多继承 44 | 45 | ```java 46 | package com.edu.testjava.testinterface; 47 | 48 | /** 49 | * @Author: 王仁洪 50 | * @Date: 2018/12/1 20:26 51 | */ 52 | public interface B extends I1, I2, I3 { 53 | void newfun(); 54 | } 55 | ``` 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /2.6类.md: -------------------------------------------------------------------------------- 1 | # 2.6 类 2 | 3 | ### 一、构造器初始化 4 | 5 | * 无法杜绝发生在构造函数执行之前的自动初始化动作 6 | ```java 7 | class MyClass{ 8 | int i; 9 | MyClass(){ 10 | i = 6; 11 | } 12 | }// i先被初始化为0,然后变成6 13 | ``` 14 | 15 | * 一个类中,变量定义的先后顺序决定了初始化的顺序。 16 | * 变量定义散布于方法定义之间,它们仍然会在构造器被调用之前初始化。 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /3.1Java中的关键字.md: -------------------------------------------------------------------------------- 1 | # 3.1 Java中的关键字 2 | 3 | ### 一、static 关键字(可以用于修饰属性,也可以用于修饰方法,还可以用于修饰类) 4 | 5 | * 1、static 修饰属性: 6 | 7 | * 无论一个类生成了多少个对象,所有这些对象**共同使用唯一 一份静态的成员变量**;一个对象对该静态成员变量进行了修改,其他对象的该静态成员变量的值也会随之发生变化。 8 | 9 | * 如果一个成员变量是 static 的,那么我们可以通过`类名.成员变量名`的方式来使用它(推荐使用这种方式)。 10 | 11 | * 非静态的成员变量在每个对象中是独立的(对象的非静态成员变量独立存在) 12 | 13 | * 2、 static 修饰方法(static 修饰的方法叫做静态方法) 14 | 15 | * **static方法其实就是没有this的方法** 16 | 17 | * 所以**静态方法内不能调用非静态方法** 18 | 19 | * 对于静态方法来说,可以使用`类名.方法名`的方式来访问。 20 | 21 | * 方法的重写与隐藏:静态方法只能继承,不能重写(Override)。 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 | >eg 52 | 53 | M m = new N(); 54 | m.output();//该引用调用M类的output()静态方法 55 | 56 | * 3、static修饰类:静态内部类 57 | 58 | * 4、 static 代码块:静态代码块。 59 | 60 | * 静态代码块的作用也是完成一些初始化工作。**首先执行静态代码块,然后执行构造方法**。静态代码块在类被加载的时候执行, 而构造方法是在生成对象的时候执行;要想调用某个类来生成对象,首先需要将类加载到 Java 虚拟机上(JVM),然后由 JVM 加载这个类来生成对象。 61 | 62 | * **类的静态代码块只会执行一次**,是在类被加载的时候执行的,因为每个类只会被加载一次,所以静态代码块也只会被执行一次;而构造方法则不然,每次生成一个对象的时候都会调用类的构造方法,所以 new 一次就会调用构造方法一次。 63 | 64 | * 如果继承体系中既有构造方法,又有静态代码块,那么首先执行最顶层的类 的静态代码块,一直执行到最底层类的静态代码块,然后再去执行最顶层类的构造方法,一直执行到最底层类的构造方法。**注意:静态代码块只会执行一次、再次new对象时只会执行构造方法**。 65 | 66 | * 5、在方法中访问成员变量 67 | 68 | * 不能在静态方法中访问非静态成员变量 69 | 70 | * 可以在静态方法中访问静态的成员变量 71 | 72 | * 可以在非静态方法中访问静态的成员变量 73 | 74 | * 总结:静态的只能访问静态的;非静态的可以访问一切。 75 | 76 | * 6、不能在静态方法中使用 this 关键字。 77 | 78 | * this关键字表示对当前对象的引用,对于一个静态方法来说可以不生成对象,直接通过类来调用,那么当前类便不知是谁,所以在静态方法中不能使用this关键字,this关键字可看做是一个非静态的成员变量。 79 | 80 | ### 二、final关键字(final 可以修饰属性、方法、类。 ) 81 | 82 | * 1、final 修饰属性:当一个属性被 final 所修饰时,表示该属性不能被改写。 83 | 84 | * 当 final 修饰一个原生数据类型时,表示该原生数据类型的值不能发生变化 (比如说不能从 10 变为 20); 85 | * 当 final 修饰一个引用类型时,表示该引用类型不能再指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。 86 | 87 | * 2、final 修饰方法:当一个方法被 final 所修饰时,表示该方法是一个终态方法, 即不能被重写(Override)。 88 | 89 | * 3、final 修饰类:当一个类被 final 所修饰时,表示该类是一个终态类,即不能被继承。 90 | 91 | * final类型成员变量赋初值 92 | 93 | * 在声明 final 类型的成员变量时就赋上初值 94 | 95 | * 在声明 final 类型的成员变量时不赋初值,但在类的所有构造方法中都为其赋 上初值。 96 | * 效率原因。如果一个方法指定为final,就是同意编译器将针对该方法的所有调用都转为内嵌(inline)调用 97 | * final和private 98 | * 类中所有的private方法隐含是final,可以为private方法增加final修饰,但没有其他额外意义 99 | 100 | 101 | ### 三、this关键字 102 | 103 | * 引例: 104 | 105 | ```java 106 | class MyClass{……} 107 | MyClass a = new MyClass(); 108 | MyClass b = new MyClass(); 109 | a.f(0); 110 | b.f(1); 111 | ``` 112 | 113 | * 都调用f(),如何区分是a的还是b的? 114 | 115 | * 编译器将“所操作对象的引用”作为第一个参数传递给调用函数 116 | 117 | ```java 118 | MyClass.f(a,0); 119 | MyClass.f(b,1); 120 | ``` 121 | * a,b参数是编译器“偷偷”传入的,无法在代码中使用。 122 | * 实际上有个参数表示“所操作对象的引用” 123 | 124 | * 如果想在f()内部使用“所操作对象的引用”怎么办?=>`this` 125 | 126 | ```java 127 | class MyClass{ 128 | private int number; 129 | private String text; 130 | MyClass(int number, String text){ 131 | this.number = number; 132 | this.text = text; 133 | } 134 | MyClass(){ 135 | this(0,"null"); 136 | } 137 | } 138 | ``` 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /3.2导包.md: -------------------------------------------------------------------------------- 1 | # 3.2导包 2 | 3 | ### 一、包 4 | 5 | * 1、作用:将完成不同功能的类分门别类,放在不同的目录(包)下。 6 | 7 | * 2、包的命名规则: 8 | 9 | * 将公司域名反转作为包名 10 | 11 | * 对于包名:每个字母都需要小写 12 | 13 | * 如果定义类的时候没有使用 package,那么 Java 就认为我们所定义的类位于默认包里面(default package)。 14 | 15 | * 3、编译带有 package 声明的 Java 源文件有两种方式: 16 | 17 | * 直接编译,然后根据类中所定义的包名,逐一手工建立目录结构,最后将生成的 class 文件放到该目录结构中(很少使用,比较麻烦)。 18 | 19 | * 使用编译参数 –d,方式为 `javac –d . 源文件.java`,这样在编译后,编译器会自动帮助我们建立好包所对应的目录结构。 20 | 21 | >eg 22 | 23 | javac -d . Test.java 24 | 25 | * 4、包与子包 26 | 27 | * 如:有两个包名,分别是 aa.bb.cc 与 aa.bb.cc.dd,那么我们称后者为前者的子包。 28 | 29 | ### 二、包的导入(import) 30 | 31 | * 作用:将使用 package 分离的各个类导入回来,让编译器能够找到所需要的类。 32 | 33 | * import 的语法: 34 | 35 | import com.edu.PackageTest; 36 | 37 | * import com.edu.* ; 表示导入 com.edu 包下面的所有类。 38 | 39 | * import aa.bb.* ; 并不会导入 aa.bb.cc 包下面的类。这时需要这样写: 40 | 41 | import aa.bb.*; 42 | import aa.bb.cc.*; 43 | 44 | ### 三、关于 package、import、class 的顺序问题: 45 | 46 | * 首先需要定义包(package),可选 47 | 48 | * 接下来使用 import 进行导入,可选 49 | 50 | * 如果两个类在同一个包下面,那么则不需要导入,直接使用即可。 51 | 52 | * 如果使用的类不在当前类的包内,则需要用import导入。 53 | 54 | * 然后才是 class 或 interface 的定义。 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /3.3访问修饰符.md: -------------------------------------------------------------------------------- 1 | # 3.3访问修饰符(access modifier) 2 | 3 | ### 一、访问修饰符(access modifier) 4 | 5 | * public(公共的):被 public 所修饰的属性和方法可以被所有类访问。 6 | 7 | * protected(受保护的):被 protected 所修饰的属性和方法可以在类内部、相同包以及该类的子类所访问。 8 | 9 | * private(私有的):被 private 所修饰的属性和方法只能在该类内部使用 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 |
访问级别访问控制修饰符同类同包不同类(不含子类)同包子类不同包不同类(不含子类)不同包子类
公开public
受保护protected--√(注意)
默认没有访问控制修饰符----
私有private----------
60 | 61 | ### 二、instanceof: 判断某个对象是否是某个类的实例。 62 | 63 | * 语法形式:`引用名 instanceof 类 名(接口名)`,返回一个 boolean 值。 64 | 65 | People people = new Man(); 66 | 67 | System.out.println(people instanceof People); 68 | //结果为 true,因为 Man 是 People 的子类,根据继承,子类就是父类,因此 Man 也可以看作是 People 的实例。 69 | -------------------------------------------------------------------------------- /3.4递归(Recursion).md: -------------------------------------------------------------------------------- 1 | # 3.4递归(Recursion) 2 | 3 | * 所谓递归(Recursion),就是方法调用自身。对于递归来说,一定有一个出口,让递归结束,只有这样才能保证不出现死循环。 4 | 5 | * 1、使用递归计算阶乘 6 | 7 | public int compute(int number) { 8 | if(1 == number) { 9 | return 1; 10 | }else { 11 | return number * compute(number -1); 12 | } 13 | } 14 | 15 | * 2、使用递归删除一个目录下的所有东西 16 | 17 | package com.edu.recursion; 18 | 19 | import java.io.File; 20 | 21 | public class DeleteAll { 22 | public static void main(String[] args) { 23 | File file = new File("D:\\1"); 24 | DeleteAll deleteAllClass = new DeleteAll(); 25 | deleteAllClass.deleteAll(file); 26 | } 27 | public void deleteAll(File file) { 28 | if(file.isFile() || file.list().length == 0) { 29 | file.delete(); 30 | }else { 31 | File[] files = file.listFiles(); 32 | for(File file2 : files) { 33 | deleteAll(file2); 34 | file.delete(); 35 | } 36 | } 37 | } 38 | } 39 | 40 | 41 | * 3、使用递归计算斐波那契数列的值(1、1、2、3、5、8、13、21、34......) 42 | 43 | public int compute(int n) { 44 | if(1 == n || 2 == n) { 45 | return 1; 46 | }else { 47 | return compute(n-1) + compute(n-2); 48 | } 49 | } 50 | 51 | * 4、:给定任意一个目录,以树形方式展现出该目录中的所有子目录和文件。另外, 在展现的时候将目录排在上面,文件排在下面。每一层要加上缩进。 52 | 53 | package com.edu.recursion; 54 | 55 | import java.io.File; 56 | import java.util.ArrayList; 57 | 58 | public class FileTree { 59 | private static int times; 60 | 61 | public static void main(String[] args) { 62 | FileTree fileTree1 = new FileTree(); 63 | File file = new File("D:\\项目"); 64 | fileTree1.deepList(file); 65 | } 66 | 67 | //递归的方法 68 | public void deepList(File file) { 69 | FileTree fileTree = new FileTree(); 70 | if(file.isFile() || 0 == file.list().length) { 71 | return; 72 | }else { 73 | File[] files = file.listFiles(); 74 | files = fileTree.sort(files); 75 | for(File file2 : files) { 76 | StringBuffer outputBuffer = new StringBuffer(); 77 | if(file2.isFile()) { 78 | outputBuffer.append(fileTree.getTabs(times)); 79 | outputBuffer.append(file2.getName()); 80 | }else { 81 | outputBuffer.append(fileTree.getTabs(times)); 82 | outputBuffer.append(file2.getName()); 83 | outputBuffer.append("\\"); 84 | } 85 | System.out.println(outputBuffer); 86 | if(file2.isDirectory()) { 87 | times++; 88 | deepList(file2); 89 | times--; 90 | } 91 | } 92 | } 93 | } 94 | 95 | //整理文件数据,使得目录排在文件之前 96 | private File[] sort(File[] files) { 97 | ArrayList sorted = new ArrayList<>(); 98 | //寻找所有的目录 99 | for(File file : files) { 100 | if(file.isDirectory()) { 101 | sorted.add(file); 102 | } 103 | } 104 | 105 | //寻找所有的文件 106 | for(File file : files) { 107 | if(file.isFile()) { 108 | sorted.add(file); 109 | } 110 | } 111 | return sorted.toArray(new File[files.length]); 112 | } 113 | //判断需要加多少Tab键 114 | private String getTabs(int times) { 115 | StringBuffer buffer = new StringBuffer(); 116 | for(int i=0;i参考答案 6 | 7 | * CallBack接口 8 | 9 | package com.edu.callback; 10 | 11 | public interface CallBackClass { 12 | public void callBack(String problem,Company company); 13 | } 14 | 15 | * 实现CallBack接口的Consumer类 16 | 17 | package com.edu.callback; 18 | 19 | public class Consumer implements CallBackClass{ 20 | private String name; 21 | public Consumer() { 22 | 23 | } 24 | public Consumer(String name) { 25 | this.name = name; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | public void solveProblem(String problem,Company company) { 32 | company.work(problem, this); 33 | } 34 | //回调函数 35 | @Override 36 | public void callBack(String problem,Company company) { 37 | // TODO Auto-generated method stub 38 | System.out.println(this.getName() + "将" + problem + "承包给" + company.getName() + "完成"); 39 | } 40 | 41 | } 42 | 43 | * Company类 44 | 45 | package com.edu.callback; 46 | 47 | public class Company { 48 | private String name; 49 | public Company() { 50 | 51 | } 52 | public Company(String name) { 53 | this.name = name; 54 | } 55 | public String getName() { 56 | return name; 57 | } 58 | //公司工作 59 | public void work(String problem,CallBackClass consumer) { 60 | consumer.callBack(problem, this); 61 | } 62 | } 63 | 64 | * 测试类 65 | 66 | package com.edu.callbackcompany; 67 | 68 | import org.junit.Test; 69 | 70 | public class TestCallBack { 71 | @Test 72 | public void test() { 73 | Consumer consumer = new Consumer("淘宝"); 74 | String problem = "商品推送项目"; 75 | Company company = new Company("外包公司"); 76 | consumer.solveProblem(problem, company); 77 | } 78 | } 79 | 80 | * 运行结果 81 | 82 | >淘宝将商品推送项目承包给外包公司完成 83 | -------------------------------------------------------------------------------- /4.1Object类.md: -------------------------------------------------------------------------------- 1 | # 4.1Object类 2 | 3 | ### 一、Object类的方法解析 4 | 5 | * 1、关于 Object 类的 equals 方法的特点 6 | 7 | * 1>自反性(reflexive):x.equals(x)应该返回 true 8 | 9 | * 2>对称性(symertic):x.equals(y)为 true,那么 y.equals(x)也为 true。 10 | 11 | * 3>传递性(transitive):若x.equals(y)为 true 且 y.equals(z)为 true,那么 x.equals(z)也应该为 true。 12 | 13 | * 4>一致性(conaistent):x.equals(y)的第一次调用为 true,那么 x.equals(y)的第二次、第三次、第 n 次调用也应该为 true,前提条件是在比较之间没有修改 x 也没有修改 y。 14 | 15 | * 5>对于非空引用 x,x.equals(null)返回 false。 16 | 17 | * 2、关于 Object 类的 hashCode()方法的特点: 18 | 19 | * 1>在 Java 应用的一次执行过程当中,对于同一个对象的 hashCode 方法的多次调用, 他们应该返回同样的值(前提是该对象的信息没有发生变化) 。 20 | 21 | * 2>对于两个对象来说,如果使用equals方法比较返回true,那么这两个对象的hashCode 值一定是相同的。 22 | 23 | * 3>对于两个对象来说,如果使用equals方法比较返回false,那么这两个对象的hashCode 值不要求一定不同(可以相同,可以不同) ,但是如果不同则可以提高应用的性能。 24 | 25 | * 4>对于Object类来说,不同的Object对象的hashCode值是不同的(Object类的hashCode 值表示的是对象的地址) 26 | 27 | **如果重写equals方法,那么也要重写hashCode方法,反之亦然。** 28 | 29 | 30 | ### 二、源代码分析 31 | ```java 32 | public boolean equals(Object obj) 33 | public String toString() 34 | public native int hashCode(); 35 | public final native Class getClass(); 36 | public final native void notify(); 37 | public final native void notifyAll(); 38 | public final native void wait(long timeout) throws InterruptedException; 39 | public final void wait(long timeout, int nanos) throws InterruptedException 40 | public final void wait() throws InterruptedException 41 | 42 | protected native Object clone() throws CloneNotSupportedException; 43 | protected void finalize() throws Throwable { } 44 | 45 | private static native void registerNatives(); 46 | ``` 47 | 48 | 49 | * 1、相等性的比较 50 | 51 | * 1>`==` 52 | 53 | * 对于原生数据类型来说,比较的是左右两边的值是否相等。 54 | 55 | * 对于引用类型来说,比较左右两边的引用是否指向同一个对象,或者说左右两边的引用地址是否相同。 56 | 57 | * 2>`equals` 58 | 59 | * 用于检测一个对象是否等于另外一个对象 60 | 61 | public boolean equals(Object obj) { 62 | return (this == obj); 63 | } 64 | 65 | * 2、java.lang.Object 类。java.lang 包在使用的时候无需显式导入,编译时由编译器自动帮助我们导入。 66 | 67 | * 3、API (Application Programming Interface),应用编程接口。 68 | 69 | * 4、当打印引用时,实际上会打印出引用所指对象的 toString()方法的返回值,因为每个类都直接或间接地继承自 Object,而 Object 类中定义了 toString(),因此每个类都有 toString()这个方法。 70 | ```java 71 | toString()方法 <=> getClass().getName() + "@" + Integer.toHexString(hashCode()); 72 | 73 | System.out.println(object.toString()); 74 | //输出:java.lang.Object@7852e922 75 | 76 | public String toString() { 77 | return getClass().getName() + "@" + Integer.toHexString(hashCode()); 78 | } 79 | ``` 80 | 81 | * 5、关于进制的表示:16 进制,逢 16 进一,16 进制的数字包括:0~9,A,B,C,D,E,F, 82 | 83 | ### 三、Object.java源代码 84 | ```java 85 | package java.lang; 86 | 87 | public class Object { 88 | 89 | private static native void registerNatives(); 90 | static { 91 | registerNatives(); 92 | } 93 | 94 | public final native Class getClass(); 95 | 96 | public native int hashCode(); 97 | 98 | public boolean equals(Object obj) { 99 | return (this == obj); 100 | } 101 | 102 | protected native Object clone() throws CloneNotSupportedException; 103 | 104 | public String toString() { 105 | return getClass().getName() + "@" + Integer.toHexString(hashCode()); 106 | } 107 | 108 | public final native void notify(); 109 | 110 | public final native void notifyAll(); 111 | 112 | public final native void wait(long timeout) throws InterruptedException; 113 | 114 | public final void wait(long timeout, int nanos) throws InterruptedException { 115 | if (timeout < 0) { 116 | throw new IllegalArgumentException("timeout value is negative"); 117 | } 118 | if (nanos < 0 || nanos > 999999) { 119 | throw new IllegalArgumentException("nanosecond timeout value out of range"); 120 | } 121 | if (nanos > 0) { 122 | timeout++; 123 | } 124 | wait(timeout); 125 | } 126 | 127 | public final void wait() throws InterruptedException { 128 | wait(0); 129 | } 130 | 131 | protected void finalize() throws Throwable { } 132 | } 133 | ``` 134 | 135 | 136 | -------------------------------------------------------------------------------- /4.3StringBuffer类.md: -------------------------------------------------------------------------------- 1 | # 4.3StringBuffer类 2 | 3 | ### 一、append()方法:在原字符串后最加字符串,并返回原字符串对象。 4 | 5 | * 1、append方法和`+`的使用 6 | 7 | package com.edu.test; 8 | 9 | public class Test3 { 10 | public static void main(String[] args) { 11 | StringBuffer buffer = new StringBuffer(); 12 | buffer = buffer.append("Hello").append(" World ").append(100).append(false); 13 | System.out.println(buffer);//输出:Hello World 100false 14 | 15 | String result = buffer.toString(); 16 | System.out.println(result);//输出:Hello World 100false 17 | 18 | String abc = "abc"; 19 | int a = 100; 20 | boolean b = true; 21 | String str = abc + a + b; 22 | System.out.println(str);//输出:abc100true 23 | 24 | int m = 100; 25 | int n = 200; 26 | System.out.println(m + n);//输出:300 27 | 28 | System.out.println(100 +200);//输出:300 29 | System.out.println("100" + 200);//输出:100200 30 | System.out.println("true" + false);//输出:truefalse 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /4.4包装类.md: -------------------------------------------------------------------------------- 1 | # 4.4包装类 2 | 3 | * 针对原生数据类型的包装,所有的包装类(8个)都位于`java.lang`包下。 4 | 5 | * 八个包装类: 6 | 7 | * Byte 8 | 9 | * Short 10 | 11 | * Integer 12 | 13 | * Long 14 | 15 | * Float 16 | 17 | * Double 18 | 19 | * Character 20 | 21 | * Boolean 22 | 23 | * 自动装箱、自动拆箱(大大方便了基本数据类型和他们包装类的使用) 24 | 25 | * 自动装箱:基本类型自动转为包装类(int -> Integer) 26 | 27 | * 自动拆箱:包装类自动转为基本类型(Integer -> int) 28 | 29 | * Integer类 30 | 31 | * Integer类有一个缓存,它会缓存介于`-128~127`之间的整数 32 | 33 | >eg 34 | 35 | package com.edu.test; 36 | 37 | import java.math.BigInteger; 38 | 39 | public class Test4 { 40 | @SuppressWarnings("unlikely-arg-type") 41 | public static void main(String[] args) { 42 | Integer integer1 = 127; 43 | Integer integer2 = 127; 44 | int int1 = 127; 45 | System.out.println(integer1 == integer2);//true 46 | System.out.println(integer1 == int1);//true 47 | 48 | Integer integer3 = 128; 49 | Integer integer4 = 128; 50 | int int2 = 128; 51 | System.out.println(integer3 == integer4);//false 52 | System.out.println(integer3 == int2);//true 53 | 54 | Integer integer5 = 10000; 55 | int int3 = 10000; 56 | System.out.println(integer5 == int3);//true 57 | 58 | BigInteger bigInteger = new BigInteger("10000"); 59 | System.out.println(integer5.equals(bigInteger));//false 60 | } 61 | } 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /5.1数组(Array).md: -------------------------------------------------------------------------------- 1 | # 5.1数组(Array) 2 | 3 | * 编译器不允许指定数组的大小(因为引用),必须用初始化表达式来初始化数组并连接到这个引用。 4 | 5 | ### 一、一维数组 6 | * 1、概念:相同类型数据的集合九叫做数组 7 | 8 | * 2、定义数组:`type[] 变量名 = new type[数组中元素的个数] ` 9 | 10 | >定义长度为10的数组 11 | ```java 12 | int[] a = new int[10]; 13 | int a[] = new int[10]; 14 | ``` 15 | 16 | * 数组中元素的索引是从0开始的。对于数组来说,最大的索引等于数组的长度减一。 17 | 18 | * 3、数组初始化: 19 | 20 | * 1>`type[] 变量名 = new type[]{逗号分隔的初始化列表}` 21 | 22 | >eg 23 | ```java 24 | int[] b = new int[]{1,2,3,4}; 25 | ``` 26 | 27 | * 2>`type[] 变量名 = {逗号分隔的初始化列表}` 28 | 29 | >eg 30 | ```java 31 | int[] b = {1,2,3,4}; 32 | ``` 33 | 34 | * 3>如果数组中的元素不是基本数据类型,那么这些元素必须被`new`。 35 | 36 | ```java 37 | Integer[] a = new Integer[6]; 38 | for(int i=0;i 79 | 80 | ```java 81 | int[] a = {1,2,3}; 82 | int[] b = {1,2,3}; 83 | System.out.println(a.equals(b)); 84 | ``` 85 | 86 | * `System.out.println(a.equals(b));`输出为false,因为equals()方法并未重写,仍是Object类中的equals()方法,所以比较a、b数组的首地址不同,故返回false。 87 | 88 | * `Person[] p = new Person[3];` 89 | 90 |
91 | 92 | ```java 93 | Person[] p = new Person[3]; 94 | p[0] = new Person(10); 95 | p[1] = new Person(20); 96 | p[2] = new Person(30); 97 | ``` 98 | 99 | ### 二、二维数组 100 | 101 | * 1、定义:二维数组是一种平面的二维结构,本质上是数组的数组。 102 | 103 | * 2、定义二维数组: 104 | ```java 105 | type[][] a = new type[2][3]; 106 | type a[][] = new type[2][3]; 107 | type []a[] = new type[2][3]; 108 | ``` 109 | * 3、定义不规则数组: 110 | 111 | 00 | 01 | 112 | :-- |:-- | 113 | 10 |11 |12 114 | 20 | 115 | 116 | ```java 117 | int[][] a = new int[3][]; 118 | a[0] = new int[2]; 119 | a[1] = new int[3]; 120 | a[2] = new int[1]; 121 | ``` 122 | * 4、初始化二维数组 123 | 124 | >eg 125 | 126 |
127 | 128 | 129 | ### 三、三维数组 130 | 131 | * 1、定义三维数组 132 | ```java 133 | type[][][] a = new type[2][3][4]; 134 | ``` 135 | * 2、三维数组的使用 136 | ```java 137 | int[][][] a = new int[2][3][4]; 138 | 139 | System.out.println(a instanceof int[][][]);//true 140 | System.out.println(a[0] instanceof int[][]);//true 141 | System.out.println(a[0][0] instanceof int[]);//true 142 | ``` 143 | ### 四、交换(swap方法) 144 | 145 | * 参数传递: 146 | 147 | * 原生数据类型:传数值 148 | 149 | * 引用类型:传地址(值) 150 | 151 | * 1、原生数据类型:(调用方法后无法交换) 152 | ```java 153 | package com.edu.test; 154 | 155 | public class Swap { 156 | public static void swap(int a,int b) { 157 | int temp = a; 158 | a = b; 159 | b = temp; 160 | } 161 | public static void main(String[] args) { 162 | int x = 3; 163 | int y = 4; 164 | Swap.swap(x, y); 165 | System.out.println("x=" + x + ";y=" + y);//输出:x=3;y=4 166 | } 167 | } 168 | ``` 169 | * 2、引用类型: 170 | ```java 171 | package com.edu.test; 172 | 173 | public class Swap { 174 | 175 | public static void swap(int[] i) { 176 | int temp = i[0]; 177 | i[0] = i[1]; 178 | i[1] = temp; 179 | } 180 | public static void main(String[] args) { 181 | int[] i = {1,2}; 182 | swap(i); 183 | System.out.println("i[0]=" + i[0] + ";i[1]=" + i[1]);//输出:i[0]=2;i[1]=1 184 | } 185 | } 186 | ``` 187 | ### 五、接口及多态在数组中的应用 188 | ```java 189 | package com.edu.test; 190 | 191 | public class ArrayTest { 192 | public static void main(String[] args) { 193 | I[] i = new I[2]; 194 | i[0] = new C(); 195 | i[1] = new C(); 196 | System.out.println("i[0]=" + i[0] + ";i[1]=" + i[1]); 197 | //输出:i[0]=com.edu.test.C@7852e922;i[1]=com.edu.test.C@4e25154f 198 | //等价于:int[] i = new I[]{new C(),new C()}; 199 | } 200 | } 201 | interface I{ 202 | 203 | } 204 | class C implements I{ 205 | 206 | } 207 | ``` 208 | * `I[] i = new I[2];`可以生成接口类型的数组,但不可生成接口类型的实例 209 | 210 | * `i[0] = new C();`接口类型的引用可以指向实现接口的实例 211 | 212 | ### 六、判断两数组内容是否一致:(与Array.equals(a,b)方法相同) 213 | ```java 214 | public static boolean isEquals(int[] a,int[] b) { 215 | if(a == null || b == null) 216 | return false; 217 | if(a.length != b.length) 218 | return false; 219 | for(int i=0;i 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
359412
14 | 15 | 16 | ### 一、冒泡排序 17 | 18 | public void bubbleSort(int[] array) { 19 | for(int i=0;i array[j+1]) { 22 | int temp = array[j]; 23 | array[j] = array[j+1]; 24 | array[j+1] = temp; 25 | } 26 | } 27 | } 28 | for(int i : array) { 29 | System.out.print(i + " "); 30 | } 31 | } 32 | 33 | ### 二、直接选择排序 34 | 35 | public void directSort(int[] array) { 36 | int index; 37 | for(int i=1;i array[index]) { 41 | index = j; 42 | } 43 | } 44 | int temp = array[array.length-i]; 45 | array[array.length-i] = array[index]; 46 | array[index] = temp; 47 | } 48 | for(int i : array) { 49 | System.out.print(i + " "); 50 | } 51 | } 52 | 53 | ### 三、反转排序 54 | 55 | public void reversalSort(int[] array) { 56 | for(int i=0;i<=array.length/2;i++) { 57 | int temp = array[i]; 58 | array[i] = array[array.length-i-1]; 59 | array[array.length-i-1] = temp; 60 | } 61 | for(int i : array) { 62 | System.out.print(i + " "); 63 | } 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /5.3集合简介.md: -------------------------------------------------------------------------------- 1 | # 5.3集合简介 2 | 3 | ### 一、集合 4 | 5 | * 集合中存放的依然是对象的引用而不是对象本身。 6 | 7 | * 集合的结构 8 | 9 |
10 | 11 | * Collection、Map、Set、List特点梳理 12 | 13 | * Collection:集合层次中的根接口,JDK没 有提供这个接口直接的实现类。 14 | 15 | * Set:**不能包含重复的元素**。SortedSet是 一个按照升序排列元素的Set。 16 | 17 | * List:是一个有序的集合,**可以包含重复的元素**。提供了按索引访问的方式。 18 | 19 | * Map:包含了key-value对。Map不能包含重复的key,value可以重复。SortedMap是一个按照升序排列key的Map。 20 | 21 | 22 | -------------------------------------------------------------------------------- /5.4集合之List.md: -------------------------------------------------------------------------------- 1 | # 5.4集合之List 2 | 3 | ### 一、List 4 | 5 | * 1、List接口: 6 | 7 | * List接口扩展了Collection并声明存储一系列 元素的类集的特性。使用一个基于零的下 标,元素可以通过它们在列表中的位置被 插入和访问。一个列表可以包含重复元素 8 | 9 | * 当类集 不能被修改时,其中的几种方法引发 UnsupportedOperation Exception异常。 10 | 11 | * 当一 个对象与另一个不兼容,例如当企图将一 个不兼容的对象加入一个类集中时,将产 生ClassCastException异常。 12 | 13 | * 2、向list接口的实现类中添加对象 14 | 15 | * 1>当向 ArrayList 添加一个对象时,实际上就是将该对象放置到了 ArrayList 底层所维护的数组当中; 16 | 17 | * 2>当向 LinkedList 中添加一个对象时,实际上 LinkedList 内部会生成一个 Entry 对象,该 Entry 对象的结构为: 18 | 19 | Entry { 20 | 21 | Entry previous; 22 | Object element; 23 | Entry next; 24 | } 25 | 26 | * 3>其中的 Object 类型的元素 element 就是我们向 LinkedList 中所添加的元素,然后 Entry 又构造好了向前与向后的引用 previous、next,最后将生成的这个 Entry 对象加入到了链表当中。换句话说,**LinkedList 中所维护的是一个个的 Entry 对象**。 27 | 28 | * 3、List中增加的方法 29 | 30 | * 1>add方法 31 | 32 | add(int, Object) 33 | addAll(int, Collection) 34 | 35 | * 基于Collection定义的add( )和addAll( )方 法,List增加了方法add(int, Object)和 addAll(int, Collection)。这些方法在指定的下标处插入元素。由Collection定义的 add(Object)和addAll(Collection)的语义也被 List改变了,以便它们在列表的尾部增加元素 36 | 37 | * 2>get() 38 | 39 | * 为了获得在指定位置存储的对象,可以用 对象的下标调用get( )方法。 40 | 41 | * 3>set() 42 | 43 | * 为了给类表中的一个元素赋值,可以调用set( )方法,指 定被改变的对象的下标。 44 | 45 | * 4>indexOf()、lastIndexOf() 46 | 47 | * 调用indexOf( )或 lastIndexOf( )可以得到一个对象的下标。 48 | 49 | * 5>subList( ) 50 | 51 | * 通过调用subList( )方法,可以获得列表的 一个指定了开始下标和结束下标的子列表。 subList( )方法使得列表处理十分方便。 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ### 二、ArrayList 62 | 63 | * 1、ArrayList的add()方法: 64 | 65 | * ArrayList 底层采用数组实现,当使用不带参数的构造方法生成 ArrayList 对象 时,实际上会在底层生成一个长度为 10 的 Object 类型数组 66 | 67 | * 如果增加的元素个数超过了 10 个,那么 ArrayList 底层会新生成一个数组,长 度为原数组的 1.5 倍+1,然后将原数组的内容复制到新数组当中,并且后续 增加的内容都会放到新数组当中。当新数组无法容纳增加的元素时,重复该过程。 68 | 69 | >eg: 70 | 71 | ArrayList list = new ArrayList(); 72 | list.add("hello"); 73 | list.add(new Integer(3)); 74 | 75 | * 2、ArrayList的remove()方法: 76 | 77 | * 对于 ArrayList 元素的删除操作,需要将被删除元素的后续元素向前移动,代价比较高。 78 | 79 | * 3、集合当中只能放置对象的引用,无法放置原生数据类型,我们需要使用原生数据类型的包装类才能加入到集合当中。 80 | 81 | >eg: 82 | 83 | List list = new ArrayList<>(); 84 | list.add(3); 85 | Integer integer = list.get(0); 86 | System.out.println(integer);//输出3 87 | 88 | ### 三、LinkedList 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | ### 四、ArrayList与LinkedList的比较分析 100 | 101 | * ArrayList 底层采用数组实现,LinkedList 底层采用双向链表实现。 102 | 103 | * 当执行插入或者删除操作时,采用 LinkedList 比较好。 104 | 105 | * 当执行搜索操作时,采用 ArrayList 比较好。 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /5.5集合之Map.md: -------------------------------------------------------------------------------- 1 | # 5.5集合之Map 2 | 3 | ### 一、Map(映射) 4 | 5 | * Map 的 keySet()方法会返回 key 的集合,因为 Map 的键是不能重复的, 因此 keySet()方法的返回类型是 Set; 6 | 7 | * 而 Map 的值是可以重复的,因此 values()方法 的返回类型是 Collection,可以容纳重复的元素。 8 | 9 | ### 二、Map.Entry接口 10 | 11 | * Map.Entry使得可以操作映射的输入。 12 | 13 | * Map接口的entrySet()方法,返回一个包含映射的集合(Set),这些集合元素的每一个都是一个Map.Entry对象。 14 | 15 | getKey() 返回key(键) 16 | getValue() 返回value(值) 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /5.6集合之Set.md: -------------------------------------------------------------------------------- 1 | # 5.6集合之Set 2 | 3 | ### 一、Set接口 4 | 5 | * 1、Set接口扩展了 Collection并说明了不允许复制元素的类集的特性。因此,如果试图将复制元素加到集合中时,add( )方法将返回false。它本身并没有定义任何附加的方法 6 | 7 | * 2、SortedSet接口: 8 | 9 | * 1>SortedSet接口扩展了Set并说明了按升序排列的集合的特性。 10 | 11 | * 当没有项包含在调用集合中时,其中的几种方法引发 NoSuchElementException异常。 12 | 13 | * 当对象与调 用集合中的元素不兼容时,引发 ClassCastException异常。 14 | 15 | * 如果试图使用null 对象,而集合不允许null时,引发 NullPointerException异常 16 | 17 | * 2>SortedSet定义了几种方法,使得对集合的处理更加方便。 18 | 19 | * first( )方法,可以获得集合中的第一个对象。 20 | 21 | * last( )方法,可以获得集合中的最后一个元素。 22 | 23 | * subSet( ) 方法,可以获得排序集合的一个指定了第一个和最后一个对象的子集合。 24 | 25 | * headSet( )方法,获得从集合的第一个元素开始的一个子集合。 26 | 27 | * tailSet( )方法,获得集合尾部的一个子集合。 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ### 二、HashSet 45 | 46 | * 当使用 HashSet 时,hashCode()方法就会得到调用,判断已经存储在集合中的对象的 hash code 值是否与增加的对象的 hash code 值一致: 47 | 48 | * 如果不一致,直接加进去; 49 | 50 | * 如果一致,再进行 equals 方法的比较,equals 方法如果返回 true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。 51 | 52 | package com.edu.test; 53 | 54 | import java.util.HashSet; 55 | 56 | public class TestSet { 57 | public static void main(String[] args) { 58 | HashSet hashSet1 = new HashSet(); 59 | //两者均可添加 60 | hashSet1.add(new People("zhangsan")); 61 | hashSet1.add(new People("lisi")); 62 | System.out.println(hashSet1);//输出:[People [name=zhangsan], People [name=lisi]] 63 | 64 | 65 | HashSet hashSet2 = new HashSet<>(); 66 | People people = new People("zahngsan"); 67 | //两者仅可添加一个 68 | hashSet2.add(people); 69 | hashSet2.add(people); 70 | System.out.println(hashSet2);//输出:[People [name=zahngsan]] 71 | 72 | 73 | HashSet hashSet3 = new HashSet<>(); 74 | //仅可添加一个,因为String类重写了equals方法(字符串池String Pool) 75 | hashSet3.add(new String("aa")); 76 | hashSet3.add(new String("aa")); 77 | System.out.println(hashSet3);//输出:[aa] 78 | } 79 | } 80 | 81 | class People{ 82 | private String name; 83 | public People() { 84 | 85 | } 86 | public People(String name) { 87 | this.name = name; 88 | } 89 | @Override 90 | public String toString() { 91 | return "People [name=" + name + "]"; 92 | } 93 | } 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /5.7集合总结.md: -------------------------------------------------------------------------------- 1 | # 5.7集合总结 2 | 3 | ### 一、通过迭代函数访问类集 4 | 5 | * 1、使用iterator可以循环通过类集中的元素(显示每一个元素、遍历元素) 6 | 7 | * iterator是一个实现Iterator(或者实现 ListIterator)接口的对象。 8 | 9 | * Iterator可以完成循环通过类集,从而获得或删除元素。 10 | 11 | * ListIterator扩展Iterator,允许双向遍历列表, 并可以修改单元。 12 | 13 | * 2、使用迭代函数 14 | 15 | * 在通过迭代函数访问类集之前,必须得到一个迭代函数。每一个Collection类都提供一个iterator( ) 函数,该函数返回一个对类集头的迭代函数。通过使用这个迭代函数对象,可以访问类集中的每一个元素,一次一个元素。通常,使用迭代函数循环通过类集的内容,步骤如下 : 16 | 17 | * 1. 通过调用类集的iterator( )方法获得对类集头的迭代函数。 18 | 19 | * 2. 建立一个调用hasNext( )方法的循环,只要 hasNext( )返回true,就进行循环迭代。 20 | 21 | * 3. 在循环内部,通过调用next( )方法来得到每一个元素 22 | 23 | * 3、通过ListIterator获得迭代函数 24 | 25 | * 对于执行List的类集,也可以通过调用 ListIterator 来获得迭代函数。 26 | 27 | * list迭代函数提供了前向或后向访问类集的能力,并可让你修改元素。 28 | 29 | * 4、迭代器工作的原理 30 | 31 |
32 | 33 | ### 二、HashSet与HashMap 34 | 35 | * 1、HashSet 底层是使用 HashMap 实现的。当使用 add 方法将对象添加到 Set 当中时, 实际上是将该对象作为底层所维护的 Map 对象的 key,而 value 则都是同一个 Object 对象(该对象我们用不上); 36 | 37 | * 2、HashMap 底层维护一个数组,我们向 HashMap 中所放置的对象实际上是存储在该数 组当中; 38 | 39 | * 3、当向 HashMap 中 put 一对键值时,它会根据 key 的 hashCode 值计算出一个位置, 该位置就是此对象准备往数组中存放的位置。 40 | 41 | * 4、如果该位置没有对象存在,就将此对象直接放进数组当中;如果该位置已经有对象 存在了,则顺着此存在的对象的链开始寻找(Entry 类有一个 Entry 类型的 next 成员变量,指向了该对象的下一个对象), 如果此链上有对象的话,再去使用 equals 方法进行比较,如果对此链上的某个对象的 equals 方法比较为 false,则将该对象放到数组当中,然后将数组中该位置以前存在的那个对象链接到此对象的后面。 42 | 43 | * 5、HashMap 的内存实现布局: 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /6.1泛型.md: -------------------------------------------------------------------------------- 1 | # 6.1泛型.md 2 | 3 | ### 一、泛型(Generics) 4 | * 1、通过引入泛型,我们将获得编译时类型的安全和运行时更小的抛出 ClassCastException 的可能。 5 | 6 | * 2、可以声明一个集合接收返回对象的类型。 7 | 8 | * 3、不使用泛型时: 9 | 10 | * 类别定义时的逻辑完全一样,只是里边成员变量的类型不同。 11 | 12 | * 若需要多个相似的类,需要定义多个文件,不同的只是变量的类别,而逻辑是完全一样的。 13 | 14 | * 4、若使用泛型,只要代码在编译时没有出现警告,就不会遇到运行时 ClassCastException 15 | 16 | * 5、所谓泛型:就是变量类型的参数化。 17 | 18 | * 泛型类 19 | 20 | * 泛型方法 21 | 22 | * 泛型接口 23 | 24 | ### 二、限制泛型可用类型 25 | 26 | * 1、在定义泛型时,预设可以使用任何的类型来实例化泛型类型中的类型。 27 | 28 | * 2、若想要限制使用泛型类别是,只能用某个**特定的类型或者其子类型**才能实例化该类型时,可以在定义类型时,使用 extends 关键字指定这个类型 **必须是继承某个类、或者实现某个接口。** 29 | 30 | * 3、当没有指定泛型继承的类型或接口时,默认使用 `T extends Object`,所以默认的情况下,任何类型都可以作为参数传入。 31 | 32 | >eg: 33 | 34 | 泛型类名称 向下限制 35 | 36 | 泛型类名称 向上限制 37 | 38 | ### 三、类型通配声明 39 | 40 | >eg: 41 | 42 | public class GenericFoo{ 43 | private T foo; 44 | 45 | public T getFoo() { 46 | return foo; 47 | } 48 | 49 | public void setFoo(T foo) { 50 | this.foo = foo; 51 | } 52 | } 53 | 54 | * 1、GenericFoo foo1 = null;//foo1只接受GenericFoo的实例 55 | 56 | * 2、GenericFoo foo2 = null;//foo2只接受GenericFoo的实例 57 | 58 | * 3、可以使用 `?` (通配符),并使用 `extends` 关键字限定类型持有者的形态: 59 | 60 | >eg: 61 | 62 | GenericFoo foo = null; 63 | foo = new GenericFoo(); 64 | foo = new GenericFoo(); 65 | 66 | * 实例化类型持有者时,它必须是实现List的类别或其子类别 67 | 68 | * 4、使用`` 或 `` 的声明方式,意味着只能通过该名称来: 69 | 70 | * 1>取得所参考实例的信息 71 | 72 | * 2>移除某些信息 73 | 74 | * 3>不可增加它的信息,因为只知道当中放置的是 `SomeClass` 的子类,但不确定是什么类的实例,编译器不让加入信息。(理由:若可以加入信息的话,那么使用者就得记得取回的实例是什么类型,然后转换为原来的类型方可进行操作,这就失去了使用泛型的意义) 75 | 76 | * 5、在使用集合时用泛型。 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /6.2枚举类型.md: -------------------------------------------------------------------------------- 1 | # 6.2枚举类型 2 | 3 | ### 一、枚举类型 4 | 5 | * 1、使用`enum` 关键字定义 6 | 7 | >eg: 8 | 9 | package com.edu.test; 10 | 11 | public class TestEnum { 12 | Color myColor = Color.Red; 13 | } 14 | enum Color{ 15 | Red,Write,Blue 16 | } 17 | 18 | * 2、枚举类型还提供了两个有用的静态方法`values()`和`valueOf()`,可以很方便的使用它们。 19 | 20 | >eg: 21 | 22 | package com.edu.test; 23 | 24 | public class TestEnum { 25 | public static void main(String[] args) { 26 | for(Color color : Color.values()) { 27 | System.out.println(color); 28 | } 29 | } 30 | } 31 | enum Color{ 32 | Red,Write,Blue 33 | } 34 | 35 | * 3、自定义的每个枚举类型均继承自`java.lang.Enum`类,枚举中的每个成员默认都是`public`、`static`、`final`的,可以通过类型名称直接使用它们。 36 | 37 | * 4、**每个枚举的成员其实就是自定义的枚举类型的一個实例(Instance)**。换句话说, 当定义了一个枚举类型后,在编译时刻就能确定该枚举类型有几个实例,分别是什么。在运行期间我们无法再使用该枚举类型创建新的实例了,这些实例在编译期间就已经完全确定下来了。 38 | 39 | * 5、定义枚举类型时本质上就是定义一个类别,只不过很多细节由编译器帮我们完成了,所以某些程度上,`enum`关键字的作用就像是`class`、`interface`。 40 | 41 | >eg1: 42 | 43 | package com.edu.test; 44 | 45 | public enum Coin { 46 | aa("aa"),penny("hello"),dime("world"); 47 | private String value; 48 | 49 | Coin(String value){ 50 | this.value = value; 51 | } 52 | 53 | public String getValue() { 54 | return value; 55 | } 56 | 57 | public static void main(String[] args) { 58 | Coin coin = Coin.penny; 59 | System.out.println(coin.getValue()); 60 | } 61 | } 62 | 63 | >eg2: 64 | 65 | package com.edu.test; 66 | 67 | public class TestEnum { 68 | Color myColor = Color.Red; 69 | public static void main(String[] args) { 70 | for(Color color : Color.values()) { 71 | System.out.println(color); 72 | } 73 | } 74 | } 75 | enum Color{ 76 | Red,Write,Blue 77 | } 78 | 79 | ### 二、EnumSet 80 | 81 | * EnumSet 的名称说明了其作用,可以协助建立枚举值的集合,它提供了一系列的静态方法,可以指定不同的集合建立方式。 82 | 83 | ### 三、EnumMap 84 | 85 | * 1、EnumMap 是个专为枚举类型设计到类别,方便使用枚举类型及Map对象。 86 | 87 | * 2、EnumMap 与单纯的使用 HashMap 比较起来的差别是:**EnumMap 将根据枚举的顺序来维护对象的排列顺序**。 88 | 89 | >eg: 90 | 91 | package com.edu.test; 92 | 93 | import java.util.EnumMap; 94 | import java.util.Map; 95 | 96 | 97 | public class TestEnumMap { 98 | public static void main(String[] args) { 99 | Map map = new EnumMap<>(Action.class); 100 | map.put(Action.TURN_RIGHT, "向右转"); 101 | map.put(Action.TURN_LEFT, "向左转"); 102 | map.put(Action.SHOOT, "射击"); 103 | 104 | for(Action action : Action.values()) { 105 | System.out.println(map.get(action)); 106 | } 107 | } 108 | } 109 | enum Action{ 110 | TURN_LEFT, 111 | TURN_RIGHT, 112 | SHOOT 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 | -------------------------------------------------------------------------------- /6.3静态导入.md: -------------------------------------------------------------------------------- 1 | # 6.3静态导入 2 | 3 | >eg: 4 | 5 | package com.edu.test; 6 | 7 | public class Common { 8 | 9 | public static final Integer AGE = 0; 10 | 11 | public static void output() { 12 | System.out.println(AGE); 13 | } 14 | } 15 | 16 | ------------------------------------------- 17 | 18 | package com.edu.test; 19 | 20 | import static com.edu.test.Common.AGE; 21 | import static com.edu.test.Common.output; 22 | 23 | public class TestStatic { 24 | public static void main(String[] args) { 25 | output(); 26 | System.out.println(AGE); 27 | } 28 | } 29 | 30 | 31 | * 1、静态导入: 32 | 33 | * 1>导入静态成员变量: 34 | 35 | import static com.edu.test.Common.AGE; 36 | 37 | * 表示导入`com.edu.test`这个包下的`Common`类的`AGE`静态成员变量。 38 | 39 | * 2>导入静态方法: 40 | 41 | import static com.edu.test.Common.output; 42 | 43 | * 表示导入`com.edu.test`这个包下的`Common`类的`output`静态方法。 44 | 45 | * 注:**使用 `import static` 时,要一直导入到类中的静态成员变量(或静态方法)** 46 | 47 | * 2、使用: 48 | 49 | * 1>要使用静态成员(方法和变量),必须给出提供这个静态成员的类。 50 | 51 | * 2>使用静态导入可以使被导入类的的所有静态本变量和静态方法在当前类直接可见。 52 | 53 | * 3>使用这些静态成员无需给出他们的类名: 54 | 55 | >eg: 56 | 57 | output(); 58 | System.out.println(AGE); 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 | -------------------------------------------------------------------------------- /6.5JAVA注解(Annotation).md: -------------------------------------------------------------------------------- 1 | # 6.5JAVA注解(Annotation) 2 | 3 | ### 一、Annotation 的由来 4 | 5 | * 1、Annotation 允许开发者定义、使用自己的 annotation 类型。此功能由一个定义 annotation 类型的语法和一个描述 annotation 声明的语法。读取 annotation 的 API ,一个使用 annotation 修饰的 class 文件,一个 annotation 处理工具(apt)组成。 6 | 7 | * 2、Annotation 工作方式 8 | 9 | * annotation 并不直接影响代码语义,但是他能够工作的方式被看做类似程序的工具或者类库,它会反过来对正在运行的程序语义有所影响。 10 | 11 | * annotation 可以从**源文件**、**`Class`文件**、或者**在运行时反射**的多种方式被读取。 12 | 13 | ### 二、Annotation 的使用 14 | 15 | * 1、`Override` 注解:表示 子类要重写(override)父类的对应方法。 16 | 17 | >eg: 18 | 19 | @Override 20 | 21 | * `java.lang.Override`是个 Marker annotation(标识性的注解),Annotation 名称本身即表示了要给工具程序提供的信息。 22 | 23 | * 2、`Deprecated` 注解:表示 方法是不建议被使用的。 24 | 25 | >eg: 26 | 27 | @Deprecated 28 | 29 | * 对编译程序说明某个方法已经不建议使用,即该方法是过时了的。`java.lang.Deprecated`也个 Marker annotation(标识性的注解)。 30 | 31 | * 3、`SuppressWarnings` 注解:表示抑制警告。 32 | 33 | >eg: 34 | 35 | @SuppressWarnings 36 | 37 | * 对编译程序说明某个方法中若有警告讯息,则加以抑制。 38 | 39 | * 4、自定义 Annotation 类型 40 | 41 | * 定义 Marker annotation(标识性的注解),也就是 Annotation 名称本身即提供信息。 42 | 43 | * 对于程序分析工具来说,主要检查是否有 Marker annotation(标识性的注解)的出现,并作出对应的动作。 44 | 45 | * :当注解中的属性名为 value 时,对其赋值时可以不指定属性的名称而直接写上属性值即可; 46 | 47 | * 除了 value 以外的其他值都需要使用 name=value 这种赋值方式,即明确指定给谁赋值。 48 | 49 | * value 成员设定默认值,用 `default` 关键字。 50 | 51 | >eg: 52 | 53 | String 54 | 55 | * 当我们使用 `@interface` 关键字定义一个注解时,该注解隐含地继承了 `java.lang.annotation.Annotation` 接口; 56 | 57 | * 如果我们定义了一个接口,并且让该接口继承自 Annotation,那么我们所定义的接口依然还是接口而不是注解;Annotation 本身是接口而不是注解。可以与 Enum 类比。 58 | 59 | * 在定义 Annotation 型态时,不能继承其他的 Annotation 型态或是接口。 60 | 61 | * 定义 Annotation 型态时,也可以使用包来管理类别,方式类同于类的导入功能。 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 | -------------------------------------------------------------------------------- /6.6通过JUnit深入理解反射与注解的使用方式和与场景.md: -------------------------------------------------------------------------------- 1 | # 6.6通过JUnit深入理解反射与注解的使用方式和与场景 2 | 3 | ### JUnit 认识(单元测试框架) 4 | 5 | * 1、JUnit 的版本(经典) 6 | 7 | * JUnit(3.8):完全基于反射来完成。 8 | 9 | * JUnit(4.X):基于反射和注解来完成。 10 | 11 | * :Keep the bar green to keep the code clean. (条绿即无错,条红即有错)。 12 | 13 | >eg:(基于反射 3.8)方法名必须以`test`开头。 14 | 15 | package com.edu.junit; 16 | 17 | import junit.framework.TestCase; 18 | 19 | public class TestJUnit extends TestCase{ 20 | public void testAdd() { 21 | System.out.println("Hello Wrold!"); 22 | } 23 | } 24 | 25 | >eg:(基于反射加注解 4.X) 26 | 27 | package com.edu.junit; 28 | 29 | import org.junit.Test; 30 | 31 | public class TestJUnit4 { 32 | @Test 33 | public void hello() { 34 | System.out.println("Hello!"); 35 | } 36 | } 37 | 38 | * 2、没有反射,很多框架就不存在了。 (No Reflection,No most frameworks) 39 | 40 | * 3、JUnit4 的执行流程: 41 | 42 | * 首先获得待测试类所对应的 Class 对象。 43 | 44 | * 然后通过该 Class 对象获得当前类中所有 public 方法所对应的 Method 数组。 45 | 46 | * 遍历该 Method 数组,取得每一个 Method 对象 47 | 48 | * 调用每个 Method 对象的 isAnnotationPresent(Test.class)方法,判断该方法是否被 Test 注解所修饰。 49 | 50 | * 如果该方法返回 true,那么调用 method.invoke()方法去执行该方法,否则不执行。 51 | 52 | 53 | * 4、单元测试不是为了证明你是对的,而是证明你没有错误。 54 | 55 | * 5、 Writing Secure Code(编写安全的代码):Input is evil(用户的输入是邪恶的)。 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /6.7异常(Exception).md: -------------------------------------------------------------------------------- 1 | # 6.7异常(Exception) 2 | 3 | ### 一、异常(Exception)`java.lang.Exception` 4 | 5 | * 1、Java 中的异常分为两大类: 6 | 7 | * Checked exception (非 Runtime Exception) 8 | 9 | * Unchecked exception(Runtime Exception) 10 | 11 | * 2、Java 中所有的异常类都会直接或间接地继承自 Exception。 12 | 13 | * 3、运行时异常(Runtime Exception) 14 | 15 | * RuntimeException 类也是直接继承自 Exception 类; 16 | 17 | * Java 中所有的运行时异常都会直接或间接地继承自 RuntimeException 类。 18 | 19 | * 4、非运行时异常 20 | 21 | * Java 中凡是继承自 Exception 而不是继承自 RuntimeException 的类都是非运行时异常。 22 | 23 | 24 |
25 | 26 | ### 二、异常处理 27 | 28 | * 1、异常处理的一般结构是: 29 | 30 | * 无论程序是否出现异常,finally 块中的代码都是会被执行的。 31 | 32 | * try...catch 33 | ```java 34 | try { 35 | 36 | } catch(Exception e) { 37 | 38 | } 39 | ``` 40 | 41 | * try...catch...finally 42 | ```java 43 | try { 44 | 45 | } catch(Exception e) { 46 | 47 | } finally { 48 | 49 | } 50 | ``` 51 | 52 | * try...finally 53 | ```java 54 | try { 55 | 56 | } finally { 57 | 58 | } 59 | ``` 60 | * >eg: 61 | ```java 62 | try{ 63 | …… 64 | }catch(ExcptionType1 ex1){ 65 | …… 66 | }catch(ExcptionType2 ex2){ 67 | …… 68 | }catch(ExcptionType3 ex3){ 69 | …… 70 | } 71 | ``` 72 | * 抛出异常后,异常处理机制将寻找第一个与异常类型相同的catch子句;每个cache子句完成后自动退出。 73 | 74 | * 2、对于非运行时异常(checked exception),必须要对其进行处理,处理方式有两种: 75 | 76 | * 第一种是使用 try.. catch…finally 进行捕获; 77 | 78 | * 第二种是在调用该会产生异常的方法所在 的方法声明 throws Exception 79 | 80 | * 3、对于运行时异常(runtime exception),我们可以不对其进行处理,也可以对其进行处理。推荐不对其进行处理。 81 | 82 | * 4、NullPointerException 是空指针异常,出现该异常的原因在于某个引用为 null,但你却调用了它的某个方法。这时就会出现该异常。 83 | 84 | ### 三、自定义异常 85 | 86 | * 1、所谓自定义异常,通常就是定义了一个继承自 Exception 类的子类,那么这个类就是一个自定义异常类。通常情况下,我们都会直接继承自 Exception 类,一般不会继承某个运行时的异常类。 87 | 88 | * 构造方法: 89 | ```java 90 | super(); 91 | super(String str); 92 | ``` 93 | 94 | ### 四、异常总结 95 | 96 | * 1、使用多个 catch 块来捕获异常,这时需要将父类型的 catch 块放到子类型的 catch 块之后,这样才能保证后续的 catch 可能被执行,否则子类型的 catch 将永远无法到达,Java 编译器会报编译错误;如果多个 catch 块的异常类型是独立的 (MyException, MyException2), 那么谁前谁后都是可以的。 97 | 98 | * 2、如果 try 块中存在 return 语句,那么首先也需要将 finally 块中的代码执行完毕,然后方法再返回。 99 | 100 | * 3、如果 try 块中存在 `System.exit(0)` 语句,那么就不会执行 finally 块中的代码,因为 `System.exit(0)`会终止当前运行的 Java 虚拟机,程序会在虚拟机终止前结束执行。 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 | -------------------------------------------------------------------------------- /6.8内部类.md: -------------------------------------------------------------------------------- 1 | # 6.8内部类 2 | 3 | * 内部类:组相关类,从而减少名称空间混乱。定义在小于一个包的范围内。 4 | 5 | * 内部类可以在另一个类中定义,在方法内部,甚至作为表达式的一部分。 6 | 7 | * 内部类有四种类型: 8 | 9 | * 静态内部类(也称为嵌套类) 10 | 11 | * 成员内部类 12 | 13 | * 局部内部类 14 | 15 | * 匿名内部类 16 | 17 | ### 一、静态内部类(也称为嵌套类): static inner classes (also called nested classes) 18 | 19 | * 1、最简单的内部类,命名不能与外部类同名,定义在一个封闭的类中,有 static 修饰符 20 | 21 | * 2、编译是完全独立的。生成 class 文件的名由**外部类名加`$`再加内部类名** 22 | 23 | * 3、只能访问外部类的静态成员和静态方法,**包括私有的静态成员** 24 | 25 | * 4、生成静态内部类对象的方式为: 26 | 27 | OuterClass.InnerClass inner = new OuterClass.InnerClass(); 28 | 29 | ### 二、成员内部类(member inner class) : 30 | 31 | * 1、定义在一个封闭的类中,而不使用 static 修饰符 32 | 33 | * 2、编译同静态内部类的编译方法(编译是完全独立的。生成 class 文件的名由**外部类名加`$`再加内部类名**) 34 | 35 | * 3、可以访问外部类的所有成员(静态的与非静态的方法与成员变量) 36 | 37 | * 4、生成成员内部类的实例: 38 | 39 | * 1>在外部类外(其他类中) 40 | 41 | OuterClass.InnerClass inner = new OuterClass().new InnerClass(); 42 | 43 | * 2>在外部类中: 44 | 45 | Inner inner = this.new Inner(); 46 | 47 | * 5、在内部类中访问外部类的成员: 48 | 49 | OuterClass.this.member; 50 | 51 | ### 三、局部内部类(Local Inner Class) 52 | 53 | * 1、定义在方法当中,只能访问方法中声明的 final 类型的变量。 54 | 55 | * 2、在方法的范围内定义,甚至在方法中更小的块。最不常用的内类形式。 56 | 57 | * 3、编译同静态内部类的编译方法(编译是完全独立的。生成 class 文件的名由**外部类名加`$`再加内部类名**) 58 | 59 | * 4、就像局部变量一样,不能被声明为 public, protected, private, static(公共的、受保护的、私有的和静态的)。只能访问最终的局部变量。 60 | 61 | ### 四、匿名内部类(Anonymous Inner Class) 62 | 63 | * 1、作为方法的参数。 64 | 65 | * 2、编译是独立完成的,生成 class 文件的名由**外部类名加`$`再加数字(1、2、3)** 66 | 67 | * 3、没有类名、没有关键字、没有关键字扩展和实现、没有构造方法。 68 | 69 | * 4、匿名内部类会隐式地继承一个父类或实现一个接口。 70 | 71 | >eg: 72 | 73 | package com.edu.innerclass; 74 | 75 | import java.awt.event.ActionEvent; 76 | import java.awt.event.ActionListener; 77 | import java.util.Date; 78 | 79 | import javax.swing.JButton; 80 | 81 | public class InnerClass { 82 | public static void main(String[] args) { 83 | 84 | new JButton("click me!").addActionListener(new ActionListener() { 85 | @Override 86 | public void actionPerformed(ActionEvent e) { 87 | System.out.println("welcome!"); 88 | } 89 | }); 90 | 91 | String str = new InnerClass().get(new Date() { 92 | public String toLocaleString() { 93 | return "Hello World!"; 94 | } 95 | }); 96 | System.out.println(str); 97 | } 98 | 99 | private String get(Date date) { 100 | return date.toString(); 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /7.1图形化界面GUI.md: -------------------------------------------------------------------------------- 1 | # 7.1图形化界面GUI(Graphical User Interface) 2 | 3 | ### 一、图形用户界面 4 | 5 | * 1、可分为两类: 6 | 7 | * 1> AWT(Abstract Window Toolkit),抽象窗口工具集,第一代的 Java GUI 组件,是重量级的。 8 | 9 | * 2> Swing,不依赖于底层细节,轻量级的组件。 10 | 11 | * 2、GUI(Graphical User Interface),图形用户界面。 12 | 13 | ### 五、事件 14 | 15 | * 1、事件:描述发生了什么的对象 16 | 17 | * 2、事件处理器:接收事件、解释事件并处理用户交互的方法 18 | 19 | * 3、事件源:事件的产生器 20 | 21 | * 9、当单击一个按钮时就会产生一个事件(ActionEvent), 然后检查是否有与该按钮关联的事件处理器(实际上就是一个方法),如果没有,那么什么都不执行;如果有,就会将该事件传递给与该按钮关联的事件处理器方法,作为该方法的参数,之后该事件处理器方法就会自动得到调用,并且该方法可以使用传递过来的 ActionEvent 对象, 进而获得事件发生时与该事件及事件源相关联的那些信息。 22 | 23 | * 10、Java 中的组件若想添加事件处理器,都会使用形如 `addXxxListener` 的方法来添加。 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /8.1IO(输入输出).md: -------------------------------------------------------------------------------- 1 | # 8.1 IO(输入输出) 2 | 3 | ### 一、File 类 4 | 5 | * 1、File 类简介 6 | 7 | * 一个File类的对象,表示了磁盘上的文件或目录 8 | 9 | * File类提供了与平台无关的方法来对磁盘上的文件或目录进行操作 10 | 11 | * File类直接处理文件和文件系统。 12 | 13 | * File类没有指定信息怎样从文件读取或向文件存储 14 | 15 | * File类描述了文件本身的属性 16 | 17 | * File对象用来获取或处理与磁盘文件相关的信息,例如权限,时间,日期和目录路径 18 | 19 | * File类还可以浏览子目录层次结构 20 | 21 | * java.io包中的File类提供了与具体平台无关的方 式来描述目录和文件对象的属性功能。其中包含大量的方法可用来获取路径、目录和文件的相关信息,并对它们进行创建、删除、改名等管理工作。因为不同的系统平台,对文件路径的描述不尽相同。为做到平台无关,在Java语言中,使用 抽象路径等概念。Java自动进行不同系统平台的文件路径描述与抽象文件路径之间的转换。 22 | 23 | * File类的直接父类是Object类。 24 | 25 | * 2、File 类的构造方法 26 | 27 | * File(String directoryPath) 28 | 29 | * File(String directoryPath, String filename) 30 | 31 | * File(File dirObj, String filename) 32 | 33 | * File(URI uri) 34 | 35 | * 注解:directoryPath是文件的路径名, filename 是文件名,dirObj 是一个指定目录的File 对象 36 | 37 | * 3、目录管理 – 目录操作的主要方法为: 38 | 39 | * public boolean mkdir() 根据抽象路径名创建目录。 40 | 41 | * public String[] list() 返回抽象路径名表示路径中 的文件名和目录名。 42 | 43 | * 4、文件管理 44 | 45 | * 在进行文件操作时,常需要知道一个关于文件的信息。 Jave的File类提供了方法来操纵文件和获得一个文件的信息。另外,File类还可以对目录和文件进行删除、 属性修改等管理工作。 46 | 47 | * File 类存在允许验证一个简单文 件对象属性的很多方法,但是没有相应的方法来改变这些属性。 48 | 49 | * 5、File 类中常用的方法 50 | 51 | * String getName() 52 | 53 | * String getPath() 54 | 55 | * String getAbsolutePath() 56 | 57 | * String getParent() 58 | 59 | * boolean renameTo( File newName) 60 | 61 | * long length() 62 | 63 | * boolean delete() 64 | 65 | * boolean mkdir() 66 | 67 | * String[] list() 68 | 69 | * boolean exists() 70 | 71 | * boolean canWrite() 72 | 73 | * boolean canRead() 74 | 75 | * boolean isFile() 76 | 77 | * boolean isDirectory() 78 | 79 | ### 二、使用 FilenameFilter 接口 80 | 81 | * 1、希望能够限制由 `list( )`方法返回的文件数目, 使它仅返回那些与一定的文件名方式或者过滤 (filter)相匹配的文件。为达到这样的目的,必须使用list( )的第二种形式 (方法重载) 82 | 83 | String[ ] list(FilenameFilter FFObj) 84 | 85 | * 该形式中,FFObj是一个实现 FilenameFilter 接口的类的对象 86 | 87 | * 2、FilenameFilter仅定义了一个方法,`accept( )`。 该方法被列表中的每个文件调用一次。它的通常形式如下: 88 | 89 | boolean accept(File directory, String filename) 90 | 91 | * 当被directory 指定的目录中的文件(也就是说, 那些与filename 参数匹配的文件)包含在列表中时,accept( )方法返回true ,当这些文件没有包括在列表中时,accept( )返回false 92 | 93 | * 3、` listFiles()`方法 94 | 95 | * `File[] listFiles()` 96 | 97 | * 返回所有的文件,与 list( )方法工作方式一样。 98 | 99 | * `File[] listFiles(FilenameFilter FFObj)` 100 | 101 | * 返回满足指定FilenameFilter接口的文件,与 list( )方法工作方式一样。 102 | 103 | * `File[] listFiles(FileFilter FObj)` 104 | 105 | * 返回满足指定 FileFilter的路径名的文件。FileFilter只定义了一个 accept( )方法,该方法被列表中的每个文件调用一次。它的通常形式如下: 106 | 107 | boolean accept(File path) 108 | 109 | * 如果文件被包括在列表中(即与path参数 匹配的文件),accept( )方法返回true, 如果不被包括,则返回false。 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /9.1序列化.md: -------------------------------------------------------------------------------- 1 | # 9.1序列化 2 | 3 | * 1、一个类若想被序列化,则需要实现 `java.io.Serializable` 接口,该接口中没有定义任何方法,是一个标识性接口(Marker Interface),当一个类实现了该接口,就表示这个类的对象是可以序列化的。 4 | 5 | * 2、在序列化时,static 变量是无法序列化的;如果 A 包含了对 B 的引用,那么在序列化 A 的时候也会将 B 一并地序列化;如果此时 A 可以序列化,B 无法序列化,那么当序列化 A 的时候就会发生异常,这时就需要将对 B 的引用设为 transient,该关键字表示变量不会被序列化。 6 | 7 | private void writeObject(java.io.ObjectOutputStream out) throws IOException { 8 | out.writeObject(age); 9 | out.writeUTF(name); 10 | System.out.println("write Object"); 11 | } 12 | private void readObject(java.io.ObjectInputStream in) throws IOException { 13 | age = in.readInt(); 14 | name = in.readUTF(); 15 | System.out.println("read Object"); 16 | } 17 | 18 | * 3、当我们在一个待序列化/反序列化的类中实现了以上两个 private 方法(方法声明要 与上面的保持完全的一致),那么就允许我们以更加底层、更加细粒度的方式控制序列化/反序列化的过程。 19 | 20 | * 4、序列化:是把一个对象状态写入一个字节流的过程。 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 | -------------------------------------------------------------------------------- /9.2线程.md: -------------------------------------------------------------------------------- 1 | # 9.2线程 2 | 3 | ### 一、线程 4 | 5 | * 1、Java 中如果我们自己没有产生线程,那么系统就会给我们产生一个线程(主线程, main 方法就在主线程上运行),我们的程序都是由线程来执行的。 6 | 7 | * 2、进程:执行中的程序(程序是静态的概念,进程是动态的概念)。 8 | 9 | * 3、线程的实现有两种方式: 10 | 11 | * 第一种方式是继承 Thread 类,然后重写 run 方法; 12 | 13 | * 第二种 是实现 Runnable 接口,然后实现其 run 方法。 14 | 15 | * 4、将我们希望线程执行的代码放到 run 方法中,然后通过 start 方法来启动线程,**start 方法首先为线程的执行准备好系统资源,然后再去调用 run 方法**。当某个类继承了 Thread 类之后,该类就叫做一个线程类。 16 | 17 | * 5、一个进程至少要包含一个线程。 18 | 19 | * 6、对于单核 CPU 来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来 看,多个线程在同时执行(宏观并行) 。 20 | 21 | * 7、对于双核或双核以上的 CPU 来说,可以真正做到微观并行。 22 | 23 | * 8、线程 24 | 25 | * 1>Thread 类也实现了 Runnable 接口,因此实现了 Runnable 接口中的 run 方法; 26 | 27 | * 2>当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下 形式:Thread-number,该 number 将是自动增加的,并被所有的 Thread 对象所共享 (因为它是 static 的成员变量)。 28 | 29 | * 3>当使用第一种方式来生成线程对象时,我们需要重写 run 方法,因为 Thread 类的 run 方法此时什么事情也不做。 30 | 31 | * 4>当使用第二种方式来生成线程对象时,我们需要实现 Runnable 接口的 run 方法,然 后使用 new Thread(new MyThread())(假如 MyThread 已经实现了 Runnable 接口)来生成线程对象,这时的线程对象的 run 方法就会调用 MyThread 类的 run 方法,这样 我们自己编写的 run 方法就执行了。 32 | 33 | * 9、局部变量与成员变量 34 | 35 | * 如果一个变量是成员变量,那么多个线程对同一个对象 的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成 员变量的改变会影响到另一个线程) 。 36 | 37 | * 如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。 38 | 39 | * 10、停止线程的方式: 40 | 41 | * 不能使用 Thread 类的 stop 方法来终止线程的执行。一般要设定一 个变量,在 run 方法中是一个循环,循环每次检查该变量,如果满足条件则继续执 行,否则跳出循环,线程结束。 42 | 43 | * 11、不能依靠线程的优先级来决定线程的执行顺序。 44 | 45 | * 12、synchronized 关键字: 46 | 47 | * 1>当 synchronized 关键字修饰一个方法的时候,该方法叫做同步 方法。 48 | 49 | * 2>Java 中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对 象的 synchronized 方法时,表示**将该对象上锁**,此时其他任何线程都无法再去访问 该 synchronized 方法了,直到之前的**那个线程执行方法完毕后(或者是抛出了异常)** , 那么将该对象的锁释放掉,其他线程才有可能再去访问该 synchronized 方法。 50 | 51 | * 3>如果一个对象有多个 synchronized 方法,某一时刻某个线程已经进入到了某个 synchronized 方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任 何 synchronized 方法的。 52 | 53 | * 13、如果某个 synchronized 方法是 static 的,那么当线程访问该方法时,它锁的并不是 synchronized 方法所在的对象,**而是 synchronized 方法所在的对象所对应的 Class 对象,因为 Java 中无论一个类有多少个对象**,这些对象会对应唯一一个 Class 对象, 因此当线程分别访问同一个类的两个对象的两个 static,synchronized 方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。 54 | 55 | * 14、synchronized 块,写法: 56 | 57 | synchronized(object) { 58 | 59 | } 60 | 61 | * 表示线程在执行的时候会对 object 对象上锁。 62 | 63 | * 15、synchronized 方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该 synchronized 方法;synchronized 块则是一种细粒度的并发控制,只会将块中的代码同 步,位于方法内、synchronized 块之外的代码是可以被多个线程同时访问到的。 64 | 65 |
66 | 67 | * 16、死锁(deadlock) 68 | 69 | * 17、wait 与 notify 方法都是定义在 Object 类中,而且是 final 的,因此会被所有的 Java 类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁, 因此对这两个方法的调用需要放在 synchronized 方法或块当中。**当线程执行了 wait 方法时,它会释放掉对象的锁**。 70 | 71 | * 18、另一个会导致线程暂停的方法就是 Thread 类的 sleep 方法,它会导致线程睡眠指定 的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。 72 | 73 | 74 | ### 二、线程的四种实现 75 | 76 | * 1、继承Thread类 77 | ```java 78 | package com.edu.testjava.thread; 79 | 80 | /** 81 | * @Author: 王仁洪 82 | * @Date: 2018/12/11 22:03 83 | */ 84 | public class Demo { 85 | public static void main(String[] args) { 86 | MyThread thread = new MyThread(); 87 | thread.start(); 88 | 89 | for (int i=0;i<10;i++){ 90 | System.out.println("MainThread:" + i); 91 | } 92 | } 93 | } 94 | class MyThread extends Thread{ 95 | @Override 96 | public void run() { 97 | for (int i=0;i<10;i++){ 98 | System.out.println("MyThread:" + i); 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | * 2、实现Runnable接口 105 | 106 | ```java 107 | package com.edu.testjava.thread; 108 | 109 | import java.util.concurrent.Callable; 110 | import java.util.concurrent.FutureTask; 111 | 112 | /** 113 | * @Author: 王仁洪 114 | * @Date: 2018/12/11 22:03 115 | */ 116 | public class Demo { 117 | public static void main(String[] args) { 118 | 119 | MyThreadRunnable runnable = new MyThreadRunnable(); 120 | Thread thread1 = new Thread(runnable); 121 | 122 | Thread thread2 = new Thread(new Runnable() { 123 | @Override 124 | public void run() { 125 | for (int i=0;i<10;i++){ 126 | System.out.println("thread2:" + i); 127 | } 128 | } 129 | }); 130 | 131 | 132 | thread1.start(); 133 | thread2.start(); 134 | for (int i=0;i<10;i++){ 135 | System.out.println("MainThread:" + i); 136 | } 137 | } 138 | } 139 | class MyThreadRunnable implements Runnable{ 140 | @Override 141 | public void run() { 142 | for (int i=0;i<10;i++){ 143 | System.out.println("MyThreadRunnable:" + i); 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | * 3、实现Callable接口,通过FutureTask包装器来创建Thread线程 150 | ```java 151 | package com.edu.testjava.thread; 152 | 153 | import java.util.concurrent.Callable; 154 | import java.util.concurrent.FutureTask; 155 | 156 | /** 157 | * @Author: 王仁洪 158 | * @Date: 2018/12/11 22:03 159 | */ 160 | public class Demo { 161 | public static void main(String[] args) { 162 | 163 | MyCallable callable = new MyCallable(); 164 | FutureTask futureTask = new FutureTask(callable); 165 | Thread thread = new Thread(futureTask); 166 | thread.start(); 167 | 168 | for (int i=0;i<10;i++){ 169 | System.out.println("MainThread:" + i); 170 | } 171 | } 172 | } 173 | class MyCallable implements Callable{ 174 | 175 | @Override 176 | public Object call() throws Exception { 177 | for (int i=0;i<10;i++){ 178 | System.out.println("MyCallable:" + i); 179 | } 180 | return null; 181 | } 182 | } 183 | ``` 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /9.3JVM之加载器.md: -------------------------------------------------------------------------------- 1 | # 9.3JVM之加载器 2 | -------------------------------------------------------------------------------- /JAVA8/API解析/Object.md: -------------------------------------------------------------------------------- 1 |

Object

-------------------------------------------------------------------------------- /JAVA8/JAVA知识点论述/接口与抽象类的区别.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/JAVA8/JAVA知识点论述/接口与抽象类的区别.md -------------------------------------------------------------------------------- /JAVA8/JAVA知识点论述/继承之private方法属性探索.md: -------------------------------------------------------------------------------- 1 |

继承之private方法属性探索

2 | 3 | -------------------------------------------------------------------------------- /JAVA8/JAVA知识点论述/继承之static方法属性探索.md: -------------------------------------------------------------------------------- 1 |

继承之static方法属性探索

2 | 3 | -------------------------------------------------------------------------------- /JAVA8/README.md: -------------------------------------------------------------------------------- 1 |

JAVA 8

2 | 3 | ### [API解析](./API解析) 4 | 5 | * [Object](./API解析/Object.md) 6 | 7 | 8 | 9 | 10 | 11 | ### [JAVA知识点论述](./JAVA知识点论述) 12 | 13 | * [接口与抽象类的区别](./JAVA知识点论述/接口与抽象类的区别.md) 14 | * [继承之private方法属性探索](./JAVA知识点论述/继承之private方法属性探索.md) 15 | * [继承之static方法属性探索](./JAVA知识点论述/继承之static方法属性探索.md) 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Java核心卷/README.md: -------------------------------------------------------------------------------- 1 |

JAVA核心卷

2 | 3 | * [第一章 JAVA程序设计概述](./第一章JAVA程序设计概述) 4 | * [第一章 JAVA程序设计概述](./第一章JAVA程序设计概述/第一章JAVA程序设计概述.md) 5 | * [第二章 JAVA程序设计环境](./第二章JAVA程序设计环境) 6 | * [第二章 JAVA程序设计环境](./第二章JAVA程序设计环境/第二章JAVA程序设计环境.md) 7 | * [第三章 JAVA的基本程序设计结构](./第三章JAVA的基本程序设计结构) 8 | * [3.1 一个简单的JAVA应用程序](./第三章JAVA的基本程序设计结构/3.1一个简单的JAVA应用程序.md) 9 | * [JAVA基础语法](./第三章JAVA的基本程序设计结构/JAVA基础语法.md) 10 | * [第四章 对象与类](./第四章对象与类) 11 | * [4.1 面向对象程序设计概述](./第四章对象与类/4.1面向对象程序设计概述.md) 12 | * [4.2 使用预定义类](./第四章对象与类/4.2使用预定义类.md) 13 | * [4.3 用户自定义类](./第四章对象与类/4.3用户自定义类.md) 14 | * [4.4 静态域与静态方法](./第四章对象与类/4.4静态域与静态方法.md) 15 | * [4.5 方法参数](./第四章对象与类/4.5方法参数.md) 16 | * [4.6 对象构造](./第四章对象与类/4.6对象构造.md) 17 | * [4.7 包](./第四章对象与类/4.7包.md) 18 | * [4.8 类路径](./第四章对象与类/4.8类路径.md) 19 | * [4.9 文档注释](./第四章对象与类/4.9文档注释.md) 20 | * [4.10 类设计技巧](./第四章对象与类/4.10类设计技巧.md) 21 | * [第五章 继承](./第五章继承) 22 | * [5.1 类、超类和继承](./第五章继承/5.1类、超类和继承.md) 23 | * [5.2 Object:所有类的超类](./第五章继承/5.2Object所有类的超类.md) 24 | * [5.3 泛型数组列表](./第五章继承/5.3泛型数组列表.md) 25 | * [5.4 对象包装器与自动装箱](./第五章继承/5.4对象包装器与自动装箱.md) 26 | * [5.5 参数数量可变的方法](./第五章继承/5.5参数数量可变的方法.md) 27 | * [5.6 枚举类](./第五章继承/5.6枚举类.md) 28 | * [5.7 反射](./第五章继承/5.7反射.md) 29 | * [5.8 继承的设计技巧](./第五章继承/5.8继承的设计技巧.md) 30 | * [第六章 接口、lambda表达式与内部类](./第六章接口、lambda表达式与内部类) 31 | * [6.1 接口](./第六章接口、lambda表达式与内部类/6.1接口.md) 32 | * [6.2 接口示例](./第六章接口、lambda表达式与内部类/6.2接口示例.md) 33 | * [6.3 lambda表达式](./第六章接口、lambda表达式与内部类/6.3lambda表达式.md) 34 | * [6.4 内部类](./第六章接口、lambda表达式与内部类/6.4内部类.md) 35 | * [6.5 代理](./第六章接口、lambda表达式与内部类/6.5代理.md) 36 | * [第七章 异常、断言和日志](./第七章异常、断言和日志) 37 | * [7.1 处理错误](./第七章异常、断言和日志/7.1处理错误.md) 38 | * [7.2 捕获异常](./第七章异常、断言和日志/7.2捕获异常.md) 39 | * [7.3 使用异常机制的技巧](./第七章异常、断言和日志/7.3使用异常机制的技巧.md) 40 | * [7.4 使用断言](./第七章异常、断言和日志/7.4使用断言.md) 41 | * [7.5 记录曰志](./第七章异常、断言和日志/7.5记录曰志.md) 42 | * [7.6 调试技巧](./第七章异常、断言和日志/7.6调试技巧.md) 43 | * [第八章 泛型程序设计](./第八章泛型程序设计) 44 | * [8.1 为什么要使用泛型程序设计](./第八章泛型程序设计/8.1为什么要使用泛型程序设计.md) 45 | * [8.2 定义简单泛型](./第八章泛型程序设计/8.2定义简单泛型.md) 46 | * [8.3 泛型方法](./第八章泛型程序设计/8.3泛型方法.md) 47 | * [8.4 类型变量的限定](./第八章泛型程序设计/8.4类型变量的限定.md) 48 | * [8.5 泛型代码和虚拟机](./第八章泛型程序设计/8.5泛型代码和虚拟机.md) 49 | * [8.6 约束与局限性](./第八章泛型程序设计/8.6约束与局限性.md) 50 | * [8.7 泛型类型的继承规则](./第八章泛型程序设计/8.7泛型类型的继承规则.md) 51 | * [8.8 通配符类型](./第八章泛型程序设计/8.8通配符类型.md) 52 | * [8.9 反射和泛型](./第八章泛型程序设计/8.9反射和泛型.md) 53 | * [第九章 集合](./第九章集合) 54 | * [9.1 Java集合框架](./第九章集合/9.1Java集合框架.md) 55 | * [9.2 具体的集合](./第九章集合/9.2具体的集合.md) 56 | * [9.3 映射](./第九章集合/9.3映射.md) 57 | * [9.4 视图与包装器](./第九章集合/9.4视图与包装器.md) 58 | * [9.5 算法](./第九章集合/9.5算法.md) 59 | * [9.6 遗留的集合](./第九章集合/9.6遗留的集合.md) 60 | * [第十四章 并发](./第十四章并发) 61 | * [14.1 什么是线程](./第十四章并发/14.1什么是线程.md) 62 | * [14.2 中断线程](./第十四章并发/14.2中断线程.md) 63 | * [14.3 线程状态](./第十四章并发/14.3线程状态.md) 64 | * [14.4 线程属性](./第十四章并发/14.4线程属性.md) 65 | * [14.5 同步](./第十四章并发/14.5同步.md) 66 | * [14.6 阻塞队列](./第十四章并发/14.6阻塞队列.md) 67 | * [14.7 线程安全的集合](./第十四章并发/14.7线程安全的集合.md) 68 | * [14.8 Callable 与 Future](./第十四章并发/14.8Callable与Future.md) 69 | * [14.9 执行器](./第十四章并发/14.9执行器.md) 70 | * [14.10 同步器](./第十四章并发/14.10同步器.md) 71 | * [14.11 线程与Swing](./第十四章并发/14.11线程与Swing.md) -------------------------------------------------------------------------------- /Java核心卷/第七章异常、断言和日志/7.1处理错误.md: -------------------------------------------------------------------------------- 1 |

6.5 代理

2 | 3 | * 1、由于程序的错误或一些外部环境的影响造成用户数据的丢失了避免这类事情的发生,至少应该做到以下几点: 4 | * 向用户通告错误; 5 | * 保存所有的工作结果; 6 | * 允许用户以妥善的形式退出程序。 7 | * 2、对于异常情况,例如,可能造成程序崩溃的错误输入,Java使用一种称为异常处理(exception handing) 的错误捕获机制处理。Java中的异常处理与 C++ 或 Delphi 中的异常处理十分类似。 8 | * 3、在测试期间,需要进行大量的检测以验证程序操作的正确性。然而,这些检测可能非常耗时,在测试完成后也不必保留它们,因此,可以将这些检测删掉,并在其他测试需要时将它们粘贴回来,这是一件很乏味的事情。——**断言** 9 | * 4、当程序出现错误时,并不总是能够与用户或终端进行沟通。此时,可能希望记录下出现的问题,以备日后进行分析。——**日志** 10 | * 5、假设在一个 Java程序运行期间出现了一个错误。这个错误可能是由于文件包含了错误信息,或者网络连接出现问题造成的,也有可能是因为使用无效的数组下标,或者试图使用一个没有被赋值的对象引用而造成的。用户期望在出现错误时,程序能够采用一些理智的行为。 11 | * 6、如果由于出现错误而使得某些操作没有完成,程序应该: 12 | * 返回到一种安全状态,并能够让用户执行一些其他的命令;或者 13 | * 允许用户保存所有操作的结果,并以妥善的方式终止程序。 14 | 15 | 16 | 17 | 18 | 19 | 20 | ### 7.1.1 异常分类 21 | 22 | * 1、在 Java程序设计语言中,异常对象都是派生于 Throwable类的一个实例。 23 | 24 |
25 | 26 | * 2、需要注意的是,所有的异常都是由 Throwable 继承而来,但在下一层立即分解为两个分支:Error 和 Exception。 27 | * 3、Error类层次结构描述了 Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了。这种情况很少出现。 28 | * 4、在设计 Java 程序时,需要关注 Exception 层次结构。这个层次结构又分解为两个分支:一个分支派生于 RuntimeException ; 另一个分支包含其他异常。 29 | * 5、划分两个分支的规则是:由程序错误导致的异常属于 RuntimeException ; 而程序本身没有问题,但由于像 I/O 错误这类问题导致的异常属于其他异常: 30 | * 6、派生于 RuntimeException 的异常包含下面几种情况: 31 | * 错误的类型转换。 32 | * 数组访问越界 33 | * 访问 null 指针 34 | * 7、不是派生于 RuntimeException 的异常包括: 35 | * 试图在文件尾部后面读取数据。 36 | * 试图打开一个不存在的文件。 37 | * 试图根据给定的字符串查找 Class对象,而这个字符串表示的类并不存在。 38 | * 8、“如果出现 RuntimeException 异常,那么就一定是你的问题”是一条相当有道理的规则。 39 | * 9、应该通过检测数组下标是否越界来避免 **ArraylndexOutOfBoundsException 异常**;应该通过在使用变量之前检测是否为 null 来杜绝 **NullPointerException 异常**的发生: 40 | * 10、Java语言规范将派生于 Error类 或 RuntimeException类的所有异常称为**非受查(unchecked) 异常**,所有其他的异常称为**受查(checked) 异常**。 41 | 42 | ### 7.1.2 声明受查异常 43 | 44 | * 1、一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。 45 | * 2、在自己编写方法时,不必将所有可能抛出的异常都进行声明。至于什么时候需要在方法中用 throws子句声明异常,什么异常必须使用 throws子句声明,需要记住在遇到下面 4 种情况时应该抛出异常: 46 | * 1)调用一个抛出受査异常的方法,例如,FilelnputStream构造器。 47 | * 2)程序运行过程中发现错误,并且利用 throw语句抛出一个受查异常。 48 | * 3)程序出现错误,例如,`a[-l]=0`会抛出一个 ArraylndexOutOffloundsException这样的非受查异常。 49 | * 4)Java虚拟机和运行时库出现的内部错误。 50 | * 如果出现前两种情况之一,则必须告诉调用这个方法的程序员有可能抛出异常。为什么? 因为任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常,当前执行的线程就会结束。 51 | * 3、对于那些可能被他人使用的 Java方法,应该根据异常规范(exception specification), 在 52 | 方法的首部声明这个方法可能抛出的异常。 53 | ```java 54 | class MyAnimation{ 55 | public Image loadlmage(String s) throws IOException{ 56 | ............. 57 | } 58 | } 59 | ``` 60 | * 4、如果一个方法有可能抛出多个受查异常类型,那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开。如下面这个例子所示: 61 | ```java 62 | class MyAnimation{ 63 | public Image loadlmage(String s) throws FileNotFoundException, EOFException{ 64 | ...... 65 | } 66 | } 67 | ``` 68 | * 5、不需要声明 Java的内部错误,即从 Error继承的错误。任何程序代码都具有抛出那些异常的潜能,而我们对其没有任何控制能力。 69 | * 6、不应该声明从 RuntimeException继承的那些非受查异常。 70 | ```java 71 | class MyAnimation{ 72 | void drawlmage(inti) throws ArraylndexOutOfBoundsException{// bad style 73 | ........ 74 | } 75 | } 76 | ``` 77 | * 7、一个方法必须声明所有可能抛出的受查异常,而非受查异常要么**不可控制(Error)**,要么就应该**避免发生(RuntimeException**)。 78 | * 8、如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误消息。 79 | * 9、除了**声明异常**之外,还可以**捕获异常**。这样会使异常不被抛到方法之外,也不需要 throws规范。 80 | * 10、如果在子类中覆盖了超类的一个方法, 81 | * **子类方法中声明的受查异常不能比超类方法中声明的异常更通用**(也就是说,子类方法中可以抛出更特定的异常,或者根本不抛出任何异常)。 82 | * **如果超类方法没有抛出任何受查异常,子类也不能抛出任何受查异常**。例如,如果覆盖 JComponent.paintComponent方法,由于超类中这个方法没有抛出任何异常,所以,自定义的 paintComponent 也不能抛出任何受查异常。 83 | * 11、如果类中的一个方法声明将会抛出一个异常,而这个异常是某个特定类的实例时,则这个方法就有可能抛出一个这个类的异常,或者这个类的任意一个子类的异常。例如,FilelnputStream构造器声明将有可能抛出一个 IOExcetion异常,然而并不知道具体是哪种 IOException异常。它既可能是 IOException异常,也可能是其子类的异常,例如,FileNotFoundException。 84 | 85 | 86 | ### 7.1.3 如何抛出异常 87 | 88 | * 1、下面是抛出这个异常的语句: 89 | ```java 90 | throw new EOFException(); 91 | 或者 92 | EOFException e = new EOFException(); 93 | throw e; 94 | 下面将这些代码放在一起: 95 | String readData(Scanner in) throws EOFException{ 96 | while (…){ 97 | if (Mn.hasNext()){ // EOF encountered 98 | if (n < len) 99 | throw new EOFException(); 100 | } 101 | } 102 | return s; 103 | } 104 | ``` 105 | 106 | * 2、EOFException 类还有一个含有一个字符串型参数的构造器。这个构造器可以更加细致的描述异常出现的情况。 107 | ```java 108 | String gripe = "Content-length: " +len+ ", Received: " + n; 109 | throw new EOFException(gripe); 110 | ``` 111 | * 3、对于一个已经存在的异常类,将其抛出非常容易D 在这种情况下: 112 | * 1)找到一个合适的异常类。 113 | * 2)创建这个类的一个对象。 114 | * 3)将对象抛出。 115 | * 一旦方法抛出了异常,这个方法就不可能返回到调用者。也就是说,不必为返回的默认值或错误代码担忧。 116 | * 4、抛出异常的过程,在Java 中,只能抛出 Throwable 子类的对象。 117 | 118 | ### 7.1.4 创建异常类 119 | 120 | * 1、在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于Exception 的类,或者派生于Exception 子类的类。 121 | * 2、例如,定义一个派生于 IOException 的类。习惯上,定义的类应该包含两个构造器,一个是默认的构造器;另一个是带有详细描述信息的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息,这在调试中非常有用)。 122 | ```java 123 | package com.edu.exception; 124 | 125 | import java.io.BufferedReader; 126 | import java.io.IOException; 127 | 128 | /** 129 | * @Author: 王仁洪 130 | * @Date: 2019/3/22 15:46 131 | */ 132 | public class FileFormatException extends IOException { 133 | public FileFormatException(){ 134 | 135 | } 136 | 137 | public FileFormatException(String gripe){ 138 | super(gripe); 139 | } 140 | } 141 | ``` 142 | * 现在,就可以抛出自己定义的异常类型了。 143 | ```java 144 | class TestException{ 145 | public String (BufferedReader in) throws FileFormatException{ 146 | ... 147 | while (...){ 148 | if (ch == -1) {//EOF encountered 149 | if (n < len){ 150 | if (n < len) 151 | throw new FileFornatException(); 152 | } 153 | } 154 | return s; 155 | } 156 | } 157 | ``` -------------------------------------------------------------------------------- /Java核心卷/第七章异常、断言和日志/7.3使用异常机制的技巧.md: -------------------------------------------------------------------------------- 1 |

7.3 使用异常机制的技巧

2 | 3 | * 1、异常处理不能代替简单的测试。使用异常的基本规则是:只在异常情况下使用异常机制。 4 | * 2、不要过分地细化异常 5 | * 3、利用异常层次结构 6 | * 4、不要压制异常 7 | * 5、在检测错误时,“苛刻”要比放任更好 8 | * 6、不要羞于传递异常 9 | * 规则 5、6 可以归纳为“**早抛出,晚捕获**' 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 | -------------------------------------------------------------------------------- /Java核心卷/第七章异常、断言和日志/7.4使用断言.md: -------------------------------------------------------------------------------- 1 |

7.4 使用断言

2 | 3 | ### 7.4.1 断言的概念 4 | 5 | * 1、在一个具有自我保护能力的程序中,断言很常用。 6 | * 2、假设确信某个属性符合要求,并且代码的执行依赖于这个属性。例如,需要计算 7 | ```java 8 | double y = Math.sqrt(x); 9 | ``` 10 | * 我们确信,这里的 X 是一个非负数值。原因是:X 是另外一个计算的结果,而这个结果不可能是负值;或者X是一个方法的参数,而这个方法要求它的调用者只能提供一个正整数。然而,还是希望进行检查,以避免让“ 不是一个数” 的数值参与计算操作。当然,也可以抛出一个异常: 11 | ```java 12 | if (x < 0) throw new 111egalArgumentException("x < 0"); 13 | ``` 14 | 15 | * 但是这段代码会一直保留在程序中,即使测试完毕也不会自动地删除。如果在程序中含有大量的这种检查,程序运行起来会相当慢。 16 | * 3、**断言机制允许在测试期间向代码中插入一些检査语句。当代码发布时,这些插人的检测语句将会被自动地移走**。 17 | * 4、Java语言引人了关键字 assert。这个关键字有两种形式: 18 | ```java 19 | assert 条件; 20 | 和 21 | assert 条件:表达式; 22 | ``` 23 | * 这两种形式都会对条件进行检测,如果结果为 false, 则抛出一个 AssertionError 异常。在第二种形式中,表达式将被传人 AssertionError 的构造器,并转换成一个消息字符串。 24 | * 5、 **"表达式" 部分的唯一目的是产生一个消息字符串**。AssertionError 对象并不存储表达式的值,因此,不可能在以后得到它。正如 JDK 文档所描述的那样:如果使用表达式的值,就会鼓励程序员试图从断言中恢复程序的运行,这不符合断言机制的初衷。 25 | * 6、要想断言?c是一个非负数值,只需要简单地使用下面这条语句 26 | ```java 27 | assert x >= 0; 28 | ``` 29 | * 或者将 x 的实际值传递给 AssertionError 对象,从而可以在后面显示出来。 30 | ```java 31 | assert x >= 0 : x; 32 | ``` 33 | 34 | ### 7.4.2 启用和禁用断言 35 | 36 | * 1、在默认情况下,断言被禁用。可以在运行程序时用`-enableassertions`或 `-ea`选项启用: 37 | ```java 38 | java -enableassertions MyApp 39 | ``` 40 | * 2、需要注意的是,**在启用或禁用断言时不必重新编译程序**。启用或禁用断言是类加载器(class loader) 的功能。当断言被禁用时,类加载器将跳过断言代码,因此,不会降低程序运行的速度。 41 | * 3、可以在某个类或整个包中使用断言,例如: 42 | ```java 43 | java -ea:MyClass -ea:com.mycompany.myylib... MyApp 44 | ``` 45 | * 这条命令将开启 MyClass类以及在 `com.mycompany.mylib`包和它的子包中的所有类的断言。选项-ea将开启默认包中的所有类的断言。 46 | * 47 | * 4、选项`-disableassertions`或 `-da`禁用某个特定类和包的断言: 48 | ```java 49 | java -ea:... -da:MyClass MyApp 50 | ``` 51 | * 5、有些类不是由类加载器加载,而是直接由虚拟机加载。可以使用这些开关有选择地启用或禁用那些类中的断言。 52 | * 6、启用和禁用所有断言的 `-ea`和`-da`开关不能应用到那些没有类加载器的“ 系统类”上。对于这些系统类来说,需要使用`-enablesystemassertions/-esa`开关启用断言。 53 | * 7、在程序中也可以控制类加载器的断言状态。 54 | 55 | ### 7.4.3 使用断言完成参数检查 56 | 57 | * 1、在 Java语言中,给出了 3种处理系统错误的机制: 58 | * 抛出一个异常 59 | * 日志 60 | * 使用断言 61 | * 2、什么时候应该选择使用断言呢? 请记住下面几点: 62 | * 断言失败是致命的、不可恢复的错误。 63 | * 断言检查只用于开发和测阶段(这种做法有时候被戏称为“ 在靠近海岸时穿上救生衣,但在海中央时就把救生衣抛掉吧”)。 64 | * 3、不应该使用断言向程序的其他部分通告发生了可恢复性的错误,或者,不应该作为程序向用户通告问题的手段。**断言只应该用于在测试阶段确定程序内部的错误位置**。 65 | * 4、由于可以使用断言,当方法被非法调用时,将会出现难以预料的结果。有时候会拋出一个断言错误,有时候会产生一个 null 指针异常,这完全取决于类加载器的配置。 66 | 67 | ### 7.4.4 为文档假设使用断言 68 | 69 | * 1、很多程序员使用注释说明假设条件。看一下 文档上的一个示例; 70 | ```java 71 | if (i % 3 == 0) 72 | else if (i % 3 == 1) 73 | else //(i % 3 == 2) 74 | 75 | 在这个示例中,使用断言会更好一些。 76 | if (i % 3 == 0) 77 | else if (i % 3 == 1) 78 | else{ 79 | ... 80 | assert i % 3 == 2; 81 | } 82 | ``` 83 | * 当然,如果再仔细地考虑一下这个问题会发现一个更有意思的内容。i%3会产生什么结果? 如果 i 是正值,那余数肯定是 0、1或 2。如果i 是负值,则余数则可以是-1 和-2。然而,实际上都认为 i 是非负值,因此,最好在 if语句之前使用下列断言: 84 | ```java 85 | assert i >=0; 86 | ``` 87 | * 2、**断言是一种测试和调试阶段所使用的战术性工具; 而日志记录是一种在程序的整个生命周期都可以使用的策略性工具**。 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 | -------------------------------------------------------------------------------- /Java核心卷/第七章异常、断言和日志/img/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第七章异常、断言和日志/img/01.png -------------------------------------------------------------------------------- /Java核心卷/第三章JAVA的基本程序设计结构/3.1一个简单的JAVA应用程序.md: -------------------------------------------------------------------------------- 1 |

3.1 一个简单的JAVA应用程序

2 | 3 | ```java 4 | public class Test1 { 5 | public static void main(String[] args) { 6 | System.out.println("Hello World!"); 7 | } 8 | } 9 | ``` 10 | 11 | * 首先,Java 区分大小写。如果出现了大小写拼写错误(例如,将 main拼写成 Main),程序将无法运行。 12 | * public:关键字 public称为访问修饰符(accessmodifier), 这些修饰符用于控制程序的其他部分对这段代码的访问级別。 13 | * class:关键字 class表明 Java程序中的全部内容都包含在类中。这里,只需要将类作为一个加载程序逻辑的容器,程序逻辑定义了应用程序的行为。**类是构建所有 Java应用程序和 applet 的构建块。Java应用程序中的全部内容都必须放置在类中。** 14 | * Test1:关键字 class后面紧跟**类名**`Test1`。 15 | * 类名命名:Java中定义类名的规则很宽松。名字必须以字母开头,后面可以跟字母和数字的任意组合。长度基本上没有限制。但是不能使用 Java保留字(例如,public或 class) 作为类名。 16 | * 类名规范之胳蛇命名法:类名是以大写字母开头的名词。如果名字由多个单词组成,每个单词的第一个字母都应该大写。eg:Test1 17 | * 源代码的文件名必须与公共类的名字相同,并用`.java`作为扩展名。因此,存储这段源代码的文件名必须为 `Test1.java`(大小写是非常重要的,千万不能写成 `test1.java`) 18 | * `main(String[] args)`:为了代码能够执行,在类的源文件中必须包含一个 main方法。 19 | * 根据 Java语言规范,main方法必须声明为 public( Java语言规范是描述 Java语言的官方文档。) 20 | * 不过,当 main 方法不是public时,有些版本的 Java解释器也可以执行 Java应用程序。在 JavaSE 1.4及以后的版本中强制 main方法是 public的。 21 | 22 |
23 | * 24 | -------------------------------------------------------------------------------- /Java核心卷/第三章JAVA的基本程序设计结构/img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第三章JAVA的基本程序设计结构/img/001.png -------------------------------------------------------------------------------- /Java核心卷/第三章JAVA的基本程序设计结构/img/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第三章JAVA的基本程序设计结构/img/002.png -------------------------------------------------------------------------------- /Java核心卷/第三章JAVA的基本程序设计结构/img/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第三章JAVA的基本程序设计结构/img/003.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/9.4视图与包装器.md: -------------------------------------------------------------------------------- 1 |

9.4 视图与包装器

2 | 3 | * keySet方法返回一个实现 Set接口的类对象,这个类的方法对原映射进行操作。这种集合称为**视图**。 4 | 5 | ### 9.4.1 轻量级集合包装器 6 | * 1、Arrays 类的静态方法 asList 将返回一个包装了普通 Java 数组的 List包装器。这个方法可以将数组传递给一个期望得到列表或集合参数的方法。例如: 7 | ```java 8 | Card[] cardDeck = new Card[52]; 9 | List cardList= Arrays.asList(cardDeck); 10 | ``` 11 | * 返回的对象不是 ArrayList。它是一个视图对象,带有访问底层数组的 get 和set方法。 12 | * 2、改变数组大小的所有方法(例如,与迭代器相关的 add 和remove方法)都会抛出一个Unsupported OperationException 异常。 13 | * 3、asList 方法可以接收可变数目的参数。例如: 14 | ```java 15 | List names= Arrays.asList("A«iy", "Bob", "Carl"); 16 | 这个方法调用 17 | Collections.nCopies(n, anObject) 18 | ``` 19 | * 将返回一个实现了 List 接口的不可修改的对象,并给人一种包含》个元素,每个元素都像是一个 anObject 的错觉。 20 | * 4、例如,下面的调用将创建一个包含100个字符串的 List, 每个串都被设置为“DEFAULT”: 21 | ```java 22 | Listsettings = Collections.nCopies(100, "DEFAULT"); 23 | ``` 24 | * 存储代价很小。这是视图技术的一种巧妙应用。 25 | * 5、Collections类包含很多实用方法,这些方法的参数和返回值都是集合。不要将它与 Collection接口混淆起来。 26 | * 6、如果调用下列方法 27 | ```java 28 | Collections.singleton(anObject) 29 | ``` 30 | * 则将返回一个视图对象。这个对象实现了 Set接口(与产生 List 的 ncopies方法不同)。 31 | * 返回的对象实现了一个不可修改的单元素集,而不需要付出建立数据结构的开销。singletonList 方法与 singletonMap 方法类似。 32 | * 7、似地,对于集合框架中的每一个接口,还有一些方法可以生成**空集、列表、映射**,等等。特别是,集的类型可以推导得出: 33 | ```java 34 | Set deepThoughts = Col1ections.emptySetO; 35 | ``` 36 | 37 | ### 9.4.2 子范围 38 | * 1、**可以为很多集合建立子范围(subrange) 视图**。例如,假设有一个列表 staff, 想从中取出第 10个 ~第 19个元素。可以使用 subList 方法来获得一个列表的子范围视图。 39 | ```java 40 | List group2 = staff.subList(10, 20); 41 | ``` 42 | * **第一个索引包含在内,第二个索引则不包含在内**。这与 String类的 substring操作中的参数情况相同。 43 | * 2、可以将任何操作应用于子范围,并且能够自动地反映整个列表的情况。例如,可以删除整个子范围: 44 | ```java 45 | group2.clear(); // staff reduction 46 | ``` 47 | * 现在,元素自动地从 staff 列表中清除了,并且 gr0up2 为空。 48 | * 3、**对于有序集和映射,可以使用排序顺序而不是元素位置建立子范围**。 49 | * 4、SortedSet 接口声明了 3个方法: 50 | ```java 51 | SortedSet subSet(E from, E to) 52 | SortedSet headSet(E to) 53 | SortedSet tailSet(E from) 54 | ``` 55 | * 这些方法将返回大于等于 from且小于 to的所有元素子集。 56 | * 5、有序映射(SortedMap)也有类似的方法: 57 | ```java 58 | SortedMap subMap(K from, K to) 59 | SortedMap headMap(K to) 60 | SortedMap tailMap(K from) 61 | ``` 62 | * 返回映射视图,该映射包含键落在指定范围内的所有元素。 63 | * 6、Java SE 6引人的 NavigableSet 接口赋予子范围操作更多的控制能力。可以指定是否包括边界: 64 | ```java 65 | NavigableSet subSet(E from, boolean fromlnclusive, E to, boolean tolnclusive) 66 | NavigableSet headSet(E to, boolean tolnclusive) 67 | Navigab1eSet tailSet(E from, boolean fromlnclusive) 68 | ``` 69 | 70 | ### 9.4.3 不可修改的视图 71 | * 1、Collections还有几个方法,用于产生集合的不可修改视图 (unmodifiableviews)。 72 | * 这些视图对现有集合增加了一个运行时的检查。 73 | * 如果发现试图对集合进行修改,就抛出一个异常,同时这个集合将保持未修改的状态。 74 | * 2、可以使用下面 8 种方法获得不可修改视图: 75 | ```java 76 | Collections.unmodifiableCollection 77 | Collections.unmodifiableList 78 | Collections.unmodifiableSet 79 | Collections.unmodifiableSortedSet 80 | Collections.unmodifiableNavigableSet 81 | Collections.unmodifiableMap 82 | Collections.unmodifiableSortedMap 83 | Collections.unmodifiableNavigableMap 84 | ``` 85 | * **每个方法都定义于一个接口**。 86 | * 例如,Collections.unmodifiableList 与 ArrayList、LinkedList或者任何实现了 List 接口的其他类一起协同工作。 87 | * 3、例如,假设想要查看某部分代码,但又不触及某个集合的内容,就可以进行下列操作: 88 | ```java 89 | List staff = new LinkedList<>(); 90 | lookAt(Collections.unmodifiableList(staff)); 91 | ``` 92 | * Collections-unmodifiableList方法将返回一个实现 List 接口的类对象。其访问器方法将从staff 集合中获取值。 93 | * 当然,lookAt方法可以调用 List接口中的所有方法,而不只是访问器。 94 | * 但是所有的更改器方法(例如,add) 已经被重新定义为抛出一个 UnsupportedOperationException异常,而不是将调用传递给底层集合。 95 | * 4、**不可修改视图并不是集合本身不可修改**。仍然可以通过集合的原始引用(在这里是 staff)对集合进行修改。并且仍然可以让集合的元素调用更改器方法。 96 | * 5、**由于视图只是包装了接口而不是实际的集合对象,所以只能访问接口中定义的方法**。 97 | * 例如,LinkedList 类有一些非常方便的方法,addFirst 和 addLast,它们都不是 List 接口的方法,不能通过不可修改视图进行访问。 98 | 99 | ### 9.4.4 同步视图 100 | 101 | * 1、如果由**多个线程访问集合**,就必须确保集不会被意外地破坏。 102 | * 例如,如果一个线程试图将元素添加到散列表中,同时另一个线程正在对散列表进行再散列,其结果将是灾难性的。 103 | * 2、类库的设计者使用视图机制来确保常规集合的线程安全,而不是实现线程安全的集合类。 104 | * 例如,Collections类的静态 synchronizedMap方法可以将任何一个映射表转换成具有同步访问方法的 Map: 105 | ```java 106 | Map map = Collections.synchronizedMap(new HashMap()); 107 | ``` 108 | * 现在,就可以由多线程访问 map对象了。像 get 和 put这类方法都是同步操作的,即**在另一个线程调用另一个方法之前,刚才的方法调用必须彻底完成**。 109 | 110 | ### 9.4.5 受查视图 111 | 112 | * 1、 “受査” 视图用来对泛型类型发生问题时提供调试支持。 113 | * 实际上将错误类型的元素混人泛型集合中的问题极有可能发生。例如: 114 | ```java 115 | ArrayList strings = new ArrayList<>(); 116 | ArrayList rawList = strings; // warning only, not an error, for compatibility with legacy code 117 | rawList.add(new Date()); // now strings contains a Date object! 118 | ``` 119 | * 这个错误的 add命令在运行时检测不到。相反,只有在稍后的另一部分代码中调用 get方法,并将结果转化为 String 时,这个类才会抛出异常。 120 | * 受査视图可以探测到这类问题。下面定义了一个安全列表: 121 | ```java 122 | List safestrings = Collections.checkedList(strings , String,class); 123 | ``` 124 | * 2、视图的 add 方法将检测插人的对象是否属于给定的类。如果不属于给定的类,就立即抛出一个 ClassCastException。这样做的好处是错误可以在正确的位置得以报告: 125 | ```java 126 | ArrayList rawList = safestrings; 127 | rawList.add(new Date());// checked list throws a ClassCastException 128 | ``` 129 | * 受查视图受限于虚拟机可以运行的运行时检查。例如,对于 ArrayList >, 由于虚拟机有一个单独的“原始” Pair 类,所以,无法阻止插入Pair。 130 | 131 | ### 9.4.6 关于可选操作的说明 132 | 133 | * 1、视图有一些局限性,即可能只可以读、无法改变大小、只支持删除而不支持插人,这些与映射的键视图情况相同。如果试图进行不恰当的操作,受限制的视图就会抛出一个 UnsupportedOperationException。 134 | * 2、在集合和迭代器接口的 API 文档中,许多方法描述为“ 可选操作”。这看起来与接口的概念有所抵触。毕竟,接口的设计目的难道不是负责给出一个类必须实现的方法吗? 确实,从理论的角度看,在这里给出的方法很难令人满意。 135 | * 一个更好的解决方案是为每个只读视图和不能改变集合大小的视图建立各自独立的两个接口。不过,这将会使接口的数量成倍增长,这让类库设计者无法接受。 136 | * 3、是否应该将“ 可选”方法这一技术扩展到用户的设计中呢? 我们认为不应该。尽管集合被频繁地使用,其实现代码的风格也未必适用于其他问题领域。 137 | * 4、在自己的编程问题中,很少遇到这样极端的局限性。应该能够找到一种不必依靠极端衡量“可选的”接口操作来解决这类问题的方案。 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 | -------------------------------------------------------------------------------- /Java核心卷/第九章集合/9.6遗留的集合.md: -------------------------------------------------------------------------------- 1 |

9.6 遗留的集合

2 | 3 | * 从 Java第 1版问世以来,在集合框架出现之前已经存在大量“ 遗留的”容器类。这些类已经集成到集合框架中,如图 9-12所示。 4 |
5 | 6 | ### 9.6.1 Hashtable 类 7 | * 1、Hashtable类与 HashMap类的作用一样,实际上,它们拥有相同的接口。 8 | ```java 9 | public class HashMap extends AbstractMap 10 | implements Map, Cloneable, Serializable { 11 | 12 | } 13 | public class Hashtable extends Dictionary 14 | implements Map, Cloneable, java.io.Serializable { 15 | 16 | } 17 | ``` 18 | * 2、与 Vector 类的方法一样。Hashtable的方法也是**同步**的。 19 | * 3、如果对同步性或与遗留代码的兼容性没有任何要求,就应该使用 HashMap。 20 | * 4、如果需要并发访问,则要使用 ConcurrentHashMap 21 | 22 | ### 9.6.2 枚举(Enumeration 接口) 23 | * 1、**传递集合要比传递迭代器更为明智**。集合对象的用途更大。当接受方如果需要时,总是可以从集合中获得迭代器,而且,还可以随时地使用集合的所有方法。 24 | 25 | ### 9.6.3 属性映射 26 | * 属性映射(property map) 是一个类型非常特殊的映射结构。它有下面 3个特性: 27 | * 键与值都是字符串。 28 | * 表可以保存到一个文件中,也可以从文件中加载。 29 | * 使用一个默认的辅助表。 30 | * 实现属性映射的 Java平台类称为 Properties。 31 | * 属性映射通常用于程序的特殊配置选项。 32 | 33 | ### 9.6.4 栈 34 | * 从 1.0版开始,标准类库中就包含了 Stack类,其中有大家熟悉的 push方法和 pop方法。 35 | * 但是,Stack类扩展为 Vector类,从理论角度看,Vector类并不太令人满意,它可以让栈使用不属于栈操作的 insert 和 remove方法,即可以在任何地方进行插入或删除操作,而不仅仅是在栈顶。 36 | 37 | 38 | ### 9.6.5 位集(BitSet类) 39 | * Java平台的 BitSet类用于存放一个位序列(它不是数学上的集,称为位向量或位数组更为合适)。 40 | * 如果需要高效地存储位序列(例如,标志)就可以使用位集。 41 | * 由于位集将位包装在字节里,所以,使用位集要比使用 Boolean对象的 ArrayList 更加高效。 42 | * BitSet 类提供了一个便于读取、设置或清除各个位的接口。使用这个接口可以避免屏蔽和其他麻烦的位操作。 43 | * 如果将这些位存储在 int 或 long变量中就必须进行这些繁琐的操作。 44 | * 例如,对于一个名为 bucketOfBits的 BitSet, 45 | ```java 46 | bucketOfBits.get(i) 47 | 如果第 i 位处于 “开” 状态,就返回 true; 否则返回 false。同样地, 48 | 49 | bucketOfBits.set(i) 50 | 将第 i 位置为 “开” 状态。最后, 51 | 52 | bucketOfBits.clear(i) 53 | 将第 i 位置为 “关” 状态。 54 | ``` 55 | 56 | ```java 57 | package com.edu.test.collection; 58 | 59 | import java.util.BitSet; 60 | 61 | /** 62 | * @Author: 王仁洪 63 | * @Date: 2019/3/31 20:25 64 | */ 65 | public class Sieve { 66 | public static void main(String[] args) { 67 | int n = 2000000; 68 | long start = System.currentTimeMillis(); 69 | BitSet bitSet = new BitSet(n + 1); 70 | int count = 0; 71 | 72 | int i; 73 | for (i=2;i<=n;i++){ 74 | bitSet.set(i); 75 | } 76 | i = 2; 77 | while (i*i <= n){ 78 | if (bitSet.get(i)){ 79 | count++; 80 | int k = 2 * i; 81 | while (k <= n){ 82 | bitSet.clear(k); 83 | k += i; 84 | } 85 | } 86 | i++; 87 | } 88 | 89 | while (i<=n){ 90 | if (bitSet.get(i)){ 91 | count++; 92 | } 93 | i++; 94 | } 95 | 96 | long end = System.currentTimeMillis(); 97 | System.out.println(count + " primes"); 98 | System.out.println(end - start + "milliseconds"); 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 | -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/01.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-1.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-10.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-11.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-12.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-2.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-3.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-4.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-5.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-6.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-7.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-8.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9-9.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/9.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/table9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/table9-2.png -------------------------------------------------------------------------------- /Java核心卷/第九章集合/img/table9-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第九章集合/img/table9-3.png -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.2Object所有类的超类.md: -------------------------------------------------------------------------------- 1 |

5.2 Object:所有类的超类

2 | 3 | * 1、Object类是 Java中所有类的始祖,在 Java中每个类都是由它扩展而来的。 4 | * 2、如果没有明确地指出超类,Object 就被认为是这个类的超类。 5 | * 3、可以使用 Object类型的变量引用任何类型的对象: 6 | ```java 7 | Object obj = new EmployeeC'Harry Hacker", 35000); 8 | ``` 9 | * 4、Object 类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体的操作,还需要清楚对象的原始类型,并进行相应的类型转换: 10 | ```java 11 | Employee e = (Employee) obj; 12 | ``` 13 | * 5、在 Java中,只有基本类型 (primitive types) 不是对象,例如,数值、字符和布尔类型的值都不是对象。 14 | * 6、所有的数组类塱,不管是对象数组还是基本类型的数组都扩展了 Object 类。 15 | ```java 16 | Employee[] staff = new Employee[10]; 17 | obj = staff; // OK 18 | obj = new int[10]; //OK 19 | ``` 20 | 21 | ### 5.2.1 equals方法 22 | 23 | * 1、Object类中的 equals方法用于检测一个对象是否等于另外一个对象。在Object 类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。 24 | * 2、getClass方法将返回一个对象所属的类,在检测中,只有在两个对象属于同一个类时,才有可能相等。 25 | 26 | ### 5.2.2 相等测试与继承 27 | 28 | * 1、Java 语言规范要求 equals 方法具有下面的特性: 29 | * 1)自反性:对于任何非空引用 x, x.equals(x)应该返回true。 30 | * 2)对称性: 对于任何引用 x 和 y, 当且仅当 y.equals(x)返回 true, x.equals(y) 也应该返回 true。 31 | * 3)传递性:对于任何引用x、y 和 z, 如果x.equals(y)返回 true,y.equals(z)返回 true,x.equals(z)也应该返回 true。 32 | * 4)一致性:如果x 和 y引用的对象没有发生变化,反复调用 x.eqimIS(y) 应该返回同样的结果。 33 | * 5)对于任意非空引用 x, x.equals(null)应该返回 false, 34 | * 2、从两个截然不同的情况看一下当参数不属于同一个类的时候的相等这个问题: 35 | * 如果**子类**能够拥有自己的相等概念,则对称性需求将强制采用 getClass 进行检测。 36 | * 如果由**超类决定**相等的概念,那么就可以使用 imtanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。 37 | * 3、**编写一个完美的 equals方法的建议**: 38 | * 1)显式参数命名为 otherObject, 稍后需要将它转换成另一个叫做 other 的变量。 39 | * 2)检测 this与 otherObject 是否引用同一个对象: 40 | ```java 41 | if (this == otherObject) return true; 42 | ``` 43 | * 这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。 44 | * 3)检测 otherObject是否为 null, 如 果 为 null, 返 回 false。这项检测是很必要的。 45 | ```java 46 | if (otherObject = null) return false; 47 | ``` 48 | * 4)比较 this与 otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用 getClass检测: 49 | ```java 50 | if (getClass() != otherObject.getCIass()) return false; 51 | 如果所有的子类都拥有统一的语义,就使用 instanceof 检测: 52 | if (!(otherObject instanceof ClassName)) return false; 53 | ``` 54 | * 5)将 otherObject转换为相应的类类型变量: 55 | ```java 56 | ClassName other = (ClassName) otherObject 57 | ``` 58 | * 6)现在开始对所有需要比较的域进行比较了。使用=比较基本类型域,使用 equals比较对象域。如果所有的域都匹配,就返回 true; 否则返回 false。 59 | ```java 60 | return fieldl == other.field 61 | && Objects.equals(fie1d2, other.field2) 62 | && ...... 63 | ``` 64 | * 如果在子类中重新定义 equals, 就要在其中包含调用super.equals(other)。 65 | 66 | ### 5.2.3 hashCode 方法 67 | 68 | * 1、散列码( hash code) 是由对象导出的一个整型值。散列码是没有规律的。如果 x 和 y 是两个不同的对象,x.hashCode( ) 与 y.hashCode( ) 基本上不会相同。 69 | * 2、字符串 s与 t 拥有相同的散列码,这是因为字符串的散列码是由内容导出的。而字符串缓冲 sb与 tb却有着不同的散列码,这是因为在 StringBuffer类中没有定义hashCode方法,它的散列码是由 Object 类的默认 hashCode方法导出的对象存储地址。 70 | ```java 71 | String s = "Ok"; 72 | StringBuilder sb = new StringBuilder(s); 73 | System.out.println(s.hashCode() + " " + sb.hashCode()); 74 | 75 | String t = new String("Ok"); 76 | StringBuilder tb = new StringBuilder(t); 77 | System.out.println(t.hashCode() + " " + tb.hashCode()); 78 | ``` 79 | 80 |
81 | * 3、如果重新定义 equals方法,就必须重新定义 hashCode方法,以便用户可以将对象插入到散列表中. 82 | * 4、String类使用下列算法计算散列码: 83 | ```java 84 | int hash = 0; 85 | for (int i = 0; i < length0;i++) 86 | hash = 31 * hash + charAt(i); 87 | ``` 88 | * 5、hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。 89 | * 6、Equals与 hashCode的定义必须一致:如果 x.equals(y) 返回 true, 那么 x.hashCode( )就必须与 y.hashCode( )具有相同的值。 90 | * 7、如果存在数组类型的域,那么可以使用静态的 Arrays.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。 91 | 92 | ### 5.2.4 toString方法 93 | 94 | * 1、随处可见 toString方法的主要原因是:只要对象与一个字符串通过操作符“+”连接起来,Java 编译就会自动地调用 toString方法,以便获得这个对象的字符串描述。 95 | * 2、在调用 x.toString( )的地方可以用`""+x` 替代。这条语句将一个空串与 x 的字符串表示相连接。这里的 x就是x.toString( )。与 toString不同的是,如果 x是基本类型,这条语句照样能够执行。 96 | * 3、如果 x 是任意一个对象,并调用`System.out.println(x);`println方法就会直接地调用 x.toString( ),井打印输出得到的字符串。 97 | * 4、Object 类定义了 toString方法,用来打印输出对象所属的类名和散列码。例如,调用 98 | ```java 99 | System.out.println(System.out) 100 | 将输出下列内容: 101 | java.io.PrintStream@2f6684 102 | ``` 103 | * 之所以得到这样的结果是因为 PrintStream类的设计者没有覆盖 toString方法。 104 | * 5、令人烦恼的是,数组继承了 object 类的 toString方法,数组类型将按照旧的格式打印。例如: 105 | ```java 106 | int[] luckyNumbers = { 2,3,5, 7S llf 13 }; 107 | String s = "" +luckyNumbers; 108 | ``` 109 | * 生成字符串“ [I@la46e30”(前缀 [I 表明是一个整型数组)。修正的方式是调用静态方法Arrays.toString。代码: 110 | ```java 111 | String s = Arrays.toString(luckyNumbers); 112 | ``` 113 | * 将生成字符串“ [2,3,5,7,11,13]”。 114 | * 要想打印多维数组(即,数组的数组)则需要调用 Arrays.deepToString方法。 115 | * 6、toString方法是一种非常有用的调试工具。在标准类库中,许多类都定义了 toString方法,以便用户能够获得一些有关对象状态的必要信息。像下面这样显示调试信息非常有益: 116 | ```java 117 | System,out.println("Current position = "+position); 118 | 更好的解决方法是: 119 | Logger,global,info("Current position = " +position); 120 | ``` 121 | * 7、强烈建议为自定义的每一个类增加 toString方法。这样做不仅自己受益,而且所有使用这个类的程序员也会从这个日志记录支持中受益匪浅。 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.3泛型数组列表.md: -------------------------------------------------------------------------------- 1 |

5.3 泛型数组列表

2 | 3 | * 1、解决运行时动态更改数组的问题最简单的方法是使用 Java 中另外一个被称为ArrayList 的类。它使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。 4 | * 2、ArrayList 是一个采用类型参数(type parameter) 的泛型类(generic class)。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面,例如,`ArrayList`。 5 | * 3、声明和构造一个保存 Employee 对象的数组列表: 6 | ```java 7 | ArrayList staff = new ArrayList(); 8 | ``` 9 | * 两边都使用类型参数 Employee,这有些繁琐。Java SE 7中,可以省去右边的类型参数: 10 | ```java 11 | ArrayList staff = new ArrayListoQ; 12 | ``` 13 | * 这被称为“ 菱形”语法,因为空尖括号`<>`就像是一个菱形。可以结合 new 操作符使用菱形语法。编译器会检查新值是什么。如果赋值给一个变量,或传递到某个方法,或者从某个方法返回,编译器会检査这个变量、参数或方法的泛型类型,然后将这个类型放在o中。在这个例子中,new ArrayListo()将赋至一个类型为 ArrayList的变量,所以泛型类型为 Employee。 14 | * 4、数组列表管理着对象引用的一个内部数组。最终,数组的全部空间有可能被用尽。这就显现出数组列表的操作魅力:如果调用 add且内部数组已经满了,数组列表就将自动地创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中。 15 | * 5、如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用ensureCapacity方法: 16 | ```java 17 | staff.ensureCapacity(lOO); 18 | ``` 19 | * 这个方法调用将分配一个包含 100 个对象的内部数组。然后调用 100次 add, 而不用重新分配空间。 20 | * 另外,还可以把初始容量传递给 ArrayList 构造器: 21 | ``` 22 | ArrayList staff = new ArrayListo(lOO); 23 | ``` 24 | * 6、分配数组列表,如下所示: 25 | ```java 26 | new ArrayList<>(lOO) // capacity is100 27 | 它与为新数组分配空间有所不同: 28 | new Employee[100] // sizeis100 29 | ``` 30 | * 数组列表的容量与数组的大小有一个非常重要的区别。如果为数组分配 100 个元素的存储空间,数组就有 100 个空位置可以使用。而容量为 100 个元素的数组列表只是拥有保存 100 个元素的潜力 (实际上,重新分配空间的话,将会超过100), 但是在最初,甚至完成初始化构造之后,数组列表根本就不含有任何元素。 31 | * 7、size方法将返回数组列表中包含的实际元素数目。例如, 32 | ```java 33 | staff.size(); 34 | ``` 35 | * 将返回 staff 数组列表的当前元素数量,它等价于数组 a 的 a.length。 36 | * 8、一旦能够确认数组列表的大小不再发生变化,就可以调用 trimToSize方法。这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。垃圾回收器将回收多余的存储空间。 37 | * 一旦整理了数组列表的大小,添加新元素就需要花时间再次移动存储块,所以应该在确认不会添加任何元素时,再调用 trimToSize。 38 | 39 | 40 | ### 5.3.1 访问数组列表元素 41 | 42 | * 1、只有 i 小于或等于数组列表的大小时,才能够调用 list.set(i,x)。例如,下面这段代码是错误的: 43 | ```java 44 | ArrayList list = new ArrayListo(100); // capacity 100,size 0 45 | list.set(0, x); // no element 0 yet 46 | ``` 47 | ```java 48 | @Test 49 | public void testList() { 50 | List list = new ArrayList<>(); 51 | 52 | Employee employee1 = new Employee("小明","男",22); 53 | Employee employee2 = new Employee("小红","女",26); 54 | 55 | //list.set(0,employee1); 56 | 57 | list.add(employee1); 58 | list.add(employee2); 59 | 60 | System.out.println(list.size());//2 61 | System.out.println(list); 62 | //[com.edu.test.Employee{name='小明', sex='男', age=22}, com.edu.test.Employee{name='小红', sex='女', age=26}] 63 | System.out.println("-----------------"); 64 | 65 | list.set(0,employee2); 66 | list.set(1,employee1); 67 | System.out.println(list); 68 | //[com.edu.test.Employee{name='小红', sex='女', age=26}, com.edu.test.Employee{name='小明', sex='男', age=22}] 69 | System.out.println("-----------------"); 70 | 71 | Employee newEmployee1 = list.get(0); 72 | System.out.println(newEmployee1); 73 | //com.edu.test.Employee{name='小红', sex='女', age=26} 74 | } 75 | ``` 76 | * 使用 add 方法为数组添加新元素,而不要使用 set 方法,它只能替换数组中已经存在的元素内容。 77 | * 2、下面这个技巧可以一举两得,既可以灵活地扩展数组,又可以方便地访问数组元素。首先,创建一个数组,并添加所有的元素。然后使用 toArray方法将数组元素拷贝到一个数组中。 78 | ```java 79 | Employee[] array = new Employee[list.size()]; 80 | list.toArray(array); 81 | for (int i=0;i) 将执行相同的运行时检查。 131 | * 3、一旦能确保不会造成严重的后果,可以用@SuppressWamings("unchecked") 标注来标记这个变量能够接受类型转换。 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.4对象包装器与自动装箱.md: -------------------------------------------------------------------------------- 1 |

5.4 对象包装器与自动装箱

2 | 3 | * 1、所有的基本类型都冇一个与之对应的类。例如,丨nteger 类对应基本类型 int。通常,这些类称为包装器 (wrapper)。这些对象包装器类拥有很明显的名字:Integer、Long、Float、Double、Short、Byte、Character、Void 和 Boolean (前6个类派生于公共的超类 Number)。 4 | * 2、对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。 5 | * 3、对象包装器类还是 final, 因此不能定义它们的子类。 6 | * 4、由于每个值分别包装在对象中,所以 `ArrayList`的效率远远低于 `int[]`数组。因此,应该用它构造小型集合,其原因是此时程序员操作的方便性要比执行效率更加重要。 7 | * 5、有一个很有用的特性,从而更加便于添加 int 类型的元素到 ArrayLisKlntegeP中。下面这个调用`list.add(3);`将自动地变换成`list.add(Integer.value0f(3));`这种变换被称为**自动装箱**(autoboxing)。 8 | * 6、当将一个 Integer 对象赋给一个 int 值时,将会**自动地拆箱**。也就是说,编译器将下列语句:`int n = list.get(i);`翻译成`int n = list.get(i).intValue();` 9 | * 7、**在算术表达式中也能够自动地装箱和拆箱**。例如,可以将自增操作符应用于一个包装器引用:`Integer n = 3; n++;`编译器将自动地插人一条对象拆箱的指令,然后进行自增计算,最后再将结果装箱。 10 | * 8、自动装箱规范要求 boolean、byte、char <= 127,介于-128 ~ 127之间的 short 和 int 被包装到固定的对象中。例如,如果在前面的例子中将 a和 b初始化为 100,对它们进行比较的结果一定成立。 11 | * 9、由于包装器类引用可以为 null, 所以自动装箱有可能会抛出一个 NullPointerException异常: 12 | ```java 13 | Integer n = null; 14 | System.out.printing*n); // Throws NullPointerException 15 | ``` 16 | * 10、如果在一个条件表达式中混合使用 Integer和 Double类型,Integer 值就会拆箱,提升为 double, 再装箱为 Double: 17 | ```java 18 | Integer n = 1; 19 | Double x = 2.0; 20 | System.out.println(true ? n : x); // Prints 1.0 21 | ``` 22 | * 11、**装箱和拆箱是编译器认可的,而不是虚拟机。编译器在生成类的字节码时,插人必要的方法调用。虚拟机只是执行这些字节码**。 23 | * 12、使用数值对象包装器还有另外一个好处,可以将某些基本方法放置在包装器中,例如,将一个数字字符串转换成数值。要想将字符串转换成整型,可以使用下面这条语句: 24 | ```java 25 | int x = Integer.parselnt(s); 26 | ``` 27 | * 这里与 Integer 对象没有任何关系,parselnt 是一个静态方法。但 Integer 类是放置这个方法的一个好地方。 28 | * API 注释说明了 Integer 类中包含的一些重要方法。其他数值类也实现了相应的方法。 29 | * 13、有些人认为包装器类可以用来实现修改数值参数的方法,然而这是错误的,由于 Java方法都是值传递,所以不可能编写一个下面这样的能够增加整型参数值的 Java方法。 30 | ```java 31 | public static void triple(int x){// won't work 32 | x = 3*x;//modifies local variable 33 | } 34 | 将 int 替换成 Integer 又会怎样呢? 35 | public static void triple(Integer x){//won't work 36 | ...... 37 | } 38 | ``` 39 | * 问题是 Integer 对象是不可变的:包含在包装器中的内容不会改变: 不能使用这些包装器类创建修改数值参数的方法。 40 | * 14、如果想编写一个修改数值参数值的方法,就需要使用在 org.omg.CORBA 包中定义的持有者(holder) 类型,包括 IntHolder、BooleanHolder 等。每个持有者类型都包含'一个公有 (!)域值,通过它可以访问存储在其中的值。 41 | ```java 42 | public static void triple(IntHolder x){ 43 | x.value = 3*x.value; 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.5参数数量可变的方法.md: -------------------------------------------------------------------------------- 1 |

5.5 参数数量可变的方法

2 | 3 | * 1、可变参数的printf方法的定义: 4 | ```java 5 | public class PrintStream{ 6 | public PrintStream printf(String fmt, Object... args) { 7 | return format(fmt, args); 8 | } 9 | } 10 | ``` 11 | * 这里的省略号 `. . .`是 Java代码的一部分,它表明这个方法可以接收任意数量的对象(除 fmt参数之外)。 12 | * 实际上,printf方法接收两个参数,一个是格式字符串,另一个是 Object ] 数组,其中保存着所有的参数(如果调用者提供的是整型数组或者其他基本类型的值,自动装箱功能将把它们转换成对象)。现在将扫描 Ant 字符串,并将第 i 个格式说明符与 args[i] 的值匹配起来。 13 | * 对于 printf 的实现者来说,`Object…`参数类型与 `Object[]`完全一样。 14 | * 2、编译器需要对 printf 的每次调用进行转换,以便将参数绑定到数组上,并在必要的时候进行自动装箱: 15 | ```java 16 | System.out.printf("M Xs", new ObjectO { new Integer(n), "widgets" } ); 17 | ``` 18 | * 3、允许将一个数组传递给可变参数方法的最后一个参数。例如: 19 | ```java 20 | System.out.printf("%d %s'\ new Object[] { new Integer(l), "widgets" } ); 21 | ``` 22 | * 4、可以将已经存在且最后一个参数是数组的方法重新定义为可变参数的方法,而不会破坏任何已经存在的代码。 -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.6枚举类.md: -------------------------------------------------------------------------------- 1 |

5.6 枚举类

2 | 3 | * 1、定义枚举类型 4 | ```java 5 | public enum Size { 6 | SMALL,MEDIUM,LARGE,EXTRA_LARGE 7 | } 8 | ``` 9 | * 实际上,这个声明定义的类型是一个类,它刚好有 4 个实例,在此尽量不要构造新对象。 10 | * 在比较两个枚举类型的值时,永远不需要调用 equals, 而直接使用“`==`” 就 11 | 可以了。 12 | * 2、如果需要的话,可以在枚举类型中添加一些构造器、方法和域。当然,构造器只是在构造枚举常量的时候被调用。 13 | ```java 14 | public enum Size { 15 | SMALL("S"),MEDIUM("M"),LARGE("L"), EXTRA_LARGE("XL"); 16 | 17 | private String abbreviation; 18 | Size(String abbreviation) { 19 | this.abbreviation = abbreviation; 20 | } 21 | public String getAbbreviation() { 22 | return abbreviation; 23 | } 24 | } 25 | ``` 26 | * 3、所有的枚举类型都是 Enum 类的子类。它们继承了这个类的许多方法。其中最有用的一个是toString,这个方法能够返回枚举常量名。例如,`Size.SMALL.toString()` 将返回字符串“SMALL”。toString的逆方法是静态方法 valueOf。例如,语句: 27 | ```java 28 | Size s = Enum.valueOf(Size,class, "SMALL");//将 s 设置成 Size.SMALL。 29 | ``` 30 | * 4、每个枚举类型都有一个静态的 values方法,它将返回一个包含全部枚举值的数组。例如,如下调用: 31 | ```java 32 | Size[] values = Size.values(); 33 | ``` 34 | * 返回包含元素Size.SMALL,Size.MEDIUM,Size.LARGE和Size.EXTRA_LARGE的数组。 35 | * 5、ordinal 方法返冋 enum声明中枚举常量的位置,位置从 0开始计数。例如:`Size.MEDIUM.ordinal()` 返回 1。 36 | * 6、如同 Class类一样,鉴于简化的考虑,Enum 类省略了一个类型参数。例如,实际上,应该将枚举类型 Size}广展为 Enum。 37 | * 7、**enum类型本质是静态类**,普通内部类里当然不能定义静态类。静态类是编译期确定的,普通内部类是运行时确定的,所以静态内部类的普通成员父类要到运行期才能确定,就不可能编译确定该普通内部类的静态内部类,就是说,该静态内部类的上层父类当中不能有一层是普通内部类,编译器编译时中间就断链了。即在内部类中只能静态包含静态,不能普通包含静态。 38 | * 8、枚举类的构造方法默认且必须为`private`修饰权限。 39 | 40 | ```java 41 | package com.edu.test.enumtest; 42 | 43 | /** 44 | * @Author: 王仁洪 45 | * @Date: 2019/3/16 19:06 46 | */ 47 | public enum Color { 48 | BLACK(3, "black"), GREEN(2, "green"), RED(1, "red");//定义枚举中的常量 49 | 50 | private int code; 51 | private String name; 52 | 53 | public int getCode() { 54 | return code; 55 | } 56 | 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | private Color(int code, String name) { 63 | this.code = code; 64 | this.name = name; 65 | } 66 | 67 | //在枚举列类中定义一个自定义方法,但如果要想能够被外面访问,需要定义成static类型。 68 | public static void containval() { 69 | 70 | Color[] color = Color.values(); 71 | 72 | for (Color c : color) { 73 | System.out.println(c.getCode() + ":" + c.getName()); 74 | } 75 | /**输出: 76 | * 3:black 77 | * 2:green 78 | * 1:red 79 | */ 80 | } 81 | } 82 | 83 | class Test{ 84 | public static void main(String[] args) { 85 | Color.containval(); 86 | } 87 | } 88 | ``` 89 | 90 | 91 | ```java 92 | package com.edu.test.enumtest; 93 | 94 | /** 95 | * @Author: 王仁洪 96 | * @Date: 2019/3/16 18:36 97 | */ 98 | public enum Size { 99 | SMALL("S"),MEDIUM("M"),LARGE("L"), EXTRA_LARGE("XL"); 100 | 101 | private String abbreviation; 102 | Size(String abbreviation) { 103 | this.abbreviation = abbreviation; 104 | } 105 | public String getAbbreviation() { 106 | return abbreviation; 107 | } 108 | } 109 | 110 | class testEnum{ 111 | public static void main(String[] args) { 112 | System.out.println(Size.SMALL==Size.MEDIUM);//false 113 | System.out.println(Size.SMALL);//SMALL 114 | System.out.println(Size.SMALL.getAbbreviation());//S 115 | System.out.println(Size.LARGE.ordinal());//2 116 | System.out.println(Size.EXTRA_LARGE.compareTo(Size.SMALL));//3 117 | System.out.println(Size.EXTRA_LARGE.compareTo(Size.MEDIUM));//2 118 | System.out.println(Size.LARGE.equals(Size.SMALL));//false 119 | } 120 | } 121 | ``` -------------------------------------------------------------------------------- /Java核心卷/第五章继承/5.8继承的设计技巧.md: -------------------------------------------------------------------------------- 1 |

5.8 继承的设计技巧

2 | 3 | * 1、将公共操作和域放在超类 4 | * 2、不要使用受保护的域 5 | * protected机制并不能够带来更好的保护,其原因主要有两点。 6 | * 第一,子类集合是无限制的,任何一个人都能够由某个类派生一个子类,并编写代码以直接访问protected的实例域,从而破坏了封装性。 7 | * 第二,在 Java程序设计语言中,在同一个包中的所有类都可以访问 proteced 域,而不管它是否为这个类的子类。 8 | * protected方法对于指示那些不提供一般用途而应在子类中重新定义的方法很有用。 9 | * 3、使用继承实现“ is-a”关系 10 | * 4、除非所有继承的方法都有意义,否则不要使用继承。 11 | * 5、在覆盖方法时,不要改变预期的行为。 12 | * 6、使用多态,而非类型信息 13 | * 7、不要过多地使用反射 -------------------------------------------------------------------------------- /Java核心卷/第五章继承/img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第五章继承/img/001.png -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.1为什么要使用泛型程序设计.md: -------------------------------------------------------------------------------- 1 |

8.1 为什么要使用泛型程序设计

2 | 3 | * 1、使用泛型机制编写的程序代码要比那些杂乱地使用Object 变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。 4 | * 2、泛型对于集合类尤其有用,例如,ArrayList 就是一个无处不在的集合类。 5 | * 3、泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用。 6 | 7 | 8 | ### 8.1.1 类型参数的好处 9 | 10 | * 1、**类型参数(type parameters)**。ArrayList 类有一个类型参数用来指示元素的类型: 11 | ```java 12 | ArrayList files = new ArrayList(): 13 | ``` 14 | * 这使得代码具有更好的可读性。人们一看就知道这个数组列表中包含的是 String对象。 15 | * 2、当调用 get 的时候,不需要进行强制类型转换,编译器就知道返回值类型为 String,而不是 Object: 16 | ```java 17 | String filename = files.get(0); 18 | ``` 19 | * 3、编译器还知道 ArrayList中 add方法有一个类型为 String的参数。这将比使用Object 类型的参数安全一些。 20 | * 4、编译器可以进行检査,避免插人错误类型的对象。例如: 21 | ```java 22 | files.add(new File(". . .")); // can only add String objects to an ArrayList 23 | ``` 24 | * 是无法通过编译的。出现编译错误比类在运行时出现类的强制类型转换异常要好得多。 25 | * 5、类型参数的魅力在于:使得程序具有更好的可读性和安全性。 26 | 27 | ### 8.1.2 谁想成为泛型程序员 28 | 29 | * 1、数组列表比数组要好一些,因为数组列表可以自动扩展。 30 | * 2、**一个泛型程序员的任务就是预测出所用类的未来可能有的所有用途**。 31 | * 3、AirayList类有一个方法 addAll 用来添加另一个集合的全部元素。程序员可能想要将`ArrayList`中的所有元素添加到 `ArrayList`中去。然而,反过来就不行了。 32 | * 4、如果只能允许前一个调用,而不能允许后一个调用呢? Java语言的设计者发明了一个具有独创性的新概念,**通配符类型(wildcard type)**, 它解决了这个问题。通配符类型非常抽象,然而,它们能让库的构建者编写出尽可能灵活的方法。 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.2定义简单泛型.md: -------------------------------------------------------------------------------- 1 |

8.2 定义简单泛型类

2 | 3 | * 1、一个泛型类(generic class) 就是具有一个或多个类型变量的类。 4 | * 2、例子 5 | ```java 6 | package com.edu.test.generic; 7 | 8 | /** 9 | * @Author: 王仁洪 10 | * @Date: 2019/3/24 11:02 11 | */ 12 | public class Pair { 13 | private T first; 14 | private T second; 15 | public Pair() { 16 | first = null; 17 | second = null; 18 | } 19 | public Pair(T first, T second) { 20 | this.first = first; 21 | this.second = second; 22 | } 23 | public T getFirst() { 24 | return first; 25 | } 26 | public T getSecond() { 27 | return second; 28 | } 29 | public void setFirst(T newValue) { 30 | first = newValue; 31 | } 32 | public void setSecond(T newValue) { 33 | second = newValue; 34 | } 35 | } 36 | ``` 37 | * Pair 类引人了一个类型变量 T,用尖括号 (`<>`) 括起来,并放在类名的后面。 38 | * 3、泛型类可以有多个类型变量。例如,可以定义 Pair类,其中第一个域和第二个域使用不同的类型: 39 | ```java 40 | public class Pair { . . . } 41 | ``` 42 | * 4、类定义中的类型变量指定方法的返回类型以及域和局部变量的类型。例如, 43 | ```java 44 | private T first; // uses the type variable 45 | ``` 46 | * 5、类型变量使用大写形式,且比较短,这是很常见的。在 Java库中,使用变量 E表示集合的元素类型,K 和 V 分别表示表的关键字与值的类型。T ( 需要时还可以用临近的字母 U 和 S) 表示“任意类型”。 47 | * 6、用具体的类型替换类型变量就可以实例化泛型类型,例如: 48 | ```java 49 | Pair 50 | 51 | 可以将结果想象成带有构造器的普通类: 52 | Pair() 53 | Pair(String, String) 54 | 和方法: 55 | String getFirst() 56 | String getSecond() 57 | void setFirst(String) 58 | void setSecond(String) 59 | 60 | 换句话说,泛型类可看作普通类的工厂。 61 | ``` 62 | 63 | ```java 64 | package com.edu.test.generic; 65 | 66 | /** 67 | * @Author: 王仁洪 68 | * @Date: 2019/3/24 11:02 69 | */ 70 | public class Pair { 71 | private T first; 72 | private T second; 73 | public Pair() { 74 | first = null; 75 | second = null; 76 | } 77 | public Pair(T first, T second) { 78 | this.first = first; 79 | this.second = second; 80 | } 81 | public T getFirst() { 82 | return first; 83 | } 84 | public T getSecond() { 85 | return second; 86 | } 87 | public void setFirst(T newValue) { 88 | first = newValue; 89 | } 90 | public void setSecond(T newValue) { 91 | second = newValue; 92 | } 93 | } 94 | 95 | class ArrayAlg{ 96 | /** 97 | * Gets the minimum and maximum of an array of strings. 98 | * @param arr an array of strings 99 | * ©return a pair with the min and max value, or null if a is null or empty 100 | */ 101 | public static Pair minMax(String[] arr){ 102 | if (null==arr || arr.length==0) 103 | return null; 104 | String min = arr[0]; 105 | String max = arr[0]; 106 | 107 | for (int i=0;i0){ 109 | min = arr[i]; 110 | } 111 | if (max.compareTo(arr[i])<0){ 112 | max = arr[i]; 113 | } 114 | } 115 | return new Pair(min,max); 116 | } 117 | } 118 | 119 | class testPair{ 120 | public static void main(String[] args) { 121 | String[] words = {"Mary","had","a","little","lamb"}; 122 | Pair minMax = ArrayAlg.minMax(words); 123 | System.out.println("min=" + minMax.getFirst()); 124 | System.out.println("max=" + minMax.getSecond()); 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 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.3泛型方法.md: -------------------------------------------------------------------------------- 1 |

8.3 泛型方法

2 | 3 | * 1、定义一个带有类型参数的简单方法: 4 | ```java 5 | class ArrayAlg{ 6 | public static T getMiddle(T...var){ 7 | return var[var.length/2]; 8 | } 9 | } 10 | ``` 11 | * 这个方法是在普通类中定义的,而不是在泛型类中定义的。 12 | * 这是一个泛型方法,可以从尖括号和类型变量看出这一点。 13 | * 注意,**类型变量放在修饰符(这里是 public static) 的后面,返回类型的前面**。 14 | * 2、**泛型方法可以定义在普通类中,也可以定义在泛型类中**。 15 | * 3、当调用一个泛型方法时,在方法名前的尖括号中放人具体的类型: 16 | ```java 17 | String middle = ArrayAlg.getMiddle("]ohn", "Q","Public"); 18 | ``` 19 | * 在这种情况(实际也是大多数情况)下,方法调用中可以省略类型参数。编译器有足够的信息能够推断出所调用的方法。它用 names的类型(即 `String[]`) 与泛型类型 `T[]`进行匹配并推断出 T 一定是 String。也就是说,可以调用: 20 | ```java 21 | String middle = ArrayAlg.getHiddle("]ohn","Q.","Public"); 22 | ``` 23 | * 4、几乎在大多数情况下,对于泛型方法的类型引用没有问题。偶尔,编译器也会提示错误,此时需要解译错误报告。看一看下面这个示例: 24 | ```java 25 | double middle = ArrayAlg.getMiddle(3.14, 1729, 0); 26 | ``` 27 | * 错误消息会以晦涩的方式指出(不同的编译器给出的错误消息可能有所不同):解释这句代码有两种方法,而且这两种方法都是合法的。简单地说,编译器将会自动打包参数为 1 个Double和 2个 Integer对象,而后寻找这些类的共同超类型。事实上;找到 2个这样的超类型:Number 和 Comparable接口,其本身也是一个泛型类型。在这种情况下,**可以采取的补救措施是将所有的参数写为 double值**。 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 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.4类型变量的限定.md: -------------------------------------------------------------------------------- 1 |

8.4 类型变量的限定

2 | 3 | * 1、计算数组中的最小元素: 4 | ```java 5 | public static T min(T[] a){ 6 | if (null==a || a.length==0) 7 | return null; 8 | T smallest = a[0]; 9 | for (int i=0;iT min(T[] a){ 19 | ...... 20 | } 21 | ``` 22 | * 实际上 Comparable接口本身就是一个泛型类型。目前,我们忽略其复杂性以及编译器产生的警告。 23 | * 现在,泛型的 min方法只能被实现了 Comparable接口的类(如 String、LocalDate等)的数组调用。由于 Rectangle类没有实现 Comparable接口,所以调用 min将会产生一个编译错误。 24 | * 2、` { 42 | private T first; 43 | private T second; 44 | public Pair() { 45 | first = null; 46 | second = null; 47 | } 48 | public Pair(T first, T second) { 49 | this.first = first; 50 | this.second = second; 51 | } 52 | public T getFirst() { 53 | return first; 54 | } 55 | public T getSecond() { 56 | return second; 57 | } 58 | public void setFirst(T newValue) { 59 | first = newValue; 60 | } 61 | public void setSecond(T newValue) { 62 | second = newValue; 63 | } 64 | } 65 | 66 | class ArrayAlg{ 67 | /** 68 | * Gets the minimum and maximum of an array of objects of type T. 69 | * @param a an array of objects of type T 70 | * @return a pair with the min and max value, or null if a is null or empty 71 | */ 72 | public static Pair minMax(T[] a){ 73 | if (null==a || a.length==0) 74 | return null; 75 | T min = a[0]; 76 | T max = a[0]; 77 | for (int i=0;i0) 79 | min = a[i]; 80 | if (max.compareTo(a[i])<0) 81 | max = a[i]; 82 | } 83 | return new Pair<>(min,max); 84 | } 85 | } 86 | 87 | class PairTest2{ 88 | public static void main(String[] args) { 89 | LocalDate[] birthday = { 90 | LocalDate.of(1996,12,8), 91 | LocalDate.of(1815,12,10), 92 | LocalDate.of(1903,12,3), 93 | LocalDate.of(1910,6,22), 94 | }; 95 | 96 | Pair mm = ArrayAlg.minMax(birthday); 97 | System.out.println("min=" + mm.getFirst()); 98 | System.out.println("max=" + mm.getSecond()); 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 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.5泛型代码和虚拟机.md: -------------------------------------------------------------------------------- 1 |

8.5 泛型代码和虚拟机

2 | 3 | #### 虚拟机没有泛型类型对象 —— 所有对象都属于普通类。 4 | 5 | ### 8.5.1 类型擦除 6 | 7 | * 1、无论何时定义一个泛型类型,都自动提供了一个相应的**原始类型 ( raw type)**。原始类型的名字就是删去类型参数后的泛型类型名。**擦除(erased) 类型变量**,并替换为限定类型(无限定的变量用 Object)。例如,Pair的原始类型如下所示: 8 | ```java 9 | public class Pair{ 10 | private Object first; 11 | private Object second; 12 | public Pair(Object first, Object second){ 13 | this.first = first; 14 | this.second = second; 15 | } 16 | public Object getFirst() { 17 | return first; 18 | } 19 | public Object getSecond() { 20 | return second; 21 | } 22 | public void setFirst(Object newValue) { 23 | first = newValue; 24 | } 25 | public void setSecond(Object newValue) { 26 | second = newValue; 27 | } 28 | } 29 | ``` 30 | 31 | * 因为 T 是一个无限定的变量,所以直接用 Object 替换。 32 | * 结果是一个普通的类,就好像泛型引人 Java语言之前已经实现的那样。 33 | * 在程序中可以包含不N类型的 Pair, 例 如,Pair或 Pair。而擦除类型后就变成原始的 Pair 类型了。 34 | * 2、**原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object 替换**。例如,类 `Pair`中的类型变量没有显式的限定,因此,原始类型用 Object 替换 T。假定声明了一个不同的类型。 35 | ```java 36 | public class Interval implements Serializable{ 37 | private T lower; 38 | private T upper; 39 | ...... 40 | public Interval (T first, T second) { 41 | if (first.compareTo(second) <= 0) { 42 | lower = first; 43 | upper = second; } 44 | else{ 45 | lower = second; 46 | upper = first; 47 | } 48 | } 49 | } 50 | 51 | 原始类型 Interval 如下所示: 52 | public class Interval implements Serializable { 53 | private Comparable lower; 54 | private Comparable upper; 55 | ...... 56 | public Interval (Comparable first, Comparable second) { 57 | . . . 58 | } 59 | } 60 | ``` 61 | * 3、读者可能想要知道切换限定:`classInterval`会发生什么。如果这样做,原始类型用 Serializable替换 T, 而编译器在必要时要向Comparable插入强制类型转换。为了提高效率,应该将标签(tagging) 接口(即没有方法的接口)放在边界列表的末尾。 62 | 63 | ### 8.5.2 翻译泛型表达式 64 | 65 | * 1、**当程序调用泛型方法时,如果擦除返回类型,编译器插入强制类型转换**。例如,下面这个语句序列: 66 | ```java 67 | Pair buddies = ... 68 | Employee buddy = buddies.getFirst(); 69 | ``` 70 | * 擦除 getFirst的返回类型后将返回 Object类型。编译器自动插人 Employee的强制类型转换。也就是说,编译器把这个方法调用翻译为两条虚拟机指令: 71 | * 对原始方法 Pair.getFirst 的调用。 72 | * 将返回的 Object 类型强制转换为 Employee 类型。 73 | 74 | * 2、**当存取一个泛型域时也要插人强制类型转换**。假设 Pair类的 first 域和 second域都是公有的(也许这不是一种好的编程风格,但在 Java中是合法的)。表达式: 75 | ```java 76 | Employee buddy = buddies.first; 77 | ``` 78 | * 也会在结果字节码中插人强制类型转换。 79 | 80 | ### 8.5.3 翻译泛型方法 81 | 82 | * 1、在一个方法覆盖另一个方法时可以指定一个更严格的返回类型。 83 | * 2、在一个方法覆盖另一个方法时覆盖方法的作用域须大于或等于被覆盖方法的作用域。 84 | * 3、有关 Java泛型转换的事实: 85 | * 虚拟机中没有泛型,只有普通的类和方法。 86 | * 所有的类型参数都用它们的限定类型替换。 87 | * 桥方法被合成来保持多态。 88 | * 为保持类型安全性,必要时插人强制类型转换。 89 | 90 | 91 | ### 8.5.4 调用遗留代码 92 | 93 | * 1、在查看了警告之后,可以利用注解 ( annotation) 使之消失。注释必须放在生成这个警告的代码所在的方法之前,如下: 94 | ```java 95 | @SuppressWarnings("unchecked") 96 | Dictionary labelTable = slider.getLabelTable(); // No warning 97 | 98 | 或者,可以标注整个方法,如下所示: 99 | @SuppressWarnings("unchecked") 100 | public void configureSlider() { 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 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.7泛型类型的继承规则.md: -------------------------------------------------------------------------------- 1 |

8.7 泛型类型的继承规则

2 | 3 | * 1、考虑一个类和一个子类,如 Employee 和 Manager。`Pair`是`Pair`的一个子类吗? 答案是“ 不是”,或许人们会感到奇怪。例如,下面的代码将不能编译成功: 4 | ```java 5 | Manager[] topHonchos = ...; 6 | Pair result = ArrayAlg.minmax(topHonchos); // Error 7 | ``` 8 | * minmax方法返回 `Pair`,而不是 `Pair`,并且这样的赋值是不合法的。 9 | * 2、无论 S 与 T 有什么联系 (如图 8-1 所示),通常,`Pair`与 `Pair`没有什么联系。 10 | 11 |
12 | 13 | * 这一限制看起来过于严格,但对于类型安全非常必要。假设允许将 `Pair`转换为 `Pair`。考虑下面代码: 14 | ```java 15 | Pair managerBuddies = new Pair<>(ceo, cfo); 16 | Pair employeeBuddies = managerBuddies; // illegal, but suppose it wasn't 17 | employeeBuddies.setFirst(lowlyEmployee); 18 | ``` 19 | * 显然,最后一句是合法的。但是 employeeBuddies和 managerBuddies引用了同样的对象。现在将 CFO 和一个普通员工组成一对,这对于说应该是不可能的。 20 | * 3、必须注意泛型与 Java数组之间的重要区别。可以将一个 Manager[]数组賦给一个类型为 Employee[] 的变量: 21 | ```java 22 | Manager[] managerBuddies = { ceo, cfo }; 23 | Employee[] employeeBuddies = managerBuddies; // OK 24 | ``` 25 | * 然而,数组带有特别的保护。如果试图将一个低级别的雇员存储到 employeeBuddies[0],虚拟机将会抛出 ArrayStoreException异常。 26 | * 4、**永远可以将参数化类型转换为一个原始类型**。例如,`Pair`是原始类型 Pair 的一个子类型。在与遗留代码衔接时,这个转换非常必要。 27 | * 转换成原始类型之后会产生类型错误吗? 很遗憾,会! 看一看下面这个示例: 28 | ```java 29 | Pair managerBuddies = new Pair<>(ceo, cfo); 30 | Pair rawBuddies = managerBuddies; // OK 31 | rawBuddies.setFirst(new File(". . .")); // only a compile-time warning 32 | ``` 33 | * 当使用 getFirst获得外来对象并赋给 Manager变量时,与通常一样,会抛出 ClassCastException异常。这里失去的只是泛型程序设计提供的附加安全性。 34 | * 5、泛型类可以扩展或实现其他的泛型类。就这一点而言,与普通的类没有什么区别。例如,`ArrayList`类实现 `List`接口。这意味着,一个 `ArrayList`可以被转换为一个 `List`。但是,如前面所见,一个 `ArrayList`不是一个`ArrayList`或 `List`。图 8-2展示了它们之间的联系。 35 | 36 |
37 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/8.9反射和泛型.md: -------------------------------------------------------------------------------- 1 |

8.9 反射和泛型

2 | 3 | * 1、反射允许你在运行时分析任意的对象。 4 | * 2、如果对象是泛型类的实例,关于泛型类型参数则得不到太多信息,因为它们会被擦除。 5 | 6 | ### 8.9.1 泛型 Class类 7 | 8 | * 1、Class类是泛型的。例如,`String.class` 实际上是一个 `Class` 类的对象(事实上,是唯一的对象)。 9 | * 2、类型参数十分有用,这是因为它允许 ClaSS方法的返回类型更加具有针对性。下面`Class`中的方法就使用了类型参数: 10 | ```java 11 | T newInstance() 12 | T cast(Object obj) 13 | T[] getEnumConstants() 14 | Class getSuperclass() 15 | Constructor getConstructor(Class... parameterTypes) 16 | Constructor getDeclaredConstructor (Class... parameterTypes) 17 | ``` 18 | * newlnstance方法返回一个实例,这个实例所属的类由默认的构造器获得。它的返回类型目前被声明为 T,其类型与 Class描述的类相同,这样就免除了类型转换。 19 | * 如果给定的类型确实是 T 的一个子类型,cast方法就会返回一个现在声明为类型 T的对象,否则,抛出一个 BadCastException异常。 20 | * 如果这个类不是 enum类或类型 T 的枚举值的数组,getEnumConstants方法将返回 null。 21 | * etConstructor与 getdeclaredConstructor方 法 返 回 一 个 `Constructor`对象。Constructor类也已经变成泛型,以便 newlnstance方法有一个正确的返回类型。 22 | 23 | 24 | ### 8.9.2 使用 `Class`参数进行类型匹配 25 | 26 | * 1、有时,匹配泛型方法中的 `Class`参数的类型变量很有实用价值。下面是一个 标准的示例: 27 | ```java 28 | public static Pair makePair(Class c) throws InstantiationException,IllegalAccessException { 29 | return new Pair<>(c.newInstance(),c.newInstance()); 30 | } 31 | ``` 32 | * 如果调用 33 | ```java 34 | makePair(Employee.class) 35 | ``` 36 | * Employee.class是类型 `Class`的一个对象。makePair方法的类型参数 T同 Employee匹配,并且编译器可以推断出这个方法将返回一个 `Pair`。 37 | 38 | ### 8.9.3 虚拟机中的泛型类型信息 39 | 40 | * 1、Java泛型的卓越特性之一是在虚拟机中泛型类型的擦除。 41 | 42 | 43 | ```java 44 | package com.edu.test.generic; 45 | 46 | import java.lang.reflect.*; 47 | import java.util.Arrays; 48 | import java.util.Scanner; 49 | 50 | /** 51 | * @Author: 王仁洪 52 | * @Date: 2019/3/26 21:01 53 | */ 54 | public class GenericReflectionTest { 55 | public static void main(String[] args) { 56 | // read class name from command line args or user input 57 | String name; 58 | if (args.length > 0){ 59 | name = args[0]; 60 | } else { 61 | try (Scanner in = new Scanner(System.in)) { 62 | System.out.println("Enter class name (e.g. java.util.Collections): "); 63 | name = in.next(); 64 | } 65 | } 66 | 67 | try { 68 | // print generic info for class and public methods 69 | Class cl = Class.forName(name); 70 | printClass(cl); 71 | for (Method m : cl.getDeclaredMethods()) 72 | printMethod(m); 73 | } catch (ClassNotFoundException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | public static void printClass(Class cl){ 79 | System.out.print(cl); 80 | printTypes(cl.getTypeParameters(), "<", ", ", ">", true); 81 | Type sc = cl.getGenericSuperclass(); 82 | if (sc != null) { 83 | System.out.print(" extends "); 84 | printType(sc, false); 85 | } 86 | printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false); 87 | System.out.println(); 88 | } 89 | 90 | public static void printMethod(Method m) { 91 | String name = m.getName(); 92 | System.out.print(Modifier.toString(m.getModifiers())); 93 | System.out.print(" "); 94 | printTypes(m.getTypeParameters(), "<", ", ", "> ", true); 95 | 96 | printType(m.getGenericReturnType(), false); 97 | System.out.print(" "); 98 | System.out.print(name); 99 | System.out.print("("); 100 | printTypes(m.getGenericParameterTypes(), "", ", ", "", false); 101 | System.out.println(")"); 102 | } 103 | 104 | public static void printType(Type type,boolean isDefinition){ 105 | if (type instanceof Class){ 106 | Class t = (Class)type; 107 | System.out.print(t.getName()); 108 | }else if (type instanceof TypeVariable){ 109 | TypeVariable t = (TypeVariable)type; 110 | System.out.print(t.getName()); 111 | if (isDefinition){ 112 | printTypes(t.getBounds(),"extends","&","",false); 113 | } 114 | }else if (type instanceof WildcardType) { 115 | WildcardType t =(WildcardType) type; 116 | System.out.print("?"); 117 | printTypes(t.getUpperBounds(), "extends","&","",false); 118 | printTypes(t.getLowerBounds(), " super ", " & ","",false); 119 | } 120 | else if (type instanceof ParameterizedType) { 121 | ParameterizedType t = (ParameterizedType) type; 122 | Type owner = t.getOwnerType(); 123 | if (owner != null) { 124 | printType(owner, false); 125 | System.out.print("."); 126 | } 127 | printType(t.getRawType(), false); 128 | printTypes(t.getActualTypeArguments() , "<" ,"," ,">" , false); 129 | } else if (type instanceof GenericArrayType) { 130 | GenericArrayType t = (GenericArrayType) type; 131 | System.out.print(""); 132 | printType(t.getGenericComponentType(),isDefinition); 133 | System.out.print("[]"); 134 | } 135 | } 136 | 137 | public static void printTypes(Type[] types,String pre,String seq,String suf,boolean isDefinition){ 138 | if (pre.equals("extends") && Arrays.equals(types,new Type[]{Object.class})){ 139 | return; 140 | } 141 | if (types.length>0){ 142 | System.out.print(pre); 143 | } 144 | for (int i=0;i0){ 146 | System.out.print(seq); 147 | } 148 | printType(types[i],isDefinition); 149 | } 150 | if (types.length>0){ 151 | System.out.print(suf); 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 | -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/img/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第八章泛型程序设计/img/01.png -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/img/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第八章泛型程序设计/img/02.png -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/img/8-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第八章泛型程序设计/img/8-3.png -------------------------------------------------------------------------------- /Java核心卷/第八章泛型程序设计/img/8-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第八章泛型程序设计/img/8-4.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/6.2接口示例.md: -------------------------------------------------------------------------------- 1 |

6.2 接口示例

2 | 3 | ### 6.2.1 接口与回调 4 | 5 | * 1、回调(callback) 是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的动作。 6 | * 2、对象可以携带一些附加的信息,所以传递一个对象比传递一个函数要灵活得多。 7 | 8 | ```java 9 | package com.edu.test.interfacetest; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.awt.event.ActionEvent; 14 | import java.awt.event.ActionListener; 15 | import java.util.Date; 16 | 17 | /** 18 | * @Author: 王仁洪 19 | * @Date: 2019/3/18 21:32 20 | */ 21 | public class TimerTest { 22 | public static void main(String[] args) { 23 | ActionListener listener = new TimePrinter(); 24 | 25 | //construct a timer that calls the listener 26 | //once every 10 seconds 27 | Timer timer = new Timer(10000,listener); 28 | timer.start(); 29 | 30 | JOptionPane.showMessageDialog(null,"Quit program?"); 31 | System.exit(0); 32 | } 33 | } 34 | 35 | class TimePrinter implements ActionListener{ 36 | 37 | @Override 38 | public void actionPerformed(ActionEvent e) { 39 | System.out.println("At the tone, the time is " + new Date()); 40 | Toolkit.getDefaultToolkit().beep(); 41 | } 42 | } 43 | ``` 44 | 45 | ### 6.2.2 Comparator 接口 46 | 47 | ### 6.2.3 对象克隆 48 | 49 | * 1、拷贝和克隆 50 | * 要了解克隆的具体含义,先来回忆为一个包含对象引用的变量建立副本时会发生什么。原变量和副本都是同一个对象的引用(见图 6-1)。这说明,任何一个变量改变都会影响另一个变量。 51 | ```java 52 | Employee original = new Employee("John Public", 50000); 53 | Employee copy = original; 54 | copy.raiseSalary(lO); //oops-also changed original 55 | ``` 56 | * 如果希望 copy是一个新对象,它的初始状态与 original 相同,但是之后它们各自会有自己不同的状态,这种情况下就可以使用 clone方法。 57 | ```java 58 | Employee copy = original,clone(); 59 | copy.raiseSalary(lO);// OK original unchanged 60 | ``` 61 | 62 |
63 | 64 | * 2、图 6-2显示了使用Object 类的 clone 方法克隆这样一个 Employee 对象会发生什么。可以看到,默认的克隆操作是“浅拷贝”,并没有克隆对象中引用的其他对象。 65 | 66 |
67 | 68 | * 不过,通常子对象都是可变的,必须重新定义 clone 方法来建立一个深拷贝,同时克隆所有子对象。 69 | 70 | * 3、**浅拷贝**会有什么影响吗? 这要看具体情况。如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的类,如 String, 就 是 这 种情况。或者在对象的生命期中,子对象一直包含不变的常量,没有更改器方法会改变它,也没有方法会生成它的引用,这种情况下同样是安全的。 71 | * 4、对于每一个类,需要确定: 72 | * 1)默认的 clone 方法是否满足要求; 73 | * 2)是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法; 74 | * 3)是否不该使用 clone。 75 | * 5、实际上第 3 个选项是默认选项。如果选择第 1 项或第 2项,类必须: 76 | * 1)实现 Cloneable 接口; 77 | * 2)重新定义 clone 方法,并指定 public 访问修饰符。 78 | * 6、Object 类中 clone 方法声明为 protected, 所以你的代码不能直接调用 anObject.clone()。但是,不是所有子类都能访问受保护方法吗? 不是所有类都是 Object 的子类吗? 幸运的是,受保护访问的规则比较微妙。子类只能调用受保护的clone方法来克隆它自己的对象。必须重新定义 clone 为 public 才能允许所有方法克隆对象。 79 | 80 | 81 | ```java 82 | package com.edu.test.interfacetest.clone; 83 | 84 | import java.util.Date; 85 | import java.util.GregorianCalendar; 86 | 87 | /** 88 | * @Author: 王仁洪 89 | * @Date: 2019/3/19 16:56 90 | */ 91 | public class Employee implements Cloneable{ 92 | private String name; 93 | private double salary; 94 | private Date hireDay; 95 | 96 | public Employee(String name,double salary){ 97 | this.name = name; 98 | this.salary = salary; 99 | hireDay = new Date(); 100 | } 101 | 102 | @Override 103 | public Employee clone() throws CloneNotSupportedException { 104 | // call Object.doneO 105 | Employee cloned = (Employee)super.clone(); 106 | // clone mutable fields 107 | cloned.hireDay = (Date)hireDay.clone(); 108 | return cloned; 109 | } 110 | 111 | /** 112 | * Set the hire day to a given date. 113 | * @param year the year of the hire day 114 | * @param month the month of the hire day 115 | * @param day the day of the hire day 116 | */ 117 | public void setHireDay(int year, int month, int day){ 118 | Date time = new GregorianCalendar(year, month - 1, day).getTime(); 119 | //Example of instance field mutation 120 | hireDay.setTime(time.getTime()); 121 | } 122 | 123 | public void raiseSalary(double byPercent) { 124 | double raise = salary * byPercent / 100; 125 | salary += raise; 126 | } 127 | 128 | @Override 129 | public String toString() { 130 | return "Employee{" + 131 | "name='" + name + '\'' + 132 | ", salary=" + salary + 133 | ", hireDay=" + hireDay + 134 | '}'; 135 | } 136 | } 137 | 138 | class ColneTest{ 139 | public static void main(String[] args) { 140 | Employee employee = new Employee("Jone Q. public", 5000); 141 | employee.setHireDay(2000,1,1); 142 | Employee clone = null; 143 | try { 144 | clone = employee.clone(); 145 | } catch (CloneNotSupportedException e) { 146 | e.printStackTrace(); 147 | } 148 | clone.raiseSalary(10); 149 | clone.setHireDay(2002,12,31); 150 | 151 | System.out.println("employee=" + employee); 152 | //employee=Employee{name='Jone Q. public', salary=5000.0, hireDay=Sat Jan 01 00:00:00 CST 2000} 153 | System.out.println("clone=" + clone); 154 | //clone=Employee{name='Jone Q. public', salary=5500.0, hireDay=Tue Dec 31 00:00:00 CST 2002} 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 | -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/6.3lambda表达式.md: -------------------------------------------------------------------------------- 1 |

6.3 lambda表达式

2 | 3 | ### 6.3.1 为什么引入 lambda表达式 4 | 5 | * 1、lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。 6 | * 2、在 Java 中传递一个代码段并不容易,不能直接传递代码段 _ Java 是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码= 7 | * 3、Java 的强大之处就在于其简单性和一致性。 8 | * 4、就现在来说,问题已经不是是否增强 Java 来支持函数式编程,而是要如何做到这一点。 9 | 10 | ### 6.3.2 lambda 表达式的语法 11 | 12 | * 1、**Java 是一种强类型语言**。 13 | * 2、传人代码来检查一个字符串是否比另一个字符串短。这里要计算: 14 | ```java 15 | first.lengthO-second.length() 16 | ``` 17 | * first 和 second是什么? 它们都是字符串。Java 是一种强类型语言,所以我们还要指定它们的类型: 18 | ```java 19 | (String first, String second)-> 20 | first.length() - second.length() 21 | ``` 22 | * 这就是你看到的第一个 lambda 表达式。lambda 表达式就是一个代码块,以及必须传人代码的变量规范。 23 | * 3、带参数变量的表达式就被称为 lambda表达式。 24 | * 4、你已经见过 Java中的一种 lambda表达式形式:`参数,箭头(->) 以及一个表达式`。如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在 `{}`中,并包含显式的 return语句。例如: 25 | ```java 26 | (String first, String second) ->{ 27 | if (first.length() < second.length()) return -1; 28 | else if (first.length() > second.length() return 1; 29 | else return 0; 30 | } 31 | ``` 32 | * 5、即使 lambda表达式没有参数,仍然要提供空括号,就像无参数方法一样: 33 | ```java 34 | () ->{ 35 | for (inti = 100;i>= 0;i ) 36 | System.out.println(i); 37 | } 38 | ``` 39 | * 6、如果可以推导出一个 lambda表达式的参数类型,则可以忽略其类型。例如: 40 | ```java 41 | Comparator comp 42 | =(first, second)// Same as (String first, String second) 43 | -> first.length() - second.length(); 44 | ``` 45 | * 在这里,编译器可以推导出 first 和 second必然是字符串,因为这个 lambda表达式将赋给一个字符串比较器。 46 | * 7、如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至还可以省略小括号: 47 | ```java 48 | ActionListener listener = event -> 49 | System.out.println("The time is "+new Date()"); 50 | // Instead of (event) -> . . . or (ActionEvent event) -> . . . 51 | ``` 52 | * 8、无需指定 lambda表达式的返回类型。lambda表达式的返回类型总是会由上下文推导得出。例如,下面的表达式 53 | ```java 54 | (String first, String second) -> first.length() - second.length() 55 | ``` 56 | * 可以在需要 int 类型结果的上下文中使用。 57 | * 9、如果一个 lambda表达式只在某些分支返回一个值,而在另外一些分支不返回值,这是不合法的。例如,`(int x)-> { if(x>=0) return1;}` 就不合法。 58 | 59 | ```java 60 | package com.edu.test.interfacetest.lambda; 61 | 62 | import javax.swing.*; 63 | import java.util.Arrays; 64 | import java.util.Date; 65 | 66 | /** 67 | * @Author: 王仁洪 68 | * @Date: 2019/3/19 17:35 69 | */ 70 | public class LambdaTest { 71 | public static void main(String[] args) { 72 | String[] planets = new String[]{"Mercury", "Venus", "Earth", "Mars", 73 | "Jupiter", "Saturn", "Uranus", "Neptune"}; 74 | System.out.println(Arrays.toString(planets)); 75 | 76 | System.out.print("Sorted in dictionary order:"); 77 | Arrays.sort(planets); 78 | System.out.println(Arrays.toString(planets)); 79 | 80 | System.out.print("Sorted by length:"); 81 | Arrays.sort(planets,(first,second)->first.length()-second.length()); 82 | System.out.println(Arrays.toString(planets)); 83 | 84 | Timer timer = new Timer(1000,event -> 85 | System.out.println("" + new Date())); 86 | timer.start(); 87 | 88 | //keep program running until user selects"0k" 89 | JOptionPane.showMessageDialog(null, "Quit program?"); 90 | System.exit(0); 91 | } 92 | } 93 | ``` 94 | 95 | ### 6.3.3 函数式接口 96 | 97 | * 1、Java 中有很多封装代码块的接口,如 ActionListener 或 Comparator。lambda 表达式与这些接口是兼容的。 98 | * 2、对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个 lambda 表达式。这种接口称为函数式接口 (functional interface)。 99 | * 3、把 lambda表达式看作是一个函数,而不是一个对象,另外要接受 lambda表达式可以传递到函数式接口。 100 | * 4、lambda表达式可以转换为接口,这一点让 lambda表达式很有吸引力。具体的语法很简短。 101 | * 5、不能把lambda表达式赋给类型为Object 的变量,Object 不是一个函数式接口。 102 | * 6、ArrayList类有一个 removelf 方法,它的参数就是一个 Predicate。这个接口专门用来传递lambda表达式。例如,下面的语句将从一个数组列表删除所有 null 值: 103 | ```java 104 | list.removelf(e -> e == null); 105 | ``` 106 | 107 | ### 6.3.4 方法引用 108 | 109 | * 1、有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。例如,假设你希望只要出现一个定时器事件就打印这个事件对象。当然,为此也可以调用: 110 | ```java 111 | Timer t = new Timer(1000, event -> System.out.println(event)); 112 | 113 | 但是,如果直接把 println方法传递到 Timer 构造器就更好了。具体做法如下: 114 | Timer t = new Timer(1000, Systei.out::println); 115 | ``` 116 | * 表达式 `System.out::println` 是一个方法引用(method reference), 它等价于 lambda表达式`x一> System.out.println(x)`。 117 | * 2、要用`::`操作符分隔方法名与对象或类名。主要有 3种情况: 118 | * object::instanceMethod 119 | * Class::staticMethod 120 | * Class::instanceMethod 121 | * 在前 2 种情况中,方法引用等价于提供方法参数的 lambda表达式。前面已经提到,`System.out::println`等价于 `x-> System.out.println(x)`。类似地,`Math::pow `等价于`(x,y)->Math.pow(x,y)`。 122 | * 对于第 3种情况,第 1 个参数会成为方法的目标。例如,`String::compareToIgnoreCase`等同于`(x,y)->x.compareToIgnoreCase(y)`。 123 | * 3、如果有多个同名的重栽方法,编译器就会尝试从上下文中找出你指的那一个方法。例如,Math.max 方法有两个版本,一个用于整数,另一个用于 double值。选择哪一个版本取决于 Math::max 转换为哪个函数式接口的方法参数。类似于 lambda表达式,方法引用不能独立存在,总是会转换为函数式接口的实例。 124 | * 4、可以在方法引用中使用 this参数。例如,`this::equals`等同于`x-> this.equals(x)`。使用super也是合法的。下面的方法表达式`super::instanceMethod`。 125 | 126 | ### 6.3.5 构造器引用 127 | 128 | * 1、构造器引用与方法引用很类似,只不过方法名为 new。例如,Person::new 是 Person构造器的一个引用。哪一个构造器呢? 这取决于上下文。假设你有一个字符串列表。可以把它转换为一个 Person对象数组,为此要在各个字符串上调用构造器,调用如下: 129 | ```java 130 | ArrayList names =. . .; 131 | Stream stream = names.stream().map(Person::new); 132 | Listpeople = stream.col1ect(Col1ectors.toList()); 133 | ``` 134 | * 2、可以用数组类型建立构造器引用。例如,`int[]::new`是一个构造器引用,它有一个参数:即数组的长度。这等价于 lambda表达式 `x->new int[x]`。 135 | * 3、Java有一个限制,无法构造泛型类型 T 的数组。数组构造器引用对于克服这个限制很有用。表达式 new T[n] 会产生错误,因为这会改为 new Object[n]。对于开发类库的人来说,这是一个问题。例如,假设我们需要一个 Person对象数组。Stream接口有一个 toArray方法可以返回 Object 数组:`Object[] people = stream.toArray();`不过,这并不让人满意。用户希望得到一个 Person引用数组,而不是Object引用数组。流库利用构造器引用解决了这个问题。可以把 `Person[]::new`传入toArray方法:`Person[] people = stream.toArray(PersonD::new);`toArray方法调用这个构造器来得到一个正确类型的数组。然后填充这个数组并返回。 136 | 137 | ### 6.3.6 变量作用域 138 | 139 | * 1、lambda表达式有 3个部分: 140 | * 1)一个代码块; 141 | * 2)参数; 142 | * 3)自由变量的值,这是指非参数而且不在代码中定义的变量。 143 | * 2、 144 | * 3、 145 | * 4、 146 | * 5、 147 | * 6、 148 | * 7、 149 | * 8、 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 | -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/6.5代理.md: -------------------------------------------------------------------------------- 1 |

6.5 代理

2 | 3 | ### 6.5.1 何时使用代理 4 | 5 | * 1、利用代理可以在运行时创建一个实现了一组给定接口的新类: 这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。 6 | * 2、**代理类可以在运行时创建全新的类**。这样的代理类能够实现指定的接口。尤其是,它具有下列方法: 7 | * 指定接口所需要的全部方法。 8 | * Object 类中的全部方法,例如,toString、equals 等。 9 | * 3、然而,不能在运行时定义这些方法的新代码。而是要提供一个调用处理器(invocation handler)。调用处理器是实现了 InvocationHandler接口的类对象。在这个接口中只有一个方法: 10 | ```java 11 | Object invoke(Object proxy, Method method, Object[] args) 12 | ``` 13 | * 无论何时调用代理对象的方法,调用处理器的 invoke方法都会被调用,并向其传递Method 对象和原始的调用参数。调用处理器必须给出处理调用的方式。 14 | 15 | ### 6.5.2 创建代理对象 16 | 17 | * 1、要想创建一个代理对象,需要使用 Proxy 类的 newProxylnstance 方法。这个方法有三个参数: 18 | * 一个类加栽器(class loader)。作为 Java安全模型的一部分,对于系统类和从因特网上下载下来的类,可以使用不同的类加载器。目前,用 null 表示使用默认的类加载器。 19 | * 一个 Class对象数组,每个元素都是需要实现的接口。 20 | * 一个调用处理器。 21 | * 2、还有两个需要解决的问题。如何定义一个处理器? 能够用结果代理对象做些什么? 当然,这两个问题的答案取决于打算使用代理机制解决什么问题。使用代理可能出于很多原因,例如: 22 | * 路由对远程服务器的方法调用。 23 | * 在程序运行期间,将用户接口事件与动作关联起来。 24 | * 为调试,跟踪方法调用。 25 | 26 | ### 6.5.3 代理类的特性 27 | 28 | * 1、代理类是在程序运行过程中创建的。然而,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。 29 | * 2、**所有的代理类都扩展于 Proxy类**。一个代理类只有一个实例域—调用处理器,它定义在 Proxy 的超类中。为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。 30 | * 3、所有的代理类都覆盖了 Object类中的方法 toString、equals和 hashCode。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的 invoke。Object 类中的其他方法(如 clone和 getClass) 没有被重新定义。 31 | * 4、没有定义代理类的名字,Sun 虚拟机中的 Proxy类将生成一个以字符串 SProxy开头的类名。 32 | * 5、对于特定的类加载器和预设的一组接口来说,只能有一个代理类。也就是说,如果使用同一个类加载器和接口数组调用两次 newProxylustance方法的话,那么只能够得到同一个类的两个对象,也可以利用getProxyClass方法获得这个类: 33 | ```java 34 | Class proxyClass = Proxy.getProxyClass(null, interfaces); 35 | ``` 36 | * 6、代理类一定是 public和 final。如果代理类实现的所有接口都是 public,代理类就不属于某个特定的包;否则,所有非公有的接口都必须属于同一个包,同时,代理类也属于这个包。 37 | * 7、可以通过调用 Proxy 类中的 isProxyClass方法检测一个特定的 Class对象是否代表一个代理类。 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 | -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/01.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/02.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/03.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/04.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/05.png -------------------------------------------------------------------------------- /Java核心卷/第六章接口、lambda表达式与内部类/img/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第六章接口、lambda表达式与内部类/img/06.png -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/14.1什么是线程.md: -------------------------------------------------------------------------------- 1 |

14.1 什么是线程

2 | 3 | * 1、每一个任务称为一个**线程(thread)**, 它是线程控制的简称。 4 | * 2、可以同时运行一个以上线程的程序称为**多线程程序(multithreaded)**。 5 | * 3、那么,多进程与多线程有哪些区别呢? 6 | * 本质的区别在于**每个进程拥有自己的一整套变量,而线程则共享数据**。 7 | * **共享变量**使线程之间的通信比进程之间的通信更有效、更容易。 8 | * 在有些操作系统中,与进程相比较,**线程更 “轻量级”**,创建、撤销一个线程比启动新进程的开销要小得多。 9 | * 4、调用 Thread.sleep不会创建一个新线程,sleep是 Thread类的静态方法,用于暂停当前线程的活动。 10 | * sleep方法可以抛出一个 IntermptedException异常。 11 | 12 | ### 14.1.1 使用线程给其他任务提供机会 13 | 14 | * 1、在一般情况下,线程在中断时被终止。因此,当发生InterruptedException异常时,run方法将结束执行。 15 | * 2、应该将要并行运行的任务与运行机制解耦合。如果有很多任务,要**为每个任务创建一个独立的线程所付出的代价太大了**。可以使用**线程池**来解决这个问题。 16 | * 3、不要调用 Thread 类或 Runnable对象的 run 方法。直接调用 run 方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用 Thread.start 方法。这个方法将创建一个执行 ran方法的新线程。 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 | -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/14.2中断线程.md: -------------------------------------------------------------------------------- 1 |

14.2 中断线程

2 | 3 | * 1、当线程的 run方法执行方法体中最后一条语句后,并经由执行 return语句返冋时,或者出现了在方法中没有捕获的异常时,**线程将终止**。 4 | * 在 Java的早期版本中,还有一个 stop方法,其他线程可以调用它终止线程。但是,这个方法现在已经被**弃用**了。 5 | * 2、**没有可以强制线程终止的方法。**然而,interrupt方法可以用来请求终止线程。 6 | * 3、当对一个线程调用 interrupt 方法时,线程的中断状态将被置位。这是每一个线程都具有的 boolean标志。每个线程都应该不时地检査这个标志,以判断线程是否被中断。 7 | * 4、要想弄清中断状态是否被置位,首先调用静态的 Thread.currentThread方法获得当前线程,然后调用 islnterrupted方法: 8 | ```java 9 | while (!Thread.currentThread().isInterrupted() && more work to do){ 10 | do more work 11 | } 12 | ``` 13 | * 5、如果线程被阻塞,就无法检测中断状态。这是产生 InterruptedExceptioii 异常的地方。 14 | * 6、当在一个被阻塞的线程(调用 sleep或 wait) 上调用 interrupt方法时,阻塞调用将会被Interrupted Exception异常中断。 15 | * 7、没有任何语言方面的需求要求一个被中断的线程应该终止。中断一个线程不过是引起它的注意。 16 | * 8、被中断的线程可以决定如何响应中断。 17 | * 9、某些线程是如此重要以至于应该处理完异常后,继续执行,而不理会中断。但是,更普遍的情况是线程将简单地将中断作为一个终止的请求。 18 | * 10、如果在每次工作迭代之后都调用 sleep方法(或者其他的可中断方法),islnterrupted检测既没有必要也没有用处。 19 | * 11、如果在中断状态被置位时调用 sleep方法,它不会休眠。相反,它将清除这一状态(!)并拋出 IntemiptedException。 20 | * 12、**有两个非常类似的方法,interrupted和 islnterrupted**。 21 | * Interrupted方法是一个静态方法,它检测当前的线程是否被中断。而且,调用 interrupted 方法会**清除该线程的中断状态**。 22 | * 另一方面,islnterrupted 方法是一个实例方法,可用来检验是否有线程被中断。调用这个方法**不会改变中断状态**。 23 | 24 | * 13、在很多发布的代码中会发现 InterruptedException异常被抑制在很低的层次上,像这样: 25 | ```java 26 | void mySubTask(){ 27 | try { 28 | sleep(delay); 29 | } catch (InterruptedException e) {//Don't ignore! 30 | 31 | } 32 | } 33 | ``` 34 | * 不要这样做! 如果不认为在 catch子句中做这一处理有什么好处的话,仍然有两种合理的选择: 35 | * 在 catch子句中调用 Thread.currentThread().interrupt()来设置中断状态。于是,调用者可以对其进行检测。 36 | ```java 37 | void mySubTask(){ 38 | try { 39 | sleep(delay); 40 | } catch (InterruptedException e) { 41 | Thread.currentThread().interrupt(); 42 | } 43 | } 44 | ``` 45 | * 或者,更好的选择是,用 throwsInterruptedException标记你的方法,不采用 try语句块捕获异常。于是,调用者(或者,最终的 run方法)可以捕获这一异常。 46 | ```java 47 | void mySubTask() throws InterruptedException{ 48 | ...... 49 | sleep(delay); 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 | -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/14.3线程状态.md: -------------------------------------------------------------------------------- 1 |

14.3 线程状态

2 | 3 | 4 | * 线程可以有如下 6种状态: 5 | 6 |
7 | 8 | ### 14.3.1 新创建线程 9 | * 1、当用 new操作符创建一个新线程时,如 newThread(r),该线程还没有开始运行。这意味着它的状态是 new。 10 | * 2、当一个线程处于**新创建状态**时,程序还没有开始运行线程中的代码。 11 | * 3、在线程运行之前还有一些基础工作要做。 12 | 13 | 14 | ### 14.3.2 可运行线程 15 | * 1、一旦调用 start方法,线程处于 **runnable 状态**。一个可运行的线桿**可能正在运行也可能没有运行**,这取决于操作系统给线程提供运行的时间。 16 | * Java的规范说明没有将它作为一个单独状态。一个正在运行中的线程仍然处于可运行状态。 17 | * 2、一旦一个线程开始运行,它不必始终保持运行。 18 | * 事实上,**运行中的线程被中断,目的是为了让其他线程获得运行机会**。 19 | * 线程调度的细节依赖于操作系统提供的服务。 20 | * 抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会(见图 14-4)。当选择下一个线程时,操作系统考虑线程的优先级。 21 |
22 | * 3、现在所有的桌面以及服务器操作系统都使用抢占式调度。 23 | * 4、但是,像手机这样的小型设备可能使用协作式调度。在这样的设备中,一个线程只有在**调用 yield方法、或者被阻塞或等待时,线程才失去控制权**。 24 | * 5、在具有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程**并行运行**。当然,如果线程的数目多于处理器的数目,调度器依然采用时间片机制。 25 | * 6、记住,在任何给定时刻,二个可运行的线程可能正在运行也可能没有运行(这就是为什么将这个状态称为可运行而不是运行)。 26 | 27 | ### 14.3.3 被阻塞线程和等待线程 28 | * 1、当线程处于被阻塞或等待状态时,它暂时不活动。它不运行任何代码且消耗最少的资源。直到线程调度器重新激活它。细节取决于它是怎样达到非活动状态的。 29 | * 2、**当一个线程试图获取一个内部的对象锁(而不是javiutiUoncurrent 库中的锁),而该锁被其他线程持有,则该线程进人阻塞状态**(我们在 14.5.3节讨论java.util.concurrent锁,在 14.5.5节讨论内部对象锁)。**当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程将变成非阻塞状态**。 30 | * 3、**当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态**。我们在第14.5.4节来讨论条件。在调用 Object.wait 方法或 Thread.join方法,或者是等待 java,util.concurrent 库中的 Lock 或 Condition 时,就会出现这种情况。实际上,**被阻塞状态与等待状态是有很大不同的**。 31 | * 4、有几个方法有一个**超时参数**。调用它们导致线程进人**计时等待(timed waiting) 状态**。这一状态将一直保持到超时期满或者接收到适当的通知。**带有超时参数的方法有Thread.sleep 和 Object.wait、Thread.join、Lock.tryLock 以及 Condition.await 的计时版**。 32 | * 5、图 14-3展示了线程可以具有的状态以及从一个状态到另一个状态可能的转换。 33 | * 当一个线程被阻塞或等待时(或终止时),另一个线程被调度为运行状态。 34 | * 当一个线程被重新激活(例如,因为超时期满或成功地获得了一个锁),调度器检查它是否具有比当前运行线程更高的优先级。如果是这样,调度器从当前运行线程中挑选一个,剥夺其运行权,选择一个新的线程运行。 35 |
36 | 37 | ### 14.3.4 被终止的线程 38 | * 1、线程因如下两个原因之一而被终止: 39 | * 因为 run 方法正常退出而自然死亡。 40 | * 因为一个没有捕获的异常终止了 nm方法而意外死亡。 41 | * 2、特别是,可以调用线程的 stop方法杀死一个线程。该方法抛出 ThreadDeath错误对象,由此杀死线程。但是,**stop方法已过时,不要在自己的代码中调用这个方法**。 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 | -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/14.4线程属性.md: -------------------------------------------------------------------------------- 1 |

14.4 线程属性

2 | 3 | ### 14.4.1 线程优先级 4 | * 1、在 Java 程序设计语言中,每一个线程有一个优先级。 5 | * 2、默认情况下,一+线程继承它的父线程的优先级。 6 | * 3、可以用 setPriority 方法提高或降低任何一个线程的优先级。 7 | * 4、可以将优先级设置为在: 8 | * **MIN_PRIORITY (在 Thread 类中定义为 1)** 与 9 | * **MAX_PRIORITY (定义为 10)** 之间的任何值。 10 | * **NORM_PRIORITY 被定义为 5**。 11 | * 5、每当线程调度器有机会选择新线程时,它**首先选择具有较高优先级的线程**。但是,线程优先级是高度依赖于系统的。 12 | * 6、当虚拟机依赖于宿主机平台的线程实现机制时,Java 线程的优先级被映射到宿主机平台的优先级上,优先级个数也许更多,也许更少。 13 | * 7、例如: 14 | * Windows 有 7个优先级别。一些 Java 优先级将映射到相同的操作系统优先级。 15 | * 在Oracle 为 Linux 提供的 Java 虚拟机中,线程的优先级被忽略一所有线程具有相同的优先级。 16 | * 8、如果有几个高优先级的线程没有进入非活动状态,低优先级的线程可能永远也不能执行。 17 | * 9、每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中进行选择,尽管这样会使低优先级的线程完全饿死。 18 | 19 | ### 14.4.2 守护线程 20 | * 1、可以通过调用 21 | ```java 22 | t.setDaemon(true); 23 | ``` 24 | * 将线程转换为**守护线程(daemonthread)**。 25 | * 2、**守护线程的唯一用途是为其他线程提供服务**。 26 | * 3、当只剩下守护线程时,虚拟机就退出了,由于如果只剩下守护线程,就没必要继续运行程序了。 27 | 28 | ### 14.4.3 未捕获异常处理器 29 | * !、线程的 run方法不能抛出任何受查异常,但是,非受査异常会导致线程终止。在这种情况下,线程就死亡了。 30 | * 2、不需要任何 catch子句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的**处理器**。 31 | * 3、该处理器必须属于一个实现 Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。 32 | ```java 33 | void uncaughtException(Thread t, Throwable e) 34 | ``` 35 | * 4、可以: 36 | * 用 setUncaughtExceptionHandler方法为任何线程安装一个处理器。 37 | * 也可以用 Thread类的静态方法 setDefaultUncaughtExceptionHandler为所有线程安装一个默认的处理器。 38 | * 5、替换处理器可以使用日志 API 发送未捕获异常的报告到日志文件。 39 | * 6、如果不安装默认的处理器,默认的处理器为空。但是,如果不为独立的线程安装处理器,此时的处理器就是该线程的 ThreadGroup对象。 40 | * 7、线程组是一个可以统一管理的线程集合。 41 | * 默认情况下,创建的所有线程属于相同的线程组, 42 | * 但是,也可能会建立其他的组。 43 | * **现在引入了更好的特性用于线程集合的操作,所以建议不要在自己的程序中使用线程组**。 44 | * 8、ThreadGroup类实现 Thread.UncaughtExceptionHandler 接口。它的 uncaughtException方法做如下操作: 45 | * 1)如果该线程组有父线程组,那么父线程组的 uncaughtException方法被调用。 46 | * 2) 否则,如果 Thread.getDefaultExceptionHandler方法返回一个非空的处理器,则调用该处理器。 47 | * 3) 否则,如果 Throwable是 ThreadDeath的一个实例,什么都不做。 48 | * 4) 否则,线程的名字以及 Throwable的栈轨迹被输出到 System.err 上。 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 | -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/14.6阻塞队列.md: -------------------------------------------------------------------------------- 1 |

14.6 阻塞队列

2 | 3 | * 1、对于许多线程问题,可以通过使用一个或多个队列以优雅且安全的方式将其形式化。 4 | * 2、生产者线程向队列插人元素,消费者线程则取出它们。使用队列,可以安全地从一个线程向另一个线程传递数据。 5 | * 3、当试图向队列添加元素而队列已满,或是想从队列移出元素而队列为空的时候,阻塞队列(blocking queue) 导致**线程阻塞**。 6 | * 在协调多个线程之间的合作时,阻塞队列是一个有用的工具。 7 | * 工作者线程可以周期性地将中间结果存储在阻塞队列中。 8 | * 其他的工作者线程移出中间结果并进一步加以修改。 9 | * 队列会自动地平衡负载。 10 | * 如果第一个线程集运行得比第二个慢,第二个线程集在等待结果时会阻塞。 11 | * 如果第一个线程集运行得快,它将等待第二个队列集赶上来。 12 | * 4、表 14-1给出了阻塞队列的方法。 13 |
14 | * 5、阻塞队列方法分为以下 3类:(这取决于当队列满或空时它们的响应方式。) 15 | * 如果将队列当作线程管理工具来使用,将要用到 put 和 take方法。 16 | * 当试图向**满的队列中添加**或从**空的队列中移出**元素时,add、remove和 element 操作抛出异常。 17 | * 当然,在一个多线程程序中,队列会在任何时候空或满,因此,一定要使用 offer、poll 和 peek方法作为替代。 18 | * 这些方法如果不能完成任务,只是给出一个错误提示而不会抛出异常。 19 | * 6、poll 和 peek 方法返回空来指示失败。因此,向这些队列中插入 null 值是非法的。 20 | * 7、**如果队列满,则 put方法阻塞**;**如果队列空,则 take方法阻塞**。在不带超时参数时,offer 和 poll 方法等效。 21 | * 8、java.util.concurrent 包提供了阻塞队列的几个变种。 22 | * 默认情况下,LinkedBlockingQueue的容量是没有上边界的,但是,也可以选择指定最大容量。 23 | * LinkedBlockingDeque是一个双端的版本。 24 | * ArrayBlockingQueue在构造时需要指定容量,并且有一个可选的参数来指定是否需要公平性。 25 | * 若设置了公平参数,则那么等待了最长时间的线程会优先得到处理。 26 | * 通常,公平性会降低性能,只有在确实非常需要时才使用它。 27 | * 9、PriorityBlockingQueue是一个**带优先级的队列**,而不是先进先出队列。 28 | * 元素按照它们的优先级顺序被移出。 29 | * 该队列是没有容量上限,但是,如果队列是空的,取元素的操作会阻塞。 30 | 31 | ```java 32 | package com.edu.test.multithreading; 33 | 34 | import java.io.File; 35 | import java.io.FileNotFoundException; 36 | import java.util.Scanner; 37 | import java.util.concurrent.ArrayBlockingQueue; 38 | import java.util.concurrent.BlockingQueue; 39 | 40 | /** 41 | * @Author: 王仁洪 42 | * @Date: 2019/4/2 21:30 43 | */ 44 | public class BlockingQueueTest { 45 | private static final int FILE_QUEUE_SIZE = 10;//文件队列大小 46 | private static final int SEARCH_THREADS = 100;//搜索线程 47 | private static final File DUMMY = new File(""); 48 | private static BlockingQueue queue = new ArrayBlockingQueue<>(FILE_QUEUE_SIZE); 49 | 50 | public static void main(String[] args) { 51 | Scanner in = new Scanner(System.in); 52 | 53 | System.out.print("Enter base directory (e.g. /opt/jdk1.8.0/src): "); 54 | String directory = in.nextLine(); 55 | 56 | System.out.print("Enter keyword (e.g. volatile): "); 57 | String keyword = in.nextLine(); 58 | 59 | Runnable enumerator = () -> { 60 | try { 61 | enumerate(new File(directory)); 62 | queue.put(DUMMY); 63 | } catch (InterruptedException e) { 64 | 65 | } 66 | }; 67 | 68 | new Thread(enumerator).start(); 69 | for (int i = 1; i <= SEARCH_THREADS; i++) { 70 | Runnable searcher = () -> { 71 | try { 72 | boolean done = false; 73 | while (!done) { 74 | File file = queue.take(); 75 | if (file == DUMMY) { 76 | queue.put(file); 77 | done = true; 78 | } 79 | else search(file, keyword); 80 | } 81 | } catch (InterruptedException e) { 82 | 83 | } 84 | }; 85 | new Thread(searcher).start(); 86 | } 87 | } 88 | 89 | /** 90 | * Recursively enumerates all files in a given directory and its subdirectories. 91 | * @param directory the directory in which to start 92 | * @throws InterruptedException 93 | */ 94 | public static void enumerate(File directory) throws InterruptedException { 95 | File[] files = directory.listFiles(); 96 | for (File file : files){ 97 | if (file.isDirectory()){ 98 | enumerate(file); 99 | }else { 100 | queue.put(file); 101 | } 102 | } 103 | } 104 | 105 | /** 106 | * Searches a file for a given keyword and prints all matching lines. 107 | * @param file the file to search 108 | * @param keyword the keyword to search for 109 | */ 110 | public static void search(File file,String keyword){ 111 | try (Scanner input = new Scanner(file,"utf-8")) { 112 | 113 | int lineNumber = 0; 114 | while (input.hasNextLine()){ 115 | lineNumber++; 116 | String line = input.nextLine(); 117 | if (line.contains(keyword)){ 118 | System.out.printf("%s:%d:%n" , file.getPath() , lineNumber , line); 119 | } 120 | } 121 | } catch (FileNotFoundException e) { 122 | e.printStackTrace(); 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 | -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第十四章并发/img/001.png -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/img/14-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第十四章并发/img/14-3.png -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/img/14-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第十四章并发/img/14-4.png -------------------------------------------------------------------------------- /Java核心卷/第十四章并发/img/table14-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第十四章并发/img/table14-1.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.10类设计技巧.md: -------------------------------------------------------------------------------- 1 |

4.10 类设计技巧

2 | 3 | * 1、一定要保证数据私有 4 | * 2、一定要对数据初始化 5 | * 3、不要在类中使用过多的基本类型 6 | * 4、不是所有的域都需要独立的域访问器和域更改器 7 | * 5、将职责过多的类进行分解 8 | * 6、类名和方法名要能够体现它们的职责 9 | * 7、优先使用不可变的类 10 | -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.1面向对象程序设计概述.md: -------------------------------------------------------------------------------- 1 |

4.1面向对象程序设计概述

2 | 3 | ### 4.1.1 类 4 | 5 | * 1、对于一些规模较小的问题,将其分解为过程的开发方式比较理想。而 **面向对象** 更加适用于解决规模较大的问题。 6 | * 2、由类构造(construct) 对象的过程称为创建类的实例 (instance)。 7 | * 3、**封装** (encapsulation, 有时称为数据隐藏)是与对象有关的一个重要概念。从形式上看,封装不过是将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式。 8 | * 4、对象中的数据称为**实例域**( instance field), 操纵数据的过程称为**方法**( method)。 9 | * 5、实现封装的**关键**在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。封装给对象赋予了“ 黑盒” 特征,这是提高**重用性**和**可靠性**的关键。 10 | * 6、在 Java 中,所有的类都源自于一个“ 神通广大的超类”,它就是 Object。 11 | * 7、在扩展一个已有的类时,这个扩展后的新类具有所扩展的类的全部属性和方法。在新类中,只需提供适用于这个新类的新方法和数据域就可以了。通过扩展一个类来建立另外一个类的过程称为**继承**(inheritance)。 12 | 13 | ### 4.1.2 对 象 14 | 15 | * 1、要想使用 OOP,—定要清楚对象的三个主要特性: 16 | * **对象的行为**(behavior)—可以对对象施加哪些操作,或可以对对象施加哪些方法? 17 | * 对象的行为是用可调用的方法定义的。 18 | * **对象的状态**(state)—当施加那些方法时,对象如何响应? 19 | * 每个对象都保存着描述当前特征的信息。这就是对象的状态。 20 | * 对象的状态可能会随着时间而发生改变,但这种改变不会是自发的。 21 | * 对象状态的改变必须通过调用方法实现(如果不经过方法调用就可以改变对象状态,只能说明封装性遭到了破坏)。 22 | * **对象标识**(identity)—如何辨别具有相同行为与状态的不同对象? 23 | * 作为一个类的实例,每个对象的标识永远是不同的,状态常常也存在着差异。 24 | 25 | ### 4.1.3 识别类 26 | 27 | * 1、识别类的简单规则是在分析问题的过程中寻找**名词**,而 方法 对应着**动词**。 28 | * 2、所谓“找名词与动词”原则只是一种经验,在创建类的时候,哪些名词和动词是重要的完全取决于个人的开发经验。 29 | 30 | ### 4.1.4 类之间的关系 31 | 32 | * 1、在类之间,最常见的关系有 33 | * 依赖(“uses-a”) 34 | * 依赖(dependence), 即“uses-a”关系,是一种最明显的、最常见的关系。 35 | * 如果一个类的方法操纵另一个类的对象,我们就说一个类依赖于另一个类。 36 | * 聚合(“has-a”) 37 | * 聚合(aggregation), 即“has-a” 关系,是一种具体且易于理解的关系。 38 | * 例如,一个Order 对象包含一些 Item 对象。聚合关系意味着类 A 的对象包含类 B 的对象。 39 | * 继承(“is-a”) 40 | * 继承(inheritance), 即“is-a” 关系,是一种用于表示特殊与一般关系的。 41 | * 如果类A 扩展类 B, 类A 不但包含从类 B 继承的方法,还会拥有一些额外的功能。 42 | 43 |
-------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.2使用预定义类.md: -------------------------------------------------------------------------------- 1 |

4.2 使用预定义类

2 | 3 | ### 4.2.1 对象与对象变量 4 | 5 | * 1、并不是所有的类都具有面向对象特征。例如,**Math类**。Math类只封装了功能,它不需要也不必隐藏数据。由于没有数据,因此也不必担心生成对象以及初始化实例域。 6 | * 2、要想使用对象,就必须首先构造对象,并指定其初始状态。然后,对对象应用方法。 7 | * 3、在 Java 程序设计语言中,使用构造器(constructor) 构造新实例。构造器是一种特殊的方法,用来构造并初始化对象。 8 | * 4、构造器的名字应该与类名相同。因此 Date类的构造器名为 Date。要想构造一个 Date对象,需要在构造器前面加上 new操作符,如下所示: 9 | ```java 10 | new Date() 11 | ``` 12 | * 5、通常,希望构造的对象可以多次使用,因此,需要将对象存放在一个变量中: 13 | ```java 14 | Date birthday = new Date(); 15 | ``` 16 |
17 | 18 | * 6、在对象与对象变量之间存在着一个重要的区别。 19 | * 例如,语句: 20 | ```java 21 | Date deadline;// deadline doesn't refer to any object 22 | ``` 23 | * 定义了一个对象变量 deadline, 它可以引用 Date类型的对象。但是,一定要认识到:变量deadline不是一个对象,实际上也没有引用对象。此时,不能将任何 Date方法应用于这个变量上。语句 24 | ```java 25 | s = deadline.toString(); // not yet 26 | 将产生编译错误。 27 | ``` 28 | * 必须首先初始化变量 deadline, 这里有两个选择。 29 | * 可以用新构造的对象初始化这个变量: 30 | ```java 31 | deadline = new Date(); 32 | ``` 33 | * 也让这个变量引用一个已存在的对象: 34 | ```java 35 | deadline = birthday; 36 | ``` 37 | * 现在,这两个变量引用同一个对象(请参见图 4-4)。 38 |
39 | * 7、一定要认识到:**一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。** 40 | * 8、在 Java中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new操作符的返回值也是一个引用。下列语句:`Date deadline = new Date();`有两个部分。表达式 `new Date()` 构造了一个 Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量 deadline中。 41 | * 9、**局部变量不会自动地初始化为 null,而必须通过调用 new 或将它们设置为 null 进行初始化**。 42 | * 10、很多人错误地认为 Java对象变量与 C++的引用类似。然而,在 C++中没有空引用,并且引用不能被赋值。可以将 Java的对象变量看作 C++的对象指针。例如, 43 | ```java 44 | Date birthday; // Java 45 | 实际上,等同于 46 | Date* birthday; // C++ 47 | 一个 Date* 指针只能通过调用new进行初始化。就这一点而言,C++ 与 Java的语法几乎是一样的。 48 | ``` 49 | * 11、**所有的 Java对象都存储在堆中**。当一个对象包含另一个对象变量时,这个变量依然包含着指向另一个堆对象的指针。 50 | 51 | ### 4.2.2 Java 类库中的 LocalDate 类 52 | 53 | * 1、时间是用距离一个固定时间点的毫秒数(可正可负)表示的,这个点就是所谓的纪元(epoch), 它是 UTC时间 `1970 年 1月 1 日 00:00:00`。UTC是 Coordinated Universal Time的缩写,与大家熟悉的 GMT(即 Greenwich Mean Time,格林威治时间)一样,是一种具有实践意义的科学标准时间。 54 | * 2、类库设计者决定将保存时间 ` 与 ` 给时间点命名 分开。所以标准 Java类库分别包含了两个类: 55 | * 一个是用来表示时间点的 Date类; 56 | * 另一个是用来表示大家熟悉的日历表示法的 LocalDate类。 57 | 58 | ### 4.2.3 **更改器方法与访问器方法** 59 | 60 | * 1、只访问对象而不修改对象的方法有时称为**访问器方法**。例如,LocalDate.getYear 和 GregorianCalendar.get 就是访问器方法。 61 | * 2、plusDays 方法会生成一个新的 LocalDate 对象,然后把这个新对象赋给 aThousandDaysLater变量。原来的对象不做任何改动。 62 | ```java 63 | LocalDate aThousandDaysLater = newYearsEve.plusDays(1000); 64 | ``` 65 | * 3、与 LocalDate.plusDays 方法不同,GregorianCalendar.add 方法是一个更改器方法(mutator method)调用这个方法后,someDay 对象的状态会改变。 66 | ```java 67 | CregorianCalendar someDay=new CregorianCalendar(1999, 11, 31); 68 | // Odd feature of that class: month numbers go from 0 to 11 69 | someDay.add(Calendar.DAY_0F_M0NTH, 1000); 70 | ``` 71 | * 4、LocalDate计算显示当月日历: 72 | ```java 73 | @Test 74 | public void testLocalDate(){ 75 | //LocalDate date = LocalDate.now(); 76 | LocalDate date = LocalDate.of(2018,7,1); 77 | int month = date.getMonthValue(); 78 | int today = date.getDayOfMonth(); 79 | 80 | //LocalDate minusDays(int n);生成当前日期之后或之前 n 天的日期。 81 | date = date.minusDays(today -1);// Set to start of month 82 | DayOfWeek weekday = date.getDayOfWeek(); 83 | int value = weekday.getValue();// 1 = Monday, ... 7 = Sunday 84 | 85 | if (value == 7) 86 | value = 0; 87 | 88 | System.out.println("Sun Mon Tue Wed Thu Fri Sat"); 89 | for (int i = 0; i < value; i++) 90 | System.out.print(" "); 91 | 92 | while (date.getMonthValue() == month) { 93 | System.out.printf("%3d" , date.getDayOfMonth()); 94 | if (date.getDayOfMonth() == today) 95 | System.out.print("*"); 96 | else 97 | System.out.print(" "); 98 | date = date.plusDays(1); 99 | if (date.getDayOfWeek().getValue() == 7) 100 | System.out.println(); 101 | } 102 | } 103 | ``` 104 | 105 |
-------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.3用户自定义类.md: -------------------------------------------------------------------------------- 1 |

4.3 用户自定义类

2 | 3 | ### 4.3.1 Employee类 4 | 5 | ```java 6 | EmployeeTest.java 7 | 8 | public class EmployeeTest{ 9 | ...... 10 | public static void main(String[] args){ 11 | ...... 12 | } 13 | } 14 | 15 | class Employee{ 16 | ...... 17 | } 18 | ``` 19 | 20 | * 1、文件名必须与 public类的名字相匹配。 21 | * 2、在一个源文件中,只能有一个公有(public)类,但可以有任意数目的非公有类。 22 | * 3、将程序中包含 main方法的类名提供给字节码解释器,以便启动这个程序: 23 | ```java 24 | java EmployeeTest 25 | ``` 26 | 27 | ### 4.3.2 多个源文件的使用 28 | 29 | * 1、将每个类放在各自的类文件中,使用`javac EmployeeTest.java`方式编译时,当 Java 编译器发现 EmployeeTest.java使用了Employee类时会查找名为Employee.class的文件。如果没有找到这个文件,就会自动地搜索 Employee.java, 然后,对它进行编译。更重要的是:如果Employee.java版本较已有的 Employee.dass文件版本新,Java编译器就会自动地重新编译这个文件。 30 | 31 | ### 4.3.3 剖析 Employee 类 32 | 33 | ### 4.3.4 从构造器开始 34 | 35 | * 1、构造器总是伴随着 **new 操作符**的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。 36 | * 2、构造器 37 | * 构造器与类同名 38 | * 每个类可以有一个以上的构造器 39 | * 构造器可以有 0个、1个或多个参数 40 | * 构造器没有返回值 41 | * 构造器总是伴随着 new 操作一起调用 42 | 43 | 44 | ### 4.3.5 隐式参数与显式参数 45 | 46 | ```java 47 | public void raiseSalary(double byPercent){ 48 | double raise = salary * byPercent /100; 49 | salary += raise; 50 | } 51 | 52 | number007.raiseSalary(5); 53 | ``` 54 | 55 | * 1、raiseSalary方法有两个参数。第一个参数称为**隐式 (implicit) 参数**,是出现在方法名前的Employee类对象。第二个参数位于方法名后面括号中的数值,这是一个**显式 (explicit) 参数**(有些人把隐式参数称为方法调用的目标或接收者。)显式参数是明显地列在方法声明中的,例如 `double byPercent` 。隐式参数没有出现在方法声明中。 56 | 57 | ### 4.3.6 封装的优点 58 | 59 | * 1、在有些时候,需要获得或设置实例域的值。因此,应该提供下面三项内容: 60 | * 一个私有的数据域; 61 | * 一个公有的域访问器方法; 62 | * 一个公有的域更改器方法。 63 | * >好处:首先,可以改变内部实现,除了该类的方法之外,不会影响其他代码。其次,更改器方法可以执行错误检查,然而直接对域进行赋值将不会进行这些处理。 64 | * 2、注意不要编写返回引用可变对象的访问器方法。如果需要返回一个可变对象的引用,应该首先对它进行克隆(clone)。 65 | * 3、对象 clone是指存放在另一个位置上的对象副本。 66 | 67 | ### 4.3.7 基于类的访问权限 68 | 69 | * 1、方法可以访问所调用对象的私有数据。 70 | * 2、方法可以访问所属类的私有特性( feature), 而不仅限于访问隐式参数的私有特性。 71 | 72 | ### 4.3.8 私有方法 73 | 74 | * 1、在实现一个类时,由于公有数据非常危险,所以应该将所有的数据域都设置为私有的。 75 | * 2、在 Java中,为了实现一个私有的方法,只需将关键字 public改为 private即可。 76 | * 3、对于私有方法,如果改用其他方法实现相应的操作,则不必保留原有的方法。 77 | * 4、如果数据的表达方式发生了变化,这个方法可能会变得难以实现,或者不再需要。然而,只要方法是私有的,类的设计者就可以确信:它不会被外部的其他类操作调用,可以将其删去。如果方法是公有的,就不能将其删去,因为其他的代码很可能依赖它。 78 | 79 | ### 4.3.9 final实例域 80 | 81 | * 1、可以将实例域定义为 final。构建对象时必须初始化这样的域。也就是说,必须确保在每一个构造器执行之后,这个域的值被设置,并且在后面的操作中,不能够再对它进行修改。 82 | * 2、final 修饰符大都应用于基本(primitive) 类型域,或不可变(immutable) 类的域(如果类中的每个方法都不会改变其对象,这种类就是不可变的类。例如,String类就是一个不可变的类)。 83 | * 3、对于可变的类,使用 final 修饰符可能会对读者造成混乱。例如: 84 | ```java 85 | private final StringBuiIcier evaluations; 86 | 在 Employee 构造器中会初始化为: 87 | evaluations = new StringBuilder(); 88 | final 关键字只是表示存储在 evaluations 变量中的对象引用不会再指示其他 StringBuilder 对象。不过这个对象可以更改: 89 | public void giveGoldStar(){ 90 | evaluations.append(LocalDate.now()+ ": Gold star!\n"); 91 | } 92 | ``` 93 | -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.4静态域与静态方法.md: -------------------------------------------------------------------------------- 1 |

4.4 静态域与静态方法

2 | 3 | ### 4.4.1 静态域 4 | 5 | * 1、如果将域定义为static, 每个类中只有一个这样的域。而每一个对象对于所有的实例域却都有自己的一份拷贝。 6 | * 2、静态域属于类,而不属于任何独立的对象。 7 | 8 | ### 4.4.2 静态常量 9 | 10 | * 1、静态变量使用得比较少,但静态常量却使用得比较多。 11 | * 2、由于每个类对象都可以对公有域进行修改,所以,最好不要将域设计为 public。然而,公有常量(即 final 域)却没问题。 12 | * 3、如果查看一下 System 类,就会发现有一个 setOut 方法,它可以将 System.out 设置为不同的流。读者可能会感到奇怪,为什么这个方法可以修改 final 变量的值。原因在于,setOut 方法是一个本地方法,而不是用 Java语言实现的。本地方法可以绕过 Java语言的存取控制机制。这是一种特殊的方法,在自己编写程序时,不应该这样处理。 13 | 14 | 15 | ### 4.4.3 静态方法 16 | 17 | * 1、静态方法是一种不能向对象实施操作的方法。例如,Math类的 pow 方法就是一个静态方法。表达式`Math.pow(x, n)`计算幂 Xⁿ 在运算时,不使用任何 Math对象。换句话说,没有隐式的参数。 18 | * 2、可以使用对象调用静态方法。建议使用类名,而不是对象来调用静态方法。 19 | * 3、在下面两种情况下使用静态方法: 20 | * 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如:Math.pow)。 21 | * 一个方法只需要访问类的静态域(例如:Employee.getNextld)。 22 | * 4、static 的含义:属于类且不属于类对象的变量和函数。 23 | 24 | ### 4.4.4 工厂方法 25 | 26 | * 1、静态方法还有另外一种常见的用途。类似 LocalDate 和 NumberFormat 的类使用静态工厂方法 (factory method) 来构造对象。你已经见过工厂方法 LocalDate.now 和 LocalDate.of。NumberFormat 类如下使用工厂方法生成不同风格的格式化对象: 27 | ```java 28 | @Test 29 | public void testNumberFormat(){ 30 | NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance(); 31 | NumberFormat percentFormatter = NumberFormat.getPercentInstance(); 32 | double x = 0.1; 33 | System.out.println(currencyFormatter.format(x));//prints ¥0.10 34 | System.out.println(percentFormatter.format(x));//prints 10% 35 | } 36 | ``` 37 | * 为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因: 38 | * 无法命名构造器。构造器的名字必须与类名相同。但是,这里希望将得到的货币实例和百分比实例采用不用的名字。 39 | * 当使用构造器时,无法改变所构造的对象类型。而 Factory方法将返回一个DecimalFormat类对象,这是 NumberFormat 的子类。 40 | 41 | ### 4.4.5 main方法 42 | 43 | * 1、main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的main方法将执行并创建程序所需要的对象。 -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.5方法参数.md: -------------------------------------------------------------------------------- 1 |

4.5 方法参数

2 | 3 | * 1、**按值调用** (call by value) 表示方法接收的是调用者提供的值。 4 | * 2、**按引用调用** (call by reference)表示方法接收的是调用者提供的变量地址。 5 | * 3、一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 6 | * 4、Java程序设计语言总是采用**按值调用**。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。 7 | * 5、方法参数共有两种类型: 8 | * 基本数据类型(数字、布尔值)。 9 | * 对象引用。 10 | * 6、**一个方法不可能修改一个基本数据类型的参数**。假定一个方法试图将一个参数值增加至 3倍: 11 | ```java 12 | public static void tripieValue(double x){// doesn't work 13 | x = 3*x; 14 | } 15 | 16 | 然后调用这个方法: 17 | double percent = 10; 18 | tripieValue(percent); 19 | ``` 20 | 21 | * 不过,并没有做到这一点调用这个方法之后,percent 的值还是 10。下面看一下具体的执行 22 | 程: 23 | * 1) x 被初始化为 percent 值的一个拷贝(也就是 10)。 24 | * 2) x 被乘以 3 后等于 30。但是 percent 仍然是 10(如图 4-6所示)。 25 | * 3) 这个方法结束之后,参数变量 X 不再使用。 26 | 27 |
28 | 29 | * 7、对象引用作为参数,可以很容易地利用下面这个方法实现将一个雇员的薪金提高两倍的操作: 30 | ```java 31 | public static void tripieSalary(Employee x){// works 32 | x.raiseSa1ary(200); 33 | } 34 | 然后调用这个方法: 35 | harry= new Employee(. . .); 36 | tripieSalary(harry); 37 | ``` 38 | * 具体的执行过程为: 39 | * 1) X 被初始化为 harry 值的拷贝,这里是一个对象的引用。 40 | * 2) raiseSalary 方法应用于这个对象引用。x 和 harry 同时引用的那个 Employee 对象的薪金提高了 200%。 41 | * 3) 方法结束后,参数变量 x 不再使用。当然,对象变量 harry 继续引用那个薪金增至 3倍的雇员对象(如图 4-7所示)。 42 | 43 |
44 | * **实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。** 45 | * 8、Java 程序设计语言对对象采用的**不是引用调用**。首先,编写一个交换两个雇员对象的方法: 46 | ```java 47 | public static void swap(Employee x, Employee y){// doesn't work 48 | Employee temp = x; 49 | x = y; 50 | y = temp; 51 | } 52 | 如果 Java 对对象采用的是按引用调用,那么这个方法就应该能够实现交换数据的效果: 53 | Employee a = new Employee("Alice", . . .); 54 | Employee b = new Employee("Bob", . . .); 55 | swap(a, b); 56 | //does a now refer to Bob, b to Alice? 57 | 但是,方法并没有改变存储在变量 a 和 b 中的对象引用。swap方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。 58 | // x refers to Alice, y to Bob 59 | Employee temp = x; 60 | x = y; 61 | y = temp; 62 | //now x refers to Bob, y to Alice 63 | 最终,白费力气。在方法结束时参数变量 X 和 y 被丢弃了。原来的变量 a 和 b 仍然引用这个方法调用之前所引用的对象(如图 4-8所示)。 64 | ``` 65 | 66 |
67 | 68 | * 这个过程说明:Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按 69 | 值传递的。 70 | 71 | * 9、Java中方法参数的使用情况: 72 | * 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。 73 | * 一个方法可以改变一个对象参数的状态。 74 | * 一个方法不能让对象参数引用一个新的对象。 -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.6对象构造.md: -------------------------------------------------------------------------------- 1 |

4.6 对象构造

2 | 3 | ### 4.6.1 重载 4 | 5 | * 1、如果多个方法(比如,StringBuilder构造器方法)有相同的名字、不同的参数,便产生了重载。 6 | * 2、编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,就会产生编译时错误,因为根本不存在匹配,或者没有一个比其他的更好。(**这个过程被称为重载解析**(overloadingresolution)。) 7 | * 3、Java允许重载任何方法,而不只是构造器方法。因此,要完整地描述一个方法,需要指出**方法名以及参数类型**。这叫做方法的**签名(signature)**。例如,String类有 4 个称为 indexOf 的公有方法。它们的签名是: 8 | ```java 9 | indexOf(int) 10 | indexOf(int, int) 11 | indexOf(String) 12 | indexOf(String, int) 13 | ``` 14 | * 4、**返回类型不是方法签名的一部分。也就是说,不能有两个名字相同、参数类型也相同却返回不同类型值的方法。** 15 | 16 | ### 4.6.2 默认域初始化 17 | 18 | * 1、如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:数值为 0、布尔值为 false、对象引用为null。 19 | * 2、必须明确地初始化方法中的局部变量。但是,如果没有初始化类中的域,将会被自动初始化为默认值(0、false或 null)。 20 | 21 | ### 4.6.3 无参数的构造器 22 | 23 | * 1、很多类都包含一个无参数的构造函数,对象由无参数构造函数创建时,其状态会**设置为适当的默认值**。例如,以下是Employee类的无参数构造函数: 24 | ```java 25 | public Employee(){ 26 | name = ""; 27 | salary = 0; 28 | hireDay = LocalDate,now(); 29 | } 30 | ``` 31 | * 2、如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数构造器。这个构造器将所有的实例域设置为默认值。于是,实例域中的数值型数据设置为 0、布尔型数据设置为 false、所有对象变量将设置为 null。 32 | * 3、如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法。因为当存在构造方法时,编译器便不会为类提供默认的无参构造方法。 33 | 34 | ### 4.6.4 显式域初始化 35 | 36 | * 1、通过重载类的构造器方法,可以采用多种形式设置类的实例域的初始状态。确保不管怎样调用构造器,每个实例域都可以被设置为一个有意义的初值,这是一种很好的设计习惯。 37 | * 2、在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,这种方式特别有用。 38 | 39 | ### 4.6.5 参数名 40 | 41 | * 1、有些程序员在每个参数前面加上一个前缀“a”: 42 | ```java 43 | public Employee(String aNaie, double aSalary){ 44 | name = aName; 45 | salary = aSalary; 46 | } 47 | ``` 48 | * 2、参数变量用同样的名字将实例域屏蔽起来。例如,如果将参数命名为 salary, salary 将引用这个参数,而不是实例域。但是,可以采用 this,salary 的形式访问实例域。回想一下,this指示隐式参数,也就是所构造的对象。下面是一个示例: 49 | ```java 50 | public Employee(String naie, double salary){ 51 | this.name = name; 52 | this.salary = salary; 53 | } 54 | ``` 55 | 56 | ### 4.6.6 调用另一个构造器 57 | 58 | * 1、如果构造器的第一个语句形如 `this(...)`,这个构造器将调用同一个类的另一个构造器。下面是一个典型的例子: 59 | ```java 60 | public Employee(double s){ 61 | // calls Employee(String, double) 62 | this("Employee #" + nextld, s); 63 | nextld++; 64 | } 65 | ``` 66 | * 当调用 `new Employee(60000)` 时,`Employee(double)` 构造器将调用`Employee(String,double)`构造器。 67 | * 采用这种方式使用 this关键字非常有用,这样对公共的构造器代码部分只编写一次即可。 68 | * 在 Java 中,this引用等价于 C++ 的 this指针。但是,在 C++中,一个构造器不能调用另一个构造器。在 C++中,必须将抽取出的公共初始化代码编写成一个独立的方法。 69 | 70 | ### 4.6.7 初始化块 71 | 72 | * 1、初始化数据域的方法: 73 | * 在构造器中设置值 74 | * 在声明中赋值 75 | * 初始化块(initialization block) 76 | * 2、调用构造器的具体处理步骤: 77 | * 1)所有数据域被初始化为默认值(0、false或 null)。 78 | * 2)按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块。 79 | * 3)如果构造器第一行调用了第二个构造器,则执行第二个构造器主体。 80 | * 4)执行这个构造器的主体。 81 | * 3、可以通过提供一个初始化值,或者使用一个静态的初始化块来对静态域进行初始化。 82 | ```java 83 | private static int nextld = 1; 84 | ``` 85 | * 4、如果对类的静态域进行初始化的代码比较复杂,那么可以使用**静态的初始化块**。将代码放在一个块中,并标记关键字 static。下面是一个示例。其功能是将雇员 ID 的起始值赋予一个小于 10000的随机整数。 86 | ```java 87 | // static initialization block 88 | static{ 89 | Random generator = new Random(); 90 | nextld = generator.nextlnt(lOOOO); 91 | } 92 | ``` 93 | * 5、在类第一次加载的时候,将会进行静态域的初始化。与实例域一样,除非将它们显式地设置成其他值,否则默认的初始值是 0、false或 null。所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行。 94 | 95 | ### 4.6.8 对象析构与 finalize方法 96 | 97 | * 1、某些对象使用了内存之外的其他资源,例如,文件或使用了系统资源的另一个对象的句柄。在这种情况下,当资源不再需要时,将其回收和再利用将显得十分重要。 98 | * 2、可以为任何一个类添加 finalize方法。finalize方法将在垃圾回收器清除对象之前调用。在实际应用中,不要依赖于使用 finalize方法回收任何短缺的资源,这是因为很难知道这个 方法什么时候才能够调用。 99 | * 3、有个名为 `System.mnFinalizersOnExit(true)` 的方法能够确保 `finalizer 方法`在 Java关闭前被调用。不过,这个方法并不安全,也不鼓励大家使用。有一种代替的方法是使用方法 `Runtime.addShutdownHook 添加“关闭钓” (shutdown hook)`。 100 | * 4、如果某个资源需要在使用完毕后立刻被关闭,那么就需要由人工来管理。对象用完时,可以应用一个 close方法来完成相应的清理操作。 -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.7包.md: -------------------------------------------------------------------------------- 1 |

4.7 包

2 | 3 | * 1、标准的 Java类库分布在多个包中,包括java.lang、java.util 和java.net 等。 4 | * 2、所有标准的Java包都处于java和javax 包层次中。 5 | * 3、**使用包的主要原因是确保类名的唯一性**。 6 | * 4、从编译器的角度来看,嵌套的包之间没有任何关系。例如,java.utU 包与java.util.jar包毫无关系。每一个都拥有独立的类集合。 7 | 8 | ### 4.7.1 类的导入 9 | 10 | * 1、一个类可以使用所属包中的所有类,以及其他包中的公有类(public class)。 11 | * 2、可以采用两种方式访问另一个包中的公有类。 12 | * 第一种方式是在每个类名之前添加完整的包名。例如: 13 | ```java 14 | java.tiie.LocalDate today = java.tine.LocalDate.now(); 15 | ``` 16 | * 第二种是用import语句,import 语句是一种引用包含在包中的类的简明描述。一旦使用了 import语句,在使用类时,就不必写出包的全名了。 17 | * 可以使用 import语句导人一个特定的类或者整个包。 18 | * import 语句应该位于源文件的顶部(但位于 package语句的后面)。 19 | * 例如,可以使用下面这条语句导人 java.util 包中所有的类。 20 | ```java 21 | import java.util.*; 22 | 然后,就可以使用 23 | LocalDate today = LocalDate.now(); 24 | 而无须在前面加上包前缀。 25 | 还可以导人一个包中的特定类: 26 | import java.time.LocalDate; 27 | ``` 28 | * 3、需要注意的是,只能使用星号(`*`) 导入一个包,而不能使用 `import java.*` 或`import java.*.*` 导入以 java为前缀的所有包。 29 | * 4、在大多数情况下,只导入所需的包,并不必过多地理睬它们。但在发生命名冲突的时候,就不能不注意包的名字了。例如,java.util 和java.sql 包都有日期( Date) 类。如果在程序中导入了这两个包: 30 | ```java 31 | import java.util.*; 32 | import java.sql.*; 33 | 在程序使用 Date类的时候,就会出现一个编译错误: 34 | Date today; // Error java.util.Date or java.sql.Date? 35 | 此时编译器无法确定程序使用的是哪一个 Date类。可以采用增加一个特定的 import语句来解决这个问题: 36 | import java.util.*; 37 | import java.sql.*; 38 | import java.util.Date; 39 | 40 | 如果这两个 Date类都需要使用,又该怎么办呢? 答案是,在每个类名的前面加上完整的包名。 41 | java.util.Date deadline = new java.util.Date(); 42 | java.sql.Date today = new java.sql.Date(...); 43 | 在包中定位类是编译器 (compiler) 的工作。类文件中的字节码肯定使用完整的包名来引用其他类。 44 | ``` 45 | 46 | ### 4.7.2 静态导入 47 | 48 | * 1、如果在源文件的顶部,添加一条指令,就可以使用 System类的静态方法和静态域,而不必加类名前缀。 49 | ```java 50 | import static java.lang.System.*; 51 | 52 | out.println("Goodbye, World!");//i.e., System.out 53 | exit(0);//i.e., System.exit 54 | ``` 55 | * 另外,还可以导入特定的方法或域: 56 | ```java 57 | import static java.lang.System.out; 58 | ``` 59 | 60 | ### 4.7.3 将类放入包中 61 | 62 | * 1、要想将一个类放人包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。 63 | ```java 64 | package com.edu.corejava; 65 | public class Employee{ 66 | ...... 67 | } 68 | ``` 69 | 70 | ### 4.7.4 包作用域 71 | 72 | * 1、标记为 public 的部分可以被任意的类使用;标记为 private 的部分只能被定义它们的类使用。如果没有指定 public 或 private, 这个部分(类、方法或变量)可以被同一个包中的所有方法访问。 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.8类路径.md: -------------------------------------------------------------------------------- 1 |

4.8 类路径

2 | 3 | * 1、为了使类能够被多个程序共享,需要做到下面几点: 4 | * 1)把类放到一个目录中,例如 /home/user/classdir。需要注意,这个目录是包树状结构的基目录。如果希望将 com.horstmann.corejava.Employee类添加到其中,这个 Employee.class类文件就必须位于子目录 /home/user/classdir/com/horstmann/corejava中。 5 | * 2)将 JAR 文件放在一个目录中,例如:/home/user/archives。 6 | * 3)设置类路径(classpath)。类路径是所有包含类文件的路径的集合。 7 | * 2、在UNIX 环境中,类路径中的不同项目之间采用冒号(:)分隔: 8 | ```java 9 | /home/user/classdir:.:/home/user/archives/archive.jar 10 | ``` 11 | * 3、在 Windows环境中,类路径中的不同项目之间采用(;)分隔: 12 | ```java 13 | c:\classdir;.;c:\archives\archive.jar 14 | ``` 15 | * 4、在2、3两种情况中,句点(.)表示当前目录。 16 | * 5、类路径包括: 17 | * 基目录 /home/user/classdir 或 c:\classes; 18 | * 当前目录 (.); 19 | * JAR文件 /home/user/archives/archive.jar 或 c:\archives\archive.jar。 20 | * 6、从 JavaSE6开始,可以在 JAR文件目录中指定通配符,如下: 21 | ```java 22 | /home/user/dassdir:.:/home/aser/archives/* 23 | 或者 24 | c:\classdir;.;c:\archives\* 25 | ``` 26 | * 7、在 UNIX 中,禁止使用 `*` 以防止 shell 命令进一步扩展。 27 | * 8、假定虚拟机要搜寻 com.horstmann.corejava.Employee类文件。它首先要查看存储在jre/lib 和jre/lib/ext 目录下的归档文件中所存放的系统类文件。显然,在那里找不到相应的类文件,然后再查看类路径。然后查找以下文件: 28 | * /home/user/classdir/com/horstmann/corejava/Employee.class 29 | * com/horstmann/corejava/Employee.class从当前目录开始 30 | * com/horstmann/corejava/Employee.classinside/home/user/archives/archive.jar 31 | * 9、编译器定位文件要比虚拟机复杂得多。如果引用了一个类,而没有指出这个类所在的包,那么编译器将首先查找包含这个类的包,并询查所有的 import指令,确定其中是否包含了被引用的类。例如,假定源文件包含指令: 32 | ```java 33 | import java.util.*; 34 | import com.horstmann.corejava.*; 35 | ``` 36 | * 并且源代码引用了 Employee类。编译器将试图查找 java.lang.Employee(因为java.lang包被默认导入)、java.util.Employee、com.horstmann.corejava.Employee和当前包中的 Employee。对这个类路径的所有位置中所列出的每一个类进行逐一查看。如果找到了一个以上的类,就会产生编译错误(因为类必须是唯一的,而 import语句的次序却无关紧要)。 37 | * 编译器的任务不止这些,它还要查看源文件(Sourcefiles) 是否比类文件新。如果是这样的话,那么源文件就会被自动地重新编译。 38 | * 仅可以导人其他包中的公有类。一个源文件只能包含一个公有类,并且文件名必须与公有类匹配。因此,编译器很容易定位公有类所在的源文件。 39 | * 也可以从当前包中导入非公有类。这些类有可能定义在与类名不同的源文件中。如果从当前包中导人一个类,编译器就要搜索当前包中的所有源文件,以便确定哪个源文件定义了这个类。 40 | 41 | 42 | ### 4.8.1 设置类路径 43 | 44 | * 1、最好采用-classpath(或 -cp) 选项指定类路径: 45 | ```java 46 | java-classpath /home/user/dassdir:.:/home/user/archives/archive.jar HyProg 47 | 或者 48 | java -classpath c:\classdir;.;c:\archives\archive.jar MyProg 49 | ``` 50 | * 整个指令应该书写在一行中。将这样一个长的命令行放在一个 shell 脚本或一个批处理文件中是一个不错的主意。 51 | * 2、利用`-classpath`选项设置类路径是首选的方法,也可以通过设置 `CLASSPATH` 环境变量完成这个操作。其详细情况依赖于所使用的 shell。 52 | * 在 BourneAgain shell (bash) 中,命令格式如下: 53 | ```java 54 | export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar 55 | ``` 56 | * 在 Windows shell, 命令格式如下: 57 | ```java 58 | set CLASSPATH=c:\classdir;.;c:\archives\archive.jar 59 | ``` 60 | * 直到退出 shell 为止,类路径设置均有效。 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/4.9文档注释.md: -------------------------------------------------------------------------------- 1 |

4.9 文档注释

2 | 3 | ### 4.9.1 注释的插入 4 | 5 | * 1、javadoc 实用程序(utility) 从下面几个特性中抽取信息: 6 | * 包 7 | * 公有类与接口 8 | * 公有的和受保护的构造器及方法 9 | * 公有的和受保护的域 10 | * 2、注释以 `/**` 开始,并以 `*/` 结束。 11 | * 3、每个 `/** . . . */` 文档注释在标记之后紧跟着自由格式文本(free-form text)。标记由@开始,如@author 或@param。 12 | * 4、自由格式文本的第一句应该是一个概要性的句子。javadoc实用程序自动地将这些句子抽取出来形成概要页。 13 | * 5、在自由格式文本中,可以使用 HTML 修饰符,例如,用于强调的`...`、用于着重强调的`...`以及包含图像的``等。不过,一定不要使用`
`, 因为它们会与文档的格式产生冲突。若要键入等宽代码,需使用 `{@code...}` 而不是`...`,这样一来,就不用操心对代码中的`<`字符转义了。 14 | 15 | ### 4.9.2 类注释 16 | 17 | * 1、类注释必须放在 import 语句之后,类定义之前。 18 | ```java 19 | /** 20 | * A {©code Card} object represents a playing card, such 21 | * as "Queen of Hearts". A card has a suit (Diamond, Heart, 22 | * Spade or Club) and a value (1= Ace, 2 . . . 10, 11 = Jack, 23 | * 12 = Queen, 13 = King) 24 | */ 25 | public class Card{ 26 | ...... 27 | } 28 | ``` 29 | 30 | ### 4.9.3 方法注释 31 | 32 | * 1、每一个方法注释必须放在所描述的方法之前。除了通用标记之外,还可以使用下面的标记: 33 | * @param 变量描述 34 | * 这个标记将对当前方法的“param”(参数)部分添加一个条目。这个描述可以占据多行,并可以使用 HTML 标记。一个方法的所有@param标记必须放在一起。 35 | * @return 描述 36 | * 这个标记将对当前方法添加“return” (返回)部分。这个描述可以跨越多行,并可以使用 HTML 标记。 37 | * @throws 类描述 38 | * 这个标记将添加一个注释,用于表示这个方法有可能抛出异常。有关异常的详细内容 39 | 40 | ```java 41 | /** 42 | * Raises the salary of an employee. 43 | * @param byPercent the percentage by which to raise the salary (e.g. 10 means 10%) 44 | * ©return the amount of the raise 45 | */ 46 | public double raiseSalary(double byPercent){ 47 | double raise = salary * byPercent / 100; 48 | salary += raise; 49 | return raise; 50 | } 51 | ``` 52 | 53 | ### 4.9.4 域注释 54 | 55 | * 1、只需要对公有域(通常指的是静态常量)建立文档。例如: 56 | ``` 57 | /** 58 | * The "Hearts" card suit 59 | */ 60 | public static final int HEARTS =1; 61 | ``` 62 | 63 | ### 4.9.5 通用注释 64 | 65 | * 1、下面的标记可以用在类文档的注释中。 66 | * @author 姓名 67 | * 这个标记将产生一个"author" (作者)条目。可以使用多个@aUthor标记,每个@author标记对应一个作者 68 | * ©version 文本 69 | * 这个标记将产生一个“version”(版本)条目。这里的文本可以是对当前版本的任何描述。 70 | * 2、下面的标记可以用于所有的文档注释中。 71 | * @sinee 文本 72 | * 这个标记将产生一个“since” (始于)条目。这里的 text 可以是对引人特性的版本描述。例如,©sinceversion1.7.10 73 | * @deprecated 文本 74 | * 这个标记将对类、方法或变量添加一个不再使用的注释。文本中给出了取代的建议。例如, 75 | ```java 76 | @deprecated Use setVIsible(true) instead 77 | ``` 78 | * 3、通过@see和@link标记,可以使用超级链接,链接到 javadoc文档的相关部分或外部文档。 79 | * @see 引用 80 | * 这个标记将在“see also” 部分增加一个超级链接。它可以用于类中,也可以用于方法中。 81 | * 需要注意,一定要使用井号(#),而不要使用句号(.)分隔类名与方法名,或类名与变量名。 82 | 83 | ```java 84 | @see com.horstraann.corejava.Employee#raiseSalary(double) 85 | @see The Core]ava home page 86 | Isee"Core Java 2 volume 2n 87 | 88 | {@link package,class#feature label] 89 | ``` 90 | 91 | ### 4.9.6 包与概述注释 92 | 93 | * 1、可以直接将类、方法和变量的注释放置在 Java源文件中,只要用 `/** ... */` 文档注释界定就可以了。但是,要想产生包注释,就需要在每一个包目录中添加一个单独的文件。可以有如下两个选择: 94 | * 1)提供一个以 package.html 命名的 HTML 文件。在标记`...`之间的所有文本都会被抽取出来。 95 | * 2)提供一个以 package-info.java命名的 Java 文件。这个文件必须包含一个初始的以 `/**和 */` 界定的 Javadoc 注释,跟随在一个包语句之后。它不应该包含更多的代码或注释。 96 | * 2、还可以为所有的源文件提供一个概述性的注释。这个注释将被放置在一个名为 overview.html 的文件中,这个文件位于包含所有源文件的父目录中。标记`...`之间的所有文本将被抽取出来。当用户从导航栏中选择“Overview” 时,就会显示出这些注释内容。 97 | 98 | ### 4.9.7 注释的抽取 99 | 100 | * 1、假设 HTML文件将被存放在目录 docDirectory下。执行以下步骤: 101 | * 1)切换到包含想要生成文档的源文件目录。如果有嵌套的包要生成文档,例如 com.horstmann.corejava, 就必须切换到包含子目录 com 的目录(如果存在 overview.html 文件的话,这也是它的所在目录)。 102 | * 2、如果省略了 -d docDirectory 选项,那 HTML 文件就会被提取到当前目录下。 103 | ```java 104 | 如果是一个包,应该运行命令: 105 | javadoc -d docDirectory nameOfPackage 106 | 或对于多个包生成文档,运行: 107 | javadoc -d docDirectory nameOfPackage\nameOfPackage .. . 108 | 如果文件在默认包中,就应该运行: 109 | javadoc -d docDirectory *.java 110 | ``` 111 | * `-link`, 用来为标准类添加超链接。例如,如果使用命令,所有的标准类库类都会自动地链接到 Oracle 网站的文档。 112 | ```java 113 | javadoc -link http://docs.oracle.eom/:javase/8/docs/api *.java 114 | ``` 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/001.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/002.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/003.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/004.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/005.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/006.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/006.png -------------------------------------------------------------------------------- /Java核心卷/第四章对象与类/img/007.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/Java核心卷/第四章对象与类/img/007.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JAVA学习笔记 2 | ================= 3 | * 第一章、JAVA 的基本程序设计结构 4 | 5 | * [1.1 认识JAVA](./1.1认识JAVA.md) 6 | * [1.2 原生数据类型](./1.2原生数据类型.md) 7 | * [1.3 运算符(Operator)](./1.3运算符(Operator).md) 8 | * [1.4 流程控制语句](./1.4流程控制语句.md) 9 | * [1.5 命名规则](./1.5命名规则.md) 10 | 11 | * 第二章、面向对象 12 | 13 | * [2.1 面向对象](./2.1面向对象.md) 14 | * [2.2 面向对象之封装(Encapsulation)](./2.2面向对象之封装.md) 15 | * [2.3 面向对象之继承(Inheritence)](./2.3面向对象之继承.md) 16 | * [2.4 面向对象之多态(Polymorphism)](./2.4面向对象之多态.md) 17 | * [2.5 接口的多继承](./2.5接口的多继承.md) 18 | * [2.6 类](./2.6类.md) 19 | 20 | * 第三章、一些零散的知识 21 | 22 | * [3.1 Java中的关键字](./3.1Java中的关键字.md) 23 | * [3.2 导包](./3.2导包.md) 24 | * [3.3 访问修饰符](./3.3访问修饰符.md) 25 | * [3.4 递归(Recursion)](./3.4递归(Recursion).md) 26 | * [3.5 类型转换](./3.5类型转换.md) 27 | * [3.6 回调](./3.6回调.md) 28 | 29 | * 第四章、API学习 30 | 31 | * [4.1 Object类](./4.1Object类.md) 32 | * [4.2 String类](./4.2String类.md) 33 | * [4.3 StringBuffer类](./4.3StringBuffer类.md) 34 | * [4.4 包装类](./4.4包装类.md) 35 | 36 | * 第五章、集合 37 | 38 | * [5.1 数组(Array)](./5.1数组(Array).md) 39 | * [5.2 数组之排序](./5.2数组之排序.md) 40 | * [5.3 集合简介](./5.3集合简介.md) 41 | * [5.4 集合之List](./5.4集合之List.md) 42 | * [5.5 集合之Map](./5.5集合之Map.md) 43 | * [5.6 集合之Set](./5.6集合之Set.md) 44 | * [5.7 集合总结](./5.7集合总结.md) 45 | 46 | * 第六章、核心 47 | 48 | * [6.1 泛型(Generics)](./6.1泛型.md) 49 | * [6.2 枚举类型](./6.2枚举类型.md) 50 | * [6.3 静态导入](./6.3静态导入.md) 51 | * [6.4 反射](./6.4反射.md) 52 | * [6.5 JAVA注解(Annotation)](./6.5JAVA注解(Annotation).md) 53 | * [6.6 通过JUnit深入理解反射与注解的使用方式和与场景](./6.6通过JUnit深入理解反射与注解的使用方式和与场景.md) 54 | * [6.7 异常(Exception)](./6.7异常(Exception).md) 55 | * [6.8 内部类](./6.8内部类.md) 56 | 57 | * 第七章、Swing 58 | 59 | * [7.1 图形化界面GUI (Graphical User Interface) ](./7.1图形化界面GUI.md) 60 | 61 | * 第八章、I/O 62 | 63 | * [8.1 IO(输入输出)](./8.1IO(输入输出).md) 64 | 65 | * 第九章、拓展 66 | 67 | * [9.1 序列化](./9.1序列化.md) 68 | * [9.2 线程](./9.2线程.md) 69 | * [9.3 JVM之加载器](./9.3JVM之加载器.md) 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /Sort.java: -------------------------------------------------------------------------------- 1 | package com.edu.test; 2 | 3 | public class Sort { 4 | public void bubbleSort(int[] array) { 5 | for(int i=0;i array[j+1]) { 8 | int temp = array[j]; 9 | array[j] = array[j+1]; 10 | array[j+1] = temp; 11 | } 12 | } 13 | } 14 | for(int i : array) { 15 | System.out.print(i + " "); 16 | } 17 | } 18 | 19 | public void directSort(int[] array) { 20 | int index; 21 | for(int i=1;i array[index]) { 25 | index = j; 26 | } 27 | } 28 | int temp = array[array.length-i]; 29 | array[array.length-i] = array[index]; 30 | array[index] = temp; 31 | } 32 | for(int i : array) { 33 | System.out.print(i + " "); 34 | } 35 | } 36 | 37 | public void reversalSort(int[] array) { 38 | for(int i=0;i<=array.length/2;i++) { 39 | int temp = array[i]; 40 | array[i] = array[array.length-i-1]; 41 | array[array.length-i-1] = temp; 42 | } 43 | for(int i : array) { 44 | System.out.print(i + " "); 45 | } 46 | } 47 | 48 | public static void main(String[] args) { 49 | //int[] array = new int[]{3,5,9,4,1,2}; 50 | int[] array = new int[]{1,2,3,4,5}; 51 | Sort sort = new Sort(); 52 | //sort.bubbleSort(array); 53 | sort.directSort(array); 54 | //sort.reversalSort(array); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /img/5-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/5-1.png -------------------------------------------------------------------------------- /img/Exception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/Exception.png -------------------------------------------------------------------------------- /img/HashMap的内存实现布局.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/HashMap的内存实现布局.png -------------------------------------------------------------------------------- /img/JAVA层次图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/JAVA层次图.png -------------------------------------------------------------------------------- /img/synchronized.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/synchronized.png -------------------------------------------------------------------------------- /img/数组1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/数组1.png -------------------------------------------------------------------------------- /img/数组2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/数组2.png -------------------------------------------------------------------------------- /img/迭代器.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/迭代器.png -------------------------------------------------------------------------------- /img/集合.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunnyandgood/JAVAStudyNotes/868104c0ca2759b8e39ac7c89fa7953d63e53cc4/img/集合.png --------------------------------------------------------------------------------