【转载】java static 关键字的四种用法
原文链接点这里,感谢博主分享
在java的关键字中,static和final是两个我们必须掌握的关键字。不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构。下面我们先来了解一下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 中非常重要的一个关键字,而且它的用法也很丰富,主要有四种用法:
- 用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
- 用来修饰成员方法,将其变为类方法,可以直接使用 “类名.方法名” 的方式调用,常用于工具类;
- 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
- 静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。
【转载】java static 关键字的四种用法的更多相关文章
- [java]static关键字的四种用法
在java的关键字中,static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.下面我们先来了解一下stat ...
- 浅谈static关键字的四种用法
1.修饰成员变量 在一个person类中,一个成员变量例如 String name,当new2个person()对象时候,这2个对象在堆的位置是不同的,给name赋值张三.李四,这两个对象的name是 ...
- 【转载】java final 关键字的几种用法
原文链接点这里,感谢博主分享 在java的关键字中,static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构 ...
- [java]final关键字的几种用法
在java的关键字中,static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.下面我们来了解一下final ...
- Static 关键字的 5 种用法,你会几种?
); System.out.println(Thread.currentThread().getName() ...
- C# this关键字的四种用法
Go to below URL to find more details and example. http://blog.csdn.net/longlong821/article/details/7 ...
- C# this关键字的四种用法(转)
用法一 this代表当前类的实例对象 namespace Demo { public class Test { private string scope = "全局变量"; pu ...
- C#中 this关键字 四种用法
/// <summary> /// 主程序入口 /// </summary> /// <param name="args"></param ...
- java中 this 关键字的三种用法
Java中this的三种用法 调用属性 (1)this可以调用本类中的任何成员变量 调用方法(可省略) (2)this调用本类中的成员方法(在main方法里面没有办法通过this调用) 调用构造方法 ...
随机推荐
- BZOJ_1717_[Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组
BZOJ_1717_[Usaco2006 Dec]Milk Patterns 产奶的模式_后缀数组 Description 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他 ...
- linux文件的基本属性
Linux 文件基本属性 Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限.为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规 ...
- 安卓开发笔记(二十八):仿写IOS switch选择器控件实现,checkbox
我们先来看看效果: 这里我们主要使用了github上的一个开源项目,配置起来比较方便,下面解释一下该如何使用:首先是:Gradle文件当中进行配置: dependencies { implementa ...
- JS的 try catch使用心得
try{ //正常执行 }catch(e/*你感觉会出错的 错误类型*/){ // 可能出现的意外 eg:用户自己操作失误 或者 函数少条件 不影响下面的函数执行 // 有时也会用在 比如 focus ...
- Navicat:实现两个数据库结构同步和数据库对比
Navicat版本:Navicat Premium 12 选择 工具 ——> 结构同步 选择源数据库和目标数据库,选择完成后点击右下角对比按钮 要修改的对象:源数据库和目标数据库中都有的 ...
- Mybatis之旅第六篇-关联查询
一.引言 通过动态SQL我们可以进行复杂SQL的编写,但之前的例子都是单表查询,在实际开发中,当然不可能都是单表,很多时候我们需要进行关联多表查询(有些公司为了性能还是尽量的使用单表查询),表与表之间 ...
- entity cannot be tracked
背景:EF Core项目中使用InMemory作为数据库提供程序,编写单元测试. 报错:“The instance of entity type 'Movie' cannot be tracked b ...
- SpringBoot集成Lombok,应用+源码解析,让代码优雅起来
一.Lombok简介 (1)Lombok官网(https://projectlombok.org/)对lombok的介绍 (2)GitHub项目地址:https://github.com/rzwits ...
- MaxCompute 2.0复杂数据类型之array
1. 含义 类似于Java中的array.有序.可重复. 2. 场景 什么样的数据,适合使用array类型来存储呢?这里列举了几个我在开发中实际用到的场景. 2.1 标签类的数据 为什么说标签类数据适 ...
- [PHP]引用返回与节省内存
PHP中的引用是什么:1.在 PHP 中引用意味着用不同的名字访问同一个变量内容2.引用可以被看作是 Unix 文件系统中的硬链接. 3.使用unset的话,只是删除他这个名字自身对内容的引用,并没有 ...