当前位置: 首页 > 编程日记 > 正文

static关键字用法

static关键字

1.修饰成员变量

在我们平时的使用当中,static最常用的功能就是修饰类的属性和方法,让他们成为类的成员属性和方法,我们通常将用static修饰的成员称为类成员或者静态成员,这句话挺起来都点奇怪,其实这是相对于对象的属性和方法来说的。请看下面的例子:(未避免程序太过臃肿,暂时不管访问控制)

复制代码
public class Person {String name;int age;public String toString() {return "Name:" + name + ", Age:" + age;}public static void main(String[] args) {Person p1 = new Person();p1.name = "zhangsan";p1.age = 10;Person p2 = new Person();p2.name = "lisi";p2.age = 12;System.out.println(p1);System.out.println(p2);}/**Output* Name:zhangsan, Age:10* Name:lisi, Age:12*///~
}
复制代码

上面的代码我们很熟悉,根据Person构造出的每一个对象都是独立存在的,保存有自己独立的成员变量,相互不会影响,他们在内存中的示意如下:

从上图中可以看出,p1和p2两个变量引用的对象分别存储在内存中堆区域的不同地址中,所以他们之间相互不会干扰。但其实,在这当中,我们省略了一些重要信息,相信大家也都会想到,对象的成员属性都在这了,由每个对象自己保存,那么他们的方法呢?实际上,不论一个类创建了几个对象,他们的方法都是一样的:

从上面的图中我们可以看到,两个Person对象的方法实际上只是指向了同一个方法定义。这个方法定义是位于内存中的一块不变区域(由jvm划分),我们暂称它为静态存储区。这一块存储区不仅存放了方法的定义,实际上从更大的角度而言,它存放的是各种类的定义,当我们通过new来生成对象时,会根据这里定义的类的定义去创建对象。多个对象仅会对应同一个方法,这里有一个让我们充分信服的理由,那就是不管多少的对象,他们的方法总是相同的,尽管最后的输出会有所不同,但是方法总是会按照我们预想的结果去操作,即不同的对象去调用同一个方法,结果会不尽相同。

我们知道,static关键字可以修饰成员变量和方法,来让它们变成类的所属,而不是对象的所属,比如我们将Person的age属性用static进行修饰,结果会是什么样呢?请看下面的例子:

复制代码
public class Person {String name;static int age;/* 其余代码不变... *//**Output* Name:zhangsan, Age:12* Name:lisi, Age:12*///~
}
复制代码

我们发现,结果发生了一点变化,在给p2的age属性赋值时,干扰了p1的age属性,这是为什么呢?我们还是来看他们在内存中的示意:

我们发现,给age属性加了static关键字之后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性,一个对象如果对age属性做了改变,其他的对象都会受到影响。我们看到此时的age和toString()方法一样,都是交由类去管理。

虽然我们看到static可以让对象共享属性,但是实际中我们很少这么用,也不推荐这么使用。因为这样会让该属性变得难以控制,因为它在任何地方都有可能被改变。如果我们想共享属性,一般我们会采用其他的办法:

复制代码
public class Person {private static int count = 0;int id;String name;int age;public Person() {id = ++count;}public String toString() {return "Id:" + id + ", Name:" + name + ", Age:" + age;}public static void main(String[] args) {Person p1 = new Person();p1.name = "zhangsan";p1.age = 10;Person p2 = new Person();p2.name = "lisi";p2.age = 12;System.out.println(p1);System.out.println(p2);}/**Output* Id:1, Name:zhangsan, Age:10* Id:2, Name:lisi, Age:12*///~
}
复制代码

上面的代码起到了给Person的对象创建一个唯一id以及记录总数的作用,其中count由static修饰,是Person类的成员属性,每次创建一个Person对象,就会使该属性自加1然后赋给对象的id属性,这样,count属性记录了创建Person对象的总数,由于count使用了private修饰,所以从类外面无法随意改变。

2.修饰成员方法

static的另一个作用,就是修饰成员方法。相比于修饰成员属性,修饰成员方法对于数据的存储上面并没有多大的变化,因为我们从上面可以看出,方法本来就是存放在类的定义当中的。static修饰成员方法最大的作用,就是可以使用"类名.方法名"的方式操作方法,避免了先要new出对象的繁琐和资源消耗,我们可能会经常在帮助类中看到它的使用:

复制代码
public class PrintHelper {public static void print(Object o){System.out.println(o);}public static void main(String[] args) {PrintHelper.print("Hello world");}
}
复制代码

上面便是一个例子(现在还不太实用),但是我们可以看到它的作用,使得static修饰的方法成为类的方法,使用时通过“类名.方法名”的方式就可以方便的使用了,相当于定义了一个全局的函数(只要导入该类所在的包即可)。不过它也有使用的局限,一个static修饰的类中,不能使用非static修饰的成员变量和方法,这很好理解,因为static修饰的方法是属于类的,如果去直接使用对象的成员变量,它会不知所措(不知该使用哪一个对象的属性)。

3.静态块

在说明static关键字的第三个用法时,我们有必要重新梳理一下一个对象的初始化过程。以下面的代码为例:

复制代码
package com.dotgua.study;class Book{public Book(String msg) {System.out.println(msg);}
}public class Person {Book book1 = new Book("book1成员变量初始化");static Book book2 = new Book("static成员book2成员变量初始化");public Person(String msg) {System.out.println(msg);}Book book3 = new Book("book3成员变量初始化");static Book book4 = new Book("static成员book4成员变量初始化");public static void main(String[] args) {Person p1 = new Person("p1初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}
复制代码

上面的例子中,Person类中组合了四个Book成员变量,两个是普通成员,两个是static修饰的类成员。我们可以看到,当我们new一个Person对象时,static修饰的成员变量首先被初始化,随后是普通成员,最后调用Person类的构造方法完成初始化。也就是说,在创建对象时,static修饰的成员会首先被初始化,而且我们还可以看到,如果有多个static修饰的成员,那么会按照他们的先后位置进行初始化。

实际上,static修饰的成员的初始化可以更早的进行,请看下面的例子:

复制代码
class Book{public Book(String msg) {System.out.println(msg);}
}public class Person {Book book1 = new Book("book1成员变量初始化");static Book book2 = new Book("static成员book2成员变量初始化");public Person(String msg) {System.out.println(msg);}Book book3 = new Book("book3成员变量初始化");static Book book4 = new Book("static成员book4成员变量初始化");public static void funStatic() {System.out.println("static修饰的funStatic方法");}public static void main(String[] args) {Person.funStatic();System.out.println("****************");Person p1 = new Person("p1初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* static修饰的funStatic方法* **************** book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}
复制代码

在上面的例子中我们可以发现两个有意思的地方,第一个是当我们没有创建对象,而是通过类去调用类方法时,尽管该方法没有使用到任何的类成员,类成员还是在方法调用之前就初始化了,这说明,当我们第一次去使用一个类时,就会触发该类的成员初始化。第二个是当我们使用了类方法,完成类的成员的初始化后,再new该类的对象时,static修饰的类成员没有再次初始化,这说明,static修饰的类成员,在程序运行过程中,只需要初始化一次即可,不会进行多次的初始化。

回顾了对象的初始化以后,我们再来看static的第三个作用就非常简单了,那就是当我们初始化static修饰的成员时,可以将他们统一放在一个以static开始,用花括号包裹起来的块状语句中:

复制代码
class Book{public Book(String msg) {System.out.println(msg);}
}public class Person {Book book1 = new Book("book1成员变量初始化");static Book book2;static {book2 = new Book("static成员book2成员变量初始化");book4 = new Book("static成员book4成员变量初始化");}public Person(String msg) {System.out.println(msg);}Book book3 = new Book("book3成员变量初始化");static Book book4;public static void funStatic() {System.out.println("static修饰的funStatic方法");}public static void main(String[] args) {Person.funStatic();System.out.println("****************");Person p1 = new Person("p1初始化");}/**Output* static成员book2成员变量初始化* static成员book4成员变量初始化* static修饰的funStatic方法* **************** book1成员变量初始化* book3成员变量初始化* p1初始化*///~
}
复制代码

我们将上一个例子稍微做了一下修改,可以看到,结果没有二致。

4.静态导包

相比于上面的三种用途,第四种用途可能了解的人就比较少了,但是实际上它很简单,而且在调用类方法时会更方便。以上面的“PrintHelper”的例子为例,做一下稍微的变化,即可使用静态导包带给我们的方便:

复制代码
/* PrintHelper.java文件 */
package com.dotgua.study;public class PrintHelper {public static void print(Object o){System.out.println(o);}
}
复制代码
复制代码
/* App.java文件 */

import static com.dotgua.study.PrintHelper.*;public class App {public static void main( String[] args ){print("Hello World!");}/**Output* Hello World!*///~ }
复制代码

上面的代码来自于两个java文件,其中的PrintHelper很简单,包含了一个用于打印的static方法。而在App.java文件中,我们首先将PrintHelper类导入,这里在导入时,我们使用了static关键字,而且在引入类的最后还加上了“.*”,它的作用就是将PrintHelper类中的所有类方法直接导入。不同于非static导入,采用static导入包后,在不与当前类的方法名冲突的情况下,无需使用“类名.方法名”的方法去调用类方法了,直接可以采用"方法名"去调用类方法,就好像是该类自己的方法一样使用即可。

总结

static是java中非常重要的一个关键字,而且它的用法也很丰富,主要有四种用法:

  1. 用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
  2. 用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
  3. 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
  4. 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。

转载于:https://www.cnblogs.com/HaveChen/p/9081538.html

相关文章:

swift x输入流_SwiftUI 探索 - 状态和数据流

SwiftUI是iOS13新出的声明式UI框架,将会完全改变以前命令式操作UI的开发方式。此文章主要介绍SwiftUI中状态管理的方式。可变状态State与React和Flutter中的State类似,只不过React和Flutter中需要显式调用setState方法。在SwiftUI 中直接修改State属性值…

qt 控件 背景色 透明 除去边框

在调试ui的时候,需要将背景色变为透明,与母控件的颜色一致,并且除去边框。 参考链接: http://www.qtcentre.org/threads/12148-how-QTextEdit-transparent-to-his-parent-window 除去背景色,使透明。ui->textBrowse…

A Strange Bitcoin Transaction

链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载。 在之前的谈谈比特币的地址安全问题这篇文章中,我们谈到一个名为”LBC”的项目,这个项目通过暴力碰撞企图打捞到一些什么东西…

Jsoncpp 使用方法解析

Jsoncpp是目前比较好用的开源Json解析库,现在总结一下它的使用方法,理解,以供以后查阅。 在引入Jsoncpp的时候我们看到Jsoncpp里边的常用的cpp文件,有json_reader.cpp,json_value.cpp,json_writer.cpp, 其中: json_val…

tomcat限速_WEB服务的下载限速(二)(限速模块安装与配置)

一、准备工作1、下载mod_bw-0.92.tgz2、安装httpd-develyum install httpd-devel二、安装限速模块tar -xvf mod_bw-0.92.tgzapxs -c -i -a mod_bw.c三、配置apachevim /usr/local/apache/conf/httpd.conf查看是否已加载 LoadModule bw_module modules/mod_bw.so如果没…

EntityFrameworkCore 安装

映射现有(多个)数据库: 安装Microsoft.EntityFrameworkCore最新版本Tools -> NuGet Package Manager -> Package Manager Console 分别输入: Scaffold-DbContext "Server.;DatabaseSxh;Trusted_ConnectionTrue;" …

python神秘的魔法函数_Python魔法函数

1.什么是魔法函数魔法函数即Python类中以__(双下划线)开头,以__(双下划线)结尾的函数,Python提供的函数,可让咱们随意定义类的特性示例:class Company(object):def __init__(self, employee_list):self.employee employee_listde…

HDU-4738-Caocao's Bridges(tarjan)

转载于:https://www.cnblogs.com/GrowingJlx/p/6642692.html

博客园美化技巧汇总

首先得有js权限 1.1 页脚js代码 <script type"text/javascript"> /*功能&#xff1a;生成博客目录的JS工具测试&#xff1a;IE8&#xff0c;火狐&#xff0c;google测试通过zhang_derek2018-01-03 */ var BlogDirectory {/*获取元素位置&#xff0c;距浏览器左…

数据事务四种隔离机制和七种传播行为

数据事务四种隔离机制和七种传播行为 一、隔离级别&#xff1a; 数据库事务的隔离级别有4个&#xff0c;由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable&#xff0c;这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。 &#xff11;.…

vue注册新节点_vue怎么重新组装slots节点

在后台列表中通常会有比较多的操作按钮 过多的按钮影响布局 也影响操作 因此想通过vue的组件来控制显示的按钮个数 多余的按钮自动被收进一个特殊的 更多 按钮里面&#xff0c;效果图&#xff1a;组件定义&#xff1a;Vue.component(button-groups, {render(createElement) {re…

什么是EOS?

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 关于EOS有很多炒作。2017年5月&#xff0c;EOS的创始人丹•拉莫(Dan Larimer)在纽约宣布这一消息时&#xff0c;一幅巨大的巨型屏幕广告在时代广场上…

【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和...

题意&#xff1a;给出N个数&#xff0c;M个操作。操作有修改和询问两种&#xff0c;每次修改将一个数改成另一个数&#xff0c;每次询问一个区间的所有连续子区间的异或和。n,m<100000,ai<1000 题解&#xff1a; 当年&#xff08;其实也就是今年&#xff09;做不出来的题…

用composer安装laravel-bjyblog

前面讲了两行命令composer的安装&#xff0c;现在我们来操作一下composer安装基于laravel的博客laravel-bjyblog。测试环境是linux&#xff0c;bt面板&#xff0c;php7.2安装扩展fileinfo/opcache/redis/imagemagick/imap/exif&#xff0c;禁用 proc_open 函数 下面开始安装&am…

微信小程序多项选择器_微信小程序三级联动之多列选择器

有些时候&#xff0c;三级联动业务场景并不只是全国地区选择&#xff0c;可能还涉及到自定义分类的三级联动&#xff0c;这时就需要使用微信的多列选择器。如果只是一列字段&#xff0c;或者每次拖动一次都去服务端取&#xff0c;会比较容易。 如果想一次定义好json,关联数据相…

eosjs-ecc中文文档

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 eosjs-ecc是eos官方处理密钥和签名的javascript开发包。访问地址&#xff1a;eosjs-ecc中文手册。 eosjs-ecc安装 nodejs环境下&#xff0c;使用N…

rocketmq 组监听_最全的RocketMQ学习指南,程序员必备的中间件技能

一、简介RocketMq是阿里开发出来的一个消息中间件&#xff0c;后捐献给Apache。官网上是这样介绍的&#xff1a; Apache RocketMQ™ is a unified messaging engine, lightweight data processing platform.RocketMQ是一个统一的处理消息引擎&#xff0c;轻量级的数据处理平台。…

【刷题】BZOJ 4516 [Sdoi2016]生成魔咒

Description 魔咒串由许多魔咒字符组成&#xff0c;魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。 一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。 例如 S[1,2,1] 时&#xff0c;它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种…

jQuery的文档操作方法

jQuery 文档操作方法 这些方法对于 XML 文档和 HTML 文档均是适用的&#xff0c;除了&#xff1a;html()。 方法描述addClass()向匹配的元素添加指定的类名。after()在匹配的元素之后插入内容。append()向匹配元素集合中的每个元素结尾插入由参数指定的内容。appendTo()向目标结…

原 EOS智能合约开发入门

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 EOS智能合约的开发需要使用llvm和abigen来生成abi文件。 为此eos提供了一个 名为eosiocpp的工具。 在这篇文章中&#xff0c;我们介绍如何使用这个工…

python安装虚拟环境virtualenv

虚拟环境 虚拟环境是一个将不同项目所需求的依赖分别放在独立的地方的一个工具&#xff0c;它给这些工程创建虚拟的Python环境。它解决了“项目X依赖于版本1.x&#xff0c;而项目Y需要项目4.x”的两难问题&#xff0c;而且使你的全局site-packages目录保持干净和可管理。 比如&…

可变分区存储管理实验报告总结_操作系统实验报告-可变分区存储管理方式的内存分配回收...

一&#xff0e;实验目的(1)深入了解可变分区存储管理方式的内存分配回收的实现。二&#xff0e;实验内容编写程序完成可变分区存储管理方式的内存分配回收&#xff0c;要求有内存空间分配表&#xff0c;并采用最优适应算法完成内存的分配与回收。三&#xff0e;实验原理在可变分…

ubuntu/linuxmint如何添加和删除PPA源

【添加】 1、sudo add-apt-repository ppa:user/ppa-name 2、sudo apt-get update (然后再安装软件sudo apt-get install <package-name>或更新软件sudo apt-get upgrade) 【删除】 1、cd /etc/apt/source.list.d/ 2、sudo rm <ppa-name> 转载于:https://www.cnblo…

了解EOS看这一篇就够了一、团队二、技术三、项目进度四、争议和风险五、展望

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 无论是混迹于币圈、链圈还是矿圈&#xff0c;对BTC&#xff08;比特币&#xff09;、ETH&#xff08;以太坊&#xff09;、EOS这三大主流币一定不会…

qt 多个模型如何显示在表格中_Qt MOOC系列教程 第五章第四节:QML中的C++模型

我们已经多次讨论过如何创建自己的模型来表示QML中的数据&#xff0c;并且在上一节中我们看到了QStandardItemModel的基本示例。通常&#xff0c;出于性能和功能方面的原因&#xff0c;需要从一开始就要实现自己的模型。QAbstactItemModel类为项目模型类提供了抽象接口&#xf…

ubuntu终端基础命令

1. 启动终端的快捷键: ctr alt t2. 终端字体放大: ctrshift3. 终端字体放大: ctr-4. ls : 查看当前目录的文件信息 4.1 ls 路径&#xff1a; 查看指定目录的信息 4.1. pwd: 查看目录所在的路径5. touch: 创建文件 5.1 touch 1.txt 2.txt 创建多个文件6. mkdir: 创建…

js中操作数组的一些方法

增 push 在数组的末尾添加一个或多个元素&#xff0c;并返回新的长度。 array.push(1,2,3.........) unshift 在数组的开头添加一个或多个元素&#xff0c;并返回新的长度。 array.unshift(1,2,3......) splice 在制定位置添加一个活多个元素&#xff0c;splice(s…

a标签怎么传参_jsp页面中怎么利用a标签的href进行传递参数以及需要注意的地方...

jsp页面中&#xff1a;这是正确写法。需要注意的地方&#xff1a;1、传递的参数是数字2、传递的参数是字符串注意多了个单引号后台直接用request.getParameter("productIdStr"); 接收就可以了。此处也有要注意的地方&#xff1a;接收后要进行判空&#xff0c;否则会报…

转】windows下使用批处理脚本实现多个版本的JDK切换

原博文出自于:  http://www.cnblogs.com/xdp-gacl/p/5209386.html      感谢&#xff01; 一.JDK版本切换批处理脚本 我们平时在window上做开发的时候&#xff0c;可能需要同时开发两个甚至多个项目&#xff0c;有时不同的项目对JDK的版本要求有区别&#xff0c;这时候…

mysql引擎介绍

1.myisam存储引擎&#xff1a;不支持事务&#xff0c;也不支持外键&#xff0c;优势是访问速度快&#xff0c;对事务完整性没有要求或者以select&#xff0c;insert为主的应用基本上可以用这个引擎来创建表。 2.innodb存储引擎&#xff1a;innodb引擎提供了具有提交&#xff0c…