此文还需完善,仅供个人使用
更多 Java 知识请参考:
一些基础概念
在JDK安装目录下可以找到一个src.zip
压缩文件,该文件中包含了Java基础类库的所有源文件。
面向对象
类描述了具有相同特性(数据元素)和行为(功能)的对象集合,所以一个类就是一个数据类型。我们在进行面向对象的程序设计中,实际上进行的是创建新的数据类型;所有的面向对象设计语言都使用class这个关键字来表示数据类型。
JDK安装和环境变量配置
JDK (Java SE Development Kit),Java标准开发包,它包含了:
- Java编译器
- Java运行时环境(JRE)
- 常用的Java类库
JRE(Java Runtime Environment),它是运行Java程序的必需条件:
- 核心虚拟机(JVM)
- 类加载器
- 字节码校验器
- 大量基础类库
- … (运行Java程序的其他环境支持)
Java EE SDK (Software Development Kit)软件开发工具包 :(它包含了JDK)包含特定的软件包,软件框架/硬件平台,操作系统等建立应用的开发工具的集合。
暂时用不到。做Java Web开发用JDK就好。
JDK的选择:
-
Oracle JDK:从Java 11开始,这是一个带有付费支持的品牌商业版本。它可以免费供开发使用,但不能用于生产。
-
Oracle OpenJDK:OpenJDK
-
Azul Zulu 的 Zulu Community版本
值得注意的是,Oracle JDK只发布二进制安装包,而OpenJDK只发布源码。也就是说任何人都可以基于OpenJDK源码构建二进制的OpenJDK发行版
- OpenJDK 有多种含义,可以称为:
- Java平台标准版(Java SE)的免费和开源实现
- http://hg.openjdk.java.net/ - Java源代码又名OpenJDK项目
- 由Oracle维护的预构建的OpenJDK二进制文件(Oracle OpenJDK)
- AdoptOpenJDK - 由社区维护的预构建的OpenJDK二进制文件
各种 OpenJDK 之间的对比:OpenJDK Comparison Matrix
安装AdoptOpenJDK :
可在AdoptOpenJDK官网手动下载;这里在我的Windows中选择使用Scoop进行安装:
|
|
这里选择: adoptopenjdk-lts-hotspot
,其中 lts
表示长期支持版, hotspot
表示是 hotspot JVM。
安装Oracle JDK: (不建议)
- 在Oracle下载安装包
- 运行安装程序
- 选择要安装的组件
- Devepment Tools:是JDK核心。实际上它已经包含了运行Java程序的JRE,该JRE会安装在JDK目录的子目录下。
- Source Code:核心类库的源代码
- Public JRE:公共JRE(可选)。独立的JRE系统。
JDK安装路径下的
src.zip
文件存放了Java所有核心类库的源代码。
API文档下载与查看:
环境变量:
配置Path环境变量:
PATH
环境变量是一系列路径,操作系统将在这些路径中依次查找命令。
在PATH
变量下追加自己的 jdk 安装路径下的 bin
目录的路径...\Java\jdk*.*.*\bin
注意: PATH
环境变量的配置**_只是为了_** 能够在命令行下执行 java
javac
等命令而已,如果使用IDEA开发java则还需要在IDEA中配置**或**为其添加**其他环境变量**。
开发环境可能会用到的环境变量:windows
JAVA_HOME
:JAVA_HOME:D:\Program Files\java\jdk1.8
CLASSPATH
:CLASSPATH:%java_home%\lib
CLASSPATH
环境变量的作用:显式指定Java类(.class文件)的搜索路径。1.4版本之前的JDK才需要设置,现在JRE默认已经能够自动搜寻当前路径.
和JDK下的lib
文件夹;如果你显式设置了CLASSPATH
,则会跳过默认值,所以记得将.
包含在里面。不推荐设置CLASSPATH
Java源文件命名规则
- Java源文件的后缀必须是
.java
- Java源文件的文件名必须与其内部的
public
类名相同
变量
变量的命名:
区分大小写
在Java中,任何对象变量的值都是存储在另外一个地方的对象的引用。new操作符的返回值也是一个引用。
变量的作用域
略
补充:
- 实例变量:声明在一个类中,但在方法和语句块之外声明
- 静态变量(类变量):在类中以static修饰符声明,但必须在方法和语句块之外声明
- 局部变量:在类中某方法中声明
就像引用类型的数组一样,当把Java对象放入数组中时,并不是真正把Java对象放入数组中,而是把对象的引用放入数组中,每个数组元素都是一个引用变量。
变量的初始化
参数传递时栈的变化
java面向对象编程 p97
常量
常量在程序运行时是不能被修改的。
在 Java 中使用 final
关键字来修饰常量。通常使用大写字母表示常量。
**编译时常量:**对于 final 类型的静态变量,如果在编译时就能计算出变量的取值(这是条件),那么这种变量被看做编译时常量。
基本类型
Java语言提供了8种基本类型。6种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
整型: byte :1字节、short:2字节、int:4字节、long:8字节
浮点型:float :4字节、double:8字节
字符型: char : 2字节
布尔类型:boolean
byte 的中文意思就是:字节
包装类型:
字符char与字节byte
来源字节流与字符流的比较。
一个字节(byte) = 8位,是计算机处理数据的基本单位?
字符型(char)通常用于表示单个的字符,比如'a'
,Java使用 16 位 (两字节) 的Unicode字符集作为编码方式。也就是说字符型要考虑字符集的问题。
TODO:字符集。《疯狂Java讲义》
|
|
虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
引用类型
new关键字
当一个引用类型的变量被声明后,如果没有初始化,那么它不指向任何对象。Java语言用new关键字创建对象。其作用如下:
- 为对象分配内存空间,将对象的实例变量自动初始化为其变量类型的默认值。
- 如果实例变量在声明时被显式初始化,那么就把初始化值附给实例变量。
- 调用构造方法。
- 返回对象的引用。
TODO 补充,内存分配
初始值
new
自增和自减
++i
、--i
:先改变变量值,再使该变量值参与表达式的运算
++i
、--i
:先使该变量值参与表达式的运算,再改变变量值
优先级:
%
%
取余/求模
小 % 大
0 % 正
逻辑判断短路现象
即一旦能够明确无误的确定整个表达式的值,就不会再计算(运算)表达式余下部分。
==和equals方法
对于基本的数值类型==
和!=
比较的是两个变量的值。
对于引用类型变量==
和!=
比较的对象的引用(地址)
如果想比较两个基本类型对象的实际内容,可以使用equals()
方法。另大多数Java类库都实现了equals()
方法,以便我们用来比较对象的内容,而非比较对象的引用。
equals()
方法是Object提供的一个实例方法,默认与==
一样比较对象的引用(地址)。正如上面说的大多数Java类库都重新实现了自己的equals()
方法;那么对于自定义的类,如果想比较该类对象的内容,也需要重写equals()
方法。
重写
equals()
方法时,推荐按照 InteliJ IDEA自动生成的equals()
的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12
@Override public boolean equals(Object o) { //判断是否是同一对象 if (this == o) return true; //判断参数是否为null,判断类型是否一致 if (o == null || getClass() != o.getClass()) return false; //强制转换为当前类型 Person that = (Person) o; // 使用Objects.equals替换String的equals,因为前者优化了空指针异常的情况 return age == that.age && Objects.equals(name, that.name); }
|
|
垃圾回收机制
时间和日期
《Java核心技术卷 I》第4章 4.1.1中有关于这些介绍。
Date表示时间,Calendar表示日期?
表示时间点的Date,表示大家熟悉的日历表示法的GregorianCalendar类。其中GregorianCalendar类扩展了一个更加通用的Calendar类。
Date类只提供了少量的方法用来比较两个时间点。例如before和after方法。(其他方法不推荐使用)
两者之间的转换:
GregorianCalendar -> Date
|
|
Date -> GregorianCalendar
|
|
获取当前日期和时间:
|
|
常用工具类和方法
检测是否为 null
|
|
import
import: import可以向某个Java文件中导入指定包层次下某个类或全部类。
import 语句中的星号(*)只能代表类,不能代表包。
示例:
import lee.Apple; //导入lee包下的单个Apple类
import lee.*; //表明导入lee包下的所有类。而lee包下的sub子包中的类则不会被导入。
import lee.sub.* //必须使用该语句,才能导入lee的子包sub中的类
import static 静态导入:
import static静态导入用来导入静态方法和静态域。而import用于导入类。
静态导入用于导入指定类的单个静态成员变量、静态方法或全部的静态成员变量、静态方法。
示例:Math类中有非常多的静态方法,使用静态导入后使用这些静态方法时便可以不添加类名Math作为前缀。
import导入后,使用导入包中某类的公共成员时,可以省略包名。
而使用import static导入后,使用导入某类的公共静态成员时可以省略类名。
(静态方法和静态域属于某个类;而类属于某个包)
数组
声明、定义和初始化示例:
|
|
**初始化:**数组元素中的基本数据类型值会自动初始化成空值(对于数字和字符,就是0,对于布尔型,就是false)
数组大小不可变。
数组是一个引用:
|
|
多维数组
Arrays工具类
Arrays类里包含的一下static修饰的方法可以直接操作数组。
- 二分查找
- 复制数组
- 比较两个数组是否相等(长度和元素都相等)
- 对数组排序
- 转换为字符串
另外:Java 8还新增了提高并行能力的对应方法。
数组与集合的比较
给出一个数组和集合:
|
|
数组的特点:
[]
: 表示数组- 数组有索引:每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。可以使用
数组名[索引]
的方式访问和修改数组元素 - 可以通过
length
属性(注意:是属性而非方法),获取数组长度:数组名.length
- 长度(大小)固定不变
(对应)集合的特点:
- 有序、可重复的集合有索引,比如List及其子类(LinkedList、 ArrayList)(自己总结)
- 可以通过
size()
方法获取元素个数
由于只有有序可重复的集合才有索引(这只是自己总结的),所以Collection没有提供索引相关的方法。比如:List集合有序,而Set集合无序。LinkedHashSet是有序的,但是没有索引。
打印数组和集合:
|
|
获取数组和集合中的元素:
|
|
对于有序、 可重复集合,可以使用和索引相关的方法比如 get()
等方法来获取对应位置上的元素。
对于无序、 不可重复集合,可以使用Collection的迭代器来遍历集合,从而获取元素。
由于部分集合才有索引,那么Collection作为集合的父类当然不能提供索引相关的方法。
注意: 不能在使用Iterator遍历集合时,对集合执行增删操作。因为Iterator并不能和集合同步(它们是不同的类),它不知道集合已经发生了改变。
String
就是char数组,唯一为其重载了加号。String的构造过程。
见《Java编程思想》
集合
单列集合:
- Collection: 所有 单列集合 的顶层接口,定义了单列集合的共性方法
add(E e) remove(Object o) size() isEmpty() clear() contains(Object o)....
- List: 存取有序,有索引,元素可以重复
-
Vector : 过时 数组结构 查询快,增删慢 线程安全,效率低
-
ArrayList :
数组结构 查询快,增删慢 线程不安全,效率高
-
LinkedList :
链表结构 查询慢,增删快 线程不安全,效率高
-
- Set: 存取无序,无索引,元素不可重复(包含的方法基本与Collection相同)
- HashSet:由哈希表保证元素的唯一性(“不保证有序"和"保证无序"不等价,HashSet 的 iterator 是前者而不是后者,所以在一次运行中看到有序的结果也是正常的,但不能依赖这个有序行为。)
- LinkedHashSet:存取有序,元素唯一, 由链表保证存取有序,有哈希表保证元素唯一
- TreeSet:由树状结构保证元素的唯一性
- List: 存取有序,有索引,元素可以重复
双列集合
没有索引,没有提供迭代器。
- Map:
- HashMap:
- LinkedHashMap:
- TreeMap:
- HashMap:
如果新添加的键值对的键重复,就会覆盖之前该键的值。
HashSet的底层依赖于HashMap实现(顺序不要错了)。
类
- Java只支持单继承,不支持多继承。
- Java支持多层继承(继承体系)。
访问权限
访问控制的4个级别:
- public:公开级别
- protected:受保护级别,向子类及同一个包中的类公开
- 默认级别:没有访问控制修饰符,向同一个包中的类公开
- private:私有级别,只有类本身可以访问,不对外公开
访问级别 | 访问控制修饰符 | 同 类 | 同 包 | 子 类 | 不同的包 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | — |
默认 | 没有访问控制修饰符 | √ | √ | — | — |
私有 | private | √ | √ | — | — |
方法覆盖与方法重载
方法重写(覆盖) (Override):子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
方法重载(Overload):对于类的方法(包括从父类中继承的方法),如果有两个方法的方法名相同,但参数不一致,那么可以说,一个方法式另一个方法的重载方法。
两者的几个不同之处:
- 方法覆盖要求签名必须一致,而方法重载要求参数签名必须不一致
- 方法覆盖要求返回类型必须一致,而方法重载对此不做限制
- 方法覆盖对方法的访问权限(不能缩小父类方法的访问权限)和抛出的异常 (子类不能抛出比父类更多(大)的异常)有特殊的要求,而方法重载没有要求。
构造方法
构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。
super和this关键字
super
和this
关键字都可以用来覆盖Java语言的默认作用域,使被屏蔽的方法或变量变为可见。
方法或变量变被屏蔽的场合:
- 场合一:在一个方法内,当局部变量和类的成员变量同名,或局部变量和父类的成员变量同名,安装变量作用域规则,只有局部变量在方法内可见。
- 场合二: 方子类的某个方法覆盖了父类的一个方法,在子类的范围内,父类的方法不可见。
- 场合三:当子类中定义了和父类同名的成员变量时,在子类的范围内,父类的成员变量不可见。
当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行;以此类推,任何Java对象,最先执行的总是 java.lang.Object
类的构造器。
子类的每个构造方法中均有默认的super()
,调用父类的空参构造。手动调用父类构造会覆盖默认的super()
。
super()
和 this()
都必须 是在构造方法的第一行,所以不能同时出现。
正如this
不能出现在static
修饰的方法中一样,super
也不能出现在static
修饰的方法中static
修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象,因而 super
限定也就失去了意义。
即,只能在构造方法或实例方法内使用super关键字,而在静态方法和静态代码块内不能使用super关键字。
多态
动态绑定,向上、向下转型
Static
修饰符 | 类 | 成员方法 | 构造方法 | 成员变量 | 局部变量 |
---|---|---|---|---|---|
abstract | √ | √ | — | — | — |
static | — | √ | — | √ | — |
public | √ | √ | √ | √ | — |
protected | — | √ | √ | √ | — |
private | — | √ | √ | √ | — |
synchronized | — | √ | — | — | — |
final | √ | √ | — | √ | √ |
被static所修饰的成员变量和成员方法归某个类所有,它不依赖特定实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定位到它们。
static变量与C中的全局变量相似;Java语言不支持不属于任何类的全局变量,静态变量提供这一功能。
静态方法必须被实现,一个类的静态方法不能被定义为抽象方法。
方法的字节码都位于方法区:不管式实例方法,还是静态方法,它们的字节码都位于方法区。
static代码块:类中可以包含静态代码块,它不存在与任何方法体中。在Java虚拟机加载类时会执行这些静态代码块。
静态成员不能访问非静态成员。
Java编译器把Java方法的源程序代码编译成二进制的编码,称为字节码。
final
final
常量
final
修饰的类不能被继承final
修饰的方法不能被子类的方法覆盖final
修饰的变量表示常量,只能被赋值一次final
不用来修饰构造方法,因为父类的构造方法和子类的构造方法不存在覆盖关系private
修饰的方法不能被子类覆盖,因此默认也是final
的。- 对于
final
类型的实例变量,可以在定义变量时,或者在构造方法中进行初始化。
final 和 abstract 互斥
抽象类
abstract修饰符
abstract可用来修饰类和成员方法。
抽象方法,抽象方法没有方法体。
- 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类。如果子类没有实现父类中的抽象方法,那么子类也必须被定义为抽象类。
- 构造方法、静态方法不能是抽象方法。但抽象类中可以有非抽象的静态方法和非抽象的构造方法。
- 抽象方法和抽象类不能被
final
修饰符修饰。(final修饰的类不能有子类,final修饰的方法不允许被子类方法覆盖) - 抽象方法没有方法体。
|
|
接口 interface
接口对其成员变量和方法做了很多限制,接口的特征如下:
- 接口中的成员变量默认都是public、static、final 类型的,必须被显示初始化。(注意要理解这句话)
- 接口中的方法默认都是public、abstract类型的。
- 接口中只能包含上面两种类型的成员或方法。
- 接口没有构造方法(也不能有)
- 一个接口不能实现另一个接口,但它能继承多个其他接口。例如:A、B都是接口,
public interface C extends A,B{//}
- 接口必须通过类来实现它的抽象方法。
- 与子类继承抽象父类相似,当类实现了某个接口时,它必须实现接口中所有的抽象方法,否则这个类必须被定义为抽象类。
- 不允许创建接口的实例,但允许定义接口类型的引用变量,该变量引用实现了这个接口的类的实例。
- 一个类只能继承一个直接的父类,但能实现多个接口。
- 接口不能包含构造器和初始化块定义。
接口里可以包含:
- 成员变量(只能是静态常量;默认使用public staic final)
- 方法(只能时抽象方法、类方法(static)或默认方法(defalt);普通方法默认使用 public absract)
- 内部类(包括内部接口、枚举)的定义。(默认使用public static修饰)
接口中的所有成员都是public的(默认就是public)
|
|
接口是一个完全抽象的类
抽象类与接口主要的两大区别:
- 在抽象类中可以为部分方法提供默认的实现,从而避免在子类中重复实现它们,提高代码的可重用性;而接口中只能包含抽象方法。接口一旦被公布,就必须非常稳定,因为随意在接口中添加抽象方法,会影响到所有的实现类。
- 一个类可以实现(或继承)多个接口,但只能继承一个抽象类。
为什么Java不允许一个类有多个直接的父类?而可以实现多个接口? 见《Java面向对象编程》P223
Java 8新的接口特性
Java 8之前interface中的方法不能有方法体。从Java 8开始interface方法可以有方法体,但是需要使用关键字default
将其指定为默认方法。
default
方法是public
的但不是static
的,这就意味着只能使用接口的实现类的实例来调用这些默认方法。
补充:
Since Java 8, we can have static method in interface. 。
marker or tagged interface(标记或标记接口): 一个没有任何成员(方法)的接口。An interface that have no member is known as marker or tagged interface. For example: Serializable, Cloneable, Remote etc. They are used to provide some essential information to the JVM so that JVM may perform some useful operation.
内嵌的interface:interface中可以内嵌interface
内部类
inner Class:是定义在另一个类范围内的类。
内部类有如下特征:
- 一个内部类被编译成一个名为
OuterClassName$InnerClassName.class
类 - 内部类可以引用定义在它嵌套的外部类中的数据和方法
- 使用可见性修饰符定义内部类时,遵从和应用与在类成员上一样的可见性规则
- 可以将内部类定义为
static
。一个static内部类可以使用外部类的名字访问。一个static类是不能访问外部类的非静态成员的 - 内部类的对象经常在外部类中创建。也可从另一个类中创建一个内部类的对象。
- 如果该内部类是非静态的,就必须先创建一个外部类的实例,然后使用下面的语法创建一个内部类的对象:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
- 如果内部类是静态的,那么使用下面的语法为他创建一个对象:
OuterClass.InnerClass innerObject = new OuterClass.InnerClass();
- 如果该内部类是非静态的,就必须先创建一个外部类的实例,然后使用下面的语法创建一个内部类的对象:
外部类的上一级程序单元是包,所以它只有两个作用域:
- 同一个包内
- 任何位置
内部类的上一级程序单元是外部类,它具有4个作用域:
-
同一个类
-
同一个包
-
父子类
-
任何位置
-
非静态内部类不能拥有静态成员。
-
内部类比外部类可以多使用三个修饰符:private、protected、static
实例内部类
- 实例内部类中不能定义静态成员,而只能定义实例成员。
- 静态内部类(静态的实例内部类)中可以定义静态成员和实例成员(非静态)
由于实例内部类的实例自动持有其外部类的实例的引用,内部类实例存在时其外部类实例肯定已经存在,所以在内部类中,可以直接访问外部类的所以成员,包括成员变量和成员方法。
在多层嵌套中,内部类可以访问所有外部类的成员。
局部内部类
局部内部类是在一个方法中定义的内部类,它的可见范围是当前方法。局部内部类不能用访问控制修饰符和static修饰。
特点:
- 只能在当前方法中使用
- 不能包含静态成员(与实例内部类一样)
- 局部内部类不能用访问控制修饰符修饰
- 可以访问外部类的所有成员;此外,局部内部类还可以访问所在方法中的final类型的参数和变量。
静态内部类
匿名类
一种特殊的内部类,该类没有名字。
特点:
- 匿名类本身没有构造方法,但是会调用父类的构造方法。
A a = new A(v){ void method(){//...} }; a.method();
- 除了可以在外部类的方法内定义匿名类外,还可以在声明一个成员变量时定义匿名类。
- 匿名类除了可以继承类外,还可以实现接口。但最多只能继承或实现一个接口或类。
//比如在一个方法内 Thread t = new Thread(new Runnable()){ //匿名内部类,实现了Runnable接口 public void run(){ } }; t.start();
- 与局部内部类一样,如果匿名类位于一个方法中,还能访问所在方法的final类型的变量和参数
内部类的继承
泛型
泛型通配符
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
举个例子大家理解使用即可:
|
|
tips:泛型不存在继承关系
Collection<Object> list = new ArrayList<String>();
这种是错误的。
受限泛型
通配符高级使用—-受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
|
|
|
|
IO流
IO流范围太宽,只介绍部分常用的类
StringWriter和StringReader
Java 8
Java8新增的Lambda表达式
Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
只能有一个抽象方法,但是可以有
default
方法。
Lambda表达式的主要作用就是替代匿名内部类的烦琐语法。
Lambda表达式由三部分组成:
- 形参列表:
()
- 箭头:
->
- 代码块:
{}
Lambda表达式与函数式接口:
Lambda表达式的类型,也被称为”目标类型"(target type),Lambda表达式的目标类型必须是"函数式接口(functional interface)"。
函数式接口:代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
Lambda表达式的结果被当成函数式接口的实例对象(对比:采用匿名类语法来创建的接口实例)所以可以使用 Lambda 表达式进行赋值。
Lambda表达式的两个限制:
- Lambda表达式的目标类型必须是明确的函数式接口
- Lambda表达式只能为函数式接口创建对象
Lambda表达式的使用场景:
- 将Lambda表达式赋值给函数式接口类型的变量
- 将Lambda表达式作为函数式接口类型的参数传给某个方法
- 使用函数式接口对Lambda表达式进行强制类型转换
|
|
|
|
使用Lambda来节省新能:
|
|
|
|
Lambda的省略规则:
- 形参列表:
- 可以省略形参类型
- 如果形参列表中只有一个参数,圆括号
()
可以省略
- 代码块:(要省全部省)
- 如果代码块中只包含一条语句,则允许省略代码块的花括号
{}
和该语句末尾的分号;
(必须同时) - 如果代码块中只有一条
return
语句,还可以省略retuen
关键字(必须同时按上一条规则进行省略)
- 如果代码块中只包含一条语句,则允许省略代码块的花括号
|
|
|
|
|
|
方法引用和构造器引用:
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑?
绝大多数情况:lambda的参数会全部传递给现有操作方案(方法引用)。
- 引用类方法:
类名::类方法
- 引用特定对象的实例方法:
特定对象::实例方法
- 引用某类对象的实例方法:
类名::实例方法
- 引用构造器:
类名::new
如果能够使用方法引用或构造器引用IDEA会有提示,这里只做介绍,以后看到这种有两个::
应该知道它是一个Lambda表达式。
Java 9
Java平台,标准版 Oracle JDK 9中的新功能(上)
Java 平台,标准版 Oracle JDK 9 新功能(中)
Java平台,标准版 Oracle JDK 9中的新功能(下)
类的生命周期
《Java面向对象编程》第10章
对象的生命周期
《Java面向对象编程》第11章
反射
递归
递归( recursion) :
无退出条件(或递归调用太多)的递归会造成栈内存溢出(StackOverflowError)的原因:
调用方法会等待被调用方法的返回,如果一直递归则只有入栈而没有出栈,就会导致栈溢出。