当前位置: 首页 > news >正文

【Java SE】Java代码块详解

Java代码块详解

  • 代码块概述
    • 什么是代码块?
    • 代码块的分类
  • 局部代码块
    • 语法格式
    • 执行时机
    • 变量的作用域规则⭐
  • 静态代码块⭐
    • 语法格式
    • 执行时机⭐
    • 注意事项
    • 主要用途
  • 实例代码块(构造代码块)
    • 语法格式
    • 执行时机
    • 主要用途
    • 实例代码块与构造方法的执行顺序⭐
  • 同步代码块
    • 语法格式
    • 执行机制
    • 锁对象的选择
    • 同步代码块的优点
  • 各种代码块的执行顺序⭐
    • 例1:继承关系+局部代码块+静态代码块+实例代码块+同步代码块
    • 例2:静态代码块和静态变量的顺序
  • 常见问题
    • Q1:静态代码块中抛出异常会怎样?
    • Q2:实例代码块和构造方法谁先执行?
    • Q3:同步代码块使用不当导致死锁

代码块概述

什么是代码块?

代码块(Block)是指使用一对花括号{}括起来的一段代码区域。在Java中,代码块可以出现在类中、方法中,甚至可以独立存在。根据声明位置和修饰符的不同,代码块可以分为多种类型,每种类型都有其特定的执行时机和用途。

代码块的分类

Java中的代码块主要分为以下四大类:

  1. 局部代码块- 定义在方法内部的普通代码块
  2. 实例代码块- 没有static修饰的代码块(又称构造代码块)
  3. 同步代码块- 使用synchronized修饰的代码块
  4. 静态代码块- 使用static修饰的代码块

局部代码块

局部代码块是定义在方法内部的普通代码块,它是最简单的一种代码块。

语法格式

publicvoidmyMethod(){// 方法中的其他代码{// 局部代码块的内容System.out.println("这是局部代码块");intlocalVar=100;}// 这里无法访问 localVar//System.out.println(localVar);}

执行时机

局部代码块的执行时机相对简单:

  1. 方法调用时执行:当程序执行到代码块位置时执行
  2. 顺序执行:按照代码的书写顺序执行
  3. 可多次执行:如果方法被多次调用,代码块也会多次执行

变量的作用域规则⭐

在局部代码块中,变量的作用域遵循以下规则:

变量类型作用域范围说明
代码块内定义的变量仅在代码块内有效出代码块后无法访问
方法参数整个方法内有效可在代码块中访问
外部方法变量整个方法内有效可在代码块中访问

静态代码块⭐

静态代码块是在类中使用static关键字修饰的代码块,它是Java中最先执行的代码块之一。

语法格式

publicclassMyClass{static{// 静态代码块的内容System.out.println("这是静态代码块");}}

执行时机⭐

静态初始化块是用static关键字修饰的代码块,用于执行类的初始化操作。其特点如下:

  1. 类加载时执行:当JVM第一次加载类时执行
  2. 只执行一次:在整个程序生命周期中仅执行一次
  3. 优先于main方法:在main方法执行之前执行
  4. 顺序执行:如果有多个静态代码块,按定义顺序执行

具体原理双亲委派模型

注意事项

  • 静态代码块中不能直接访问非静态成员
  • 静态代码块中不能使用thissuper关键字
  • 静态代码块中不能抛出受检异常
  • 如果类中包含多个静态代码块,它们会按顺序执行

主要用途

静态代码块常用于以下场景:

  • 初始化静态变量:为静态成员变量赋初始值
  • 加载配置文件:读取properties或XML配置文件
  • 建立数据库连接:创建数据库连接池
  • 注册驱动:如JDBC驱动注册
publicclassDatabaseConfig{privatestaticStringurl;privatestaticStringusername;privatestaticStringpassword;// 静态代码块 - 加载配置文件static{System.out.println("静态代码块开始执行:加载配置文件");// 模拟读取配置文件url="jdbc:mysql://localhost:3306/mydb";username="root";password="123456";try{// 注册JDBC驱动Class.forName("com.mysql.jdbc.Driver");System.out.println("数据库驱动注册成功");}catch(ClassNotFoundExceptione){System.out.println("数据库驱动注册失败");}System.out.println("静态代码块执行完成");}publicstaticvoidmain(String[]args){System.out.println("main方法开始执行");System.out.println("数据库URL: "+url);System.out.println("用户名: "+username);}}

执行结果:

静态代码块开始执行:加载配置文件 数据库驱动注册成功 静态代码块执行完成 main方法开始执行 数据库URL: jdbc:mysql://localhost:3306/mydb 用户名: root

实例代码块(构造代码块)

实例代码块是定义在类中但没有static修饰的代码块,它属于对象的级别。由于它在构造方法之前执行,因此也被称为构造代码块

语法格式

publicclassMyClass{{// 实例代码块的内容System.out.println("这是实例代码块");}}

执行时机

实例代码块的执行具有以下特点:

  1. 创建对象时执行:每次使用new关键字创建对象时都会执行
  2. 优先于构造方法:在构造方法之前执行
  3. 每次创建都执行:无论使用哪个构造方法,实例代码块都会执行
  4. 顺序执行:如果有多个实例代码块,按定义顺序执行

主要用途

实例代码块主要用于以下场景:

  • 提取共性代码:将多个构造方法中相同的代码提取出来
  • 初始化实例变量:为实例变量赋初始值
  • 执行对象创建的准备工作:如日志记录、计数统计等
publicclassStudent{privateStringname;privateintage;privatestaticintcount=0;// 静态变量,记录创建的对象数量// 实例代码块{count++;// 每创建一个对象,计数器加1System.out.println("实例代码块执行:这是创建的第"+count+"个学生对象");}// 无参构造方法publicStudent(){System.out.println("无参构造方法执行");this.name="未知";this.age=0;}// 有参构造方法publicStudent(Stringname,intage){System.out.println("有参构造方法执行");this.name=name;this.age=age;}publicstaticvoidmain(String[]args){System.out.println("开始创建对象...");Studentstu1=newStudent();Studentstu2=newStudent("张三",20);Studentstu3=newStudent("李四",22);}}

执行结果:

开始创建对象... 实例代码块执行:这是创建的第1个学生对象 无参构造方法执行 实例代码块执行:这是创建的第2个学生对象 有参构造方法执行 实例代码块执行:这是创建的第3个学生对象 有参构造方法执行

实例代码块与构造方法的执行顺序⭐

通过反编译可以得知,实例代码块的代码实际上会被Java编译器复制到每一个构造方法的最前面(在super()调用之后)。因此,执行顺序是:

父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法

同步代码块

同步代码块是使用synchronized关键字修饰的代码块,用于解决多线程并发访问共享资源时的线程安全问题。

语法格式

publicclassMyClass{publicvoidmyMethod(){synchronized(lockObject){// 需要同步的代码System.out.println("这是同步代码块");}}}

执行机制

同步代码块的核心机制是

  1. 获取锁:线程进入同步代码块前必须获取指定对象的锁
  2. 执行代码:只有获得锁的线程才能执行代码块中的内容
  3. 释放锁:代码块执行完毕后自动释放锁
  4. 互斥访问:同一时刻只有一个线程可以执行该代码块

锁对象的选择

同步代码块的锁对象可以是任何Java对象,常见的选择有:

锁对象类型示例适用场景
当前对象synchronized(this)实例方法中的同步
类对象synchronized(MyClass.class)静态方法中的同步
自定义对象private final Object lock = new Object()细粒度控制
字符串常量synchronized("lock")不建议使用,容易造成死锁
publicclassBankAccount{privatedoublebalance;// 账户余额privatefinalObjectlock=newObject();// 自定义锁对象publicBankAccount(doubleinitialBalance){this.balance=initialBalance;}// 存款方法publicvoiddeposit(doubleamount){synchronized(lock){System.out.println(Thread.currentThread().getName()+" 开始存款: "+amount);doublenewBalance=balance+amount;// 模拟处理耗时try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}balance=newBalance;System.out.println(Thread.currentThread().getName()+" 存款完成,当前余额: "+balance);}}// 取款方法publicvoidwithdraw(doubleamount){synchronized(lock){System.out.println(Thread.currentThread().getName()+" 开始取款: "+amount);if(balance>=amount){doublenewBalance=balance-amount;// 模拟处理耗时try{Thread.sleep(100);}catch(InterruptedExceptione){e.printStackTrace();}balance=newBalance;System.out.println(Thread.currentThread().getName()+" 取款成功,当前余额: "+balance);}else{System.out.println(Thread.currentThread().getName()+" 取款失败,余额不足");}}}publicstaticvoidmain(String[]args){BankAccountaccount=newBankAccount(1000);// 创建多个线程同时操作同一个账户Threadt1=newThread(()->account.withdraw(800),"张三");Threadt2=newThread(()->account.deposit(500),"李四");Threadt3=newThread(()->account.withdraw(700),"王五");t1.start();t2.start();t3.start();}}

同步代码块的优点

相比于同步方法,同步代码块具有以下优势:

  1. 细粒度控制:可以只同步需要同步的代码,提高并发性能
  2. 灵活选择锁:可以使用任意对象作为锁,实现更复杂的同步逻辑
  3. 减少锁持有时间:只对关键代码加锁,减少线程阻塞时间

各种代码块的执行顺序⭐

例1:继承关系+局部代码块+静态代码块+实例代码块+同步代码块

classParent{static{System.out.println("1. 父类静态代码块");}{System.out.println("3. 父类实例代码块");}publicParent(){System.out.println("4. 父类构造方法");}}publicclassChildextendsParent{static{System.out.println("2. 子类静态代码块");}{System.out.println("5. 子类实例代码块");}publicChild(){System.out.println("6. 子类构造方法");}publicvoidtestMethod(){System.out.println("8. 开始执行testMethod方法");// 局部代码块{System.out.println("9. 局部代码块");}// 同步代码块synchronized(this){System.out.println("10. 同步代码块");}System.out.println("11. testMethod方法执行结束");}publicstaticvoidmain(String[]args){System.out.println("开始创建第一个对象");Childchild1=newChild();System.out.println("\n开始调用方法");child1.testMethod();System.out.println("\n开始创建第二个对象");Childchild2=newChild();}}

执行结果:

1. 父类静态代码块 2. 子类静态代码块 开始创建第一个对象 3. 父类实例代码块 4. 父类构造方法 5. 子类实例代码块 6. 子类构造方法 开始调用方法 8. 开始执行testMethod方法 9. 局部代码块 10. 同步代码块 11. testMethod方法执行结束 开始创建第二个对象 3. 父类实例代码块 4. 父类构造方法 5. 子类实例代码块 6. 子类构造方法

通过上面的示例,可以总结出Java中各类代码块的完整执行顺序:

  1. 类加载阶段:父类静态代码块 → 子类静态代码块(只执行一次)

    具体原理双亲委派模型

  2. 对象创建阶段:父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法
  3. 方法调用阶段:方法中的普通代码 → 局部代码块 → 同步代码块(如果遇到)

例2:静态代码块和静态变量的顺序

publicclassOrderTrick{staticintx=10;static{System.out.println("静态代码块1: x = "+x);x=20;}staticinty=x+5;// y会是多少?static{System.out.println("静态代码块2: x = "+x+", y = "+y);x=30;y=100;}publicstaticvoidmain(String[]args){System.out.println("main: x = "+x+", y = "+y);}}
静态代码块1: x = 10 静态代码块2: x = 20, y = 25 main: x = 30, y = 100
  • 静态变量声明时的赋值和静态代码块,按照出现顺序合并,后面的赋值会覆盖前面的值

常见问题

Q1:静态代码块中抛出异常会怎样?

publicclassExceptionDemo{static{// 静态代码块中抛出异常会导致类初始化失败if(true){thrownewRuntimeException("静态代码块异常");}}publicstaticvoidmain(String[]args){try{// 访问该类的任何内容都会导致ExceptionInInitializerErrorClass.forName("ExceptionDemo");}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(ExceptionInInitializerErrore){System.out.println("类初始化失败: "+e.getCause());}}}

解决方案:静态代码块中应该避免抛出受检异常,使用try-catch处理可能的异常。

Q2:实例代码块和构造方法谁先执行?

publicclassOrderDemo{{System.out.println("实例代码块");}publicOrderDemo(){System.out.println("构造方法");}publicstaticvoidmain(String[]args){newOrderDemo();// 输出:实例代码块 → 构造方法}}

答案:实例代码块总是先于构造方法执行。

Q3:同步代码块使用不当导致死锁

publicclassDeadLockDemo{privatefinalObjectlock1=newObject();privatefinalObjectlock2=newObject();publicvoidmethod1(){synchronized(lock1){System.out.println("method1持有lock1");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized(lock2){System.out.println("method1持有lock2");}}}publicvoidmethod2(){synchronized(lock2){System.out.println("method2持有lock2");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized(lock1){// 与method1相反的顺序导致死锁System.out.println("method2持有lock1");}}}}

解决方案:始终保持一致的锁获取顺序,或使用tryLock()等高级并发工具。

http://www.jsqmd.com/news/470485/

相关文章:

  • sudo 设置 - a
  • 从“屎山代码”到“智能体协同”:资深架构师眼中的财务自动化演进与 AI Agent 实战
  • 3. OpenClaw自定义skill通过环境变量传参
  • js基础和数据类型
  • 使用裁剪插件开发的 头像处理组件 响应式
  • 网安人的周末,其实是拉开差距的黄金期
  • 瑞祥商联卡回收渠道全解析:选择最佳方法安全又便捷 - 团团收购物卡回收
  • 一键把“纯色区划地图”数字化为 GIS 面要素:Map Digitizer Pro
  • 2026年大专商务英语毕业起薪规划与提升路径
  • 2026年中小企业主必看:北京代理记账公司选型指南与核心价值适配解析 - 十大品牌推荐
  • Flutter 三方库 malison 的鸿蒙化适配指南 - 强大的终端仿真与文本处理框架
  • 目前我还有一家同行业头部公司的offer,全年总包是XX万,贵司如果能把薪资调整到XX万,我可以立刻回绝其他offer,优先入职贵司
  • 什么是CRM?CRM系统和ERP有什么区别 - 纷享销客智能型CRM
  • OpenClaw 模型配置与切换经验分享
  • Idea中JDK版本引起的问题
  • 2026年比较好的上海办公室装修厂家推荐:上海写字楼装修制造厂家哪家靠谱 - 行业平台推荐
  • nacos连接DM达梦数据库
  • 电脑实时监控软件有什么?珍选8款电脑实时监控APP,2026新排行
  • 黄瓜遗传转化
  • 大模型开发全攻略:从零到一,打造你的智能应用!大模型项目实战教程(非常详细)
  • 人员姿态人员动作人员跌倒检测数据集VOC+YOLO格式7998张5类别
  • 安装 GitLab Runner
  • 中科蓝汛-对耳之间同步标志位状态-bt897
  • 代码写离婚协议:用递归函数分割房产——软件测试从业者的专业视角
  • 解决neo4j找不到neo4j.conf时修改neo4j.conf中内容方法
  • 【环境搭建】(八)Ubuntu22.04安装cuda_12.8.0+cudnn_8.9.6
  • C++<x>new和delete
  • LLM开发:Dify与LangChain入门讲解
  • sqli-labs注入全流程!2026最新(less8-13)
  • 如何解决0x80004005错误代码:全面剖析Windows系统更新问题与解决方案