深度理解Java中的static

深度理解Java中的static目录一、static的用法:二、static的误区(问题思考)三、问题思考:一、static的用法:static可以用来修饰类的成员方法、类的成员变量、类中的内部类(以及用static修饰的内部类中的变量、方法、内部类),另外可以编写static代码块来优化程序性能。作用:方便在没有创建对象的情况下来进行调用(方法/变量)。被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。①修饰类的成员变量:static.

大家好,又见面了,我是你们的朋友全栈君。

目录

一、static的用法:

使用:

1、修饰类的成员变量:

2、修饰类的成员方法:

3、修饰代码块:形成静态代码块以优化程序性能。

4、修饰内部类:

二、static的误区(问题思考)

1、static关键字会改变类中成员的访问权限吗?

2、static能作用于局部变量么?

3、在静态的方法内,不能使用this和super关键字:

三、问题思考

1、java中为什么要有static关键字?

①static可以不需要实例化对象就可以访问类中的属性和方法。

②main方法必须用static修饰

③资源必须要优先加载时要static修饰。

关于Java运行的过程大概分为两个步骤:

2、使用static的作用

前端静态资源与java的static修饰的资源类比

3、开发时,如何确定一个属性和方法是否要声明为static


一、static的用法:

static可以用来修饰类的成员方法、类的成员变量、类中的内部类(以及用static修饰的内部类中的变量、方法、内部类),另外可以编写static代码块来优化程序性能。

使用:

方便在没有创建对象的情况下来进行调用(方法/变量)。

被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。

1、修饰类的成员变量:

static修饰的成员变量称为静态变量:

静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static成员变量的初始化顺序按照定义的顺序进行初始化。

2、修饰类的成员方法:

static修饰的成员方法称为静态方法:在静态方法中不能访问类的非静态成员方法和非静态成员变量。但是在非静态方法中可以访问静态成员方法和静态成员变量。

public class Person {
    //声明静态成员变量:公有的name 和 私有的age——验证访问范围
    public static String name = "huahua";
    private static int age = 31;
    //声明公有的非静态成员变量 tags
    public String tags = "sing";

    //静态成员方法
    public static void print1() {
        System.out.println(name);
        System.out.println(age);
        //静态方法中调用非静态成员变量和非静态成员方法报错!
       // System.out.println(tags);//报异常
       // print2();//报异常
    }
    //非静态成员方法——可调用静态成员方法和静态成员变量
    public void print2() {
        System.out.println(name);
        System.out.println(age);
        System.out.println(tags);
        print1();
    }

image.png

说到静态函数,就不得不提Java另一个关键词this,指的是当前的引用。意思是调用这个函数的对象,这意味着和static修饰的函数水火不容。被static修饰的函数,不能出现this关键字,否则便会报错。

参考博客:Java关于static的作用 – 一剑天门 – 博客园

3、修饰代码块:形成静态代码块以优化程序性能。

static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。

参考:Java中的static关键字解析 – Matrix海子 – 博客园

4、修饰内部类:

public class Car extends AbsCar {
    //静态内部类中可以用static继续修饰内部类
    static class A {
        static class B {
            static int num0 = 10;
        }
    }

    //静态内部类C
    static class C {
        int num1 = 10;
    }

    //普通内部类D
    class D{
        int num2 =10;
    }

    public static void main(String[] args) {
        int num = A.B.num0;

        C c = new C();
        int a = c.num1;

    }
  }

二、static的误区(问题思考)

1、static关键字会改变类中成员的访问权限吗?

Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。

public class Main {
    public static void main(String[] args) {
        //通过类直接访问静态成员变量和静态成员方法
        System.out.println(Person.name);//公有name,私有的age访问不到
        Person.print1();
        
       // 通过类直接访问非静态成员变量和非静态成员方法不可以!——无论公有还是私有
        //System.out.println(Person.tags);//非静态的公有tags不可以通过类直接访问
        //Person.print2();//通过类直接访问非静态成员方法不可以!
        
        Person person = new Person();
        //通过对象访问非静态的公有tags属性和非静态成员方法
        System.out.println(person.tags);
        person.print2();
    }
}

image.png

2、static能作用于局部变量么?

在C/C++中static是可以作用域局部变量的,但是在Java中切记:static是不允许用来修饰局部变量。

image.png

理由:

①局部变量最好不要设成静态变量,局部变量是有生命周期的,用完后JAVA很快就回收资源了。

如果设成静态变量,那JAVA怎么回收被其占用的内存。

②在方法里面定义的变量是局部变量,就是说他有一定的作用范围和生命周期,就只能在方法里面使用而不能将其扩展到别的地方,这个变量在方法结束后就会被回收器回收,就不再存在了,而你要强制将其加上static就是要把它的作用范围扩展到整个类,这就与你开始定义这个变量时违背了,这是语法上的错误。

③static 变量是给类用的。这样类初始化的时候,就会给static进行初始化

如果你在方法里面定义一个static。这时候编译器就不知道你这个变量怎么初始化了,这个是和java的设计相关的。java全是面向对象设计的,单独一个方法不能持有一块空间。

④一个类中,一个static变量只会有一个内存空间,虽然有多个类实例,但这些类实例中的这个static变量会共享同一个内存空间。所以声明为static的变量实质上就是全局变量。所以static不能修饰局部变量。

此外,局部变量是存放在栈中的,程序运行完立即释放。它只能在定义它的方法内部使用。所以不用static修饰符。

参考:Java static关键字为什么不能应用于局部变量? – 调试易

3、在静态的方法内,不能使用this和super关键字:

this,指的是当前的引用。意思是调用这个函数的对象,这意味着和static修饰的函数水火不容。被static修饰的函数,不能出现this关键字,否则便会报错。

三、问题思考

1、java中为什么要有static关键字?

①static可以不需要实例化对象就可以访问类中的属性和方法。

②main方法必须用static修饰

a. 程序被打包成.jar文件后(相当于.exe文件),给外界唯一的接口就是main方法。使用者双击.jar文件,其实就是让虚拟机执行main方法。

b. main方法不是提供给程序员的,而是提供给虚拟机和使用客户的。 一个软件你没法让客户知道你内部的详情,当然客户也就没办法知道怎么去实例化对象,更不知道实例化对象时需要输入什么参数了。所以只能采用静态方法。

③资源必须要优先加载时要static修饰。

java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法<clinit>都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。

初始化顺序是先静态对象后非静态对象。当JVM创建一个类的对象时,会先在方法区里边找该类的信息,如果没有,会马上加载该类,把该类的类型信息放到方法区中。简而言之:在JVM创建类对象之前要把类的类型信息(静态变量、静态方法等)放到方法区中。

关于Java运行的过程大概分为两个步骤:

(1)类的加载

类被加载到虚拟机内存中开始,到卸载出内存为主,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个阶段称为连接(Linking)(参考《深入了解Java虚拟机》)类的生命周期如图:

深度理解Java中的static

深度理解Java中的static

  加载:通过一个类的全限定名来获取定义此类的二进制字节流(Class文件);将这个二进制字节流所代表的静态存储结果转化为方法区的运行时数据结构;在内存中生成一个java.lang.Class对象,注意:存放在方法区!

  验证:验证目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全;使用纯粹的Java代码无法做到诸如访问数组边界意外的数据、将一个对象转型为它未实现的类型、跳转到不存在的代码之类的事情,如果这样做了,编译器将拒绝编译!

  准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。首先这时候进行内存分配的仅包括类变量(static修饰的变量),而不是实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。

public static int value = 123; 

  变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,在类初始化的时候才会将value的值赋为123.

  解析:解析阶段是虚拟机将class常量池内的符号引用替换为直接引用的过程。

     符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可;

       直接引用:是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。有了直接引用,那引用的目标必定已经在内存中存在。

  初始化:类初始化阶段是类加载过程的最后一步;在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源:初始化阶段是执行类构造器<clinit>( )方法的过程。

  <clinit>( )方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static { }块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。 

(2)类的执行

  需要说明的一点的是:JVM主要在程序第一次运行时主动使用类的时候,才会立即去加载,加载完毕就会生成一个java.lang.Class对象,并且存放在方法区。换言之,JVM并不是在运行时就会把所有使用到的类都加载到内存中,而是用到,不得不加载的时候,才加载进来,而且只加载一次,初始化类构造器<clinit>()方法也只执行一次,所以static{} 块,类变量赋值语句也就只执行一次,只生成一个java.lang.Class对象!

参考博客:Java 代码 编译和执行过程_春天的早晨的博客-CSDN博客

Java程序编译和运行的过程 – 邱明成 – 博客园

Java编译程序和运行过程详解 – helloworldhaha – 博客园

Java类加载器 — classloader 的原理及应用_阿里巴巴淘系技术团队官网博客的博客-CSDN博客

2、使用static的作用

①效率,加快速度。

②被static修饰的变量属于类变量,通过字面意思就说明了这个变量的归属(类),与之相对的是没有被static修饰的成员变量,也称为实例变量,说明这个变量是属于某个具体的对象。

static修饰的有状态,对象级创建对象都是新的。

③static节省内存(全局唯一)。

前端静态资源与java的static修饰的资源类比

web中什么是静态资源和动态资源

静态资源:html、css、images等,我们访问服务器地址时就直接拿编译好的静态的html,css文件,直接去渲染成页面,而js文件不是静态的,是动态的,js这个文件是我们在动态点击页面的时候随点随访问这个js文件,点击的时候触发响应的代码。

静态资源是一切的基础,要提前编译好,在动态资源显示前就渲染出来接收动态的数据。

image.png

java中static修饰的变量也可以看成是静态资源,而static修饰的方法可以看成是静态资源对外提供的接口,以便访问和调用静态资源,与web中的静态资源有异曲同工之妙,也是要在类初始化对象之前,在类加载时就开辟了内存空间,便于为类和对象的访问提供资源。

3、开发时,如何确定一个属性和方法是否要声明为static

属性:

-属性可以被多个对象所共享,不会随着对象的不同而不同
-类中的常量也常常声明为static

方法:

-操作静态属性的方法,通常设置为static的
-工具类中的方法,习惯上声明为static的,比如Math,Arrays,Collections

参考:Java中的static关键字解析 – Matrix海子 – 博客园

static关键字作用总结 – 罗纳德 – 博客园

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/160539.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 整理22道经典Java面试题,答对20k应该没问题!(建议收藏)

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 原文:https://blog.csdn.net/qq_21924011/article/details/80399…

  • 怎么在python中安装matplotlib_matplotlib依赖库

    怎么在python中安装matplotlib_matplotlib依赖库您也可以试试直接用第5步的pycharm安装试试,或许会更快。1.快捷键win+R输入cmd打开DOS命令框。2.虽然下载Python的时候自带有pip,但这里更新一下pip,输入更新pip命令:python-mpipinstall–upgradepip3.然后使用pip下载matplotlib:到这里如果直接使用命令:pipinstallmatplotlib基本很难成功。在安装模块时指定国内镜像:pipinstall包名-ihttp://pypi

  • SQL server分页的四种方法(算很全面了)

    SQL server分页的四种方法(算很全面了)  这篇博客讲的是SQLserver的分页方法,用的SQLserver2012版本。下面都用pageIndex表示页数,pageSize表示一页包含的记录。并且下面涉及到具体例子的,设定查询第2页,每页含10条记录。  首先说一下SQLserver的分页与MySQL的分页的不同,mysql的分页直接是用limit(pageIndex-1),pageSize就可以完成,但是SQLse…

  • 项目管理复习题

    项目管理复习题蓝字位注释,红字为错误原因,紫字为重点本复习题链接:https://pan.baidu.com/s/1ZJ4l6mKxAt9dqhw0Qa58xA提取码:j4jz第一章一、填空题1.敏捷模型包括(4)个核心价值,对应(12)个敏捷原则。2.项目管理包括(启动过程组)、(计划过程组)、(执行过程组)、(控制过程组)、(收尾过程组)5个过程组。二、判断题1、搬家属于项目。(√)2、项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的永久性的努力。(×)3、过程管理就是.

  • Anaconda 的安装教程(图文)「建议收藏」

    Anaconda的介绍Anaconda指的是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。因为包含了大量的科学包,Anaconda的下载文件比较大。这么说可能有点抽象,大家可以直接把Anaconda理解为一个python的傻瓜捆绑包就行了Anaconda下载下载地址:https://www.anaconda.com/download/…

  • futex验证_fulvic

    futex验证_fulvic1,验证代码转载#include#include#include#include#includesem_tsem_a;void*task1();intmain(void){ intret=0; pthread_tthrd1; sem_init(&sem_a,0,1);  //createchildrenpr

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号