超类(superclass)和子类(subclass),
基类(base class)和派生类(derived class),
父类(parent class)和孩子类(child class)
在 Java 中,所有的继承都是公有继承。
this
是当前对象的引用
super
不是一个对象的引用,不能将super
赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字。
我们可以通过 super 实现对超类构造器的调用。使用 super 调用构造器的语句必须是子类构造器的第一条语句。
如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。如果超类没有不带参数的构造器,并且在子类的构造器中又没有显式地调用超类的其他构造器,则 Java 编译器将报告错误。
继承层次
与继承链
。
Java 不支持多继承。
一个对象变量可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamic binding)
在 Java 中,不需要将方法声明为虚函数(C++)。动态绑定是默认的处理方式。如果不希望让一个方法具有虚拟特征,可以将它标记为 final 。
在覆盖方法时,一定要保证返回类型的兼容性。允许子类将覆盖方法的返回类型定义为原返回类型的子类型。(协变返回类型)
重载可以有不同的返回类型。
在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。
虚拟机预先为每个类创建了一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。
private 方法、static 方法、final 方法或者构造器,是静态绑定
详见P155
final 类不允许extends,final 方法不允许Override;
final 类中的所有方法自动称为 final 方法,而域不会。
如果方法很简短、被频繁调用且没有被覆盖,那么即时编译器就会将这个方法进行内联处理。
例如,内联调用 e.getName() 将被替换为访问 e.name域。
如果虚拟机加载了另外一个子类,而在这个子类中包含了对内联方法的覆盖,那么优化器将取消对覆盖方法的内联。这个过程很慢,不过很少发生。
如果试图在继承链上进行向下的类型转换,并且“谎报”有关对象包含的内容,运行这个程序时,Java runtime system 将报告这个错误,并产生一个 ClassCastException 异常。
Manager boss = (Manager)staff[1]; // Error
// 在进行类型转换之前,先查看一下是否能够成功地转换
if (staff[1] instanceof Manager)
{
boss = (Manager)staff[1];
}
// 将会产生编译错误,这是因为 String 不是 Employee 的子类
String c = (String)staff[1];
人们希望超类中的某些方法允许被子类访问,或允许子类的方法访问超类的某个域。
为此,需要将这些方法或域声明为 protected。
例如,如果将超类 Employee 中的 hireDay 声明为proteced
,而不是私有的,Manager 中的方法就可以直接地访问它。
不过,Manager 类中的方法只能够访问 Manager 对象中的 hireDay 域,而不能访问其他 Employee 对象中的这个域 。
事实上,Java 中的受保护部分对所有子类及同一个包中的所有其他类都可见。
包访问级别(package-private):没有访问修饰符的类的访问级别是包级,其public方法也会变成包级。
Java 用于控制可见性的 4 个访问修饰符:
在 Java 中,只有基本类型不是对象。
所有的数组类型,不管是对象数组还是基本类型的数组都扩展了 Object 类。
在 Object 类中,这个方法将判断两个对象是否具有相同的引用。
如果两个参数都为 null,Objects.equals(a, b) 调用将返回 true;
如果其中一个参数为 null,则返回 false;
否则,如果两个参数都不为 null,则调用 a.equals(b)。
在子类中定义 equals 方法时,首先调用超类的 equals。如果检测失败,对象就不可能相等。
如果超类中的域都相等,就需要比较子类中的实例域。
getClass()
or instanceOf
?
显式参数命名为 otherObject , 稍后需要将它转换成另一个叫做 other 的变量 。
检测 this 与 otherObject 是否引用同一个对象:
if (this == otherObject) return true;
检测 otherObject 是否为 null , 如果为 null , 返回 false:
if (otherObject == null) return false;
比较 this 与 otherObject 是否属于同一个类
将 otherObject 转换为相应的类类型变量:
ClassName other = (ClassName) otherObject;
现在开始对所有需要比较的域进行比较了。使用 == 比较基本类型域,使用 equals 比较对象域。
如果所有的域都匹配,就返回true;否则返回 false。
return field1 == other.field1
&& Objects.equals(field2, other.field2)
&& . . .;
如果在子类中重新定义 equals,就要在其中包含调用 super.equals(other)。
Arrays.equals
方法检测相应的数组元素是否相等。Object 类中的 hashCode 方法,每个对象都有一个默认的散列码,其值为对象的存储地址。
如果重新定义 equals 方法,就应该重新定义 hashCode 方法;
equals 与 hashCode 的定义必须一致:如果 x.equals(y) 返回 true,那么 x.hashCode() 就必须与 y.hashCode() 具有相同的值。
Objects.hashCode
,如果其参数为 null,这个方法会返回 0,否则返回对参数调用 hashCode 的结果;
需要组合多个散列值时,可以调用Objects.hash
并提供多个参数。这个方法会对各个参数调用 Objects.hashCode,并组合这些散列值。
public int hashCode()
{
return 7 * Objects.hashCode(name)
+ 11 * Double.hashCode(salary)
+ 13 * Objects.hashCode(hireDay);
}
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
可以使用静态方法 Double.hashCode 来避免创建 Double 对象;
如果存在数组类型的域,那么可以使用静态的 Arrays.hashCode 方法计算一个散列码,这个散列码由数组元素的散列码组成。
Object 类定义了 toString 方法,用来打印输出对象所属的类名和散列码。
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
String s = "" + luckyNumbers; // s = "[I@1a46e30", [I 表示类型为整型数组
String s = Arrays.toString(luckyNumbers); // s = "[2, 3, 5, 7, 11, 13]"
① class A extends B;
② B.toString(){ getClass().getName(); }
③ A.toString(){ super.toString() + "[this is A]"; }
调用 A.toString 时调用的 “getClass.getName” 会是 A 而不是 B
对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。
同时,对象包装器类还是 final,因此不能定义它们的子类 。
Integer 对象是不可变的:包含在包装器中的内容不会改变。这些包装器类不能用来实现修改数值参数的方法(像 C++ 的引用那样)。
如果想编写一个修改数值参数值的方法,就需要使用在 org.omg.CORBA 包中定义的持有者(holder)类型,
包括 IntHolder、 BooleanHolder 等。每个持有者类型都包含一个公有域值,通过它可以访问存储在其中的值。
public static void triple(IntHolder x)
{
x.value = 3 * x.value;
}
java.lang.Integer
/// API
int value();
static String toString(int i);
static String toString(int i, int radix);
static int parseInt(String s);
static int parseInt(String s, int radix);
static Integer valueOf(String s);
static Integer valueOf(String s, int radix);
java.text.NumberFormat
///API
Number parse(String s);
参数里Object[] args
和Object... args
等价。
System.out.printf("%d %s", new Object[] { new Integer(n), "widgets" } );
public static double max(double... values){}
public static void main(String... args){}
例如调用double m = max(3.1, 40.4, -5);
,编译器会传递new double[] { 3.1, 40.4, -5 }
进去。
public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE };
/// 添加构造器、方法和域
public enum Size {
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation) {
this.abbreviation = abbreviation;
}
public String getAbbreviation() {
return abbreviation;
}
private String abbreviation;
}
主要是一些类及其API
java.lang.Class
/// API
static Class forName(String className);
Object newInstance();
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Constructor
/// API
Object newInstance(Object[] args);
java.lang.reflect.Modifier
使用java.lang.reflect.Array
来实现通用的数组拷贝
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;
}
使用java.lang.reflect.Method
调用任意方法:
public Object invoke(Object implicitParameter, Object[] explicitParameters)
建议仅在必要的时候才使用 Method 对象。
特别要重申:建议 Java 开发者不要使用 Method 对象的回调功能。使用接口进行回调会使得代码的执行速度更快,更易于维护。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章