[Java]-异常(进阶)

异常的概念

异常是程序在“编译”或者“执行”的过程中可能出现的问题

异常应该尽量提前避免,但是无法做到绝对避免,异常的可能情况太多,开发中只能提前干预。

异常一旦出现,如果没有提前避免,程序就会退出JVM虚拟机而终止,开发中异常是需要提前处理的。研究异常,并且避免异常,然后提前处理异常,体现的是程序的安全性,健壮性

Java会为常见的代码异常都设计一个类来代表

无标题脑图.mindmap

Error

错误的意思,严重错误Error,无法通过处理的错误,一旦出现,程序员难以进行修改,一般只能重启系统,优化项目。例如:内存崩溃,JVM本身崩溃

Exception

异常类,是开发中代码在编译或者执行过程中可能出现的错误,它是需要提前处理的,以便程序更加健壮
1. 编译时异常:继承自Exception的异常或者其子类,编译阶段就会报错,必须程序员进行处理,否则代码编译无法通过
2. 运行时异常,继承自RuntimeException的异常或其子类,编译阶段不报错,运行阶段出现,运行时异常可处理也可不处理

运行时异常

常见的运行时异常

  • 数组索引越界异常:ArrayIndexOutOfBoundsException
  • 空指针异常:NullPointerException(直接输出没有问题,但是调用空指针变量的功能就会报错)
  • 类型转换异常:ClassCastException
  • 迭代器遍历没有此元素异常:NoSuchElementException
  • 数学操作异常:ArithmeticException
  • 数字转换异常:NumberFormatException
public class test1 {


    public static void main(String[] args) {
        //数组索引越界异常:ArrayIndexOutOfBoundsException
        int[] array={10,20,30};
        System.out.println(array[3]);
        //空指针异常:NullPointerException
        String str1=null;
        System.out.println(str1); //直接输出没有问题
        System.out.println(str1.length());  //但是调用空指针变量的功能就会报错
        //类型转换异常:ClassCastException
        Object name="Leslie";
        Integer s=(Integer) name;
        //迭代器遍历没有此元素异常:NoSuchElementException
        //数学操作异常:ArithmeticException
        int c=10/0;
        //数字转换异常:NumberFormatException
        String num="23a";
        Integer n=Integer.valueOf(num);
    }
}

编译时异常

代码编译阶段就会报错
package ExceptionTest;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo {
    //这里如果不设置throws ParseException,就会抛出编译异常
    //原因就在于parse操作很容易不规范
    public static void main(String[] args) throws ParseException {
        String date="2015-01-12 10:23:21";
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d =sdf.parse(date);
        System.out.println(d);
    }
}

异常的默认处理机制

异常产生后的默认处理过程是自动处理过程

  1. 默认会在出现异常的代码处自动创建一个异常对象:ArithmeticException等
  2. 异常会从方法中出现的点这里先抛出给调用者,各层调用者不断抛出最终抛出给JVM虚拟机
  3. 虚拟机接收到异常对象后,现在控制台输出/打印异常栈信息数据
  4. 直接从当前执行的异常点终止当前程序
这种默认异常处理机制并不好(对于项目开发),一旦出现真的异常,会立即导致程序的死亡

编译时异常处理方式

方式一:直接抛出错误

方法 throws 异常1,异常2,...{
    ...
}

//上述方式,需要对异常逐个抛出,当异常很多时并不方便
//一般建议采用throws Exception这种方式,直接抛出根类异常

方法 throws Exception{
    ...
}
package ExceptionTest;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo1 {
    public static void main(String[] args) throws ParseException {
        System.out.println("----程序开始----");
        parseDate("2020-05-15 08:54:05");  //如果程序正确可以输出结果
        parseDate("2020/05/15/08/54/05");  //如果程序错误依旧会报错并直接终止程序
        System.out.println("----程序结束----");
    }
    public static void parseDate(String time) throws ParseException {
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d=sdf.parse(time);
        System.out.println(d);
    }

}

这种异常处理方式并不好,整个过程中只是逐层把异常向上层抛出,本质与默认处理方式相同,只是避免了编译阶段的报错,让程序能够正常编译。但一旦出现错误,仍然会导致JVM虚拟机终止程序。这种方法适用于程序较小,报错原因较易排查的项目

方式二:在出现异常的地方自己处理,谁出现谁处理

采用监视捕获异常的方式,即try catch
package ExceptionTest;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo1 {
    public static void main(String[] args)  {
        System.out.println("----程序开始----");
        parseDate("2020-05-15 08:54:05");  //程序正确
        parseDate("2020/05/15/08/54/05");  //程序错误
        System.out.println("----程序结束----");
    }

    public static void parseDate(String time) {
        try{
            SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date d=sdf.parse(time);
            System.out.println(d);
        }
        catch (ParseException e){  //可以并列多个异常,或是直接用Exception监视所有可能异常
            e.printStackTrace();  //打印异常栈信息,不会引起程序死亡
        }
    }

}

(在企业开发中,一般直接采用监视Exception根类的方式,这样可以监视并打印所有可能的异常)

第二种处理异常的方式可以处理异常,并且出现异常后代码也不会死亡而是正常执行,但这种方式也存在不足之处,在没有返回值的情况下,下层独自监视处理异常,导致上层不了解下层的处理结果而是盲目的运行程序

方式三:在出现异常的地方把异常逐层抛出给最外层调用者,最外层调用者几种捕获处理(规范做法)

package ExceptionTest;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo1 {
    public static void main(String[] args)  {
        System.out.println("----程序开始----");
        try {
            parseDate("2020-05-15 08:54:05");  //程序正确
            parseDate("2020/05/15/08/54/05");  //程序错误
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("----程序结束----");
    }

    public static void parseDate(String time) throws ParseException {
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d=sdf.parse(time);
        System.out.println(d);
    }

}

这种方案最外层调用者可以知道底层执行的情况,同时程序在出现异常后也不会立即死亡,这是理论上最好的方案

运行时异常的处理机制

运行时异常的处理规范:直接在最外层捕获处理即可,底层会自动抛出

package ExceptionTest;

public class ExceptionDemo2 {
    public static void MyDivision(int a,int b){
        System.out.println(a/b);
    }

    public static void main(String[] args) {
        System.out.println("----程序开始----");
        try{
            MyDivision(10,0);
            System.err.println("运行成功!");
        }catch (Exception e){
            e.printStackTrace();
            System.err.println("运行失败!");
        }
    }
}

异常捕获的本质就是直接将上层传输的异常捕获,然后输出它的异常栈信息,避免异常传输到JVM虚拟机,终止程序。从而保证了程序能够顺利执行完毕


finally关键字

用在捕获处理的异常格式中的,放在捕获异常的最后面
try{
    //可能出现异常的代码
}catch(Exception e){
    e.printStackTrace();
}finally{
    //无论代码是否出现异常还是正常执行
    //最终一定要执行这里的代码
}

在捕获异常中,try固定出现一次,catch出现0~N次(当存在finally时,可以省略catch,其他情况下不可以),finally可以出现至多一次

finally作用

可以在代码执行完毕之后进行资源(资源都实现了Close able接口,自带close()方法)的释放操作

package ExceptionTest;

public class ExceptionDemo2 {
    public static double MyDivision(double a,double b){
        try{
            return a/b;
        }
        catch (Exception e){
            return 0.0;
        }
        finally {
            System.out.println("====finally被执行====");
        }
    }

    public static void main(String[] args) {
        System.out.println(MyDivision(10,2));
        /* 这里就可以看出finally的强制执行效果
           程序正常执行,原本应该进入try,在try中会执行return语句
           return语句理应终止程序运行,但程序还是被强制执行了finally中的内容
           也就是在这里实际执行流程是在执行到return语句后被强制跳转到finally语句,先执行其中内容

           因此,在finally语句中加入return语句是十分危险的
           因为无论任何情况,只要finally中包含return语句,最后都只会执行finally中的return语句

         */


        //return 233; 
    }
}

自定义异常

自定义编译时异常

package ExceptionTest;


/*
* 自定义编译时异常:
* 1. 继承Exception
* 2. 重写构造器
* */


public class MyException extends Exception{
    public MyException() {
    }

    public MyException(String message) {
        super(message);  //调用父类构造器
    }

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }

    public MyException(Throwable cause) {
        super(cause);
    }

    public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

调用自定义异常

package ExceptionTest;

public class ExceptionDemo3 {
    public static void main(String[] args) {
        try {
            ageControl(12);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public static void ageControl(int age) throws MyException {
        if(age>60 || age<18){
            throw new MyException("/ age is illegal");
        }
        else{
            System.out.println("age is "+age);
        }
    }
}

自定义的编译时异常,在调用时会直接报错,所以直接用throws抛出给上层即可

注意:

throws用在方法上,用于抛出方法中的异常给调用方

throw用在出现异常的地方,用于创建异常对象且立即从此处抛出

自定义运行时异常的方式跟自定义编译时异常基本一致,只是运行时异常需要继承的是RuntimeException,并且在编译阶段并不会报错


### RT-DETRv3 网络结构分析 RT-DETRv3 是一种基于 Transformer 的实时端到端目标检测算法,其核心在于通过引入分层密集正监督方法以及一系列创新性的训练策略,解决了传统 DETR 模型收敛慢和解码器训练不足的问题。以下是 RT-DETRv3 的主要网络结构特点: #### 1. **基于 CNN 的辅助分支** 为了增强编码器的特征表示能力,RT-DETRv3 引入了一个基于卷积神经网络 (CNN) 的辅助分支[^3]。这一分支提供了密集的监督信号,能够与原始解码器协同工作,从而提升整体性能。 ```python class AuxiliaryBranch(nn.Module): def __init__(self, in_channels, out_channels): super(AuxiliaryBranch, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1) self.bn = nn.BatchNorm2d(out_channels) def forward(self, x): return F.relu(self.bn(self.conv(x))) ``` 此部分的设计灵感来源于传统的 CNN 架构,例如 YOLO 系列中的 CSPNet 和 PAN 结构[^2],这些技术被用来优化特征提取效率并减少计算开销。 --- #### 2. **自注意力扰动学习策略** 为解决解码器训练不足的问题,RT-DETRv3 提出了一种名为 *self-att 扰动* 的新学习策略。这种策略通过对多个查询组中阳性样本的标签分配进行多样化处理,有效增加了阳例的数量,进而提高了模型的学习能力和泛化性能。 具体实现方式是在训练过程中动态调整注意力权重分布,确保更多的高质量查询可以与真实标注 (Ground Truth) 进行匹配。 --- #### 3. **共享权重解编码器分支** 除了上述改进外,RT-DETRv3 还引入了一个共享权重的解编码器分支,专门用于提供密集的正向监督信号。这一设计不仅简化了模型架构,还显著降低了参数量和推理时间,使其更适合实时应用需求。 ```python class SharedDecoderEncoder(nn.Module): def __init__(self, d_model, nhead, num_layers): super(SharedDecoderEncoder, self).__init__() decoder_layer = nn.TransformerDecoderLayer(d_model=d_model, nhead=nhead) self.decoder = nn.TransformerDecoder(decoder_layer, num_layers=num_layers) def forward(self, tgt, memory): return self.decoder(tgt=tgt, memory=memory) ``` 通过这种方式,RT-DETRv3 实现了高效的目标检测流程,在保持高精度的同时大幅缩短了推理延迟。 --- #### 4. **与其他模型的关系** 值得一提的是,RT-DETRv3 并未完全抛弃经典的 CNN 技术,而是将其与 Transformer 结合起来形成混合架构[^4]。例如,它采用了 YOLO 系列中的 RepNCSP 模块替代冗余的多尺度自注意力层,从而减少了不必要的计算负担。 此外,RT-DETRv3 还借鉴了 DETR 的一对一匹配策略,并在此基础上进行了优化,进一步提升了小目标检测的能力。 --- ### 总结 综上所述,RT-DETRv3 的网络结构主要包括以下几个关键组件:基于 CNN 的辅助分支、自注意力扰动学习策略、共享权重解编码器分支以及混合编码器设计。这些技术创新共同推动了实时目标检测领域的发展,使其在复杂场景下的表现更加出色。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值