首页 > 其他分享 >Lab2 - ADT&OOP 回顾总结

Lab2 - ADT&OOP 回顾总结

时间:2024-05-08 12:33:50浏览次数:8  
标签:ADT 静态方法 对象 方法 equals Lab2 回顾总结 static 重写

Lab2-ADT & OOP 回顾

在忙于干活与忙于划水的薛定谔叠加态中度过一个月后想起了博客,考虑到如果自己再不回顾之前的实验+复习软件构造内容就要和肯尼迪和安倍晋三一桌打复活赛结果会很悲惨的情况,决定打开博客开始码字。

现在对Lab2- ADT & OOP的内容进行回顾

目录

Static与非Static的区别(变量与方法)

Static/非Static变量

在C语言的学习中已经接触过,static修饰下变量全局可用,可以在当前整个程序源码中任意位置供其他函数调用。
在所有函数外初始化的数据结构默认视为被static修饰的变量。

# include <stdio.h>
# include <stdlib.h>
# include <windows.h>

// 计时用 基于含Windows api的头文件
// 这里初始化的数据是整个源码内可用的 
LARGE_INTEGER temp; // 用于存储性能计数器的值
LONGLONG start, end, freq; // 用于存储开始时间、结束时间和频率
int origin_num[N];
int num1[N];
int main()
{
    int static overall_mark = 0; // 函数中也可使用
    // ...
}

而在Python,Java等面向对象的编程语言中,类内static修饰的变量称为类变量/全局变量/成员变量。如同C中static修饰的变量在运行之初初始化加载一样,这些变量是在类被加载时候就被初始化的,只要类存在,类变量就存在。

非static修饰的成员变量是在对象new出来的时候划分存储空间,是与具体的对象绑定的,该成员变量仅为当前对象所拥有的。

在引用类变量时,我们直接通过类名.变量名调用,对象在引用实例变量时只能通过对象名.变量名调用。在类中调用成员变量时直接调用或者以类名.变量名方式调用,实例变量则用this或者直接调用。

例如:

public class Test1 {
    private static String testKey = "Init Key";
    private String testObjectKey;
    public Test1(){
        testObjectKey = "Init Object Key";
    }
    
    public static void main(String[] args){
        Test1 testObject = new Test1();
        System.out.println(testObject.testKey);
        System.out.println(Test1.testObjectKey);
    }
}

在上面的代码中,main方法里第2行代码会提示Warning:

无法从 static 上下文引用非 static 字段 'testObjectKey'

第三行代码则直接在testObjectKey显示红色,并在主方法内第一行代码处报错:

通过实例引用访问 static 成员 'Test1.testKey'

在之前的Lab1的P3中,如下:

public class FriendshipGraph {
    // Object "person" existed in current Friendship graph.
    private static Set<String> personSet;

    public FriendshipGraph(){
        personSet = new HashSet<>();
    }

笔者对FriendshipGraph类内用于统计图内已有Person顶点的集合属性personSet错误地添加了static关键字,并在主方法中直接通过属性名调用personSet。这样做就导致在测试类内先运行的测试方法会将之前的图数据保留,从而影响后面测试方法的结果。

笔者并未意识到这个static关键字的问题,选择在构造方法内将这个共用的静态控件重新初始化为一新的空集合,这样可以解决先运行的测试方法结果影响后面测试方法的结果的问题,但一旦遇到同时需要统计多个友谊图的情况,便会出现后一个友谊图覆盖前一个友谊图的问题。

最终修改代码,删去static关键字并在主方法中初始化一个FriendshipGraph对象,用对象名.属性名访问每个对象单独的personSet属性用于统计,问题解决。

private static Set personSet; -> private static Set personSet;

(in main method)

FriendshipGraph friendshipGraph = new FriendshipGraph();

personSet -> friendshipGraph.personSet

方法上

静态方法使用static关键字修饰,属于类而不属于对象,在实例化对象之前我们便可以通过“类名.方法名”调用静态方法。

  • 在静态方法中,可以调用静态方法。
  • 在静态方法中,不能调用非静态方法。
  • 在静态方法中,可以引用static变量。
  • 在静态方法中,不能引用非static变量。
  • 在静态方法中,不能使用super和this关键字(涉及父类与当前类的对象,静态方法不能先于这些对象产生)。

非静态方法不含有static关键字,属于对象而不属于类。必须通过new关键字创建对象后再通过对象调用。

  • 在普通方法中,可以调用普通方法。
  • 在普通方法中,可以调用静态方法。
  • 在普通方法中,可以引用类变量和成员变量。
  • 在普通方法中,可以使用super和this关键字。

静态方法的生命周期跟相应的类一样长,类内带static的方法与变量会随着类的定义而被分配和装载入内存中。直至线程结束,这些静态的属性和方法才会被销毁。

非静态方法的生命周期和类的实例化对象一样长,只有当类实例化了一个对象,非静态方法才会被创建,而当这个对象被销毁时,非静态方法也马上被销毁。

main方法不带static关键字时,在IDEA环境下会提示Warning:

方法 'main' 没有签名 'public static void main(String[])'

如果main方法非静态,就意味着Java虚拟机在执行的时候需要先创建一个对象才能调用main方法,我们为作为程序的入口的main方法再创建一个额外的对象显然是没有必要的。

重写Hashcode和Equals方法

在做对象间的比较时,我们通常采用equals方法来判断它们是否相等。查阅Object的euqals实现:

public boolean equals(Object obj) {
    return (this == obj);
}

这里的符号“==”是这样定义的:

  • 对于基本类型(int,byte,boolean,long,short,double,float,char),==操作比较值相等
  • 对于对象类型,比较两个对象的内存地址。
  • 对于封装类型和基本类型间的比较,编译器会转换为基本类型后再比较

一个拼接好的hello world的字符串和一个new出来的hello world作比较,它们的内容是一致的而地址值不同,这时候用equals判断它们不相等,这显然是不符合我们需要的。

重写equals方法,目的就是实现符合我们需要的“合乎情理”的情景。如果不重写的话,源码中就仅仅会比较俩个对象的地址值。

那么hashcode的重写又是否是必要的?只重写Hashcode和equals其中的一个方法可行吗?

首先一般程序开发中有规定:两个对象如果使用equals比较为true,那么它们的hash值必须一样,因而我们重写equals方法的同时,都会重写hashCode。

只重写equals方法是可行的,但对于数据量庞大的对象,比如这样一个情况:在做俩个对象A和B的比较时,B对象是存放在集合里的,且集合里的数据多达几万条,调用equals方法来一个一个比较嘛效率就必然会出现明显的下降。这个时候,hashcode方法直接进行比较显然更为合理。

只重写hashcode方法,由于规定对象equals比较为true则hash值必须一样,也就是说hashcode相等是两个对象实际相等的必要条件,因此是只重写hashcode方法是不成立的。

对于大多数我们需要进行比较,涉及判断是否相等的类,需要进行equals与hashcode的重写。

@Override
    public int hashCode() {
        final int prime = 31;
        int res = 1;
        res = prime * result + ((Key1 == null) ? 0 : Key1.hashCode());
        res = prime * result + ((Key2 == null) ? 0 : Key2.hashCode());
        return res;
    }

hashcode方法的重写规则可以参考数据结构课中哈希部分的内容结合实际情况来确定。一般来说可以在重写时使用“31”作为系数进行hashcode的构造,这是因为31是质数,乘法构造可以使hashcode保持唯一,并且31作为一个质数具有如下性质:

n * 31 等价于 (n * 2 * 2* 2 * 2 * 2 - n) or (n << 5) - n

总结

  • static是类的,是一开始就加载的,是陪你走过岁月长河的
  • 非static是对象的,是随new对象随产生的,是生不带来死不带走的
  • 对于大多数我们需要进行比较,涉及判断是否相等的类,需要进行equals与hashcode的重写。

例如P1中的FriendshipGraph,Person类涉及比较需要进行equals与hashcode重写,至于FriendshipGraph我们并不考虑需要对两个图进行比较的情况,因而重写它的equals与hashcode方法是不必要的。

参考资料

https://blog.csdn.net/LINDAMAN00/article/details/97898803
https://zhuanlan.zhihu.com/p/258751142
https://zhuanlan.zhihu.com/p/60871182

标签:ADT,静态方法,对象,方法,equals,Lab2,回顾总结,static,重写
From: https://www.cnblogs.com/HaoranLuo/p/18179406

相关文章

  • mit6.828 - lab2笔记
    目标:重点学习内存管理的相关知识,包括内存布局、页表结构、页映射任务:完成内存管理的相关代码lab2中,完全可以跟着实验手册的节奏走,逐步完善内存管理的代码。环境准备:实验2包含以下新的源文件:inc/memlayout.hkern/pmap.ckern/pmap.hkern/kclock.hkern/kclock.cmemlay......
  • 《安富莱嵌入式周报》第336期:开源计算器,交流欧姆表,高性能开源BLDC控制器,Matlab2024a,操
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 本周更新一期视频教程:BSP视频教程第30期:UDSISO14229统一诊断服务CAN总线专题,常用诊断执行流程精讲,干货分享,图文并茂https://www.armbbs.cn/forum.php?mod=viewthread&tid=12......
  • IfcLoopHeadToTail
    IfcLoopHeadToTail函数定义如果对于输入边循环的边,每条边的结束顶点与其后续边的开始顶点相同,则此函数返回TRUE。IFC2x2中的新功能 EXPRESSSpecificationFUNCTIONIfcLoopHeadToTail(ALoop:IfcEdgeLoop):LOGICAL;LOCALN:INTEGER;P:LOGICAL:=TRU......
  • 列表删除按钮,分页错位问题解决思路 table delete page loadTable
    列表删除按钮,分页错位问题解决思路this.$api('/xxx/xxx/deletexxx',{ids:id}).then(res=>{if(res.status!==20)returnthis.$Message.destroy()this.$Message.success('删除成功')if(this.tableData.leng......
  • CS61B Lab2 Debugging
    实验2主要内容教你使用IDE中调试步骤,学会设置断点调试代码学以只用,学会设置断点之后,就开始改代码错误了本节需要学什么?Java配置Configration当你导入一个项目模块时,需要添加修改configration的以下内容。Junit的导入有时候运行的时候会出现“junit不存在等情况”这时......
  • Pycharm报错:ReadTimeoutError: HTTPSConnectionPool(host=‘files.pythonhosted.org‘
    今天在pycharm里面pipinstall库的时候报了这个错,如图所示:第一种,设置超时时间,命令如下:pip--default-timeout=1000install-U模块名第二种,用镜像网站进行下载,这种方法下载的速度超快的哦=.=,命令如下:pip--default-timeout=100install库名称-ihttp://pypi.douban.com/......
  • MIT6.S081 - Lab2: system calls
    Lab2:systemcalls预备知识执行一次系统调用的流程:USERMODEstep1:系统调用声明user/user.h:系统调用函数(如intfork(void))step2:ecall进入内核态user/usys.S(该文件由user/usys.pl生成,后续添加函数可以在这里添加):执行如下命令.globalforkfork:lia7,SYS_f......
  • MIT 6.5830 simpleDB Lab2
    Exercise1需要完成的是:src/java/simpledb/execution/Predicate.javasrc/java/simpledb/execution/JoinPredicate.javasrc/java/simpledb/execution/Filter.javasrc/java/simpledb/execution/Join.java这里主要实现两个功能:Filter:通过Predicate过滤一部分满足条件的Tupl......
  • 广度优先搜索 Breadth-First Search(BFS)
    问题引入​对于每一个问题,都会有相应的解,在之前的学习中求解的过程,都是以一条条线的形式产生可能解进行筛选验证是否正确。本章节我们来考虑另外一种思路,类似于洪水爆发,从一个源头开始逐渐蔓延开来,直到所有可达的区域都被洪水淹没,所以我们也把这种算法称之为洪泛法。洪泛法会......
  • 数据结构之————线性表ADT、以数组存储方式实现抽象类型的一个实例
    前言:基础填坑1、ADT在文章开始前,我们要弄明白什么是ADT(AbstractDataType)抽象数据类型1、ADT是用户定义的数据类型,它包含一组数据以及在这组数据上进行的操作。只定义操作的行为,没有具体的实现细节2、它存在的目的是使我们能够独立于程序的实现细节来理解数据结构的特......