代码块又称初始化块,属于类中的成员(是类的一部分),类似于方法:将逻辑语句封装在方法体中。通过{}包围起来
但是和方法不同,匿名代码块没有方法名,返回类型,参数。只有方法体,而且不用通过对象或类显示调用,而是加载类时,或创建对象时隐式调用。
[修饰符]{
…
}
public class Movie {
}
class testMovie{
private String name;
private String director;
public testMovie(String name) {
System.out.println("电影开始了");
System.out.println("放广告");
this.name = name;
}
public testMovie(String name, String director) {
System.out.println("电影开始了");
System.out.println("放广告");
this.name = name;
this.director = director;
}
}
当两个构造器中都有重复且与对象不相关的语句时,就可以抽象到匿名代码块中
class testMovie{
private String name;
private String director;
{
System.out.println("电影开始了");
System.out.println("放广告");
}
public testMovie(String name) {
this.name = name;
}
public testMovie(String name, String director) {
this.name = name;
this.director = director;
}
}
实例化两个对象看看效果:
public class Movie {
public static void main(String[] args) {
testMovie m1 = new testMovie("三体");
testMovie m2 = new testMovie("海贼王","尾田");
}
}
电影开始了
放广告
电影开始了
放广告
类加载并不指的是实例化对象,除了实例化对象时会对类进行加载,在调用类的静态成员时也会加载类
加载类的三个场景
1. 实例化对象时(new)
2. 创建子类对象时,父类也会被加载
3. 使用类的静态成员时(静态属性,静态方法)
场景演示:
1.实例化对象时
public class test {
public static void main(String[] args) {
new AA();
}
}
class AA{
static{
System.out.println("AA的静态代码块被执行");
}
}
输出:AA的静态代码块被执行
2.创建子类对象时,父类也会被加载
public class test {
public static void main(String[] args) {
new AAson();
}
}
class AA{
static{
System.out.println("AA的静态代码块被执行");
}
}
class AAson extends AA{
static {
System.out.println("AAson子类的静态代码块被执行");
}
}
输出:
AA的静态代码块被执行
AAson子类的静态代码块被执行
可以看到是先加载父类再加载子类的,就像先有父亲才有儿子,所以当需要加载一个子类时,父类必须先加载。
3.使用类的静态成员时(静态属性,静态方法)
public class test {
public static void main(String[] args) {
System.out.println(AA.num);
}
}
class AA{
static int num = 99;
static{
System.out.println("AA的静态代码块被执行");
}
}
输出:
AA的静态代码块被执行
99
第三点补充:当使用类的静态成员时,会加载类从而执行静态代码块。但是由于不是实例化对象所以普通代块不会被执行
public class test {
public static void main(String[] args) {
System.out.println(AA.num);
}
}
class AA{
static int num = 99;
{
System.out.println("AA的普通代码块被调用");
}
static{
System.out.println("AA的静态代码块被执行");
}
}
输出:
AA的静态代码块被执行
99
根据上述结论可以得出普通代码块和静态代码块的区别
public class test {
public static void main(String[] args) {
new AAson();
}
}
class AA{
{
System.out.println("AA的普通代码块被调用");
}
static{
System.out.println("AA的静态代码块被执行");
}
}
class AAson extends AA{
{
System.out.println("AAson的普通代码块被调用");
}
static {
System.out.println("AAson子类的静态代码块被执行");
}
}
输出:
AA的静态代码块被执行
AAson子类的静态代码块被执行
AA的普通代码块被调用
AAson的普通代码块被调用
要弄清楚为什么会这样输出,首先我们得弄明白当没有继承关系时类执行的顺序是怎样的。
首先 静态代码块优先于普通代码块执行这是毋庸置疑的
当一个类中有静态代码块和静态属性初始化时,两者会按照定义的顺序从上往下依次执行,它们是平等的。待静态代码块和静态初始化执行完,就开始执行普通代码块和普通属性初始化
因此完整顺序为:
代码演示:
public class test {
public static void main(String[] args) {
new AA();
}
}
class AA{
static int num = getNum();
int num1 = getNum1();
{
System.out.println("AA的普通代码块被执行");
}
static{
System.out.println("AA的静态代码块被执行");
}
public static int getNum(){
System.out.println("静态属性初始化被执行");
return 50;
}
public int getNum1(){
System.out.println("普通属性初始化被执行");
return 50;
}
}
根据上述顺序依次分析执行的顺序为:
首先寻找静态成员 :
1.发现定义了静态属性 num 且调用了静态方法getNum返回一个值进行初始化,所以第一个输出静态方法里的:静态属性初始化被执行
2.然后继续往下寻找静态成员发现了静态代码块:执行输出里面的:AA的静态代码块被执行
3.接着往下寻找静态成员,发现只剩下一个静态方法了,但是方法没被调用就不执行,所以静态成员结束
开始从头寻找普通成员
4.找到普通属性 num1 调用普通方法getNum1初始化,执行输出里面的:普通属性初始化被执行
5.接着寻找普通成员,找到普通代码块,执行输出:AA的普通代码块被调用
6.接着寻早发现只有普通方法了。结束 调用默认的无参构造代码块
因此运行程序会输出:
静态属性初始化被执行
AA的静态代码块被执行
普通属性初始化被执行
AA的普通代码块被执行
根据上面的案例,其实可以总结成一个规律:
首先执行静态代码块和静态成员初始化
然后执行构造器,但是构造器有默认的super调用父类的构造器,还有隐式的调用普通代码块和初始化普通属性
public 类名(){
//super();
//调用普通代码块/初始化普通属性
…构造器语法
}
总结:先执行所有静态内容再执行构造器,而构造器里有默认的super()和调用普通代码块
根据上面的分析其实已经可以得出开头小问题为什么是那样执行的。
看以下代码分析输出的是什么?
public class test {
public static void main(String[] args) {
new BB();
}
}
class AA{
static int num = getNum();
int num1 = getNum1();
{
System.out.println("AA的普通代码块被执行");
}
static{
System.out.println("AA的静态代码块被执行");
}
public static int getNum(){
System.out.println("AA的静态属性初始化被执行");
return 50;
}
public int getNum1(){
System.out.println("AA的普通属性初始化被执行");
return 50;
}
public AA(){
System.out.println("AA的无参构造器执行");
}
}
class BB extends AA{
static int b1 = getB1();
int b2 = getB2();
public static int getB1(){
System.out.println("BB的静态属性初始化");
return 50;
}
public int getB2(){
System.out.println("BB的普通属性初始化");
return 40;
}
static{
System.out.println("BB的静态代码块执行");
}
{
System.out.println("BB的普通代码块执行");
}
public BB(){
System.out.println("BB的无参构造器执行");
}
}
(需仔细理解)
首先执行所有静态内容(子类和父类都是)
由于首先加载父类再加载子类所以从父类开始找:
1.按照顺序查找发现静态属性执行:AA的静态属性初始化被执行
2.静态代码块执行:AA的静态代码块被执行
父类的静态内容执行完毕
开始加载子类
3.从子类开始查找静态内容,发现静态属性:BB的静态属性初始化
3.然后找到下面的静态代码块:BB的静态代码块执行
此时所有静态内容执行完毕,开始创建实例对象
进入BB的构造器
4.BB的构造器中隐式的有super()和调用BB的普通代码块/初始化普通属性
5.根据super()进入父类AA的构造器,AA的构造器也有super()和调用BB的普通代码块/初始化普通属性
6.但是AA的super()是Object的什么也不会输出,所以忽略即可
7.接着执行AA构造器中调用普通代码块和初始化普通属性(按照顺序)
8.所以从上往下查找发现普通属性定义且初始化:AA的普通属性初始化被执行
9.接着发现普通代码块:AA的普通代码块被执行
10.没有普通内容可以执行后,接着执行AA构造器的语句:AA的无参构造器执行
11.至此AA的构造器执行完毕返回BB的构造器执行super()下面的调用BB的普通代码块/初始化普通属性
12.按照顺序依次初始化普通属性和调用普通代码块(平等关系,按照顺序)
13.b1的初始化:BB的普通属性初始化
14.执行BB的普通代码块:BB的普通代码块执行
15.普通内容全部执行完毕后,执行BB构造器的内容:BB的无参构造器执行
至此终于程序执行完毕
所以按照分析会依次输出:
AA的静态属性初始化被执行
AA的静态代码块被执行
BB的静态属性初始化
BB的静态代码块执行
AA的普通属性初始化被执行
AA的普通代码块被执行
AA的无参构造器执行
BB的普通属性初始化
BB的普通代码块执行
BB的无参构造器执行

分析完全正确
因此可以得出类加载和实例化对象的基本区别和大致概念
类加载:加载类的信息
实例化对象:类加载+运行构造器
也可以得出当实例化对象时的执行顺序:
1.父类的静态代码块和静态属性初始化
2.子类的静态代码块和静态属性初始化
3.父类的普通代码块和普通属性初始化
4.父类的构造器代码块
5.子类的普通代码块和普通属性初始化
6.子类的构造器代码块
ps:代码块和属性初始化是平等关系谁先定义就谁先
代码块分静态和普通,规则也和之前说的静态和非静态一样
静态代码块:只能调用静态成员
非静态代码块:都可以调用
1.下列代码会输出什么?
public class test1 {
public static void main(String[] args) {
System.out.println("total = "+Person.total);
System.out.println("total = "+Person.total);
}
}
class Person{
public static int total;
static {
total = 100 ;
System.out.println("in static block");
}
}
分析:
输出类名.静态属性,
所以输出内容如下:
in static block
total = 100
total = 100
2.看看下面输出什么?
public class test1 {
Sample s1 = new Sample("s1成员初始化");
static Sample s2 = new Sample("s2静态成员初始化");
static {
System.out.println("static代码块执行");
if(s2 == null){
System.out.println("s2 is null");
}
}
test1(){
System.out.println("test1的无参构造器被调用");
}
public static void main(String[] args) {
test1 a = new test1();
}
}
class Sample{
Sample(String s){
System.out.println(s);
System.out.println("Sample的有参构造器被调用");
}
Sample(){
System.out.println("Sample的无参构造器被调用");
}
}
1.进入main方法,发现要实例化一个test1的对象,因此先加载test1类
2.开始查找test1类的静态内容:发现类变量s2,由于初始化是Sample类的实例化,所以先去加载Sample类
3.发现Sample类没有静态内容和普通内容,所以直接执行Sample的有参构造器输出:s2静态成员初始化 和 Sample的有参构造器被调用
4.接着回到test1继续寻找下面的静态内容,执行静态代码块输出:static代码块执行。s2!= null,所以语句不输出
5.静态内容全部执行完毕,开始执行普通内容
6.初始化普通变量 s1,调用Sample的普通内容,但是它没有所以直接执行构造器输出:s1成员初始化 和 Sample的有参构造器被调用
7.普通内容也执行完毕后,再是test1的构造器输出:test1的无参构造器被调用
所以程序运行后依次输出:
s2静态成员初始化
Sample的有参构造器被调用
static代码块执行
s1成员初始化
Sample的有参构造器被调用
test1的无参构造器被调用