Java中的Final关键字,区分基本数据类型与引用类型,值传递与引用传递

来源:互联网 发布:手机表白神器软件 编辑:程序博客网 时间:2024/06/06 01:32

Final可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了。对于基本数据类型声明为Final后其值无法再次改变,对于引用类型,可以改变改引用的内容,但是无法改变这个引用,即不能再次指向新的引用地址,也就是重新赋值,new一个新的对象。

首先我们来看看基本数据类型与引用类型的区别:

这里写图片描述
如上如所述;
对于基本类型,变量中保存的是其值。对于引用类型,变量中保存的只是实际对象的地址。一般称这种变量为”引用”,引用指向实际对象,实际对象中保存着内容。
引用类型的变量(实际对象的地址)是保存在栈中的,引用所指向的实际对象是保存在堆中的。所以说基本类型保存在栈中,引用类型保存在栈中。

这里写图片描述
对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。
如上图所示,”hello” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)

关于值传递与引用传递

首先看看什么是按值传递,什么是引用传递。
按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。
按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。.

个人的理解是:Java中只有值传递
当参数是基本数据类型时传递的是变量的值,当参数是引用类型的时候传递的是引用地址(栈中保存的引用变量地址)。
String,Integer, Double等immutable类型因为没有提供自身修改的函数,每次操作都是新生成一个对象,所以要特殊对待。可以认为是传值。
Integer 和 String 一样。保存value的类变量是Final属性,无法被修改,只能被重新赋值/生成新的对象。 当Integer 做为方法参数传递进方法内时,对其的赋值都会导致 原Integer 的引用被 指向了方法内的栈地址,失去了对原类变量地址的指向。对赋值后的Integer对象做得任何操作,都不会影响原来对象。

final关键字的好处

下面总结了一些使用final关键字的好处
final关键字提高了性能。JVM和Java应用都会缓存final变量。
final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
使用final关键字,JVM会对方法、变量及类进行优化。

关于final的重要知识点

final关键字可以用于成员变量、本地变量、方法以及类。
final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
你不能够对final变量再次赋值。
本地变量必须在声明时赋值。
在匿名类中所有变量都必须是final变量。
final方法不能被重写。
final类不能被继承。
final关键字不同于finally关键字,后者用于异常处理。
final关键字容易与finalize()方法搞混,后者是在Object类中定义的方法,是在垃圾回收之前被JVM调用的方法。
接口中声明的所有变量本身是final的。
final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
final方法在编译阶段绑定,称为静态绑定(static binding)。
没有在声明时初始化final变量的称为空白final变量(blank final variable),它们必须在构造器中初始化,或者调用this()初始化。不这么做的话,编译器会报错“final变量(变量名)需要进行初始化”。
将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
按照Java代码惯例,final变量就是常量,而且通常常量名要大写:

public class TestFinal {    public static void main(String[] args) {        StringBuilder str = new StringBuilder("test");        System.out.println(testFinal(str));        Student stu = new Student(20, "xfl");        System.out.println(testFinal(stu));        Map<String,String> map = new HashMap<String, String>();        map.put("final", "final");        System.out.println(changeValue(map).get("final"));        System.out.println(change(map).get("final"));        System.out.println(map.get("final"));    }    public static StringBuilder testFinal(final StringBuilder str){        //改变str的内容 但是无法改变str所指向的引用 ,也就是str不能指向新的引用地址        str.append("Fianl");        return str;    }    public static int testFinal(final int tmp){        //这里无法改变tmp的值        return tmp;    }    public static Student testFinal(final Student stu){        //可以改变内容  但是不能让其指向一个新的对象        stu.age = 21;        return stu;    }    public static Map<String,String> changeValue(final Map<String,String> map){        map.put("final", "alredyChange");        return map;    }    public static Map<String,String> change(Map<String,String> map){        //改变了引用地址所指向的内容        map.put("final", "alredyChange");        //改变了map的引用  指向了一个新的对象        map = new HashMap<String, String>();        map.put("final", "newMap");        return map;    }    public static void changInt(Integer a){        //这里相当与创建了一个新的对象        a = 6;    }    static class Student{        private int age;        private String name;        public Student(int age,String name) {            this.age = age;            this.name = name;        }        public void setAge(int age) {            this.age = age;        }        public int getAge() {            return age;        }        public void setName(String name) {            this.name = name;        }        public String getName() {            return name;        }        @Override        public String toString() {            return "age:"+ age +" name: " +name;        }    }}

参考资料:
https://www.zhihu.com/question/31203609
http://www.importnew.com/7553.html

1 0