1 Java中常见的关键字
访问控制 | private | protected | public | ||||
---|---|---|---|---|---|---|---|
类,方法和变量修饰符 | abstract | class | extends | final | implements | interface | native |
new | static | strictfp | synchronized | transient | volatile | ||
程序控制 | break | continue | return | do | while | if | else |
for | instanceof | switch | case | default | |||
错误处理 | try | catch | throw | throws | finally | ||
包相关 | import | package | |||||
基本类型 | boolean | byte | char | double | float | int | long |
short | null | true | false | ||||
变量引用 | super | this | void | ||||
保留字 | goto | const |
2 static关键字
static 关键字在 Java 中可修饰变量、方法、语句块和内部类(只能用于内部类,无法作为顶层类):
- 静态变量又称类变量,该变量属于类,类的所有实例共享一份,在内存中也仅存一份。静态变量存放在Java内存区域中的方法区。
- 静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。静态方法中不能使用 this 和 super。
- 静态代码块在类初始化时运行一次。
- 非静态内部类依赖于外部类的实例,而静态内部类不需要。此外,静态内部类不能访问外部类的非静态变量和方法。
- static 关键字与 final 一起用于定义常量。
静态类的使用场景:
- 当A类需要使用B类,并且B某类仅为A类服务,那么就没有必要单独写一个B类,因为B类在其他类中不会使用,所以只需将B类作为A的内部类。例如 ThreadLocal 与 ThreadLocalMap。
当某个类需要接受多个参数进行初始化时,推荐使用静态类构建,例如:
public class Car {
private String name;
private String model;
private int height;
private int width;
private Car(Builder build){
this.name=build.name;
this.model= build.model;
this.height=build.height;
this.width=build.width;
}
public static class Builder {
private String name;
private String model;
private int height;
private int width;
public Builder(){
}
public Builder withName(String name){
this.name=name;
return this;
}
public Builder withModel(String model){
this.model=model;
return this;
}
public Builder withHeight(int height){
this.height=height;
return this;
}
public Builder withWidth(int width){
this.width=width;
return this;
}
public Car build(){
return new Car(this);
}
}
}
3 父子类的初始化顺序
优先级如下:
父类(静态变量、静态代码块)
- 子类(静态变量、静态代码块)
- 父类(实例变量、普通代码块)
- 父类(构造函数)
- 子类(实例变量、普通代码块)
- 子类(构造函数)
4 内部类与static关键字的关系
| | 普通内部类 | 静态内部类 | | —- | —- | —- | | 修饰符 | 没有 static 修饰 | 使用 static 修饰 | | 初始化 | 创建这个内部类时必须先创造外部类,即内部类依赖于外部类 | 创建这个内部类时无需先创造外部类,即内部类不依赖于外部类 | | 内部类访问权限 | 内部类对象可以访问外部类中的所有访问权限字段 | 静态内部类无法访问外部类的非静态成员,因为外部类的非静态成员属于每一个外部类对象 | | 外部类访问权限 | 外部类对象也可以通过内部类的对象引用访问内部类中定义的所有访问权限的字段。 | 外部类可以访问静态内部类对象的所有访问权限的成员。 |
package org.example.InnerClass;
public class InnerClassDemo {
public int out1 = 1;
protected int out2 = 2;
int out3 = 3;
private int out4 = 4;
static int out5 = 5;
public InnerClassDemo() {
System.out.println("创建外部类");
}
public void show1() {
// 在外部类的方法里面,可以直接创建内部类
InnerClassA innerClassA = new InnerClassA();
// 外部类可以访问普通内部类中所有的属性
System.out.println("in1 = " + innerClassA.in1);
System.out.println("in2 = " + innerClassA.in2);
System.out.println("in3 = " + innerClassA.in3);
System.out.println("in4 = " + innerClassA.in4);
}
public void show2() {
// 在外部类的方法里面,可以直接创建内部类
InnerClassB innerClassB = new InnerClassB();
// 外部类可以访问静态内部类中所有的属性
System.out.println("in1 = " + innerClassB.in1);
System.out.println("in2 = " + innerClassB.in2);
System.out.println("in3 = " + innerClassB.in3);
System.out.println("in4 = " + innerClassB.in4);
// 静态属性只能通过类名.属性名进行访问
System.out.println("in5 = " + InnerClassB.in5);
}
// 普通内部类
public class InnerClassA {
public int in1 = 6;
protected int in2 = 7;
int in3 = 8;
private int in4 = 9;
// 普通内部类不能有静态成员变量
// static int in5 = 10;
public InnerClassA() {
System.out.println("创建普通内部类");
// 可以访问外部类的所有成员变量
System.out.println("out1 = " + out1);
System.out.println("out2 = " + out2);
System.out.println("out3 = " + out3);
System.out.println("out4 = " + out4);
System.out.println("out5 = " + out5);
}
public void show() {
System.out.println("这是一个普通内部类");
}
}
public static class InnerClassB {
public int in1 = 11;
protected int in2 = 12;
int in3 = 13;
private int in4 = 14;
static int in5 = 15;
public InnerClassB() {
System.out.println("创建静态内部类");
// 只能访问外部类的静态成员变量
System.out.println("out5 = " + out5);
}
public void show() {
System.out.println("这是一个静态内部类");
}
}
}
package org.example.InnerClass;
public class InnerClassMain {
public static void main(String[] args) {
// 注意普通内部类和静态内部类的不同创建方式
// 普通内部类需要先创建外部类,再通过外部类创建内部类
InnerClassDemo demo = new InnerClassDemo();
InnerClassDemo.InnerClassA innerClassA = demo.new InnerClassA();
innerClassA.show();
System.out.println("-------1111111----------");
// 上面两步可以合并成一行
InnerClassDemo.InnerClassA innerClassA2 = new InnerClassDemo().new InnerClassA();
innerClassA2.show();
System.out.println("-------2222222----------");
// 静态内部类可以直接创建
InnerClassDemo.InnerClassB innerClassB = new InnerClassDemo.InnerClassB();
innerClassB.show();
System.out.println("-------3333333----------");
// 外部类可以访问普通内部类中所有的属性
demo.show1();
System.out.println("-------4444444----------");
// 外部类可以访问静态内部类中所有的属性
demo.show2();
}
}
关于内部类的详细介绍,可以参考这篇文章:内部类详细介绍。
5 final关键字
final 用于声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
- 作用于基本类型变量,final 使数值不变。
- 作用于引用类型变量,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的,例如数组。
- 作用于方法,final 使得该方法不能被重写。被 final 修饰的方法运行速度快于非 final 方法,因为在编译时已经被静态绑定,而无需运行时动态绑定。private 方法则被隐式地指定为 final。
- 作用于类,final 使得该类无法被继承,例如 String,Integer。
- final 与 abstract具有反相关关系。
使用final关键字的好处:
- final 关键字提高了性能,JVM 和 Java 应用都会缓存 final 变量。
- final 变量可以安全地在多线程环境下运行,无需同步开销。
-
6 static与final修饰变量所处JVM中的位置
如果这里理解有困难,可以回过头看一下 JVM 中的内存区域。 ```java class Fruit { static int x = 10; // static int x 在方法区 static BigWaterMelon bigWaterMelon_1 = new BigWaterMelon(x); // static BigWaterMelon bigWaterMelon_1在方法区,而new BigWaterMelon(x)在堆上
int y = 20; // int y=20 在堆上 BigWaterMelon bigWaterMelon_2 = new BigWaterMelon(y); // BigWaterMelon bigWaterMelon_2 与 new BigWaterMelon(y) 都在堆上
public static void main(String[] args) { // String[] args 在main栈帧上
final Fruit fruit = new Fruit(); // Fruit fruit 在main栈帧上,而 new Fruit() 在堆上
int z = 30; // int z = 30 在main栈帧上
BigWaterMelon bigWaterMelon_3 = new BigWaterMelon(z); // BigWaterMelon bigWaterMelon_3 在main栈帧上,而new BigWaterMelon(z)在堆上
new Thread() {
@Override
public void run() {
int k = 100; // int k=100 在run栈帧上
setWeight(k);
}
void setWeight(int waterMelonWeight) { // int waterMelonWeight 在setWeight栈帧上
fruit.bigWaterMelon_2.weight = waterMelonWeight;
}
}.start();
} }
class BigWaterMelon { public BigWaterMelon(int weight) { this.weight = weight; }
public int weight;
}
![](https://cdn.nlark.com/yuque/0/2021/png/21447701/1620612438397-c266654e-2717-40f4-a890-d725d4ef8a6f.png#height=844&id=CNvJp&originHeight=1125&originWidth=1947&originalType=binary&ratio=1&size=0&status=done&style=none&width=1460)
<a name="native"></a>
## 7 native
native 用来修饰方法表示告知 JVM 调用,该方法在外部定义,我们可以用任何语言去实现它。简单地讲,一个 native Method 就是一个 Java 调用非 Java 代码的接口。
<a name="instanceof"></a>
## 8 instanceof
instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例。<br />用法为:boolean result = obj instanceof Class;其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果 result 都返回 true ,否则返回 false 。<br />编译器会检查 obj 是否能转换成右边的 class 类型,如果不能转换则直接报错;如果不能确定类型,则通过编译,具体看运行时定。
```java
System.out.println(null instanceof Object); // 输出false
ArrayList arrayList = new ArrayList();
System.out.println(arrayList instanceof List); // 输出true
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
关于instanceof的详细介绍,可以参考这篇文章:instanceof详解。
9 super
可以使用 super() 函数访问父类的构造函数,从而委托父类进行一些初始化工作;可以使用 super.变量名 或 super.函数名 访问父类的变量或调用函数。super 与 this 的区别如下:
- super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句);this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
- super 引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 或 super.成员函数名();this 代表当前对象名(在程序中易产生二义性之处,应使用 this 来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用 this 来指明成员变量名)
- 调用 super() 必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用 super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
- super() 和 this()类似,区别在于 super() 从子类中调用父类的构造方法,this() 在同一类内调用其它方法。
- super() 和 this()均需放在构造方法内第一行。
- 尽管可以用 this 调用一个构造器,但却不能调用两个。
- this 和 super 不能同时出现在一个构造函数里面,因为 this 必然会调用其它的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this() 和 super() 都指的是对象,所以,均不可以在 static 环境中使用。包括:static 变量,static 方法,static 语句块。
从本质上讲,this 是一个指向本对象的指针, super 是一个Java关键字。
10 包访问权限控制符
四种访问控制符具有不同级别的访问权限:
private:只能在类的内部使用,不能被任何一个外部的类所访问。
- default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访问。
- protected:即使子类在不同的包中也可以访问。
- public:可以被外部包所访问。
- 如果一个成员需要被本包下的其他类所访问,则需要使用default及以上级别的修饰符;若一个成员想使用同类中其他成员,则使用任意一个修饰符即可。
值得注意的是 Java 的访问控制是停留在编译层的,也就是它不会在 .class 文件中留下任何的痕迹,只在编译的时候进行访问控制的检查。其实,通过反射的手段,是可以访问任何包下任何类中的成员,例如,访问类的私有成员也是可能的。所以访问控制符有时是无效的。
权限 | 类内 | 同包 | 不同包子类 | 不同包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
关于包的访问权限的测试,可以参考这篇文章:包访问测试实例。
参考
- https://snailclimb.gitee.io/javaguide/#/docs/java/basis/Java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86?id=%e5%9f%ba%e6%9c%ac%e8%af%ad%e6%b3%95
- https://www.pdai.tech/md/java/basic/java-basic-lan-basic.html#%E5%85%B3%E9%94%AE%E5%AD%97
- https://blog.nowcoder.net/n/8f0280724e074093a7e7b5951098c2bc
- https://blog.csdn.net/sdjadycsdn/article/details/81560096
- https://www.cnblogs.com/ysocean/p/8486500.html
- https://blog.csdn.net/hacker_zhidian/article/details/82193100#_7