static的用法,较比原文添加了static静态内部类。java
另外添加了静态内部类和非静态内部类的用法:jvm
--------------------------------------------------------函数
在java的关键字中,static和final是两个咱们必须掌握的关键字。不一样于其余关键字,他们都有多种用法,并且在必定环境下使用,能够提升程序的运行性能,优化程序的结构。下面咱们先来了解一下static关键字及其用法。工具
在咱们平时的使用当中,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构造出的每个对象都是独立存在的,保存有本身独立的成员变量,相互不会影响,他们在内存中的示意以下:spa
从上图中能够看出,p1和p2两个变量引用的对象分别存储在内存中堆区域的不一样地址中,因此他们之间相互不会干扰。但其实,在这当中,咱们省略了一些重要信息,相信你们也都会想到,对象的成员属性都在这了,由每一个对象本身保存,那么他们的方法呢?实际上,不论一个类建立了几个对象,他们的方法都是同样的:code
从上面的图中咱们能够看到,两个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修饰,因此从类外面没法随意改变。
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修饰的方法是属于类的,若是去直接使用对象的成员变量,它会不知所措(不知该使用哪个对象的属性)。
在说明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初始化
*///~
}
咱们将上一个例子稍微作了一下修改,能够看到,结果没有二致。
相比于上面的三种用途,第四种用途可能了解的人就比较少了,可是实际上它很简单,并且在调用类方法时会更方便。以上面的“PrintHelper”的例子为例,作一下稍微的变化,便可使用静态导包带给咱们的方便:
上面的代码来自于两个java文件,其中的PrintHelper很简单,包含了一个用于打印的static方法。而在App.java文件中,咱们首先将PrintHelper类导入,这里在导入时,咱们使用了static关键字,并且在引入类的最后还加上了“.*”,它的做用就是将PrintHelper类中的全部类方法直接导入。不一样于非static导入,采用static导入包后,在不与当前类的方法名冲突的状况下,无需使用“类名.方法名”的方法去调用类方法了,直接能够采用"方法名"去调用类方法,就好像是该类本身的方法同样使用便可。
若是一个类要被声明为static的,只有一种状况,就是静态内部类。
静态内部类实际上与普通类(即类名必须与文件名同样的顶级类)同样,只是静态内部类在某一类的内部定义了而已,既然是类,要想使用就必须实例化。概念上与静态变量、静态方法是不同的,不要被“静态”两个字迷惑了(不要觉得凡是静态的东西就不须要实例化就能够直接使用,静态内部类是有区别),并且只有静态内部类,而没有静态类(顶级类)的概念。
例子:
public class Singleton{ private Singleton(){} private static class SingletonHolder{ private final static Singleton instance; } public Singleton getInstance(){ return SingletonHolder.instance; } }
/* 下面程序演示如何在java中建立静态内部类和非静态内部类 */ class OuterClass{ private static String msg = "GeeksForGeeks"; // 静态内部类 public static class NestedStaticClass{ // 静态内部类只能访问外部类的静态成员 public void printMessage() { // 试着将msg改为非静态的,这将致使编译错误 System.out.println("Message from nested static class: " + msg); } } // 非静态内部类 public class InnerClass{ // 不论是静态方法仍是非静态方法均可以在非静态内部类中访问 public void display(){ System.out.println("Message from non-static nested class: "+ msg); } } } class Main { // 怎么建立静态内部类和非静态内部类的实例 public static void main(String args[]){ // 建立静态内部类的实例 OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass(); // 建立静态内部类的非静态方法 printer.printMessage(); // 为了建立非静态内部类,咱们须要外部类的实例 OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); // 调用非静态内部类的非静态方法 inner.display(); // 咱们也能够结合以上步骤,一步建立的内部类实例 OuterClass.InnerClass innerObject = new OuterClass().new InnerClass(); // 一样咱们如今能够调用内部类方法 innerObject.display(); } }
static是java中很是重要的一个关键字,并且它的用法也很丰富,主要有四种用法: