4 章 对象与类
- 封装的优点
- 更改器可以执行错误检查,防止出错
- 注意:不要返回引用可变对象的访问器方法,这样会破坏类成员的封装性,因为这意味着返回了一个引用
- 如果想要返回一个可变数据域的拷贝,应该使用 clone
- 静态方法之于工厂方法
- 使用静态工厂方法产生不同风格的格式对象
- 方法参数的使用情况
- 方法不能修改基本数据类型的参数(数值型和布尔型)
- 方法可以改变对象的状态,比如说改变对象的属性值
- 方法不能让对象参数引用一个新的对象,即该对象引用本身的值不能被改变
-
java 中的函数参数没有引用传递
-
java 中可以显式域初始化,即定义类成员变量时赋值, c++ 中不能直接初始化类的实例域。
- 对象析构和 finalize 方法
- 类的 finalize方法将在垃圾回收器清除对象之前调用。但是因为 java 有自动的垃圾回收器,因此该很难知道这个方法什么时候会被调用。对于某些资源,非必须的情况下不应该放在该函数中回收。
- 包
- import 语句能够导入包中特定的类,使用 .* 可以导入包中所有的公有类
- 静态导入
- import static java.lang.System.*; 可以使用 System 类的静态方法和静态域
- import static java.lang.System.out; 可以导入特定的静态方法或域
- 没有 static 关键字会报错
- 包作用域
- 如果没有指定 public 还是 private, 这个部分可以被同一包内的所有方法访问
- javadoc api 文档自动生成器
- @关键字
- 添加包注释的方法
- 类设计技巧
- 保证数据私有性
- 对数据初始化
- 不要在类中用过多的基本类型
- 应该将多个相关的基本类型使用一个类来代替
- 并非所有的域都需要访问器和更改器
- 将职责较多的类进行分解
- 尽可能地让类的智能单一
- 类名和防范要能够体现它们的职责
5 章 继承
-
super 和 this 并非类似的概念,super 不是一个对象的引用,不能将 super 赋给另一个对象变量,它只是一个指示编译器调用超类的特殊关键字。
- this 有两个用途
- 引用隐式参数
- 调用该类的其他的构造器
- super 两个用途
- 调用超类的方法
- 调用超类的构造器
-
Java 中不需要将方法声明为虚拟方法,动态绑定是默认的处理方式,如果不想让一个方法具有虚拟特征,可以将其标记为 final
- 继承关系,”is-a” 规则
- 举例说明,Manager 类是 Employee 类的子类,因为每个经理都是雇员
- 调用对象方法的执行过程
- 查看对象的声明类型和方法名,编译器获得所有可能被调用的候选方法
- 查看所有候选调用方法的参数类型,查找和参数类型匹配的方法,这被称为重载解析
- 查看方法的修饰符,编译器将指导应该调用哪个方法,这被称为静态绑定
- 动态绑定调用方法,虚拟机需要调用与 x 所引用对象的实际类型最合适的那个类的方法
- 注:实际上虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用的方法,节省搜索的时间。
-
在覆盖一个方法的时候,子类方法不能低于超类方法的可见性,特别是超类方法是 public,子类方法一定要声明为 public,否则,调用该方法是,实际上会调用父类方法
- 阻止继承:final 类和方法
- 阻止类被继承: final class Manager{…}
- 阻止方法被继承: public final String getName(){…}
- 对象之间的强制转换
- 预防在继承链上进行向下的类型转换,可以使用 instanceof 运算符来避免,比如
if(staff[1] instanceof Manager){ // 先判断是否是 Manager 类的对象 boss = (Manager)staff[1]; }
- 应该尽量避免对象之间进行强制转换,尽量使用多态性的动态绑定机制处理
- 预防在继承链上进行向下的类型转换,可以使用 instanceof 运算符来避免,比如
- 抽象类
- 抽象类和抽象方法的声明
abstract class Person{ private String name; public abstract String getName(){ return name; } }
- 抽象类中可以有具体的数据和具体的方法
- 含有抽象方法的类一定是抽象类,抽象类也可以不含抽象方法,但是这样就失去了将其定义为抽象类的意义。
- 抽象类不能有实例化对象
- 抽象类和抽象方法的声明
- java 用于控制可见性的 4 个访问修饰符
- private:仅对本类可见
- public: 对所有类可见
- protected:对本包和所有子类可见
- 默认(无修饰符):对本包可见
- equals 方法
- Object 类中的 equals方法, 用于检测一个对象是否等于另外一个对象,只是判断两个对象是否具有相同的引用。但是这样的判等没有什么意义。大多数的 equals 方法都需要重写,比如 String类的 equals方法
- 重写 equals 时,首先调用超类的 equals,再比较子类中的实例域
- equals 方法的相等测试与继承
- java 语言规范规定 equals 需要具有的规范
- 自反性,对称性,传递性,一致性,对 null 应返回 false
- 子类和父类之间的 equals 问题
- 使用 instanceof 做类相等测试时,会造成对称性方面的问题
- JDK 中的 equals 方法实现多种多样
- java 语言规范规定 equals 需要具有的规范
- hashCode 方法
- Object 默认的 hashCode 是返回对象存储地址,两个对象一定不同
- String 的散列码是由内容导出的,因此两个内容相同的字符串的 hashCode 会是相同的
- 注意:
- 如果重新定义了 equals 方法,需要重新定义 hashCode 方法,以便在使用散列表中不会出现 key 相同的情况
- toString 方法
- Object 类的 toString 方法,打印输出对象所属的类名和散列码
- 数组的 toString 方法
- 一维数组打印: Arrays.toString(array);
- 多维数组打印: Arrays.deepToString(array);
- 对象包装器
- 基本类型的包装类有: Integer, Long, Float, Double, Short, Byte, Charater, Void, Boolean - 包装器中会有缓存对象数组,比如 Integer 会缓存 -128~127 的 Integer 对象。
- 包装器中包含了很多有用的静态方法,比如 Integer.parseInt(s),将字符串转换成数字
- 自动装箱
- list.add(3); 相当于自动调用 list.add(Integer.valueOf(3));
- 自动拆箱
- int n = list.get(i); 相当于调用 list.get(i).intValue();
- 包装器之间的判等是有条件的
- 包装器根本上来说是对象,直接 == 实际上是比较的对象在内存中放置的地址
- 但是包装器出于优化的目的,在内存中提前放置了常用值,比如 int 类型的包装器维护了一个 -128~127 的数组,Integer 的值在 -128~127 之间时,会被判做相等。但是超过这个范围就会被判做不等。
- 包装器类型之间的比较应该使用 equals
- 包装器根本上来说是对象,直接 == 实际上是比较的对象在内存中放置的地址
- 参数数量可变的方法
- Object[] args
static void multiArg(String ...strings) { // 特别的地方就是 ... , 实际上 strings 是一个 String 数组 for (String string : strings) { System.out.println(string); } } ... 调用方法 multiArg("wo", "hello");
- Object[] args
- 枚举类
- 枚举类
- 简单定义
public enum Size{SMAILL, MEDIUM, LARGE, EXTRA_LARGE};
此处声明定义的类型是一个类,有 4 个实例。 * 使用
java Size size = Size.MEDIUM; if(size == Size.MEDIUM) { System.out.println("M"); }
- 简单定义
- 枚举类
- 继承设计的技巧
- 将公共操作和域放在超类
- 不要使用受保护域
- 子类在继承过程中,会破坏封装性
- 同一个包中的所有子类都可以访问protected域
- 使用继承实现 “is a” 关系
- 除非所有继承的方法都有意义,否则不要使用继承,应该使用接口实现?
- 在覆盖方法时,方法实现要和预期的行为一致
- 使用多态,而非使用类型信息,即用 intanseof 来判断类型
- 不要过多使用反射
- 编译器很难帮助人们发现其中的错误,只有运行时才会发现错误并导致异常。
第 6 章 接口和内部类
6.1 接口
接口的特性:
- 接口的方法默认为 public 方法,可以不必显式声明
- 实现接口的类,需要在实现的方法上显示声明 public,否则就是默认的 default 访问权限而非 public
- 接口不是类,不能使用 new 来声明一个实例
- 接口中的域(即成员变量)自动被设置为
public static final
接口和抽象类的区别(笔试和面试常见知识点)
java 8 已经向接口中引入默认方法和静态方法了。
6.2 对象克隆
- clone 方法是 Object 类的一个 protected 方法,不能在外部调用
- 如果先想用 clone 方法来拷贝一个对象,必须要实现一个 public 方法,且在方法中调用本类的 clone 方法
- 浅拷贝,原对象的成员变量和拷贝对象的变量指向的空间相同,相当于复制过程中只复制了指针
- 深拷贝,各自单独一份
常用的替代 clone 的方法:先将对象序列化到内存,然后反序列化得到的对象就是深拷贝的。
6.3 接口和回调
回调,常用在观察者模式中,当被观察者完成某一操作之后,被观察者会通过回调观察者的某一个方法来通知观察者。
6.4 内部类
出现内部类的原因:
- 内部类方法可以访问外部类的数据域,包括私有数据
- 内部类可以对同一个类文件中的其他类隐藏起来
- 实现回调方法,如果不想单独定义一个类,可以使用匿名内部来来实现
- 可以实现多继承,让多个内部类分别去继承不同的类,实现多继承
内部类分为
- 静态内部类
- 非静态内部类
- 成员内部类,和成员变量等级一样
- 内部不能定义 static 变量,但是可以定义 static final 变量,或者是只有 final 修饰的变量
- 局部内部类,定义在方法中,只能在该方法或条件的作用域内才能使用,退出这写作用域就无法引用。
- 只能访问方法内的 final 变量,为了防止方法完成后退出销毁局部变量之后,局部内部类中还在引用该局部变量。
- 匿名内部类,如果调用方法期间只需要使用一次,可以定义成匿名内部类。
- 成员内部类,和成员变量等级一样