提示
Java 中有50个关键字,这些关键字支撑起 Java 的底层操作。@ermo
# Java 关键字
Java 中的关键字 JDK 内部使用,因此开发人员不可以使用关键字进行声明变量或者实例化对象。
强制使用时编译器会提示异常。
public class KeywordDemo {
public static void main(String[] args) {
// Compile error
int this = "Don't use java keyword to define variable.";
System.out.println(this);
}
}
Java 中有50个关键字,其中 const
和 goto
作为保留字,目前没有使用到。
简单看下 Java 中的关键字。
# strictfp
strictfp
是 strict float point 的缩写,即精确浮点数。浮点数计算在不同的平台上会有不同的结果。为解决这个问题,strictfp
在 JDK1.2 被提出,并遵循 IEEE 754 (opens new window) 标准去计算。
strictfp
可以作用到接口、类和方向上。
public class Person {
strictfp void sum() {}
}
public strictfp interface StrictfpInterface {
float sum();
}
public strictfp class Point {
}
strictfp
不能作用于抽象方法上,但是可以用于抽象类和抽象接口上。
# transient
transient
关键字只能用来修饰成员变量,被修饰的成员变量不参与序列化过程。
参考 Java 序列化与反序列化。
# final
final
关键词作为不可访问的修饰符,可以作用到类、方法和变量上。
# 变量
被 final
修饰的基础变量不可更改,可以将 final
作用到成员变量上,这样的变量必须在构建对象的时候初始化,并且一经初始化后不可更改。
初始化有三种方式:
- 构造器
- 代码块
- 静态代码块
public class FinalDemo {
private final int j;
// 方式1
public FinalDemo() {
this.j = 1;
}
// 方式2
private final int num;
{
num = 5;
}
private static final int i;
// 方式3
static {
i = 1;
}
}
实际开发过程中应该将不可变常量与不可变参数用 final
修饰,是一种很好的开发习惯。
public class FinalDemo {
private final int num;
public FinalDemo () {
this.num = 1;
}
public static void main(String[] args) {
FinalDemo finalDemo = new FinalDemo();
// Cannot assign a value to final variable 'num'
finalDemo.num = 3;
}
}
public class FinalDemo {
private final int age = 18;
public static final double PI = 3.14;
public static void sum(final int num) {
final int appleNum = 5;
}
}
被 final
修饰的引用对象不可变,这里指的是变量引用的内存地址不可变。对象的内部变量依然可以更改。
public class FinalDemo {
private int num = 5;
public static void main(String[] args) {
final FinalDemo finalDemo = new FinalDemo();
finalDemo.num = 6;
System.out.println(finalDemo.num); // 6
// 编译错误
finalDemo = new FinalDemo();
}
}
# 方法
被 final
修饰的方法不能被重写。下面代码编译会报错。
public class FinalDemo {
class Parent {
public final void sum() {
System.out.println("sum method.");
}
}
class Children extends Parent {
// Compile error
public void sum() {
System.out.println("children sum method.");
}
}
}
父类方法使用 private
声明,该方法默认加有 final
修饰符。
这时子类可以定义同名方法,编译器认为这是两个独立的方法。
public class FinalDemo {
static class Parent {
private void sum() {
System.out.println("sum method.");
}
}
static class Children extends Parent {
private void sum() {
System.out.println("children sum method.");
}
}
public static void main(String[] args) {
Parent parent = new Parent();
parent.sum(); // sum method.
Children children = new Children();
children.sum(); // children sum method.
}
}
# 类
被 final
修饰的类不可变,同时也不能被继承。
JDK 中基础类型的包装类 Integer
、Float
以及 String
都使用了 final
进行定义。
public final class Integer extends Number implements Comparable<Integer> {
// 代码省略
}
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 代码省略
}
# static
static
修饰符主要用于内存管理,使用 static
修饰的方法和变量不依赖类的实例,它们在内存中使用的是同一个内存地址。
static
修饰符可以在以下场景使用。
# 静态变量(Static Variables)
静态变量和成员变量的区别是,成员变量依赖于实例,每一个类都有一个成员变量副本。
而静态变量在内存地址中只有一份,所有类共享这一份内存地址。
使用成员变量前需要实例化当前类,静态变量可以直接通过类名调用。
静态变量不常使用,开发中经常使用的是 final
修饰的静态常量。
public class StaticVariables {
private static final double PI = 3.14;
private static j = 2;
private int num = 1;
public static void main(String[] args) {
StaticVariables staticVariables = new StaticVariables();
System.out.println(staticVariables.num); // 1
System.out.println(StaticVariables.PI); // 3.14
System.out.println(StaticVariables.j); // 2
}
}
# 静态代码块(Static Block)
初始化静态变量的一种常见方法就是在变量声明的时候直接赋值。
另一个方法就是使用静态代码块进行静态变量的初始化。静态代码块在类加载时就会执行。
关于静态代码块有一个高频考点,那就是类的执行顺序:
- 父类静态变量
- 父类静态代码块
- 子类静态变量
- 子类静态代码块
- 父类实例变量
- 父类普通代码块
- 子类实例变量
- 子类普通代码块
- 子类构造器
# 静态方法(Static Methods)
静态方法不是在对象上执行的方法,使用静态方法有以下三种限制:
- 静态方法只能访问所属类的静态字段和静态方法,不能访问成员变量
- 静态方法不能使用
abstract
抽象关键字,该方法必须要有实现 - 静态方法不能使用
this
和super
关键字
我们最常用的主方法就是静态方法。
public static void main(String[] args) {
}
静态方法另一种常见的用途就是工厂方法。比如日期类 LocalDate.now()
。
public static LocalDate now() {
return now(Clock.systemDefaultZone());
}
Java8 中的 Objects
判空方法也是静态方法。
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
# 静态内部类(Nested Class)
在 Java 中,每个 .java
文件称为一个类,在这个类内部再定义一个类称之为内部类,相对而言,包含内部类的类称之为外部类。
内外部类只是针对开发人员来说的,编译器会将内部类也编译为一个独立的字节码文件。
静态内部类和普通的类差别不大,它可以有自己独立的静态变量、静态方法、成员变量、成员方法和构造器等。
静态内部类可以直接访问外部类的私有静态变量和方法,不能访问外部类的成员变量和成员方法。
为什么静态内部类可以访问外部类的静态私有变量和静态私有方法?
因为在字节码编译的过程中,编译器自动为外部类新增一个非私有的静态方法,方法内返回静态私有变量,然后内部类通过调用该静态方法,从而访问到了外部的静态私有变量。
除此之外,静态内部类的创建不依赖于外部类的实例,而非静态内部类需要依赖外部类的实例。
public class Outer {
private static int count = 1;
public static class StaticInner {
public void share() {
System.out.println("share=" + count);
}
}
public class Inner {
}
public static void main(String[] args) {
StaticInner staticInner = new StaticInner();
Outer outer = new Outer();
Inner inner = outer.new Inner();
}
}
# 参考资料
- Java 编程的逻辑,马昌俊
- Java 核心技术卷一,[美] 凯·S·霍斯特曼