第5章 继承
5.1 一般性总结
- 子类不可以访问父类的私有域
- 使用super调用父类构造器的语句必须是子类构造器语句的第一条
- final类和final方法:
- final方法不可以被子类覆盖,但是可以被继承,只是不能覆盖而已
- final类不可以被继承,它的所有方法都是final方法
- xx instanceof yy 用来检查xx对象是否可以强转为yy
- 抽象类相关:
- 只要包含一个抽象方法,就必须将该类声明为abstract类
- 也可以将一个不含抽象方法的类定义为抽象类,这样该类就不能实例化了
- 实际开发中,很少用到抽象类,实现抽象类的具体方法用extends
- 抽象类可以有构造函数,但是不能被实例化,对于继承它的子类,可以调用super()来完成构造函数的调用。
- Java中数组也是一种对象,都扩展了Object类
- Object.equals(a,b)和a.equals(b)的对比:
- a.equals(b):当a对象为null时,该方法会报错
- Objects.equals(a,b):a和b都为null,返回true。其中一个为null,返回false,否则就调用a.equals(b)
- 默认的equals()返回对象的地址,而默认的hashCode()得到对象的存储地址,这两个方法必须一致!
5.2 反射
- 获得Class对象的3种方法:
- 利用对象的getClass()
- 利用Class.forName(带包名的类名),用这个方法必须进行异常处理
- Java类型.class,例如User.class
- class对象的newInstance()方法只能调用空参构造,没有的话会有异常
- Class对象的各种get方法:
- getFields、getMethods、getConstructors:分别返回类提供的public域、方法和构造器,其中包括超类的公有成员
- getDeclareFields、getDeclareMethods、getDeclareConstructors:分别返回类提供的全部域、方法和构造器,但是不包括超类的成员
- Modifier类的各种静态方法负责接管修饰符的String化,还有判断一个modifiers是不是private、public等等,典型的调用是:Modifier.toString(对象.getModifiers())
- 如果要利用反射机制访问私有域、方法,可以调用Field、Method、Constructor对象的.setAccessible(true)来实现,这样调用Field对象f.get(0bj)就可以访问私有域了。
- 通用toString方法的编写:
public class ObjectAnalyzer {
private List<Object> container = new ArrayList<>();
private String toStringCore(Object obj){
// 如果对象为空,直接返回null
if(obj == null) return "null";
// 如果该对象的地址已经打印过了,就打印...
if(container.contains(obj)) return "...";
// 新对象,加入存在列表中
container.add(obj);
// 获取该对象的class对象
Class clazz = obj.getClass();
// 如果该类型是一个String,直接返回即可
if(clazz == String.class) return (String) obj;
// 如果对象是一个数组,注意不是集合对象
if(clazz.isArray()){
// 拿到数组的长度
int length = Array.getLength(obj);
// 拿到数组的元素类型
Class componentType = clazz.getComponentType();
String s = componentType.getName() + "[]{";
// String s = componentType.toString() + "[]{";
for (int i = 0; i < length; i++) {
Object component = Array.get(obj, i);
if(i > 0) s += ", ";
if(componentType.isPrimitive()) s += component;
else s += toStringCore(component);
}
return s + "}";
}
String r = clazz.getName();
r += "[";
do{
// 拿到所有的域
Field[] fields = clazz.getDeclaredFields();
// 一定记得设置私有域可访问
AccessibleObject.setAccessible(fields,true);
// 遍历所有的域
for (Field field : fields) {
// 如果这个域是个static域,它不隶属于对象,不打印
if(!Modifier.isStatic(field.getModifiers())){
if(!r.endsWith("[")) r += ", ";
// 拿到域的名字
String name = field.getName();
r += name + "=";
try {
// 拿到该域的值
Object val = field.get(obj);
// 拿到该域的装的东西的class对象
Class type = field.getType();
// 判断是不是基本类型
if(type.isPrimitive()) r += val;
else r += toStringCore(val);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
clazz = clazz.getSuperclass();
}while (clazz != null);
r += "]";
return r;
}
public String toString(Object object){
String s = toStringCore(object);
container.clear();
return s;
}
}
- 编写扩展任意类型数组的方法(Array.copyOf只能扩展对象数组,基本类型的不行)
public static Object goodCopyOf(Object obj, int newLength){
Class c = obj.getClass();
// 如果不是数组,就滚蛋
if(!c.isArray()) return null;
// 拿长度
int length = Array.getLength(obj);
// 先new一个新的数组出来,这里一定不能直接new,要根据原数组的元素类型来new
Class componentType = c.getComponentType();
Object newArray = Array.newInstance(componentType,newLength);
/*
arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
方法解释:
Object src : 原数组
int srcPos : 原数据要拷贝的第1个位置
Object dest : 目标数组
int destPos : 目标数组的粘贴的第1个位置
int length : 从原数组要copy的数组的长度
*/
System.arrraycopy(obj,0,newArray,0,Math.min(length,newLength));
return newArrray;
}
- 如果要调用MethodObj.invoke(obj,args...)去激活静态方法,obj置为null
- Class对象可以调用Method getMethod(String s,Class...param)来获取Method对象,param代表参数列表的的类型