类加载,构造器初始化及static关键字
来源:互联网 发布:深证指数收盘价数据 编辑:程序博客网 时间:2024/06/05 19:52
一.类加载与构造器初始化
1.类加载:Java命令的作用是启动虚拟机,虚拟机通过输入流,从磁盘上将字节码文件(.class文件)中的内容读入虚拟机,并保存起来的过程就是类加载。
2.类加载特性: 在Java中,类是按需加载,只有当需要用到这个类的时候,才会加载这个类,并且在虚拟机的生命周期中一个类只被加载一次。
3.类加载的原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的
4.从概念上讲,初始化与创建是彼此独立的,然而java中,初始化和创建是捆绑在一起的,两者不能分离
5.对于方法内的局部变量,声明后不会自动初始化赋值,如果没手动初始化,那么使用该变量时,编译器会报错
对于类中的数据成员(也就是属性),声明后系统会自动初始化为该类数据类型的初始值
6.在生成对象的过程中,会先初始化对象的成员变量,然后再执行构造器。也就是说类中的变量会在任何方法(包括构造器)调用之前得到初始化,即使变量散步于方法定义之间。
7.类加载的步骤:
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
- 装载:查找和导入类或接口的二进制数据
- 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的
- 校验:检查导入类或接口的二进制数据的正确性
- 准备:给类的静态变量分配并初始化存储空间
- 解析:将符号引用转成直接引用
- 初始化:激活类的静态变量的初始化Java代码和静态Java代码块
8.类加载的时机:
(1)第一次创建对象时要加载类(因为创建对象会调用构造方法,而构造方法也是static方法,只不过是隐式的)
(2)调用静态方法时要加载类,访问静态属性时会加载类
(3)加载子类时必定会先加载父类
(4)创建对象引用不加载类
(5)子类调用父类的静态方法时
- 当子类没有与父类同名的静态方法时(这时是运行父类的方法,所以只需加载父类),只加载父类,不加载子类
- 当子类有与父类同名的静态方法时(这是是运行子类的方法,需要加载子类,所以必须先加载父类),既加载父类,又加载子类
(6)访问静态常量,如果编译器可以计算出常量的值,则不会加载类,例如:public static final int a =123;否则会加载类,例如:public static final int a = math.PI;
总的来说,类是在其任何static成员被访问时加载的.初次使用之处也是static初始化之处,所有static对象和static代码段都会在加载时依程序中的顺序(即定义类时的书写顺序)而依次初始化,当然定义为static的东西只会被初始化一次
9.对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。
总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。
10.一个类中对象的定义一般都是分为以下两步来进行的:
(1)A a; //定义了一个类A的引用
(2)a=new A(“10″,”2563”); //真正地建立了对象a,也就是a指向了内存中一块连续的区域.
11.本地方法是一种在java中调用非java代码的方式
二.static关键字
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。
static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上只需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。
static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
- 类名.静态方法名(参数列表…)
- 类名.静态变量名
用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块。
三.Static关键字的用途
1.Static变量
static变量也称作静态变量(也叫类变量),静态变量和非静态变量(实例变量)的区别是:静态变量被所有的对象所共享,在内存中只有一个副本(因此在程序中任何对象对静态变量做修改,其他对象看到的是修改后的值),它当且仅当在类初次加载时会被初始化。可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
注意:不能把任何方法体内的变量声明为静态(即static是不允许用来修饰局部变量)
2.Static方法
静态方法可以直接通过类名调用(任何的实例也可以调用),因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员方法),只能访问所属类的静态成员变量和静态成员方法。因为实例成员和实例方法与特定的对象关联.
因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。(即静态方法不能是抽象的)
如果说想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。我们最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。
另外记住,即使没有显示地声明为static,类的构造器实际上也是静态方法。
3.Static代码块
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会自动执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,静态代码块没有名字,因此不能显式调用,每个代码块只会在类加载时被自动执行一次(因为在虚拟机的生命周期中一个类只被加载一次;又因为static{}是伴随类加载执行的,所以,不管你new多少次对象实例,static{}都只执行一次)。静态块常用来执行类属性的初始化.
总结如下:
- 每个静态代码块只会在类加载时被执行一次
- 当一个类中有多个static{}的时候,按照static{}的定义顺序,从前往后执行
- 先执行完static{}语句块的内容,才会执行调用语句
- 静态代码快中不能访问非static成员(属性和方法)
- 如果静态变量在定义的时候就赋给了初值(如 static int X=100),那么赋值操作也是在类加载的时候完成的,并且当一个类中既有static{}又有static变量的时候,同样遵循“先定义先执行”的原则
例如:
class Test{ public static int X=300; static{ System.out.println(X); X=200; System.out.println(X); }}public class StaticBlockTest{ public static void main(String args[]){ System.out.println(Test.X); }}
结果:程序会依次输出300,200,200,先执行完X=300,再执行static{}语句块。
4.Static类
一个普通类是不能声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例化一个外部类。
例如:
public class StaticCls{ public static void main(String[] args){ OuterCls.InnerCls oi=new OuterCls.InnerCls(); }} class OuterCls{ public static class InnerCls{ InnerCls(){ System.out.println("InnerCls"); } }}
结果:程序输出InnerCls
5.import static静态导入:详情见博文《静态导入》
6.static和final一块用表示什么?
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”
- 对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
- 对于方法,表示不可覆盖,并且可以通过类名直接访问。
注意:对于被static和final修饰过的实例常量,实例本身不能再改变了,但对于一些容器类型(比如,ArrayList、HashMap)的实例变量,不可以改变容器变量本身,但可以修改容器中存放的对象,这一点在编程中用到很多。
例如:
public class TestStaticFinal { private static final String strStaticFinalVar = "aaa"; private static String strStaticVar = null; private final String strFinalVar = null; private static final int intStaticFinalVar = 0; private static final Integer integerStaticFinalVar = new Integer(8); private static final ArrayList<String> alStaticFinalVar = new ArrayList<String>(); private void test() { System.out.println("-------------值处理前----------\r\n"); System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n"); System.out.println("strStaticVar=" + strStaticVar + "\r\n"); System.out.println("strFinalVar=" + strFinalVar + "\r\n"); System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n"); System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n"); System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n"); //strStaticFinalVar="哈哈哈哈"; //错误,final表示终态,不可以改变变量本身. strStaticVar = "哈哈哈哈"; //正确,static表示类变量,值可以改变. //strFinalVar="呵呵呵呵"; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 //intStaticFinalVar=2; //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 //integerStaticFinalVar=new Integer(8); //错误, final表示终态,在定义的时候就要初值(哪怕给个null),一旦给定后就不可再更改。 alStaticFinalVar.add("aaa"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。 alStaticFinalVar.add("bbb"); //正确,容器变量本身没有变化,但存放内容发生了变化。这个规则是非常常用的,有很多用途。 System.out.println("-------------值处理后----------\r\n"); System.out.println("strStaticFinalVar=" + strStaticFinalVar + "\r\n"); System.out.println("strStaticVar=" + strStaticVar + "\r\n"); System.out.println("strFinalVar=" + strFinalVar + "\r\n"); System.out.println("intStaticFinalVar=" + intStaticFinalVar + "\r\n"); System.out.println("integerStaticFinalVar=" + integerStaticFinalVar + "\r\n"); System.out.println("alStaticFinalVar=" + alStaticFinalVar + "\r\n"); } public static void main(String args[]) { new TestStaticFinal().test(); } }
四.分析几道题目
1.下面这段代码的输出结果是什么?
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()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。
2.这段代码的输出结果是什么?
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的构造器。
3.这段代码的输出结果是什么?
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块的顺序执行的。
4.这段代码的输出结果是什么?
public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new Cupboard() in main"); new Cupboard(); System.out.println("Creating new Cupboard() in main"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table = new Table(); static Cupboard cupboard = new Cupboard();}class Bowl{ Bowl(int marker){ System.out.println("Bowl("+marker+")"); } void f1(int marker){ System.out.println("f1("+marker+")"); }}class Table{ static Bowl bowl1 = new Bowl(1); Table(){ System.out.println("Table()"); } void f2(int marker){ System.out.println("f2("+marker+")"); } static Bowl bowl2 = new Bowl(2);}class Cupboard{ Bowl bowl3 = new Bowl(3); static Bowl bowl4 = new Bowl(4); Cupboard(){ System.out.println("Cupboard"); bowl4.f1(2); } void f3(int marker){ System.out.println("f("+marker+")"); } static Bowl bowl5 = new Bowl(5);}
输出结果:
Bowl(1)
Bowl(2)
Table()
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)
- 类加载,构造器初始化及static关键字
- java--构造器与static,初始化顺序
- 类初始化及加载
- (5)this和static关键字及对象初始化过程
- java类加载及初始化过程(静态变量、静态代码块、代码块、构造函数)
- 成员变量和局部变量的区别、方法的形参为类的情况及匿名对象、封装(private关键字)、this关键字、构造方法、static关键字
- 类的加载及初始化
- 初始化及类的加载!
- 初始化及类的加载
- 初始化及类的加载
- 类的初始化练习和static关键字的用法
- 类创建时,变量及构造器初始化顺序
- 第五章.初始化与清理 this static 构造器
- static关键字修饰变量的加载和初始化过程(Java)
- [学习笔记]Java构造器和static关键字
- 1 java类、构造函数、代码块、this、static关键字
- 习题(练习类、构造函数和static关键字)
- String、StringBuffer、StringBuilder区别,HashMap与HashTable区别,final关键字详解及初始化成员变量位置,static关键字详解,抽象类与接口区别
- Java BigInteger相关知识点
- Spark分布式计算和RDD模型研究
- 侧滑菜单-自定义HorizaontalScrollView
- HDU 1009FatMouse' Trade
- Hdu-4507 吉哥系列故事——恨7不成妻(数位DP)
- 类加载,构造器初始化及static关键字
- CSDN-markdown编辑器使用方式
- Log4net 简明手册
- 求两个数组的交集
- 常用排序算法Java实现
- If not now,when? If not me,who?
- 算法之约瑟夫问题
- String、StringBuffer与StringBuilder的整理
- HDU 5858 Hard problem (几何)