关于java关键字static

来源:互联网 发布:mac qq如何上传群文件 编辑:程序博客网 时间:2024/06/05 15:40

1. static关键字

在java中,static可以用来修饰类,方法,变量,代码块。static修饰的方法,变量属于类,可以通过类直接调用,也可以通过类的对象来调用。static修饰的代码块在类加载的时候就会加载执行,主要用来初始化一些固定变量。
用static修饰就是为了在没有创建对象的情况下调用方法或者变量,以及静态代码块优化程序性能。

2. static修饰的类

普通类是不允许声明为静态的,只有内部类才可以。被static修饰的内部类就属于外部类本身,而不属于某各类的对象,可以直接作为一个普通类来使用。static关键字的作用是把类的成员变成类相关,就是static修饰的成员属于整个类而不是属于单个对象,外部类的上一级程序单元是包,所以不能使用static修饰。
静态内部类可以包含静态成员也可以包含非静态成员,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。

public class StaticClass {    private int stac1=5;    private static int stac2=9;    static class StaticInnerClass{        //静态内部类可以包含静态成员        private static int age;        public void accessOuterStac(){            //下面一行代码报错:Cannot make a static reference to the non-static field stac1            //静态内部类无法访问外部类的实例变量            System.out.println(stac1);            System.out.println(stac2);        }    }}

静态内部类是外部类的一个静态成员,因此外部类的所有方法,所有初始化块中都可以使用静态内部类来定义变量创建对象等,但是外部类依然不能直接访问静态内部类的成员,单可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员,例如:

public class StaticClass {    private int stac1=5;    private static int stac2=9;    static class StaticInnerClass{        private static int age1 = 5;        private int age2 = 9;    }    public void accessInnerProp(){        System.out.println(StaticInnerClass.age1);        System.out.println(new StaticInnerClass().age1);    }}

除此之外,java还允许在接口里边定义内部类,接口里边定义的内部类默认使用public static修饰,也就是说接口内部类只能是静态内部类。如果为接口内部类制定访问控制符,则只能是public,如果省略访问控制符,则该内部类默认是public修饰。

3. static修饰的方法

static方法一般称作静态方法,静态方法是属于累的,不依赖于任何对象就可以进行访问,所以对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,就是static修饰的方法不能访问没有static修饰的方法和变量,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
  但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。例如:

public class StaticTest {    private static String strA = "static property";//静态成员变量    private String strB = "property";//非静态成员变量    public StaticTest(){//无参构造函数    }    public void testStaticA(){//非静态方法        /*         * 非静态方法可以访问静态成员变量和静态方法         */        System.out.println(strA);        System.out.println(strB);        testStaticB();    }    public static void testStaticB(){//静态方法        /*         * 静态方法可以访问静态成员变量,         * 但是不可以访问非静态方法和非静态成员变量         */        System.out.println(strA);        System.out.println(strB);//此处报错:Cannot make a static reference to the non-static field strB        testStaticA();//此处报错:Cannot make a static reference to the non-static method testStaticA() from the type StaticTest    }    /*     * main方法是static修饰,所以可以访问静态成员。     * 如果要访问非静态成员,需要通过对象。     * main方法是程序的入口,在调用main方法是还没有产生类的对象,所以main方法要用static修饰     */    public static void main(String[] args) {        testStaticB();        new StaticTest().testStaticA();        System.out.println(strB);//不可以访问非静态成员,此处报错:Cannot make a static reference to the non-static field strB    }}

在上面的代码中,testStaticB方法用static修饰,是独立于对象存在的,可以直接用类名调用,在类初始化的时候根本就不存在strB变量,所以会报错。
而在testStaticB中调用testStaticA方法,此时根本就不知道testStaticA方法是否访问了非静态成员,也就是说有可能访问了非静态成员,但是此时根本不存在对象,也就没有非静态成员,就会造成编译错误。
而对于非静态成员方法,它访问静态成员方法/变量显然是毫无限制的。
如果想不创建对象就访问某个方法或者变量,那就用static修饰,大部分的工具类就是这样的.
我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
另外记住,即使没有显示地声明为static,类的构造器实际上也是静态方法。

4. static修饰的变量

在java语言中,变量可以分为成员变量和局部变量。
这里写图片描述
成员变量指的是在类里定义的变量,局部变量指的是在方法里定义的变量。
成员变量被分为类变量和实力变量两种,定义成员变量时没有用static修饰的就是实例变量,否则就是类变量,其中类变量从该类的准备阶段开始存在,直到系统销毁这个类,类变量的作用域与这个类的生存范围相同;而实例变量则从该类的实例被创建开始存在,直到系统完全销毁这个实例,实例变量的作用域与对应实例的生存范围相同。
用static修饰的变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。

5. static修饰的代码块

static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
static修饰的代码块与构造器的作用非常类似,就是对java对象进行初始化操作,所以被叫做初始化块,初始化块是java类里可以出现的地4中成员(成员变量,方法,构造器),一个类里可以有多个初始化块,初始化块的修饰符只能是static,被称为静态初始化块,也叫作类初始化快块,负责对类进行初始化,如果没有修饰符,是普通初始化块,负责对对象执行初始化。静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是在创建对象时才执行,因此静态初始化块总是比普通初始化块先执行,又比构造器先执行。静态初始化块不能对实例变量进行初始化处理。
static块可以用来优化程序性能,就是因为它的特性:只会在类加载的时候执行一次。例如:

import java.sql.Date;public class StaticCodePerson {    private Date birthDate;    public StaticCodePerson(Date birthDate) {        this.birthDate = birthDate;    }    boolean isBornBetween() {        //用来判断这个人是否是1946-1964年出生的,而每次isBornBetween被调用的时候,都会生成startDate和birthDate两个对象,造成了空间浪费        Date startDate = Date.valueOf("1946");        Date endDate = Date.valueOf("1964");        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;    }}

改成下面这样效率会更好:

import java.sql.Date;public class StaticCodePerson {    private Date birthDate;    private static Date startDate,endDate;    static{        //静态初始化块在类准备阶段就会初始化这两个变量        //而且只执行一次        startDate = Date.valueOf("1946");        endDate = Date.valueOf("1964");    }    public StaticCodePerson(Date birthDate) {        this.birthDate = birthDate;    }    boolean isBornBetween() {        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;    }}

从上面两段代码可以看出,讲台初始化块可以很大程度提高程序性能,因此很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。例如数据库连接的基本连接信息。
实际上,初始化块是一个假象,使用javac命令编译java类后,java类中的初始化块会消失–初始化块中代码会被还原到每个构造器中,并且位于构造器所有代码的前面。

原创粉丝点击