JAVA核心技术卷I(继承)
一、类、超类、子类
1.1 覆盖方法
派生类继承了父类的方法,但是,如果觉得父类的方法不好用,可以自己重新设计一个方法名和参数与父类方法一样的方法,这叫做覆盖。如果覆盖中需要调用父类的方法可以使用 super
关键字,如:
void getSomething() { |
当然对于构造器方法,需要在第一行用super给父类字段赋值:
public class Cat extend Animal { |
1.2 多态性
父类引用变量可以赋值子类对象,但是编译器只允许调用在类中声明的对象。
public class ClassA { |
1.3 final 阻止继承
阻止利用某个类定义子类,可以用:
public final class Person{} // Person类将无法被继承 |
也可以使用在方法上,那么就无法被覆盖了。一个类被声明为final,那么其方法自动变为final,而不包括字段。
1.4 强制类型转换
if (obj1 instanceof obj2) { |
1.5 抽象类
包含一个或多个抽象方法的类本身必须被声明为抽象类abstract
。
抽象类可以有具体的方法和字段。抽象方法充当占位方法的角色,在子类中被部分实现,或全部实现。如果部分实现,那么该子类依旧是抽象类,需要声明 abstarct
。
抽象类不能被实例化。可以创建具体子类的对象。但是可以定义抽象类的对象变量,即多态特性。
1.6 访问控制修饰符
- private:仅对本类可见
- public:对外部完全可见
- protected:对本包和所有子类可见
- 默认(很遗憾),不需要修饰符:对本包可见
二、Object 所有类的超类
2.1 equals
Object类中实现的 equals
方法将确定两个对象引用是否相等。但是有时候我们需要比较的是两个对象的状态(字段值)是否全部相等。
完美 equals 方法的建议
显式参数命名为otherObject,稍后需要将它强制转换成另一个名为other的变量
检查this与otherObject是否相等:
javaif (this == otherObject) return true;
这条语句只是一个优化。
检测otherObject是否为null,如果为null,返回false。这项检测很有必要。【是吗?】
比较this与otherObject的类。如果equals的语义可以在子类中改变,就使用getClass检测:
javaif (getClass() != otherObject.getClass()) return false;
如果所有的子类都有相同的相等性语义,可以使用instanceof检测:
javaif (!(otherObject instanceof ClassName)) return false;
将otherObject强制转换为相应类型的变量:
javaClassName other = (ClassName)otherObject;
现在根据相等性概念的要求来比较字段。使用==比较基本类型字段,使用Objects.equals比较对象字段。
java// JAVA7 java.util.Objects
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
如果所有的字段都匹配,返回true,否则返回false。
如果在子类中重新定义equals,就要在其中包含一个super.equals(other)调用。
提示:对于数组可以用Arrays.equals(xxx[] a, xxx[] b)
2.2 hashCode
需要组合多个散列值时,可以调用Objects.hash并提供所有这些参数。
// java.util.Objects.hash |
equals与hashCode的定义必须相容:如果x.equals(y)返回true,那么x.hashCode() 必须与y.hashCode()返回相同的值。
2.3 toString
输出类名
尽量使用 getClass().getName()
来获得类名的字符串。
数组的toString
int[] a = {1, 2, 3, 4, 5}; |
三、泛型数组列表
3.1 ArrayList 数组列表
常用API:
ArrayList<E>()
:构造一个空数组列表ArrayList<E>(int initialCapacity)
:构造一个指定容量的数组列表void add(E obj):末尾追加元素
int size():返回个数
void ensureCapacity(int capacity):确保数组列表在不重新分配内部存储数组的情况下,有足够的容量存储给定数量的元素(可以理解为变为普通的数组的定长)
void trimToSize():将存储容量减为当前大小(存储容量>=当前大小)
E set(int index, E obj):修改指定下标的类型,并返回之前的值
E get(int index):返回下标的元素
void add(int index, E obj)
E remove(int index)
指定位置添加/删除,不过需要移动后面的所有元素,效率低(如果元素少不影响)
3.2 类型化与原始数组列表的兼容性
比如在老版本中有一个方法:
public void update(ArrayList list){} |
你可以直接将对象传递给update方法:
ArrayList<Employee> staff = ...; |
出于兼容性的考虑,编译器检查到没有发现违反规则的现象后,就将所有的类型化数组列表转换成原始ArrayLsit对象(即不管有没有添加泛型,最后都会变成AarrayList,泛型只是用来编译时候的检查)。在程序运行时,所有数组列表都是一样的,即虚拟机中没有类型参数。
四、对象包装器与自动装箱
4.1 包装器
Integer
Long
Float
Double
Short
Byte
以上6种,派生于公共超类Number
Character:char
Boolean
4.2 自动装箱
var list = new ArrayList<Integer>(); // 这里不能用int |
4.3 自动拆箱
int n = list.get(i); |
4.4 比较
如果直接用 == 是比较内存地址,但是有时候也许会成功。因此推荐用equals方法来比较。
Integer a = 100; |
注意:自动装箱规范要求boolean、byte、char <= 127,介于 -128和127之间的short和int被包装到固定的对象中。例如,如果将a和b(都是Integer类型)初始化为100,那么他们的比较结果(即,==)一定成功
4.5 valueOf
尽量使用valueOf构建包装类,而不是用new。
public static Integer valueOf(int i) { |
可以看到,如果用valueOf方法来创建一个对象,那么会判断是否是在范围里,如果是,就从缓存种直接返回对象的引用,如果不是才去new一个新的对象。
4.6 常用API
Integer -> int
- int intValue():返回int类型的值
int -> String
- static String toString(int i):返回10进制数值
- static String toString(int i, int radix):返回数值i的指定radix进制
String -> int
static int parseInt(String s)
static int parseInt(String s, int radix)
返回字符串s表示的整数,第一个方法10进制,第二个指定进制。
String -> Integer
static Integer valueOf(String s)
static Integer valueOf(String s, int radix)
返回Integer对象,字符串必须是10进制(第一个方法),或者指定进制(第二个方法)
五、枚举
5.1 定义
public enum Size { |
5.2 常用API
- static Enum valueOf(Class enumClass, String name):返回给定类中有指定名字的枚举常量
- String toString():返回枚举常量名
- int ordinal():返回枚举常量在enum声明中的位置,位置从0开始计数
- int compareTo(E other):如果枚举常量出现在other之前,返回一个负整数;
六、反射
6.1 Class 类
在启动时,包含main方法的类被加载。它会加载所有需要的类。这些被加载的类又要加载它们需要的类,以此类推。
6.1.1 获取Class类的三种方式
static Class forName(String className) |
Class cl = Employee.class; |
Class cl = employree.getClass(); |
6.1.2 创建实例
// 获取构造器 |
6.2 资源
java.lang.Class
URL getResource(String name)
InputStream getResourceAsStream(String name)
找到与类位于同一个位置的资源,返回一个可以用来加载资源的URL或者输入流。如果没有找到资源,返回null,所以不会抛出IO异常。
6.3 利用反射分析类
6.3.1 java.lang.Class
字段
Field[] getFields()
Field[] getDeclaredFields()
getFields返回一个包含Field对象的数组,这些对象对应这个类或其超类的公共字段。而getDeclaredFields返回所有字段。
方法
Method[] getMethods()
Method[] getDeclaredMethods()
getMethods返回所有公共方法,包括超类继承来的。getDeclaredMethods返回这个类或接口的全部方法。
构造器
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
返回Class对象的所有公共构造器(getConstructors),和全部构造器(getDeclaredConstructors)
6.3.2 java.lang.reflect.Field/Method/Constructor
Class getDeclaringClass()
返回一个Class对象,表示定义了这个构造器、方法或字段的类。(即哪个类定义了这个方法、构造器或字段)
Class[] getExceptionTypes():
在Constructor和Method classes类中
返回一个Class对象数组,其中各个对象表示这个方法所抛出的异常的类型
int getModifiers()
返回一个整数,描述这个构造器、方法或字段的修饰符。使用Modifier类中的方法来分析这个返回值。(下面介绍)
String getName()
返回一个构造器、字段或方法的名字的字符串
Class[] getParameterTypes():
在Constructor和Method classes类中
返回一个Class对象数组,里面各个对象表示参数的类型
Class getReturnType():
在Method类中
返回一个用于表示返回类型的Class对象
6.3.3 java.lang.reflect.Modifier
static String toString(int modifiers)
返回一个字符串,包含对应modifiers中设置的修饰符
static boolean isAbstract(int modifiers)
static boolean isFinal(int modifiers)
static boolean isInterface(int modifiers)
static boolean isNative(int modifiers)
static boolean isPrivate(int modifiers)
static boolean isPublic(int modifiers)
static boolean isStatic(int modifiers)
static boolean isStrict(int modifiers)
static boolean isSynchronized(int modifiers)
static boolean isVolatile(int modifiers)
6.4 使用反射在运行时分析对象
6.4.1 获取字段
java.lang.Class
- Field getField(String name):根据名字获取公共字段
- Field[] getFields()
- Field getDeclaredField(String name):根据名字获取字段
- Field[] getDeclaredFields()
6.4.2 获取/设置 字段值
java.lang.reflect.Field
- Object get(Object obj):返回obj对象中,用这个Field对象描述的字段的值
- void set(Object obj, Object newValue):更新这个obj对象的Feild描述的字段的值
6.4.3 取消访问标志
如果是私有字段,在调用上述get/set方法时,会有异常。这时候就要修改。‘
java.lang.reflect.AccessibleObject
- void setAccessible(boolean flag):设置或取消这个可访问对象的可访问标志,如果拒接访问会抛出一个IllegaAccessExceptioin异常。【true为允许访问】
- boolean isAccessible()
- static void setAccessible(AccessibleObject array, boolean flag):便利方法,用于设置一个对象数组的可访问标志
6.5 使用反射编写泛型数组代码
// 其中a传入数组 |
6.6 调用任意方法和构造器
java.lang.reflect.Method
public Object invoke(Object implicitParameter, Object[] explicitParameters)
调用这个对象描述的方法,传入给定参数,并返回方法的返回值。对于静态方法,传入null作为隐式参数。
七、继承设计技巧
- 将公共操作和字段放在超类中
- 不要使用受保护的字段
- 使用继承实现“is-a”关系
- 除非所有继承的方法都有意义,否则不要使用继承
- 在覆盖方法时,不要改变预期的行为
- 使用多态,而不要使用类型信息
- 不要滥用反射