我们知道类的生命周期为装载、连接、初始化、使用和卸载,
而静态代码块,构造代码块,普通代码块,静态变量等,
在不同的阶段被创建静态的代码块、方法、变量可以说就是类的一部分,在类加载时就已经加载(不一定执行)
1.普通代码块: 就是类中方法的方法体
public void xxx(){
//code
}
2.构造块: 用{}裹起来的代码片段,构造块在创建对象时会被调用,每次创建对象时都会被调用,并且优先于类构造函数执行。 构造块中定义的变量是局部变量。
{
//code
}
3.静态块: 用static{}裹起来的代码片段,只会被执行一次(第一次加载此类时执行,比如说用Class.forName("")加载类时就会执行static block),静态块优先于构造块执行。
static{
//code
}
4.同步代码块: 使用synchronized(obj){}裹起来的代码块,在多线程环境下,对共享数据进行读写操作是需要互斥进行的,否则会导致数据的不一致性。常见的是synchronized用来修饰方法,其语义是任何线程进入synchronized需要先取得对象锁如果被占用了,则阻塞,实现了互斥访问共享资源。而synchronized也是有代价的。一个常见的场景是,一个冗长的方法中,其实只有一小段代码需要访问共享资源,这时使用同步块,就只将这小段代码裹在synchronized block,既能够实现同步访问,也能够减少同步引入的开销。 同步代码块须写在方法中。
synchronized(obj){
//code
}
用static修饰符修饰的属性和方法叫作静态属性和静态方法 --用于给类初始化是全局变量和全局方法。
声明为static关键词修饰的变量叫做静态成员变量,也叫作全局变量。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
静态代码块的特点: 随着类的加载而执行,而且只执行一次
(静态代码块,静态变量)>>(非静态代码块,非静态变量)>>构造方法
静态代码块的执行顺序: 静态代码块----->非静态代码块-------->构造函数(注意main方法一定是在非静态代码块的前面 ,继承时在父类静态代码块后面,子类静态代码块前面)
执行顺序是先执行父类的静态代码块,然后执行子类的静态代码块;然后执行父类的非静态代码块,再执行父类的构造方法;之后再执行子类的非静态代码块,再执行子类的构造方法。即:静态代码块》非静态代码块》构造方法。
静态代码块、静态方法 两者的区别就是:静态代码块是自动执行的; 静态方法是被调用的时候才执行的.
静态代码块不能存在于任何方法体中
执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。
静态代码块只能写在类中方法外,不能写在方法中,它会随着类的加载而优先于各种代码块和构造方法的加载,并且只会加载一次,如果出现多个静态代码块,会按照书写顺序加载。
static 代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。在静态方法里面只能直接调用同类中其他的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。因为对于非静态的方法和变量,需要先创建类的实例对象后方可使用,而静态方法在使用前不用创建任何对象。
静态代码块的作用:
一般情况下,有些代码需要在项目启动的时候就执行,这时候就需要静态代码块,比如一个项目启动需要加载配置文件,或初始化内容等。
对于普通方法: 普通方法是需要加载类new出一个实例化对象,通过运行这个对象才能运行代码块,而静态方法随着类加载就运行了。
对于静态方法: 在类加载时静态方法也加载了,但是必须需要类名或者对象名才可以访问,相比于静态代码块,静态方法是被动运行,而静态代码块是主动运行。
非静态代码块:
执行的时候如果有静态初始化块,先执行静态初始化块再执行非静态初始化块,在每个对象生成时都会被执行一次,它可以初始化类的实例变量。非静态初始化块会在构造函数执行时,在构造函数主体代码执行之前被运行。
非静态代码块的执行顺序在构造方法执行之前,类每new一次都会执行。
但是面试中,面试官可能会连着其他知识点一起问,比如说继承,这边我就写个小列子,
注:只有new对象的时候 才会执行该对象的构造方法,非静态代码块
public class StaticTestFathers {
static {
System.out.println("父类中的静态代码块");
}
StaticTestFathers() {
System.out.println("父类中的无参构造");
}
{
System.out.println("父类中的非静态方法");
}
public static void main(String[] args) {
System.out.println("父类中的main方法");
new StaticTestSon();
//父类中的静态代码块
//父类中的main方法
//子类中的静态代码块
//父类中的非静态方法
//父类中的无参构造
//子类中的非静态方法
//子类中的无参构造
}
}
public class StaticTestSon extends StaticTestFathers {
static {
System.out.println("子类中的静态代码块");
}
StaticTestSon() {
System.out.println("子类中的无参构造");
}
{
System.out.println("子类中的非静态方法");
}
public static void main(String[] args) {
System.out.println("子类中的main方法");
new StaticTestSon();
//父类中的静态代码块
//子类中的静态代码块
//子类中的main方法
//父类中的非静态方法
//父类中的无参构造
//子类中的非静态方法
//子类中的无参构造
}
}
静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法 (就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为类所属的实例变量和实例方法需要对象创建之后才会存在。
静态方法不能以任何方式引用this和super关键字,因为静态方法在使用前不用创建任何实例对象,当静态方法调用时,this所引用的对象根本没有产生。
方法区:主要是存储类信息,常量池(static 常量和 static 变量),编译后的代码(字 节码)等数据。
注:不是优先于构造函数执行,而是依托于构造函数,如果不创建对象就不会执行构造代码块。
普通代码块和构造代码块的区别在于,构造代码块是在类中定于的,而普通代码块是在方法体中定义的,执行顺序和书写顺序一致。
①凡是static的都跟对象有关,都类级别的。
②凡是static的在加载期会有特殊处理。
③构造方法不能使用static。
④static修饰的在加载时产生在数据段的静态区内。
private static修饰成员变量,private 限制了它的访问范围,只能在本类中被访问,static 只是控制了它不需要实例化就可以访问,是一个私有的共享的数据。
private static 修饰成员方法也是一个意思,本类中可用的私有的属于类的静态方法。
注意!!!不要搞混 访问控制 和 static 的概念
default\ protected\ public\ private 限制的是访问范围。
static 修饰的变量或者方法,不需要实例化,在类加载时就已经存在了,是属于类的。
private static 是私有的,不能在外部访问,只能通过静态方法调用,这样可以防止对变量的修改
public static 是公开的,在外部就可以访问,且可以对该值修改,
在编程中,如果不希望静态变量随意修改,就应该声明private(私有)的,这样外部不能随意修改,此时在把构造函数私有,这样这个变量就没有办法使用一般方法修改了,
如果使用公开的,则可以在任意地方对其进行修改。
static是针对面向对象中的“多态”而提出来的,static修饰的静态成员不存在多态性。
static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
java中也不可以覆盖private的方法,因为private修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到private变量或方法的,当然也不能覆盖。
方法重载:在同一个类中,方法相同,方法的参数个数或者参数类型不同,为了实现方法的多态性。
方法重写(覆盖):重写或覆盖是发生在子类和父类之间,而且是基于动态绑定的,但static修饰的是静态绑定编译的。
覆盖是发生在子类和父类之间,所以private修饰的变量或方法是无法被子类访问的,无法继承,所以更没办法覆盖。
static和final的意义是不同的,
static修饰的时候代表对象是静态的,而final修饰的时候代表对象只能赋值一次,
他们连用的时候是因为定义的那个对象既要它是静态的,也要求它的值不能再被修改。
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。
举例说明:
static int a=1;
static final b=1;
这里a和b的区别在于,a在程序里可以被重新赋值为2或3或等等的整数,而b在程序里不能被重新赋值,b永远都为1,也就是说b是一个常量。
final int c=1;
static final b=1;
这里c和b的区别在于,b存放在静态空间,不会在程序运行时被释放,它永远占着内存直到程序终止,而c在程序用完它而不会再用到它的时候就会被自动释放,不再占用内存。
当一个常数或字符串我们需要在程序里反复反复使用的时候,我们就可以把它定义为static final,这样内存就不用重复的申请和释放空间。
有时你希望定义一个类成员,使它的使用完全独立于该类的任何对象。通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例。在成员的声明前面加上关键字static(静态的)就能创建这样的成员。如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。你可以将方法和变量都声明为static。static 成员的最常见的例子是main( ) 。因为在程序开始执行时必须调用main() ,所以它被声明为static。
声明为static的变量实质上就是全局变量。当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量。声明为static的方法有以下几条限制:
•它们仅能调用其他的static 方法。
•它们只能访问static数据。
•它们不能以任何方式引用this 或super(关键字super 与继承有关,在下一章中描述)。
如果你需要通过计算来初始化你的static变量,你可以声明一个static块,Static 块仅在该类被加载时执行一次。下面的例子显示的类有一个static方法,一些static变量,以及一个static 初始化块:
它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。
final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。
final可以修饰变量、方法及类,当你定义一个final变量时,jvm会将其分配到常量池中,程序不可改变其值;
当你定义一个方法时,改方法在子类中将不能被重写;
当你修饰一个类时,该类不能被继承。
注意:父类的private成员方法是不能被子类方法覆盖的,因此private类型的方法默认是final类型的。
final类不能被继承,因此final类的成员方法没有机会被覆盖 ,
默认都是final的。在设计类时候,如果这个类不需要有子类,类的实现细节不允许改变,
并且确信这个类不会载被扩展,那么就设计为final类。
如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为final方法。
使用final方法的原因有二:
第一、把方法锁定,防止任何继承类修改它的意义和实现。
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。
用final修饰的成员变量表示常量,值一旦给定就无法改变!
final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。
从下面的例子中可以看出,一旦给final变量初值后,值就不能再改变了。
另外,final变量定义的时候,可以先声明,而不给初值,这中变量也称为final空白,
无论什么情况,编译器都确保空白final在使用之前必须被初始化。
但是,final空白在final关键字final的使用上提供了更大的灵活性,
为此,一个类中的final数据成员就可以实现依对象而有所不同,却有保持其恒定不变的特征。
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象市,不生成static变量的副本,而是类的所有实例共享同一个static变量。
static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用–废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。
static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
类名.静态方法名(参数列表…)
类名.静态变量名
用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块(用处非常大)。
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:
对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。因为实例成员与特定的对象关联!这个需要去理解,想明白其中的道理,不是记忆!!!
因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次
格式:java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
执行时机: 构造代码块在创建对象的时候被调用,每创建一次对象都会调用一次,但是优先于构造函数执行,需要注意的是,听名字我们就知道,构造代码块不是优先于构造函数执行的,而是依托于构造函数,也就是说,如果你不实例化对象,构造代码块是不会执行的。怎么理解呢?先看看下面的代码段:
public class Test1 {
{
System.out.println("构造代码块");
}
public Test1(){
System.out.println("无参构造函数");
}
public Test1(String str){
System.out.println("有参构造函数");
}
public void sayHello(){
System.out.println("普通代码块");
}
public static void main(String[] args) {
Test1 test1 = new Test1();
//构造代码块
//无参构造函数
}
}
(1)和构造函数的作用类似,都能够对象记性初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不会再每个对象创建的时候都执行(多个构造函数的情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)
(2)利用每次创建对象的时候都会提前调用一次构造代码块特性,我们做诸如统计创建对象的次数等功能。
1.构造函数必须和类名完全相同。在java中,普通函数可以和构造函数同名,但是必须带有返回值。
2.构造函数的功能主要用于在类创建时定义初始化的状态。没有返回值,也不能用void来进行修饰。这就保证额它不仅什么也不用自动返回,而且根本不能有任何选择,而其他方法都有返回值,尽管方法体本身不会自动返回什么,但是仍然可以返回一些东西,而这些东西可能是不安全的;
3.构造函数不能被直接调用,必须通过New运算符在创建对象的时才会自动调用;而一般的方法是在程序执行到它的时候被调用的
4.当定义一个类的时候,通常情况下都会现实该类的构造函数,并在函数中指定初始化的工作也可省略,不过Java编译期会提供一个默认的构造函数,此默认的构造函数是不带参数的,即空参构造。而一般的方法不存在这一特点。
普通代码块和构造代码块的区别是,构造代码块是在类中定义的,而普通代码块是在方法体重定义的。并且普通代码块的执行顺序和书写顺序是一致的
执行顺序:1.静态代码块>构造代码块>构造函数>普通代码块
public class CodeBlock {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public CodeBlock() {
System.out.println("无参构造函数");
}
public CodeBlock(String str) {
System.out.println("有参构造函数");
}
public void sayHello() {
System.out.println("普通代码块");
}
public static void main(String[] args) {
System.out.println("执行了main方法");
new CodeBlock().sayHello();
System.out.println("---------------------------");
new CodeBlock().sayHello();
//静态代码块
//执行了main方法
//构造代码块
//无参构造函数
//普通代码块
//---------------------------
//构造代码块
//无参构造函数
//普通代码块
}
}
public class Test1 {
static{
int x = 5 ;//由于是局部变量,所以x= 5 不影响后面的值 去掉int变全局
}
static int x ,y;//这个时候会将x和y进行初始化,得到x=0;y=0
public static void main(String args[]){
x--;//-1
myMethod();//运行myMethod方法,x之前是-1,开始调用myMethod()函数
System.out.println(x+y++ +x);//最终的运行结果为2 1+0+1=2
}
public static void myMethod(){
y=x++ + ++x;//-1+1=0 步骤2:这个地方的调用要注意:x++ + ++x 是将-1先自加然后加1,得到y=0
}
}
相同点:都是在JVM加载类时且在构造方法执行之前执行,在类中都可以定义多个,
一般在代码块中对一些static变量进行赋值。
不同点:静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。
静态代码块只在第一次new执行一次,之后不再执行,而非静态代码块在每new
一次就执行一次。非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。
https://blog.csdn.net/qq_43060759/article/details/83244041
https://blog.csdn.net/caoxiaohong1005/article/details/73719279
https://www.cnblogs.com/dyh-air/articles/7788854.html
https://www.cnblogs.com/luoyanli/archive/2012/12/04/2800758.html
https://blog.csdn.net/qq_31635851/article/details/108726756
https://www.cnblogs.com/protected/p/6419217.html
Java方法区和堆分别储存什么