Java|语法结构

本文仅代表个人对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得到的字符串值还会在堆中创建)。

// 声明、实例化对象(变量)
【修饰符】 数据类型 变量名 = 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个要求:

  1. 形参的类型、数量和顺序要与接口中定义的一致;

  2. 返回值类型要与接口中定义的一致。

// lambda表达式引用方法
// 方法归属者:静态方法的归属者为类名,普通方法归属者为类名或对象名
接口名 变量名 = 方法归属者::引用方法名;
变量名.接口方法(【实参列表】);

 

构造器引用:符合SAM的接口是某个对象的生成器,方法的实现就是创建对象。

指向的对象需满足2个要求:

  1. 对象构造器的形参类型、数量和顺序要与接口中定义的一致;

  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、可用位置
@Target({ElementType.METHOD,ElementType.TYPE})
// 2、滞留阶段
@Retention(RetentionPolicy.RUNTIME)
// 3、是否可被javadoc.exe读取
@Documented
// 4、是否可被子类继承
@Inherited
【修饰符】 @interface 注解名{
语句;
}

Java|语法结构

上一篇:一些简单的排序算法模板


下一篇:hdu 1180:诡异的楼梯