java静态块中的变量_java中静态变量,静态代码块,静态方法,实例变量,匿名代码块的加载顺序...

1. java中静态变量,静态代码块,静态方法,实例变量,匿名代码块

在Java中,使用{}括起来的代码称为代码块,代码块可以分为以下四种:

(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

}

下面是一个实例:

public class Test { //1.第一步,准备加载类

public static void main(String[] args)

{

new Test(); //4.第四步,new一个类,但在new之前要处理匿名代码块

// 这里必须等待类加载完

System.out.println("done..");

Test.run();

}

static int num = 4; //2.第二步,静态变量和静态代码块的加载顺序由编写先后决定

static

{

System.out.println("num:"+num); // 3.第三步,静态块,然后执行静态代码块,因为有输出,故打印a

System.out.println("a");

}

{

num += 3;

System.out.println("b:"+num); //5.第五步,按照顺序加载匿名代码块,代码块中有打印

}

int a = 5; //6.第六步,按照顺序加载变量

{ // 成员变量第三个

System.out.println("c:"+a); //7.第七步,按照顺序打印c

}

Test() { // 类的构造函数,第四个加载

System.out.println("d"); //8.第八步,最后加载构造函数,完成对象的建立

}

static void run() // 静态方法,调用的时候才加载

{

System.out.println("e");

}

}

运行:

num:4

a

b:7

c:5

d

done..

e

一般顺序:静态块(静态变量)——>成员变量——>构造方法——>静态方法

1、静态代码块(只加载一次)

2、构造方法(创建一个实例就加载一次)

3、静态方法需要调用才会执行

2864d71f80978e6f4cca53cc6369376b.png

1d55e8a127c23e325709d0d8c576c2bd.png

继承类的静态变量,静态代码块,静态方法,实例变量之间的执行顺序:

例子1:

class Print {

public Print(String s){

System.out.print(s + " ");

}

}

class Parent{

public static Print obj1 = new Print("1");

public Print obj2 = new Print("2");

public static Print obj3 = new Print("3");

static{

new Print("4");

}

public static Print obj4 = new Print("5");

public Print obj5 = new Print("6");

public Parent(){

new Print("7");

}

}

class Child extends Parent{

static

{

//System.out.println(" problem...");

new Print("a");

}

public static Print obj1 = new Print("b");

public Print obj2 = new Print("c");

public Child (){

new Print("d");

}

public static Print obj3 = new Print("e");

public Print obj4 = new Print("f");

}

public class Test1 {

public static void main(String [] args){

Parent obj1 = new Child ();

Parent obj2 = new Child ();

}

}

运行:

1 3 4 5 a b e 2 6 7 c f d 2 6 7 c f d

输出结果表明,程序的执行顺序为:

如果类还没有被加载:

1、先执行父类的静态代码块和静态变量初始化,并且静态代码块和静态变量的执行顺序只跟代码中出现的顺序有关。

2、执行子类的静态代码块和静态变量初始化。

3、执行父类的实例变量初始化

4、执行父类的构造函数

5、执行子类的实例变量初始化

6、执行子类的构造函数

如果类已经被加载:

则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。

例子2:

class H1{

{

System.out.println("父类代码块");

}

public H1(){

System.out.println("父类构造");

}

static{

System.out.println("父类静态代码块");

}

}

class H2 extends H1{

static{

System.out.println("子类静态代码块");

}

{

System.out.println("子类代码块");

}

public H2(){

System.out.println("子类构造");

}

}

public class Test1 {

public static void main(String [] args){

new H2();

}

}

运行:

父类静态代码块

子类静态代码块

父类代码块

父类构造

子类代码块

子类构造

执行流程分析:

1.java程序中静态内容是随着类的加载而加载的,由于存在继承关系,因此先加载父类而后加载子类,相应的就是先执行父类静态代码块,再执行子类静态代码块

2.类加载完成后程序就开始执行main方法中,紧接着进行初始化工作,由于代码块执行优于构造方法,因此出现先执行父类代码块,再执行父类构造方法,紧接着子类代码块,子类构造方法。

3.类的初始化是分层初始化的,先对父类进行初始化,再对子类进行初始化。在目标类中执行顺序为:1.成员变量初始化:默认初始化----》显示初始化----》构造方法初始化

2. 普通内部类和静态内部类总结

普通内部类可以访问其外部类的各种类型成员,但是静态内部类只能访问静态成员

普通内部类里面不能定义各种静态的成员(包括静态变量、静态方法、静态代码块和静态内部类),而静态内部类中则可以;

(1)静态变量和静态方法会出现这个语法错误(static methods can only be declared in a static or top level type)意思就是static方法只能在静态或者顶级类型(顶级类型应该就是外部类中)中声明,当然static变量和static内部类也是一样的道理。原因在静态变量和静态方法都只需要通过类名就能访问,不必通过任何实例化对象;而普通内部类的初始化要利用外部类的实例化对象,这明显违背了static的设计初衷。

(2)静态代码块会出现这个语法错误(Cannot define static initializer in inner type Outer.Inner)意思是不能在内部类中定义静态的初始化程序。

原因跟以上的差不多,static声明的成员只能为类所共有,而不能仅属于一个实例化对象,通俗点来说就是不管有多少层的引用,都只能是类来引用而不能是对象。

3. 理解向上转型:父类引用指向子类对象A a = New B()

向上转型是JAVA中的一种调用方式,是多态的一种表现。向上转型并非是将B自动向上转型为A的对象,相反它是从另一种角度去理解向上两字的:它是对A的对象的方法的扩充,即A的对象可访问B从A中继承来的和B复写A的方法,其它的方法都不能访问,包括A中的私有成员方法。

例子:

class Animal{

public void sleep(){

System.out.println("Animal sleep");

}

public void eat() {

System.out.println("Animal eat");

}

}

class Dog extends Animal {

public void eat() {

System.out.println("dog eat meat");//重写父类方法

}

//子类定义了自己的新方法

public void methods() {

System.out.println("dog method");

}

}

public class Demo {

public static void main(String[] args) {

Animal a = new Dog();

a.sleep();

a.eat();

//a.methods();/*报错:The method methods() is undefined for the type Animal*/

}

}

运行:

Animal sleep

dog eat meat

可以看出:

向上转型后的a对象只能访问从Animal中继承来的和Dog复写Animal的方法,其它的方法都不能访问,包括Animal中的私有成员方法。但如果要访问Dog类自己的方法,必须强制向下转型 Dog c = (Dog)a;。

另一个例子:

class Fu {

public int num = 100;

public void show() {

System.out.println("show Fu");

}

public static void function() {

System.out.println("function Fu");

}

}

class Zi extends Fu {

public int num = 1000;

public int num2 = 200;

public void show() {

System.out.println("show Zi");

}

public void method() {

System.out.println("method zi");

}

public static void function() {

System.out.println("function Zi");

}

}

public class DuoTaiDemo {

public static void main(String[] args) {

// 要有父类引用指向子类对象。

// 父 f = new 子();

Fu f = new Zi();

System.out.println(f.num);

// 找不到符号

// System.out.println(f.num2);

f.show();

// 找不到符号

// f.method();

f.function();

}

}

运行:

100

show Zi

function Fu

我们可以看到多态中的成员访问特点:

成员变量

编译看左边,运行看左边

构造方法

子类的构造都会默认访问父类构造

成员方法

编译看左边,运行看右边

静态方法

编译看左边,运行看左边

所以静态方法不能算方法的重写

参考:

https://blog..net/mrzhoug/article/details/51581994

https://blog..net/gh2391292/article/details/74421308

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值