五 继承
5.1 类、超类和子类
-
关键字this
-
一是引用隐式参数
-
二是调用该类其他的构造器
-
-
关键字super
-
一是调用超类的方法
-
二是调用超类的构造器
-
-
方法调用过程
注1:方法的名字和参数列表称为方法的签, 如 public int setAge(int age),签名为 setAge(int)
注2:若子类中定义一个与超类签名相同的方法, 那么子类中的这个方法就覆盖了超类中的这个方法
- 编译器査看对象的声明类型和方法名
- 编译器将査看调用方法时提供的参数类型
- 如果是private 方法、static 方法、final 方法或者构造器, 那么编译器将可以准确地知道应该调用哪个方法, 我们将这种调用方式称为静态绑定
- 当程序运行,并且采用动态绑定调用方法时, 虚拟机一定调用与x 所引用对象的实际类型最合适的那个类的方法。
-
抽象类
- 包含一个或多个抽象方法的类本身必须被声明为抽象的
- 类即使不含抽象方法,也可以将类声明为抽象类。
- 抽象类不能被实例化,但可以创建一个具体子类的对象。
-
Java 用于控制可见性的4 个访问修饰符
- 仅对本类可见private
- 对所有类可见public
- 对本包和所有子类可见protected
- 对本包可见—默认(很遗憾),不需要修饰
5.2 Object:所有类的超类
-
所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object 类
-
编写一个完美的equals 方法的建议
-
显式参数命名为otherObject, 稍后需要将它转换成另一个叫做other 的变量
-
检测this 与otherObject 是否引用同一个对象:
if (this = otherObject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
-
检测otherObject 是否为null , 如果为null , 返回false。这项检测是很必要的。
if (otherObject = null ) return false;
-
比较this 与otherObject 是否属于同一个类。如果equals 的语义在每个子类中有所改变,就使用getClass检测:
if (getClass() != otherObject.getCIassO) return false;
-
如果所有的子类都拥有统一的语义,就使用instanceof 检测:
if (!(otherObject instanceof ClassName)) return false;
-
将otherObject 转换为相应的类类型变量:
ClassName other = (ClassName) otherObject
-
现在开始对所有需要比较的域进行比较了。使用= 比较基本类型域,使用equals 比较对象域。如果所有的域都匹配, 就返回true; 否则返回false。
return fieldl == other.field && Objects.equa1s(fie1d2, other.field2 && ...);
-
如果在子类中重新定义equals, 就要在其中包含调用 super.equals(other)
-
-
Object 类的默认hashCode 方法导出的对象存储地址。
-
字符串的散列码是由内容导出的,因此字符串相同的 hashCode 也相同。
int hash = 0; for (int i = 0; i < length0; i++) hash = 31 * hash + charAt(i);
-
如果重新定义equals 方法, 就必须重新定义hashCode 方法, 以便用户可以将对象插人到散列表中
-
Equals 与hashCode 的定义必须一致:如果x.equals(y) 返回 true, 那么x .hashCode( ) 就必须与y.hashCode( ) 具有相同的值。
-
-
建议为自定义的每一个类增加toString 方法。
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13, }; //打印一位数组 Strig s = Arrays.toString(luckyNumbers); //打印多维数组 Strig s = Arrays.deepToString(luckyNumbers);
5.3 泛型数组列表
ArrayList<Object>
-
不必指出数组的大小。
-
使用add 将任意多的元素添加到数组中。
-
使用size() 替代 length 计算元素的数目。
-
使用a.get(i) 替代 a[i] 访问元素。
5.4 对象包装器与自动装箱
-
所有的基本类型都冇一个与之对应的类。
Integer、Long、Float、Double、Short、Byte、Character 、Void 和Boolean ( 前6 个类派生于公共的超类Number)。
注意:
-
对象包装器类是不可变的, 即一旦构造了包装器, 就不允许更改包装在其中的值。
-
对象包装器类还是final , 因此不能定义它们的子类。
-
尖括号中的类型参数不允许是基本类型
警告:
- 由于每个值分别包装在对象中, 所以ArrayList
的效率远远低于int[ ] 数组。因此, 应该用它构造小型集合, 其原因是此时程序员操作的方便性要比执行效率更加重要。
如下:
ArrayList<int> list = new ArrayList<>(); //ERROR ArrayList<Integer> list = new ArrayList<>(); //OK
-
-
自动装箱和拆箱
-
当将一个int对象赋给一个 Integer 值时, 将会自动装箱。
// 编译器将下列语句 list.add(3); // 翻译成 list.add(Integer.value0f(3);
-
当将一个Integer 对象赋给一个int 值时, 将会自动地拆箱。
// 编译器将下列语句 int n = list.get(i); // 翻译成 int n = list.get(i).intValue();
-
由于包装器类引用可以为null , 所以自动装箱有可能会抛出一个 NullPointerException 异常
-
装箱和拆箱是编译器认可的, 而不是虚拟机。编译器在生成类的字节码时, 插人必要的方法调用。虚拟机只是执行这些字节码。
-
5.5 泛型数组列表
参数数量可变的方法
-
printf 方法是这样定义的:
public class PrintStream { public PrintStream printf(String fmt, Object...args) { return format(fmt, args); } }
这里的省略号. . . 是Java 代码的一部分,它表明这个方法可以接收任意数量的对象(除fint参数之外)。
对于printf 的实现者来说,Object… 参数类型与Object[ ]完全一样。
-
允许将一个数组传递给可变参数方法的最后一个参数。
System.out.printf("%d %s", new Object[] { new Integer(1), "widgets" }) ;
5.6 枚举类
枚举类:
- 在比较两个枚举类型的值时, 永远不需要调用equals, 而直接使用 “==” 即可
- 所有的枚举类型都是 Enum 类的子类,其中最有用的一个是 toString, 这个方法能够返回枚举常量名
5.7 反射
-
能够分析类能力的程序称为反射( reflective)
- 在运行时分析类的能力
- 在运行时查看对象, 例如, 编写一个 toString 方法供所有类使用。
- 实现通用的数组操作代码。
- 利用Method 对象, 这个对象很像中的函数指针。
反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者, 而不是应用程序员
-
Class 类
(1)获取方式
-
使用getClass方法将会返回一个Class 类型的实例
Class cl = e.getClass();
-
调用静态方法forName 获得类名对应的Class 对象。
Class cl = Class.forName("java.util.Random");
-
如果T 是任意的Java 类型(或void 关键字),T.class 将代表匹配的类对象。
Class cl = Random.class;
注意:一个Class 对象实际上表示的是一个类型, 而这个类型未必一定是一种类。例如,int 不是类, 但 int.class 是一个Class 类型的对象。
(2)newInstance() 方法,动态地创建一个类的实,可以将将forName 与 newInstance 配合起来使用
String s = "java.util.Random"; Object m = Class.forName(s).newInstance();
注意:如果需要以这种方式向希望按名称创建的类的构造器提供参数, 就不要使用上面那条语句, 而必须使用 Constructor 类中的 newInstance方法。
-
-
java.lang.reflect
三个重要的类:Field、Method 和 Constructor分别用于描述类的域、方法和构造器,这三个类都有一个叫做getName 的方法, 用来返回项目的名称。Feld 类有一个getType 方法, 用来返回描述域所属类型的Class 对象。Method 和Constructor 类有能够报告参数类型的方法, Method 类还有一个可以报告返回类型的方法。这三个类还有一个叫做getModifiers 的方法, 它将返回一个整型值, 用不同的位开关描述public 和static 这样的修饰符使用状况。另外,还可以利用java.lang.reflelt 包中的Modifier类的静态方法分析 getModifiers 返回的整型数值。例如, 可以使用Modifier 类中的isPublic、isPrivate 或isFinal判断方法或构造器是否是public、private 或final。我们需要做的全部工作就是调用Modifier类的相应方法, 并对返回的整型数值进行分析, 另外, 还可以利用Modifier.toString 方法将修饰符打印出来。
Class 类中的 getFields、getMethods 和getConstructors 方法将分别返回类提供的public 域、方法和构造器数组, 其中包括超类的公有成员。Class 类的getDeclareFields、getDeclareMethods 和getDeclaredConstructors 方法将分别返回类中声明的全部域、方法和构造器, 其中包括私有和受保护成员,但不包括超类的成员。
-
AccessibleObject类(void setAccessible(boolean flag)方法,为反射对象设置可访问标志。flag 为true 表明屏蔽Java 语言的访问检查,使得对象的私有属性也可以被査询和设置。)
-
Array 类(允许动态地创建数组)
EmployeeQ a = new Employee[100]: ... // array is full a = Arrays.copyOf(a, 2 * a.length); //通用方法 public static Object badCopyOf(Object[] a, int newLength){ //不使用 ObjectD newArray = new Object[newlength]; System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength)); return newArray; }
Array 类中的静态方法newInstance,它能够构造新数组,在调用它时必须提供两个参数,一个是数组的元素类型,一个是数组的长度。
Object newArray = Array.newInstance(componentType, newLength);
获得新数组的长度: 可以通过调用 Array.getLength(a) 获得数组的长度, 也可以通过Array 类的静态getLength方法的返回值得到任意数组的长度。
获得新数组元素类型:
- 首先获得a 数组的类对象。
- 确认它是一个数组。
- 使用Class 类(只能定义表示数组的类对象)的getComponentType 方法确定数组对应
的类型。
//这个CopyOf 方法可以用来扩展任意类型的数组 //应该将参数声明为Object类,而不是对象型数组(Object[])。整型数组类型 int[]可以被转换成Object,但不能转换成对象数组。 public static Object goodCopyOf(Object a, int newLength) { Class cl = a.getClass(); if (cl.isArray()) { return null; } Class componentType = cl.getComponentType(); int length = Array.getLength(a); Object newArray = Array.newInstance(componentType, newLength); System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength)); return newArray; }
-
-
**Method **
(1)在Method 类中有一个invoke 方法,它允许调用包装在当前Method 对象中的方法。invoke 方法的签名是:
Object invoke(Object obj, Object... args)
(2)获取Method方法
-
通过调用getDeclareMethods 方法, 然后对返回的Method 对象数组进行查找, 直到发现想要的方法为止
-
调用Class 类中的getMethod 方法得到想要的方法(必须提供想要的方法的参数类型)。函数签名为:
Method getMethod(String name, Class... parameterTypes)
-
例子:
Method ml = Employee.class.getMethod("getName"); Method m2 = Employee.class.getMethod("raiseSalary", double.class);
(3)invoke 的参数和返回值是Object 类型的,因此必须进行多次的类型转化。使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。(仅在必要的时候才使用Method 对象)
-
5.8 继承的设计技巧
继承的设计技巧:
-
将公共操作和域放在超类
-
不要使用受保护的域(protected 方法对于指示那些不提供一般用途而应在子类中重新定义的方法很有用 )
-
使用继承实现 “is-a” 关系
-
除非所有继承的方法都有意义, 否则不要使用继承
-
在覆盖方法时, 不要改变预期的行为
-
使用多态(不同数据类型的实体提供统一的接口), 而非类型信息
-
不要过多地使用反射(编译器很难帮助人们发现程序中的错误, 因此只有在运行时才发现错误并导致异常。)
API注释
5.2.2 相等测试与继承
java.util.Arrays 1.2
static Boolean equals(type[] a , type[] b) 5.0
// 如果两个数组长度相同, 并且在对应的位置上数据元素也均相同, 将返回true。数组的元素类型可以是Object、int、long、short、char、byte、boolean、float 或double。
java.util.Objects 7
static boolean equals(Object a, Object b)
// 如果a 和b 都为null, 返回true ; 如果只有其中之一为null, 则返回false ; 否则返回a.equals(b)
5.2.3 hashCode 方法
java.util.Object 1.0
int hashCode( )
// 返回对象的散列码。散列码可以是任意的整数, 包括正数或负数。两个相等的对象要求返回相等的散列码。
java.util.Objects 7
static int hash(Object... objects )
// 返回一个散列码,由提供的所有对象的散列码组合而得到。
static int hashCode(Object a )
// 如果a 为null 返回0, 否则返回a.hashCode()
java.lang.(Integer|Long|Short|Byte|Double|Float|Character|Boolean) 1.0
static int hashCode((int|long|short|byte|double|float|char|boolean) value) 8
// 返回给定值的散列码。
java.utii.Arrays 1.2
static int hashCode(type[] a) 5.0
// 计算数组a的散列码。组成这个数组的元素类型可以是 objec,t in,t long, short, char, byte, boolean, float 或double。
5.2.4 toString 方法
java.lang.Object 1.0
Class getClass( )
// 返回包含对象信息的类对象。稍后会看到Java 提供了类运行时的描述, 它的内容被封装在Class 类中。
boolean equals(Object otherObject )
// 比较两个对象是否相等, 如果两个对象指向同一块存储区域, 方法返回true ; 否则方法返回false。在自定义的类中, 应该覆盖这个方法。
String toString( )
// 返冋描述该对象值的字符串。在自定义的类中, 应该覆盖这个方法。
java.lang.Class 1.0
String getName( )
// 返回这个类的名字。
Class getSuperclass( )
// 以Class 对象的形式返回这个类的超类信息。
5. 3 泛型数组列表
java.util.ArrayList
ArrayList<E>()
// 构造一个空数组列表。
ArrayList<E>(int initialCapacity)
// 用指定容量构造一个空数组列表。
// 参数:initalCapacity数组列表的最初容量
boolean add(E obj)
// 在数组列表的尾端添加一个元素。永远返回true。
// 参数:obj添加的元素
int size()
// 返回存储在数组列表中的当前元素数量。(这个值将小于或等于数组列表的容量。)
void ensureCapacity(int capacity)
// 确保数组列表在不重新分配存储空间的情况下就能够保存给定数量的元素。
// 参数:capacity需要的存储容量
void trimToSize()
// 将数组列表的存储容量削减到当前尺寸。
5.3.1 访问数组列表元素
java.util.ArrayList
void set(int index,E obj)
// 设置数组列表指定位置的元素值, 这个操作将覆盖这个位置的原有内容。
// 第5章? 承1 8 3
// 参数: index 位置(必须介于0 ~ size()-l 之间)
// obj 新的值
E get(int index)
// 获得指定位置的元素值。
// 参数: index 获得的元素位置(必须介于0 ~ size()-l 之间)
void add(int index,E obj)
// 向后移动元素, 以便插入元素。
// 参数: index 插入位置(必须介于0 〜size()-l 之间)
// obj 新兀素
E removed(int index)
// 删除一个元素,并将后面的元素向前移动。被删除的元素由返回值返回。
// 参数:index 被删除的元素位置(必须介于0 〜size()-l 之间)
5.4 对象包装器与自动装箱
java.lang.Integer 1.0
int intValue( )
// 以int 的形式返回Integer 对象的值(在Number 类中覆盖了intValue 方法)。
static String toString(int i )
// 以一个新String 对象的形式返回给定数值i 的十进制表示。
// 參static String toString(int i ,int radix)
// 返回数值i 的基于给定radix 参数进制的表示。
static int parseInt(String s)
static int parseInt(String s,int radix)
// 返回字符串s 表示的整型数值, 给定字符串表示的是十进制的整数(第一种方法),或者是radix 参数进的整数(第二种方法)。
static Integer valueOf(String s)
static Integer valueOf(String s, int radix)
// 返回用s 表示的整型数值进行初始化后的一个新Integer 对象, 给定字符串表示的是十进制的整数(第一种方法),或者是radix 参数进制的整数(第二种方法)。
java.text.NumberFormat 1.1
Number parse(String s)
// 返回数字值,假设给定的String 表示了一个数值。
5.6 枚举类
java.Iang.Enum
static Enum valueOf(Class enumClass, String name )
// 返回指定名字、给定类的枚举常量。
String toString()
// 返回枚举常量名。
int ordinal()
// 返回枚举常量在enum 声明中的位置,位置从0 开始计数。
int compareTo(E other )
// 如果枚举常量出现在Other 之前, 则返回一个负值;如果this=other,则返回0; 否则,返回正值。枚举常量的出现次序在enum 声明中给出。
5.7.2 捕获异常
java.lang.Ciass 1.0
static Class forName(String className)
// 返回描述类名为className 的Class 对象。
Object newInstance()
// 返回这个类的一个新实例。
java.Iang.reflect.Constructor 1.1
Object newInstance(Object[] args)
// 构造一个这个构造器所属类的新实例。
// 参数:args 这是提供给构造器的参数。有关如何提供参数的详细情况请参看5.7.6节的论述。
java.Iang.Throwable 1.0
void printStackTrace()
// 将Throwable 对象和栈的轨迹输出到标准错误流。
5.7.3 利用反射分析类的能力
java.lang.Class 1.0
Field[] getFields() 1.1
Field[] getDeclaredFie1ds() 1.1
// getFields 方法将返回一个包含Field 对象的数组, 这些对象记录了这个类或其超类的公有域。getDeclaredField 方法也将返回包含Field 对象的数组, 这些对象记录了这个类的全部域。如果类中没有域, 或者Class 对象描述的是基本类型或数组类型, 这些方法将返回一个长度为0 的数组。
Method[] getMethods() 1.1
Method[] getDeclareMethods() 1.1
// 返回包含Method 对象的数组: getMethods 将返回所有的公有方法, 包括从超类继承来的公有方法;getDeclaredMethods 返回这个类或接口的全部方法, 但不包括由超类继承了的方法。
Constructor[] getConstructors() 1.1
Constructor[] getDeclaredConstructors() 1.1
// 返回包含Constructor 对象的数组, 其中包含了Class 对象所描述的类的所有公有构造器(getConstructors ) 或所有构造器(getDeclaredConstructors)。
java.lang.reflect.Constructor 1.1
Class getDeclaringClass()
// 返冋一个用于描述类中定义的构造器、方法或域的Class 对象。
Class[] getExceptionTypes() ( 在Constructor 和Method 类中)
// 返回一个用于描述方法抛出的异常类型的Class 对象数组。
int getModifiers()
// 返回一个用于描述构造器、方法或域的修饰符的整型数值。使用Modifier 类中的这个方法可以分析这个返回值。
String getName()
// 返冋一个用于描述构造器、方法或域名的字符串。
Class[] getParameterTypes() ( 在Constructor 和Method 类中)
// 返回一个用于描述参数类型的Class 对象数组。
Class getReturnType() ( 在Method 类中)
// 返回一个用于描述返H类型的Class 对象。
java.lang.reflect.Modifier 1.1
static String toString(int modifiers)
// 返回对应modifiers 中位设置的修饰符的字符串表7K。
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 isProtected(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)
// 这些方法将检测方法名中对应的修饰符在modffiers 值中的位r
5.7.4 在运行时使用反射分析对象
java.Iang.reflect.AccessibleObject 1.2
void setAccessible(boolean flag)
// 为反射对象设置可访问标志。flag 为true 表明屏蔽Java 语言的访问检查,使得对象的私有属性也可以被査询和设置。
boolean isAccessible( )
// 返回反射对象的可访问标志的值。
static void setAccessible(AccessibleObject[] array, boolean flag)
// 是一种设置对象数组可访问标志的快捷方法。
java.lang.Class 1.1
Field getField(String name)
Field[] getField()
// 返回指定名称的公有域, 或包含所有域的数组
Field getDeclaredField(String name)
Field[] getDeclaredFields()
// 返回类中声明的给定名称的域, 或者包含声明的全部域的数组。
java.Iang.reflect.Field 1.1
Object get(Object obj)
// 返回obj 对象中用Field 对象表示的域值。
void set(Object obj, Object newValue)
// 用一个新值设置Obj 对象中Field 对象表示的域。
5.7.5 使用反射编写泛型数组代码
java.lang.reflect.Array 1.1
static Object get(Object array,int index)
static xxx getXxx(Object array,int index)
// ( xxx 是boolean、byte、char、double、float、int、long、short 之中的一种基本类M。)
// 这些方法将返回存储在给定位置上的给定数组的内容。
static void set(Object array,int index,Object newValue)
static void setXxx(Object array,int index,xxx newValue)
// ( xxx 是boolean、byte、char、double、float、int、long、short 之中的一种基本类型。)
// 这些方法将一个新值存储到给定位置上的给定数组中。
static int getLength(Object array)
// 返回数组的长度。
static Object newInstance(Class componentType,int length)
static Object newInstance(Class componentType,int[]lengths)
// 返回一个具有给定类型、给定维数的新数组。
5.7.6 调用任意方法
java.Iang.reflect.Method 1.1
public Object invoke(Object implicitParameter, Object[] explicitParamenters)
// 调用这个对象所描述的方法, 传递给定参数,并返回方法的返回值。对于静态方法,把null 作为隐式参数传递。在使用包装器传递基本类型的值时, 基本类型的返回值必须是未包装的。
评论区