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.第三步,静态块,然后执行静态代码块,因为有输出,故打印aSystem.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:4ab:7c:5ddone..e
一般顺序:静态块(静态变量)——>成员变量——>构造方法——>静态方法
1、静态代码块(只加载一次)
2、构造方法(创建一个实例就加载一次)
3、静态方法需要调用才会执行
继承类的静态变量,静态代码块,静态方法,实例变量之间的执行顺序:
例子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 sleepdog 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();}}
运行:
100show Zifunction Fu
我们可以看到多态中的成员访问特点:
成员变量
编译看左边,运行看左边构造方法
子类的构造都会默认访问父类构造成员方法
编译看左边,运行看右边静态方法
编译看左边,运行看左边
所以静态方法不能算方法的重写
参考:
/mrzhoug/article/details/51581994
/gh2391292/article/details/74421308