精华内容
下载资源
问答
  • 对象专业化适用类型
    千次阅读
    2021-04-20 15:08:53

    今天做机房收费系统时,将DataGridView中的数据导入到Excel中,当运行到这一句代码”xlApp.Cells(rows + 2, j + 1) = DataGridView1(j, rows).Value.ToString “时,出现了”未将对象引用设置到对象的实例“这种情况。后来经过调试发现是将DataGridView中的空行导入到Excel中,也就是循环到DataGridView中的单元格时此单元格却没有赋值的问题。引起这种”未将对象引用设置到对象的实例“的根源,归结为一点”对控件赋文本值时,值不存在。我对“未将对象引用设置到对象的实例”这种现象,经过查阅资料做了一下总结。

    1、ViewState 对象为Null。

    2、DateSet 空。

    3、sql语句或Datebase的原因导致DataReader空。

    4、声明字符串变量时未赋空值就应用变量。

    5、未用new初始化对象。

    6、Session对象为空。

    7、对控件赋文本值时,值不存在。

    8、使用Request.QueryString()时,所获取的对象不存在,或在值为空时未赋初始值。

    9、使用FindControl时,控件不存在却没有做预处理。

    10、重复定义造成未将对象引用设置到对象的实例错误.

    对于这种问题,一方面是我们要慢慢的积累经验,一方面要心思缜密细,写代码潜意识要主动防范。

    SQL Sever无法打开链接对话框,未将对象引用设置到对象的实例。(AppIDPackage)

    前几天刚做完系统,先装的是SQL Sever2008,装完后还试了一下,OK~没问题,然后就继续装VS2012等一些软件.搞到很晚没有继续试试就睡了,第二天运行SSMS出问题了..(如图 1.0 所示 ...

    WPF中未将对象引用设置到对象的实例

    前几天,我开始了WPF的基础学习,一上来我就遇到了一个令我头痛的问题,按照书上的例子我写了一段属于自己的代码,一个简单的色调器.满心期待的编译运行,就出现了未将对象引用设置到对象的实例.我在网上查阅了 ...

    c#:未将对象引用设置到对象的实例--可能出现的问题总结(转)

    1.c#:未将对象引用设置到对象的实例--可能出现的问题总结(转):http://www.cnblogs.com/KeenLeung/archive/2013/06/23/3150578.html

    IIS报错 未将对象引用设置到对象的实例。

    在vs中运行正常的项目 ,发布到IIS总是提示 未将对象引用设置到对象的实例. 运行静态页面 html正常,只是打开.aspx页面的时候报错,在确保了数据库,配置,权限均正常的情况下. 错误原因:先安 ...

    解决使用DevExpress开发错误:未将对象引用设置到对象的实例

    在使用DevExpress是总是会出现一些状况.这次同事在他的机器上调试完成的代码发过来,却出现"未将对象引用设置到对象的实例"的错误,提示是Resources.resx的问题.另 ...

    asp.net 中Session的运用,及抛出错误“未将对象引用设置到对象的实例”

    1. 页面载入后,必须要等到page_Load方法执行建立 page对象后才可以使用Session 2. 在.aspx和.cs文件中使用Session的区别 (1).aspx: Session[&qu ...

    一般处理程序中使用Session出现未将对象引用设置到对象的实例

    遇到问题:未将对象引用设置到对象的实例 那就在你的一般处理程序中加入红色背景的代码吧 using System; using System.Collections.Generic; using Sys ...

    解决:getWeatherbyCityName(city),服务器无法处理请求。 ---> 未将对象引用设置到对象的实例。

    原文:getWeatherbyCityName(city),服务器无法处理请求. ---> 未将对象引用设置到对象的实例. 解决方法:不要直接使用 “服务引用” , 添加为 “Web 引用” 最 ...

    新建WindowsPhone项目时提示未将对象引用设置到对象的实例

    问题: 安装好新系统之后(只有Windows8 专业版和企业版支持hyper-v),然后安装vs2012,再安装Wp8 Sdk,安装完毕后新建Windows Phone项目,会提示未将对象引用设置到对 ...

    随机推荐

    rsa加密--选择padding模式需要注意的问题。。。

    最近在做一个项目中需要,在android对一个密码字段首先进行 一次md5加密后再进行一次rsa加密,然后把加密的结果通过 json协议传输给nginx服务器进行解密.在android中,可以直接 使 ...

    Android ListView初始化简单分析

    下面是分析ListView初始化的源码流程分析,主要是ListVIew.onLayout过程与普通视图的layout过程完全不同,避免流程交代不清楚,以下是一个流程的思维导图. 思维导图是顺序是从左向 ...

    BULK SQL

    DECLARE TYPE TY_EMP IS TABLE OF EMP%ROWTYPE; --如果是IS TABLE OF行类型(ROWTYPE.RECORD等)就是二维 V_Emp TY_EMP; ...

    CTF 和 PHP ,数据库

    引言 接触CTF 差不多一个月了,网上题目刷得也差不多,做了不少笔记,老想总结一下.当然,这种文章网上多得是. 正文笔记 少不了弱类型,松散比较,放一张图 2.  mysql_real_escape_ ...

    快捷使用Node Inspector调试NodeJS

    一:介绍 NodeJS开发有很多种调试方式,比如输出Log.WebStorm自带的调试器.Node Inspector等,其中Node Inspector是比较流行和被推荐的一种. 但是Node In ...

    iOS开发-适配器和外观模式

    适配器模式,属于结构型模式,其主要作用是将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.适配器模式有对象适配器和类适配器两种,类适配器模 ...

    iOS App之间常用的五种通信方式及适用场景总结

    iOS系统是相对封闭的系统,App各自在各自的沙盒(sandbox)中运行,每个App都只能读取iPhone上iOS系统为该应用程序程序创建的文件夹AppData下的内容,不能随意跨越自己的沙盒去访问 ...

    数学规划求解器lp_solve超详细教程

    前言 最近小编学了运筹学中的单纯形法.于是,很快便按奈不住跳动的心.这不得不让我拿起纸和笔思考着,一个至关重要的问题:如何用单纯形法装一个完备的13? 恰巧,在我坐在图书馆陷入沉思的时候,一位漂亮的小 ...

    牛客网 牛客小白月赛12 B.华华教月月做数学-A^B mod P-快速幂+快速乘

    链接:https://ac.nowcoder.com/acm/contest/392/B来源:牛客网 华华教月月做数学 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32768K,其 ...

    eg_3

    3. 编写一个程序,返回一个 double 类型的二维数组,数组中的元素通过解析字符串参数获得,如字符串参数:“1,2;3,4,5;6,7,8”,则对应的数组为: d[0][0]=1.0, d[0][ ...

    更多相关内容
  • 三种存储类型:块存储、文件存储、对象存储

    万次阅读 多人点赞 2020-11-02 10:21:27
    ... ... 存储类型 先从三种存储类型开始。...看了很多文章,感觉都无从下手,因为我还不了解为什么有这么多的存储方式和存储类型,所以先不看这些概念的定义,先了解为什么会有这些概念。 为什么会有这么多存储

    链接:

    https://www.zhihu.com/question/21536660

    https://www.cnblogs.com/hukey/p/8323853.html

    https://www.cnblogs.com/sylar5/p/11520149.html

    https://juejin.im/post/6844903541169979400

    存储类型

    先从三种存储类型开始。

    看了很多文章,感觉都无从下手,因为我还不了解为什么有这么多的存储方式和存储类型,所以先不看这些概念的定义,先了解为什么会有这些概念。

    为什么会有这么多存储类型?

    因为业务需要,不同场景需要满足不同的人,其实有很多存储类型可以用,三种只是常见的。就像有很多种编程语言,没有优劣之分,只是在各自领域里各司其职

    三种存储类型有什么区别?

    这个本来该了解了三种存储类型得具体含义后再说,但我觉得知乎上的答案说的很好:这三者的本质差别是使用数据的“用户”不同:块存储的用户是可以读写块设备的软件系统,例如传统的文件系统、数据库;文件存储的用户是自然人;对象存储的用户则是其它计算机软件。

    先说分布式存储:

    知乎:首先要说明一下的是,这三个概念都是分布式存储中的概念,由不同的网络存储协议实现。

    所谓分布式存储,就是底层的存储系统,因为要存放的数据非常多,单一服务器所能连接的物理介质是有限的,提供的IO性能也是有限的,所以通过多台服务器协同工作,每台服务器连接若干物理介质,一起为多个系统提供存储服务。为了满足不同的访问需求,往往一个分布式存储系统,可以同时提供文件存储、块存储和对象存储这三种形式的服务。

    来个通俗易懂的理解,用不同的存储类型存储玉米:

    块存储

    块存储一般体现形式是卷或者硬盘(比如windows里面看到的c盘),数据是按字节来访问的,对于块存储而言,对里面存的数据内容和格式是完全一无所知的。好比上面图中,数据就像玉米粒一样堆放在块存储里,块存储只关心玉米粒进来和出去,不关心玉米粒之间的关系和用途。

    块存储只负责数据读取和写入,因此性能很高,适用于对响应时间要求高的系统。比如数据库等。

    文件存储

    文件存储一般体现形式是目录和文件(比如C:\Users\Downloads\text.doc),数据以文件的方式存储和访问,按照目录结构进行组织。文件存储可以对数据进行一定的高级管理,比如在文件层面进行访问权限控制等。好比上面图中,数据像玉米粒一样组成玉米棒子,再对应到不同的玉米杆上,要找到某个玉米粒,先找到玉米杆,再找到玉米棒子,然后根据玉米粒在玉米棒子上的位置找到它。

    文件存储可以很方便的共享,因此用途非常广泛。比如常用的NFS、CIFS、ftp等都是基于文件存储的。

    对象存储

    对象存储一般体现形式是一个UUID(比如https://www.youtube.com/watch?v=nAKxJbcec8U),数据和元数据打包在一起作为一个整体对象存在一个超大池子里。对于对象访问,只需要报出它的UUID,就能立即找到它,但访问的时候对象是作为一个整体访问的。好比上面图中,数据的玉米粒被做成了玉米罐头,每个玉米罐头都有一个唯一出厂号,但是买卖罐头,都一次是一盒为单位。

    从设计之初衷(一般的对象存储都是基于哈希环之类的技术来实现),对象存储就可以非常简单的扩展到超大规模,因此非常适合数据量大、增速又很快的视频、图像等。

     

    块存储

    传统的文件系统,是直接访问存储数据的硬件介质的。介质不关心也无法去关心这些数据的组织方式以及结构,因此用的是最简单粗暴的组织方式:所有数据按照固定的大小分块,每一块赋予一个用于寻址的编号。以大家比较熟悉的机械硬盘为例,一块就是一个扇区,老式硬盘是512字节大小,新硬盘是4K字节大小。老式硬盘用柱面-磁头-扇区号(CHS,Cylinder-Head-Sector)组成的编号进行寻址,现代硬盘用一个逻辑块编号寻址(LBA,Logical Block Addressing)。所以,硬盘往往又叫块设备(Block Device),当然,除了硬盘还有其它块设备,例如不同规格的软盘,各种规格的光盘,磁带等。

    至于哪些块组成一个文件,哪些块记录的是目录/子目录信息,这是文件系统的事情。不同的文件系统有不同的组织结构,这个就不展开了。为了方便管理,硬盘这样的块设备通常可以划分为多个逻辑块设备,也就是我们熟悉的硬盘分区(Partition)。反过来,单个介质的容量、性能有限,可以通过某些技术手段把多个物理块设备组合成一个逻辑块设备,例如各种级别的RAID,JBOD,某些操作系统的卷管理系统(Volume Manager)如Windows的动态磁盘、Linux的LVM等。

    块设备的使用对象除了传统的文件系统以及一些专用的管理工具软件如备份软件、分区软件外,还有一些支持直接读写块设备的软件如数据库等,但一般用户很少这样使用。

    在网络存储中,服务器把本地的一个逻辑块设备——底层可能是一个物理块设备的一部分,也可能是多个物理块设备的组合,又或者多个物理块设备的组合中的一部分,甚至是一个本地文件系统上的一个文件——通过某种协议模拟成一个块设备,远程的客户端(可以是一台物理主机,也可以是虚拟机,某个回答所说的块设备是给虚拟机用是错误的)使用相同的协议把这个逻辑块设备作为一个本地存储介质来使用,划分分区,格式化自己的文件系统等等。这就是块存储,比较常见的块存储协议是iSCSI。

    优点

    • 这种方式的好处当然是因为通过了Raid与LVM等手段,对数据提供了保护;
    • 可以将多块廉价的硬盘组合起来,称为一个大容量的逻辑盘对外提供服务,提高了容量;
    • 写入数据时,由于是多块磁盘组合出来的逻辑盘,所以几块硬盘可以并行写入的,提升了读写效率;
    • 很多时候块存储采用SAN架构组网,传输速度以及封装协议的原因,使得传输速度和读写效率得到提升

    缺点

    • 采用SAN架构组网时,需要额外为主机购买光纤通道卡,还要购买光纤交换机,造价成本高;
    • 主机之间数据无法共享,在服务器不做集群的情况下,块存储裸盘映射给主机,在格式化使用后,对于主机来说相当于本地盘,那么主机A的本地盘根本不能给主机B去使用,无法共享数据
    • 不利于不同操作系统主机间的数据共享:因为操作系统使用不同的文件系统,格式化后,不同的文件系统间的数据是共享不了的。 例如一台win7,文件系统是FAT32/NTFS,而linux是EXT4,EXT4是无法识别NTFS的文件系统的

    使用场景

    docker容器、虚拟机磁盘存储分配。
    日志存储。
    文件存储。

    典型设备

    磁盘阵列、硬盘

    典型存储方式

    • DAS(Direct Attach STorage):是直接连接于主机服务器的一种储存方式,每一台主机服务器有独立的储存设备,每台主机服务器的储存设备无法互通,需要跨主机存取资料时,必须经过相对复杂的设定,若主机服务器分属不同的操作系统,要存取彼此的资料,更是复杂,有些系统甚至不能存取。通常用在单一网络环境下且数据交换量不大,性能要求不高的环境下,可以说是一种应用较为早的技术实现。
    • SAN(Storage Area Network):是一种用高速(光纤)网络联接专业主机服务器的一种储存方式,此系统会位于主机群的后端,它使用高速I/O 联结方式, 如 SCSI, ESCON 及 Fibre- Channels。一般而言,SAN应用在对网络速度要求高、对数据的可靠性和安全性要求高、对数据共享的性能要求高的应用环境中,特点是代价高,性能好。例如电信、银行的大数据量关键应用。它采用SCSI 块I/O的命令集,通过在磁盘或FC(Fiber Channel)级的数据访问提供高性能的随机I/O和数据吞吐率,它具有高带宽、低延迟的优势,在高性能计算中占有一席之地,但是由于SAN系统的价格较高,且可扩展性较差,已不能满足成千上万个CPU规模的系统。

    文件存储

    文件存储的用户是自然人,最容易理解。计算机中所有的数据都是0和1,存储在硬件介质上的一连串的01组合对我们来说完全无法去分辨以及管理。因此我们用“文件”这个概念对这些数据进行组织,所有用于同一用途的数据,按照不同应用程序要求的结构方式组成不同类型的文件(通常用不同的后缀来指代不同的类型),然后我们给每一个文件起一个方便理解记忆的名字。而当文件很多的时候,我们按照某种划分方式给这些文件分组,每一组文件放在同一个目录(或者叫文件夹)里面,当然我们也需要给这些目录起一个容易理解和记忆的名字。而且目录下面除了文件还可以有下一级目录(称之为子目录或者子文件夹),所有的文件、目录形成一个树状结构。我们最常用的Windows系统中,打开资源管理器就可以看到以这种方式组织起来的无数个文件和目录。

    为了方便查找,从根节点开始逐级目录往下,一直到文件本身,把这些目录、子目录、文件的名字用特殊的字符(例如Windows/DOS用“\”,类Unix系统用“/”)拼接起来,这样的一串字符称之为路径,例如Linux中的“/etc/systemd/system.conf”或者Windows中的“C:\Windows\System32\taskmgr.exe”。人类用路径作为唯一标识来访问具体的文件。而由作为自然人的程序员所编写的各种软件程序,绝大部分也使用这种方式来访问文件。

    把存储介质上的数据组织成目录-子目录-文件这种形式的数据结构,用于从这个结构中寻找、添加、修改、删除文件的程序,以及用于维护这个结构的程序,组成的系统有一个专用的名字:文件系统(File System)。文件系统有很多,常见的有Windows的FAT/FAT32/NTFS,Linux的EXT2/EXT3/EXT4/XFS/BtrFS等。而在网络存储中,底层数据并非存储在本地的存储介质,而是另外一台服务器上,不同的客户端都可以用类似文件系统的方式访问这台服务器上的文件,这样的系统叫网络文件系统(Network File System),常见的网络文件系统有Windows网络的CIFS(也叫SMB)、类Unix系统网络的NFS等。而文件存储除了网络文件系统外,FTP、HTTP其实也算是文件存储的某种特殊实现,都是可以通过某个url来访问一个文件。

    优点

    • 造价低:随便一台机器就可以,另外普通的以太网就可以,根本不需要专用的SAN网络,所以造价低
    • 方便文件共享

    缺点

    • 读写速率低,传输速率慢:以太网,上传下载速度较慢
    • 所有读写都要1台服务器里面的硬盘来承受,相比起磁盘阵列动不动就十几上百块硬盘同时读写,速率慢了许多。

    使用场景

    日志存储。
    有目录结构的文件存储。

    典型设备

    FTP、NFS服务器

    典型存储方式

    • 通常,NAS产品都是文件级存储。NAS(Network Attached Storage):是一套网络储存设备,通常是直接连在网络上并提供资料存取服务,一套 NAS 储存设备就如同一个提供数据文件服务的系统,特点是性价比高。例如教育、政府、企业等数据存储应用。它采用NFS或CIFS命令集访问数据,以文件为传输协议,通过TCP/IP实现网络化存储,可扩展性好、价格便宜、用户易管理,如目前在集群计算中应用较多的NFS文件系统,但由于NAS的协议开销高、带宽低、延迟大,不利于在高性能集群中应用。

    对象存储

    之所以出现对象存储这种东西,是为了克服块存储与文件存储各自的缺点,发扬各自的优点。简单来说块存储读写快,不利于共享,文件存储读写慢,利于共享。能否弄一个读写块,利于共享的存储出来呢?于是就有了对象存储。

    一个文件包含了属性(术语:metadata,元数据,例如该文件的大小、修改时间、存储路径等)以及内容(数据)。
        像FAT32这种文件系统,是直接将一份文件与metadata一起存储的,存储过程先将文件按照文件系统的最小块大小来打散(例如4M的文件,假设文件系统要求一个块4K,那么就将文件打散称为1000个小块),再写进硬盘里,过程中没有区分数据和metadata的。而每个块最后会告知你下一个要读取的块地址,然后一直这样顺序的按图索骥,最后完成整份文件的所有块的读取。
        这种情况下读写速率很慢,因为就算你有100个机械臂在读写,但是由于你只有读取到第一个块,才能知道下一个块在哪里,其实相当于只能有1个机械臂在实际工作。

    而对象存储则将元数据独立出来了,控制节点叫元数据服务器(服务器+对象存储管理软件),里面主要负责存储对象的属性(主要是对象的数据被打散存放到了那几台分布式服务器中的信息)而其他负责存储数据的分布式服务器叫做OSD,主要负责存储文件的数据部分。当用户访问对象,会先访问元数据服务器,元数据服务器只负责反馈对象存储在哪里OSD,假设反馈文件A存储在B、C、D三台OSD,那么用户就会再次直接访问3台OSD服务器去读取数据。
        这时候由于是3台OSD同时对外传输数据,所以传输的速度就会加快了,当OSD服务器数量越多,这种读写速度的提升就越大,通过此种方式,实现了读写快的目的。
        另一方面,对象存储软件是有专门的文件系统的,所以OSD对外又相当于文件服务器,那么就不存在共享方面的困难了,也解决了文件共享方面的问题
        所以对象存储的出现,很好的结合了块存储和文件存储的优点。

    为什么对象存储兼具块存储和文件存储的好处,还要使用块存储和文件存储呢?

        (1)有一类应用是需要存储直接裸盘映射的,例如数据库。因为数据需要存储楼盘映射给自己后,再根据自己的数据库文件系统来对裸盘进行格式化的,所以是不能够采用其他已经被格式化为某种文件系统的存储的。此类应用更合适使用块存储。
        (2)对象存储的成本比起普通的文件存储还要较高,需要购买专门的对象存储软件以及大容量硬盘。如果对数据量要求不是海量,只是为了做文件共享的时候,直接用文件存储的形式好了,性价比高。

    优点

    • 具备块存储的读写高速。
    • 具备文件存储的共享等特性。
    • 可扩展性高:对象存储能够扩展数十乃至数百EB的容量,能够充分利用高密度存储;
    • 效率高:扁平化结构,不受复杂目录系统对性能的影响;
    • 无需迁移:对象存储是一种横向扩展系统,随着容量的增加,数据根据算法自动分布于所有的对象存储节点;
    • 安全性高:对象存储通常凭借HTTP调用对象存储本身提供的认证密钥来提供数据访问;
    • 访问方便:不光支持HTTP(S)协议,采用REST的API方式调用和检索数据,同样增加了NFS和SMB支持;
    • 成本相对低:与块存储方式相比,对象存储是最具成本效益的数据存储类型,并且与云计算搭配,把对象存储的这一特性发挥的淋漓尽致。

    缺点

    • 最终一致性:由于不同节点的位置不同,数据同步时可能会有一定时间的延迟或者错误;
    • 不易做数据库:对象存储比较适合存储那些变动不大甚至不变的文件,而对于像数据库这种需要直接与存储裸盘相互映射的应用,还是块存储更合适。

    使用场景

    对象存储服务OSS(英文Object Storage Service)主要应用于以下场景。

    • 图片和音视频等应用的海量存储。OSS可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web网站程序、移动应用可以直接向OSS写入或读取数据。OSS支持流式写入和文件写入两种方式
    • 网页或者移动应用的静态和动态资源分离。利用BGP带宽,OSS可以实现超低延时的数据直接下载。也可以配合阿里云CDN加速服务,为图片、音视频、移动应用的更新分发提供最佳体验
    • 云端数据处理。上传文件到OSS后,可以配合媒体转码服务(MTS)和图片处理服务(IMG)进行云端的数据处理。

    典型设备

    内置大容量硬盘的分布式服务器。对象存储最常用的方案,就是多台服务器内置大容量硬盘,再装上对象存储软件,然后再额外搞几台服务作为管理节点,安装上对象存储管理软件。管理节点可以管理其他服务器对外提供读写访问功能。

    典型存储方式

    (网上找到的对象存储的技术挺多的,存储方式我没找到。。)

    三种存储类型的差异:

     

    展开全文
  • 缺点: 设计框架和维护关键字对自动专业知识要求比较高。 实现该框架的成本相对较高,而且设置起来也比较耗时和复杂。 综上所述,实现用于自动测试的框架需要选择一种灵活的工具。该工具应支持广泛的应用程序...

    每一个测试人员都应该了解每种框架的优缺点,以帮助你的团队更好地确定最适合的测试的框架,以达到事半功倍。

    什么是测试自动化框架?

    自动化测试框架就是用于测试自动化的框架。具体来说,它提供了自动化测试用例编写、自动化测试用例执行、自动化测试报告生成等基础功能。我们只需要基于这个框架,完成和业务高度相关的测试用例设计和实现即可。另外,框架会为我们处理好复杂度与扩展性的问题,我们无需为此操心。

    测试自动化框架是在创建和设计测试用例时使用的一组最佳实践或准则。
    测试准则集可以包括编码标准,对象存储库,测试数据处理方法,有关外部存储访问的信息等。

    这些准则并非强制,但是在自动化脚本过程中,它们提高了测试的效率并产生了有益的结果。

    使用测试自动化框架的好处

    • 更全的测试范围
    • 降低脚本维护成本
    • 节省人力成本
    • 提高测试速度和效率
    • 可重用的测试代码
    • 易于报告

    根据需求使用适配的自动化测试框架有助于加快测试过程,并消除人为错误。它还使测试维护更加容易,加快测试进度,节省成本、时间和精力。此外,框架QA团队能够充分开发、执行和报告测试过程,同时还使代码可在多种情况下重用。

    5种最流行的自动化测试框架类型

    团队根据团队规模、经验水平、用户需求等因素来选择测试框架。以下是五种最流行的框架及其优缺点:

    1、线性框架
    这是最基本的框架类型。它通常被称为“记录和回放(record and playback)”框架。

    在这个过程中,测试代码的创建和执行是按线性或顺序编写的——测试人员手动记录每一个步骤,并自动回放记录的脚本。这些步骤包括导航、用户输入和检查点。它最适合小型应用程序或团队。
    在此过程中,测试代码的创建和执行以线性或顺序方式编写-测试人员手动记录每个步骤并自动播放记录的脚本。这些步骤包括导航,用户输入和检查点。最适合小型应用程序或小团队。

    优点:
    线性框架最大的好处是生成测试用例的速度快,直接录制;无须代码基础,无须手动编写测试代码,因此门槛较低、易于上手。

    缺点:
    然而线性框架的不足之处也很明显:录制的脚本是固定的(hardcode),不可重用。这意味着,当应用发生微小变化时,上一次录制的脚本可能就无法使用了,需要重新录制(rework),从而产生大量的后期维护成本。

    2、基于模块化的框架
    顾名思义,此框架允许将被测应用程序划分为单独的模块,单元或部分。每个模块都会为它们创建独立的测试脚本。因此,每个模块及其测试脚本的组合可以构建代表各种测试案例的更大的测试。

    优点:
    该框架在创建模块时使用抽象。因此,应用程序更改将只影响与它们相关联的测试脚本所涉及的模块,而不影响其他部分。
    高度的模块化,这使得维护更加容易且具有成本效益
    创建测试用例所需的精力最少,因为可以重复使用不同模块的测试脚本。

    缺点:
    如果没有语言开发基础,则建立框架可能会很困难。
    由于将数据硬编码到测试脚本中,因此无法重复使用数据集——因为测试是单独执行的。

    3、库结构框架
    该库体系结构框架建立在模块化框架的基础上,但具有其他好处。这样做的好处是,它不仅可以将被测应用程序划分为测试脚本,还可以将测试脚本中的相似任务划分为通用功能。

    然后创建一个库,该库构成了AUT的常用功能,可以在需要时由测试脚本调用。

    优点:
    高度的模块化,这使得测试维护简单且预算友好。
    它具有高度的可重用性,因为它的公共函数库可以被几个测试脚本使用。
    缺点:
    框架中引入的库使其更加复杂。
    测试数据也被硬编码到测试脚本中。因此,数据中的更改必须适用于测试脚本。
    测试脚本的开发需要更多的时间和技术。

    4、数据驱动框架
    在数据驱动框架中,测试数据和测试脚本是分离的。在许多测试场景中,需要使用不同的测试数据多次测试同一功能或特性。如果测试数据是hardcode进测试脚本的,那么每更换一次测试数据都需要修改测试脚本。这是很大的工作量。此时,可以使用数据驱动框架。具体来说,测试脚本是固定的,而测试数据可以从外部的数据文件,以Excel、CSV、SQL等形式作为参数传入测试脚本。这样,我们只需要维护一份脚本和一份数据文件即可。
    优点:
    总体来说,这种框架最大的好处就是易于维护。
    测试脚本中的任何更改都不会影响测试数据。因此,可以避免对数据进行硬编码。
    可以使用多组数据进行测试。
    可以通过更改外部数据库中的测试数据来测试各种测试方案,从而减少所需的测试脚本数量。
    缺点:
    准备和计划框架的通用测试脚本,识别与格式化测试数据需要花费时间。
    框架设计的使用需要经验丰富的测试人员,因为它的复杂性,需要具备多种编程语言知识。

    5、关键字驱动框架
    该框架是数据驱动框架的扩展。测试数据和测试脚本也被分离,不同的是,该框架要更进一步地将测试脚本中的通用功能剥离出来,形成关键词(keyword)。测试脚本本质上就是对一系列通用的或者自定义的关键词的调用。这样做的好处是关键词可以在多个测试中复用,并且测试脚本更加易于维护。不过,实现这样一个框架并非易事。
    优点:
    与数据驱动不同,运行此框架不需要脚本知识。
    可以独立于被测应用程序构建测试脚本。
    一个关键字可以在多个测试脚本中使用。因此该代码是可重用的。
    缺点:
    设计框架和维护关键字对自动化的专业知识要求比较高。
    实现该框架的成本相对较高,而且设置起来也比较耗时和复杂。

    综上所述,实现用于自动化测试的框架需要选择一种灵活的工具。该工具应支持广泛的应用程序,并满足测试要求。另外,应该有正确的策略来定义应该自动化哪些部分。

    需要指出的是,业界已经有了实现上述各种测试自动化框架的工具。通常来说,我们并不需要重新发明一个新的框架,而是基于已有的框架去进行优化升级,使之适合自己的项目需求,来完成自动化测试工作。
    那么,面对一个新的自动化测试框架,如何着手工作呢?
    我们应该聚焦在以下四个问题上。

    1、如何生成测试用例?
    不同的框架,生成测试用例的方法不一样。
    对于线性框架来说,无须编写脚本,只需要点击预设的按钮就能够生成测试用例;比如Katalon,直接录制生成测试用例。
    对于多数框架来说,生成测试用例需要编程。当然,不同框架使用的编程语言、编程风格有差异。
    对于Selenium框架来说,使用的是通用编程语言Java和Python,可能更多的是对Selenium进行二次封装,以便更好更快的生成用例;
    对于Robot Framework来说,使用的是其专用的Robot Framework编程语言。
    一般来说,使用框架编程的过程很多时候就是调用库接口的过程。因此作为前提,在编写用例之前,我们需要熟悉框架提供的库的种类和功能,以及这些库所提供的API的使用方法。

    2、如何执行测试用例?
    当测试用例完成之后,我们需要运行测试用例。
    自动化测试是通过GUI图形界面来触发,还是通过CLI命令触发,这因框架而异。
    当然,仅仅知道如何触发测试是不够的。我们通常有更多的需求。
    例如,如何选择性地执行满足特定条件的测试用例子集?
    如何设置全局的执行参数(超时时间、日志路径、报告形式等)?
    如何动态地给测试用例集传入参数?

    一般来说,一个完整的框架需要提供足够多的控制选项,从而让我们根据需求定制执行测试的方式。
    以Robot Framework为例,其执行用例的命令具有30多个不同的选项。
    这提供了足够的自由度和一些非常棒的功能。例如其dryrun选项,可以让我们在不实际执行用例的情况下,快速检查出测试用例中参数不匹配、语法不正确、关键词找不到、库导入失败等错误,非常实用。

    3、如何检查测试结果?
    测试执行结束之后,我们需要关注测试结果。不同的框架会以不同形式提供测试结果。例如,测试结果既能以控制台日志的形式体现、也能够以图表和报告的形式体现,并以邮件发送或者展示在网站上。
    根据测试结果,我们可以很容易地了解测试的执行情况,包括测试的成功/失败情况、测试的整体/局部用时等。
    当测试失败时,我们尤其需要关注测试失败的具体情况。
    通常,我们关心失败是由于我们使用框架的方法不当造成的,还是由于被测软件的质量问题。这一点,只能通过检查和分析测试结果得到。

    4、如何扩展测试框架?
    一般来说,框架只是提供了最基本的功能。很多时候,框架并不能直接满足自动化测试的需求。这时我们可以寻求第三方的、与框架本身兼容的库或者插件。如果第三方工具不能满足我们的需求,我们就需要开发自己的库和工具。
    例如,对于HTTP、SSH等公有协议,我们很容易在网络上找到某个框架的第三方库;
    而对于只用于公司产品的私有协议,我们通常无法找到第三方库,只能自己开发。
    自己开发时,需要注意的是要遵从框架的规范,使得开发出的库能够与框架无缝兼容。

    说在最后,选择了一个框架,在享受其好处时,也不得不承受其不足。
    如果我们的关键需求受制于框架,并且框架也不容易扩展,那么我们就可能需要开发自己的框架。这是一件投入较大的事情。在大多数情况下,还是建议重用和有限扩展已有的框架。毕竟,不要重新发明轮子——Don’t reinvent the wheel!

    展开全文
  • Java 泛型,你了解类型擦除吗?

    万次阅读 多人点赞 2017-08-05 22:32:18
    所以泛型就是能广泛适用类型。 但泛型还有一种较为准确的说法就是为了参数化类型,或者说可以将类型当作参数传递给一个类或者是方法。 那么,如何解释类型参数呢? public class Cache { Object ...

    泛型,一个孤独的守门者。

    大家可能会有疑问,我为什么叫做泛型是一个守门者。这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇。泛型是 Java 中一个很小巧的概念,但同时也是一个很容易让人迷惑的知识点,它让人迷惑的地方在于它的许多表现有点违反直觉。

    文章开始的地方,先给大家奉上一道经典的测试题。

    List<String> l1 = new ArrayList<String>();
    List<Integer> l2 = new ArrayList<Integer>();
    		
    System.out.println(l1.getClass() == l2.getClass());
    
    

    请问,上面代码最终结果输出的是什么?不了解泛型的和很熟悉泛型的同学应该能够答出来,而对泛型有所了解,但是了解不深入的同学可能会答错。

    正确答案是 true。

    上面的代码中涉及到了泛型,而输出的结果缘由是类型擦除。先好好说说泛型。

    泛型是什么?

    泛型的英文是 generics,generic 的意思是通用,而翻译成中文,泛应该意为广泛,型是类型。所以泛型就是能广泛适用的类型。

    但泛型还有一种较为准确的说法就是为了参数化类型,或者说可以将类型当作参数传递给一个类或者是方法。

    那么,如何解释类型参数化呢?

    public class Cache {
    	Object value;
    
    	public Object getValue() {
    		return value;
    	}
    
    	public void setValue(Object value) {
    		this.value = value;
    	}
    	
    }
    

    假设 Cache 能够存取任何类型的值,于是,我们可以这样使用它。

    Cache cache = new Cache();
    cache.setValue(134);
    int value = (int) cache.getValue();
    cache.setValue("hello");
    String value1 = (String) cache.getValue();
    

    使用的方法也很简单,只要我们做正确的强制转换就好了。

    但是,泛型却给我们带来了不一样的编程体验。

    public class Cache<T> {
    	T value;
    
    	public Object getValue() {
    		return value;
    	}
    
    	public void setValue(T value) {
    		this.value = value;
    	}
    	
    }
    

    这就是泛型,它将 value 这个属性的类型也参数化了,这就是所谓的参数化类型。再看它的使用方法。

    Cache<String> cache1 = new Cache<String>();
    cache1.setValue("123");
    String value2 = cache1.getValue();
    		
    Cache<Integer> cache2 = new Cache<Integer>();
    cache2.setValue(456);
    int value3 = cache2.getValue();
    
    

    最显而易见的好处就是它不再需要对取出来的结果进行强制转换了。但,还有另外一点不同。
    这里写图片描述
    泛型除了可以将类型参数化外,而参数一旦确定好,如果类似不匹配,编译器就不通过。
    上面代码显示,无法将一个 String 对象设置到 cache2 中,因为泛型让它只接受 Integer 的类型。

    所以,综合上面信息,我们可以得到下面的结论。

    1. 与普通的 Object 代替一切类型这样简单粗暴而言,泛型使得数据的类别可以像参数一样由外部传递进来。它提供了一种扩展能力。它更符合面向抽象开发的软件编程宗旨。
    2. 当具体的类型确定后,泛型又提供了一种类型检测的机制,只有相匹配的数据才能正常的赋值,否则编译器就不通过。所以说,它是一种类型安全检测机制,一定程度上提高了软件的安全性防止出现低级的失误。
    3. 泛型提高了程序代码的可读性,不必要等到运行的时候才去强制转换,在定义或者实例化阶段,因为 Cache<String>这个类型显化的效果,程序员能够一目了然猜测出代码要操作的数据类型。

    下面的文章,我们正常介绍泛型的相关知识。

    泛型的定义和使用

    泛型按照使用情况可以分为 3 种。

    1. 泛型类。
    2. 泛型方法。
    3. 泛型接口。

    泛型类

    我们可以这样定义一个泛型类。

    public class Test<T> {
    	T field1;
    }
    

    尖括号 <>中的 T 被称作是类型参数,用于指代任何类型。事实上,T 只是一种习惯性写法,如果你愿意。你可以这样写。

    public class Test<Hello> {
    	Hello field1;
    }
    

    但出于规范的目的,Java 还是建议我们用单个大写字母来代表类型参数。常见的如:

    1. T 代表一般的任何类。
    2. E 代表 Element 的意思,或者 Exception 异常的意思。
    3. K 代表 Key 的意思。
    4. V 代表 Value 的意思,通常与 K 一起配合使用。
    5. S 代表 Subtype 的意思,文章后面部分会讲解示意。

    如果一个类被 <T>的形式定义,那么它就被称为是泛型类。

    那么对于泛型类怎么样使用呢?

    Test<String> test1 = new Test<>();
    Test<Integer> test2 = new Test<>();
    
    

    只要在对泛型类创建实例的时候,在尖括号中赋值相应的类型便是。T 就会被替换成对应的类型,如 String 或者是 Integer。你可以相像一下,当一个泛型类被创建时,内部自动扩展成下面的代码。

    public class Test<String> {
    	String field1;
    }
    
    
    

    当然,泛型类不至接受一个类型参数,它还可以这样接受多个类型参数。

    public class MultiType <E,T>{
    	E value1;
    	T value2;
    	
    	public E getValue1(){
    		return value1;
    	}
    	
    	public T getValue2(){
    		return value2;
    	}
    }
    
    
    

    泛型方法

    public class Test1 {
    
    	public <T> void testMethod(T t){
    		
    	}
    }
    

    泛型方法与泛型类稍有不同的地方是,类型参数也就是尖括号那一部分是写在返回值前面的。<T>中的 T 被称为类型参数,而方法中的 T 被称为参数化类型,它不是运行时真正的参数。

    当然,声明的类型参数,其实也是可以当作返回值的类型的。

    public  <T> T testMethod1(T t){
    		return null;
    }
    

    泛型类与泛型方法的共存现象

    public class Test1<T>{
    
    	public  void testMethod(T t){
    		System.out.println(t.getClass().getName());
    	}
    	public  <T> T testMethod1(T t){
    		return t;
    	}
    }
    

    上面代码中,Test1<T>是泛型类,testMethod 是泛型类中的普通方法,而 testMethod1 是一个泛型方法。而泛型类中的类型参数与泛型方法中的类型参数是没有相应的联系的,泛型方法始终以自己定义的类型参数为准

    所以,针对上面的代码,我们可以这样编写测试代码。

    Test1<String> t = new Test1();
    t.testMethod("generic");
    Integer i = t.testMethod1(new Integer(1));
    
    

    泛型类的实际类型参数是 String,而传递给泛型方法的类型参数是 Integer,两者不想干。

    但是,为了避免混淆,如果在一个泛型类中存在泛型方法,那么两者的类型参数最好不要同名。比如,Test1<T>代码可以更改为这样

    public class Test1<T>{
    
    	public  void testMethod(T t){
    		System.out.println(t.getClass().getName());
    	}
    	public  <E> E testMethod1(E e){
    		return e;
    	}
    }
    

    泛型接口

    泛型接口和泛型类差不多,所以一笔带过。

    public interface Iterable<T> {
    }
    

    通配符 ?

    除了用 <T>表示泛型外,还有 <?>这种形式。 被称为通配符。

    可能有同学会想,已经有了 <T>的形式了,为什么还要引进 <?>这样的概念呢?

    class Base{}
    
    class Sub extends Base{}
    
    Sub sub = new Sub();
    Base base = sub;			
    

    上面代码显示,Base 是 Sub 的父类,它们之间是继承关系,所以 Sub 的实例可以给一个 Base 引用赋值,那么

    		
    List<Sub> lsub = new ArrayList<>();
    List<Base> lbase = lsub;
    
    

    最后一行代码成立吗?编译会通过吗?

    答案是否定的。

    编译器不会让它通过的。Sub 是 Base 的子类,不代表 List<Sub>List<Base>有继承关系。

    但是,在现实编码中,确实有这样的需求,希望泛型能够处理某一范围内的数据类型,比如某个类和它的子类,对此 Java 引入了通配符这个概念。

    所以,通配符的出现是为了指定泛型中的类型范围

    通配符有 3 种形式。

    1. <?>被称作无限定的通配符。
    2. <? extends T>被称作有上限的通配符。
    3. <? super T>被称作有下限的通配符。

    无限定通配符 <?>

    无限定通配符经常与容器类配合使用,它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关。

    public void testWildCards(Collection<?> collection){
    }
    

    上面的代码中,方法内的参数是被无限定通配符修饰的 Collection 对象,它隐略地表达了一个意图或者可以说是限定,那就是 testWidlCards() 这个方法内部无需关注 Collection 中的真实类型,因为它是未知的。所以,你只能调用 Collection 中与类型无关的方法。
    这里写图片描述

    我们可以看到,当 <?>存在时,Collection 对象丧失了 add() 方法的功能,编译器不通过。
    我们再看代码。

    List<?> wildlist = new ArrayList<String>();
    wildlist.add(123);// 编译不通过
    

    有人说,<?>提供了只读的功能,也就是它删减了增加具体类型元素的能力,只保留与具体类型无关的功能。它不管装载在这个容器内的元素是什么类型,它只关心元素的数量、容器是否为空?我想这种需求还是很常见的吧。

    有同学可能会想,<?>既然作用这么渺小,那么为什么还要引用它呢? 

    个人认为,提高了代码的可读性,程序员看到这段代码时,就能够迅速对此建立极简洁的印象,能够快速推断源码作者的意图。

    <? extends T>

    <?>代表着类型未知,但是我们的确需要对于类型的描述再精确一点,我们希望在一个范围内确定类别,比如类型 A 及 类型 A 的子类都可以。

    <? extends T> 代表类型 T 及 T 的子类。 ```java public void testSub(Collection<? extends Base> para){ } ``` 上面代码中,para 这个 Collection 接受 Base 及 Base 的子类的类型。 但是,它仍然丧失了写操作的能力。也就是说 ```java para.add(new Sub()); para.add(new Base()); ``` 仍然编译不通过。 没有关系,我们不知道具体类型,但是我们至少清楚了类型的范围。 ## ```<? super T> ``` 这个和 ```<? extends T>```相对应,代表 T 及 T 的超类。 ```java public void testSuper(Collection<? super Sub> para){ } ``` ```<? super T>```神奇的地方在于,它拥有一定程度的写操作的能力。 ```java public void testSuper(Collection<? super Sub> para){ para.add(new Sub());//编译通过 para.add(new Base());//编译不通过 } ``` # 通配符与类型参数的区别 一般而言,通配符能干的事情都可以用类型参数替换。 比如 ```java public void testWildCards(Collection<?> collection){}
    可以被
    ```java
    public <T> void test(Collection<T> collection){}
    

    取代。

    值得注意的是,如果用泛型方法来取代通配符,那么上面代码中 collection 是能够进行写操作的。只不过要进行强制转换。

    public <T> void test(Collection<T> collection){
    	collection.add((T)new Integer(12));
    	collection.add((T)"123");
    }
    

    需要特别注意的是,类型参数适用于参数之间的类别依赖关系,举例说明。

    public class Test2 <T,E extends T>{
    	T value1;
    	E value2;
    }
    
    
    public <D,S extends D> void test(D d,S s){
    		
    	}
    

    E 类型是 T 类型的子类,显然这种情况类型参数更适合。
    有一种情况是,通配符和类型参数一起使用。

    public <T> void test(T t,Collection<? extends T> collection){
    	
    }
    

    如果一个方法的返回类型依赖于参数的类型,那么通配符也无能为力。

    public T test1(T t){
    	return value1;
    }
    

    类型擦除

    泛型是 Java 1.5 版本才引进的概念,在这之前是没有泛型的概念的,但显然,泛型代码能够很好地和之前版本的代码很好地兼容。

    这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除

    通俗地讲,泛型类和普通类在 java 虚拟机内是没有什么特别的地方。回顾文章开始时的那段代码

    List<String> l1 = new ArrayList<String>();
    List<Integer> l2 = new ArrayList<Integer>();
    		
    System.out.println(l1.getClass() == l2.getClass());
    

    打印的结果为 true 是因为 List<String>List<Integer>在 jvm 中的 Class 都是 List.class。

    泛型信息被擦除了。

    可能同学会问,那么类型 String 和 Integer 怎么办?

    答案是泛型转译。

    public class Erasure <T>{
    	T object;
    
    	public Erasure(T object) {
    		this.object = object;
    	}
    	
    }
    
    

    Erasure 是一个泛型类,我们查看它在运行时的状态信息可以通过反射。

    Erasure<String> erasure = new Erasure<String>("hello");
    Class eclz = erasure.getClass();
    System.out.println("erasure class is:"+eclz.getName());
    
    

    打印的结果是

    erasure class is:com.frank.test.Erasure
    

    Class 的类型仍然是 Erasure 并不是 Erasure<T>这种形式,那我们再看看泛型类中 T 的类型在 jvm 中是什么具体类型。

    Field[] fs = eclz.getDeclaredFields();
    for ( Field f:fs) {
    	System.out.println("Field name "+f.getName()+" type:"+f.getType().getName());
    }
    

    打印结果是

    Field name object type:java.lang.Object
    

    那我们可不可以说,泛型类被类型擦除后,相应的类型就被替换成 Object 类型呢?

    这种说法,不完全正确。

    我们更改一下代码。

    public class Erasure <T extends String>{
    //	public class Erasure <T>{
    	T object;
    
    	public Erasure(T object) {
    		this.object = object;
    	}
    	
    }
    

    现在再看测试结果:

    Field name object type:java.lang.String
    

    我们现在可以下结论了,在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T>则会被转译成普通的 Object 类型,如果指定了上限如 <T extends String>则类型参数就被替换成类型上限。

    所以,在反射中。

    public class Erasure <T>{
    	T object;
    
    	public Erasure(T object) {
    		this.object = object;
    	}
    	
    	public void add(T object){
    		
    	}
    	
    }
    
    

    add() 这个方法对应的 Method 的签名应该是 Object.class。

    Erasure<String> erasure = new Erasure<String>("hello");
    Class eclz = erasure.getClass();
    System.out.println("erasure class is:"+eclz.getName());
    
    Method[] methods = eclz.getDeclaredMethods();
    for ( Method m:methods ){
    	System.out.println(" method:"+m.toString());
    }
    

    打印结果是

     method:public void com.frank.test.Erasure.add(java.lang.Object)
    

    也就是说,如果你要在反射中找到 add 对应的 Method,你应该调用 getDeclaredMethod("add",Object.class)否则程序会报错,提示没有这么一个方法,原因就是类型擦除的时候,T 被替换成 Object 类型了。

    类型擦除带来的局限性

    类型擦除,是泛型能够与之前的 java 版本代码兼容共存的原因。但也因为类型擦除,它会抹掉很多继承相关的特性,这是它带来的局限性。

    理解类型擦除有利于我们绕过开发当中可能遇到的雷区,同样理解类型擦除也能让我们绕过泛型本身的一些限制。比如
    这里写图片描述

    正常情况下,因为泛型的限制,编译器不让最后一行代码编译通过,因为类似不匹配,但是,基于对类型擦除的了解,利用反射,我们可以绕过这个限制。

    public interface List<E> extends Collection<E>{
    	
    	 boolean add(E e);
    }
    
    

    上面是 List 和其中的 add() 方法的源码定义。

    因为 E 代表任意的类型,所以类型擦除时,add 方法其实等同于

    boolean add(Object obj);
    

    那么,利用反射,我们绕过编译器去调用 add 方法。

    public class ToolTest {
    
    
    	public static void main(String[] args) {
    		List<Integer> ls = new ArrayList<>();
    		ls.add(23);
    //		ls.add("text");
    		try {
    			Method method = ls.getClass().getDeclaredMethod("add",Object.class);
    			
    			
    			method.invoke(ls,"test");
    			method.invoke(ls,42.9f);
    		} catch (NoSuchMethodException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (SecurityException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalAccessException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IllegalArgumentException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (InvocationTargetException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		for ( Object o: ls){
    			System.out.println(o);
    		}
    	
    	}
    
    }
    
    

    打印结果是:

    23
    test
    42.9
    

    可以看到,利用类型擦除的原理,用反射的手段就绕过了正常开发中编译器不允许的操作限制。

    泛型中值得注意的地方

    泛型类或者泛型方法中,不接受 8 种基本数据类型。

    所以,你没有办法进行这样的编码。

    List<int> li = new ArrayList<>();
    List<boolean> li = new ArrayList<>();
    
    

    需要使用它们对应的包装类。

    List<Integer> li = new ArrayList<>();
    List<Boolean> li1 = new ArrayList<>();
    

    对泛型方法的困惑

    public <T> T test(T t){
    	return null;
    }
    
    

    有的同学可能对于连续的两个 T 感到困惑,其实 <T>是为了说明类型参数,是声明,而后面的不带尖括号的 T 是方法的返回值类型。
    你可以相像一下,如果 test() 这样被调用

    test("123");
    

    那么实际上相当于

    public String test(String t);
    

    Java 不能创建具体类型的泛型数组

    这句话可能难以理解,代码说明。

    List<Integer>[] li2 = new ArrayList<Integer>[];
    List<Boolean> li3 = new ArrayList<Boolean>[];
    
    

    这两行代码是无法在编译器中编译通过的。原因还是类型擦除带来的影响。

    List<Integer>List<Boolean>在 jvm 中等同于List<Object>,所有的类型信息都被擦除,程序也无法分辨一个数组中的元素类型具体是 List<Integer>类型还是 List<Boolean>类型。

    但是,

    List<?>[] li3 = new ArrayList<?>[10];
    li3[1] = new ArrayList<String>();
    List<?> v = li3[1];
    

    借助于无限定通配符却可以,前面讲过 代表未知类型,所以它涉及的操作都基本上与类型无关,因此 jvm 不需要针对它对类型作判断,因此它能编译通过,但是,只提供了数组中的元素因为通配符原因,它只能读,不能写。比如,上面的 v 这个局部变量,它只能进行 get() 操作,不能进行 add() 操作,这个在前面通配符的内容小节中已经讲过。

    泛型,并不神奇

    我们可以看到,泛型其实并没有什么神奇的地方,泛型代码能做的非泛型代码也能做。

    而类型擦除,是泛型能够与之前的 java 版本代码兼容共存的原因。

    可量也正因为类型擦除导致了一些隐患与局限。

    但,我还是要建议大家使用泛型,如官方文档所说的,如果可以使用泛型的地方,尽量使用泛型。

    毕竟它抽离了数据类型与代码逻辑,本意是提高程序代码的简洁性和可读性,并提供可能的编译时类型转换安全检测功能。

    类型擦除不是泛型的全部,但是它却能很好地检测我们对于泛型这个概念的理解程度。

    我在文章开头将泛型比作是一个守门人,原因就是他本意是好的,守护我们的代码安全,然后在门牌上写着出入的各项规定,及“xxx 禁止出入”的提醒。但是同我们日常所遇到的那些门卫一般,他们古怪偏执,死板守旧,我们可以利用反射基于类型擦除的认识,来绕过泛型中某些限制,现实生活中,也总会有调皮捣蛋者能够基于对门卫们生活作息的规律,选择性地绕开他们的监视,另辟蹊径溜进或者溜出大门,然后扬长而去,剩下守卫者一个孤独的身影。

    所以,我说泛型,并不神秘,也不神奇

    读者们都在下面的二维码所示的免费的知识星球问我问题:
    在这里插入图片描述

    展开全文
  • java面向对象简介

    千次阅读 多人点赞 2021-11-06 13:59:14
    因此,尽管人们发现C++不太适用,但在设计Java的时候还是尽可能地接近C++,以便系统更易于理解,Java剔除了C++中许多很少使用、难以理解、易混淆的特性。在目前看来,这些特性带来的麻烦远远多于其带来的好处。 的确...
  • Java基础-面向对象

    千次阅读 2020-01-16 17:50:17
    Java基础-面向对象 由于年前工作太忙、实在没时间整理Java基本知识。 但是可能长时间不更新不好看、再说我个人也没有半途而废的习惯。所以决定节前还是更新一篇吧。 这也算是Java的基本思想和一些偏进阶的东西。 ...
  • 案例上手 Python 数据可视

    万次阅读 多人点赞 2019-02-27 23:30:05
    课程亮点 ...数据可视是数据分析和机器学习的重要环节,比如数据清洗、特征工程、机器学习、数据分析(特别是报告)、评估等环节都会用到“数据可视”技术。 数据可视同时还广泛存在于各...
  • SAS(二)SAS基本数据类型及SAS基本模块的介绍 SAS基本介绍 SAS 是英文Statistical Analysis System的缩写,翻译成汉语是统计分析系统,最初由美国北卡罗来纳州立大学两名研究生研制,1976 年创立SAS公司, 2006年...
  • Java对象与Java类

    千次阅读 多人点赞 2021-07-28 22:23:35
    对象与引用基本类型和引用类型的区别:值传递与引用传递8.static关键字概念static属性static方法代码块9.类的加载执行10.包包的概念:包的作用:包(package)的命名规范:访问权限修饰符11.面向对象语言的三大特征1....
  • 面向对象编程基本概念

    千次阅读 2018-04-18 15:41:53
    OOP专注于模块,改变容忍度,代码重用,易于理解和分布式开发。今天大多数项目都使用面向对象的概念。由于C ++引入了OOP,因此用户体验已经产生了关于如何最好地利用OOP的更多知识。有很多关于面向对象的协议和更...
  • 文件存储NAS与对象存储OSS

    千次阅读 2021-02-04 15:44:47
    文件存储与对象存储 摘要:本文主要介绍文件存储NAS与对象存储OSS这2种目前主要的存储技术,以及差异,并...NAS采用NFS或CIFS命令集访问数据,以文件为传输协议,通过TCP/IP实现网络存储,可扩展性好、价格便..
  • 数字成熟度评估模型一文读尽

    千次阅读 2022-03-03 16:26:53
    虽然纯“打分”的数字成熟度评分对企业并没有太大帮助,但理解这些模型的设计思想对于我们理解数字转型很有价值。 正文开始 本文一共提供了CMM、DMM、DCMM、DCAM、MD3M、DataFlux、IBMMMI、DSMM、IOMM、中新联团...
  • 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表(存储层次...
  • 数据聚焦于数据的采集、清理、预处理、分析和挖掘,图形聚焦于解决对光学图像进行接收、提取信息、加工变换、模式识别及存储显示,可视聚焦于解决将数据转换成图形,并进行交互处理。 信息:是数据的内涵,信息是...
  • 数据可视概览

    万次阅读 多人点赞 2017-12-07 12:08:07
    科学可视(Scientific Visualization)、 信息可视(Information Visualization)和可视分析学(Visual Analytics)三个学科方向通常被看成可视的三个主要分支。而将这三个分支整合在一起形成的新学科 “数据...
  • Java基础篇面向对象总结

    千次阅读 多人点赞 2022-04-09 08:10:57
    基本类型和引用类型 匿名对象的使用 方法重载loading.... 方法重写 方法重载与方法重写的区别: 可变个数的形参 递归地使用 封装和隐藏 四种权限修饰符 继承 多态 属性 方法 构造器 代码块 内部类(了解) this...
  • 计算机科学与技术专业:主要学什么? 本文主要包括如下内容: 程序员职位要求 CS 专业课程列表 怎样寻找资源学习? 程序员高薪排行榜 程序员职位要求 看课程之前,我们先来看看 BAT 的招聘岗位 JD: 新...
  • 未知 第3章 用泛型实现参数化类型 本章内容 理解泛型类型和方法 泛型方法的类型推断 类型约束 反射和泛型 CLR行为 泛型的限制 与其他语言的对比 一个真实 1 的故事:前几天,我老婆和我准备去超市进行每周例行的...
  • 均值滤波适用于处理什么样的噪声

    千次阅读 2020-12-20 02:48:49
    图像降噪是图像处理中的专业术语。在现实生活中,我们看到的数字图像,在数字和传输过程中由于常受到成像设备与外部环境噪声干扰等影响,把这些图像称为含噪图像或者叫噪声图像。减少数字图像中噪声的过程称为图像...
  • 信息运维建设

    千次阅读 2017-08-12 16:40:51
    信息综合运维体系建设 目 录1. 用户现状分析 32. 运维服务的总体思路 32.1. 运维服务理念 32.2. 服务的目标及内容 42.3. 运维服务的保障体系 43. 运维服务体系 53.1. 运维服务管理 53.2. 运维服务工作规范 63.3....
  • 今天闲来无事,有空闲的时间,所以想坐下来聊一聊Java的GC以及Java对象在内存中的分配。 和标题一样,本篇绝对是用最直接最通俗易懂的大白话来聊 文章中基本不会有听起来很高大上专业术语,也不会有太多概念性的...
  • 七:面向对象(中)

    万次阅读 多人点赞 2021-02-05 14:16:14
    文章目录01、继承性的使用与理解1.1、继承性练习02、方法的重写(override/overwrite)2.1、方法重写的细节2.2、方法的练习03、四种访问权限修饰符04、关键字:super05、子类对象实例过程06、面向对象特征之三:多态...
  • iOS自动测试的那些干货

    万次阅读 多人点赞 2017-03-09 12:08:37
    本文所讲解的均是基于XCode 8.2.1,有些概念可能不适用于低版本的XCode 自动测试 自动测试就是写一些测试代码,用代码代替人工去完成模块和业务的测试。 其实不管是开发还是测试,如果你在不断...
  • Java实现面向对象编程

    万次阅读 多人点赞 2018-07-17 16:18:06
    1.1用面向对象设计电子宠物系统... 14 1.1.1为什么使用面向对象... 14 1.1.2使用面向对象进行设计... 15 1.2通过创建对象实现领养宠物功能... 17 1.2.1创建类的对象... 17 1.2.2构造方法及其重载... 23 1.2.3...
  • 2020 年前端技术发展盘点

    万次阅读 多人点赞 2021-03-30 08:36:09
    上面的专业术语不好理解的话,我们也可以这么理解。微服务 —— 也就是把单独一个业务写成一个独立的服务,有独立的服务器、接口体系、并且有一个单独的团队进行开发与维护。 就比如,淘宝的订单和用户两个 “模块...
  • 提起配对t检验的适用条件,大家都知道,有人问配对样本T检验的假设前提是什么,另外,还有人想问t检验的应用条件是什么,你知道这是怎么回事?其实两独立样本T检验的适用范围是什么,下面就一起来看看独立样本T检验...
  • Java 下数据业务逻辑开发技术 JOOQ 和 SPL

    万次阅读 多人点赞 2022-09-11 10:27:45
    JOOQ支持完整的面向对象的编程...JOOQ可以使用Java的Lambda表达式、函数调用接口和流程控制语法,理论上也支持面向函数和面向过程,但这些表达式\语法没有为JOOQ的结构数据对象(Result)而设计,使用时还不够方便。
  • 从应用角度看块存储、文件存储、对象存储 产品和市场需求有各种相互影响的关系,但不管是哪一种,最终呈现都是产品和应用需求需要对应匹配。应用需求越多样,市场也就划分得更加细,产品种类也就更加丰富。在...
  • 一、什么是自动测试自动测试是把以人为驱动的测试行为改成机器执行,通过精心设计的测试用例,由机器按照测试用例的执行步骤对应用进行自动操作,然后输出结果,由测试人员进行比较。自动测试可以极大的节省...
  • 软件工程原则: 抽象与自顶向下,逐层细化 信息隐蔽和数据封装 模块 局部 确定性 一致性和标准 完备性和可验证性 瀑布模型: 开发活动的特征:(1)以上一项活动方产生的工作对象为输入(2)利用这一输入,实施...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 45,943
精华内容 18,377
热门标签
关键字:

对象专业化适用类型