本文仅代表个人对Java的语法结构理解。
Java以类、接口等作为一个基本单元,内部存在实现功能的语句;组件组成成员和类、接口等基本单元的框架,成员按照一定流程成为完成一定功能的语句。
章节括号内数字代表涉及到的Java关键字的个数,涉及过的关键字不再标注,Java共包含52个关键字。
一、组件
可组成类、方法、语句等的最基础单位。
修饰符(14)
1、定义
class、interface、enum
2、权限修饰符
public、缺省、protect、private
3、其他修饰符
static、final、abstract
native、synchronized、volatile、transient、strictfp
运算符
按照大致顺序
1、算数运算符——2、位运算符——3、比较运算符——4、逻辑运算符——5、条件运算符——6、赋值运算符
按照详细顺序
小数点和括号:. ()
算数:自增(++),自减(--) 位:按位取反(~)逻辑: 逻辑非(!)
算数:乘(*),除(/),取余(%)
算数:加(+),减(-)
位:左移(<<) 右移(>>) 无符号右移(>>>) 按位与(&) 按位或(|),按位异或(^)
比较:< > <= >= instanceof
比较:== !=
逻辑:逻辑与(&)
逻辑:逻辑异或(^)
逻辑:逻辑或(|)
逻辑:短路与(&&)
逻辑:短路或(||)
条件:三元运算符(条件 ? 结果 1:结果2)
赋值:= *= /= %=
赋值:+= -= <<= >>=
赋值:>>>= &= ^= |=
数据类型(9)
基本数据类型:byte、short、int、long、float、double、char、boolean
引用数据类型(使用):预先提供类(接口等)、自定义类(接口等)、枚举类
无类型:void
数组:xxx[]、xxx[][]
泛型(JDK5引入):<数据类型>、<T extends 上限1&上限2...>、<E> 、<? extends 上限>、<? super 下限>、<?>
// 泛型类、接口
【修饰符】 class 类名<泛型形参列表>{
语句;
}
?
【修饰符】 interface 接口名<泛型形参列表>{
语句;
}
?
// 泛型方法
【修饰符】 <泛型形参列表> 返回值类型 方法名(【数据形参列表】){
语句;
}
命名
CamelCase:类、接口、枚举(定义)、注解
camelCase:变量(定义和使用)、方法(定义)
CONSTANT_NAME:常量
aa.bb.cc:包名
值(3)
数字:123、123L、123F、
字符:‘单字符‘、"字符串"
布尔型:true、false
数组:{1,2,3}
数组(new创建):int[3]、int[]{1,2,3}、int[3][]、int[3][4]、int[3][]{{1},{1,2},{3}}
实例对象
空值、null
参数
形参:数据类型 变量名(eg:String[] args)
可变参数:数据类型... 变量名
实参
其他关键字(14)
保留字:goto、const
继承和实现:extends、implements
实例化:new
指针:super(本对象父类开始访问)、this(本对象开始访问)
判断左边对象(非基本数据类型)是否是右边类或它的子类:instanceof
流程控制:return、break、continue
导入:package、import
断言:assert
二、成员
类、接口等的基础成员。
变量/常量
变量/常量,又叫属性、对象。
变量声明在类中(不在方法中)即为成员变量,声明在方法中即为局部变量。
// 声明、实例化对象(变量)
【修饰符】 数据类型 变量名 = new 类名(【参数】);
?
// 声明常量
final 数据类型 常量名 = 值;
?
// 枚举中声明常量列表
常量名1【(值)】,常量名2【(值)】;
方法
声明在类中。
方法信息与类信息一起存储在方法区,并在栈中执行。
// 声明
【修饰符】 返回值类型 方法名(【形参列表】){
语句;
}
?
// 调用(有返回值可赋值)
【变量名.】方法名(【实参列表】);
一些特殊的方法:
// 启动类中main方法
public static void main(String[] args){
语句;
}
?
// 抽象方法,只能放在抽象类和接口中,无方法体
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
构造器
声明在方法中。
// 初始化方法,没有显示声明构造器,默认生成无参构造
权限修饰符 类名(【形参列表】){
this.a = a;
}
?
// 调用构造器
this(【实参列表】);
?
// 枚举构造器
private 枚举名(【形参列表】){
this.a = a;
}
代码块
声明在方法中。
非静态代码块先于构造器执行,与成员变量显式赋值按代码先后顺序执行,三者合成.class文件中的<init>(【形参列表】)方法,在实例化对象时调用,有几个构造器,就有几个<init>(【形参列表】)方法,如果有父类会先实例化父类对象。
静态代码块与静态变量显式的赋值按代码先后顺序执行,两者合成.class文件中的<clinit>()方法,在初始化类时调用,一个类只有一个<clinit>()方法,如果有父类且父类未初始化会先初始化父类。
【static】{
语句;
}
?
// 同步代码块
synchronized(锁对象) {
语句;
}
内部类
声明在类中(不在方法中,也可在代码块内)即为成员内部类;声明在方法中即为局部内部类,未命名的局部内部类为匿名内部类。
静态内部类使用时才会初始化,不会随着外部类初始化而初始化。静态内部类中只能有静态成员;非静态内部类中不能有静态成员,但可以调用外部的静态成员。
// 匿名内部类
// 无法调用扩展方法,可调用重写方法
父类名 变量名 = new 父类名(【实参列表】){
语句;
};
?
接口名 变量名 = new 接口名(【形参列表】){
语句;
};
?
// 扩展方法和重写方法可直接调用
new 父类名(【实参列表】){
语句;
}.方法名(【实参列表】);
?
new 接口名(【形参列表】){
语句;
}.方法名(【实参列表】);
Lambda表达式
JDK8新增特性,简化匿名内部类的代码,实现SAM(Single Abstract Method:唯一抽象方法)的接口可使用,接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
可使用注解@FunctionalInterface修饰接口,要求接口中的抽象方法只有一个, 这个注解往往会和 lambda 表达式一起出现。
/*
【形参列表】:抽象方法的参数
Lambda体:抽象方法的方法体语句
->:读作“goes to”
*/
接口名 变量名 = (【形参列表】)->{Lambda体}
变量名.方法(【实参列表】);
?
// 1、无参无返回值
接口名 变量名 = () -> {
语句;
}
?
// 2、一个参数无返回值
接口名 变量名 = (数据类型 变量名) -> {
语句;
}
?
// 3、多个参数无返回值
接口名 变量名 = (数据类型 变量名,数据类型 变量名) -> {
语句;
}
?
// 4、无参有返回值
接口名 变量名 = () -> {
语句;
return 返回值;
}
?
// 5、一个参数有返回值
接口名 变量名 = (数据类型 变量名)-> {
语句;
return 返回值;
}
?
// 6、多个参数有返回值
接口名 变量名 = (数据类型 变量名,数据类型 变量名) -> {
语句;
return 返回值;
}
特殊情况下,可将lambda表达式简化。
// lambda表达式简化
// 1、简化所有参数数据类型
接口名 变量名 = (变量名,变量名) -> {
语句;
}
?
接口名 变量名 = (变量名,变量名) -> {
语句;
return 返回值;
}
?
// 2、只有一个参数,简化数据类型和小括号
接口名 变量名 = 变量名 -> {
语句;
}
?
接口名 变量名 = 变量名 -> {
语句;
return 返回值;
}
?
// 3、无返回值只有一条语句,省略大括号
接口名 变量名 = () -> 语句;
?
接口名 变量名 = 变量名 -> 语句;
?
接口名 变量名 = (变量名,变量名) -> 语句;
?
// 4、有返回值只有return,省略大括号
接口名 变量名 = () -> 返回值;
?
接口名 变量名 = 变量名 -> 返回值;
?
接口名 变量名 = (变量名,变量名) -> 返回值;
方法引用:lambda体也可以快速指向一个已经实现的方法,不需要自己再次重写SAM。
指向的方法需满足2个要求:
-
形参的类型、数量和顺序要与接口中定义的一致;
-
返回值类型要与接口中定义的一致。
// lambda表达式引用方法
// 方法归属者:静态方法的归属者为类名,普通方法归属者为类名或对象名
接口名 变量名 = 方法归属者::引用方法名;
变量名.接口方法(【实参列表】);
构造器引用:符合SAM的接口是某个对象的生成器,方法的实现就是创建对象。
指向的对象需满足2个要求:
-
对象构造器的形参类型、数量和顺序要与接口中定义的一致;
-
对象的类型与接口定义的返回值类型一致。
// 构造器引用
接口名 变量名 = 类名::new; // (【形参列表】) -> new 类名(【形参列表】)
类名 对象名 = 变量名.接口方法(【实参列表】);
常用SAM接口
JDK8中新增的java.util.function包,增加了大量的SAM接口。
接口 | SAM |
---|---|
Runnable | void run() |
Comparator | int compare(T t1,T t2) |
Comparable | int comparaTo(T t) |
Iterable | Iterator iterator() |
FileFilter | boolean accept(File pathname) |
InvovationHandler | Object invoke(Object proxy,Method method,Object[] args) |
Consumer<T> | void accept(T t) |
Supplier<T> | T get() |
Predicate<T> | boolean test(T t) |
Function<T,R> | R apply(T t) |
Java8中给Iterable<T>这个接口增加了一个默认方法:
default void forEach(Consumer<? super T> action)可使用Lambda表达式对遍历的元素进行操作。(见流程.循环.for)
闭包问题:这个问题我们在匿名内部类中也会存在,在lambda方法体中引用的外部变量,编译期间虚拟机会帮我们加上 final 修饰关键字。
三、执行流程(12)
顺序
语句顺序
分支
1、if
//(1)单分支;
if(条件表达式){
语句;
}
?
//(2)双分支;
if(条件表达式){
语句;
}else{
语句;
}
?
//(3)多分支;
if(条件表达式1){
语句;
}else if(条件表达式2){
语句;
}else{
语句;
}
2、switch
// 找不到相同常量值时,从default进入
switch(表达式){
case 常量值1:
语句;
break;
case 常量值2:
语句;
break;
...
default:
语句;
}
3、三元运算符
条件表达式 ? true结果:false结果;
循环
4、for
for(变量初始值;循环条件;循环后迭代){
语句;
}
?
// forEach遍历,实现可迭代(Iterable)接口
for(元素类型 元素临时名:数组和集合名){
语句;
}
?
// forEach的lambda表达式
list.forEach(元素临时名 -> {
语句;
});
5、while
while(循环条件){
语句;
}
do{
语句;
}while(循环条件);
异常
6、try
try(运行完自动关闭的资源){
语句;
}catch(异常类型 变量名){
语句;
}catch(异常类型 e){
e.printStackTrace();
}finally{
语句;
}
7、throws、throw
// throws抛出异常给调用者
【修饰符】 返回值类型 方法名(【形参列表】) throws 异常列表{
语句;
}
?
// throw主动抛异常
throw new 异常类型("带回信息");
// 自定义异常类
【修饰符】 class 异常名 extends 异常类型{
public 异常名() {
super();
}
public 异常名(String message) {
super(message);
}
}
四、类、接口
类
类的声明一般分为:1、加载 2、连接 3、初始化 4、使用 5、卸载,java程序需要某个类是,JVM会确保这个类已经1、加载 2、连接 3、初始化:
1、加载:通过类加载器将.class文件加载到方法区,封装数据结构,堆中创建指向方法区的Class对象。
2、连接:
验证阶段:验证.class文件的合法性(结构、语义、字节码、兼容性)。
准备阶段:为静态变量赋予默认值,静态常量直接赋予常量值。
解析阶段:将符号引用换为直接引用。
3、初始化:<clinit>()方法,,如果有父类且父类未初始化会先初始化父类。
1、一般类
// 声明
【修饰符】 class 类名 【extends 继承类】 【implements 实现接口】{
语句;
}
?
// 实例化对象(变量、常量)
类名 变量名 = new 类名(【参数】);
2、抽象类
抽象类中的抽象方法子类必须重写。
// 声明
【其他修饰符】 abstract class 类名{
语句;
// 抽象方法
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
}
接口
全局静态常量修饰符(public static final)可省略,公共抽象方法修饰符(public abstract)可省略。
// 声明
【权限修饰符】 interface 接口名 【extends 继承接口(可多继承)】{
// 1、全局静态常量
数据类型 常量名 = 值;
// 2、公共抽象方法
返回值类型 方法名(【形参列表】);
// JDK1.8后新增静态方法和默认方法
// 3、静态方法
【权限修饰符】 static 返回值类型 方法名(【形参列表】){
语句;
};
// 4、默认方法(避免重复写方法)
【权限修饰符】 default 返回值类型 方法名(【形参列表】){
语句;
};
}
枚举
JDK1.5之前在类中声明全局静态常量,JDK1.5以后使用枚举
// 声明
【修饰符】 enum 枚举名 【implements 实现接口】{
常量对象列表;
语句;
}
?
// 引用常量
枚举类型[] 变量名 = 枚举类型.values();
枚举类型 变量名 = 枚举类型.valueOf("常量名");
注解
// 元注解
// 1、可用位置