Java关键词final

1、修饰类

        final修饰类时,这个类不能被继承

        final类的成员变量可以根据需要设为final,但是final类中的所有成员方法都会被隐性地指定为final方法,不可继承

        类的private()方法会隐式地指定为final方法。

        除非这个类在之后都不会用来继承或者出于安全考虑,尽量不要将类设计为final类。

2、修饰方法

        a.把方法锁定,不可重写,以防任何继承类修改它的含义(但可以重载);

        b.效率。早期的Java实现版本中,会将final方法转为内嵌调用。如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。

      只有在想确定禁止该方法在子类中被覆盖的情况下,才将方法设置为final。

3、修饰变量

        表示该变量在编译之后变成一个常量,不可以被修改。对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用数据类型的变量,则在对其初始化之后便不能再让其指向另一个对象

4、类的final变量和普通变量有什么区别?

        当final作用于类的成员变量时,成员变量必须再定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。

public class Final01 {
    public static void main(String[] args) {
        String a = "hello2";
        final String b = "hello";
        String c = "hello";
        String d = b+2;
        String e = c+2;
        System.out.println((a==d)); //true
        System.out.println((a==e)); //false
    }
}

//当final变量是基本数据类型或String类型时,如果在编译期间能知道它的确切值,则编译器会把它当作编译期常量使用。也就是在用到该final变量的地方会直接访问这个常量,不需要在运行时确定(和C语言的宏替换有点像)。
//代码中由于b变量被final修饰,因此会被当做编译器常量,在使用b的地方会直接使用b的值。访问c却需要在运行时通过链接来进行。
//只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化。
public class Test {
    public static void main(String[] args) {
        String a = "hello2";
        final String b = getHello();
        String c = b+2;
        System.out.println(a==c); //false
    }

    public static String getHello() {
        return "hello";
    }
}

//只有在编译期间确切知道final变量值的情况下,编译器才会对此进行优化。
//编译期间变化可见文章“Java编译与运行时”(http://t.csdn.cn/H7lGy)

5、被final修饰的引用类型变量指向的对象内容可变。

6、final和static和static final

        A.final

        a1.final的作用是用来保证变量不可变。

        a2.Java中的String类和Integer类都是final类型。

        a3.final类中所有的变量都必须是final变量。

        a4.接口中声明的所有变量本身都是final的,类似匿名类。

        a5.final和abstract这两个关键字是相反的,final类不可能是abstract。

        a6.可以修饰:类、方法、成员变量、局部变量

             不能修饰:构造方法、代码块、修饰的方法不能重写。

public class Test {
    public static void main(String[] args) {
        MyClass myClass1 = new MyClass();
        MyClass myClass2 = new MyClass();
        System.out.println(myClass1.i); //与myClass2.i不相等
        System.out.println(myClass1.j); //与myClass2.j相等
        System.out.println(myClass2.i);
        System.out.println(myClass2.j);
    }
    class MyClass {
        public final double i = Math.random(); // 大于0小于1的随机数
        public static double j = Math.random();
    }
}

// static修饰变量只创建一次

        B.static

        b1.static表示全局的 or 静态的。

        b2.在JVM中,被static修饰的方法和变量存放在方法区中(JDK8称为元空间),它属于全局贡献的,而不是某个线程私有的。同时,独立于该类中的任何对象,它不依赖于类的特定实例(对象),被类的所有实例共享,可以直接用类名调用类中的任何方法和变量(静态方法中,只能调用今天该方法的方法和属性;非静态方法中,既可以调用非静态的方法和属性,也可以调用静态的方法和属性)。

        b3.static作用于成员变量用来表示只保存一份副本。

        b4.修饰属性(静态变量或类变量):类中的多个对象如果共享一个静态变量,当通过某一个变量改变静态对象时,所有的对象对应的值都会发生改变。(注意:1、由于类只会加载一次,所以静态变量在类加载时创建并只会在内存中存一份,存在方法去的静态域中;2、静态变量的加载要早于对象的创建)

        b5.修饰代码块(即静态代码块):当JVM加载类时,会执行该代码块,只会被执行一次。

        b6.static方法不能使用this或super关键字,且必须被实现,不能是抽象的abstract。

        b7. 可以修饰:类、方法、成员变量、代码块、属性;

              不可修饰:构造器、局部变量、static方法不能被重写

        C.static final

        c1.static final和final static没有区别,两者不会相互修饰,javac成 .java后没有任何区别。修饰变量一旦赋值不可修改,作为类常量,并且可以用类直接访问或调用;修饰方法不可覆盖,并且可以通过类直接访问或调用。

7、匿名内部类中使用的外部局部变量只能是final变量。(原因在:)

8、final修饰基本数据类型和引用数据类型变量

方法中作为参数的基本数据类型变量,在方法中被修改时,无法影响方法外该变量的值,除非重新赋值。

public class final02 {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        int i = 0;
        myClass.ChangeValue(i);
        System.out.println(i); //0
    }
}

class MyClass {
    int ChangeValue(int i){
        return ++i;
    }
}

// changeValue和main方法中的变量i不是同一个地址,java参数传递采用的是值传递,对于基本类型的变量,相当于直接将变量进行了拷贝。

方法中作为参数的引用数据类型变量,在方法中被修改时,无论是否在形参前面加上final都会改变方法外改变量的值。 

Public class Final03{
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        StringBuffer buffer = new StringBuffer("hello");
        myClass.changeValue(buffer);
        System.out.println(buffer); //helloworld
        System.out.println(buffer.toString); //helloworld
    }
}

class MyClass {
    void changeValue(final StringBuffer buffer) {
        buffer.append("world");
    }
}

//用final进行修饰并没有阻止changeValue中改变buffer指向的对象的内容。
//java采用的是值传递,对于引用变量,参数传递的是引用的值。即让实参和形参都指向了同一个对象。

9、final的好处

        a.提高了性能,JVM在常量池中会缓存final变量;

        b.final变量在多线程中并发安全,无需额外的同步开销;

        c.final方法是静态编译的,提高了调用速度

        d.final类创建的对象是只可读的,在多线程中可以安全贡献。

10、final内存分配

        当调用一个函数时,除了函数本身的执行时间之外,还需要额外的时间去寻找这个函数(类内部有一个函数签名和函数地址的映射表)。所以减少函数调用的次数就等于降低性能消耗。final修饰的函数会被编译器优化,优化的结果是减少了函数调用的次数,具体实现如下:

public class Test{
    final void func(){
        System,out.println("g");
    }
    public void main(String[] args){
        for(int i=0;i<1000;i++){
            func();
        }
    }
}

//经过编译器优化之后,变成以下
public class Test{
    final void func(){
        System.out.peintln("g");
    }
    public void main(String[] args){
        for(int i=0;i<1000;i++){
            System.out.println("g");
        }
    }
}

//编译器直接将func的函数体内嵌到调用函数的地方,这样做节省了1000次函数调用。
//当然编译器处理成字节码,只是我们可以想象成这样

        不过如果函数体太长的话,用final可能会适得其反,因为经过编译器内嵌之后的代码长度大大增加,于是就增加了jvm解释字节码的时间。

        在使用final修饰方法的时候,编译器会把被final修饰过的方法插入到调用处,提高运行速度和效率,但被final修饰的方法体不能过大,编译器可能会放弃内联。

11、使用final修饰方法会提高速度和效率吗?

——加了比不加好一点

public class Test{
    public static void getJava(){
        String str1="Java";
        String str2="final";
        for(int i =0;i<10000;i++){
            str1 += str2;
        }
    }
    public static final void getJava_Final(){
        String str1="Java";
        String str2="final";
        for(int i=0;i<10000;i++){
            str1 += str2;
        }
    }
    public static void main(String[] args){
        long start = System.currentTimeMillis(); // 查看从1970.1.01 00:00:00:000到当前时间的时刻距离,类型为long
        getJava();
        System.out.println("调用不带final修饰的方法执行时间为" + (System.currentTimeMillis() - start) + "毫秒时间"); //1732、1217、1154、1139、1186
        start = System.currentTimeMillis();
        String str1 = "Java";
        String str2 = "final";
        for(int i=0; i<10000; i++){
            str1 += str2;
        }
        System.out.println("正常的执行时间为:" + (System.currentTimeMillis() - start)+"毫秒时间"); //1498、1031、1140、999、1030
        start = System.currentTimeMillis();
        getJava_Final();
        System.out.println("调用final修饰方法的执行时间为:" + (System.currentTimeMillis() - start) + "毫秒时间"); //1593、1124、1202、1092、1109
    }
}

//执行5次后可以看出,执行最快的是”正常的执行“即代码直接编写。使用final修饰的方法,位于两者之间(有些书上说使用final修饰的方法速度和效率与”正常执行“无异)。最慢的是不加final修饰的方法。

12、如何保证final修饰引用数据类型的变量值不会被修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值