软件构造:3-1 Data Type and Type Checking

本文围绕Java中的数据类型与类型检验展开。介绍了静态/动态类型检查的区别、适用场景及可检查的错误,阐述了可变与不可变数据类型的特性与优缺点。还通过程序快照可视化变量变化,分析了迭代器暗中破坏问题,指出使用ArrayList迭代移除元素的正确方法。

数据类型与类型检验

  1. 静态/动态类型检验
  2. 可变/不可变的数据类型
  3. 可变数据的危险性
  4. 不可变数据的优越性
  5. 用Snapshot图理解数据类型
  6. 用集合类表达复杂数据类型
  7. Null的危害

Java 中的数据类型

在这里插入图片描述
注意几个小问题:

  1. object在初始化之后都是有ID的,而基本数据类型没有;
  2. class不都是mutable的!常见的immutable的class的有很多,比如将基本类型包装的对象类型:String BigInteger BigDecimal Boolean Integer Short Long Character Float Double等;

静态/动态类型检查

静态类型语言与动态类型语言的区别主要在于类型检查的时间:
静态类型检查在编译阶段进行检查,动态类型检查在运行阶段进行检查。
静态类型检查的部分bug可以自动在程序运行前被找到,而动态类型检查的bug只能在代码执行时被找到。
静态类型检查>>动态类型检查>>不检查

静态类型检查可以检查的错误:

  1. 语法错误
  2. 类名/函数名错误
  3. 参数数目错误
  4. 参数类型错误
  5. 返回值类型错误
    动态类型检查可以检查的错误:
  6. 非法的参数值(比如除零异常(稍微注意除零报的是异常),区别参数类型错误)
  7. 非法的返回值(注意区别返回值类型错误)
  8. 越界
  9. 空指针错误

静态类型检查是关于类型的,检测出的问题是不依赖于值(除非final关键字的赋值问题)的;而动态类型检查考虑值的检查

引入一下强类型语言与弱类型语言:

强类型:偏向于不容忍隐式类型转换。
弱类型:偏向于容忍隐式类型转换。
静态类型:编译的时候就知道每一个变量的类型,因为类型错误而不能做的事情是语法错误。
动态类型:编译的时候不知道每一个变量的类型,因为类型错误而不能做的事情是运行时错误。譬如说你不能对一个数字a写a[10]当数组用。

借用网络上的一张图描述强弱类型语言以及静态/动态类型检查的划分:

可变与不可变

主要内容PPT上描述很详细,只记录自己认为重要的点,嘿嘿
final方法也会被子类继承,但是无法被子类重写;

在性能上,不可变类型表现的较差,对其频繁的修改会产生大量的临时拷贝
可变类型可最少化拷贝以提高效率,也可以获得更好的性能,同时适用于在各个模块之间共享数据

防御式拷贝是针对可变类型的,不可变类型节省频繁复制的代价,与上面相反

程序快照 (作为代码层面运行时视图)

程序快照提供了一个将改变变量和改变变量的值可视化的方法
基本类型的值和对象类型的值
不可变对象用双线椭圆

不可变的引用用双线箭头

对于对象来说,final修饰的对象的引用是不可变的,但是指向的值是可变的;对于可变的引用,也可以指向不可变得值

迭代器暗中破坏

        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        for (String s : list)
            if (s.equals("2"))
                list.remove(s);
        for (String s : list)
            System.out.println(s);

尝试此段代码会报如下错误:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
	at Plan.main(Plan.java:29)

查看原因:
迭代器内置的modcount与ExpectedModCount不匹配导致报错,modCount在ArrayList中定义为记录改变了ArrayList的size(即增减操作)的次数;
当ArrayList调用iterator()方法,初始化iterator时,将list的modCount值赋给expectedModCount,此时此刻,如果后续expectedModCount和modCount的值不变,或者同步改变保持相等,iterator.next()是不会抛并发修改异常的。既然抛了,它们肯定没有同步修改,直接跟进list.remove()到ArrayList.remove()方法中查看,源码如下:

跟进fastRemove()源码如下:

System.arraycopy()效果如下图:

回到刚才说的list.remove(),方法中modCount自增了,而expectedModCount是没有修改的,因此,在下一次iterator调用next()时,将抛出ConcurrentModificationException。
问题似乎已经弄明白了,我们再看看iterator.remove()方法为什么是OK的。如下是其源码:

首先调用外部类ArrayList的remove()方法,即前面图片中所示,其中修改了modCount。而此处,同时将expectedModCount置为modCount的值,它俩的值再次同步了,所以,iterator的remove()是OK的。
关于可能其它的地方也修改了modCount或expectedModCount的值,在上面代码中,已经确认,除了本文所述的地方存在修改以及list.add()修改了modCount,从迭代开始,是没有其它修改的。
结论:
在使用ArrayList过程中,当使用iterator迭代获取元素并需要移除元素时,需使用iterator的remove()方法移除元素。如果使用list.remove(),将在iterator下一次调用next()时抛出ConcurrentModificationException。如果中途调用list.add(),同样会修改modCount,导致抛出异常。

[root@yfw ~]# cd /etc/prosody [root@yfw prosody]# sudo vim /etc/prosody/prosody.cfg.lua [root@yfw prosody]# sudo prosodyctl check config Checking config... The following configuration files have been loaded: - /etc/prosody/prosody.cfg.lua Some of your hosts may be missing features due to a lack of configuration. For more details, use the &#39;prosodyctl check features&#39; command. Done. All checks passed, congratulations! [root@yfw prosody]# sudo systemctl restart prosody [root@yfw prosody]# sudo journalctl -u prosody -f --since "30 seconds ago" -- Logs begin at Tue 2025-11-11 19:47:24 CST. -- Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: Stopping Prosody XMPP (Jabber) server... Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: prosody.service: Succeeded. Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: Stopped Prosody XMPP (Jabber) server. Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: Started Prosody XMPP (Jabber) server. ^C [root@yfw prosody]# systemctl status prosody ● prosody.service - Prosody XMPP (Jabber) server Loaded: loaded (/usr/lib/systemd/system/prosody.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2025-11-12 04:13:38 CST; 2min 24s ago Main PID: 1647416 (prosody) Tasks: 1 (limit: 23712) Memory: 20.2M CGroup: /system.slice/prosody.service └─1647416 /usr/bin/lua /usr/bin/prosody -F Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: prosody.service: Succeeded. Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: Stopped Prosody XMPP (Jabber) server. Nov 12 04:13:38 yfw.szrengjing.com systemd[1]: Started Prosody XMPP (Jabber) server. [root@yfw prosody]# curl http://127.0.0.1:5280/http-bind/ <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Prosody BOSH endpoint</title> <style> :root{color-scheme:light dark} body{margin-top:14%;text-align:center;font-family:sans-serif} h1{font-size:xx-large} p{font-size:x-large} p.warning>span{font-size:large;background-color:yellow} p.extra{font-size:large;font-family:courier} @media(prefers-color-scheme:dark){ p.warning>span{background-color:inherit;color:yellow} } </style> </head> <body> <h1> Prosody BOSH endpoint</h1> <p>It works! Now point your BOSH client to this URL to connect to Prosody.</p> </body> </html> [root@yfw prosody]# curl https://szrengjing.com/http-bind/ curl: (92) HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1) [root@yfw prosody]# curl -v -X POST \ > --data &#39;<?xml version="1.0"?><body rid="123" sid="" xmlns="http://jabber.org/protocol/httpbind"/>&#39; \ > https://szrengjing.com/http-bind/ Note: Unnecessary use of -X or --request, POST is already inferred. * Trying 124.71.230.244... * TCP_NODELAY set * Connected to szrengjing.com (124.71.230.244) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, [no content] (0): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=szrengjing.com * start date: Oct 13 03:37:46 2025 GMT * expire date: Jan 11 03:37:45 2026 GMT * subjectAltName: host "szrengjing.com" matched cert&#39;s "szrengjing.com" * issuer: C=US; O=Let&#39;s Encrypt; CN=R13 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): * Using Stream ID: 1 (easy handle 0x55b005235690) * TLSv1.3 (OUT), TLS app data, [no content] (0): > POST /http-bind/ HTTP/2 > Host: szrengjing.com > User-Agent: curl/7.61.1 > Accept: */* > Content-Length: 89 > Content-Type: application/x-www-form-urlencoded > * TLSv1.3 (OUT), TLS app data, [no content] (0): * We are completely uploaded and fine * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS app data, [no content] (0): * Connection state changed (MAX_CONCURRENT_STREAMS == 128)! * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (IN), TLS app data, [no content] (0): < HTTP/2 200 < server: nginx < date: Tue, 11 Nov 2025 20:18:55 GMT < content-type: text/xml; charset=utf-8 < content-length: 95 < access-control-allow-origin: * < access-control-max-age: 7200 < access-control-allow-headers: Content-Type < access-control-allow-methods: OPTIONS, GET, POST < x-request-id: L-II2Qx728I9 < set-cookie: server_name_session=f4349da8d0b0247f211f975b8bf78f80; Max-Age=86400; httponly; path=/ < access-control-allow-origin: * < access-control-allow-methods: POST, GET, OPTIONS < access-control-allow-headers: Content-Type, Accept, Authorization < * TLSv1.3 (IN), TLS app data, [no content] (0): * Connection #0 to host szrengjing.com left intact <body xmlns=&#39;http://jabber.org/protocol/httpbind&#39; condition=&#39;item-not-found&#39; type=&#39;terminate&#39;/>[root@yfw prosody]#
最新发布
11-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值