Java基础总结三(泛型、异常)

Java 泛型是什么?一文带你吃透泛型 它是一个泛型类,而我之前使用的时候并没有传递,说明 Java 语法是允许的,这个时候传递的类型是 Object 类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。泛型接口有两使用方式,要么在实现接口时确定泛型类的类型,要么在实现接口时先不确定泛型类类型,而在实例化对象时再确定。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。,泛型在类中可以创建变量,也可以作为方法的参数或者返回值。 阅读详情

Java基础总结三(泛型、异常)

泛型

泛型的创建

泛型类

我们最常用泛型的地方就是集合,因此,我们编写自己的List来体会泛型:

public class TestList<T> {

    private Object[] instances = new Object[0];

    public T get(int index) {
        return (T) instances[index];
    }

    public void set(int index,T newInstance) {
        instances[index] = newInstance;
    }

    public void add(T newInstance) {
        instances = Arrays.copyOf(instances,instances.length + 1);
        instances[instances.length - 1] = newInstance;
    }
}

看一下使用方法:

TestList<String> testList = new TestList<>();
testList.add("wlk");
String str = testList.get(0);
System.out.println(str); // wlk
泛型接口

我们除了创建泛型类,泛型接口与其很类似:

public interface Shop <T>{
    T buy();
    float refund(T item);
}

// 实现
public class RealShop<E> implements Shop<E>{
    @Override
    public E buy() {
        return null;
    }

    @Override
    public float refund(E item) {
        return 0;
    }
}

再比如,我们想要实现一个水果商店:

public class Fruit {
}

public class Apple extends Fruit{
}

public class FruitShop<E> implements Shop<E>{
    @Override
    public E buy() {
        return null;
    }

    @Override
    public float refund(E item) {
        return 0;
    }
}

FruitShop<Apple> fruitShop = new FruitShop<>(); // 这个商店卖苹果
FruitShop<Phone> stringShop = new FruitShop<>(); // 这个水果商店卖手机??? 当然不和逻辑

由于我们的商店泛型 E 没有加以限制,会导致错误,我们来给他加上限制:

public class FruitShop<E extends Fruit> implements Shop<E>{
    @Override
    public E buy() {
        return null;
    }

    @Override
    public float refund(E item) {
        return 0;
    }
}

现在这个水果商店只可以卖 Fruit 或者 Fruit 的子类,如Apple,而不能卖手机。

泛型方法

自己声明了泛型的方法

泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

我们给我们的苹果商店创建一个方法,它支持用其他水果换取苹果:

public class FruitShop<E extends Fruit> implements Shop<E>{
    @Override
    public E buy() {
        return null;
    }

    @Override
    public float refund(E item) {
        return 0;
    }

    public <T extends Fruit> E exchange(T item){
        return null;
    }
}

// 使用
FruitShop<Apple> appleShop = new FruitShop<>();
Apple apple = appleShop.<Banana>exchange(new Banana());
//类型推导,<Banana>可省略
Apple apple = appleShop.exchange(new Banana());

类型擦除

泛型的好处是在编译期进行类型检查和类型转换,且泛型只在编译期有效。泛型是在 JDK 1.5 里引入的,如果不做泛型擦除,那么 JVM 需要对应使得能正确的的读取和校验泛型信息;另外为了兼容老程序,需为原本不支持泛型的 API 平行添加一套泛型 API。

在运行期,声明时的泛型参数会被擦除,在使用处的泛型会被泛型的父类替换,如<E extends Fruit>,此时使用E的地方会被Fruit替换。

泛型的协变与逆变

定义:如果A、B表示类型,f()表示一个类型的构造函数,Type1≤Type2表示Type1是Type2的子类型,Type1≥Type2表示Type1是Type2的超类型;

  • f()是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;
  • f()是逆变(contravariant)的:当A≤B时有f(B)≤f(A)成立;
  • f()是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。

对于任意两个不同的类型 Type1 和 Type2,不论它们之间具有什么关系,给定泛型类 G<T>,G<Type1> 和 G<Type2> 都是没有关系的,即 Java 中泛型是不变的,而数组是协变的,可以验证以下:

ArrayList<Number> list = new ArrayList<Integer>(); // 编译器报错

// 可以顺利通过编译,正常使用。
Number[] arr = new Integer[5];
arr[0] = 1;
// 但也有一定的问题,这个数组接受Number其他子类也不会在编译期报错,而在运行期报错。泛型从根源杜绝了这种错误

在Java的泛型中,是可以支持协变与逆变的,但会有很大的限制:

  • ? extends T(上边界通配符)实现协变关系,表示?是继承自T的任意子类型。也表示一种约束关系,只能提供数据,不能接收数据。
  • ? super T(下边界通配符)实现逆变关系,表示?是T的任意父类型。也表示一种约束关系,只能接收数据,不能提供数据。

协变所谓只能提供数据,不能接收数据,是指在实例化出泛型类后,仅可以调用该类中返回值为泛型的方法,而不可以调用参数为该泛型的方法。

逆变则恰好相反。

那么如此限制,?在我们实际开发中应该如何应用呢?

List<? extends Number> list = new ArrayList<Integer>();

这样的用法在我们平时是用不到的,只有一些场景化的需要,才会使用到泛型通配符。就拿我们的水果商店举例,我们现在有这样一个需求,需要计算集合中水果的总重量,所以编写了如下方法:

public int totalWeight(List<? extends Fruit> list) {
    int total = 0;
    for(Fruit fruit : list) {
        total += fruit.weight;
    }
    return total;
}

// 使用起来,就可以向这个方法中传入任意Fruit子类的集合了
List<Apple> appleList = new ArrayList<>();
totalWeight(appleList);
List<Banana> bananaList = new ArrayList<>();
totalWeight(bananaList);

再举一个逆变的用法:

List<Apple> appleList = new ArrayList<Fruit>();

当然这种用法是极其少有的,不做讨论。我们给苹果类添加一个方法,这个方法的作用就是给苹果装入集合:

public class Apple extends Fruit{

    public void addToList(List<? super Apple> list) {
        list.add(this);
    }

}

这样,当我们传来Apple集合的时候,就会把Apple装入这个集合,同时,我们也可以传入Fruit的集合:

List<Apple> appleList = new ArrayList<>();
List<Fruit> fruitList = new ArrayList<>();
List<Banana> bananaList = new ArrayList<>();

Apple apple = new Apple();
apple.addToList(appleList); // 编译通过
apple.addToList(fruitList); // 编译通过
apple.addToList(bananaList); // 报错

异常

异常体系

Java中异常的体系是树形结构,所有异常的超类是Throwale,它有俩个子类:Error和Exception,分别表示错误和异常,其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。

  1. Error与Exception
    • Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
    • Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常,程序中应当尽可能去处理这些异常。
  2. 运行时异常和非运行时异常
    • 运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,程序中可以选择捕获处理,也可以不处理。
    • 非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过,如IOException、SQLException等以及用户自定义的Exception异常。

异常处理

  • 在try中return后,依旧会执行finally。
  • finally语句在return语句执行之后return返回之前执行的。
  • finally块中的return语句会覆盖try块中的return返回。
  • 如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变也可能不变(具体区分值和引用)。
  • 当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。
  • 在Java中如果不发生异常的话,try/catch不会造成任何性能损失。在 Java 类编译后,正常流程与异常处理部分是分开的,类会跟随一张异常表,每一个try-catch都会在这个表里添加行记录。当执行抛出了异常时,首先去异常表中查找是否可以被catch,如果可以则跳到异常处理的起始位置开始处理,如果没有则原地return,并且copy异常的引用给父调用方,接着看父调用的异常表,以此类推。

我们来看一个例子:

public int test(){
    int i = 1;
    try {
        i++;
        return i;
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        System.out.println("finally");
        i++;
    }
    return 0;
}

System.out.println(test()); 

// out ->
finally
2

我们看到,finally执行了,那么i++应该也是执行了,为什么返回2,我们通过查看字节码可知

Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iinc          1, 1
         5: iload_1
         6: istore_2
         7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #12                 // String finally
        12: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: iinc          1, 1
        18: iload_2
        19: ireturn
        20: astore_2
        21: aload_2
        22: invokevirtual #15                 // Method java/lang/Exception.printStackTrace:()V
        25: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: ldc           #12                 // String finally
        30: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        33: iinc          1, 1
        36: goto          53
        39: astore_3
        40: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        43: ldc           #12                 // String finally
        45: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        48: iinc          1, 1
        51: aload_3
        52: athrow
        53: iconst_0
        54: ireturn

总结一下,总体的意思就是,当执行完try块的时候,会计算return表达式的值,然后把这个返回值存在另一个临时变量里面,最后返回的时候会重新读取存放的变量,因此在finally后面无论如何修改,都不会影响返回值。那么,如果finally中加了return后,结果就会覆盖原有的返回值了。

FAA和EASA在机载电子硬件适航中的发展历史与差异 FAA和EASA在机载电子硬件的适航审查中虽然存在一些差异,但通过近年来的合作,这些差异正在逐步缩小。两者通过。 阅读详情

相关推荐

Spring Authorization Server自定义密码模式:突破标准OAuth2限制,实现MFA与复杂业务集成

OAuth 2.0授权框架是构建现代应用安全体系的基石,其密码模式(Resource Owner Password Credentials Grant)提供了一种直接的资源所有者凭证交换令牌的机制。该模式的核心原理是客户端收集用户密码并直接向授权服务器请求访问令牌,适用于受信任的客户端场景。在技术价值上,它简化了某些传统应用的OAuth集成流程。然而,标准实现往往难以满足复杂的现实业务需求,例如需要集成多因素认证(MFA)、对接外部LDAP目录服务或执行自定义密码加密校验等场景。Spring Authori

weixin_34302561的博客 438

java异常处理机制

文章目录java异常处理机制一、异常描述二、异常处理机制01、使用try...catch捕获异常02、异常类的继承体系03、Java7提供的多异常捕获04、访问异常信息05、使用finally回收资源06、异常处理的嵌套07、自动关闭资源的try语句三、Checked(检查)异常和Runtime(非受查)异常体系01、两者关系与区别02、使用throws声明抛出异常四、使用throw抛出异常01、...

qq_36649701的博客 874

Camera Sensor接口协议全解析(六)聚焦onsemi HiSPi接口的技术突破与应用实践

摘要: Onsemi HiSPi是一种高速并行接口协议,专为高分辨率图像传感器设计,具备1600 Mbps传输速率、低功耗和高兼容性特点。通过对比DVP、MIPI CSI-2等主流协议,HiSPi在工业/医疗领域表现突出,支持动态功耗控制、多ROI和CRC校验。其创新架构包含多PHY设计,提供分组SP、流SP等多种工作模式,满足灵活传输需求。实际应用中,HiSPi在PCB缺陷检测(5μm精度)和液晶面板检测(0.3% Mura检出率)等场景展现卓越性能,成为高速成像领域的关键技术。

GateWorld的博客 1437

Java异常类和常用类,容器,泛型

异常类分两大类型:Error类代表了编译和系统的错误,不允许捕获;Exception类代表了标准Java库方法所激发的异常。Exception类还包含运行异常类Runtime_Exception和非运行异常类Non_RuntimeException这两个直接的子类。运行异常类对应于编译错误,它是指Java程序在运行时产生的由解释器引发的各种异常。运行异常可能出现在任何地方,且出现频率很高,因此为了避免巨大的系统资源开销,编译器不对异常进行检查。所以Java语言中的运行异常不一定被捕获。

2301_77204575的博客 751

涨姿势|小众建模软件Make human,MakeClothes插件使用详解教程(2)

衣服可以从头开始建模,或者通过改变人体网格或由MakeClothes工具提供的“服装助手”(见下文)来建模。在设计和建模一件衣物时,应该记住两个限制。首先,将服装网格映射到人体网格的算法要求服装网格完全由四面体组成。其次,知道MakeClothes每件衣服只支持一种材料是很重要的。 完成MakeClothes工作流程后,您的HOME / makehuman / v1 / data / clothes中的工具将创建一个新文件夹。该文件夹将采用用户为服装项目提供的唯一名称。该文件夹将填充必要的资源,以使该项目在

Crazy_in_3Dmax的博客 1875

Anomalib实战指南:从零开始构建自定义异常检测数据集

本文提供了一份详细的Anomalib实战指南,指导用户从零开始构建自定义异常检测数据集。文章深入解析了数据准备的核心步骤、目录结构规范,并对比了PatchCore与FastFlow等关键模型的适用场景。通过清晰的代码示例,手把手演示了环境配置、模型训练与推理评估的全流程,旨在帮助开发者快速掌握这一工业视觉异常检测利器,有效解决实际生产中的缺陷识别难题。

weixin_29054245的博客 644

异常、泛型、集合框架

Java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类。1、自定义编译时异常:定义一个异常类继承Exception类,并重写构造方法,通过throw new 异常类(xxx) 创建异常对象并抛出。2、自定义运行时异常:定义一个异常类继承RuntimeException类,并重写构造方法,通过throw new 异常类(xxx) 创建异常对象并抛出。//自定义编译时异常//1、继承Exception做爸爸。

2303_76664877的博客 1043

STM32声源定位:实现声源定位的嵌入式解决方案

通过以上代码,我们可以获取麦克风音频信号样本,并使用交叉相关法计算声源的位置。然而,我可以帮助您解答有关STM32声源定位的问题,提供相关的代码和指导。获取了麦克风音频信号后,我们可以使用信号处理算法来计算声源的位置。该算法通过计算不同麦克风之间的信号延迟来确定声源的方向。首先,我们需要获取每个麦克风的音频信号。声源定位是一项重要的技术,可以帮助我们确定声音的来源位置。在嵌入式系统中,使用STM32微控制器可以实现精确的声源定位。声源定位的基本原理是通过多个麦克风阵列接收到的声音信号来计算声源的位置。

UplKubernetes的博客 2582

泛型与异常处理

1.1    异常的继承体系 在Java中使用Exception类来描述异常。 查看API中Exception的描述,Exception 类及其子类是 Throwable 的一种形式,它用来表示java程序中可能会产生的异常,并要求对产生的异常进行合理的异常处理。   继续观察,我们可以发现Exception有继承关系,它的父类是Throwable。Throwable是Java 语言中所有

小菜鸟的博客 693

《拉依达的嵌入式\驱动面试宝典》—计算机硬件篇(一)

《拉依达的嵌入式\驱动面试宝典》 整合了所有我认为可能遇到的技术面试问题。所有问题及其答案均是我个人学习后查阅资料总结,每个回答都做了仔细的分析,在嵌入式及其驱动面试相关问题解析做到全网独一无二。学习或者转行到嵌入式、驱动开发领域学习的不二之选

拉依达不拉跨的博客 769

173、LLC谐振变换器的PCB设计实战(布线)

LLC谐振变换器PCB设计实战要点 LLC谐振变换器的PCB设计核心在于控制寄生参数,避免高频振铃和EMI问题。功率回路需确保电流路径最短且紧耦合,谐振电容焊盘需加宽以减少发热。驱动走线应独立于功率回路,避免干扰,并合理控制宽度和长度。变压器绕组结构影响共模噪声,建议原边在外层并加屏蔽铜箔。地平面设计需根据功率等级选择整块或分割,大功率下功率地与信号地应单点连接。谐振电流采样推荐四端开尔文电阻以减少电感干扰。散热需优化铜皮和过孔布局,功率器件优先放置顶层。布局应先确定谐振腔,再排布控制电路。经验上需实测寄生

Jack010169的博客 47

11.泛型与异常

泛型与异常,包括throws和throw区别,throws和try catch 区别,内含练习代码

改变从现在开始 405

JavaScript实现车牌识别系统(附完整源码)

JavaScript实现车牌识别系统(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1113

Java基础总结三(泛型、异常

当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了。

2401_83641098的博客 904

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

【Java编程动手学】Java三大核心特性:异常、注解与泛型

Java异常处理、注解系统和泛型机制是构建健壮程序的三大支柱。异常体系采用分层结构(Throwable/Error/Exception),通过声明式、捕获式和异常链实现错误处理。注解系统支持元数据编程,从编译期检查(@Override)到运行时处理(自定义注解)。泛型通过类型参数约束和通配符(PECS原则)确保类型安全,同时保持代码灵活性。这些特性共同保障了Java程序的安全性与可扩展性。

杰西笔记 976

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

JAVA基础学习(三)——反射、注解、泛型、集合、IO及异常等

本文汇总一下杂七杂八的知识,重要程度会在文中表述。一、反射在学golang时候接触过反射,但并没有看懂,这里温习一下。现在给你一个类Object,要获取他的字段,方法,父类等信息,如何做呢?知道这个类是啥,求上述信息很简单,但万一不知道呢?在测试中可能就会出现这种情况。反射就是程序在运行期间,获取对象的各种信息的过程。换句话说,根据class实例,获取class各种信息的过程就是反射。任何一个类class的实例都是由JVM动态加载实现的。

m0_45978228的博客 1845

Java语言基础(三)——异常、容器、泛型

一、异常 实际开发工作中,遇到的情况不可能是完全理想的,其遇到的问题就叫异常。Exception。 1.1、简单异常 1.1.1、检查型异常 最具代表性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如用户要打开一个不存在的文件时引发的异常,这些异常在编译时不能被简单地忽略。 1.1.2、运行时异常 是可能被程序员避免的异常,与检查性异常相反,运行时异常可以在编译时忽略 1.1.3、错误Error 错误不是异常,而是脱离程序员控制的问题。错误在代码经常被忽略。例如当栈溢出,一个异常就

weixin_42956490的博客 639

Java语法基础三:异常处理、泛型和反射

异常处理的分类、异常处理机制;泛型的定义、作用和使用,和反射的定义、操作示例和优缺点

Xyuan的博客 1048

【Java编程】JavaSE基础总结(三):异常机制、泛型

在理想的情况下,我们的程序会按照我们的思路去运行,按理说是不会出现问题的,但是,代码实际编写后并不一定是完美的,可能会有我们没有考虑到的情况,如果这些情况能够正常得到一个错误的结果还好,但是如果直接导致程序运行出现问题了呢?

Data · Model · Agent · Repeat 588

Effective Java读书笔记(三):泛型、枚举、注解、异常

来到读书笔记的第三篇了,这一次的主题是讲泛型、枚举、注解这三个Java1.5引入的语言特性。关于泛型,很久以前我写过一篇初识的文章,http://blog.youkuaiyun.com/leelit/article/details/39504873,里面的一些基础知识现在看起来依然能起到复习作用。而另外两个特性的基础知识这里不再展开,因为Effective Java这本书讲的就是一些编程技巧或者说良好的习惯。然

leelit笔记本 1168

1.Java基础(三)-异常、泛型、匿名内部类、集合

项目 数据库管理系统StudentSystemV1.0 包名结构: cn.system.student.ocean888.entity --实体类 Student.java cn.system.student.ocean888.manager --管理类 StudentManager.java cn.system.student.ocean888.test --测试类 SystemTest.java cn.system.studen

Love&Share 3238

异常,泛型,集合框架

java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了.

liang_zhi_bin的博客 435

【java基础】泛型

1、 泛型概述 我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。 大家观察下面代码: public class GenericDemo { public static void main(String[] args) { Collection coll = new Arr...

宜春 1万+
上一篇: Java基础总结二(集合)
下一篇: Java集合源码分析—ArrayList
<天各一方>
博客等级 码龄6年 17粉丝 17原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值