final 和 finally{ },以及"常量池"的特点及区别
final
-
final: Final用于修饰类、成员变量和成员方法
-
final修饰的类,不能被继承(String、StringBuilder、StringBuffer、Math,不可变类),所以不能同时用abstract和final修饰类(abstract修饰的类是抽象类,抽象类是用于被子类继承的(抽象类被子类继承必须重写父类的抽象方法),和final起相反的作用;
-
final修饰的方法不能被重写(这里需要注意的是不能被重写,但是可以被重载,这里很多人会弄混),但是子类可以用父类中final修饰的方法;
final修饰的成员变量是不可变的,如果成员变量是基本数据类型,初始化之后成员变量的值不能被改变,如果成员变量是引用类型,那么它只能指向初始化时指向的那个对象,不能再指向别的对象,但是对象当中的内容是允许改变的。
public static void main(String[] args) {
String a = "shuaige2";
final String b = "shuaige";
String d = "shuaige2";
String c = b + 2;
String e = d + 2;
System.out.println((a == c));
System.out.println((a == e));
}
}
因上面代码阅读会用到常量池,所以在此补充.
常量池知识点:
常量池分类
常量池大体可以分为:静态常量池,运行时常量池。
静态常量池 存在于class文件中,比如经常使用的javap -verbose中,常量池总是在最前面把?
运行时常量池呢,就是在class文件被加载进了内存之后,常量池保存在了方法区中,通常说的常量池指的是运行时常量池。所以呢,讨论的都是运行时常量池
public class Demo5 {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2); // true,s1 = = s2 很容易可以判断出来。s1 和 s2 都指向了方法区常量池中的Hello。
System.out.println(s1 == s3); // true,s1 = = s3 这里要注意一下,因为做+号的时候,会进行优化,自动生成Hello赋值给s3,所以也是true
System.out.println(s1 == s4); // false,s1 = = s4 s4是分别用了常量池中的字符串和存放对象的堆中的字符串,做+的时候会进行动态调用,最后生成的仍然是一个String对象存放在堆中。
System.out.println(s1 == s9); // false,s1 = = s9 在JAVA9中,因为用的是动态调用,所以返回的是一个新的String对象。所以s9和s4,s5这三者都不是指向同一块内存
System.out.println(s4 == s5); // false,s4和s5是在堆中创建了两个对象,对象的地址不同("=="比较的是地址)
System.out.println(s1 == s6); // trues1 = = s6 为啥s1 和 s6地址相等呢? 归功于intern方法,这个方法首先在常量池中查找是否存在一份equal相等的字符串如果有的话就返回该字符串的引用,没有的话就将它加入到字符串常量池中,所以存在于class中的常量池并非固定不变的,可以用intern方法加入新的
}
}
finally{ }
Java 中的 finally 关键一般与try{},catch{}起使用,无论程序是因为异常而中止或其它方式返回终止的,finally块的内容一定会被执行
在finally代码块中,可以运行清理类型等收尾善后性质的语句
Finally代码块出现在catch代码块最后
当try和catch中有return时,finally仍然会执行;
finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
public class Demo2 {
public static void main(String[] args) {
Demo2 t=new Demo2();
try {
t.score(103);
} catch (Demo3 demo3) {//自定义类异常
//demo3.getMessage();
demo3.printStackTrace();
}finally {
System.out.println("---------------");//这句代码一定会执行
}
}
public void score(int s) throws Demo3 {
if(s<0||s>100)
{
throw new Demo3("成绩不合法");
}
System.out.println("123456789");
if (s>90){
System.out.println("优秀");
}
if(s<90) {
System.out.println("不优秀");
}
}
}
**注意:**当try语句块捕捉异常时,语句块中如果有显式的thow抛出异常则不能有return.因为编译器认为try块中是又可能产生异常操作的,也就是说在return语句之前如果出现异常的话,那么return语句根本没有机会得到执行,所以编译器会认为缺少return语句.但如果把return写在thow抛出异常前面,那依旧会报错,因为你已经返回了怎么还会有代码继续执行呢.
解决办法:解决办法:
a、在catch块中加入return语句,因为一旦出现异常,catch中的语句可以保证函数会有一个返回值
b、在finally块中加入return语句或者在函数末尾加入return语句,我认为此种方法不可行,因为无论哪种,只要系统不退出,finally语句块或函数结尾的return会始终得到执行,这样就会覆盖try块中的return,程序在逻辑上会出现错误!
public class Demo6 {
static void methodA() {
try {
System.out.println("进入方法A");//1
throw new RuntimeException("制造异常");
} finally {
System.out.println("用A方法的finally");
}
}
static int methodB() {
try {
System.out.println("进入方法B");
throw new Exception();
} catch (Exception e) {
return 3;
} finally {
System.out.println("调用B方法的finally");
return 2;
}
}
public static void main(String[] args) {
try {
methodA();
} catch (Exception e) {
//System.out.println(e.getMessage());
}
int i = methodB();//2
System.out.println(i);//6
} }