4.[Java 基础]介绍关键字


1. Java 关键字分类

Java 关键字分类如下表格所示:

修饰符

class

abstract

extends

final

implements

interface

native

new

static

strictfp

volatile

synchronized

transient

访问

控制

private

public

protected

 

 

 

 

程序

控制

break

continue

return

do

while

if

else

for

case

switch

default

instanceof

错误

处理

try

catch

throw

throws

finally

数据

类型

boolean

byte

char

double

float

int

long

short

null

true

false

enum

包相关

import

package

 

 

 

 

 

变量

引用

super

this

void

保留字

goto

const

其他

_(下划线)

assert

2. 关于修饰符功能的关键字

2.1 static

static 可以用于修饰类, 方法, 代码块和类。

  1. static修饰属性

无论一个类生成了多少个对象,所有这些对象共同使用唯一一份静态的成员变量;一个对象对该静态成员变量进行了修改,其他对象的该静态成员变量的值也会随之发生变化。如果一个成员变量是static的,那么我们可以通过 类名.成员变量名 的方式来使用它。

  1. static修饰方法

static修饰的方法叫做静态方法。对于静态方法来说,可以使用 类名.方法名 的方式来访问。静态方法只能继承,不能重写(Override),因为重写是用于表现多态的,重写只能适用于实例方法,而静态方法是可以不生成实例直接用类名来调用,这就会与重写的定义所冲突,与多态所冲突,所以静态方法不能重写,只能是隐藏。

  • static方法 与 非static方法
操作 可行性
静态方法中访问非静态成员变量
静态方法中访问静态的成员变量 ✔️
非静态方法中访问静态的成员变量
静态方法可以直接用类名来调用,
而非静态成员变量是在创建对象实例时才为变量分配内存和初始化变量值。
✔️
静态方法中使用this关键字
静态方法可以直接用类名来调用,而this实际上是创建实例时,
实例对应的一个应用,所以不能在静态方法上使用this。
  1. static 修饰代码块

静态代码块。静态代码块的作用也是完成一些初始化工作。首先执行静态代码块,然后执行构造方法。静态代码块在类被加载的时候执行,而构造方法是在生成对象的时候执行;要想调用某个类来生成对象,首先需要将类加载到Java虚拟机上(JVM),然后由JVM加载这个类来生成对象。

类的静态代码块只会执行一次,是在类被加载的时候执行的,因为每个类只会被加载一次,所以静态代码块也只会被执行一次;而构造方法则不然,每次生成一个对象的时候都会调用类的构造方法,所以new一次就会调用构造方法一次。如果继承体系中既有构造方法,又有静态代码块,那么首先执行最顶层的类的静态代码块,一直执行到最底层类的静态代码块,然后再去执行最顶层类的构造方法,一直执行到最底层类的构造方法。

  1. static修饰类:

被 static 修饰的类只能是 内部类, 且被static所修饰的内部类可以用new关键字来直接创建一个实例,不需要先创建外部类实例。static内部类可以被其他类实例化和引用(即使它是顶级类)。

2.2 final

在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。final方法在编译阶段绑定,称为静态绑定(static binding)。下面就从这四个方面来了解一下final关键字的基本用法。

  • final 修饰类

当用 final 修饰一个类时,表明这个类不能被继承,不能有子类。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

  • final 修饰方法

使用 final 修饰方法的原因有两个。第一个原因是把方法 锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了, 如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。

类的private方法会隐式地被指定为final方法。

  • 修饰变量

对于一个 final 修饰的变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

  • final参数

当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值或者引用指向。道理同final变量。

注意:

  1. abstract 意味着必须扩展类,final 意味着不能扩展类。所以一个方法不能即是 abstract 又是 final。abstract 意味着必须重写方法,final 意味着不能重写方法, 两者是相互矛盾的。

  2. 当final修饰类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。

  3. 当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会进行优化,会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。

  4. 局部内部类和匿名内部类只能访问局部final变量。因为这里的局部变量,需要在编译阶段便需要确定下来的。也就是说,如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。

2.3 class

class 关键字用来声明新的 Java 类。类是面向对象的程序设计语言的基本构造单位。类是对象的模板。每个对象都是类的一个实例。要使用类,通常使用 new 操作符将类的对象实例化,然后才能调用类的方法来访问类的功能。

2.4 interface

  • interface 关键字用来声明新的 Java 接口,接口是方法的集合。

  • 接口不能用于创建对象

  • 接口方法没有主体, 接口方法的主体由 “实现” 类提供

  • 在实现接口时,必须重写其所有方法

  • 接口中的方法在默认情况下修饰符为 abstract 和 public

  • 接口中的属性在默认情况下修饰符为 public, static 和 final

  • 接口不能包含构造函数(因为它不能用于创建对象)

为什么以及何时使用接口?

为了实现安全性-隐藏某些细节,仅显示对象(接口)的重要细节。Java不支持“多重继承”(一个类只能继承自一个父类)。但是,可以使用接口来实现,因为该类可以实现多个接口。 注意:要实现多个接口,请用逗号分隔。

2.5 new

new 关键字用于创建某个类的新实例。

new 关键字后面的参数必须是类名,并且类名的后面必须是一组构造方法参数(必须带括号)。 参数集合必须与类的构造方法的全名匹配。

= 赋值号左侧的变量的类型必须与要实例化的类或接口具有赋值兼容关系。

2.6 abstract

abstract 关键字可以修饰类或方法。

  • abstract 修饰类:

称为抽象类, 抽象类可以扩展(增加子类),但不能直接实例化。

仅当 abstract 类的子类实现其超类的所有 abstract 方法时,才能实例化 abstract 类的子类。这种类称为具体类,以区别于 abstract 类。

如果 abstract 类的子类没有实现其超类的所有 abstract 方法,该子类也是 abstract 类。

  • abstract 修饰方法:

称为抽象方法, 抽象方法必须在抽象类中, 并且抽象类不能有实现,但必须在某个子类中重写。

abstract 关键字不能应用于 static、private 或 final 方法,因为这些方法不能被重写,因此,不能在子类中实现。

// 抽象类
public abstract class AbsCLass {
  // 抽象方法, 不能在抽象类中实现,需要在子类中重写
  public abstract String getName();
}

2.7 extends

Java中使用 extends 关键字实现类的继承机制, 在 class 或 interface 的声明中使用 extends 关键字用于说明声明的class 或 interface 继承自某个类.

Java 只支持单继承, 一个子类只能有一个父类, 但是父类可以有多个子类.

通过继承子类自动有用了父类(基类)的所有 public 和 protected 的 成员变量与成员方法, 但不包括构造方法.

子类可以重写父类的任何非 final 方法

class Plant{
    public void m1(){
        System.out.println("m1");
    }
    private void m2(){
        System.out.println("m1");
    }
    public final void m3(){
        System.out.println("m1");
    }
}

class Apple extends Plant{
    @Override
    public void m1(){
        System.out.println("m1");
    }

    // 父类中的 m2 方法是 private 级别, 无法重写, 所以这里的 m2 与父类的 m2 方法没有任何关系
    public void m2(){
        System.out.println("m2");
    }

    // 父类中 m3 方法是 Final 修饰的, 所以子类中不能重写
    public final void m3(){
        System.out.println("m3");
    }
//  java: Apple中的m3()无法覆盖Plant中的m3()
//  被覆盖的方法为final
}

2.8 implements

implements 关键字用于实现接口。

要访问接口方法,接口必须由另一个类使用 implements 关键字 “实现”。接口方法的主体由 “实现” 类提供。

一个类可以实现多个接口, 如果类实现了接口, 那么必须实现接口中的所有方法.

2.9 native

使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++ 语言实现的,并且被编译成了 DLL,由java去调用。

JDK的源代码中并不包含这些函数, 函数的实现体在DLL中, 你应该是看不到的。对于不同的平台它们也是不同的。

这也是java的底层机制,实际上java就是在不同的平台上调用不同的native方法实现对操作系统的访问的。

Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。

可以将native方法比作Java程序同C程序的接口,其实现步骤:

  1. 在Java中声明native()方法,然后编译;

  2. 用javah产生一个.h文件;

  3. 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);

  4. 将第三步的.cpp文件编译成动态链接库文件;

  5. 在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

Java native 适用于如下情况:

  1. 为了使用底层的主机平台的某个特性,而这个特性不能通过Java API访问

  2. 为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用Java编写的

  3. 为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。

2.10 strictfp

strictfp 的意思是 FP-strict,也就是说精确浮点的意思。在Java虚拟机进行浮点运算时,如果没有指定 strictfp 关键字时,Java的编译器以及运行环境在对浮点运算通常会出现精度损失,以致于得到的结果往往无法令人满意。

当使用了 strictfp 来声明一个类、接口或者方法时,那么所声明的范围内Java的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果想让浮点运算更加精确,而且不会因为不同的硬件平台所执行的结果不一致的话,那就请用关键字strictfp。

可以将一个类、接口以及方法声明为strictfp,但是不允许对接口中的方法以及构造函数声明strictfp关键字。

2.11 volatile

volatile 关键字用于表示可以被多个线程异步修改的成员变量。

volatile 修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到主内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

注意:volatile 关键字在许多 Java 虚拟机中都没有实现。 volatile 的目标用途是为了确保所有线程所看到的指定变量的值都是相同的。

Java语言规范中指出: 为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。

而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝, 而应直接与共享成员变量交互。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。

2.12 synchronized

synchronized 关键字可以应用于方法或语句块,被 synchronized 修饰的代码段无法同时被多个线程执行。

  • 应用与静态方法: 当该方法一次由一个线程执行时,整个类将被锁定。
  • 应用于实例方法: 当该方法一次由一个线程访问时,该实例将被锁定。
  • 应用于对象或数组: 当代码块一次由一个线程执行时,对象或数组将被锁定。
  1. synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁

每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。

在Java中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。

synchronized 方法的缺陷:若将一个大的方法声明为 synchronized 将会大大影响效率,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized块。

  1. synchronized块

当两个并发线程访问同一个对象 object 中的这个 synchronized(this) 同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

当一个线程访问 object 的一个 synchronized(this) 同步代码块时,另一个线程仍然可以访问该 object 中的非 synchronized(this) 同步代码块。

当一个线程访问 object 的一个 synchronized(this) 同步代码块时,其他线程对 object 中所有其它 synchronized(this) 同步代码块的访问将被阻塞。同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。其它线程对该object对象所有同步代码部分的访问都被暂时阻塞, object的对象锁只有一把,一把锁对应一个线程。

2.13 transient

transient 关键字可以应用于类的成员变量,以便指出该成员变量不应在包含它的类实例已序列化时被序列化。

Java 的 serialization 提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,如果不想用 serialization 机制来保存它。为了在一个特定对象的一个域上关闭 serialization,就可以在这个域前加上关键字transient。

当一个对象被序列化的时候,transient 型变量的值不包括在序列化的表示中,然而非 transient 型的变量是被包括进去的。

3. 关于访问控制的关键字

3.1 private 私有的

private 关键字是访问控制修饰符,可以应用于类、方法或变量。 只能在声明 private(内部)类、方法或变量的类中引用这些类、方法或字段。在类的外部或者对于子类而言,它们是不可见的。

3.2 protected 受保护的

protected 关键字是访问控制修饰符, 可以应用于类、方法或变量, 可以在声明 protected 类、方法或变量, 同一个包中的其他任何类以及任何子类中引用这些类、方法或变量。

3.3 public 公共的

public 关键字是访问控制修饰符, 可以应用于类、方法或变量的访问控制修饰符。 可能只会在其他任何类或包中引用 public 类、方法或变量。

访问权限 当前类 子类 其他包
public ✔️ ✔️ ✔️ ✔️
protect ✔️ ✔️ ✔️
default ✔️ ✔️
private ✔️

4. 关于程序控制的关键字

4.1 if

if 关键字指示有条件地执行代码块。条件的计算结果必须是布尔值。

if 语句可以有可选的 else 子句,该子句包含条件为 false 时将执行的代码。

包含 boolean 操作数的表达式只能包含 boolean 操作数。

4.2 else

else 关键字总是在 if-else 语句中与 if 关键字结合使用。else 子句是可选的,如果 if 条件为 false,则执行该子句。

4.3 do

do 关键字用于指定一个在每次迭代结束时检查其条件的循环。

do 循环体至少执行一次。 条件表达式后面必须有分号。

4.4 while

while 关键字用于指定一个只要条件为真就会重复的循环。

4.5 for

for 关键字用于指定一个在每次迭代结束前检查其条件的循环。

for 语句的形式为 for(initialize; condition; increment) 控件流进入 for 语句时,将执行一次 initialize 语句。 每次执行循环体之前将计算 condition 的结果。如果 condition 为 true,则执行循环体。

每次执行循环体之后,在计算下一个迭代的 condition 之前,将执行 increment 语句。

4.6 switch

switch 语句用于基于某个表达式选择执行多个代码块中的某一个。

switch 条件的计算结果必须等于 byte、char、short 或 int。

case 块没有隐式结束点。break 语句通常在每个 case 块末尾使用,用于退出 switch 语句。

如果没有 break 语句,执行流将进入所有后面的 case 和/或 default 块。

4.7 case

case 用来标记 switch 语句中的每个分支。

case 块没有隐式结束点。break 语句通常在每个 case 块末尾使用,用于退出 switch 语句。

如果没有 break 语句,执行流将进入所有后面的 case 和/或 default 块。

4.8 default

default 关键字用来标记 switch 语句中的默认分支。

default 块没有隐式结束点。break 语句通常在每个 case 或 default 块的末尾使用,以便在完成块时退出 switch 语句。

如果没有 default 语句,其参数与任何 case 块都不匹配的 switch 语句将不执行任何操作。

4.9 break

break 关键字用于提前退出 for、while 或 do 循环,或者在 switch 语句中用来结束 case 块。

break 总是退出最深层的 while、for、do 或 switch 语句。

4.10 continue

continue 关键字用来跳转到 for、while 或 do 循环的下一个迭代。

continue 总是跳到最深层 while、for 或 do 语句的下一个迭代。

4.11 return

return 关键字会导致方法返回到调用它的方法,从而传递与返回方法的返回类型匹配的值。 如果方法具有非 void 的返回类型,return 语句必须具有相同或兼容类型的参数。 返回值两侧的括号是可选的。

4.12 instanceof

instanceof 关键字用来确定对象所属的类。

5. 关于错误处理的关键字

5.1 try

try 关键字用于包含可能引发异常的语句块。

每个 try 块都必须至少有一个 catch 或 finally 子句。

如果某个特定异常类未被任何 catch 子句处理,该异常将沿着调用栈递归地传播到下一个封闭 try 块。如果任何封闭 try 块都未捕获到异常,Java 解释器将退出,并显示错误消息和堆栈跟踪信息。

5.2 catch

catch 关键字用来在 try-catch 或 try-catch-finally 语句中定义异常处理块。

开始和结束标记 { 和 } 是 catch 子句语法的一部分,即使该子句只包含一个语句,也不能省略这两个标记。 每个 try 块都必须至少有一个 catch 或 finally 子句。

如果某个特定异常类未被任何 catch 子句处理,该异常将沿着调用栈递归地传播到下一个封闭 try 块。如果任何封闭 try 块都未捕获到异常,Java 解释器将退出,并显示错误消息和堆栈跟踪信息。

5.3 throw

throw 关键字用于引发异常。

throw 语句将 java.lang.Throwable 作为参数。Throwable 在调用栈中向上传播,直到被适当的 catch 块捕获。 引发非 RuntimeException 异常的任何方法还必须在方法声明中使用 throws 修饰符来声明它引发的异常。

5.4 throws

throws 关键字可以应用于方法,以便指出方法引发了特定类型的异常。

throws 关键字将逗号分隔的 java.lang.Throwables 列表作为参数。

引发非 RuntimeException 异常的任何方法还必须在方法声明中使用 throws 修饰符来声明它引发的异常。 要在 try-catch 块中包含带 throws 子句的方法的调用,必须提供该方法的调用者。

5.5 finally

在异常处理机制当中,它的作用就像是人吃饭一样,必须得做的,不论有异常还是没有异常都要执行的代码就可以放到finally块当中去。finally块,必须要配合try块一起使用,不能单独使用,也不能直接和catch块一起使用。

finally 关键字用来定义始终在 try-catch-finally 语句中执行的块。

finally 块通常包含清理代码,用在部分执行 try 块后恢复正常运行。

6. 关于数据类型的关键字

boolean, byte, int, short, long, char, double, float,enum 都为数据类型, 会在后面基本类型章节介绍.

7. 包相关的关键字

7.1 import

import 关键字使一个包中的一个或所有类在当前 Java 源文件中可见。可以不使用完全限定的类名来引用导入的类。

当多个包包含同名的类时,许多 Java 程序员只使用特定的 import 语句来避免不确定性。

7.2 package

package 关键字指定在 Java 源文件中声明的类所驻留的 Java 包。

package 语句(如果出现)必须是 Java 源文件中的第一个非注释性文本。 例:java.lang.Object。

如果 Java 源文件不包含 package 语句,在该文件中定义的类将位于 “默认包” 中。请注意,不能从非默认包中的类引用默认包中的类。

8. 关于变量引用的关键字

8.1 super

super 关键字用于引用使用该关键字的类的超类。

作为独立语句出现的 super 表示调用超类的构造方法。 super.() 表示调用超类的方法。

只有在如下情况中才需要采用这种用法:要调用在该类中被重写的方法,以便指定应当调用在超类中的该方法。

8.2 this

this 关键字用于引用当前实例。 当引用可能不明确时,可以使用 this 关键字来引用当前的实例。

8.3 void

void 关键字表示 null 类型。 void 可以用作方法的返回类型,以指示该方法不返回值。

5.8 保留字

保留字是为java预留的关键字,他们虽然现在没有作为关键字,但在以后的升级版本中有可能作为关键字。 识别java语言的关键字,不要和其他语言如c/c++的关键字混淆。 const和goto是java的保留字。

9.1 goto

goto 保留关键字,但无任何作用。结构化程序设计完全不需要 goto 语句即可完成各种流程,而 goto 语句的使用往往会使程序的可读性降低,所以 Java 不允许 goto 跳转。

9.2 const

const 保留字,是一个类型修饰符,使用const声明的对象不能更新。与final某些类似。

其它关键字

10.1 _(下划线)

从Java 9开始,_ 不允许将其用作任何合法标识符名称,而不仅限于lambda参数名称。

10.2 assert

assert 意为断言的意思,这个关键字可以判断布尔值的结果是否和预期的一样,如果一样就正常执行,否则会抛出AssertionError。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
5.[Java 基础]介绍数据类型 5.[Java 基础]介绍数据类型
1. 数据类型分类Java 是一种强类型的语言, 这意味着每个变量在编译时就有确定的类型, 变量类型限制了这个变量能被赋予的值, 并限制了在这个值上能进行的操作 强类型语言在编译时语法检查更严格, 减少了错误的出现, 强类型包含两个方面的含
2018-01-12
下一篇 
3.[Java 基础]注释,标识符与JavaDoc工具的介绍 3.[Java 基础]注释,标识符与JavaDoc工具的介绍
1. 注释 注释在程序中用来说明代码的作用, 类的用途, 方法的功能, 以及该方法的参数与返回值的数据类型以及意义等功能. 在程序运行时, 被注释的代码不会被运行, 所以注释也常被用于程序的调试, 使用可以判断异常代码的位置或缩小异常代码
2018-01-07
  目录