1、native
native关键字可以应用于方法,以指示该方法是用 Java 以外的语言实现的
2、transient
transient关键字可以应用于类的成员变量,以便指出该成员变量不应在包含它的类实例已序列化时被序列化;Java 的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它,为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。transient 是 Java 语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient 型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的.
3、final
3.1、final 修饰符
修饰变量:对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象;
修饰方法:方法前面加上 final 关键字,代表这个方法不可以被子类的方法重写;final 方法比非 final 方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定
类的 private 方法会隐式地被指定为final方法
修饰类:当用 final 修饰一个类时,表明这个类不能被继承,final 类中的所有成员方法都会被隐式地指定为 final 方法;Java 中许多类都是 final 类,如:String,Integer
不能修饰构造方法;
不能修饰static方法;
如果final修饰对象的时候,只是对象的引用不可变,而对象本身的内部属性是可以变化的;
3.2、注意点
- final 和 static:static 作用于成员变量用来表示只保存一份副本,而 final 的作用是用来保证变量不可变看代码:每次打印的两个j值都是一样的,而i的值却是不同的
public class Demo01 {
public static void main(String[] args) {
MyDemo1 d1 = new MyDemo1();
MyDemo1 d2 = new MyDemo1();
System.out.println(d1.i);
System.out.println(d2.i);
System.out.println(d1.j);
System.out.println(d2.j);
}
}
class MyDemo1{
public final double i = Math.random();
public static double j = Math.random();
}
匿名内部类中使用的外部局部变量为什么只能是 final 变量
- 成员变量如果使用final修饰,可以使用如下三种方式赋值:
定义变量直接final变量后直接赋值:private final int a = 1;
在构造方法中对定义的成员变量进行赋值操作;
在构造代码块中赋值; - 成员变量如果使用static final修饰,可以使用如下两种种方式赋值:
定义变量直接final变量后直接赋值:private static final int a = 1;
在静态代码块中对成员变量进行赋值操作; - 如果是在方法体内定义的final变量,需要在使用该变量前对其进行赋值;
3.4、为什么使用 final
- final 关键字提高了性能。JVM 和 Java 应用都会缓存 final 变量。
- final 变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
- 使用 final 关键字,JVM 会对方法、变量及类进行优化;
3.5、不可变类创建不可变类要使用 final 关键字。
不可变类是指它的对象一旦被创建了就不能被更改了。String 是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等;
- 不可变对象:如果某个对象在被创建后其状态不能被修改,那么这个对象就称为不可变对象,不可变对象一定是线程安全的。
- 如何编写不可变类:
将类声明为final,所以它不能被继承;
将所有的成员声明为私有的,这样就不允许直接访问这些成员;
对变量不要提供setter方法;将所有可变的成员声明为final,这样只能对它们赋值一次;
通过构造器初始化所有成员,进行深拷贝(deep copy);
在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝; - 对于集合(Collection,Map)类可以使用 Collections 里的 unmodified 相关方法创建对于的类;或者是 Guava 包类的不可变类
3.6、知识点
- final 成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误;
- 接口中声明的所有变量本身是 final 的;final 和 abstract 这两个关键字是反相关的,final 类就不可能是
abstract 的; - final 方法在编译阶段绑定,称为静态绑定(static binding)
- 将类、方法、变量声明为 final 能够提高性能,这样 JVM 就有机会进行估计,然后优化;
4、instanceof
4.1、一些使用注意事项
- 只能用于对象的判断,不能用于基本类型的判断;
- 若左操作数是 null 则结果直接返回 false,不再运算右操作数是什么类:(String)null instanceof String; // false;;因为 null 没有类型,所以即使做类型转换还是 null
- instanceof的右操作符必须是一个接口或者类: “demo” instanceof null; // 编译错误
- 数组类型也可以使用 instanceof 判断:
String[] str = new String[10];
str instanceof String[]; // true
4.2、instanceof与clazz.isInstance(obj)
- instanceof运算符用来在运行时指出对象是否是特定类的一个实例,通过返回一个布尔值来指出这个对象是否是这个特定类或者是它的子类的一个实例。
但是 instanceof 在 java 的编译状态和运行状态是有区别的,在编译状态中class可以是 object 对象的父类、自身类、子类,在这三种情况下java 编译时不会报错,在运行转态中 class 可以是 object 对象的父类、自身类但不能是子类;当为父类、自生类的情况下 result 结果为 true,为子类的情况下为 false;result = object instanceof class
- clazz.isInstance(obj):表明这个对象能不能被转化为这个类,一个对象能被转化为本身类所继承类(父类的父类等)和实现的接口(接口的父接口)强转;
4.3、instanceof 与 clazz.getClass():
instanceof 进行类型检查规则是你属于该类吗?或者你属于该类的派生类吗?clazz.getClass():获得类型信息采用 == 来进行检查是否相等的操作是严格比较,不存在继承方面的考虑;
4.4、instanceof实现原理
JVM有一条名为 instanceof 的指令,而Java源码编译到Class文件时会把Java语言中的 instanceof 运算符映射到JVM的 instanceof 指令上。
- 指令格式:instanceof|indexbyte1|indexbyte2
- 指令执行前后的栈顶状态:
……,objectref=>
……,result
- indexbyte1和indexbyte2用于构造对当前类的常量池的索引,objectref为reference类型,可以是某个类,数组的实例或者是接口
- 基本过程:对indexbyte1和indexbyte2构造的常量池索引进行解析,然后根据java规范判断解析的类是不是objectref的一个实例,最后在栈顶写入结果