一.static关键字
1.为什么要有static关键字?
之前我们在类中定义成员变量时,每个对象都会包含一份(称为实例变量),因为要使用这些信息来描述具体的对象。假如每个对象的某个属性相同时,那么这个属性大可不必在这个类所创建的每个对象中都存储一份,而是需要让所有对象来共享。在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,而是所有对象共享的。
2.如何使用static关键字?
1.static修饰成员变量
static修饰成员变量,称为静态成员变量(类成员),被放在方法区,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
public class Person {
public String name;
public int age;
public static String from = "China";
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person person1 = new Person("zhangsan",12);
Person person2 = new Person("lisi",12);
Person person3 = new Person("wangwu",12);
System.out.println(Person.from);
//静态成员变量可以通过类名.变量名的方式直接访问
System.out.println("=====================");
System.out.println(person1.from);
System.out.println(person2.from);
System.out.println(person3.from);
//也可以通过对象访问,但是from是三个对象共享的
}
}
运行结果为:
事实上:
1.静态成员变量不属于某个具体的对象,是类的属性,所有对象所共享的,不存储在某个对象的空间中;
2.静态成员变量既可以通过对象访问,又可以通过类名访问,但一般我们推荐使用类名访问;
3.生命周期伴随类的一生,静态成员变量随着类的加载而创建,随类的卸载而销毁。?
2.static修饰成员方法
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
public class Person {
public String name;
public int age;
public static String from = "China";
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
Person person1 = new Person("zhangsan",12);
Person.announce();
//通过类名.静态成员方法名来调用静态方法
person1.announce();
//通过对象名.静态成员方法名来调用静态方法,但不推荐这么做
}
}
运行结果:
我们可以看出:
1.静态方法不属于某个具体的对象,而是一个类的方法;
2.可以通过对象调用静态方法但是不推荐这么做,我们应该更多的使用类名.静态方法名的方式来调用静态方法;
3.因为静态方法是属于类的不是属于对象的,所以没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态的成员变量。?
4.静态方法中不能调用任何的非静态方法,因为非静态方法有this参数,而在静态方法中调用的时候无法传递this引用;
5.静态方法无法重写,不能用来实现多态。
3.static成员变量初始化
需要注意的是:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性。而静态成员变量的初始化分为两种:
1.就地初始化(在定义类时直接给出初始值)
public class Person {
public String name;
public int age;
public static String from = "China";
//在定义类时直接给出初始值
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
System.out.println(Person.from);
}
}
运行结果:
?2.静态代码块初始化
我们首先来说一下什么是代码块?
使用{ }定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又分为以下四种
1.普通代码块;
2.构造代码块;
3.静态代码块;
4.同步代码块(多线程部分再谈)
1.普通代码块就是定义在方法中的代码块,例如:
public class Person {
public static void main(String[] args) {
{
System.out.println("hahaha");
}
//普通代码块,直接使用{}定义;
System.out.println("heiheihei");
}
}
运行结果:
看运行结果好像普通代码块并没有什么用,事实上这种用法确实比较少见;
2.构造代码块:
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
例如:
public class Person {
public String name;
public int age;
//实例成员变量
{
this.name = "zhangsan";
this.age = 18;
}
//实例代码块
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.age);
System.out.println(person.name);
}
}
运行结果:
注意:实例代码块优先于构造方法执行,?因为在编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法的第一条语句前。
3.静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
例如:
public class Person {
public String name;
public int age;
public static String from;
//实例成员变量
{
this.name = "zhangsan";
this.age = 18;
}
//实例代码块
static{
from = "中国";
System.out.println("静态代码块执行!");
}
//静态代码块
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
Person person3 = new Person();
System.out.println(Person.from);
}
}
运行结果:
?可以看出我们在上段代码中new了三个Person对象但是静态代码块只执行了一次
注意:1.静态代码块不管生成多少对象,只会执行一次;
? ? ? ? ? ?2.静态成员变量是类的属性,因此是在jvm加载类时开辟空间并初始化的;
? ? ? ? ? ?3.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后顺序依次合并,最终放在生成的<>方法中,该方法在类加载时调用,并且只调用一次。
? ? ? ? ? ?4.实例代码块只会在创建对象时执行,
例如:
public class Person {
public String name;
public int age;
public static String from;
//实例成员变量
{
this.name = "zhangsan";
this.age = 18;
}
//实例代码块
static{
from = "中国";
System.out.println("静态代码块执行!");
}
//静态代码块
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
}
}
运行结果:
?我们在上端代码的main方法中如果什么都不做,Person类的静态代码块依旧执行!所以静态方法在构造对象之前就会执行
实例代码块,构造方法,静态代码块的执行顺序
看下面这段代码,我们对上述代码段都做出了标记
public class Person {
public String name;
public int age;
public static String from;
public Person() {
System.out.println("构造方法执行!");
}
//实例成员变量
{
this.name = "zhangsan";
this.age = 18;
System.out.println("实例代码块执行!");
}
//实例代码块
static{
from = "中国";
System.out.println("静态代码块执行!");
}
//静态代码块
public static void announce(){
System.out.println("我是中国人!");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println(person1.age);
System.out.println(person1.name);
}
}
运行结果:
?
我们发现总是静态代码块先执行,实例代码块(构造代码块)次之,最后则是构造方法执行。
那么在继承中,这些代码执行的顺序又如何呢?我们来看下面这个例子
?
public class Person {
public String name;
public int age;
public static String from;
public Person() {
System.out.println("父类构造方法执行!");
}
//实例成员变量
{
this.name = "zhangsan";
this.age = 18;
System.out.println("父类实例代码块执行!");
}
//实例代码块
static{
from = "中国";
System.out.println("父类静态代码块执行!");
}
//静态代码块
public static void announce(){
System.out.println("我是中国人!");
}
}
public class Student extends Person{
public Student() {
System.out.println("子类构造方法执行!");
}
{
System.out.println("子类实例代码块执行");
}
static{
System.out.println("子类静态方法执行!");
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student();
}
}
?
运行结果为:
就如执行结果所示,在继承关系中,各种代码的执行顺序为:
1.父类静态代码块执行 2.子类静态方法执行 3.父类实例代码块执行 4.父类构造方法执行 5.子类实例代码块执行 6.子类构造方法执行
|