一、关于final
根据程序上下文环境,Java
关键字fina
有这是无法改变的或者终态的含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
final
类不能被继承,没有子类,final
类中的方法默认是final
的。
final
方法不能被子类的方法覆盖,但可以被继承。
final
成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final
不能用于修饰构造方法。
注意:父类的private
成员方法是不能被子类方法覆盖的,因此private
类型的方法默认是final
类型的。
1、final
类
final
类不能被继承,因此final
类的成员方法没有机会被覆盖,默认都是final
的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,并且确信这个类不会再被扩展,那么就设计为final
类。
2、final
方法
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final
方法。
使用final
方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final
方法时候会转入内嵌机制,大大提高执行效率。
3、final
变量(常量)
用final
修饰的成员变量表示常量,值一旦给定就无法改变!
final
修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
另外,final
变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,无论什么情况,编译器都确保空白final
在使用之前必须被初始化。但是,final
空白在final
关键字final
的使用上提供了更大的灵活性,为此,一个类中的final
数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
4、final
参数
当函数参数为final
类型时,你可以读取使用该参数,但是无法改变该参数的值(网上最流行的说法)。
特例
public class Test {
public static void main(String[] args) {
MyClass myClass = new MyClass();
StringBuffer buffer = new StringBuffer("hello");
myClass.changeValue(buffer);
System.out.println(buffer.toString());
}
}
class MyClass {
void changeValue(final StringBuffer buffer) {
buffer.append("world");
}
}
====> helloworld
Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.
如果一个被final
关键字修饰的变量A
指向一个对象B
的引用,那么这个变量A
的状态可能会随着B
的改变而改变,但A
一直都指向B
的
在上例中,变量A
(buffer
)指向一个对象B
(StringBuffer
)的引用,对象B
(StringBuffer
)的值改变了,但是他的内存地址没有改变。
final StringBuffer a = new StringBuffer("Hello");
a = new StringBuffer("World"); //this wont compile
二、关于static
static
表示全局或者静态的意思,用来修饰成员变量和成员方法,也可以形成静态static
代码块,但是Java
语言中没有全局变量的概念。
1、static
变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static
修饰的变量,叫静态变量或类变量;另一种是没有被static
修饰的变量,叫实例变量。两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存), JVM 只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
用public
修饰的static
成员变量和成员方法本质是全局变量和全局方法,当声明其他类的对象时,不生成static
变量的副本,而是类的所有实例共享同一个static
变量。
static
变量前可以有private
修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用,但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private
是访问权限限定,static
表示不要实例化就可以使用,这样就容易理解多了。static
前面加上其它访问权限关键字的效果也以此类推。
2、static
方法
静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this
和super
关键字,不能直接访问所属类的实例变量和实例方法(就是不带static
的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!
因为static
方法独立于任何实例,因此static
方法必须被实现,而不能是抽象的abstract
。
3、static
代码块
static
代码块也叫静态代码块,是在类中独立于类成员的static
语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM
加载类时会执行这些静态的代码块,如果static
代码块有多个,JVM
将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
4、static
和final
一块用表示什么
static
、final
用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。
特别要注意一个问题:
对于被static
和final
修饰过的实例常量,实例本身不能再改变了,但对于一些容器类型(比如,ArrayList
、HashMap
)的实例变量,不可以改变容器变量本身,但可以修改容器中存放的对象,这一点在编程中用到很多。
5、静态内部类
这里简单介绍下,什么是静态内部类。
简单的说内部类前加static
就是静态内部类了,上代码:
public class Outer {
static int x =1;
static class Nest {
void print(){
System.out.println("Nest "+x);
}
}
public static void main(String[] args){
Outer.Nest nest = new Outer.Nest();
nest.print();
}
}
当内部类是static
时,意味着:
要创建静态内部类的对象,并不需要其外部类的对象;
不能够从静态内部类的对象中访问外部类的非静态成员;
与普通的内部类的一个区别:在非静态内部类中不可以声明静态成员,只有将某个内部类修饰为静态类,然后才能够在这个类中定义静态的成员变量与成员方法。
6、静态导包
所谓静态导入包:import static com. ... . ClassName.*
这样写可以导入相关类里面的所有静态方法,或者也可以直接静态导入具体到静态方法名。
这样写的好处是,引用静态方法不用在前面加上类名.
需要注意两点:
提防含糊不清的命名
static
成员。例如,如果你对Integer
类和Long
类执行了静态导入,引用MAX_VALUE
将导致一个编译器错误,因为Integer
和Long
都有一个MAX_VALUE
常量,并且Java
不会知道你在引用哪个MAX_VALUE
。方法名的命名尽量明确,让看代码的人看到名称就知道这个方法是干嘛用的,不然静态导入会让代码变的难读。
附:
对象的初始化顺序:
- 首先执行父类静态的内容,父类静态的内容执行完毕后,
- 接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,
- 再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,
- 父类的非静态代码块执行完毕,接着执行父类的构造方法;
- 父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。
- 子类的非静态代码块执行完毕再去执行子类的构造方法。
总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
**注意: 子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法**。如果父类没有不带参数的构造方法,那么子类必须用
super
关键子来调用父类带参数的构造方法,否则编译不能通过。
三、transient
java 的transient
关键字为我们提供了便利,你只需要实现Serilizable
接口,将不需要序列化的属性前添加关键字transient
,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
一旦变量被
transient
修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。transient
关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient
关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable
接口。被
transient
关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient
修饰,均不能被序列化。