
java effective
文章平均质量分 63
java编写的效率和规范
fastjson_
这个作者很懒,什么都没留下…
展开
-
并发——线程池优于new Thread
每次new Thread的时候,都会创建一个单线程,但是,我们在项目中基本都是使用线程池,本文主要整理了线程和线程池的优缺点。原创 2023-06-12 14:36:26 · 1492 阅读 · 0 评论 -
并发——同步访问共享的可变数据
按照这种观点,对象被创建的时候处于一致的状态,当有方法访问它的时候,它就被锁定了。虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是它并不保证一个线程写入的值对于另一个线程将是可见的。同步不仅可以阻止一个线程看到对象处于不一致的状态之中,它还可以保证进入同步方法或代码块的每个线程,都看到由一个同步锁保护的之前所有的修改效果。因为 voletile 只可见,不提供互斥,而 count++ 实际上是3个动作,先读取count 值,然后+1 ,最后写回到count 值。以下代码可以做个试验。原创 2023-06-10 16:44:48 · 823 阅读 · 0 评论 -
通用程序设计——将局部变量的作用域最小化
如果不细心的话,好像真的很难发现“复制-粘贴”引发的这个问题:第二个 while 循环的条件中使用了之前的变量 iterator,而不是它应该使用的 iterator1(粘贴后遗漏了变量的修改)。第二个 for 循环使用了和第一个 for 循环一模一样的代码,连 iterator 这个变量也不需要修改了。(容许我尴尬一下,在写这篇文章之前,我一直用的上面这种 for 循环格式。但这种写法仍有改进的地方,因为从字节码的角度来看,每次循环都要调用一次。否则的话,变量的作用域要么开始的太早,要么结束的太晚。原创 2023-06-09 14:27:44 · 567 阅读 · 0 评论 -
通用程序设计——当心字符串连接的性能
为连接 n 个字符串而重复使用+操作符,需要n的平方级的时间。这是由于字符串是不可变的,每次操作都是拷贝他们的内容,合并到一个新的 String 实例中。最后,+自动优化是jdk 提供优化的,但并不是标准,所以不存在一定会优化,也不一定会优化到什么程度,所以,把自己代·码写好才是最根本的。+ 除了表示基本数据的加法计算之外,也可用作字符串连接操作符,任何类型都可以和字符串使用+来合并为一个新的字符串。但自动优化能做的毕竟是有限的,有些场景是无法被自动优化的,这时候需要手动来处理,避免频繁使用+操作。原创 2023-06-04 18:53:46 · 158 阅读 · 0 评论 -
通用程序设计——如果需要精确,避免使用float和double
BigDecimal , 但要注意一点,BigDecimal 初始化小数的时候要使用字符串来初始化,如果用 double 初始化,比如初始化上面的0.1 的时候,在初始化阶段就已经丢失精度了,后续 BigDecimal 已经无能为力了。或者说如果你的系统中对钱有明确下限,就用它做单位来存储计算,当然, BigDecimal 也有优点,可以自由控制舍入问题,它允许从多种舍入方案中选择,比如常见的四舍五入,银行家舍入法,五舍六入 远零舍入,近零舍入 等,可以根据需要完成舍入,具体各种舍入可以参考文档。原创 2023-06-04 17:11:42 · 738 阅读 · 0 评论 -
通用程序设计——基本类型优先于包装类
java 默认对 Integer 的 -128~127做了缓存,所以两个1用的是同一个实例,而221则使用了不同的 Integer 实例,而且,即便是1,也可以构建出不同来。另外,如果不注意自动装箱拆箱问题,也可能造成性能下降,虽然拆箱装箱性能差异在系统中不算很重要,但这是无畏的浪费,应该避免。但实际上,两者是有区别的,如果不注意,可能造成性能损失,甚至造成异常错误。所以,两者的使用是有区别的,很多时候,基本类型会更合适,它们性能更好,且不会出现空指针。上面的代码看起来没问题,但实际上问题很大。原创 2023-06-04 16:55:04 · 613 阅读 · 0 评论 -
方法——返回0长度的数组或集合,而不是 null
在非常多的返回集合的场景中,null 与 0长度集合都表示没有符合条件的结果,也就是多数时候,他们是等价的,而本条目讨论的就是这种情况。当然最不合适的就是有些情况返回 null,有时候又返回空集合,这种情况使用者总要做两种判断来处理,并且可能造成意义上的困扰。在方法的返回值中, null 和 空集合/数组 可能有不同的意义或相同的意义,本条目针对的是相同意义下应该返回什么。null 有微弱的性能优势,即便是这样的优势,空集合/数组也能通过优化来无限接近 null 的性能。很多方法需要返回一个数组或集合。原创 2023-06-03 14:48:05 · 547 阅读 · 0 评论 -
方法——慎用可变参数
比如: jdbc 中 preparedStatement 中?占位符是不固定的(确实是 0 - n 个),那么他的实际替换值也是不固定,所以这里用比较方便,而且 jdbc中处理 占位符的性能在 jdbc与数据库交互间不值一提,所以用可变参数的性能问题不再是问题。可变参数依然是来自于 jdk 1.5 ,可见 jdk1.5 在 java 中有多么重要。所以可变参数方法也可声明如下(其他参数与正常的方法格式一样,只有可变参数放在最后)由上一条可知,一个方法最多允许1个可变参数。原创 2023-06-03 14:26:49 · 215 阅读 · 0 评论 -
方法——慎用重载
/ 方法重载的选择是会选择最精确的匹配,如上面的实例,我们的参数为 Integer,当方法重载的时候,java 会优先使用参数为 integer 的方法,也就是 dosh(Integer i). 当此方法不存在(被删除)时,也会自动选择 dosh(Object o ) ,因此,重载方法可能造成的问题就是在重构的时候,可能导致程序编译不报错,但实际执行的方法已经变了,可能造成程序在运行时才会出错,更大的麻烦可能是程序没有报错,但执行一段时间后,我们发现了数据错误,又很难找到原因。原创 2023-05-28 15:48:53 · 789 阅读 · 0 评论 -
方法——必要时进行保护性拷贝
后者的情况比较罕见,毕竟对于恶意破坏的程序,我们其实更合适的是根本不给对方访问权限,比如将 Period 定义为某个类的内部类,所有与外部的交互都严格校验,防止反射等修改手段,并且不会返回任何内部的引用,只返回数据的复制对象或复制的基本数据。程序的问题可能来自故意的破坏,也可能来自无意的破坏。本篇的真正启示在于,如果可能,尽量使用基本类型或者final类型,这样就不存在引用问题了,比如上面方法可以改为。保护性拷贝有两种级别,一种是避免无意的破坏,另一种是尽可能避免任何有意的破坏。本篇的重点在于第一种。原创 2023-05-28 15:07:18 · 149 阅读 · 0 评论 -
方法——检查参数的有效性
当编写方法或者构造方法的时候,应该考虑它的参数有哪些限制,应该把这些限制写到文档中,并且在这个方法的开头处,通过显示的检查来实施这些限制.养成这个习惯是非常必要的.原创 2023-05-27 11:54:37 · 1178 阅读 · 0 评论 -
枚举——用接口模拟可伸缩的枚举
在 java 中,普通类默认继承了 Object 类, 如果显示声明 extends ,则变为继承指定的类,但无论如何,所有类的最终继承都指向了 Object 类,因此,一切实例都可以被 Object 接受。枚举的这种限制让它可以被 jvm 优化,提供了优异的性能,自 jdk 1.5 之后,单例的最佳实现就是枚举,jvm 帮我们优化处理好了一切,包括反射,克隆,序列化等都无法破坏枚举的单例。幸好,java 也考虑到了这一点,让枚举可以实现接口,因此,我们可以通过接口来模拟枚举的扩展。原创 2023-05-27 11:20:54 · 181 阅读 · 0 评论 -
枚举——用EnumMap代替序号索引
本条目与 EnumSet 类似,强调的是 int 类型数据在 java 中本身其实没有特殊意义,对于数组来说,下标与内容也没有严格对应关系,因此依赖数组下标也是不太好的行为。在上面的例子中,为了把Plant 按照播种季节区分,使用了泛型数组, Set[] types = new Set[Type.values().length];再操作这个数组的时候,我们必须注意索引范围,避免使用错误的 int index 得到错误的数据,或者报出ArrayIndexOutOfbounds 异常。原创 2023-05-27 10:36:54 · 218 阅读 · 0 评论 -
枚举——用EnumSet代替位域
通过二进制可以发现,他们的值的组成都是单一的, 例如 3 只能 是 1 和 2 组成, 13只能是1和4和8组成,只要能看懂二进制都能明白这个道理,不懂的需要先熟悉一下二进制。如果 我们选取了 0、1、2、3 来标识abcd这四个值,那么不定项选择中,如果答案为bc,那么值是 1+2=3,但问题来了,d选项的值也是3,那么3对应的就是两种可能,bc或者d,因为是不定项选择,我们也没法确定是哪个,所以不能随意标识值,只能选择那些组合出来的值只能被分解成唯一的几个子值,位移就是其中一个完美的选择。原创 2023-05-21 16:55:48 · 497 阅读 · 0 评论 -
枚举——使用实例域代替序号
可是随着后来业务发展,我们定义了一个新的状态,这时候,我们就要注意到一个隐藏的细节,为了不改变 0 代表 FAILED, 1 代表SUCCESS,我们必须把新的状态放在枚举的最后,而不能放在其他位置,并且,不能移除已有的枚举,否则他们的天然序号都变了,业务中某些地方就可能出现问题,而且这种错误非常隐蔽,可能直到很久之后才会爆发出来。序号指的是 枚举自带的 ordinal,类似于数组的下标,用于返回枚举的顺序。比如 TimeUnit。如何避免上面的问题?原创 2023-05-14 12:30:49 · 179 阅读 · 0 评论 -
枚举——用enum代替int常量
这种枚举类型,提供了编译时的类型安全检查,如果声明了一个参数的类型为DAY,就可以保证,被传到该参数上的任何非null的对象引用一定属于其他有效值中的一个,试图传递类型错误的值时,会导致编译时错误,就像试图将某种枚举类型的表达式赋给另一种枚举类型的变量,或者试图利用==操作符比较不同枚举类型的值一样。c、int枚举是编译时常量,被编译到客户端中,如果枚举常量关联的int发生变化,客户端必须重新编译,如果没有重新编译,程序仍可以运行,但行为就不确定了,如DAY_MONDAY关联的常量不再是1,而是0。原创 2023-05-14 10:50:08 · 1229 阅读 · 0 评论 -
泛型——优先考虑类型安全的异构容器
对于 List<T> Set<T> 这一类的问题, 目前还没有合适的完美处理办法, 因为 List 本身是无限制的, 使用者完全可以在 List 中同时保存 Integer String User 等各种类型, 并且即便你的方法参数声明了泛型, 调用者也可以无视泛型, 直接用原生类型 List Set , 所以这一类问题 , 目前只能将方法参数化, 配合合适的注释和对于性能的实际要求考虑是否进行运行时检查。jdk 本身提供的util 为了灵活性, 没有做太大的限制, 这种灵活性也可能导致数据的不安全。原创 2023-05-03 17:34:57 · 509 阅读 · 0 评论 -
泛型——使用有界通配符提高api灵活性
平时我们Map 使用也比较多, 如果写工具类的时候也会面临同样问题 , 使用Map 会导致要求入参的泛型, 如果去掉泛型直接用 Map , 又导致编译警告, 并且,也会导致入参格式可能有问题. 使用通配符就可以解决 Map原创 2023-04-30 16:19:36 · 163 阅读 · 0 评论 -
泛型——尽量消除非受检警告
有些警告源于我们的设计, 无法轻易消除,比如我们原始信息就是多种,这时候 我们知道这个数据就是这个样子, 那我们只能使用强转来处理类型。为了处理这种警告, 我们需要使用注解 @SuppressWarnings("unchecked")正确处理掉这些警告之后, 我们可以保证至少泛型的类型转换不会出错, 如以下的场景就可以避免。这时候获取的时候就会报 unchecked cast (非受检强制转换警告)比如我们设计一个缓存, 为了方便使用, 我们允许存储各种格式。关于 @SuppressWarnings。原创 2023-04-30 15:13:31 · 1624 阅读 · 0 评论 -
泛型——List 优于数组
泛型是编译类型,意味着 List 通过忽略泛型,是可以保存 Long 的。数组是真实类型,代表着无论如何,不能把 String 放到 Long[] 中。2、而List 不是 List的父类型。1、Object[] 是 String[] 的父类型。因此,java 语法不允许创建泛型数组。原创 2023-04-30 10:47:32 · 945 阅读 · 0 评论 -
泛型——请不要在新代码中使用原生态类型
java 从 1.5 版本出现泛型之后, java.util 包中的工具类大多数都已经实现了泛型化,有了泛型之后,java 编译时便可以帮我们检查出很多类型错误,因此,除非是维护 1.5 之前的版本,否则不要用原始的类型,在可能的情况下,尽量使用泛型化的类。在上面的例子中,如果使用错误示范,就意味着你的代码中丢失了泛型,之后所有的 get 方法都默认只能得到 Object,然后自行强转,编译器会给出 warning,但无法帮你处理为真实类型。原创 2023-04-22 19:08:28 · 126 阅读 · 0 评论 -
类和接口——优先考虑静态成员类
匿名类的扩展成员变量无法被外部访问,只能访问那些父类中的变量和方法 ( 因为匿名类是被他的父类所引用的, 对于程序而言,只能拿到接口或者父类引用, 也就只能访问父类或接口的变量与方法)不仅如此, 内部类一旦实例化, 他就与外部类的实例已经绑定, 这种关系无法修改,无法解绑. 进而也会影响 gc (外部实例和内部实例只要有一个无法被回收, 那就都无法被回收)一般来说 内部类的最常用的场景是作为外部类的辅助类, 也就是他自身单独是没有什么作用的, 他是辅佐外部类来实现功能的。匿名类无法扩展实现接口。原创 2023-04-05 16:27:21 · 366 阅读 · 0 评论 -
类和接口——复合优先于继承
理由如下:当我们要扩展一个类时,特别是一个别人写好的类,一个类库的类,我们往往关心的仅仅是单个api的功能,而不关心他的实现,但是存在的一个问题就是,同一个类的各个方法直接可能存在联系,可能一个方法的实现依赖于另一个方法,这就意味着,当我们调用一个我们想要操作的方法时,“继承”会隐式的调用另一个方法,这就可能存在问题。因为,在hashSet的addAll()实现中,是循环调用add()方法的,所以导致3*2。需求:新建一个集合类,维护一个addCount变量,记录,一共添加了多少次新值。原创 2023-04-02 14:22:36 · 219 阅读 · 0 评论 -
类和接口——Java中的不可变对象
一个类的对象在通过构造方法创建后如果状态不会再被改变,那么它就是一个不可变类。它的所有成员变量的赋值仅在构造方法中完成,不会提供任何 setter 方法供外部类去修改。自从有了多线程,生产力就被无限地放大了,所有的程序员都爱它,因为强大的硬件能力被充分地利用了。但与此同时,所有的程序员都对它心生忌惮,因为一不小心,多线程就会把对象的状态变得混乱不堪。为了保护状态的原子性、可见性、有序性,我们程序员可以说是竭尽所能。其中,synchronized(同步)关键字是最简单最入门的一种解决方案。原创 2023-03-19 12:45:28 · 584 阅读 · 0 评论 -
类和接口——使用getter setter代替public属性
如果一个类需要对外提供某些属性,不要直接使用publc 属性字段,而是属性私有化, 对外提供 getter setter,因为public属性直接操作时无法进行安全检查,而getter setter 方法可以灵活地进行重构升级, 也可以在操作前进行安全检查。正例(getter setter 中可以进行额外的操作-- 虽然大多数时候可能不会这样, 但这样做可以避免未来升级时可能的问题)反例 (外部成员直接操作 name 可能导致 null)原创 2023-03-19 11:04:08 · 209 阅读 · 0 评论 -
类和接口——访问修饰权限最小化
ExcelWriter , ExcelReader , PoiUtil类 , 其中 PoiUtil 中很多都是无修饰符(包私有级别), 因为它只需要被其他两个类调用, 而Reader 和 Writer 中也有很多私有的, 因为是内部数据操作, 如果暴露出去, 则可能导致数据错误。这样对于使用者只需要查看能访问到的信息, 不需要关注其他的内部细节同时也避免了外部类的误操作导致的问题 而且被隐藏的类和方法可以放心的进行重构去优化, 不必保证私有方法的兼容性。包级 (本包内可以访问)原创 2023-03-19 10:37:40 · 161 阅读 · 0 评论 -
通用方法——考虑实现Comparable
如果自实现的类涉及到内在排序,譬如按字母排序或数字排序或日期排序的,强烈建议实现Comparable接口。实现了Comparable接口的类,可以和许多的泛型算法以及集合进行协作。譬如,列表或数组中的元素实现了Comparable接口,则排序时可以直接调用Collections.sort(list)或Arrays.sort(array)得到结果。原创 2023-03-11 17:10:52 · 349 阅读 · 0 评论 -
通用方法——建议始终覆盖toString
222原创 2023-03-05 12:33:43 · 262 阅读 · 0 评论 -
通用方法——为什么重写equals还要重写hashCode
111原创 2023-03-04 16:56:31 · 708 阅读 · 0 评论 -
创建和销毁对象——消除过期的对象引用
33原创 2023-02-25 12:42:25 · 240 阅读 · 0 评论 -
创建和销毁对象——遇到多个构造器参数时要考虑使用构建器
2原创 2023-02-19 11:10:32 · 456 阅读 · 0 评论 -
创建和销毁对象——用静态工厂方法代替构造器
1原创 2023-02-18 19:28:20 · 333 阅读 · 0 评论 -
序列化——考虑用序列化代理代替序列化实例
333原创 2023-01-15 16:44:32 · 520 阅读 · 0 评论 -
序列化——对于实例控制,枚举类型优先于 readResolve
22原创 2023-01-15 13:10:40 · 366 阅读 · 0 评论 -
序列化——保护性地编写readObject方法
22原创 2023-01-14 21:11:12 · 569 阅读 · 0 评论 -
序列化——考虑使用自定义的序列化形式
222原创 2023-01-14 12:04:33 · 418 阅读 · 0 评论 -
序列化——谨慎实现Serializable
2原创 2023-01-08 16:32:50 · 747 阅读 · 0 评论 -
Effective Java —— 异常篇
333原创 2022-12-19 10:55:33 · 355 阅读 · 0 评论 -
JAVA的可变类与不可变类
1. 可变类和不可变类(Mutable and Immutable Objects)的初步定义:可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。举个例子:String和StringBuilder,String是immutable的,每次对于String对象的修改都将产生一个新的String对象,而原来的对象保持不变,而StringBuilder是mutabl.原创 2022-03-13 10:22:34 · 1155 阅读 · 0 评论 -
创建和销毁对象——避免创建不必要的对象
① 不可变类,采用静态工厂方法对于不可变类,若同时提供了静态工厂方法和构造器的不可变类,通常可以使用静态工厂而不是构造器,以避免创建不必要的对象。例如:静态工厂方法Boolean.valueOf(String)几乎总是优先于构造器Boolean(String)。②初始化后还会改变的对象,如何重用?考虑适配器(adapter)的情形,有时也叫做视图(view)。适配器是指这样一个对象:它把功能委托给一个后备对象(backing object),从而为后备对象提供一个可以代替的接口*。由于适配器除了原创 2022-02-27 12:43:24 · 295 阅读 · 0 评论