Java中的static

static是"静态的"的意思.

静态方法

首先看看<Java编程思想>对静态方法的解释:

static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来倒是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法.这实际上正是static方法的主要用途.

关于静态方法内不能调用非静态方法,而反过来可以.经过上面的解释,我们知道静态方法的调用不需要对象,那么其中也就不存在this.而非静态方法是需要具体的对象调用的,那么也就无法被静态方法直接调用,需要创建对象后用对象调用.

那反过来为什么可以呢,因为static方法不需要创建对象就可以访问.

所以,静态成员可以直接在静态方法或非静态方法中访问,非静态成员必须创建对象才能在静态方法中访问.

public class Static {
    private static int number = 1; //静态
    private String name = "a";     //非静态
    private void out(){            //非静态
        test();                    //可以直接访问静态方法
        System.out.println(name + number); //也可以直接访问静态变量
    }
    private static void test(){                 //静态方法
        //out();                                //这样是不可以的
        //System.out.println(number + name);    //这样是不可以的
        Static s = new Static();                //需要创建对象后才可以访问非静态成员
        s.out();
        System.out.println(s.name + number);    
    }
}

main为什么是static的?

就比如我们运行程序所需要的main(),就是个典型的静态方法:

    public static void main(String[] args){}

为什么main()要用static?试想一下,如果main()不是static的,那么JVM就需要使用如下形式调用main()(假设类名为A)

    A a = new A();
    a.main();

看起来没什么问题,但是你在哪里创建这个A对象呢?一样还是需要main(),这样就无法启动程序.所以作为程序的入口,必须可以直接用类名就调用得到:

    A.main();

 静态方法的具体应用

最常见的地方就是工具方法,比如Math类中的诸多方法,都是直接通过Math类来调用:

    Math.add();
    Math.abs();
    Math.pow();
    Math.max();
        .
        .
        .

还有就是工厂方法:

工厂方法模式(英语:Factory method pattern)是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”                    ---维基百科 工厂方法 词条.

class Point {
    private int x;
    private int y;
    private int z;
    private int length;

    private Point(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
        length = 3;
    }

    private Point(int x, int y){
        this.x = x;
        this.y = y;
        length = 2;
    }
    static Point createXYZPoint(int x, int y, int z){
        return new Point(x, y , z);
    }

    static Point createXYPoint(int x, int y){
        return new Point(x, y);
    }

    @Override
    public String toString() {
        if (length == 3){
            return "(" + x + "," + y + "," + z + ")";
        } else {
            return "(" + x + "," + y + ")";
        }
    }
}

这个类中两个return Point对象的方法就是工厂方法,分别用来创建两种不同的点. 

public class TestPoint{
    public static void main(String[] args) {
        Point p1 = Point.createXYPoint(1, 2);
        Point p2 = Point.createXYZPoint(3, 4, 5);
        System.out.println(p1);
        System.out.println(p2);
    }
}

运行: 

    (1,2)
    (3,4,5)

可以看到测试类中并没有使用new关键字也创建了对象. 因为你不能在测试类使用new创建创建Point对象:所有的构造器都是私有的.

这样的工厂方法往往有可以展示自己可以创建什么对象的名字,而不像构造器一样只有类名一个名称,可能有多个构造器而造成混乱(方法重载),这也是一点优势.

静态变量

与静态方法一样无需使用对象调用.

静态变量只存在一份:被所有对象共享,仅在类初次加载时初始化一次.与此对比,非静态变量每个对象都可以持有一份,内存中可以存在多个.下面的代码展示了两者的区别:

class Count {
    static int times = 0;
    int number = 0;

    public static int getTimes() { return times; }
    public int getNumber() { return number; }
}

public class TestCount{
    public static void main(String[] args) {
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            Count.times++;
        }
        System.out.println("静态变量自增127次I:" + Count.getTimes());
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            Count.times++;
        }

        System.out.println("静态变量自增127次II:" + Count.getTimes());
        System.out.println();
        Count c1 = new Count();
        Count c2 = new Count();
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            c1.number++;
            c2.number++;
        }
        System.out.println("非静态变量1自增127次I:" + c1.getNumber());
        System.out.println("非静态变量2自增127次I:" + c2.getNumber());
        System.out.println();

        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            c1.number++;
            c2.number++;
        }
        System.out.println("非静态变量1自增127次II:" + c1.getNumber());
        System.out.println("非静态变量2自增127次II:" + c2.getNumber());
        System.out.println();

        c1 = new Count();
        c2 = new Count();

        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            c1.number++;
            c2.number++;
        }
        System.out.println("非静态变量1自增127次III:" + c1.getNumber());
        System.out.println("非静态变量2自增127次III:" + c2.getNumber());
    }
}

运行结果:

静态变量自增127次I:127
静态变量自增127次II:254

非静态变量1自增127次I:127
非静态变量1自增127次I:127

非静态变量1自增127次I:254
非静态变量1自增127次I:254

非静态变量1自增127次II:127
非静态变量1自增127次II:127

这里非静态变量创建了两个,每个的值都是独立的,而静态变量的值是共享的,无论多少次操作都是那一个静态变量.所以静态变量的一个用途就是用作计数器. 

常量

前面在数据类型中引用了下面两个数值:

    Math.E;
    Math.PI;

 进入Math类源码,可以看到它们两个的定义略有不同,多了一点东西:

    public static final double E = 2.7182818284590452354;

    public static final double PI = 3.14159265358979323846;

多了一个final,意思是"最终的",也就是说,不能改变.这里先不说,下一篇里面再详细说说final.

 我们应用最多的常量可能就是System中的out,它是PrintStream类型的:

    public final static PrintStream out = null;

再来看看输出语句的含义:

    System.out.println();

通过System.out来访问System类中的PrintStream类型的常量out,再通过out来调用PrintStream中的println(). 

我们自己写一个来看看:

import java.io.PrintStream;

public class Static {
    public static void main(String[] args) {
        Status.STATUS.showStatus(); //Status类中的showStatus方法.
        Status.PRINT.println("通过Integer类型常量调用Integer中的floatValue():" + Status.INTEGER.floatValue());
        //通过Integer类型常量调用Integer中的floatValue():1.0
        Status.PRINT.println(Status.STR); //Status类中的String类型常量.
    }
}

class Status{
     static final Status STATUS = new Status();
     static final Integer INTEGER = 1;
     static final PrintStream PRINT = new PrintStream(System.out);
     static final String STR = "Status类中的String类型常量.";
     void showStatus(){
        System.out.println("Status类中的showStatus方法.");
    }
}

这里我通过Status类型常量调用到了Status类中的showStatus(),还通过PrintStream类型常量PRINT调用println()输出了以下信息:Integer类型常量INTEGER访问到了Integer中的floatValue(),又通过Status类调用到了其中String类型的常量STR.

当然通过本类型常量调用本类型方法,普通的静态变量也是可以的.比如我把上面Status类型常量的final去掉这些代码照样可以正常运行.

关于常量的命名,一般来说是全部使用大写字母,这不是硬性规定.

静态代码块

如果我们想预先加载一些资源,比如图片,就可以使用独立的static块,static块的特点是,先于类被执行,一般用来做准备活动.

    static{}

比如: 

public class Static {
    Static(){System.out.println("构造器.");}
    public static void main(String[] args) {
        Static s = new Static();
        s.out();
        System.out.println("main方法.");
    }
    private void out(){
        System.out.println("out方法");
    }
    static{System.out.println("static块");}
}

运行结果: 

    static块
    构造器.
    out方法
    main方法.

可以看到static块的执行顺序先于对象的初始化. 当存在多个static块时,按从前到后的顺序依次执行.

由此可以一个经典的问题,看看以下代码的输出结果是什么:

public class Static {
    Static(){
        System.out.println("父类Static构造器.");  //1
    }
    public static void main(String[] args) {
        new StaticSubClass();                    
    }
    static {
        System.out.println("父类Static的静态块1.");  //2
    }

    A a = new A("Static"); //3

    static {
        System.out.println("父类Static的静态块2."); //4
    }
}

class StaticSubClass extends Static{
    A a = new A("StaticSubClass");   //5
    StaticSubClass(){
        System.out.println("子类StaticSubClass构造器."); //6
    }
    static {
        System.out.println("子类StaticSubClass静态块."); //7
    }
}

class A{
    String string;
    A(String s){
        System.out.println( s + " A.");        
    }
    static {
        System.out.println("A类的静态块.");    //8
    }
}

运行结果(顺序:24783156):

父类Static的静态块1.
父类Static的静态块2.
子类StaticSubClass静态块.
A类的静态块.
Static A.
父类Static构造器.
StaticSubClass A.
子类StaticSubClass构造器.

由于main()在父类中,所以最先加载父类的静态块,但是父类有两个静态块,就按照从前到后的顺序执行(24).主方法中创建了一个子类对象,就执行子类中的静态块(7),然后父类中有A的对象创建,检查发现没有A的对象,就加载A对象,于是就执行A中的静态块(8),此时可以创建A对象,就调用了A的构造器(父类中)(3),再执行父类构造器(1),子类中再执行A的创建(5),最后执行子类自己的构造器(6).

static就说到这里,下一篇来说说final.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值