Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。
Java语言共定义了如下所示的关键字:
用于数据类型:boolean、byte、char、 double、 false、float、int、long、new、short、true、void、instanceof
用于语句:break、case、 catch、 continue、 default 、do、 else、 for、 if、return、switch、try、 while、 finally、 throw、this、 super
用于修饰:abstract、final、native、private、 protected、public、static、synchronized、transient、 volatile
用于方法、类、接口、包和异常:class、 extends、 implements、interface、 package、import、throws
Java保留的没有意义的关键字:cat、 future、 generic、innerr、 operator、 outer、rest、var
3个保留字(不是关键字,但和关键字一样,作为标识符):true、false、null
这里只针对性的讲解几个比较常用、值得深究的关键字,其他的关键字,有兴趣的可以去百度百科或其它地方自行查看。
transient
初识transient
transient关键字主要作用是可以用一句话来总结:让对象的某个被 transient 修饰的属性不被系列化。
示例:
定义一个学生类
@Data
@Accessors(chain = true)
public class Student implements Serializable {
private static final long serialVersionUID = 123456L;
private String name;
private transient int age;// 使用transient修饰
}
测试demo:
public class transientTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialDemo();
deSerialDemo();
}
// 序列化
private static void serialDemo() throws IOException {
Student student = new Student();
student.setName("李四").setAge(22);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template"));
objectOutputStream.writeObject(student);// 使用流模拟网络上的数据交替
objectOutputStream.close();
System.out.println("添加了 transient 关键字的序列化:age = " + student.getAge());
}
// 反序列化
private static void deSerialDemo() throws IOException, ClassNotFoundException {
File file = new File("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Student student = (Student) objectInputStream.readObject();
System.out.println("添加了 transient 关键字的反序列化:age = " + student.getAge());
}
}
运行结果:
添加了 transient 关键字的序列化:age = 22
添加了 transient 关键字的反序列化:age = 0
通过运行结果,可以得出 age 字段没有被序列化,所以在反序列化的时候,由于没有保存 age 字段的值,才会使用默认值,int 的默认值为 0
深入理解 transient
transient 实现原理
所谓的序列化就是把对象的状态存入到硬盘中(持久化),等需要的时候从硬盘中读取;而 transient 的作用就是让对象的某个属性的生命周期仅存于调用者的内存中而不会被写入到磁盘中。
被 transient 修饰的变量就一定不能变序列化吗?
Java中有两种序列化接口,一种就是常见的 Serializable,另一种是 Externalizable,看名字我们可以猜测出Externalizable是一个扩展的序列化接口,看过源码的人一定知道,Externalizable 是 Serializable 子类,其接口中有 writeExternal(ObjectOutput out) 和 readExternal(ObjectInput in) 两个方法,返回值都是 void ,这两个方法是用来指定对象中的那个属性会被序列化;哪怕改属性是被 transient 修饰,也会被系列化。下面我们来看一个示例:
实现接口为Externalizable的学生类
@Data
@Accessors(chain = true)
public class Student2 implements Externalizable {
private static final long serialVersionUID = 123L;
private String name;
private transient int age;
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(age); // 指定 age 字段会被序列化
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = (int) in.readObject(); // 读取被序列化的字段
}
}
测试类
public class transientTest2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialDemo();
deSerialDemo();
}
// 序列化
private static void serialDemo() throws IOException {
Student2 student = new Student2();
student.setName("李四").setAge(22);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template2"));
objectOutputStream.writeObject(student);
objectOutputStream.close();
System.out.println("添加了 transient 关键字的序列化:" + student.toString());
}
// 反序列化
private static void deSerialDemo() throws IOException, ClassNotFoundException {
File file = new File("D://my//ideaWorkSpace//reStudy//javaBaseStudy//javaBase//src//main//java//com//davidkity//javaBasicStudy//keyWords/template2");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Student2 student = (Student2) objectInputStream.readObject();
System.out.println("添加了 transient 关键字的反序列化:" + student.toString());
}
}
输出结果:
添加了 transient 关键字的序列化:Student2(name=李四, age=22)
添加了 transient 关键字的反序列化:Student2(name=null, age=22)
通过上面的示例可知,Student2类中的age属性被transient修饰,而name属性为普通属性,实现序列化的接口为Externalizable,age被指定为要序列化的属性,最终的输出结果表明即使是被transient修饰的属性,也不一定不能被序列化,还要看实现序列化的接口是什么。
instanceof
instanceof是 Java 的一个严格二元操作符,类似于 == ,>,< 等操作符。它的作用就是用于测试左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时决定
基本语法:
boolean result = obj instanceof Class
下面我们来看看instanceof的集中情况
instanceof 使用注意事项
1、obj 必须是引用类型,不能为基本类型
示例:
int i = 1;
System.out.println(i instanceof Integer);// 编译的时候就会报错
2、obj 为null时,返回的结果就是false
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsNull(); // 输出:object 为 null 时,是 Object 类型吗? 不是
}
private static void objIsNull(){
System.out.println("object 为 null 时,是 Object 类型吗? " + ((null instanceof Object) ? "是" : "不是"));
}
}
3、obj 为 class 类实例对象
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsClass(); // 输出:obj 为 类的实例化对象是,是String类型吗? 是
}
private static void objIsClass(){
String str = "ss";
System.out.println("obj 为 类的实例化对象是,是String类型吗? " + ((str instanceof String) ? "是" : "不是"));
}
}
obj 为 class 的接口实现类
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsInterface(); // 输出:obj为接口的实现类,是List类型吗? 是
}
private static void objIsInterface(){
ArrayList arrayList = new ArrayList<>();
System.out.println("obj为接口的实现类,是List类型吗? " + ((arrayList instanceof List) ? "是" : "不是"));
}
}
obj 为 class 类的直接或间接子类
示例:
public class instanceofTest {
public static void main(String[] args) {
objIsParentOrSon();
}
private static void objIsParentOrSon(){
Animal animal = new Animal();
Animal cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println(animal instanceof Cat); // false
System.out.println(cat1 instanceof Cat); // true
System.out.println(cat2 instanceof Cat); // true
System.out.println(animal instanceof Animal); // false
System.out.println(cat1 instanceof Animal); // true
System.out.println(cat2 instanceof Animal); // true
}
}
从输出的结果可以看出,父类的类型不等于子类的类型,子类的可以等于父类的类型(instanceof),简单的说,只能向上,不能向下。
final
final关键字可以修饰类、变量、方法,一但被final修饰,那么被修饰的引用将不能改变,若视图让其改变,编辑器会报编译错误。
修饰变量
在开发中,修饰变量的时候,final常和static关键字一起使用,用于声明一个常量。final修饰基本数据类型的时候,必须赋初始值且不能改变;修饰引用类型时,引用变量不能再指向其他的对象。
示例:
public class FinalTest {
// public final String s; // 没有初始化,编译报错
public final String str = "sss";
public static void main(String[] args) {
FinalTest finalTest = new FinalTest();
// finalTest.str = "abc"; // 修改被 final 修饰的变量,编译报错
}
}
修饰方法
final修饰方法的时候,代表这个不可以被子类重写。当我们认为这个方法已经很完善,不需要子类去重写,可以使用 final 修饰。这样的好处就是运行速度会比较快,原因在于被final修饰的方法在类的初始化就已经静态绑定了,不需要在运行时再动态绑定,所以运行的速度会比较快。
修饰类
和修饰方法一样,被final修饰的类是不能被子类继承的。只有当我们认为这个类已经很完善了,才会去用final修饰,就像String类一样。
static
static关键字,在之前的文章中也讲到了一些,这里稍微详细的讲解一下 static 关键字。static 关键字的基本概念可以用一句话来概括:方便在没有创建对象的情况下来进行调用。static 能修饰的对象有类、变量、代码块、方法,下面就分别对其进行一定的解析。
修饰内部类
static不能修饰普通的类,只能修饰内部类。
public class StaticTest {
public static class StaticInnerClass{
public StaticInnerClass(){
System.out.println("静态内部类的无参构造方法");
}
public void staticInnerMethod(){
System.out.println("静态内部类的方法");
}
}
public static void main(String[] args) {
StaticInnerClass staticInnerClass = new StaticInnerClass();
staticInnerClass.staticInnerMethod();
}
}
修饰方法
示例:
public class StaticMethodTest {
public static void staticMethod(){
System.out.println("静态方法");
}
public static void main(String[] args) {
StaticMethodTest.staticMethod();
}
}
修饰变量
public class StaticVarTest {
private static String str = "hello";
public static void main(String[] args) {
System.out.println(StaticVarTest.str);
}
}