java基础二、类与继承
员工类 Employee, 经理类:Manager
|
|
1. extends 关键字表示类之间的继承关系。子类继承基类中的所有方法和数据。
2. 重写覆盖基类方法。当有些基类方法并不适用于子类时,可以重写该方法。Manager类 增加了数据bonus,在计算薪水的时候getSalary理应也要把bonus加进去。子类实例可以调用继承的基类public成员(包括方法和数据),但private成员只有Employee类自己可见。当在Manager中使用this.salary时会报 'salary' has private access in 'Employee' 的错误。this.getSalary()是调用子类函数getSalary本身。这时可以用super指代基类,调用基类方法 —> super.getSalary();
3. 可以使用基类的变量指向子类实例,如上Employee boss = new Manager("boss", 1000000, 2011, 5, 1, 500000); 在调用方法时是实例去调用,即boss.getSalary()输出的是 1000000 + 500000。但不能调用子类独有的方法,如setBonus,会造成编译错误。
一个对象变量可以引用多种实际类型------->多态: 上例代码 boss可以指向Manager实例,当然也可以指向Employee实例。
4. 子类构造函数可以在第一句使用super(param)的方式显示调用基类的构造函数。如果没有显式调用,则将自动调用基类的默认构造函数,如果基类没有默认构造函数,则会编译错误。也就是说子类的构造函数总是要先执行基类构造。
5. final 修饰变量, 变量只能初始化一次; 修饰方法,方法不能被子类重写; 修饰类, 类不能被继承。
6. 抽象类。abstract关键字修饰抽象类和抽象方法。包含抽象方法的类本身必须被声明为抽象类。抽象类可以包含具体数据和具体方法。抽象类不能被实例化。
7. 重写equals方法,多有类都派生于Object类,Object类的equals源码如下,直接比较引用,这对很多其他类并不适用:
public boolean equals(Object obj) {
return (this == obj);
}
- 应该重写Object中的equals,应该定义为 public boolean equals(Object otherObject),不要改变参数类型,否则就不是重写了;
- if(this == otherObject) 优化,地址相等直接返回。
- 检测otherObject是否为空, 即if(otherObject == null) return false;
- 比较this和otherObject是否属于同类。如果equals在每个子类中都有自己的定义,则应该使用getClass判断类型。如果所有子类都有统一的语义,则可使用 if(!otherObject instanceof ClassName)return false;
- 将otherObject转换为相应类型的变量, ClassName other = (ClassName)otherObject;
- 比较数据,基本类型 使用 ==比较。类类型可使用Objects.equals比较。
8. hashCode 由Object类定义的,返回一个整型值。源码:public native int hashCode(); 详细信息见下面的注释。
看源码的注释
|
1. 这方法是用来优化hash tables的,例如 HashMap。 2. 只要对象数据没有改变(equals方法中使用的数据),不论什么时候去获取hashCode,都应该也一样。但不是不允许hashCode变化。 3.两个对象如果equals判定相等, 必须有相同的hashCode 4. 当equals判定两个对象不相等时,并不要求hashCode一定不相等。但是要明白,不同对象(equals比较)有不同的hashCode可以提高 哈希表的性能。 5. Object类的hashCode方法返回的是由地址转换的整型。不同对象hashCode不同。 它是native方法,其他语言实现的。 |
hashCode()方法被定义是用来使得哈希表可以高效工作的。hashCode 为什么对哈希表很重要呢? 例如我们用的 HashMap,当我们调用get(Object key)时,首先就要查找在容器中是否存在这个对象,一般来说如何查找,肯定是要逐个去比较是否相等的,那就要使用equals方法才正确。但如果数据很多,equals的工作效率就很慢。这个时候就要有一个代替的方案:hashCode。当我们调用哈希容器的get(Object obj)方法时,首先查找是否有相同哈希值的对象,如果有再用equals比较是否相等,相等则返回该哈希处的对象。否则当然都是null,hashCode()返回的是一个整型值,比较自然很快。
总结:
- 重写equals,必须要重写hashCode。equals判定相等,hashCode必须一致。
- equals是判定两对象是否相等的充要条件。
- hashCode方法是判定两对象是否相等的必要非充分条件。
在重写hashCode时,最好使用null安全的Objects.hashCode方法。或者当需要组合多个散列值时,调用Objects.hash方法直接生成。我们重写Employee类的equals和hashCode方法。
|
|
计算数组类型的hashCode 可以使用 Arrays.hashCode(type[] a)方法。
9. 对象包装器类: Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean。都是不可变类型。自动装拆箱。
10. "..."表示接受任意数量的对象。例如 Object... 同等于Object[]。
11. 反射。
- Class 类。
Class cl = object.getClass(); //Object的getClass方法返回一个Class类型的实例,一个Class对象表示一个特定类的属性。
String className = new Date().getClass().getName() //值为"java.util.Date", cl.getName()返回该类的名字,名字包含包名.
这样我们就可以从一个对象实例获得其Class对象和类名。同样我们也可以通过类名获得对应的Class对象并构建类实例。
String className = "java.util.Date";
Class cl = Class.forName(className); //forName方法只有在className字符串是类名或接口名时才能够执行,否侧会抛checked exception。
//获得Class对象的另一种方法。T.class。T为java的任意类型
Class cl1 = Date.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
//通过Class对象的newInstance()方法创建类的实例。
Date now = (Date)cl.newInstance();
//newInstance方法调用默认的构造函数初始化对象。如果没有默认或者无参的构造函数将报错。
这样我们就可以通过字符串获得具体需要执行的类,例如将类名写在配置文件中,在需要更换类的时候就不必再更改代码了。
String classNameStr = readFromProperties; //"ClassA"
Class c = Class.forName(classNameStr);
ClassAInterface factory = (ClassAInterface)c.newInstance();
当按类名构造类实例需要传入参数时,可以使用 Constructor类的newInstance()方法。
- 分析类的能力
java.lang.reflect包中有三个类,Field、Method、Constructor,分别用于描述类的属性、方法、和构造器。 Class和这三个类都有一个方法叫做getModifiers(),该方法返回一个整型值,表示类、数据、方法和构造器的修饰属性,例如 1 表示public。可以使用Modefier类的方法(如: isPublic,isFinal等等)分析getModifilers()返回的值,。这三个类也都有getName方法返回名称。
Class类 getName() getSuperclass() getModifiers() getFields()---返回public 数据,getDeclaredFields ---返回所有数据-----Field类 getMethods()---返回public 方法, getDeclaredMethods---返回所有方法---Method类 getConstructors()---返回public 构造器,getDeclaredConstructors--返回所有构造器---Constructor类 newInstance() static forName |
Field getName() getType()----返回类型,Class对象 getModifiers() |
Method getName() getReturnType() getModifiers() getParameterTypes() |
Constructor getName() getModifiers() getParameterTypes() |
- 运行时使用反射分析对象
通过反射机制查看对象的数据,例:
Employee marry = new Employee("Marry", 50000,2022, 8, 1);
Class c = marry.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true); //Importance setAccessible方法 设置为true时,使得Field.get()方法可以访问private属性。
/**
AccessibleObject类的静态方法setAccessible()如下,可以设置Field[]为可读取的。
public static void setAccessible(AccessibleObject[] array, boolean flag)
*/
String name = (String)f.get(marry); //Field.get() 方法返回的类型为Object,String类可以,但是当访问salary属性时就不行了,
//基本类型不是Object的子类,这时可以使用Field.getDouble()方法。
- 通过invoke调用任意方法
这种方法不推荐使用, Method m1 = Employee.class.getMethod("raiseSalary", double.class); m1.invoke(object,10)
当有返回值时,invoke返回的都是Object对象。