一键搞定内网穿透 联行号查询|开户行查询 在线工具箱 藏经阁
当前位置:首页 / 杂记 / 正文
java static关键字及类初始化顺序
https://www.cnblogs.com/starhu/p/5150241.html
https://blog.csdn.net/anlidengshiwei/article/details/43523629

先说一下类的修饰符

类的修饰符

一个java文件中最多只能有一个public修饰的类,并且这个类的名字要和文件名相同。 一个java文件中可以有多个类,最多只能有一个类使用public修饰符,并且使用public修饰符的类类名必须和文件名相同。 类的修饰符只能加public或都不加修饰符,不能加private/protected。 不加public修饰符的类类名可以与文件名不相同,但非常不建议这样作,因为类不加修饰符时,同一命名空间/包下可以调用它,但在这个命名空间外就不能调用它了。

static特性

1. static用来修饰类的类的方法或成员变量,但不能修饰类本身。 2. static方法一般称作静态方法,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。 3. 非静态成员方法中是可以访问静态成员方法/变量。 因此,如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。 先看下面的一个例子,这段代码输出的结果是什么
public class Main {
 static int value = 33;
 
 public static void main(String[] args) throws Exception{
  new Main().printValue();
 }
 
 private void printValue(){
  int value = 3;
  System.out.println(this.value);
 }
}
这里面主要考察队this和static的理解。this代表什么?this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。

下面这段代码的输出结果是什么
public class Test extends Base{
 
 static{
  System.out.println("test static");
 }
  
 public Test(){
  System.out.println("test constructor");
 }
  
 public static void main(String[] args) {
  new Test();
 }
}
 
class Base{
  
 static{
  System.out.println("base static");
 }
  
 public Base(){
  System.out.println("base constructor");
 }
}
//输出
base static
test static
base constructor
test constructor
在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。


 ♦ 成员变量的赋值初始化优先于构造函数。
看以下例子
public class Test {
 Person person = new Person();
 public Test() {
  System.out.println("test constructor");
 }

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

class Person{
 public Person() {
  System.out.println("person constructor");
 }
}
//输出
person constructor
test constructor
如果成员变量在声明时被显式初始化,那么就把初始化值赋给成员变量,然后再执行构造方法。
如果成员变量没有显示赋值,则自动初始化为其变量类型的默认值,然后再执行构造方法。


这段代码的输出结果是什么
public class Test {
 Person person = new Person("Test");
 static{
  System.out.println("test static");
 }
  
 public Test() {
  System.out.println("test constructor");
 }
  
 public static void main(String[] args) {
  new MyClass();
 }
}
 
class Person{
 static{
  System.out.println("person static");
 }
 public Person(String str) {
  System.out.println("person "+str);
 }
}
 
 
class MyClass extends Test {
 Person person = new Person("MyClass");
 static{
  System.out.println("myclass static");
 }
  
 public MyClass() {
  System.out.println("myclass constructor");
 }
}
输出
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
类似地,我们还是来想一下这段代码的具体执行过程。首先加载Test类,因此会执行Test类中的static块。接着执行new MyClass(),而MyClass类还没有被加载,因此需要加载MyClass类。在加载MyClass类的时候,发现MyClass类继承自Test类,但是由于Test类已经被加载了,所以只需要加载MyClass类,那么就会执行MyClass类的中的static块。在加载完之后,就通过构造器来生成对象。而在生成对象的时候,必须先初始化父类的成员变量,因此会执行Test中的Person person = new Person(),而Person类还没有被加载过,因此会先加载Person类并执行Person类中的static块,接着执行父类的构造器,完成了父类的初始化,然后就来初始化自身了,因此会接着执行MyClass中的Person person = new Person(),最后执行MyClass的构造器。


这段代码的输出结果是什么
public class Test {
  
 static{
  System.out.println("test static 1");
 }
 public static void main(String[] args) {
   
 }
  
 static{
  System.out.println("test static 2");
 }
}
//输出
test static 1
test static 2
虽然在main方法中没有任何语句,但是还是会输出,原因上面已经讲述过了。另外,static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。

类初始化顺序

初始化的顺序是:先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是非静态对象。在类的内部,变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。 总结对象创建的过程: (1)首次创建对象时,类中的静态方法/静态字段首次被访问时,java解释器必须先查找类路径,以定位.class文件;(2)然后载入.class(这将创建一个class对象),有关静态初始化的所有动作都会执行(静态变量的初始化和静态代码块执行顺序按代码书写顺序依次执行,静态变量和静态代码块是平级的)。因此,静态初始化只在Class对象首次加载的时候进行一次。(3)当用new XX()创建对象时,首先在堆上为对象分配足够的存储空间。(4)这块存储空间会被清0,这就自动地将对象中的所有基本类型数据都设置成了缺省值(对数字来说就是0,对布尔型和字符型也相同),而引用则被设置成了null。(5)执行所有出现于字段定义处的初始化动作(非静态对象的初始化)。(6)执行构造器。