public Manager(String name, double salary,int year, int month, int day){
super(name,salary,year,month,day);//调用超类中含有name,salary,year,month和day参数得构造器
bonus = 0;
}
super(xxx);
表示调用超类中含有xxx参数的构造器
由于子类得构造器不能访问超类的私有域,所以必须利用超类的构造器对这部分私有域进行初始化,可以通过super实现对超类构造器的调用。使用super调用构造器的语句必须是子类构造器的第一条语句。如果子类的构造器没有显示地调用超类的构造器,则将自动地调用超类默认(没有参数)的构造器。
this:(1)引用隐式参数;(2)调用该类其他的构造器
super:(1)调用超类的方法;(2)调用超类的构造器
一个对象变量可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamic binding)。
在Java中,动态绑定是默认的处理方式。
“is-a”规则的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。
//例如,可以将一个子类的对象赋给超类变量。
Employee e;
e = new Employee(. . .);//Employee object expected
e = new Manager(. . .);// OK,Manager can be used as well
在Java程序设计语言中,对象变量是多态的。
//从程序清单5-1中,已经看到了置换法则的优点:
Manager boss = new Manager(. . .);
Employee[] staff = new Employee[3];
staff[O]= boss;
//在这个例子中,变量staff[O]与boss引用同一个对象。但编译器将staff[0]看成Employee对象。
//这意味着,可以这样调用
boss.setBonus(5000);// OK
//但不能这样调用
staff[0].setBonus(5000);// Error
//这是因为staff[0]声明的类型是Employee,而setBonus不是Employee类的方法。
不能将一个超类的引用赋给子类变量。
Manager m = staff[i]; //Error,不是所有的雇员都是经理
在Java中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。
方法调用的过程
如果在子类中定义了一个与超类签名相同的方法,那么子类中的这个方法就覆盖了超类中的这个相同签名的方法。
允许子类将覆盖方法的返回类型定义为原返回类型的子类型。
//假设Employee类有 public Employee getBuddy(){...} //经理不会想找这种地位低下的员工。为了反映这一点,在后面的子类Manager中, //可以按照如下所示的方式覆盖这个方法 public Manager getBuddy() {...}// OK to change return type
动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展。
在覆盖一个方法的时候,子类方法不能低于超类方法的可见性。
正像有时候需要将浮点型数值转换成整型数值一样,有时候也可能需要将某个类的对象引用转换成另外一个类的对象引用。
进行类型转换的唯一原因是:在暂时忽视对象的实际类型之后,使用对象的全部功能。
将一个子类的引用赋给一个超类变量,编译器是允许的。但将一个超类的引用赋给一个子类变量,必须进行类型转换。
instanceof 操作符:
只能在继承层次内进行类型转换。
在一般情况下,应该尽量少用类型转换和instanceof运算符。
Person p = new Student("Jacky", "CS");
Java语言规范要求equals方法具有下面的特性:
如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。
如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。
在标准Java库中包含150多个equals方法的实现,包括使用instanceof检测、调用getClass检测、捕获ClassCastException或者什么也不做。
下面给出编写一个完美的equals方法的建议:
if (this == other0bject) return true;
这条语句只是一个优化。实际上,这是一种经常采用的形式。因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多。
if (other0bject = null) return false;
if (getClass() != other0bject.getClass0) return false;
如果所有的子类都拥有统一的语义, 就使用instanceof检测:
if (! (other0bject instanceof ClassName)) return false;
ClassName other = (className) other0bject;
return field1 = other. field1
&& 0bjects. equals(field2, other.field2)
&& ...;
如果在子类中重新定义equals,就要在其中包含调用super.equals(other)。
对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。如果两个数组长度相同,并且在对应的位置上数据元素也均相同,将返回true。
为了避免发生类型错误,可以使用@Override对覆盖超类的方法进行标记:
@Override public boolean equals(0bject other)
散列码(hashcode)是由对象导出的一个整型值。
如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中(有关散列表的内容将在第9章中讨论)。
hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。
//例如,下面是Employee类的hashCode方法。
public class Employee
{
public int hashCod(){
return 7 * name.hashCode()
+11 * new Double(salary).hashCode()
+13* hireDay.hashCode();
}
}
不过,还可以做得更好。首先,最好使用null安全的方法Objects.hashCode。如果其参数为null,这个方法会返回0,否则返回对参数调用hashCode的结果。
另外,使用静态方法Double.hashCode来避免创建Double对象。
public int hashCode(){
return 7 *0bjects.hashCode(name)
+11* Doub1e.hashCode(salary)
+13 * 0bjects.hashCode(hireDay);
}
还有更好的做法,需要组合多个散列值时,可以调用Objects.hash并提供多个参数。
public int hashCode(){
return 0bjects.hash(name,salary,hireDay);
}
Equals 与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode( )就必须与y.hashCode()具有相同的值。
如果存在数组类型的域,那么可以使用静态的Arrays.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。
返回表示对象值的字符串。
绝大多数(但不是全部)的toString方法都遵循这样的格式:类的名字,随后是一对方括号括起来的阈值。
实际上,还可以设计得更好一些。最好通过调用getClass().getName( )获得类名的字符串,而不要将类名硬加到toString方法中。
public String toString(){
return getC1ass().getName()
+"[name=" +name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+"]";
}
toString方法也可以供子类调用
如果超类使用了getClass( ).getName( ),那么子类只要调用super.toString( )就可以了。
随处可见toString方法的主要原因是:只要对象与一个字符串通过操作符“+”连接起来,Java编译就会自动地调用toString方法,以便获得这个对象的字符串描述。
在调用x.toString( )的地方可以用""+x 替代。
Object 类定义了toString方法,用来打印输出对象所属的类名和散列码。
System.out.println(System.out);
//将输出下列内容(PrintStream类的设计者没有覆盖toString方法)
java.io.PrintStream@2f6684
数组继承了object类的toString方法,数组类型将按照旧的格式打印
Arrays.toString
。代码:String s = Arrays.toString(luckyNumbers);
//将生成字符串“[2,3,5,7,11,13]”。
Arrays.deepToString
方法。强烈建议为自定义的每一个类增加toString方法。这样做不仅自已受益,而且所有使用这个类的程序员也会从这个日志记录支持中受益匪浅。
java.lang.Class.getName()
返回这个类的名字
java.lang.Class.getSuperClass()
以Class对象的形式返回这个类的超类信息。
ArrayList,它使用起来有点像数组,但在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。
ArrayList 是一个采用类型参数(type parameter)的泛型类(generic class)。为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面.
ArrayList<Employee> staff = new ArrayList<>();
这被称为“菱“形语法。
尖括号中的类型参数不允许是基本类型
在Java SE5.0以后的版本中,没有后缀<.. .>仍然可以使用ArrayList,它将被认为是一个删去了类型参数的“原始”类型。
使用add方法可以将元素添加到数组列表中。
数组列表管理着对象引用的一个内部数组。最终,数组的全部空间有可能被用尽。这就显现出数组列表的操作魅力:如果调用add且内部数组已经满了,数组列表就将自动地创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中。
如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用ensureCapacity方法:
staff. ensureCapacity(100);
另外,还可以把初始容量传递给ArrayList 构造器:
ArrayList<Employee> staff = new ArrayList<>(100);
数组列表的容量与数组的大小有一个非常重要的区别。
size方法将返回数组列表中包含的实际元素数目。
一旦能够确认数组列表的大小不再发生变化,就可以调用trimToSize方法。
数组列表自动扩展容量的便利增加了访问元素语法的复杂程度。
原始的ArrayList存在一定的危险性。它的add和set方法允许接受任意类型的对象(即返回Object对象,需要进行类型转换)。
下面这个技巧可以一举两得,既可以灵活地扩展数组,又可以方便地访问数组元素。
Arraylist<X> list = new ArrayList<>0;
while (...)
{
x=.. .;
list.add(x);
}
X[] a= new X[list.size()];
list.toArray(a);
除了在数组列表的尾部追加元素之外,还可以在数组列表的中间插入元素,使用带索引参数的add方法。
Employee e = staff. remove(n);
如果数组存储的元素数比较多,又经常需要在中间位置插入、删除元素,就应该考虑使用链表。
可以使用”for each“循环遍历数组列表。
@Suppress Warnings( "unchecked")
标注来标记这个变量能够接受类型转换,如下所示:@suppressWarnings("unchecked") Arraylist<Employee> result =
(Arraylist<Employee>) employeeDB. find(query); // yields another warning
包装器(wrapper):需要将int这样的基本类型转换为对象。
由于每个值分别包装在对象中,所以ArrayList
自动装箱(autoboxing)
由于包装器类引用可以为null,所以自动装箱有可能会抛出一个NullPointerException异常。
另外,如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会拆箱,提升为double,再装箱为Double。
最后强调一下,装箱和拆箱是编译器认可的,而不是虚拟机。编译器在生成类的字节码时,插入必要的方法调用。虚拟机只是执行这些字节码。
两个包装器对象比较时调用equals方法。
自动装箱规范要求boolean、byte、char≤127,介于-128~127之间的short和int被包装到固定的对象中。
可以将某些基本方法放置在包装器中。如Integer.parseInt()
Integer 对象是不可变的:包含在包装器中的内容不会改变。不能使用这些包装器类创建修改数值参数的方法。
printf方法是这样定义的:
public class PrintStream
public PrintStream printf(String fmt, 0bject... args)
{ return format(fmt, args); }
用户自己也可以定义可变参数的方法,并将参数指定为任意类型,甚至是基本类型。
允许将一个数组传递给可变参数方法的最后一个参数。(!!!)
public enum Size { SMALL, MEDIUM, LARCE, EXTRA_ LARGE };
所有的枚举类型都是Enum类的子类。它们继承了这个类的许多方法。
toString()方法:返回枚举常量名。
Size.SMALL.toString(); //返回字符串”SMALL“
逆方法是静态方法valueOf()
Size s = Enum.valueOf(Size.class, "SMALL"); //将s设置成Size.SMALL
每个枚举类型都有一个静态的values方法,它将返回一个包含全部枚举值的数组。
ordinal方法返回enum声明中枚举常量的位置,位置从0开始计数。例如: Size. MEDIUM. ordinal()
返回1。
compareTo(E other)
如果枚举常量出现在other之前,则返回一个负值;如果this==other,则返回0;否则,返回正值。枚举常量的出现次序在enum声明中给出。
public class EnumTest
{
public static void main(String[]args){
Scanner in = new Scanner(System.in);
System.out.print("Enter a size: (SMALL,MEDIUM,LARCE, EXTRA_LARCE)");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class,input);
System.out.print1n("size=" + size);
System.out.print1n("abbreviation=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE)
System.out.println("Good job--you paid attention to the _.");
}
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;
}
原文:https://www.cnblogs.com/nojacky/p/13906501.html