- 博客(42)
- 收藏
- 关注
原创 第三十四节:使用enum代替int常量
从这章开始一直到这本书结束所学的东西不仅易理解而且还比较干货,在实际项目中处处会用到。Java支持两种特殊用途的引用类型:一种是称为枚举类型的类,一种是称为注解类型的接口(很多注解类的源码里有,比如@Transactional中@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented)。
2024-08-14 22:11:43
926
原创 第三十三条:考虑类型安全的异构容器
每日一问:什么是异构容器???Java中的异构容器是一种可以存储多种不同类型的数据结构,通常采用泛型和类型安全的机制实现。在计算机科学中,容器是用于存储、操作和管理数据的数据结构。Java提供了丰富的容器类型,如Set、List、Map等,这些容器通过泛型来实现类型安全,确保每个容器只能存储特定类型的对象。然而,有时需要更多灵活性,允许在同一个容器中存储不同类型的对象,这就需要异构容器。异构容器在Java中是通过利用Java的泛型特性与反射机制来实现的,能够提供类型安全的同时,还具备高度的灵活性。泛型最常用
2024-08-11 21:21:29
836
原创 第三十二条:谨慎混用泛型和可变参数
这段代码分配了一个 Object[] 类型的数组,它是保证保存这些实例的最具体的类型,而不管在调用位置传递给 pickTwo 的对象是什么类型。如果选择使用泛型(或参数化)可变参数编写方法,请首先确保该方法是类型安全的,然后使用 @SafeVarargs 注解对其进行标注,以免造成使用不愉快。,除了两个例外:将数组传递给另一个可变参数方法是安全的,这个方法是用 @SafeVarargs 正确标注的, 将数组传递给一个非可变参数的方法是安全的,该方法仅计算数组内容的一些方法。可悲的是,他们并没有。
2024-08-07 21:26:42
817
原创 第三十一条:使用有限制的通配符增加API的灵活性
幸运的是,有一种解决办法。(使用关键字 ex ten也有些误导 :回忆一下第29 条中的说法,确定了子类型( subtype )后,每个类型便都是自身的子类型,即使它没有将自身扩展。popAll 的输入参数类型不应该为“ E 的集合”,而应该为“ E 的某种超类的集合”(这里的超类是确定的,因此 E 是它自身的一个超类型)。换句话说,对于任何两个截然不同的类型 Typel 和 Type2 而言, List
2024-08-07 20:34:54
883
原创 第三十条:首选泛型方法
但是恒等函数很特殊:它返回未被修改的参数,因此我们知道无论T的值是什么,用它作为UnaryFunction都是类型安全的。要完成这其中的任何一项工作,要求列表中的每个元素要都能与列表中的每个其他元素相比较,换句话说,列表的元素可以互相比较(mutually comparable)。类型参数T定义的类型,可以与实现Comparable的类型的元素进行比较。上面的union方法即为一般的泛型方法,但是它有一个限制,要求三个集合的类型(两个输入参数及一个返回值)必须全部相同。-Set是一种原始类型。
2024-08-04 21:34:49
829
原创 第二十九条:首选泛型类型
但是,它确实会造成堆污染(heap pollution)( 32 ):数组的运行时类型与编译时类型不匹配(除非 E 碰巧是 Object)。这使得一些程序员非常不安,他们选择了第二种技术,尽管在这种情况下堆的污染是无害的。第一个更可读:数组被声明为 E[] 类型,清楚地表明它只包含 E 实例。它也更简洁:在一个典型的泛型类中,你从代码中的许多点读取数组;一般来说,在自己的声明中进行参数化,以及使用JDK提供的泛型类型和方法,都不是特别困难。难得是自己写的泛型类型,但这是值得花时间学习的。
2024-07-30 21:07:55
283
原创 第二十八条:列表优于数组
协变的意思就是,对于数组,如果 Sub 为 Super 的子类型,那么数组类型 Su[]就是Super[]的子类型。而泛型,List<Sub>不是List<Super>的子类,List<Super>也不是List<Sub>的超类。下面两种代码,数组实现的是合法的,能通过编译器的检验,但是在运行时环境下会报错。而List实现的则不能,即在编译时就回报错。我们更希望不合理的代码在编译时就报错,在运行时报错会增大排查成本。数组:运行时检验。因为数组在编译时其创建的类是具体化的,在运行期间才检验类型约束是否正确。
2024-07-29 21:19:14
260
原创 第二十七条:消除unchecked类型的警告
当你遇到你不能消除的警告时,如果你能保证被警告的代码类型是什么,就可以使用(也只能使用这种方式)@SuppressWarnings(“unchecked”)注解来消除警告。但是当你不能确保其类型的时候,就不要使用这个注解消除警告,因为即使你在编译期间消除了警告,在运行时也可能会报出ClassCastException的异常。但是当你不能确保其类型的时候,就不要使用这个注解消除警告,因为即使你在编译期间消除了警告,在运行时也可能会报出ClassCastException的异常。
2024-07-25 21:31:29
315
原创 第二十六条:不要使用原始类型
有人会说,这样不是挺好的么,想放什么就放什么。我取数据应该怎么取?从这一章开始讲解关于泛型以及将泛型的好处最大化并将其负面影响最小化。这样的代码是不是可读性、可维护性实在是太差了!每日一问:为啥不要使用原始类型?使用原始类型是合法的,
2024-07-25 21:19:17
118
原创 第二十四条:与静态类相比,优先选择静态成员类
简而言之,有四种不同的嵌套类,各有应有场景:如果一个嵌套类需要在单个方法之外仍然是可见的,或者他太长了,不适合方法内部,就应该使用成员类。否则,就做成局部类。嵌套类有四种:静态成员类(static member class)、非静态成员类(nonstatic member class)、匿名类(anonymous class)和局部类(local class)。如果嵌套类的实例可以在他外围类的实力之外独立存在,这个嵌套类就必须是静态成员类:在没有外围实例的情况下,要想创建非静态成员类的实例是不可能的。
2024-07-20 21:52:27
377
原创 第二十五条:将源文件限制为单个顶层类
如果你足够幸运用命令javac Main.java Dessert.java编译这个程序,那么编译会失败,而且这个编译器将会告诉你:你已经多次定义了Utensil and Dessert类。确实如此,因为编译器将会编译 Main.java,而且当它看见Utensil(它早于Dessert的引用)的引用,它将在Utensil.java中寻找这个类,然后发现了Utensil和Dessert。解决这个问题,就像分解这个顶层类(在我们例子情形中,Utensil和Dessert)到不同的源文件这么简单。
2024-07-20 21:35:25
238
原创 第二十三条:优先使用类层次结构而不是标记类
域不能做成是final的,除非构造器初始化了不相关的域,产生更多的样板代码。构造器必须不借助编译器,来设置标签域,并初始化正确的数据域:如果初始化了错误的域,程序就会在运行时失败。为了将标签类转变成类层次,首先要为标签类中的每个方法都定义一个包含抽象方法的抽象类,这每个方法的行为都依赖于标签值。如果还有其他的方法其行为不依赖于标签的值,就把这样的方法放在这个类中。在我们的示例中,radius是特定于圆形的,length和width是特定于矩形的。注意,上述层次中的域是被直接访问,而不是通过访问方法。
2024-07-20 21:14:19
264
原创 第二十二条:接口仅用于定义类型
更糟糕的是,它代表了一种承诺:如果在将来的发行版本中,这个类被修改了,它不再需要使用这些常量了,它依然必须实现这个接口,以确保二进制兼容性。如果非final类实现了常量接口,它的所有子类的命名空间也会被接口中的常量所“污染”。使用这些常量的类实现这个接口,以避免用类名来修饰常量名。如果这些常量与某个现有的类或者接口紧密相关,就应该把这些常量添加到这个类或者接口中。因此,类实现了接口,就表明客户端可以对这个类的实例实施某些动作(接口中定义的方法)。当类实现接口时,接口就充当可以引用这个类的实例的。
2024-07-15 21:11:18
291
原创 第二十一条:为传诸后世而设计接口
Java8引入了默认方法,目的就是允许向现有的接口中添加方法。但是向现有的接口中添加新方法还是充满风险的。在存在默认方法的情况下,一个接口的现有实现可能在编译时没有错误或警告,但在运行时却失败了。尽管默认方法现在已经成为java平台的一部分,但谨慎设计接口仍然是及其重要的。虽然接口在发布之后再修正一些缺陷也是有可能的,但千万不要寄希望于此。还要注意的是,默认方法不支持从接口中删除方法或改变现有方法的签名。
2024-07-15 20:51:10
268
原创 第十九条:要么为继承而设计并提供文档说明,要么就禁止继承
如果你决定在一个为了继承而设计的类中实现Serializable,并且该类有一个readResolve或者writeReplace方法,就必须使readResolve或者writeReplace成为受保护的方法,而不是私有的方法。如果类是为了继承而被设计的,无论实现这其中的那个接口通常都不是一个好主意,因为他们它一下实质性的负担转嫁到扩展这个类的程序员的身上。当设计一个可能被广泛使用的用于继承的类时,要意识到,我们对写在文档中的方法的自身使用情况,以及隐含在受保护的方法和字段的实现决策做出了永久性的承诺。
2024-06-26 22:08:38
360
原创 第十八条:组合优先于继承
每日一问:为啥建议组合优先与继承?结果:过了一段时间,David觉得要再自己声明的A类上加个参数job 于是Tom写的代码就出现了错误,然而David自始至终都不知道Tom用了自己的代码。
2024-06-24 21:31:33
410
原创 第十七条:使可变性最小化
不可变的类变成 final 的另 一种办法就是,让类的所有构造器都变成私有的或者包级私有的,并添加公有的静态工厂( static factory )来代替公有的构造器(详见第 1 条)。虽然从技术上讲,允许不可变的类具有公有的 final域,只要这些域包含基本类型的值或者指向不可变对象的引用,但是不建议这样做,因为这样会使得在以后的版本中无法再改变内部的表示法。你应该总是使 一些小的值对象。这种技巧可以很好地工作,因为对象是不可变的,它的不可变性保证了这些计算如果被再次执行,就会产生同样的结果。
2024-06-17 21:57:24
481
原创 第十六条:在公有类中,使用访问器方法,而不使用公有的字段
要拿到name的值是要通过getter访问器去取值,这样就不会轻易被外界随意修改了。(例子举的不是太恰当,意思到位就好!也就是说我要修改对象Employ是随意修该,不需要花太大的代价。,以保留修改该类的内部表示的灵活性。声明对象Employ。
2024-06-16 21:06:47
163
原创 第十五条:最小化类和成员的可访问性
如果类或者接口可以被做成包级私有,就应该被做成包级私有,这样它实际上成了包的实现的一部分,而不是该包导出的API的一部分,在以后的发行版本中,可以对它进行修改、替换、删除,而无需担心影响现有的客户端程序。即使域是final的,并且引用不可变的对象,当把这个域变成公有的时候,也就放弃了“切换到一种新的内部数据表示法”的灵活性(因为公有的是导出API,必须始终坚持这种数据表示,保证兼容)。有一条规则限制了降低方法的可访问性的能力,如果方法覆盖了超类的一个方法,子类的访问级别不允许低于超类中的访问级别。
2024-06-13 21:45:47
230
原创 第十四条:考虑实现Comparable接口
此外,所得到的方法不太可能比使用项中描述的技术编写的方法快得多。许多程序猿更喜欢这种方法的简洁性,尽管它的性能成本很低:在我的机器上排序PhoneNumber实例的数组大约慢10%。使用这种方法时,考虑使用Java的静态导入功能,这样您就可以通过简单的名称来引用静态比较器构造方法,以获得清晰和简洁【代码】。compareTo 方法不但允许进行简单的等同性比较,而且允许执行顺序比较,除此之外 ,它与 Object 的 equals 方法具有相似的特征,它还是个泛型( generic )。
2024-06-06 22:08:04
815
原创 第十二条:总是重写toString方法
最后说下,重写hashCode可以用@注解来手动编写也可以用ide软件提供的方法(快捷键Alt+ins,选中对应方法即可)重写toString方法对象就是number='182-4565-0000',是不是比不重写方法看到更多信息?不重写toString方法对象就是@bebdb06。
2024-05-30 21:27:44
133
原创 第九条:与try-finally相比,首选try-with-resources
在处理必须关闭资源时,应该总是选择try-with-resources,这样得到的代码更短、更清晰,生成的异常也更有价值。
2024-05-23 21:30:14
128
1
原创 第八条:避免使用终结方法和清理方法
但是当存在终结方法时,情况就不同了,final类不会受到此攻击,因为攻击者无法为其编写一个恶意子类。为了保护非final类免受此攻击,可以在其中编写一个空的final的finalize方法。从java9开始,终结方法已经被废弃了,但java类库还在使用。java9引入了清理方法来代替终结方法。清理方法的危险性比终结方法要小,但仍然不可预测,而且运行很慢,一般来说也是不必要的。首先说明一下,青哥干开发到现在印象里没有写过终结方法和清理方法。因此,不要依赖终结方法和清理方法来更新持久化状态。
2024-05-22 21:21:42
198
原创 第七条:清除过期的对象引用
如果你实现了一个API,客户端注册了回调,但是没有显示地注销,除非你采取一些措施,否则这些回调对象就会不断的积累起来。确保回调及时被垃圾收集处理的一个方法是只存储对他们的弱引用,例如,将其仅作为weakHashmap中的键来存储。Stack类的哪方面的特性使其容易发生内存泄漏呢?只有在缓存外有对其键的引用时,它才有存在的意义,那么就可以用weakHashmap来实现。现在我们正式的项目是不需要手动去清除垃圾,都是自动清除的,这点大家不要多虑。一般的说,每当出现类自己管理自己的内存的情形时,
2024-05-20 21:33:09
253
1
原创 第5条:优先考虑依赖注入来连接资源
总而言之,对于依赖一个或多个底层资源,而且资源的行为会对其行为造成影响的类,不要使用singleton或者静态类来实现,也不要让该类直接创建这些资源。相反,应该将资源或创建资源的工厂传递给构造器(静态工厂,或生成器)。这种做法,也就是所谓的依赖注入,将极大的提升类的灵活性、复用性和可测试性。将这样的类实现为静态工具类的做法不少见。以上两种方法都不能令人满意,因为他们都假定只有一个词典值得使用。那么问题来了,依赖注入又有哪些方式?
2024-05-13 21:54:10
135
原创 第4条:通过私有构造器强化不可实例化的能力
很多人第一时间会有疑问,java中为啥防止类被实例化?new一个类用get set实在太爽了,为啥不给new?
2024-05-09 21:46:24
400
1
原创 第3条:用私有构造器或者枚举类型强化Singleton属性
Singleton是指被实例化一次的类,通常表示无状态的对象,如函数。如果要使其singleton序列化仅仅implements Serializable是不行的,还要提供一个readResolve方法。有两种常见实现Singleton的方式,原理都是将构造器设置为私有,通过导出一个公有静态成员来提供对唯一实例的访问。
2024-05-08 21:50:20
394
1
原创 Effective Java 第2条:遇到多个构造器时要考虑使用构建器
像态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。这些标签中有几个域是必需的:每份的含量、有罐的含量以及每份的卡路里。还有超过 20个的可选域:总脂防量、饱和脂防量、转化临、腿固醉、钠,等等。大多数产品在某几个可选域中都会有非零的值。,在这种模式下,提供的第一个构造器只有必要的参数,第二个构造器有一个可选参数,第三个构造器有两个可选参数,最后一个构造器包含所有可选参数。对于这样的类,应该用哪种构造器或者静态工厂来编写呢?2.其实我们也可以用注解的builder进行入参,而且更加方便。
2024-05-07 20:57:33
283
原创 Effective java 第一章 引言
术语“导出的API”(exported API),或者简单地说API,是指类、接口、构造器(constructor)、成员和序列化形式 (serialized form),程序员通过它们可以访问类、接口或者包。使用API编写的员被称为 API 的用户(user),在类的实现中使用了 API的类被称为该API的客户端(client)。不严格地讲,一个包的导出 API是由该包中的每个公有(public)类或者接口中所有公有的或者受保护的 (protected)成员和构造器组成。签名不包括方法的返回类型。
2023-11-21 19:41:46
59
原创 Effective java 阅读感想及笔记
从事java两年多时间,但对于系统结构还是认为不是太懂,刚好同事推荐了这本《Effective java》,之前一直在忙没有整理,现阶段到明年,这边会系统整理好笔记,直到更新整本书,当然笔记中应该会出现错误,也请读者及时发现指正。
2023-11-21 19:01:21
50
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人