coreJava_7——static、final

一、static修饰符

static修饰符可以用来修饰类的成员变量、成员方法和代码块。

  • 用static修饰的成员变量表示静态变量,可以直接通过类名来访问;
  • 用static修饰的成员方法表示静态方法,可以直接通过类名来访问;
  • 用static修饰的程序代码表示静态代码块,当Java虚似机加载类时,就会执行该代码块;
    被static所修饰的成员变量和成员方法表明归某个类所有,它不依赖于类的特定实例,被类的所有实例共享。

1、static变量

成员变量:定义在类里面、方法外面的变量, 分两种:
a. 实例变量;
b. 静态变量;形式和实例变量类似,在实例变量前面加static关键字;
static变量和实例变量的区别:
. static变量对于每个类而言在内存中只有一个,能被类的所有实例所共享;实例变量对于每个类的每个实例都有一份,它们之间互不影响;
. Java虚拟机在加载类的过程中为static变量分配内存,实例变量在加载完类后创建对象时分配内存;
. static变量可以直接通过类名访问,实例变量通过引用类型变量访问;

例:

public class Counter {
public int count1 = 0;
public static int count2 = 0;
public static void main(String[] args){
Counter counterA = new Counter();
Counter counterB = new Counter();
counterA.count1++;
counterA.count2++;
counterB.count1++;
counterB.count2++;
     }
 }

2、static方法

成员方法分为静态方法和实例方法。用static修饰的方法叫静态方法,或类方法。静态方法也和静态变量一样,不需要创建类的实例,可以直接通过类名来访问。

public class Sample1 {
 
  public static int add(int x, int y) {
     return x+y;
    }
}
    public class Sample2 {
    public void method() {
    int result = Sample1.add(1,2);
     System.out.println("result= " + result);
   }
}

a. static方法可以直接访问所属类的实例变量和实例方法,直接访问所属类的静态变量和静态方法;

注:1、 不能使用this关键字;
2、不能使用super关键字,super关键字用来访问当前实例从父类中继承的方法和属性。super关键字与类的特定实例相关;
3、静态方法必须被实现。静态方法用来表示某个类所特有的功能,这种功能的实现不依赖于类的具体实例,也不依赖于它的子类。既然如此,当前类必须为静态方法提供实现。

b. 父类的静态方法不能被子类覆为非静态方法。以下代码编译出错。

3. static 代码块

类中可以包含静态代码块,它不存于任何方法中。在Java虚拟机中加载类时会执行这些静态代码块。如果类中包含多个静态代码块,那么Java虚拟机将按照它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。

    public class Sample {
    static int i = 5;
    static {//第一个静态代码块
System.out.println("First Static code i="+i++);
    }
    static {//第二个静态代码块
System.out.println("Second Static code i="+i++);
    }
    public static void main(String[] args) {
Sample s1 = new Sample();
Sample s2 = new Sample();
System.out.println("At last, i= "+i);
    }
}

类的构造方法用于初始化类的实例,而类的静态代码块则可用于初始化类,给类的静态变量赋初始值。

静态代码块与静态方法一样,也不能直接访问类的实例变量和实例方法,而必须通过实例的引用来访问它们。

思考:new一个对象的时候JVM都做了那些事情?

1.之前没有进行类加载

1.类加载,同时初始化类中静态的属性(赋默认值),查看静态变量是否有指定的值,有替换默认值
2.执行父类静态代码块,在执行子类静态代码块
3.分配内存空间,同时初始化非静态的属性(赋默认值)
4.先调用父类的匿名代码块

5.父类构造器执行完后,如果子类声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
6.执行子类代码块
7.执行子类构造器
8.返回内存地址

例子:

package com.briup.ch06;

public class Test {
public static void main(String[] args) {
 
 A a = new B();
}

}

class A{
protected String name = "lisi";
public A() {
 System.out.println("父类构造器A");
 System.out.println("父类构造器A中调用test方法开始,由于子类重写过test方法所以这里执行子类的test方法");
 test();
 System.out.println("父类构造器A中调用test方法结束");
}
public void test(){
 
 }

}

class B extends A{
private String name = "tom";
{
 System.out.println("子类匿名代码块中:"+name);
}
public B() {
 System.out.println("子类构造器B");
}
public void test(){
 
 System.out.println("test方法中:this.name="+this.name);
 System.out.println("test方法中:super.name="+super.name);
 
  }

}

打印结果:
父类构造器A //子类构造器调用父类构造器
父类构造器A中调用test方法开始,由于子类重写过test方法所以这里执行子类的test方法
test方法中:this.name=null //这个时候父类构造器还没有执行完 所以子类中的属性不会显示赋值 所以只有初始的默认值null
test方法中:super.name=lisi //这个时候父类构造器开始调用 了 所以父类中的属性已经有了显示赋的值了
父类构造器A中调用test方法结束
子类匿名代码块中:tom //这个时候父类构造器已经调用结束 所以子类中的属性已经有了显示赋的值了
子类构造器B

结论: 子类中的属性的显示赋值的时机 是在 父类构造器执行完之后和子类的匿名代码块执行之前的某个时候

2.之前已经进行了类加载

1.分配内存空间,同时初始化非静态的属性(赋默认值)
2.调用父类构造器
3.父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
4.执行匿名代码块
5.执行构造器
6.返回内存地址

4、静态导入

静态导入也是JDK5.0引入的新特性。
要使用静态成员(方法和变量)我们必须给出提供这个静态成员的类。使用静态导入可以使被导入类的静态变量和静态方法在当前类中可以直接使用,使用这些静态成员无需再在前面写上他们所属的类名。
//例如:

import static java.lang.Math.random;
import static java.lang.Math.PI;;

public class Test {

 public static void main(String[] args) {

//之前是需要Math.random()调用的
System.out.println(random());
System.out.println(PI);

 }

}

二、final修饰符

final具有"不可改变的"含义,它可以修饰非抽象类、非抽象成员方法和变量。
. 用final修饰的类不能被继承,没有子类;
. 用final修饰的方法不能被子类的方法覆盖;
. 用final修饰的变量表示常量,只能被赋一次值;

final不能用来修饰构造方法,因为"方法覆盖"这一概念仅适用于类的成员方法,而不适用于类的构造方法,父类的构造方法和子类的构造方法之间不存在覆盖关系. 因此用final修饰构造方法是无意义的。父类中用private修饰的方法不能被子类的方法覆盖,因此private类型的方法默认是final类型的。

  1. final类
    继承关系的弱点是打破封装,子类能够访问父类的方法,而且能以方法覆盖的方式修改实现细节。在以下情况下,
    可以考虑把类定义为final类型,使得这个类不能被继承。
    . 子类有可能会错误地修改父类的实现细节;
    . 出于安全,类的实现细节不允许有任何改动;
    . 在创建对象模型时,确信这个类不会再被扩展;

例如JDK中java.lang.String类被定义为final类型;

  1. final方法;

    某些情况下,出于安全原因,父类不允许子类覆盖某个方法, 此时可以把这个方法声明为final类型。例如在
    java.lang.Object类中,getClass()方法为final类型。

  2. final变量:
    final修饰的属性(成员变量)赋值的位置:
    非静态的成员变量
    1.声明的同时
    2.匿名代码块
    3.构造器(类中出现的所有构造器)

静态的成员变量
1.声明的同时
2.static代码块

a. final可以修饰静态变量、实例变量、局部变量;
b. final变量都必须显示初始化,否则会导致编译错误;
1) 静态变量,定义变量时进行初始化或者static代码块中赋值;
2) 实例变量,可以在定义变量时,或者在构造方法中进行初始化;
c. final变量只能赋一次值。

      public class Sample {
  private final int var1 = 1;
  public Sample() {
       var1 = 2;     //编译出错,不允许改变var1实例变量的值;
  }

  public void method(final int param) {
       final int var2 = 1;         
       var2++;       //编译出错,不允许改变var2局部常量的值
       param++;      //编译出错,不允许改变final类型参数的值;
  }
      }

      public class Sample {
  final int var1;    //定义var1实例常量
  final int var2 = 0;           //定义并初始化var2实例常量

  Sample() {
var1 = 1;    //初始化var1实例常量
  }

  Sample(int x) {
var1 = x;     //初始化var1实例常量
  } 
}       

你可能感兴趣的:(coreJava_7——static、final)