深度理解Java中的static_java static-程序员宅基地

技术标签: 架构师成长之路  Java  新星计划  

目录

一、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虚拟机》)类的生命周期如图:


  加载:通过一个类的全限定名来获取定义此类的二进制字节流(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 代码 编译和执行过程_春天的早晨的博客-程序员宅基地

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

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

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

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关键字作用总结 - 罗纳德 - 博客园

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Ginny97/article/details/113957277

智能推荐

Ipa Guard软件介绍:启动界面和功能模块全解析,保护你的iOS应用源码-程序员宅基地

文章浏览阅读522次。界面分顶部的显示控制,中介的文件列表,底部的是否处理开关。签名配置界面可以配置签名证书,描述文件,设置app的权限(次功能大部分app是用不到的),设置混淆加密完是否要直接安装到设备。代码混淆界面随左侧的菜单不同略有区别。右侧主功能区会随着功能变化,但是整体分3块,顶部显示过滤区,中间主体内容显示区,底部开关控制和强度控制。这个界面可以选中打开文件,从配置加载文件,切换软件显示的语言,登录账号,查看软件的版本信息等。左侧菜单:按模块分成启动界面,代码模块,文件模块,重签名与测试模块。

Android:com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536-程序员宅基地

文章浏览阅读240次。错误信息如下:Error:Execution failed for task ':app:transformClassesWithDexForDebug'.&gt; com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concur...

第七重关:有依赖背包_价值有依赖背包应用-程序员宅基地

文章浏览阅读227次。问题描述:已知如下条件: N个物品容量为V的背包vi:第i个物品所占用的容量空间 valuei:第i个物品所获得的价值数值 但是包和包之间有依赖关系,例如,如果要是想选择A那么必须要先选择B(这里指考虑一重依赖关系,并且不会形成依赖环)求解: 该背包可以装下的最大价值是多少?解题思路:开始的思路还是一样的枚举每一件物品..._价值有依赖背包应用

Android-带你从源码角度理解SharedPreferences存储原理_android sp原理-程序员宅基地

文章浏览阅读2.8k次。SharedPreferences因非常适合存储较小键值集合数据且使用非常简单的特点,而受到广大程序员们热爱。_android sp原理

跨域的时候报“没有权限”错误-程序员宅基地

文章浏览阅读607次。转http://blog.csdn.net/wolfcyl/article/details/6612089加try{//报错代码}catch(e){}或try{//报错代码}catch(e){setTimeout(arguments.callee,0);}引用[code="java"] IE下使用location对象有时会出现“没有权限”的错误..._额外域提示没有权限

js实现图片缩放_图片缩放 data:image js-程序员宅基地

文章浏览阅读2k次。HTML代码<div id="imgDiv"> <img id="img"/></div>js代码//给图片绑定鼠标滚轮事件$("#img").addEventListener("mousewheel", function(){ var zoom = parseInt(self.viewImg.style.zoom, 10) || 100; zoom += event.wheelDelta / 12; if (zoom >= 1_图片缩放 data:image js

随便推点

spring5与jdk 版本要求_spring5.3 jetty版本-程序员宅基地

文章浏览阅读5.1k次。spring5.0** 在 2017 年 9 月发布了它的 GA(通用)版本。该版本是基于 jdk8 编写的, 所以 jdk8 以下版本将无法使用。 同时,可以兼容 jdk9 版本。 tomcat 版本要求 8.5 及以上 jdk要求jdk8以上。注: 我们使用 jdk8 构建工程,可以降版编译。但是不能使用 jdk8 以下版本构建工程。 由于 jdk 和 tomc_spring5.3 jetty版本

TI AWR1642 评估板 77G行人检测雷达代码分析(1)-main函数分析(MSS工程)_mmwave_mcb* ptrmmwavemcb =(mmwave_mcb*)gmmwmssmcb.-程序员宅基地

文章浏览阅读3.5k次,点赞10次,收藏37次。TI AWR1642 评估板 77G行人检测雷达代码分析(1)大家好,这里是电子与数学方法专栏,今天带领大家一起来分析 TI AWR1642评估板的行人检测代码,考虑到本代码的结构比较复杂,因此会分为很多部分进行拆解,走读完一个例程,可以举一反三,别的例程也就基本会了。那么下面我们就开始吧。首先我选择的例程是mmwave_industrial_toolbox_2_2_0\labs\lab0011-pplcount。在开始之前,我想大家应该能够自己配置好雷达,并能够实现开机演示,这些教程在这个例程的d_mmwave_mcb* ptrmmwavemcb =(mmwave_mcb*)gmmwmssmcb.ctrlhandle;

【THM】Burp Suite: The Basics(基础知识)-初级渗透测试-程序员宅基地

文章浏览阅读607次,点赞28次,收藏11次。撰写本文目的仅仅提供个人查看笔记用途,大家可以去官网查看,都一样的

socket阻塞和非阻塞的区别_delphi clientsocket阻塞和非阻塞-程序员宅基地

文章浏览阅读461次。socket阻塞和非阻塞的区别简单点说:阻塞就是干不完不准回来, 非组赛就是你先干,我现看看有其他事没有,完了告诉我一声我们拿最常用的send和recv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果T_delphi clientsocket阻塞和非阻塞

Tomcat源代码阅读系列之八:Tomcat 设计模式总结_设计模式根据源代码而分析得到总结报告-程序员宅基地

文章浏览阅读123次。本篇我们将来分析一下Tomcat中所涉及到设计模式,本文我们将主要来分析外观模式,观察者模式,责任链模式,模板方法模式,命令模式。 在开始本文之前,笔者先说明一下对于设计模式的一点看法。笔者曾经经常看到网上有人讨论设计模式,也偶尔会遇到有人非要严格按照GOF设计模式的类图以及其中的角色去套用别人的设计,只要类图不一样,或者角色多了或者少了就会觉得怎么和官方定义的模式不一样,其实这都是对设计模式的误..._设计模式根据源代码而分析得到总结报告

JSON格式转Map_json转map-程序员宅基地

文章浏览阅读9.3k次,点赞3次,收藏10次。JSON格式转Map。_json转map

推荐文章

热门文章

相关标签