JAVA关键字final和static原理

final

定义:final是Java保留的关键字,其含义为“无法改变的”,“终态”。可以用来修饰类(非抽象),方法,变量。
先说结论:
1.final类不能被继承,没有子类,final类中的方法是默认为final的
2.final方法不能被子类方法覆盖,但可以被继承,final不能用于修饰构造方法
3.final成员变量表示常量,只能被赋值一次,赋值之后不再改变,final成员变量必须在定义时或者构造方法中进行初始化赋值。
即:final用来声明变量,方法,类,分别表示:变量值不可变,方法不可被override,类不可被继承

父类的private方法不能被子类覆盖,因此private类型的方法默认是final类型

final类

当用final修饰一个类时

表明这个类不能被继承,因此final类的成员方法没有机会被覆盖,都是默认为final的,即被认为此类很完美不需要进行修改或者扩展,在使用中尽量不要将类设计为final类。日常使用中多是工具类被声明为final类,如:java.lang.Math,java.util.Collections。Math类还将其构造函数声明为private,即无法实例化Math类,只能通过类名,直接调用Math类中的变量或方法,当然如果采用java反射技术,是可以破坏这种限制的。

final方法

使用final修饰方法有两个原因,第一,说明这个方法提供的功能已经满足要求,不需要扩展,并且不允许任何从此类继承的子类来override这个方法,但可以继承使用这个方法。第二,final声明的方法执行效率高,编译器在遇到调用final方法时,会将所有对此方法的调用转化为inline调用机制,但是当有多处调用时,可能反而会影响使用效率,使用final定义方法要慎重。

final变量

final变量是最为常见的使用场景,*如果是final类型的基本数据类型变量,则其数值一旦定义后便不能更改;如果是引用类型变量,则再其初始化之后便不让再指向另一个对象了,但是其指向对象的内容是可以变化的。本质是是一回事,即final变量要求变量的地址不能变。*final变量的初始化可以在两个地方进行,一是在定义变量时便初始化,二是在类的构造方法中进行初始化,两者只能选其一

static

定义:static是Java保留的关键字,其含义是“静态的”,可以用来修饰变量,方法,内部类(不能修饰普通类),代码块

主要作用:在没有创建对象的情况下,可以直接通过类来调用的方法。static方法是没有this的方法,static方法内部不能调用非静态方法,反过来非静态方法中调用静态方法是可以的。

static方法

static修饰方法时,此方法可以不用依赖对象即可访问,因此同时静态方法是没有this的,所以在静态方法中是不能访问此类非静态成语变量和成员方法

static变量

static修饰变量时,此时变量又称为类变量。也是可以直接通过类名进行访问的。静态变量被类的所有对象共享,在内存中只有一个副本,仅在类加载的时候初始化一次.需要注意的是static修饰变量只能修饰成员变量,而不能修饰局部变量

static内部类

static修饰类时,只能修饰内部类,初始化静态内部类可以直接使用: new 外部类名.静态内部类()进行初始化,有个比较有趣的是,静态内部类中可以声明静态变量和非静态变量,但是普通成员内部类中只能声明非静态变量而不能声明静态变量(原因是成员内部类中的静态变量是会违反java对static设计的初衷,成员内部内的静态变量无法通过内部类.静态变量进行方法,因为内部类只有在外部类实例化时,才会被加载,所以在成员内部类没有被加载的情况下,其中却又有一个在类加载时就要被加载的静态变量,显然是逻辑相悖的)

static代码块

static修饰代码块,可以放置在类的任何地方,类中可以有多个静态代码块,类在初次被加载时,会按照静态代码块的顺序依次加载,且只会加载一次,静态代码块的加载顺序是在类的构造方法之前的。

额外需要注意的两个关键字修饰变量时,对变量存储位置的影响:
1.static关键字会对变量存储区域产生影响,静态变量在jvm中的方法区中进行存储
2.final关键字对变量的存储不会产生影响,只是表明该变量一旦赋值便不可改变
3.但是被final修饰的变量会在编译时期被加入常量池(运行时常量池,只是加入引用)

既然说到由final修饰的变量引用会被加入到常量池中,所以还需要讨论一下jvm中的几个“常量池”

jvm常量池主要分为Class文件常量池,运行时常量池,字符串常量池

Class文件常量池

众所周知,class文件是一组以字节为单位的二进制数据流。当java代码被编译为.class文件时,Class常量池就已存在,class文件中除了有class文件的版本号,编译后的字节码等一些其他class信息,剩下主要就是常量池,在这个class文件常量池中主要存放两大常量:字面量,符号引用
在这里插入图片描述

字面量,比较接近java中常量的概念,主要包括:
文本字符串,例如: String str = “abc”;
用final修饰的成员变量,静态变量,局部变量,即被final修饰的变量

文本字符串:
在这里插入图片描述

final变量:
在这里插入图片描述

可见class常量池中保存的都是字面量的值

符号引用
1.类和接口的全限定名,如java/lang/String,将原来类名全路径中的".“替换为”/"
在这里插入图片描述

2.字段名,和描述符
在这里插入图片描述

3.方法名,和描述符(参数和返回值)
在这里插入图片描述

运行时常量池

运行时常量池这个比较熟悉,在jvm运行时数据区的方法区中,其作用域是全局的。已知jvm在使用某个类时,必须进过 加载,链接(验证,解析,准备),初始化 三个步骤
第一步骤加载主要完成以下内容:
“通过类的全路径名,来获取对应类的二进制字节流”
“将字节流对应的静态存储结构转化为方法区的运行时数据结构”
“在内存中生成一个Class对象,代表加载的这个类,作为方法区这个类的各种数据访问入口,Class对象是在jvm加载该类时生成的,且为单例。”

其中的"将静态存储结构转化为方法区的运行时数据结构",就是class常量池进运行时常量池的过程。不同的类共用同一个运行时常量池,且在多个类中相同的常量字符串,在运行时常量池中只会存一份,相当于共用了。

运行时常量池与class类常量池的区别在于不仅保存了所有class常量池中的字符,而且还具有动态性,也就是在运行时可以通过代码向其中添加内容,使用这种特性最多的就是String.intern()

字符串常量池

在java中创建字符串有两种方式,String str = "abc"和String str1 = new String(“abc”);
第一种声明的为字面量,在编译时期就已经是确定的值,所以会被加入Class类常量池,运行时常量池,且当运行时,还会在字符串常量池中保存一个它的引用,其实际的对象值,仍是在中创建的
第二种声明,是最常见的声明对象,调用String的构造方法,因此这种方式是在运行时确定值的,对象毫无疑问是在堆中创建的
所以如果判断str == str1是否相等,结果为false,虽然二者都是在堆中创建的,但显然地址是不同的

jvm运行时数据区:
在这里插入图片描述

分为:

堆,非堆(永久代(方法区,字符串常量池),当然现在方法区已经不在永久代了,而是保持在本地内存中叫元数据区,同时在JDK1.7之后,字符串常量池被移到堆中了),图中的“interned Strings”即为字符串常量池
"string str = ‘abc’"这个声明的加载流程是:加载到方法区中的class类常量池->加载到运行时常量池->引用保存到字符串常量池 ,同样真正的对象是在堆中创建的
当线程开始创建str变量时,jvm首先会去字符串常量池中查找是否有equals(“abc”)的String,如果存在则把在字符串常量池中的引用进行返回,如果没有找到值相同的引用变量,则自身先会在堆中创建一个对象,然后把对象的引用先驻留在字符串常量池中,最后再返回给变量str

字符串常量池底层实现是一个cpp实现的hashtable,作用是jvm用来维护的一个字符串引用列表

“String的字面量是何时进入字符串常量池的?”
就HotSpot vm来说,加载类的时候,字符串字面量会进入运行时常量池,不会进入字符串常量池(即在string table中并无引用,在堆中也没有对应的对象产生),只在Load Constant指令执行时,才会触发lazy resolution这个动作。
LDC指令执行字符串时,resolve的过程如果发现String table已经有了内容匹配的java.lang.string的引用,则直接返回这个引用,如果string table中尚无这个引用,则会现在堆中创建这个对象,并在string table中记录这个引用,并返回这个引用(就如上面所说的)

java基本类型封装类的常量池

在java中基本类型也基本都实现类常量池,包括Byte,Short,Integer,Long,Character,Boolean都实现了常量池,两个浮点类的包装类没有实现常量池
此外,以上几种实现了常量池的基本类型包装类,其中数值类型只有当值的范围在<=127时,才会使用常量池,否则则还是创建新的对象
在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值