结合项目理解super关键字的用法【Java基础题】

1.super关键字的用法

super主要与继承有关系。一般指向(离自己最近)的父类。

几种用法:

(1).普通的直接引用

直接用super.xxx引用父类的成员(不能访问private修饰的成员)

(2).子类的成员变量(或方法)与父类的成员变量(或方法)同名时,用super进行区分

当子类和父类有同名的成员(包括属性与方法)时,可能是 方法覆盖(Override)或者 字段隐藏(Field Hiding),就可以用super来调用父类中被override的同名方法,或者父类中被隐藏的字段。

关于字段隐藏(Field Hiding)

子类在继承父类的时候,可以定义与父类中被修饰为public或protected或者default的相同名称和类型的属性,尽管它们的值可以不同。

当子类定义了一个与父类同名的属性时,子类的该属性会隐藏父类中对应的属性。这意味着,当通过子类的实例访问这个属性时,将访问到子类中定义的属性,而不是父类中的。

字段隐藏并不影响方法的多态性。也就是说,如果子类覆盖了父类的方法,那么无论是父类类型的引用还是子类类型的引用,调用该方法时都遵循多态原则,即调用的是实际对象类型的方法实现。但是,对于字段访问,Java使用的是静态绑定,即字段的访问只与引用变量的编译时类型有关,而与实际对象的类型无关。

例子:

class Parent {
    String name = "Parent";
}

class Child extends Parent {
    String name = "Child"; // 隐藏父类的name属性
}

public class Test {
    public static void main(String[] args) {
        Parent parent = new Parent();
        Child child = new Child();
        Parent parentRefToChild = new Child();

        System.out.println(parent.name); // 输出 Parent
        System.out.println(child.name); // 输出 Child
        System.out.println(parentRefToChild.name); // 输出 Parent,因为访问的是Parent类的name属性
    }
}

但如果,如果父类中的变量是用private修饰符修饰的,那么这个变量只能在父类内部被访问,对于子类来说是不可见的。在这种情况下,如果子类定义了一个同名的变量,这并不是隐藏(因为private变量对于子类来说是不可见的),而是简单地在子类中定义了一个新的、独立的变量。

(3).引用父类的构造函数

super(参数):在子类的构造器中的第一句,可以使用super调用父类的构造器,这通常是为了初始化继承自父类的部分。(如果你没有在子类构造器中显式调用父类的构造器(无论是默认还是参数化的),Java编译器会插入一个默认的super()调用到子类构造器的最开始部分。这意味着父类的无参构造器会被调用。这是属于隐式的。如果父类没有无参构造器(且没有其他构造器被显式调用),则会编译错误。)。

this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)

2.this与super的区别

相对应的,this也有三种用法:普通地引用自身、形参与成员名重名时用this区分、引用本类地构造函数

this与super的区别:

super()和this()均需放在构造方法内第一行。

尽管可以用this调用一个构造器,但却不能调用两个。

this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句 的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

this()和super()都指的是对象,所以,均不可以在static环境中【需延伸知识点】使用。包括:static变量,static方法,static语句块。

原因:static成员属于类本身,在类加载时执行的,而不是类的任何特定对象实例。与对象实例的创建无关。而this和super都要涉及到对象。

从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字

原因:this在Java中被用作一个引用变量,它指向了当前对象或当前正在执行的方法所属的对象。你可以将this视为一个“指针”(虽然Java中没有指针的概念,通常我们说的是引用),它指向了对象自身。

从本质上来说,this作为一个引用变量,直接指向了当前对象,而super作为一个关键字,提供了一种方式来访问父类的成员(属性和方法)。this强调的是对当前对象本身的引用,而super强调的是对父类成员的访问权限。(this与对象的具体地址相关联,super并不储存任何父类对象的具体地址)

3.super是指针吗?

在Java中,super不是指针,而是一个关键字,它用于在子类中引用父类的对象。这种引用方式使得子类能够访问从父类继承的方法和属性。尽管在概念上你可以认为super提供了一种方式来"指向"父类,但它实际上并不存储任何内存地址或指针信息,如同C或C++语言中的指针那样。

在Java中,super关键字能够找到对应的父类或父类的方法是通过Java的类加载机制【需延伸知识点】运行时类型信息(RTTI)来实现的。当Java程序被编译时,每个类的信息(包括其父类信息、方法、字段等)都会被编译到生成的字节码中。这个过程确保了在运行时,Java虚拟机(JVM)能够准确地知道每个类的结构以及它们之间的继承关系。

以下是super关键字工作原理的简化说明:

  1. 类加载时的角色:当类被加载到JVM时,它的类定义中包含了父类的引用信息。这意味着JVM在加载类时已经明确了哪个是父类。因此,当使用super关键字时,JVM已经具备了足够的信息来确定对应的父类。

  2. 运行时解析:当代码中使用super关键字调用一个方法时,JVM会在运行时查找当前对象的父类,并解析出要调用的父类方法。这是通过方法调用的动态绑定实现的,其中JVM根据对象的实际类型来确定方法调用应该如何被解析和执行。

  3. 编译时检查:在Java代码被编译成字节码时,编译器会检查super关键字的使用是否合法,比如检查是否存在对应的父类方法。这个阶段确保了在运行时使用super时能够找到正确的方法。

  4. 继承层次的遍历:如果存在多层继承,JVM会按照继承层次向上遍历,直到找到对应的方法或字段。这个过程是自动的,确保了即使在复杂的继承关系中,super也能正确地引用到父类的成员。

总的来说,super关键字能够正确找到对应的父类或父类的方法,依赖于Java的编译时检查和运行时动态方法解析机制。这些机制确保了代码在运行时的行为与你通过super所期望的父类引用是一致的。

 

4.项目中使用super的例子

这里有一个生成优惠券的参数DTO:

public class CouponCreateReqDTO {
    /**
     * 营销规则id
     */
    private Long id;
    /**
     * 优惠券名称
     */
    @NotEmpty(message = "优惠券名称不能为空!",groups = {Add.class})
    @Size( max = 32, message = "优惠券名称不能超过64字节;")
    private String name;
    /**
     * 优惠券说明
     */
    @NotEmpty(message = "优惠券说明不能为空!",groups = {Add.class})
    //@Size( max = 64, message = "优惠券说明不能超过128字节;")
    private String memo;
    /**
     * 优惠券描述
     */
    @NotEmpty(message = "优惠券描述不能为空!",groups = {Add.class})
    @Size( max = 128, message = "优惠券描述不能超过256字节;")
    private String description;
    /**
     * 是否模板,1是,0否
     */
    @NotNull(message = "是否模板不能为空",groups = {Add.class})
    private Integer temlpateType;
    /**
     * 开始时间,模板券不填
     */
    private String beginAt;
    /**
     * 结束时间,模板券不填
     */
    private String endAt;
    /**
     * 优惠券类型 1满减券 2折扣券
     */
    @NotNull(message = "优惠券类型不可为空",groups = {Add.class})
    private Integer type;
    /**
     * 有效期,年|月|日,如0|1|0表示有效期1个月
     */
    private Integer templateTime;
    /**
     * 品牌列表,全部品牌传空列表
     */
    @NotNull(message = "销售前端不能为空",groups = {Add.class})
    private List<Long> brandIdList;
    /**
     * 类目列表,全部类目传空列表
     */
    @NotNull(message = "类目列表不能为空",groups = {Add.class})
    private List<Long> categoryIdList;
    /**
     * 商品设置,0全部商品,1指定商品
     */
    @NotNull(message = "商品设置不能为空",groups = {Add.class})
    private Integer productSet;
    /**
     * 店铺设置,0全部店铺,1全部门店,2仅官网,3指定店铺
     */
    @NotNull(message = "店铺设置不能为空",groups = {Add.class})
    private Integer shopSet;
    /**
     * 等级Id列表,全部等级传空列表
     */
    @NotNull(message = "等级Id列表不能为空",groups = {Add.class})
    private List<Long> levelIdList;
    /**
     * 状态,1启用,0禁用
     */
    @NotNull(message = "状态不能为空",groups = {UpdateStatus.class,Add.class})
    private Integer status;
    /**
     * 使用门槛,单位元
     */
    @NotNull(message = "使用门槛不能为空",groups = {Add.class})
    private BigDecimal threshold;
    /**
     * 优惠金额,单位元
     */
    private BigDecimal amount;
    /**
     * 优惠折扣比例
     */
    private BigDecimal discount;
    /**
     * 总数量
     */
    @NotNull(message = "总数量不能为空",groups = {Add.class})
    private Integer quantity;
    /**
     * 每人最多领取数量
     */
    @NotNull(message = "每人最多领取数量不能为空",groups = {Add.class})
    private Integer maxHoldQuantity;
    /**
     * 兑换所需积分
     */
    private Integer exchangNeedPint;
    /**
     * 开始显示时间
     */
    private String showBeginAt;
    /**
     * 显示结束时间
     */
    private String showEndAt;
    /**
     * 渠道
     */
    @NotNull(message = "销售前端不能为空",groups = {Add.class})
    private List<Long> frontIdList;

    private Integer show;
    /**
     * 优惠券分享图片
     */
    private String sharePic;
    /**
     * 开始生效时间
     */
    private Integer templateAvailableTime;

    /**
     * 是否正价券
     */
    private Boolean isPositivePrice;

   

  //忽略对应的public的get、set方法以及构造方法等

   



}

 对应的有一个修改优惠券的参数DTO:

public class CouponUpdateReqDTO extends CouponCreateReqDTO {
    @Override
    @NotNull(message = "优惠券id不能为空")
    @Min(value = 0, message = "优惠券id必须大于0")
    @Digits(integer = 19, fraction = 0, message = "优惠券id必须为正整数")
    public Long getId() {
        return super.getId();
    }
}

GPT对这样设计的分析:

这种设计模式是在软件开发中常见的一种做法,尤其是在处理与数据传输对象(Data Transfer Objects, DTOs)相关的场景中。CouponCreateReqDTOCouponUpdateReqDTO类的设计体现了一种继承关系,用于区分创建和更新优惠券时所需的请求数据。下面是这种设计方法的几个关键点和潜在的意义:

代码复用

通过继承CouponCreateReqDTOCouponUpdateReqDTO可以复用所有在CouponCreateReqDTO中定义的字段和方法,而不需要重新编写相同的代码。这减少了代码重复,使得代码更加简洁和易于维护。

功能扩展

尽管CouponUpdateReqDTO在当前示例中没有新增任何字段或方法,通过重写getId方法并添加验证注解(如@NotNull, @Min, @Digits),它实际上扩展了基类的功能。这意味着在更新优惠券时,对ID的要求更为严格,确保了优惠券ID的有效性和合法性。(原来约束注释可以用在方法上!

参数验证

CouponCreateReqDTO中,使用注解(如@NotEmpty, @NotNull, @Size等)对各个字段进行参数验证,确保了创建优惠券请求的数据满足特定的条件。CouponUpdateReqDTO通过继承这些设置,保证了更新操作也会进行相应的验证。此外,通过在子类中对getId方法进行更严格的验证,进一步确保了更新操作的数据完整性和正确性。

面向不同的操作定义不同的类

通过为创建和更新操作定义不同的DTO类,可以明确区分这两种操作的数据要求。虽然在很多情况下,创建和更新操作所需的数据字段可能大致相同,但也可能存在一些差异,例如在更新操作中可能需要一个明确的ID,而创建操作则不需要。通过使用继承,可以在保持代码整洁的同时,灵活地为不同的操作定义不同的数据要求。

总结

这样的设计允许开发者清晰地分离和管理不同操作(如创建和更新)所需的数据结构,同时通过继承机制来实现代码复用和功能扩展。它还利用了Java的注解和验证框架来确保数据的合法性和正确性,这对于维护数据完整性和应用安全性至关重要。

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值