资讯详情

《Effective Java》读书笔记,移动安全入门

equals()实现等价关系的方法。

  1. 。对于任何非null的引用值x,x.equals(x)必须返回true.(对象必须等于自己)

  2. 。对于任何非null引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。(警惕子类父类不同equals实现时)

  3. 。对于任何非null的引用只x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equeals(z)也必须返回true。(优先使用组合而不是继承,可以避免违反传递性 对称性,否则没有完美的解决方案。 只要超类不能直接创建实例,就不会违反原则)

  4. 。对于任何非null引用值x和y,只要equals对象中使用的比较操作信息未修改,多次调用x.equals(y)返回值一致。

  5. 。对于任何非null的引用值x,x.equals(null)必须返回false。(不需要重复判空,只需要使用instance 即可)

实现的诀窍:

  1. 检查参数是否被引用。如果是,返回true。这是一种性能优化,适的性能优化。

  2. 参数是否正确。如果没有,返回false。

  3. 把参数正确的类型。

  4. 这一类中的每个关键域。(。 有些filed可能为null,且合法。 可以用 (field ==null? o.field==null: field.equals(0.field)) 。 如果field和o.field通常引用相同的对象,所以以下做法更快: ( field == o.field || field!=null && field.equlas(0.field))

  5. 编写完equals方法完成后,应进行测试 对称性、传递性、一致性。

  6. 覆盖equals同时覆盖hashCode。

  7. 不要将替换为其他类型。(public boolean equals(MyClass o)){})

//一个示例

@Override

public boolean equals(Object o) {

if (o == this) {

return true;

}

if (!(o instanceof EnumSingletonTest)) {

return false;

}

EnumSingletonTest data = (EnumSingletonTest) o;

return data.xxx == xxx

&& dta.bbb == bbb;

}

9 覆盖equals时 hashCode

不覆盖这种方法会导致这种方法无法合作HashMap、HashSet和Hashtable一起使用。

一个好的散列函数通常倾向于”。

10 toString

11 地覆盖clone

一般,x.clone() != x ,x.clone().getClass() == x.getClass(), x.clone().equals(x) (但也不强求)

实际上,就是;您必须确保它不会伤害原始对象,并确保克隆对象中的约束条件正确创建。

更好的办法是那样,提供 。并且可以转换构造器和工厂。 比如你有一个HashSet,想得到TreeSet,clone该方法不能提供这样的功能,但很容易使用转换结构。 new TreeSet(s)。

专家程序员不建议使用clone。

12 实现 Comparable 接口

实现compareTo()方法 。它的约定和equals类似的方法。(自反、传输、对称),但不需要类型检查。顺序很重要,

用于TreeSet TreeMap,以及工具类Collections、Arrays,内部包含搜索和排序算法。

若未实现一个域Comparable接口,或使用非标准的排序关系。可以使用替换,或使用现有的Comparator。例如:

public final class A implements Comparable{

private String s ;

public int compareto(A a){

return String.CASE_INSENSITIVE_ORDER_.compare(s,a.s);

}

}

有时可以直接使用 this.s - other.s 作为返回值,但要保证 最。否则,计算结果过int型溢出时, 结果会相反。

第四章 类和接口


使类和成员的可访问性最小化

隐藏内部数据和实现细节。

使每个类别或成员

对于可能的访问级别:

如果是包级私有顶层活接口 只是在某一个类的内部被用到,就应该考虑使他成为那个类的私有嵌套类。

然而,比上一点更重要的是降低不必要的共同类别的可访问性。

成员依次有:

protected应少用,和public都作为公开API的一部分。

若该方法涵盖了超类中的一种方法,。这样可以保证子类也可以在任何可以使用超类的地方使用。

。如果域是非final如果是这样,那就更不合适了。如果是这样的话。如果是这样的话。final该域包含可变对象的引用,可以修改引用对象,导致灾难性后果。

非零长度的数组总是可变的。。因为客户端可以,这是一个安全漏洞。

有两种方法可以纠正这个问题:

  1. 同时,访问权限为私有

private static final A[] PRIVATE_VALUES = {…};

public static final List VALUES =

Collections.unmdifiableList(Arrays.asList(PRIVATE_VALUES ));

  1. 访问权限设为私有,

private static final A[] PRIVATE_VALUES = {…};

public static final A[] values(){

return PRIVATE_VALUES .clone();

}

14 在公有类中使用访问方法而非公有域

永远都。偶尔会

有时候会,无论这个类可变还是不可变。

15 使可变性最小化

每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变。例如String、基本类型的包装类、BigInteger、BigDecimal。

不可变类遵循下面五条规则:

  1. 保证类。(final或者 私有、包级私有构造器,提供公有静态工厂)

  2. 使所有的的。

  3. 使所有的的。

  4. 确保对于任何可变组件的互斥访问。

大多数不可变类在修改域时,都是通过返回一个新的对象做的。

这被成为 做法,与之对应的是,这些方式会导致状态改变。

例如

public class ImmutableExampleTest {

private final int value;

public ImmutableExampleTest(int value) {

this.value = value;

}

public int getValue() {

return value;

}

public ImmutableExampleTest add(ImmutableExampleTest b) {

return new ImmutableExampleTest(value + b.value);

}

}

不可变对象可以只有一种状态,即被创建时的状态。

不可变对象本,它们不要求同步。

不可变对象可以被自由地共享。鼓励客户端尽可能地。可以

不可变对象

不仅可以共享不可变对象,甚至也可以。例如BigInteger类的negate()方法,返回的BigInteger对象中的数组指向原始实例中的同一个内部数组。

不可变对象

不强求所有的域都是final的,但必须保证 :没有一个方法能够对对象的状态产生 的改变。可以用非final域去缓存一些昂贵开销的计算的结果(配合lazy initialization)。

如果不可变类实现了Serializable接口,并且包含一个或多个指向可变对象的域,需要特殊处理。

不可变对象

如果你执行一个多步骤的操作,并且每个步骤都会产生一个新的对象,除了最后的结果之外其他的对象最终都会被丢弃,此时就会暴露出来。两种解决办法:

  1. 先猜测一下经常用到哪些多步骤操作,然后将它们作为基本类型提供。

  2. 如果无法预测,最好的办法就是。String类是不可变类,它的可变配套类是StringBuilder。BigInteger对应BigSet。

总结:

,例如String、BigInteger。

不应该提供“重新初始化”方法,它会增加复杂性。

16 复合优先于继承(组合大于继承)

继承是实现代码重用的有力手段,但它并非永远是最佳工具。

非常安全,因为它属于同一个程序员的

对于

子类依赖于其超类中特定功能的实现细节。超类的实现有可能随着发行版本的不同而有所变化,如果发生了变化,子类可能遭到破坏,即使子类的代码完全没有改变。

继承机制会把超类API中的所有缺陷传播到子类中,而复合(装饰、代理)则允许设计新的API 来隐藏这些缺陷。

17 要么为继承而设计,并提供文档说明,要么就禁止继承。

构造器决的方法。不管是直接的还是间接的调用。

如果类是为了继承而被设计的,无论实现CloneableSerializable都不是好主意。因为它把一些实质性的负担转嫁给扩展这个类的程序员的身上。因为**clone()readObject()方法行为上类似于构造器,所以也不可以调用可覆盖的方法,不管以直接还是间接的形式。**

如果必须从这种类继承,一种办法是 确保这个类永远不会调用它的任何可覆盖的方法。

18 接口优于抽象类

现有的类可以很容易被更新,以实现新的接口。

接口是定义mixin(混合类型)的理想选择。

接口允许我们构造非层次(竖向)结构的类型框架。

虽然接口不允许包含方法的实现。**但通过对每个重要接口都提供一个抽象的骨架实现(skeletal implementation)类,把接口和抽象类的优点结合起来。**接口的作用仍然是定义类型,但是骨架实现类接管了所有与接口实现相关的工作。

按照惯例,骨架实现类称为AbstractInterface,例如AbstractCollectionAbstractSet等。

骨架实现上有个小小的不同,就是。AbstractMap.SimpleEntry就是个例子,简单实现类似于骨架实现类,因为它实现了接口,并且

抽象类比接口有一个:抽象类的比接口的演变要

如果在后续的版本中,希望在抽象类中增加新的方法,始终可以增加具体方法,包含合理的默认实现即可。

一般来说,想在公有接口中增加方法,而不破坏实现这个接口的所有现有的类,这是不可能的。可以通过在为接口增加新方法的同时,也为骨架实现类增加同样的新方法,来一定程度上减小破坏。

总结:

接口通常是定义允许多个实现(多态)的最佳途径。

如果你想要演变的容易性比功能、灵活性更重要,而且你接受抽象类的局限性,就用抽象类。

接口->骨架实现类->简单实现类。

19 接口只用于定义类型

除此之外,为了任何其他的目的而定义接口是不恰当的。

说白了,定义接口是为了接口的行为。

有一种接口是常量接口,这是对接口的不良使用。因为常量是实现细节,不应该泄漏出去。对类的用户来说没有价值,反而会干扰。而且如果非final类实现了常量接口,它的所有子类的命名空间也会被接口中的常量所“污染”。

20 类层次优于标签

想到了自己项目里 群聊创建模块。 原本就是一个标签类,改造成了类层次。

类层次好处:

简单清晰,没有样板代码,不受无关数据域的拖累,多个程序员可独立扩展层次结构。而且类层次可以反映类型之间本质上的层次关系,有助于增强灵活性。

标签类有很多缺点:

  1. 可读性差

  2. 内存占用增加因为实例承担着属于其他风格的不相关的域。

  3. 域不能是final的。

总之它过于冗长,容易出错,效率低下。

将标签类转化成类层次:

  1. 为标签类的每个方法都定义一个包含的抽象类,这里的每个方法的行为都依赖于 标签值。

  2. 如果有行为不依赖于标签值的方法,就将这些放在抽象类里。

  3. 如果所有的方法都用到了某些域,则将域也放在抽象类里。

  4. 为每种原始标签书写相应的子类。

21 用函数对象表示策略

如果一个类,它只有一个方法,这个方法执行其他对象(通过参数传入)上的操作。

这个类的实例实际上等同于一个指向该方法的指针。

这样的实例称之为。也是一个具体策略类。

我们在设计具体的策略类时,还需要定义一个策略接口。

总结:函数指针的主要用途就是实现策略模式。

为了在java中实现这个模式,要声明一个接口来表示该策略,并且为每个具体策略实现该接口。

当具体策略只使用一次时,通常用匿名内部类来做。

当重复使用时,通常实现为某个类私有的静态成员类,并通过 公有 静态 final 域 被导出,域类型是策略接口类型。

22 考虑静态成员类

嵌套类指定义在另一个类内部的类。目的是 只为它的外围类服务。

如果嵌套类将来可能用于其他的环境中,它就应该是顶层类。

嵌套类四种:

  1. 静态成员类(优先)

  2. 非静态成员类

  3. 匿名类(不能拥有静态成员,如果只有一处创建的地方)

  4. 局部类(有多处创建的地方)

后三种被称为

静态成员类是最简单的一种嵌套类,最好把它看作是普通的类,只是碰巧被声明在另一个类的内部而已。

内部类会包含一个额外的指向外围对象的引用,保存这份引用要消耗时间和空间,也会影响GC.

匿名类的常见用法:

  1. 动态创建函数对象。(21条)

  2. 创建过程对象,比如Runnable、Thread。

  3. 静态工厂方法的内部。

第五章 泛型


出错之后应该尽快发现,最好是在编译时就发现。

23 请不要在新代码中使用原生态类型、

List<T>对应的原生态类型就是List,如果使用原生态类型,就。它只是为了兼容遗留代码的。

无限制的通配符类型:List<?>,等于List,它们和List<Object>都不相等。

List.class String[].class ini.class都合法,List<String>.class 和List<?>.class都不合法。

24 消除非受检警告

尽可能地消除每一个非受检警告。 如果消除所有警告,则可以确保代码是类型安全的。意味着在运行时不会出现ClassCastException异常。

如果无法消除警告,同时可以证明引起警告的代码是类型安全的,只有在这种情况之下,才可以用一个@SuppressWarnings(“unchecked”)注解来禁止这条警告。

应该在尽可能小的范围里使用该注解。并添加注释解释为什么这么做是安全的。

25 列表优先于数组

数组与泛型相比,有两个重要的不同:

如果Sub是Super的子类型,那么Sub[]就是Super[]的子类型。

然而泛型则不是。对于任意两个不同的类型Type1,Type2,List<Type1>和List<Type>不可能是子类型或父类型的关系。

实际上,这不是泛型的缺陷,反而是数组的缺陷:

//fails at runtime!

Object[] objectArray = new Long[1];

objectArray[0] = “I don’t fit in”;//Throws ArrayStoreException

//Won’t compile

List listObject = new ArrayList();//Incompatible types

上述代码,无论哪种方法,都不能将String放入Long容器中。但是利用列表,可以在编译时发现错误。

26 优先考虑泛型

不能直接创建 new T[16],可以通过创建 (T[])new Object[16];强转。也可以在 取出数据时强转。

有些泛型如ArrayList,必须在数组上实现。

为了,其他泛型如HashMap也在数组上实现。

27 优先考虑泛型方法

静态工具方法尤其适用于泛型化。

泛型方法一个显著特征,可以配合 类型推导,无需明确指定类型参数的值。

28 利用 来提升API的灵活性

还要记住所有的

为了获得最大限度的灵活性,要在表示生产者或者消费者的

如果某个 即是生产者又是消费者,则通配符类型对你没有好处,需要严格的类型匹配。

如果类的用户必须考虑通配符类型,类的API或许就会出错。

显式的类型参数:

Set numbers = Union.union(integers,doubles);

一个复杂的例子:

public static <T extends Comparable<? super T>> T max(List<? extends T> list)

类型参数和通配符之间具有双重性,许多方法都可以利用其中一个或者另一个进行声明。

一般来说,如果只在,就可以

在公共API中,推荐第二种。

例如:

//类型参数和通配符之间具有双重性,许多方法都可以利用其中一个或者另一个进行声明

//一般来说,如果类型参数只在方法声明中出现一次,就可以用通配符取代它。

public static void swap1(List list, int i, int j) {

}

public static void swap2(List<?> list, int i, int j) {

}

public static void swap3(E list, int i, int j) {

}

//这个是错误的, 因为E不是类型参数

/* public static void swap4(? list, int i, int j) {

}*/

29 优先考虑 类型安全 的 异构容器

类型安全:当你向它请求String时,它从来不会返回一个Integer给你

异构容器:不像普通的map,它的所有key都是不同类型的。

泛型最常用于集合。

注解API广泛利用了有限制的类型令牌。 被注解的元素,本质上是个类型安全的异构容器。

第六章 枚举和注解


1.5中增加了 两个新的引用类型:

30 用enum代替 int 常量。

枚举易读,

如果枚举具有普适性,应该成为一个顶层类。如果用在特定顶层类中,应该成为该顶层类的一个成员类。

但枚举有小小的性能缺点,装载和初始化枚举时会有空间和时间的成本。

31 用实例域

永远不要根据枚举的序数导出与它关联的值,而是要将它保存在一个实例域中。

最好完全避免ordinal方法的使用。

32 用EnumSet

位域有更多缺点,打印时翻译位域更困难,遍历位域也困难。

EnumSet内部用单个long保存,(枚举类型小于等于64个)

枚举类型要用在集合中,所以没有理由用位域来表示它。

用EnumSet,简洁、性能也有优势。

public static void main(String[] args) {

EnumSet

System.out.println(bold);

}

enum Style {

BOLD, ITALIC, UNDERLINE;

}

//[BOLD, ITALIC]

33 用EnumMap

按Enum分组索引,key是Enum。

最好不要用序数来索引数组,而要使用EnumMap。

34 用接口模拟可伸缩的枚举

用接口模拟可伸缩枚举有个小小的不足,无法从一个枚举类型继承到另一个枚举类型。

35注解优先于

命名模式缺点:

  1. 文字拼写错误会导致失败,且没有任何提示。

  2. 无法确保它们只用于相应的程序元素上。

  3. 没有提供将参数值与程序元素关联起来的好办法。

36坚持使用Override注解

37用定义类型

标记接口是没有包含方法声明的接口,而只是知名一个类实现了具有某种属性的接口。

最后是今天给大家分享的一些独家干货:

Android学习PDF+架构视频+面试文档+源码笔记

num分组索引,key是Enum。

最好不要用序数来索引数组,而要使用EnumMap。

34 用接口模拟可伸缩的枚举

用接口模拟可伸缩枚举有个小小的不足,无法从一个枚举类型继承到另一个枚举类型。

35注解优先于

命名模式缺点:

  1. 文字拼写错误会导致失败,且没有任何提示。

  2. 无法确保它们只用于相应的程序元素上。

  3. 没有提供将参数值与程序元素关联起来的好办法。

36坚持使用Override注解

37用定义类型

标记接口是没有包含方法声明的接口,而只是知名一个类实现了具有某种属性的接口。

最后是今天给大家分享的一些独家干货:

Android学习PDF+架构视频+面试文档+源码笔记

[外链图片转存中…(img-2MvEeqzn-1644045782234)]

[外链图片转存中…(img-R88n5qkM-1644045782236)]

[外链图片转存中…(img-o4Q96iIj-1644045782236)]

标签: omron钮型传感器r88d

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台