final
final:修饰类、方法和变量(成员变量和局部变量)
修饰类:
当final修饰类,说明该类不能被继承。Final类中的所有成员方法被隐式的指定为final方法。要谨慎选择是否要用final修饰类,除非这个类真的以后不会被用来继承或者出于安全考虑,尽量不要把类设计为final。
修饰方法:
(1)将方法锁定,防止继承类修改它的含义。
(2)原因是效率。现在已经不用final方法进行优化了。
只有当明确禁止方法被子类覆盖的情况下,才将方法用final修饰
修饰变量:
(1)基本数据类型:表示为常量,一旦初始化不得修改。
(2)引用类型的变量:初始化不可将其指向另一个对象,但对象的内容是可变的。
(3)final成员变量必须在定义时或在构造方法中进行初始化赋值,一旦初始化不可再被赋值。(final局部变量只要在使用之前初始化即可)
当final变量是基本数据类型以及String类型时,如果在编译期间能知道其确切值,则编译器会视为编译期常量,在运行时直接赋值。
public static void main(String[] args) {
String a = "hello2";
final String b ="hello";//确切知道值,视为编译期常量
final String b2 = getHello();//运行期间才能确定值,且指向的对象的内容可变,不属于编译期常量
String d = "hello";
String c = b + 2;
String c2 = b2+2;
String e = d + 2;
System.out.println((a == c)); //true,编译期常量
System.out.println((a == c2)); //false
System.out.println((a == e)); //false,比较的是地址
}
public static String getHello(){
return “hello”;
}
Final 和 static
public static void main(String[] args) {
Test2 test2 = new Test2();
Test2 test22 = new Test2();
System.out.println(test2.i);
System.out.println(test22.i);
System.out.println(test2.j);
System.out.println(test22.j);
System.out.println(test2.i == test22.i);//false
System.out.println(test2.j == test22.j);//true
}
public class Test2 {
final double i = Math.random();//保证指向的对象不变,但对象的内容可以改变
static double j = Math.random();//保证只有一个副本
}
final修饰参数时使用无效
java采用的是值传递:
对于基本数据类型变量,相当于把值进行拷贝,因此即使没有final修饰参数的情况下,在方法内部改变了基本数据类型的变量i的值也不会影响方法外的i。
对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响。
public static void main(String[] args) {
Test2 test2 = new Test2();
StringBuffer str = new StringBuffer("hello");
test2.addString(str);
System.out.println(str.toString());//helloworld!
}
public class Test2 {
public void addString(final StringBuffer str){
str.append("world!");
}
}
若final修饰基本数据类型作为形参时,对形参操作时会报错:The final local variable i cannot be assigned. It must be blank and not using a compound assignment
Finally
(1)只有与 finally 相对应的try语句块得到执行的情况下,finally语句块才会执行。
public static void main(String[] args) {
System.out.println("return value of test(): " +test());
}
public static int test() {
int i = 1;
//if(i == 1)
// return 0; //若把这里的注释去掉,会在try代码前返回,也不会进入try代码块以及finally
System.out.println("the previous statement of try block");
i = i / 0; //throw Exception.不会进入到try代码块
try {
System.out.println("try block");
return i;
}finally {
System.out.println("finally block");
}
}
(2)不管 try 语句块正常结束还是异常结束,finally语句块是保证要执行的。如果try语句块正常结束,那么在try语句块中的语句都执行完之后,再执行finally语句块。
(3)当try或catch中有控制语句转移语句,如return、throw、break、continue时,finally是在控制转移语句前执行。其中return和throw把程序控制权转交给它们的调用者(invoker),而break和continue的控制权是在当前方法内转移。
public class Test {
public static void main(String[] args) {
System.out.println("reture value of test() : " + test());
}
public static int test(){
int i = 1;
try {
System.out.println("try block");
i = 1 / 0;
return 1;
}catch (Exception e){
System.out.println("exception block");
return 2;
}finally {
System.out.println("finally block");
}
}
}
结果
try block
exception block
finally block
reture value of test() : 2
//finally 语句块在 catch语句块中的 return语句之前执行。
对于return 和throw返回给调用者数时,会需要注意返回的值。参考:http://www.ibm.com/developerworks/cn/java/j-lo-finally/
例1:
public class Test {
public static void main(String[] args) {
System.out.println("return value of getValue(): " + getValue());
}
public static int getValue() {
try {
return 0;
} finally {
return 1;
}
}
}
return value of getValue(): 1
例2:
public class Test2 {
public static void main(String[] args) {
System.out.println("return value of getValue(): " +getValue());
}
public static int getValue() {
int i = 1;
try {
return i;
} finally {
i++;
}
}
}
例2解释:Java 虚拟机会把 finally语句块作为subroutine(对于这个subroutine不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解。)直接插入到try语句块或者catch语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行subroutine(也就是finally语句块)之前,try或者catch语句块会保留其返回值到本地变量表(Local Variable Table)中。待subroutine执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过return或者throw语句将其返回给该方法的调用者(invoker)。
public static void main(String[] args) {
System.out.println(test());
}
public static String test() {
try {
System.out.println("try block");
return test1();
} finally {
System.out.println("finally block");
}
}
public static String test1() {
System.out.println("return statement");
return "after return";
}
结果
try block
return statement //return test1()看作:String tmp = test1();return tmp;
finally block
after return
总结:异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的catch子句就会执行,然后控制就会进入finally块(如果有的话)。一般异常处理块需要。
public class text {
String string = "";
public static void main(String[] args) {
text text = new text();
text.match(1);//12
text.match(0);//1223
System.out.println(text.string);//1223
}
public void match(int i){
try{
if(i==1){
string+="1";
return;
}
}catch (Exception e) {
}finally{
string+="2";
}
string+="3";
}
}
Finalize
在 Java 中,当你创建一个对象时,Java虚拟机(JVM)为该对象分配内存、调用构造函数并开始跟踪你使用的对象。当你停止使用一个对象(就是说,当没有对该对象有效的引用时),JVM通过垃圾回收器将该对象标记为释放状态。
当垃圾回收器将要释放一个对象的内存时,它调用该对象的finalize() 方法(如果该对象定义了此方法,)。垃圾回收器以独立的低优先级的方式运行,只有当其他线程挂起等待该内存释放的情况出现时,它才开始运行释放对象的内存。(事实上,你可以调用System.gc()方法强制垃圾回收器来释放这些对象的内存。)
(1)finalize方法是在Object类中定义的,因此所有的类都继承了它,子类覆盖finalize方法以整理系统资源或者执行其他清理工作。Finalize方法是在垃圾收集器删除对象之前对这个对象调用的。
(2)只有当垃圾回收器释放该对象的内存时,才会执行finalize()
(3)如果在 Applet 或应用程序退出之前垃圾回收器没有释放内存,垃圾回收器将不会调用finalize()。
(4)除非垃圾回收器认为你的 Applet 或应用程序需要额外的内存,否则它不会试图释放不再使用的对象的内存。如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说filalize()可能永远不被执行,显然指望它做收尾工作是靠不住的。
(5)finalize的主要用途是回收特殊渠道申请的内存。Java程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种JNI(Java Native Interface)调用non-Java程序(C或C++),finalize()的工作就是回收这部分的内存。
总结:finalize方法名
Java 技术允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize() 方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。