java 常用关键字
1、static
static,静态,表示随着类的加载而加载,不会重复加载,执行顺序在 main 方法之前。在 JVM 内存里,static 修饰的变量存在于方法区中。
2、final
final 用于声明属性、方法和类,分别表示属性不可变,方法不可覆盖,类不可被继承(不能再派生出新的子类)。
final 属性
被 final 修饰的变量不可变,由于不可变有两重含义,一是引用不可变,二是对象不可变。那么 final 修饰的变量不可变到底指的是哪种含义?下面通过一个例子进行说明:
例1
运行结果
Hello world
例2
编译报错
从以上例子可以看出,final 修饰的属性指的是引用的不可变性,即它的引用只能指向初始时指向的那个对象,而不关心指向对象内容的变化。所以,final 修饰的变量必须被初始化。
final 方法
当一个方法声明为 final 时,该方法不允许任何子类重写这个方法,但子类仍可以使用这个方法。
另外还有一种被称为 inline (内联)的机制,当调用一个被声明为 final 的方法时,直接将方法主体插入到调用处,而不是进行方法调用,这样做能提高程序的效率。
final 参数
用来表示这个参数在这个方法内部不允许被修改。
final 类
当一个类被声明为 final 时,此类不能被继承,所有方法不能被重写。但并不表示 final 类的成员变量是不可变的,要想做到 final 类的成员变量不可变,必须给成员变量增加 final 修饰。值得注意的是,一个类不能既被声明为 abstract,又被声明为 final。
引申:为什么匿名内部类只能使用成员变量或者被 final 修饰的局部变量?
这是因为匿名类的生存周期可能比一般的局部变量更久。例如一个 Runnable 的实现体,有可能在数秒之后才被调用,而它的外部方法体已经随着代码的执行完毕而消亡了,之前定义在外部方法体内变量也随着方法区内存的回收也一起消亡了。
3、transient
Java 的 serialization 提供了一种持久化对象实例的机制,当持久化一个对象时,可能并不想持久化所有的属性,对于这种情况,可以通过在属性前加上关键字 transient 来实现。
什么是序列化?
在分布式环境下,当进行远程通信时,无论是任何类型的数据,都会以二进制序列的形式在网络上传送。序列化是一种将对象转换成字节序列的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要时候把该流读取出来重新构造成一个相同的对象。
所有要实现序列化的类都必须实现 Serializable 接口,只要这个类实现了 Serializable 接口,这个类所有的属性和方法都会自动序列化。然而在实际开发过程中,会有一些敏感信息(如密码,银行卡号等)为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也使用)中被传输,这些信息对应的变量就可以加上 transient 关键字。换句话说,被 transient 修饰的变量其生命周期仅存在于调用者的内存中而不被写到磁盘里持久化。
transient 使用小结
1)一但变量被 transient 修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient 关键字只能修饰变量,不能修饰方法和类。注意,本地变量是不能被 transient 关键字修饰的。变量如果是用户自定义类变量,则该类需要实现 Serializable 接口。
3)被 transient 关键字修饰的变量不能再被序列化,一个静态变量不管是否被 transient 修饰,均不能被序列化。
反序列化后类中的 static 修饰的变量值可以被访问,其实该值为 JVM 中对应 static 变量的值,并不是反序列化得出的。
扩展:transient关键字与@Transient注解的区别
实体类中使用了@Table注解后,想要添加表中不存在的字段,就要使用@Transient注解。
使用@Transient表示该属性并非是一个映射到数据库表中的字段,只是起辅助作用,ORM框架将会忽略该属性。
@Transient注解可以加在属性上,也可以加在get()方法上。
4、volatile(保证可见性、有序性,不保证操作的原子性)
该关键字用于修饰会被多线程访问的属性,以保持修改对所有线程可见。
相比于 synchronized,它仅限于修饰变量,且它只保持线程安全三要素中的可见性和有序性,并不能保证操作的原子性。所以它不严格地保证线程安全。
volatile 实现原理:
volatile 实现修改对所有线程可见是通过 store 和 load 指令完成的。
也就是对 volatile 修饰的变量进行操作时,会在操作后加入一条 store 指令,即强迫线程将最新的值刷新到主内存中;
而在读操作时,会加入一条 load 指令,即强迫从主内存中读入变量的值。
5、synchronized(保证可见性和原子性)
在 Java 中,synchronized 关键字是用来控制线程同步的,就是在多线程的环境下,控制 synchronized 代码段不被多个线程同时执行。
synchronized 实现原理:
synchronized 能够实现原子性和可见性;在 Java 内存模型中,synchronized 规定,在线程加锁时,先清工作内存 → 在主内存中拷贝最新变量的副本到工作内存 → 执行完代码 → 将改后的共享变量的值刷新到主内存中 → 释放互斥锁。
使用方式:
(1)修饰一个代码块,被修饰的代码块称为同步代码块,作用范围是 { } 括起来的代码。
注:this 表示锁是当前对象,锁可以自定义,但是要实现同步必须是同一把锁才可以。
public void method() {
synchronized(this) {
// todo some thing
}
}
(2)修饰一个普通方法,被修饰的方法称为同步方法,其作用范围是整个方法。
public synchronized void method() {
// todo some thing
}
(3)修饰一个静态方法,作用范围是整个静态方法。
public static synchronized void method() {
// todo some thing
}
(4)修饰一个类,作用范围是 synchronized 后面括号括起来的部分。
class DemoClass {
public void method() {
synchronized(DemoClass.class) {
// todo some thing
}
}
}