第5章 继承与反射

第5章 继承

5.1 一般性总结

  1. 子类不可以访问父类的私有域
  2. 使用super调用父类构造器的语句必须是子类构造器语句的第一条
  3. final类和final方法:
  • final方法不可以被子类覆盖,但是可以被继承,只是不能覆盖而已
  • final类不可以被继承,它的所有方法都是final方法
  1. xx instanceof yy 用来检查xx对象是否可以强转为yy
  2. 抽象类相关:
  • 只要包含一个抽象方法,就必须将该类声明为abstract类
  • 也可以将一个不含抽象方法的类定义为抽象类,这样该类就不能实例化了
  • 实际开发中,很少用到抽象类,实现抽象类的具体方法用extends
  • 抽象类可以有构造函数,但是不能被实例化,对于继承它的子类,可以调用super()来完成构造函数的调用。
  1. Java中数组也是一种对象,都扩展了Object类
  2. 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)
  1. 默认的equals()返回对象的地址,而默认的hashCode()得到对象的存储地址,这两个方法必须一致

5.2 反射

  1. 获得Class对象的3种方法:
    • 利用对象的getClass()
    • 利用Class.forName(带包名的类名),用这个方法必须进行异常处理
    • Java类型.class,例如User.class
  2. class对象的newInstance()方法只能调用空参构造,没有的话会有异常
  3. Class对象的各种get方法:
    • getFields、getMethods、getConstructors:分别返回类提供的public域、方法和构造器,其中包括超类的公有成员
    • getDeclareFields、getDeclareMethods、getDeclareConstructors:分别返回类提供的全部域、方法和构造器,但是不包括超类的成员
  4. Modifier类的各种静态方法负责接管修饰符的String化,还有判断一个modifiers是不是private、public等等,典型的调用是:Modifier.toString(对象.getModifiers())
  5. 如果要利用反射机制访问私有域、方法,可以调用Field、Method、Constructor对象的.setAccessible(true)来实现,这样调用Field对象f.get(0bj)就可以访问私有域了。
  6. 通用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;
    }

}
  1. 编写扩展任意类型数组的方法(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;
}
  1. 如果要调用MethodObj.invoke(obj,args...)去激活静态方法,obj置为null
  2. Class对象可以调用Method getMethod(String s,Class...param)来获取Method对象,param代表参数列表的的类型
原文地址:https://www.cnblogs.com/doubest/p/12862495.html