static和final

1. static和final

1.1. static关键字

1.1.1. static修饰成员变量

static关键字可以修饰成员变量,它所修饰的成员变量不属于对象的数据结构,而是属于类的变量,通常通过类名来引用static成员。

当创建对象后,成员变量是存储在堆中的,而static成员变量和类的信息一起存储在方法区, 而不是在堆中,

一个类的static成员变量只有“一份”(存储在方法区),无论该类创建了多少对象。看如下的示例

:
class Cat {
    private int age;
    private static int numOfCats;
    public Cat(int age) {
        this.age = age;
        System.out.println(++numOfCats);
    }
}

在main方法中声明两个Cat类对象:

Cat c1 = new Cat( 2);
Cat c2 = new Cat( 3);

其内存分配如下图-2所示:

图- 2

如上的代码中,声明两个Cat类对象后,numOfCats的值为2。当声明第一个Cat类对象后,numOfCats值增1变为1,声明第二个Cat类对象后,因为numOfCats存在方法区中并且只有一份,所以其值在刚刚的1的基础之上变为2。

1.1.2. static修饰方法

通常的方法都会涉及到对具体对象的操作,这些方法在调用时,需要隐式的传递对象的引用(this),如下代码所示,在调用distance方法时,除了传递p2参数外,还隐式的传递了p1作为参数,在方法中的this关键字即表示该参数:

int d = p1.distance(p2);

而static修饰的方法则不需要针对某些对象进行操作,其运行结果仅仅与输入的参数有关,调用时直接用类名引用即可,如下代码所示:

double c = Math.sqrt(3.0 * 3.0 + 4.0 * 4.0);

上面的方法在调用时,没有隐式的传递对象引用,因此在static方法中是不可以使用this关键字的。另外,由于static在调用时没有具体的对象,因此在static方法中不能对非static成员(对象成员)进行访问。

static方法的作用在于提供一些“工具方法”和“工厂方法”(后面课程详细介绍)等。像如下的一些工具方法,只是完成某一功能,不需要传递this:

Point.distance(Point p1, Point p2)
RandomUtils.nextInt()
StringUtils.leftPad(String str,  int size,  char padChar);
Math.sqrt()   Math.sin()   Arrays.sort()

1.1.3. static块

static块为属于类的代码块,在类加载期间执行的代码块,只执行一次,可以用来在软件中加载静态资源(图像、音频等等)。如下代码演示了static块的执行:

class Foo {
   static { 
       //类加载期间,只执行一次
       System.out.println(" Load  Foo.class ");
   }
   public Foo() {
       System.out.println("Foo()");
   }
}
class Test{
    public static void main(String[] args){
        Foo  foo = new Foo();
}
}

上面代码的输出结果为:

  • Load Foo.class
  • Foo();

因为,在Foo类加载时,先运行了静态块,而后执行了构造方法,即,static块是在创建对象之前执行的。

1.2. final关键字

1.2.1. final修饰变量

final关键字修饰变量,意为不可改变。final可以修饰成员变量,也可以修饰局部变量,当final修饰成员变量时,可以有两种初始化方式:

  1. 声明同时初始化
  2. 构造函数中初始化

final关键字修饰局部变量,在使用之前初始化即可。参见如下示例:

public class Emp {
    private final int no = 100;  // final成员变量声明时初始化
    public static void main(String[] args) {
         no = 99; 
    }
}

如上的语句,no=99会出现编译期错误,因为final的变量不可被改变。

1.2.2. final修饰方法

final关键字修饰的方法不可以被重写。使一个方法不能被重写的意义在于:防止子类在定义新方法时造成的“不经意”重写。参见下面的代码:

public class Car {
   // 点火
   public void fire() {…}
   … … …
}
public class Tank extends Car {
   // 开炮
   public void fire() {…}
   … … …
}

上面的代码中,Car类有一个方法为fire()点火,当点火后即汽车启动,坦克类Tank继承自Car类,重写了fire()点火方法,而Tank类的点火即为开炮,而非坦克启动。此即Tank类误重写了Car类的fire()方法。

若想避免这种情况发生,可以将Car类的fire()方法声明为final的,那样该方法将不可以被子类重写了,如下代码所示:

public class Car {
   // 点火
   public final void fire() {…}
   … … …
}

1.2.3. final修饰类

final关键字修饰的类不可以被继承。使一个类不能被继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害。在JDK中的一些基础类库被定义为final的,例如:String、Math、Integer、Double 等等。自己定义的类也可以声明为final的,如下代码所示:

final  Foo {  }
class  Goo  extends  Foo {   } 

上面的代码中,声明了final的Foo,而后Goo继承了Foo,此句会出现编译错误,因为final修饰的类不可以被继承。

1.2.4. static final常量

static final 修饰的成员变量称为常量,必须声明同时初始化,并且不可被改变。常量建议所有字母大写。

实际应用中应用率较广,因为static final常量是在编译期被替换的,可以节约不必要的开支,如下代码演示了static final的用法:

class Foo {
    public static final int NUM = 100;
}
class Goo {
    public static void main(String[] args) {
        Sytem.out.println(Foo.NUM);  
        // 代码编译时,会替换为:System.out.println(100);
    }
}

说明:static final常量Foo.NUM会在编译时被替换为其常量值(100),在运行Goo类时,Foo类不需要被载入。这样减少了不必要的开支。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值