news 2026/9/10 19:41:20

吃透Java权限修饰符:从public到private的可访问性边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
吃透Java权限修饰符:从public到private的可访问性边界

写这篇笔记的起因,是前阵子帮一个朋友排查线上问题,翻到一段把业务字段全部设成public的代码——那个类的字段就这么裸奔在系统里,谁都能改,改完还不一定知道是哪个环节动的手。于是聊到权限修饰符,他说:“public/private谁不会啊,不就是访问范围大小吗?”这话对,但也不全对。很多时候我们在 IDE 里点两下就能补全修饰符,但真要讲清楚“为什么这里要用 protected 而不是 public”“为什么这个字段死活不该给外面改”,很多人就开始含糊了。尤其在 Java 面试里,权限修饰符几乎是被当成“八股文”来背的,但背归背,实际写代码时该用错的照样用错。这篇笔记就把我对这几个修饰符的理解重新梳理一遍,从基础语义到跨包访问的坑、从重写约束到反射对抗,尽量讲透,而不是背熟。

1. 权限修饰符的职责边界:决定“谁能看见”,而不是“谁来执行”

先把最基础的东西砸实。Java 里一共有四种访问控制级别,从宽到窄依次是publicprotected、默认(也叫包私有,不写修饰符)、private。很多人脑子里只有“public 最大,private 最小”这个印象,但四个层级在不同的场景下有完全不同的行为,尤其protected和默认修饰符,是面试里最容易混淆的一组。

1.1 四个修饰符的访问矩阵:从一张表讲清楚

我先把访问矩阵列出来,这张表建议存下来,后面所有讨论都围绕它展开。

修饰符同类同包类异包子类异包非子类
public可以可以可以可以
protected可以可以可以(特殊规则,后面详谈)不可以
默认(不写)可以可以不可以不可以
private可以不可以不可以不可以

注意几个细节。

第一,protected和默认修饰符的区别在“异包子类”这一列。也就是说,一个类如果在不同包下继承了父类,那么在子类的代码里是可以访问父类protected成员的,但默认修饰符不行。这个差别直接决定了框架设计里“模板方法”模式为什么爱用 protected。

第二,同包下protected和默认修饰符看起来效果一样——都能访问。所以很多人就误以为 protected 比默认修饰符“只多了一个异包子类的权限”。方向是对的,但具体的“异包子类访问规则”有个大坑,这在后面第三章单独讲。

第三,很多人忽略的一点是,表格里的“可以访问”指的是代码所在的位置。比如“同类”那一列,指的是在这个类内部写代码访问自己的成员;“异包非子类”那列,指的是在另一个包里,跟这个类没有任何继承关系的代码。

把这层语义理顺了,后面分析问题才有基础。权限修饰符管的是“这段代码能不能看见那个成员”,而不是“那个成员的内存有没有被分配”——成员一直都在,只是编译器让不让你碰的问题。

1.2 为什么命名为“修饰符”:可见性的本质是编译期契约

Java 里管publicprivate这类关键字叫修饰符(modifier),这个“修饰”不是装饰的意思,而是“限定、约束”。它约束的不是运行时的行为,而是编译期的“可见性契约”。

这个概念很关键,因为很多人理解成“private 成员在运行时被锁住了,别人碰不到”。实际上不是这样。Java 的访问控制在绝大多数情况下是编译器检查:javac在编译时扫描到某处代码访问了无权访问的成员,直接报编译错误,根本不会等到运行时。哪怕你想用歪门邪道绕过检查,比如用反射,private也能被强行打开——后文第 5 章会专门聊这个。

你可以把权限修饰符类比成公司的门禁卡。public是访客都可以进的公共大厅,private是只有本人刷卡才能进的工位区。但门禁卡只决定“你进不进得来”,不决定“你进来之后能不能偷看别人屏幕”。换句话说,修饰符管的是“可见边界”,不是“数据加密”。

过了编译这一关之后,类加载阶段 JVM 的校验器(verifier)还会做一次字节码层面的访问检查,防止有人手工修改 class 文件的访问标记。这个是第二道防线,但一般开发者感知不到。绝大多数情况下,权限修饰符的拦阻都发生在编译期,这就是为什么 IDE 里写错修饰符时,不是运行报错,而是红波浪线立刻标出来。

2. 封装为什么不是限制:public/private 背后是一份 API 契约

很多刚接触 Java 的人会有一个疑问:既然public给得最多,那是不是把所有东西都设成public最方便,反正都能访问。确实,从“能不能跑”的角度来说,全 public 的程序完全能跑,甚至跑得很欢。但从工程维护的角度来说,这是灾难的开始。权限修饰符的本质不是“限制你”,而是“告诉别人哪些是承诺,哪些是细节”。

2.1 从“全局变量随手可见”到“最小暴露原则”

假设你写了一个缓存工具类,内部用一个HashMap存数据。如果字段直接 public,外部代码就能执行cache.map.clear(),把你的缓存清空,甚至往里塞一些你意料之外的数据。这时候你的类就完全失控了。但如果你把字段设为private,只对外暴露两个方法get(String key)put(String key, Object value),那外部代码能做的就只有你允许做的事。

这就是最小暴露原则:对外暴露的 API 越少,后续改动时你的自由空间越大。你可以把HashMap换成ConcurrentHashMap,可以加过期策略,可以加缓存统计,只要对外方法签名不变,调用方完全无感知。但如果字段是 public 的,换数据结构这种本来很普通的内部优化,就会因为外部代码直接引用了字段而变成破坏性变更。

这个道理听起来很朴素,但实际项目里大量代码坏就坏在“图省事”三个字上。很多新人对 public 的理解是“方便”,对 private 的理解是“麻烦”,这个认知反了。public 是“一旦发布就极难撤回的承诺”,private 才是“留给自己的自由”。

2.2 现实中如何划分 public 接口和 private 实现细节

那实际操作中怎么划分?我的经验是遵循几个粗糙但实用的原则。

  • 字段一律 private。哪怕你想对外暴露,也通过 getter/setter 转发,因为 getter/setter 是方法,方法可以在将来加逻辑,字段不行。
  • 对外提供的能力用 public 方法表达。比如工具类的静态方法、业务类的服务方法。
  • 类内部协作的辅助方法用 private。如果一个 private 方法大到你想测试它,那说明该拆了,把它拆成包内可见的另一个类的方法,用默认修饰符,这样同包下的测试类能直接测。
  • 默认修饰符适合包内协作。同一个包里的类,比如一个主类带几个辅助类,它们之间可以直接访问彼此的包私有成员,而不必暴露到包外。
  • protected 留给“子类要重写”的场景。模板方法模式就是典型,父类定义好骨架流程,其中某一步用 protected 方法暴露给子类去实现。

这里要特别强调 getter/setter 不是银弹。很多人写一个类,所有字段都自动生成 getter/setter,看起来是封装了,其实只是把 public 字段换成了 public 方法,思想没变。真正的封装是:这个类暴露的方法集合,要能表达这个类的能力,而不是表达这个类的字段明细。一个User类如果有getName()setName()getAge()setAge()getAddress()setAddress()……这本质上还是一堆数据袋子,谈不上封装。

3. protected 的隐藏陷阱:跨包子类为何连“自己人”都访问不了

如果面试官想用权限修饰符挖坑,protected 绝对是最佳选择。因为四张表里只有 protected 的“异包子类”这一格带了个“(特殊规则)”,而这个特殊规则恰恰是大多数人凭直觉猜错的。

3.1 跨包子类访问 protected 的两个条件

先看 Java 语言规范里对 protected 成员跨包访问的定义。一个类 C 的 protected 成员,在另一个包的子类 S 中是可以被访问的,但有限制:访问必须通过 S 类型的对象或 S 的子类类型的对象来发起。换句话说,S 的代码里能访问的,不是 C 的所有 protected 成员,而是“通过 S 或 S 的子类引用”访问的那些。

这句话绕,直接看代码。

package com.example.a; public class Parent { protected void doSomething() { System.out.println("parent do something"); } }

再看子类,它在另一个包里。

package com.example.b; import com.example.a.Parent; public class Child extends Parent { public void testOk() { // 情况1:通过 this 访问,this 是 Child 实例,可以 this.doSomething(); // 情况2:直接调用,隐含 this,可以 doSomething(); // 情况3:通过 new Child() 访问,引用类型是 Child(S),可以 new Child().doSomething(); } }

这三行都是合法的,因为它们都符合“通过 S 类型的引用”这个条件。但下面这些写法,编译直接报错。

package com.example.b; import com.example.a.Parent; public class Child extends Parent { public void testNotOk() { // 情况4:通过父类引用访问,引用类型是 Parent(C),不行 Parent p = new Parent(); p.doSomething(); // 编译错误 } public void testAlsoNotOk(Child other) { // 情况5:另一个子类对象?如果这个类是 Child,这里的 other 也是 Child // 等等,这个其实是可以的,因为 other 类型是 Child(也就是 S 自身) other.doSomething(); // 可以 } }

情况4 是最大的坑。很多人的直觉是:parent 是子类的父类,我在子类里访问父类对象上的 protected 方法,应该没问题吧?结果编译器告诉你不行。原因就是刚才那句规范:跨包子类访问 protected 成员,引用类型必须是子类自身或其子类,不能是父类类型。

更隐蔽的是情况5 的变体。如果你还有一个类叫Sibling,它和Child一样继承自Parent,那么Child类内部访问Sibling实例上的 protected 成员,也是不行的,因为Sibling既不是Child也不是Child的子类,虽然它们是“同一个爹”。

package com.example.b; public class Sibling extends Parent { // 什么都不做 } public class Child extends Parent { public void testSibling() { Sibling s = new Sibling(); s.doSomething(); // 编译错误,Sibling 不是 Child 或 Child 的子类 } }

这个规则抽象一下就是:跨包 protected 访问,保护的其实是“子类给自己用”,而不是“所有兄弟互相用”。同厂不同部门的兄弟团队,没经过管事部门授权,是不能随便动对方手上那批资源的。只有在同一个包内,大家才能相互访问 protected 成员——这时候它退化成包私有级别的访问。

3.2 实测:子类中 new 父类对象会报什么错

我把上面情况4 的代码编了一遍,javac的报错信息是:

错误: doSomething() 在 Parent 中是 protected 访问受保护

注意这个报错很有迷惑性。它只说“protected 访问受保护”,并没有告诉你“应该用什么类型的引用”。如果你没理解引用类型这一层规则,很容易一头雾水,觉得“我明明在子类里啊,为什么不能访问父类的 protected 方法”。网上很多 Java 面试八股文爱问这个,其实就是考这条规范。

那正确的写法是什么?如果你确实想通过一个引用调用,那就把引用声明成子类类型。或者,直接在子类里写一个转发方法:

public void exposeDoSomething() { doSomething(); // this 调用,完全没问题 }

很多框架就是这么干的。父类定义 protected 的生命周期钩子,子类在内部方法里调用,永远用this隐式引用。

3.3 记忆口诀和判断步骤

为了避免每次都被绕晕,我自己总结了一套三步判断法,遇到 protected 跨包访问的问题,按顺序套就行。

  1. 当前代码所在的类 S,是不是父类 C 的子类?不是,直接不能访问。
  2. 被访问的引用是什么类型 T?T 必须是 S 或 S 的子类,否则不能访问。
  3. 如果引用是 S 的类型,再看这个引用指向的对象是否真的发生了继承关系——其实到第 2 步就已经能判断了。

口诀就是:在子类里,用自己能看见的类型去访问。不要在子类里拿父类类型去 new 对象再访问人家的 protected 成员,那不是“子类访问父类”,那是“外部人访问另一个类的受保护资源”。

4. 方法重写的可见性约束:只许放大,不许缩小

权限修饰符不只是影响“能不能访问”,还影响“能不能重写”。这个点很容易在面试里跟多态结合着考。

4.1 为什么子类不能降低可见性

Java 的规则是:子类重写父类方法时,访问修饰符的访问级别不能比父类方法更低。也就是说,父类是 public,子类重写时必须是 public;父类是 protected,子类重写成 protected 或 public 都行,但不能降成默认或 private。

这条规则底层原因就是多态。看个场景:

package com.example.a; public class Base { public void handle() { System.out.println("base handle"); } }

假设子类能把方法降成 private:

package com.example.b; public class Sub extends Base { private void handle() { // 假设编译器允许 System.out.println("sub handle"); } }

那么外部代码持有的是Base类型的引用,调用handle()时,方法的可见性是由静态类型Base决定的——Base.handle 是 public,外部可以调;但实际执行的是 Sub 重写后的逻辑。这样一来,外部代码就能通过父类引用调用一个在子类里被标记为 private 的方法,私有性就名存实亡了,而且编译器已经放行了base.handle()的调用,运行期才去子类里找方法,逻辑上自相矛盾。

Java 干脆把这个口子堵死:你重写可以,但不能把可见性调小。所以编译器会在重写方法上强制检查修饰符。如果你试图把 public 方法重写成 private,javac会报:

错误: Sub 中的 handle() 无法覆盖 Base 中的 handle() 被覆盖的方法为 public

报错里最扎眼的就是“被覆盖的方法为 public”这句话,翻译过来就是:父类是 public,你子类不能比我更小气。

4.2 接口实现中的可见性:默认必须是 public

接口的方法还有一个特殊之处:Java 8 之前接口方法全是 public abstract,Java 8 之后加了 default 方法和 static 方法,但默认方法的访问级别依然是 public。实现类在重写接口方法时,也只能用 public,不能降级。

这一点经常被忽略的地方在“包私有实现类”的场景。假设接口是 public 的,实现类没有加修饰符(包私有),那这个实现类对外部的可见性就由类修饰符决定——包外的代码连这个类都看不见,更别说调用它实现的方法了。但方法本身在类内部的重写级别仍然是 public,修饰符约束的是“方法能不能被调用”,类层面的可见性是另一码事。

4.3 重写时最容易踩的修饰符配合坑

除了可见性不能降级,还有一个容易忽略的点:父类方法是static的,子类如果想“重写”,其实只能隐藏(hide),而且修饰符的组合没那么灵活。static方法不参与多态,子类定义一个同签名 static 方法,访问级别可以随意,因为它们是两个方法,只是名字相同。但这不在权限修饰符的管辖范围里,不在本文展开。

真正和权限修饰符相关的重写场景,是父类 protected 方法被子类重写成 public 的用法。这是模板方法模式的标配:父类的execute()模板方法里调用一个 protected 的doExecute()钩子,子类将其重写为 public 后,外部可以直接调子类的钩子,也可以让父类模板去调用。这里“重写为 public”其实是契约放宽,外部才获得了直接调用的能力,如果子类不重写,外部依然只能执行父类的模板流程,无法直接碰 protected 钩子。这一层设计,很多框架里用得非常多,理解了之后读源码会顺很多。

5. 进阶场景里的权限设计:内部类、构造器、反射对抗

到了这一层,权限修饰符就不再只是“四个关键字选哪个”的问题了,而是“怎么利用可见性设计出更安全、更清晰的代码结构”。这才是进阶笔记该有的内容。

5.1 内部类的 private 修饰:把实现细节藏得再深一层

内部类本身也可以有修饰符,而且这个修饰符的语义和普通类不一样。普通顶层类只能是 public 或默认,但内部类可以是 private、protected。private内部类意味着这个类只能在外部类内部被实例化,外面完全不知道它的存在。

一个常用场景是:外部类的方法返回一个集合,但这个集合的真实实现是一个 private 内部类。调用方拿到的是接口类型,根本不用关心底层是 ArrayList 还是别的什么。JDK 源码里到处是这种设计,ArrayList的迭代器就藏在 private 内部类里,HashMap的 EntrySet、KeySet 也是。调用方只看到IteratorSet这种接口,具体实现被修饰符牢牢锁在内部,将来想换实现,外部零感知。

这个技巧在业务代码里也适用。我写过不少“返回一个只读视图”的小工具,就是用一个 private 内部类实现接口,内部类里把原始数据包一层,所有修改方法直接抛 UnsupportedOperationException,从根上杜绝调用方误改数据。

5.2 私有构造器:单例、工具类与静态工厂的修饰符博弈

构造器也是成员,所以也能用权限修饰符控制。最经典的用法是工具类:一个类全是静态方法,不需要实例化,那就把构造器设为 private,防止别人 new 出无意义的对象。Java 的ArraysCollections这些工具类都是这么干的。

private 构造器也是单例模式的基础。无论是饿汉式还是懒汉式,核心思路都是:构造器 private,外部不能 new,唯一获取入口是静态工厂方法。这个设计本质上就是在用权限修饰符建立一道“只能通过我给你的入口拿实例”的墙。

静态工厂方法也是这一层思想的延伸。私有构造器 + 静态工厂方法,类内部可以控制实例创建的数量、时机、缓存策略,这正是 public 构造器做不到的。面试里如果问“为什么推荐静态工厂方法而不是构造器”,底层原因有一部分就是权限修饰符带来的控制力。

5.3 反射与 setAccessible:权限修饰符的边界与反制

如果读者之前看过动态代理或者 ORM 框架的源码,一定见过setAccessible(true)这个调用。它的作用就是无视权限修饰符,强行打开 private 成员。

严格来说,setAccessible(true)能让反射代码访问原本不可见的字段和方法。Spring 的依赖注入、MyBatis 的结果映射、Jackson 的反序列化,全靠这一手才能在 private 字段上为所欲为。这也是为什么我说“权限修饰符是编译期契约,不是运行时加密”。它约束的是“正常路径”下的代码访问,反射是另一条路,能绕过,但需要调用者显式授权自己。

不过,从 Java 9 引入模块系统之后,想要反射打开别的模块里的 private 成员,还必须加--add-opens参数,否则 JVM 会拒绝。这说明 JVM 在模块层面对反射做了收紧,但类路径下的代码,反射依然能突破 private。

所以真正严肃的结论是:不要用权限修饰符来保护机密数据。它是设计约束,不是安全边界。你可以在字段上写 private,但如果有强需求,反射照样能拿到。权限修饰符挡的是无知和误用,不是恶意攻击。

6. 从字节码看权限修饰符:javap 反编译下的修饰符标志

进阶到一定阶段,就不能只看源码层了。权限修饰符最终会被编译到 class 文件的 access_flags 字段里,用 javap 反编译一眼就能看出来。

6.1 javap 查看 access_flags

随手写一个类,然后用命令行反编译它的字节码。

javac TestModifier.java javap -v TestModifier.class

输出里有一段这样的内容:

public class com.example.test.TestModifier minor version: 0 major version: 52 flags: (0x0021) ACC_PUBLIC, ACC_SUPER

注意flags这一行。0x0021 是十六进制,拆开看:0x0020 是 ACC_SUPER(表示这个类要支持新版的 invokespecial 语义),0x0001 是 ACC_PUBLIC,加起来就是 0x0021。

字段和方法的访问级别也有对应的标志位。比如一个private static final字段,flags 会是:

flags: (0x001A) ACC_PRIVATE, ACC_STATIC, ACC_FINAL

各标志位对应的值在 JVM 规范里有表,常见几个:ACC_PUBLIC 0x0001、ACC_PRIVATE 0x0002、ACC_PROTECTED 0x0004、ACC_STATIC 0x0008、ACC_FINAL 0x0010、ACC_SYNCHRONIZED 0x0020、ACC_VOLATILE 0x0040、ACC_TRANSIENT 0x0080。

为什么要提字节码?因为权限修饰符的“检查”在 javac 阶段确实做了,但 class 文件本身存下来的只是这些标志位。JVM 在类加载时,会再次校验这些标志位与访问行为是否匹配。比如一个方法在字节码层面拿不到访问权限,JVM 的访问控制检查会抛IllegalAccessError。虽然这套机制很少在正常编码中被触发,但理解了它,你就知道“编译能过,运行不一定能过”的说法是怎么来的——编译器和 JVM 各检查一遍,双保险。

6.2 命名规范与修饰符的选择策略:我推荐的默认组合

代码规范里对修饰符的顺序是有约定的,虽然编译器不强制,但读代码的人会有预期。按照 Java 官方推荐的修饰符顺序:

  1. public / protected / private
  2. abstract
  3. static
  4. final
  5. transient
  6. volatile
  7. synchronized
  8. native
  9. strictfp

比如public static final是合法的,static public final也能编译,但读起来就很别扭。多数规范检查工具(Checkstyle、PMD)都配置了修饰符顺序检查,老实按顺序写就好。

真正值得说一句的是我在实际编码里默认的修饰符组合。在没有特殊理由的情况下,我用这几个默认值:

  • 字段:一律private,顶多加final
  • 对外方法:public
  • 内部辅助方法:private
  • 模板方法中需要子类定制的点:protected
  • 包内协作的辅助类:默认修饰符。
  • 常量:public static final,但尽量避免 public 常量泛滥,因为 public 常量也是一种 API 承诺,一旦别人用上了,你改值就是破坏性变更。

这套组合敲了很多年,没踩过大坑。很多新人会问“字段为什么不能 protected 或默认”,我的回答是:能,但没必要。字段是状态,状态越容易被外部触碰,类的不变量越难维护。protected 字段尤其危险,子类和同包都能直接改,一旦字段状态失控,排查成本极高。如果有子类需要访问父类的某个内部状态,最好的做法是提供一个 protected 的 getter 或 setter,而不是直接把字段放出去。

6.3 与模块系统的一些联想:权限的粒度正在变细

Java 9 的模块系统引入了一个新的访问控制维度:模块导出(exports)和开放(opens)。以前权限修饰符控制的是“类/成员级别”的可见性,模块系统在之上又加了一层“模块级别”的可见性。一个类即使是 public,如果它所在的包没有被模块导出,那模块外的代码依然访问不到。

这其实是对权限控制粒度的一次升级。从类的 public/private,到包的导出/不导出,再到模块的开放/不开放,控制粒度越来越细。理解了权限修饰符,再去学模块系统,会有一种“同一套思维在不同层级复用”的感觉:都是划定边界,只不过从方法级别扩到了模块级别。

最后分享几个我在实际项目里的使用习惯

权限修饰符这个知识点,平时写代码不一定能感受到它的存在,但一旦代码规模上来,团队的协作人数变多,它的价值就会被无限放大。我这几年的体会是,真正拉开代码质量差距的,往往不是设计模式用得有多花哨,而是这些最基本的可见性边界划得有多清晰。

一个很实用的习惯是:写一个新类的时候,先默认把所有成员都设成 private,然后逐个问自己“这个真的需要对外暴露吗”。需要的时候再放宽成 public,或者 protected。从紧到松的路径比从松到紧安全得多,因为把 private 改成 public 是零成本的,但把 public 改成 private 可能就要牵连所有调用方了。

还有一个排错经验:遇到“明明在同一个包里,为什么访问不了另一个类的默认修饰符成员”这类问题,先检查是不是放在不同包、不同模块里了。我在用 IDE 自动移动类的时候踩过这种坑,看起来代码在同一个目录,实际上包名被自动调整过,默认真空修饰符立刻失效,排查半天才找到原因。权限修饰符的生效前提是包结构稳定,包路径一动,默认修饰符的“同包可见”就变成了“不同包不可见”,这类问题很隐蔽,要注意。

最后一条:别迷信“把字段全部 private 就是封装”。封装的本质是行为建模,不是字段隐藏。private 只是实现隐蔽的手段,真正的目的是让外部代码依赖稳定的接口,而不是依赖易变的内部结构。什么时候你写类的时候边界想清楚了,什么时候你的 Java 就算真正进阶了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 19:40:37

S7-200 PLC与组态王在锅炉温度控制中的应用

1. 工业锅炉温度控制的智能化升级在中小型锅炉房的控制系统中,温度参数的精准采集与稳定控制直接关系到能源利用效率和设备安全。传统的人工监控方式存在响应滞后、精度不足等问题,而采用S7-200 PLC配合组态王的解决方案,相当于为锅炉装上了&…

作者头像 李华
网站建设 2026/9/10 19:40:12

机器学习与人工智能:核心概念与实战入门指南

1. 机器学习与人工智能:从概念到实战的全面解析 在咖啡馆里,我经常遇到两种极端的朋友:一种认为AI就是科幻电影里的机器人,另一种则把机器学习等同于万能魔法。实际上,这两个概念既没有那么遥远神秘,也不是…

作者头像 李华
网站建设 2026/9/10 19:39:35

CAD图纸转SVG并集成TinyMCE:解决在线文档矢量图丢失问题

在芯片厂做文档系统集成,最常碰到的一个怪问题就是:工程师辛辛苦苦在AutoCAD里画好的版图、封装示意图、工艺流程图,复制粘贴到基于TinyMCE的工艺管理系统后,保存出来变成一张模糊的图片。缩放一下,字体边缘全是锯齿&a…

作者头像 李华
网站建设 2026/9/10 19:39:30

CANN/GE图引擎API:创建UInt32向量常量

EsCreateVectorUInt32 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、Tens…

作者头像 李华
网站建设 2026/9/10 19:38:26

2026必备!AI论文写作工具测评:最新推荐与深度对比

2026年真正好用的AI论文写作工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一…

作者头像 李华