我们说Java是一种面向对象编程的语言,而对象是把数据及对数据的操作方法放在一起,作为一个相互依存的整体,对同类对象抽象出其共性,便是Java中的类,我们可以用类描述世间万物,也可以说万物皆对象。但是这里有个特殊的东西——static,它不属于对象,那么为什么呢?

  static 是Java的一个关键字,可以用来修饰成员变量、修饰成员方法、构造静态代码块、实现静态导包以及实现静态内部类,下面我们来分别介绍。

1、修饰成员变量

  用 static 修饰成员变量可以说是该关键字最常用的一个功能,通常将用 static 修饰的成员变量称为类成员或者静态成员,那么静态成员和不用 static 修饰的非静态成员有什么区别呢?

  我们先看看不用 static 修饰的成员变量在内存中的构造。

 package com.ys.bean;

 /**
* Create by YSOcean
*/
public class Person {
private String name;
private Integer age; public Person(String name, Integer age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//get和set方法省略
}

  首先,我们创建一个实体类 Person,有两个属性 name 和 age,都是普通成员变量(没有用 static 关键字修饰),接着我们通过其构造方法创建两个对象:

 Person p1 = new Person("Tom",21);
Person p2 = new Person("Marry",20);
System.out.println(p1.toString());//Person{name='Tom', age=21}
System.out.println(p2.toString());//Person{name='Marry', age=20}

  这两个对象在内存中的存储结构如下:

  

  由上图可知,我们创建的两个对象 p1 和 p2  存储在堆中,但是其引用地址是存放在栈中的,而且这两个对象的两个变量互相独立,我们修改任何一个对象的属性值,是不改变另外一个对象的属性值的。

  下面我们将 Person 类中的 age 属性改为由 static 关键字修饰:

 package com.ys.bean;

 /**
* Create by YSOcean
*/
public class Person {
private String name;
private static Integer age; public Person(String name, Integer age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//get和set方法省略 }

  同样我们还是向上面一样,创建 p1 和 p2 两个对象,并打印这两个对象,看看和上面打印的有啥区别呢?

 Person p1 = new Person("Tom",21);
Person p2 = new Person("Marry",20);
System.out.println(p1.toString());//Person{name='Tom', age=20}
System.out.println(p2.toString());//Person{name='Marry', age=20}

  我们发现第三行代码打印的 p1 对象 age 属性变为 20了,这是为什么呢?

  

  这是因为用在 jvm 的内存构造中,会在堆中开辟一块内存空间,专门用来存储用 static 修饰的成员变量,称为静态存储区,无论我们创建多少个对象,用 static 修饰的成员变量有且只有一份存储在静态存储区中,所以该静态变量的值是以最后创建对象时设置该静态变量的值为准,也就是由于 p1 先设置 age = 21,后来创建了 p2 对象,p2将 age 改为了20,那么该静态存储区的 age 属性值也被修改成了20。

  PS:在 JDK1.8 以前,静态存储区是存放在方法区的,而方法区不属于堆,在 JDK1.8 之后,才将方法区干掉了,方法区中的静态存储区改为到堆中存储。

  总结:static 修饰的变量被所有对象所共享,在内存中只有一个副本。由于与对象无关,所以我们可以直接通过 类名.静态变量 的方式来直接调用静态变量。对应的非静态变量是对象所拥有的,多少个对象就有多少个非静态变量,各个对象所拥有的副本不受影响。

2、修饰修饰成员方法

  用 static 关键字修饰成员方法也是一样的道理,我们可以直接通过 类名.静态方法名() 的方式来调用,而不用创建对象。

 public class Person {
private String name;
private static Integer age; public static void printClassName(){
System.out.println("com.ys.bean.Person");
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//get和set方法省略 }

  调用静态方法:

 Person.printClassName();//com.ys.bean.Person

3、静态代码块

  用 static 修饰的代码块称为静态代码块,静态代码块可以置于类的任意一个地方(和成员变量成员方法同等地位,不可放入方法中),并且一个类可以有多个静态代码块,在类初次载入内存时加载静态代码块,并且按照声明静态代码块的顺序来加载,且仅加载一次,优先于各种代码块以及构造函数。

  关于静态代码块、构造代码块、构造函数、普通代码块的区别可以参考我的这篇博客

 public class CodeBlock {
static{
System.out.println("静态代码块");
}
}

  由于静态代码块只在类载入内存时加载一次的特性,我们可以利用静态代码块来优化程序性能,比如某个比较大配置文件需要在创建对象时加载,这时候为了节省内存,我们可以将该配置文件的加载时机放入到静态代码块中,那么我们无论创建多少个对象时,该配置文件也只加载了一次。

4、静态导包

  用 static 来修饰成员变量,成员方法,以及静态代码块是最常用的三个功能,静态导包是 JDK1.5以后的新特性,用 import static 包名 来代替传统的 import 包名 方式。那么有什么用呢?

  比如我们创建一个数组,然后用 JDK 自带的 Arrays 工具类的 sort 方法来对数组进行排序:

 package com.ys.test;

 import java.util.Arrays;
/**
* Create by YSOcean
*/
public class StaticTest { public static void main(String[] args) {
int[] arrays = {3,4,2,8,1,9};
Arrays.sort(arrays);
}
}

  我们可以看到,调用 sort 方法时,需要进行 import java.util.Arrays 的导包操作,那么变为静态导包呢?

 package com.ys.test;

 import static java.util.Arrays.*;
/**
* Create by YSOcean
*/
public class StaticTest { public static void main(String[] args) {
int[] arrays = {3,4,2,8,1,9};
sort(arrays);
}
}

  我们可以看到第三行代码的 import java.util.Arrays 变为了 import static  java.util.Arrays.*,意思是导入 Arrays 类中的所有静态方法,当然你也可以将 * 变为某个方法名,也就是只导入该方法,那么我们在调用该方法时,就可以不带上类名,直接通过方法名来调用(第 11 行代码)。

  静态导包只会减少程序员的代码编写量,对于性能是没有任何提升的(也不会降低性能,Java核心技术第10版卷1第148页4.7.1章节类的导入有介绍),反而会降低代码的可读性,所以实际如何使用需要权衡。

5、静态内部类

  首先我们要知道什么是内部类,定义在一个类的内部的类叫内部类,包含内部类的类叫外部类,内部类用 static 修饰便是我们所说的静态内部类。

  定义内部类的好处是外部类可以访问内部类的所有方法和属性,包括私有方法和私有属性。

  访问普通内部类,我们需要先创建外部类的对象,然后通过外部类名.new 创建内部类的实例。

 package com.ys.bean;

 /**
* Create by hadoop
*/
public class OutClass { public class InnerClass{ }
}
  * OuterClass oc = new OuterClass();
* OuterClass.InnerClass in = oc.new InnerClass();

  访问静态内部类,我们不需要创建外部类的对象,可以直接通过 外部类名.内部类名 来创建实例。

 package com.ys.bean;

 /**
* Create by hadoop
*/
public class OutClass { public static class InnerClass{ }
}
 OuterClass.StaticInnerClass sic = new OuterClass.StaticInnerClass();

6、常见问题

  ①、静态变量能存在于普通方法中吗?

  能。很明显,普通方法必须通过对象来调用,静态变量都可以直接通过类名来调用了,更不用说通过对象来调用,所以是可以存在于普通方法中的。

  ②、静态方法能存在普通变量吗?

  不能。因为静态方法可以直接通过类名来直接调用,不用创建对象,而普通变量是必须通过对象来调用的。那么将普通变量放在静态方法中,在直接通过类来调用静态方法时就会报错。所以不能。

  ③、静态代码块能放在方法体中吗?

  不能。首先我们要明确静态代码块是在类加载的时候自动运行的。

  普通方法需要我们创建对象,然后手工去调用方法,所静态代码块不能声明在普通方法中。

  那么对于用 static 修饰的静态方法呢?同样也是不能的。因为静态方法同样也需要我们手工通过类名来调用,而不是直接在类加载的时候就运行了。

  也就是说静态代码块能够自动执行,而不管是普通方法还是静态方法都是需要手工执行的。

  ④、静态导包会比普通导包消耗更多的性能?

  不会。静态导包实际上在编译期间都会被编译器进行处理,将其转换成普通按需导包的形式,所以在程序运行期间是不影响性能的。

  ⑤、static 可以用来修饰局部变量吗?

  不能。不管是在普通方法还是在静态方法中,static 关键字都不能用来修饰局部变量,这是Java的规定。稍微想想也能明白,局部变量的声明周期是随着方法的结束而结束的,因为static 修饰的变量是全局的,不与对象有关的,如果用 static 修饰局部变量容易造成理解上的冲突,所以Java规定 static 关键字不能用来修饰局部变量。

Java关键字(三)——static的更多相关文章

  1. java 关键字final static native详解

    java 关键字native static final详解 一.final 根据程序上下文环境,Java关键字final有"这是无法改变的"或者"终态的"含义, ...

  2. Java关键字:static

    通常,当创建类时,就是在描述那个类的外观和行为.只有用new创建类的对象时,才分配数据存储空间,方法才能被调用.但往往我们会有下面两种需求: 1.我想要这样一个存储空间:不管创建多少对象,无论是不创建 ...

  3. Java关键字之static

    static 表示"全局"或者"静态"的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被stati ...

  4. Java关键字之static的典型用法分析

    static关键字是java中非常重要的一个关键字,用的好的话可以提高程序的运行性能,优化程序结构.接下来我们来总结一下static关键字及其用法.1.static变量 static变量也称作静态变量 ...

  5. Java关键字final、static使用总结(转)

    原文链接:Java关键字final, static一些要点 1. final   final类不能被继承,没有子类,final类中的方法默认是final的 final方法不能被子类的方法复盖,但可以被 ...

  6. Java关键字final、static

    一.final根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方法和变量.你可能出于两种理解而需要阻止改变:设计或效率. final ...

  7. (转)Java中的static关键字解析

    转载: http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: &q ...

  8. Java中的static关键字解析

    Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...

  9. 【转】Java关键字final、static使用总结

    转自:http://lavasoft.blog.51cto.com/62575/18771/   Java关键字final.static使用总结   一.final        根据程序上下文环境, ...

随机推荐

  1. Android 滑动定位+吸附悬停效果实现

    在前两篇文章中,分别介绍了tablayout+scrollview 和 tablayout+recyclerview 实现的滑动定位的功能,文章链接: Android 实现锚点定位 Android t ...

  2. Vue之resource请求数据

    导入resource文件 <script src="https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js& ...

  3. java8中Lambda表达式和Stream API

    一.Lambda表达式 1.语法格式 Lambda是匿名函数,可以传递代码.使用“->”操作符,改操作符将lambda分成两部分: 左侧:指定了 Lambda 表达式需要的所有参数 右侧:指定了 ...

  4. (后台)SQL Server 代理(已禁用代理 XP) 怎么解决(转)

    百度知道搜索的答案: 在SQL Server Management Studio中连接到SQL Server实例后,会显示“SQL Server 代理”节点.如果当前该实例的Agent服务没有启动,“ ...

  5. [20181122]模拟ORA-08103错误.txt

    [20181122]模拟ORA-08103错误.txt $ oerr ora 810308103, 00000, "object no longer exists"// *Caus ...

  6. Python输出和输入

    一.input()函数 在 Python 中,使用内置函数 input()可以接收用户的键盘输入. input()函数的基本用法如 下: variable = input("提示文字") 其中,var ...

  7. Linux进程调度器的设计--Linux进程的管理与调度(十七)

    1 前景回顾 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为 ...

  8. 两个Map的对比,三种方法,将对比结果写入文件。

    三种方法的思维都是遍历一个map的Key,然后2个Map分别取这2个Key值所得到的Value. #第一种用entry private void compareMap(Map<String, S ...

  9. DubboAdmin部署

    1.软件下载 部署管理后台和监控中心需要以下软件 opensesame  下载地址:https://github.com/alibaba/opensesame Dubbo源码下载  https://g ...

  10. Windows7安装Bitvise开启ssh服务

    Windows7安装Bitvise开启ssh服务 by:铁乐猫 在Liunx和windows10上配置SSH服务是一件很容易的事,毕竟系统己经自带了ssh的服务功能. 不过在windows7上可不容易 ...