- Object类:所有类的超类。
- 在Java中,只有基本类型不是对象。
- equals方法:在Object类中,这个方法判断两个对象是否有相同的引用。
- Objects.equals(a,b)方法。注:若a ,b 都为null,Objects.equals(a,b) 将返回true ;若只有其中之一为null,则返回null ;否则,返回 a.equals(b) 。
- Java要求equals方法具有以下特性:
- 自反性 :x.equals(x) == true 。
- 对称性 : x.equals(y) ↔ y.equals(x) 。
- 传递性: 若a equals b, b equals c,那么可知 a equals c 。
- 一致性:反复调用结果一致。
- 对于任意 x.equals(null) 都应返回false 。
- 编写equals方法的建议:
- 显示参数命名为 otherObject ,后续转换成另一个名为 other 的变量。
- 检测 this 与 otherObject 是否引用同一对象。
- 检测 otherOject是否为 null 。
- 比较 this 与 otherObject 是否属于同一个类。若子类有专属自己的equals, 那么使用 getClass 检测;否则,使用 instanceof 检测。
- 将 otherObject 转换为 相应的 类类型变量。
- 将需要比较的域一一进行比较。
- 如果在子类中重新定义equals,就要在其中包含super.equals(other) 。
- 为了避免发生类型错误,可以使用 @Override 对覆盖超类的方法进行标记。
- hashCode方法:
- 散列码(hash code)是由对象导出的一个整型值。
- hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。
- 如果重新定义equals方法,就必须重新定义hashCode方法。若x.equals(y),那么 x.hashCode()的值必须与 y.hashCode()的值相等。
- java.util.Objects:
- static int hash( Object... objects ) 返回一个散列码,由提供的所有对象的散列码的组合而得到。
- static int hashCode( Object a ) 若a为null则返回0,否则返回 a.hashCode()
- toString方法:
- 绝大多数的toString方法都遵循这样的格式:类的名字,随后是一对方括号括起来的域值。
- 在调用 x.toString() 方法的地方可以用 "" + x 替代。
- Object类定义了 toString 方法,用来打印输出对象所属的类名和散列码。
- 数组继承了object类的 toString 方法,如果需要打印数组,可以使用 Arrays.toString 方法。若需要打印多维数组,则需要调用 Arrays.deepToString 方法。
- java.lang.Class:
- String getName()
- Class getSuperClass()
- 示例程序:(实现 Employee类 和 Manager类 的 equals方法、hashCode方法 和 toString方法)
package coreJava_5_8;
public class Test5_8 {
public static void main( String args[] ) {
Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);
System.out.println("alice1 == alice2: " + (alice1 == alice2) );
System.out.println("alice1 == alice3: " + (alice1 == alice3) );
System.out.println("alice1.equals(alice3): " + (alice1.equals(alice3)) );
System.out.println("alice1.equals(bob): " + (alice1.equals(bob)) );
System.out.println("bob.toSting(): " + bob );
Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15 );
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15 );
boss.setBonus(5000);
System.out.println("boss.toString(): " + boss );
System.out.println( "boss.equals(carl): " + (boss.equals(carl)) );
System.out.println("alice1.hashCode(): " + alice1.hashCode() );
System.out.println("alice3.hashCode(): " + alice3.hashCode() );
System.out.println("bob.hashCode(): " + bob.hashCode() );
System.out.println("carl.hashCode(): " + carl.hashCode() );
System.out.println("boss.hashCode(): " + boss.hashCode() );
}
}
package coreJava_5_8;
import java.time.*;
import java.util.Objects;
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee( String name, double salary, int year, int month, int day ) {
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public boolean equals( Object otherObject ) {
if( this == otherObject ) return true;
if( otherObject == null ) return false;
if( getClass() != otherObject.getClass() ) return false;
Employee other = (Employee) otherObject;
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
}
public int hashCode() {
return Objects.hash(name, salary, hireDay);
}
public String toString() {
return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}
}
package coreJava_5_8;
public class Manager extends Employee {
private double bonus;
public Manager( String name, double salary, int year, int month, int day ) {
super(name,salary,year,month,day);
bonus = 0;
}
public void setBonus( double bonus ) {
this.bonus = bonus;
}
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public boolean equals( Object otherObject ) {
if( !super.equals(otherObject) ) return false;
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
public int hashCode() {
return super.hashCode() + 17*Double.hashCode(bonus);
}
public String toString() {
return super.toString() + "[bonus=" + bonus + "]";
}
}
- 泛型数组列表:
- ArrayList是一个采用 类型参数 的泛型类。ArrayList<Employee> staff = new ArrayList<Employee>()
- ArrayList 类似于C++的 vector模板,但 ArrayList没有重载 []运算符。
- 可以用for each循环处理ArrayList。
- java.util.ArrayList<E>
- ArrayList<E>() //构造一个空数组列表
- ArrayList<E>(int initialCapacity) //initialCapacity 数组列表的最初容量
- boolean add(E obj) //在数组列表的尾端添加一个元素。永远返回true。
- int size()
- void ensureCapacity(int capacity) //确保数组列表在不重新分配存储空间的情况下就能够保存给定数量的元素。
- void trimToSize() //将数组列表的存储容量削减到当前的尺寸
- void set(int index, E obj)
- E get(int index)
- void add(int index, E obj)
- E remove(int index)
- 可以将一个 类型化数组列表 传递给 “原始的”数组列表 。但是将 “原始的”数组列表赋值给 类型化数组列表 会得到警告。使用类型转换也不能避免。若确保这些警告不会造成严重的后果,可以用 @SuppressWarnings("unchecked") 来标记这个变量可以接受类型转换。
- @SuppressWarnings("unchecked") ArrayList<Employee> result = (ArrayList<Employee>) employeeDB.find(query)
- 示例程序:(测试ArrayList,用ArrayList存储几个员工对象,并打印所有人的信息)
package coreJava_5_11;
import java.util.*;
public class Test5_11 {
public static void main( String args[] ) {
ArrayList<Employee> staff = new ArrayList<Employee>();
staff.add( new Employee( "Damon", 65000, 1999, 10, 1 ) );
staff.add( new Employee( "Stefan", 55000, 1998, 5, 13 ) );
staff.add( new Employee( "Cathrine", 40000, 2002, 4, 12 ) );
for( Employee e : staff )
e.raiseSalary(10);
for( Employee e : staff )
System.out.println(e);
}
}
package coreJava_5_11;
import java.time.LocalDate;
import java.util.Objects;
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee( String name, double salary, int year, int month, int day ) {
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDay() {
return hireDay;
}
public void raiseSalary( double byPercent ) {
double raise = salary * byPercent / 100;
salary += raise;
}
public boolean equals( Object otherObject ) {
if( this == otherObject ) return true;
if( otherObject == null ) return false;
if( getClass() != otherObject.getClass() ) return false;
Employee other = (Employee) otherObject;
return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
}
public int hashCode() {
return Objects.hash(name, salary, hireDay);
}
public String toString() {
return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}
}
- 对象包装器与自动装箱:
- 所有的基本类型都有一个与之对应的类。通常,这些类被称为 包装器(wrapper)。
- Integer、Long、Float、Double、Short、Byte,这六个类都派生于公共的超类Number。
- Character 、 Boolean 、Void。
- 对象包装器类是不可变的,同时它们还是final的。
- <...>中的类型参数不能使用基本类型,就需要借助包装器。
- 自动装箱。如:list.add(3) → list.add(Integer.valueOf(3))
- 自动拆箱。如:int n = list.get(i) → int n = list.get(i).intValue()
- 自动装箱规范要求 boolean 、byte 、char <= 127, 介于 -128 ~ 127 的 short 和 int 被包装到固定的对象中。
- 比较两个包装器对象一定要使用 equals方法 而不是 == 。
- 装箱和拆箱是编译器认可的,虚拟机只是执行对应的字节码。
- Java.lang.Integer:
- int intValue()
- 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)
- 参数数量可变的方法:
- 如: public PrintStream printf( String fmt, Object... args ) → public PrintStream printf(String fmt, Object[] args )
- 允许将一个数组传递给可变参数方法的最后一个参数。
- 枚举类:
- 所有枚举类都是 Enum类 的子类。
- 若需要,可以在枚举类型中添加一些构造器、方法和域。构造器只是在构造枚举常量的时候调用。
- java.lang.Enum<E>:
- static Enum valueOf( Class enumClass, String name )
- String toString()
- int ordinal()
- int compareTo( E other )
- 示例程序:(测试枚举类)
package coreJava_5_12;
import java.util.*;
public class EnumTest {
public static void main( String args[] ) {
Scanner in = new Scanner( System.in );
System.out.println("Enter a size: (SMALL, MEDIUM, LARGE, EXTRA_LARGE)");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);
System.out.println( "size=" + size );
System.out.println( "abbreviation=" + size.getAbbreviation() );
in.close();
}
}
enum Size{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private String abbreviation;
private Size(String abbreviation ) {
this.abbreviation = abbreviation;
}
public String getAbbreviation() { return abbreviation; }
}
- 继承的设计技巧:
- 将公共操作和域放在超类
- 不要使用受保护的域
- 破坏封装性
- 同包中一样可见
- 使用继承实现 “is-a” 关系,不滥用,考虑清楚是不是真的是 一般与特殊 的关系。
- 除非所有继承的方法都有意义,否则不要使用继承。
- 在覆盖方法时,不要改变预期的行为。
- 使用多态,而非类型信息。使用多态比多种类型进行检测的代码更易于维护和扩展。
- 不要过多地使用反射。反射是脆弱的,编译器难以对检查错误方面提供更多的助益。