2024-06-01

19 篇热帖

1. 京东给我送了一次货,我就欠他们一百多块钱了
我上周分别在淘宝和京东上买了榴莲肉。那天正在上班,接到快递的电话,我知道是我买的榴莲肉到了。我听到的声音比较模糊,我估计送货员是在我门口打的,因为周围都是水泥墙,信号不好,我心想不就是个快递嘛,我正在上班就一直嗯嗯嗯,喜欢快点结束通话,你放在门口就行了。

下班回到小区门口,把另一个快递找到,就是一个封闭的泡沫箱。回到家,看到一个帆布保温箱,我爸还问我要不要还,我说我不清楚,快递打的电话我也没听清,我回打个电话问问送货员。送货员的电话打不通。

我又在京东买了大米,今天送货到了,快递员问我保温箱呢,我说扔了,他说保温箱要一百多块钱呢。

我大意了。
2. 什么时候会变得好点?

昨天也就是周五,妹妹通知被优化了,听到消息的时候我正写代码写的高兴,脑子突然就变慢了,她们之前也裁了几次,一个软件公司现在测试只剩下三个,基本上也就到头了。

我入职新公司 76 天还没转正,上家公司和妹妹差不多的情况,巅峰时期近 500 人,现在应该 100 出头,在那里呆了两年半,总的来说还是可以的,但总是偶尔担心什么要找新工作了。

20 年毕业,19 年实习,实习公司在我走的前几个月开始拖欠工资,第一家正式的公司是我主动离开的,第二家公司呆了不到 2 个月被开了,又过了一个多月全员解散,和我约谈的时候,我问领导为什么知道公司不行了还要招人,他说当时预期还好。第三家公司虽然干了两年半,但一直过的忐忐忑忑,还没转正的时候就有预期不太好。

我表哥去年初的时候公司解散,去仲裁才拿到赔偿,找了大半年,今年四月底的时候公司又解散了,这周告诉我他找到了新工作,我看了下他的新公司,感觉不太好,他说:“这对我来说已经是最好的选择了”。


妹妹去年 7 月来深圳找工作,8 月底入职,3k+的前端干了快一年,上个月转正答辩,说还没拿到毕业证,每个月涨一点,拿到毕业证后 8k 。她还没有出去租房子,此后我们经常聊天说什么时候去租房子,我还加了一些中介,偶尔关注附近有没有合适的房子,也经常说有了 8k 一个月可以存多少,她很开心,我也很开心,我爸妈也很开心。昨天在群里说被优化的时候,我妈表现的很失落,妹妹还安慰了她几句。

5 月初的时候,我和老婆出去旅游本来会带妹妹一起的,她说要回学校去弄毕业的事情,我想她回到学校的时候定然也是感慨加兴奋的。回想我当时拿到毕业证从武汉出发来深圳的时候,那是我第一次做卧铺,在火车上我很激动,设想未来如何美好,在火车一直循环听着“安河桥”。我喜欢歌词里面的两句:“我知道那些夏天,就像青春一样回不来,我知道吹过的牛逼,也会随青春一笑了之”。

我经常在想,如果我不做程序员,还能干点什么,想不到一个结果,我不愿意去跑外卖和滴滴,我的身体也不支持我去做这些事(当然更大可能是因为事情还没到这一步),所以我不知道我能干什么。既然没办法去多赚钱,那就想办法节约钱,理论上工资上涨生活水平也会随之上涨,而我却每周都和我老婆讨论怎么才能省点,还能从什么地方再省点?

来深圳的前三年我是坚决的不去做饭,我觉得很浪费时间,做的也不好吃,刚开始没钱就吃快餐,后面觉得快餐不那么干净,还想吃点好的,每周都会去吃看起来干净贵点的餐厅。现在周末做饭已经坚持了好几个月了,确实省钱,5 月第一次实现了生活费不超支(我个人生活费是 2100 ,只做为我日常地铁和吃饭的开销,之前是 2200 现在减了 100 )


我自己做了几年的程序员,但却给不了妹妹太多的帮助,我甚至不知道该如何去安慰她,好在她比我乐观。我妈问我要不要让妹妹回家去,在家里准备这样经济压力会小点,我拒绝了这个,理由是回去了大概率出不来了,在村里压力更大。

她肯定是可以找到新工作的,但不知道下次失业的是谁,是我还是我老婆?这事总是压在心里,时不时的就会被挑起,让我 emo 一下,但我又改变不了什么。

什么时候才会变好呢?我所谓的变好,不是每个人都可以活得很好,最起码 80%的人可以有一份相对稳定点的工作吧?


最近看完了《小米创业思考》,我的焦虑是什么时候会失业,大佬的焦虑是如何才能让世界变得更好一点。大部分人的梦想都会被生活磨灭,但我觉得不应该那么早就被磨灭,我才不到 27 ,虽然我不会自杀,不会报复社会,我的生活相对来说还很健康,只是缺少了年轻人该有的活力。但还是应该永远相信美好的事情即将发生。

3. 要对单个 6.20TB 的超大 csv 文件保持顺序的情况下进行去除重复行,有什么好思路?显然不可能加载进内存
4. 这个份子钱要给吗

一个多年的好朋友明天结婚,在犹豫要不要给他份子钱,因为有些纠葛。

  • 他欠了我 6000 元,好几年了一直没还,因为欠钱的事情,我们的交流也变少了,微信上也基本不会主动聊天了。
  • 4 年前我结婚时他好像是给了 1000 ,但是当时也没有借钱的事情。
  • 没有撕破脸,因为真的是很多年的交情,但是我对他的看法也不像以前了,他有钱的时候也没考虑过还我,比如花 6 万八去报什么 MBA 。这让我感到很不舒服。
  • 不在一个城市,已经和他说了我不去参加婚礼。
5. 现在买 mac 是不是 49 年入国军?要不要等 M4 系列?

M4 的 GPU 性能比前代提升很多,意味着它将可能对 AI 有更好的支持

结合苹果可能在 WWDC 上发布相关的内容

这是否意味着买 M4 的 mac 才有最大的可能性搭上 AI Mac 早班车?

感觉要是现在买了旧款 Mac ,AI Mac 发布后说什么只支持 M4 及以上芯片,就尴尬了

6. 招行借款利率再创新低, 24 个月先息后本,利率 2.95

对比下来手上的房贷目前 4.5 ,心好痛

7. 红米 k40 v2rayNG 太 bug 了
Redmi K40 Gaming 手机,
MIUI 12.5.6 稳定版 12.5.6.0 ( RKJCNXM )
v2rayNG v1.7.31 ( Lib v24 ,Xray-core v1.7.0 )

对比实验:不管 vmess 还是 trojan ,只要是使用一天,第二天端口就无法访问。
在 MacOS 或者 iPadOS 上使用小飞机无此现象发生。
结论:严重怀疑红米 MIUI OS 有什么模块在上报翻墙信息给 GFW 。
8. 说说你们一加 12, 16+512 入手价格

说说你们一加 12 ,16+512 入手价格
我先来,京东自营 3994 元。

9. 燃气公司不同意帮我移表怎么办?
我最近要装修,因为户型的问题,我打算把厨房和阳台调换位置,这俩挨着,互为隔壁,中间是承重墙。
原本的燃气立管和燃气表都在之前的厨房,现在厨房位置调整到隔壁之后需要把燃气表也移动过来(燃气公司的规定),装修前找了燃气公司的师傅过来看了说的没问题,过几天过去帮我移。
但过来几周之后,装修已经进行到一半了,师傅又说需要再确认下,然后找了另一个师傅一起上门看了半天,最终跟我说这个不能移,并且说立管和燃气表都必须在厨房,但是立管是不能移动的,我这么装修会导致立管在客厅,燃气表在厨房,这个公司不允许。
现在的问题是,装修已经进行一大半了,追责也没有意义,我只想知道到底还有没有什么办法解决这个问题,我肯定希望使用燃气,如果不能解决那么我可能会决定继续装修,但是就不使用燃气了,使用电磁炉和电热水器,买个质量好一点的,但不确定这样会不会有什么问题,谢谢各位。
10. 求第一次买车的建议!

我姐这边想买一辆预算在 13w 左右的车,比较偏向于油电混动、SUV 和轿车都行,各位有没有什么相关建议,谢谢!

11. 家用公网 ip 多域名映射方法如何实现

现在家用的宽带有动态公网 ip ,通过路由器端口转发,可以通过 ip:端口 的方式访问到本地服务。 现在想在本地搭建多个微服务的一个测试环境,实现通过多个子域名可以访问到对应的多个服务。

路由器上面的 ddns 设置,但是到 oray.com 上面不知道可以如何配置域名,如果用内网穿透,按照条数的话,多几条就很贵了,比如要 vip 了。是否还有其他方式,可以直接将比如国内备案的域名转发到自己的公网 ip 上面吗?

我在想是否可以用一台阿里云的主机当作中转,将用到的子域名全部解析到这台阿里云服务器上面,然后在阿里云服务器上面设置,让其访问到对应的 ip 地址,这样实现访问域名最终访问到自己的本地环境。有没有朋友熟悉的,有没有可行性?

12. 洗一次衣服,光洗涤剂的成本就要 1 块 5 了。。。你有什么洗涤剂推荐?

之前买的洗衣凝珠,成本算下来是 3 毛钱 1 颗。一次洗一桶,一桶用 1 颗。发现这类洗衣凝珠洗不干净,排汗衣洗过失去了吸汗效果。懂户外的知道我说啥就不废话了。

后来经过各种折腾,反正希望恢复排汗衣的吸汗效果。逐步研究发现,一方面是衣服洗不干净,一方面是衣服有硅油预处理。

后来遇到一款 dalli 的洗衣液,价钱很贵,一小桶 1.1L 特价的时候好像 35 块,官方说能用 20 次左右。德国产的,这算进口。这下来洗一次衣服的成本光洗涤剂就要 1 块 5 了。不过,这个东西洗的确实干净,我的排汗衣,洗过一次就恢复了,有硅油的经过两三次洗涤后,也恢复了。

问题来了,这玩意确实贵啊,特价的时候 35 块,没碰上特价 有时候要 70 多。。。哎。。。人不能一棵树上吊死,也是来问问 V 友,你有啥推荐?

避雷洗衣凝珠,我猜啊,3-5 毛钱一颗的洗衣凝珠都大差不差,工业化大生产下来,原料,成本,提纯度啥的各种成本压缩下来,清洁效果,我想你也能猜到。。。

13. 过去、现在和未来 —— Java 的现代化之路

过去、现在和未来 —— Java 的现代化之路

Java ,一门广受赞誉,却又饱受诟病的语言,在从其诞生至今,便无时不刻的被于其他语言对比,有时候这种对比是空穴来风的诽谤,但更多的是对这门语言未来的担心,而近 10 年来涌现的一个又一个新生的程序语言更是让 Java 一次又一次地被推上风口浪尖,使公众一次又一次的质疑:Java ,是否真的停滞不前了?

2024 年,从大街上随便抓一个 Java 程序员,询问其 Java 有哪些槽点,我相信你的这个下午大概是别想离开这个人的声音了 —— 从泛型不支持基本数据类型到各种各样令人抓耳挠腮的奇怪问题,你绝对可以听这个人滔滔不绝地说上一整天。那么这些问题 Java 官方知道吗?当然知道,他们在解决吗? Ummm ,至少我们可以说,他们一直以来都正在积极的为解决这些问题而努力,并且有些槽点,其实早已在最新版本的 Java 中被解决。

因此,本篇文章的目的,便是带领读者从过去走向现在,再走向未来,回顾并前瞻 Java 已经推出,或是即将推出的全新特性,这些特性再 Java 的历史中都扮演着决定性的作用,为 Java“赶 Go 超 Rust”贡献着自己的努力。

碍于篇幅所限,我们将只重点提及几个 Java 语言史上的重大改动,而其他小的(但不代表不重要)更新,我们姑且一概掠过。若要了解 Java 从过去到现在全部的特性更新,也许你可以看看 OpenJDK 的 Java 特性提案索引页 JEP 0: JEP Index,了解更多。

Java8:Lambda 表达式和 Stream API

Java 8 无论是从 JVM 层面的变动,还是 Java 语法和标准库的变动,都可以说是 Java 有史以来第一次大规模的增补,毋庸置疑的,这次更新也为 Java 带来了第二春,使之焕发新生,而其长达近 20 年的 LTS 支持,也使其成为了 Java 历史上使用率最高,最经久不衰的 Java 版本。

在这次更新中,Java 自然是引入了全新且复杂的 Date & Time API ,看起来好像有点用但实际上很鸡肋的 Optional API 这类谈不上小但是也很难说重大的标准库修补。但是更为被人津津乐道,且在本人看来是 Java 8 最重要的两个更新,便是 Lambda 表达式和 Stream API 。

Lambda 表达式

也许是考虑到兼容性,也许就是纯粹 Java 开发者懒,自 Java 7 以前,Java 虚拟机( JVM )基本没有什么重大改动,纵然 Java 语言已经引入了诸如自动拆装箱、参数化类型(泛型)这样的重大语言特性,JVM 依然不动如山,全靠 javac 衬托。

然而到了 Java 7 ,天塌了。JVM 引入了一个全新的指令 invokedynamic,其可以在运行时动态的分派一个函数调用,这个指令最初并没有被 Java 语言本身所使用,相反,它的出现是为了解决基于 JVM 的动态类型语言(例如 Groovy )在运行时由于 JVM 无法支持函数类型动态分派而导致的巨大性能问题。

而这个指令第一次在 Java 语言中登场,便是神奇的 Lambda 表达式了。

即使你不知道 Lambda 表达式,或者他背后的函数式接口,我相信你一定写过这样的代码:

new Thread(() -> Foo.bar()).start(); // 更好的一个写法其实是 new Thread(Foo::bar).start();

这很自然,就像你可能不会泛型编程,但一定也用过带泛型的 Java 容器一样。但如果我告诉你,在过去的 Java 版本中,人们只能这么写:

new Thread(new Runnable() {
    @Override
    public void run() {
        Foo.bar();
    }
}).start();

是不是会有一种天然的碰见庞然大物的恐惧感。而事实上,在 Java 8 以前,函数式编程是不可能的,这主要源自于 Java 的一个语法缺陷:在 Java 中,函数(方法)不是一等公民。

什么是“一等公民”?来看看在 JavaScript 中大家习以为常的一段代码:

function foo(){
  console.log("foo!");
}

function bar(barFoo){
  barFoo();
}

bar(foo);

最后一行中,我们为 bar 函数直接传入 foo 函数作为其实参,并在 bar 函数中调用这个函数。我们可以将一个函数(或者说,函数指针)作为参数传入到函数中,就像其他数据类型一样。

但是 Java 是没有办法直接传入函数指针的,如果你了解 C# 的话,C# 用 Delegate(委托)机制解决这个问题,而 Java 则绕的更远一些,选择了 Functional Interface(函数式接口)作为其函数式编程的解决方案。那么,什么是函数式接口?

通俗的来讲,任意一个仅有一个抽象方法的接口,都是函数式接口(无论其是否标注 @FunctionalInterface 注解),例如我们上边看到的 Thread 构造方法中的 Runnable 接口:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

这个接口只有一个名为 run 的抽象方法,并没有任何返回值。我们可以为需要函数式接口实例的地方传入 Lambda 表达式,在运行时,Lambda 表达式会被转换为对应函数式接口的实例,就像我们为 Thread 传入构造函数参数所做的那样一样。

当然,请不要误解我的意思,并不是自 Java 8 引入函数式接口这个概念之后,才有了 Runnable 接口,相反,Runnable 接口古早有之,是函数式接口的概念被引入后,Runnable 也正巧成为了函数式接口的一部分。

Stream API

Lambda 表达式的一大创新之处,就是为在 Java 语言进行函数式编程提供了可能,由此,Stream (流) API 应运而生。这里所说的“流”并不是指 I/O 流,而是一种数据流动的管道。举个例子,现在有一个包含 10000 个数字的 int 数组:

int[] array = new int[10000];

我想找出该数组中所有数字大于 5000 的数字,然后让他们加一个不大于 500 的随机数,最后求和。在不使用 Stream API 的情况下我们会这么写:

public int sumRandomNumber(int[] array, Random random){
        int rst = 0;
        for (int i : array) {
            if (i > 5000) {
                rst += i + random.nextInt(500);
            }
        }
        return rst;
    }

但如果有了 Stream API ,只需要一行代码就可以解决:

public int sumRandomNumberWithStreamAPI(int[] array, Random random) {
        return Arrays.stream(array).filter(i -> i > 5000).map(i -> i + random.nextInt(500)).sum();
    }

在上述代码中,我们通过调用 Arrays.stream 方法将 array 转换为一个 IntStream 流对象,然后顺次调用 filtermap 流中间方法,过滤和映射数据,最终调用 sum 流终结方法,获得求和结果。

一种特定类型的数据经过流中间方法的加工处理,最终经过流终结方法收集为我们想要的形式,这极大地提高了开发效率,而在以前的 Java 中,想要达成这样的操作,会使代码变得极度复杂。

Project Loom:Java 迈向现代化的第一步

相信各位对“Coroutine (协程)”这个名词一定不陌生,被称为“轻量级线程”的它,在 I/O 密集型的应用程序开发领域可谓是如日中天。所谓“协程”,便是一种用户态的线程,它们构建于线程之上,由用户程序负责调度,而不是操作系统。比起原生的操作系统线程,他更轻量,而比起 Event Loop (事件循环)的解决方案,它又能保证对用户程序足够透明,降低开发过程中的心智负担。

许多现代语言都配备了协程的原生支持,尽管它们各自的实现方式并不相同,例如 Go 的 Goroutine,Kotlin 的 Kotlin Coroutines 或是 C++ 20 的 Coroutines。在早期版本的 Java 中,其实有一个名为“Green thread (绿色线程)”的协程实现,但因为各种原因,最终被替换回了现在的操作系统线程。

但是我们确实需要协程,于是 2017 年,Project Loom 应运而生,它的使命就是为 Java 提供自己的有栈协程实现,早期被称为“Fiber (纤程)”,后被称为“Virtual thread (虚拟线程)”,经过两个大版本的预览,其终于在 Java 21 中正式推出,这意味着 Java 平台也拥有了自己的原生协程实现。

于是现在,你可以通过 Thread 对象的静态工厂方法 ofVirtual 创建一个虚拟线程:

Thread.ofVirtual().start(()->{
	// some heavy IO stuff
});

就是这么简单,如果你在用 Spring Boot 3 ,只需要一行配置便可以在你的项目中启用虚拟线程支持:

spring.threads.virtual.enabled=true

很简单对不?现在就去试试看吧,我保证带来的性能提升是立竿见影的。

当然有关并发编程,另一个绕不开的话题便是异步编程了,Java 目前原生的异步编程由 Future 等对象支持,用起来不能说十分好用,只能说味同嚼蜡。在 Java 19 引入的 Structured Concurrency (结构化并发)事实上在一定程度上为异步编程提供了更好的解决方案,篇幅所限,在这里我们也不再展开。

Project Panama:外地人向本地人的妥协

长期以来,Java 一直以“一次编写,到处运行”作为自己的卖点,然而很不幸的是,Java 没能向开发者提供所有他们想要的原材料,因此,开发者们决定自己做,最终,在各种 JNI 函数和 Unsafe 调用的狂轰滥炸下,Java 最终还是变成了“一次编写,到处调试”的样子。

JNI 好用吗?我相信没人会说好用,不然也不可能会有 JNA 一类的库出现,JNI 看似提供了 Java 向 native 调用的接口,但实际上它完全不够灵活,无法在运行时根据程序的需要动态的链接不同的函数。自 Java 1.1 引入 JNI 开始,这个东西就基本没什么变化,大家只能捏着鼻子用这样一套并不好用的东西,或者只能叹叹气,然后另寻他法。

再回过头来看看 Unsafe ,在过去版本的 Java 中,管理堆外内存是非常复杂且危险的,尤其是当我们通过 hacky 的方式获取 sun.misc.Unsafe 类实例,并使用其中的 allocateMemory 方法来分配堆外内存时。这意味着,我们需要手动管理这些堆外内存的分配和释放,一不小心,就可能造成 JVM 虚拟机和 GC 无法处理的内存泄漏。

有些人可能会说:JVM 本来就不希望你使用堆外内存,你为什么要这么用,这不是自找没趣吗?但是很遗憾的是,有时要想获得高性能的数据吞吐或是确保数据的一致性,我们不得不这么做,例如在 Java 中使用 mmap, CAS ,或是设置内存屏障。在 Java 8 ,如果你想设置一个操作系统级别的重量级锁,你可以使用 LockSupport.park;自 Java 9 开始,如果你想对一个对象中的字段 CAS 写入,则可以用 VarHandle.compareAndSet 方法;但是其他 JVM 未能提供的操作,也许你只能像使用 JNI 一样,绕一个大圈,或是看看社区上有没有已经做好的,也许可能充满各种漏洞的小玩具。

但是事情还是需要解决的,最终这场争端以 Java 这个外地人向本地人的妥协而告终:Project Panama 应运而生。经过三个大版本的预览,Project Panama 的一个重要特性,The Foreign Function & Memory (FFM) API 终于在 Java 22 正式落地。FFM API 有什么用?首先,它可以提供灵活的本地库访问:

Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle strlen = linker.downcallHandle(
    stdlib.find("strlen").orElseThrow(),
    FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);

try (Arena arena = Arena.ofConfined()) {
    MemorySegment cString = arena.allocateFrom("Hello");
    long len = (long)strlen.invokeExact(cString); // 5
}

上述代码创建了一个操作系统标准库的链接器,在其中查找 strlen 函数并以一个从 JVM 创建的堆外字符串作为参数执行,获取结果。在这个过程中,还需要告诉 JVM 函数和参数的内存布局,以便 JVM 可以正确传入他们。

接下来,FFM API 向我们提供了更适合 Java 宝宝的堆外内存 API ,Arena ,你可以通过这种方式创建一个会自动被 GC 清理的堆外内存:

MemorySegment segment = Arena.ofAuto().allocate(100, 1);
...
segment = null; // the segment region becomes available for deallocation after this point

或者,你可以直接创建一个基于作用域的堆外内存,并使用 try-with-resource 语法包裹,只要离开 try 作用域,则分配的堆外内存会被自动释放:

MemorySegment segment = null;
try (Arena arena = Arena.ofConfined()) {
    segment = arena.allocate(100);
    ...
} // segment region deallocated here
segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException

别忘了 mmap,现在 FFM API 可以直接提供这种支持,只需要调用 FileChannel.map 方法即可:

Arena arena = Arena.ofAuto();
try {
    try (FileChannel channel = FileChannel.open(Path.of("large_file"), StandardOpenOption.READ)) {
        MemorySegment segment = channel.map(FileChannel.MapMode.READ_ONLY, 0, FILE_SIZE, arena);
        // use segment in your way
    }
} catch (IOException e) {
    throw new RuntimeException(e);
}

是不是简简又单单呢?有了 FFM API 这把瑞士军刀,相信以后 Java 能做的事情会更有趣和疯狂。

Project Valhalla:走向未来

至此,我们已经介绍完了 Java 走向现代化三座大山中已经落地的前两座,如你所见的是,他们每一个都充满诱惑,十分大胆,令 Java 焕发新生,但是 Project Valhalla 将带给我们的,比前面我讲过的那些特性更加疯狂,更加颠覆:为 Java 引入值类型对象,补上长久以来 Java 泛型编程的缺陷,并为 JVM 虚拟机提供运行时可见的泛型参数。

让我们先来回忆一下泛型的前世今生:泛型于 Java 1.5 被首次引入,其更官方、也更直观的名称应该是 Parameterized Type (参数化类型),其允许将类型作为类或函数的参数提供,以便于更好的进行类型检查或是根据不同的泛型特化代码实现,然而后者并不被 Java 泛型所支持,因为 Java 泛型采用的方案于 C++, Go, Rust 这些语言的泛型方案有本质不同:Java 的泛型只是编译器语法糖,在运行时并没有影响代码执行,这意味着,当你在 C++ 中使用 Vector<bool>Vector<int> 时,C++ 编译器事实上会生产两个不同版本的 Vector 类(这也是其名称“模板”的由来),但 Java 并不会改变这一点,List<Boolean>List<Integer> 和其未泛化原始类型 List 没有任何差别,编译器会在需要提供或返回泛型参数时帮你做类型安全检查或自动类型转换,而 JVM 不会感知到泛型的存在。

泛化泛型和具化泛型的争端从未停止,本文也无心讨论此两者之间各自的优劣,但是不可否认是,泛化泛型确实为 Java 引入了一个难以逾越的语法鸿沟:那就是参数化类型无法接受基本数据类型作为参数,这意味着在 C# 程序员严重看起来十分正常的代码:

List<int> list = new List<>();

在 Java 中是不可能的。而长久以来,Java 程序员只能被迫在需要将基本数据类型放入集合的场景下进退两难:要么把 int 装箱成 Integer,忍受额外的对象创建开销;要么自行构建,或者使用各种工具库提供的特化集合类型(例如 IntArrayList, DoubleArrayList 等)。而事实上,这种语法鸿沟在 Java 中由来已久,例如 switch 语句不支持 double 等类型,instanceof 关键字不支持针对基本数据类型的模式匹配等,颇令新手疑惑,好在在最近的版本( Java 23 )中,这些问题都逐步得到完善,进入预览的流程。

再回过头来看看基本数据类型的装箱机制,这实际上是十分不明智的,因为基本数据类型这种可能被程序大量使用的数据,他们本应将其数值直接存储到内存中,而不是被包装一个含有比他们实际内容更为复杂的对象和对象头,这无疑增加了系统的内存压力。而参数化类型对基本数据类型的缺位更是加剧了这一问题。

为此,Project Valhalla 横空出世,直指这些痛点问题,并推出了它们的解决方案:值类型和通用泛型。

在未来的 Java 版本中,我们将可以通过 value class 标识创建一个值类型类:

value record Color(byte red, byte green, byte blue) {} // 值记录类型

这种类型没有对象头,其 hashCode 直接据其所含字段计算,这同时也意味着,对值类型进行 == 比较将会比较其值,而不是其地址。在未来,所有的基本数据类型包装类都会被升级为这种值类型。而原本的类型将会被称为 Identity class ,意为具有身份的类型。

而通用泛型(这是一个早前叫法,但我觉得放到这里更直观,所以接着沿用下来)将允许我们在未来在泛型中直接使用基本数据类型作为泛型参数,而这种实现有可能依然是通过自动拆装箱实现的。

如果你恰巧用过 Java 16 及以上的版本,你可能会发现有一个新特性和上述特性有点类似,那就是 Record (记录)类型,该特性允许你通过简单的语法创建一个不可变的 POJO 对象,并为其实现 equals/hashCode/toString 方法和构造函数,而不必由你手动实现,或是使用额外的 @lombok.Data@lombok.AllArgsConstructor注解:

record Point(int x, int y) { } // 通过 new Point(0, 0) 构造,通过 point.x()/point.y() 访问

但实际上,record 和 value class 是有本质区别的。Record 本质上还是一个对象,他依然是一个特殊的语法糖,并没有改变对象的本质;而 value class 则彻底颠覆了 Java 原有的对象模型。

除此之外,Project Valhalla 还有一些很有意思的提案,例如为 JVM 添加可 null 和非 null 类型,就将 C# 和 Kotlin 所做的那样;亦或者在运行时保留泛型参数,提供特化类型的实现等。

最后要说的是,Project Valhalla 的相关提案仍在不断更新,早在草案时期,相关提案就已被推翻重置了多次,因此对于该提案的相关描述在未来可能会是不准确的,希望读者悉知。

引用

14. 正在开发网站,个人无法申请微信支付接口 很好奇这些第四方支付是怎么帮你申请微信支付官方接口的

个人无法在微信支付官方申请接口,为什么在他们这边交钱就可以,而且不需要营业执照。

要交钱的话,我给微信支付官方交钱也乐意啊,偏偏个人在官方不能开户。但在第四方却可以交钱开户,他们是做到的,有懂的么?

15. Google Photo 后台总被杀掉,没法备份

开启了后台刷新,所有权限都开了,后台只能保留一小会,切到别的软件玩一会,就不会继续备份了。我知道 IOS 有所谓的什么墓碑机制,但他就是被杀了,如果只是短暂的切出去,备份不会被停止,切出去超过 1 分钟后台就会被杀掉,就停止备份了。如何解决这个问题?

16. 有没有什么低成本的单盘位 NAS 推荐

arm 平台的是不是能便宜点

主要用来当异地备份的目地的:

1.支持 wireguard 和 webdav 就行,这俩应该不难,能装 linux 就行,况且都有 go 版本的二进制包.

2.带盘位;用 usb 硬盘盒之类的方式容易坏硬盘,供电也不行.

3.不要太大

4.支持 docker 加分,非必须

最便宜的现成方案有什么推荐吗?

17. 收费的 Charles 比免费的 Fiddler Classic 好用在哪里?哪个功能值 50 美元了?看好多人在用,我觉得 Fiddler 够好用了, Charles 是 Java 写的还卡一点
18. 如何用 Pythony 语言写一个在京东商城抢购 618 特价商品的程序
下面是我在 Chatgpt 上得到的代码程,我是个初学者,这个程序这样写有问题吗?
import requests
from bs4 import BeautifulSoup
import time

# 京东的商品 URL
url = 'https://item.jd.com/100012043978.html'

# 设置请求头,模拟浏览器
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36'
}

def check_price(url):
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, 'html.parser')

# 解析价格 (需要根据具体页面结构调整)
price_tag = soup.find('span', {'class': 'price'})
if price_tag:
price = price_tag.text.strip()
print(f"当前价格: {price}")
else:
print("未能获取价格信息")

def main():
while True:
check_price(url)
# 等待一段时间后再次检查,避免频繁请求
time.sleep(60) # 每 60 秒检查一次

if __name__ == "__main__":
main()
19. 不打算买房,公积金要提取出来吗
在上海,没有近期买房的打算,公积金账户里有 17w ,补充公积金账户里还有 7w 。看了下上海的公积金贷款政策,账户里有 3w 就可以贷满 80w 了?所以多出来的部分是不是提出来比较好?顺便问下现在提取只能租房提取吗?每个月额度好像只有 5000 ?