JAVA基础之(五)”继承“

一、子类与父类

“继承”概念:“is-a”(xxx是xxx)关系是继承的主要特点,子类(也叫派生类)继承父类(也叫基类、超类)不光可以重用父类的代码,而且可以新增加代码(方法/属性);

实现方式:用“extends”关键字表示继承;“extends”表明正在构造的新类派生于一个已经存在的类;

特点:

  • “子类”比“超类”更加丰富;但是不能删除继承的任何域(也就是属性)和方法;
  • “子类”可以重写(override)“超类”的方法;而且override的返回类型可以修改;
  • “子类”可以用“super”关键字调用“超类”的构造器与方法;区别于this,super不能当做对象来赋值,super只是个编译器调用父类的特殊表示方式;但是子类调用父类构造器必须在子类构造方法中的第一行;
  • 置换法则:“超类”出现的任何地方都可以用“子类”替换;“父类”变量可以指向“子类”引用,但是"子类"变量不能指向“父类”引用;(这里注意“引用”与“变量”概念的区别,我容易混淆)

继承中的“多态”: 也就是“置换法则”;父类可以用子类替换,而且父类变量可以指向子类引用;父类 = new 子类;是允许的;反之不允许;

继承中的“动态绑定”:运行时自动选择调用的方法;也就是重载(overload)的方法的调用;

  • “静态绑定”:对于private方法,static方法,final方法,无需判断就可准确知道需要调用的方法;
  • “动态绑定”优点:无需对现有方法进行更改,就可以扩展程序;

阻止继承的final类与方法:final修饰的类与方法不允许被继承;(String类就是final的)

强制类型转换:父类变量可以指向子类的引用,子类变量不能指向父类的引用,但是可以进行强制转换,是否可以强制转换可以种“instanceof”运算符进行判断,这个在一般类中作用不大,主要用于Object类;(instancefo用法:对象 instanceof 类型)这个;该运算符在“equals”方法的重写中作用很大;

抽象类:包含一个或多个抽象方法的类叫做抽象类;抽象类不能够被实例化;

  • 抽象类方法:用“abstract”修饰的方法,而且没有具体的实现;
  • 抽象类中可以有具体的实现方法和属性;
  • 受保护类(protected)的访问;private/default/public/protected修饰符的区别;

二、Object超类(所有类默认的超类)

  •  “equals”方法:object中的equals方法用于检测两个对象具有相同的引用;实际的使用中由于所有类继承了object类,所以可以重写“equals”方法,改变对比的规则;

建议的“equals”重写方法:对于数组可以使用Arrays.equals()方法;为容错,可以使用Objects.equals()方法;

 

 1 @Override
 2 public boolean equals(Object obj) {
 3     //首先用instanceof验证 obj是否是 该类的实例
 4     if (obj instanceof JAVABaseTest_045) {
 5         //验证obj非空,且this与obj引用同一对象,且this与obj属于同一个类
 6         if (obj != null && this == obj && getClass() == obj.getClass()) {
 7             //将obj强转为该类,并进行内部的 属性/对象 的验证
 8             JAVABaseTest_045 mt = (JAVABaseTest_045) obj;
 9             return (this.getA1() == mt.getA1()) && super.equals(obj);
10         }
11     }
12     return false;
13 }

 

  • “hashcode”方法:hashcode也叫“哈希值”是通过特殊的算法,对一个数进行计算得到一个“散列码”;这样可以将一组数分为几组数,方便进行对比,降低了逐个对比的难度;

对于一个对象,可以调用Objects.hashcode();对于多个对象,可以调用Objects.hash();对于一个数组,可以调用Arrays.hashcode();

equals方法与hashcode方法的联系:equals相等,hashcode一定相等;hashcode相等,equals不一定相等;所以重写了equals方法,一定要重写hashcode方法;

  • toString方法:特殊点:toString在使用(对象“+”xxx)的时候,会自动调用(对象.toString());方便打印各种需要类打印出来的数据(一种很好的调试工具);
  • 其他特殊方法:Object.getClass();Class.getName();Class.getSuperclass();

三、泛型数组列表注意:ArrayList是集合没有错,但是Arraylist不是链表,他是个“数组列表”)

普通数组Arrays可以在定义的时候定义长度,但是不能进行修改,于是可以用ArraysList这个类,“ArraysList”是一个采用“类型参数”的“泛型类”;而且这个类存放的元素的大小可以变化;但是,Arraylist虽然大小可以变化,但是“插入”、“删除”数组的时候比较麻烦,这就需要后面的其他“集合类型”;

声明方式:ArraysList<类型> 变量名 = new ArraysList<>();

常用方法

  • 构造方法ArrayList<T>();ArraysList<T>(初始数组大小);
  • add(T)
  • size()
  • ensureCapacity(int)
  • trimtoSize()
  • toArray()

ArrayList可以转换为有“参数类型”的Arraylist,但是需要强转,而且需要有@SuppressWarning(“unchecked”)标注;

扩展普通数组大小的方法(a为一个普通数组):a = Arrays.copyOf(a,2*a.length);

四、对象包装器与自动装箱

由于上面的泛型数组列表需要使用“对象类型”参数,所以如果是基本类型,就需要使用基本类型的封装类(如Integer/Long/Float/Double/Short/Byte/Character/Void/Boolean),这些类型叫做“包装类”;

特点

  • 都是final的,不允许继承;
  • Integer这类包装类的常用方法:valueof,可以进行自动装箱,自动拆箱;这是“编译器”处理的;

五、参数数量可变的方法

举例如下System.out.printf()方法的使用:

public PrintStream printf(String format, Object... args) {
    return format(format, args);
}

其中的参数:(Object ... args)表明任意数量的对象;同(Object[])相同,有了这种java代码,就可以写出,参数数量可变的方法;(可以将已经存在且最后一个参数是数组的方法重新定义为可变参数的方法)

1 public static double max(double... values){
2     double largest = Double.MIN_VALUE;
3     for(double v : values){
4         if (v>largest) largest = v;
5     }
6     return largest;
7 }

 六、枚举类

枚举类型,底层是个类,枚举的值就是类中的实例,所以对比两个枚举类型的值,使用“==”就可以,也可以使用Enum的方法compareTo;

七、反射(能够分析类能力的程序)

反射大量用于JavaBeans中;

▲Class是一种类型,有自己的静态方法forName(),与newInstance()联合使用可以根据相应类的名称,获得相应的无参构造器生成的新的对象;如下:

String s = "java.util.Date";
Object m = Class.forName().newInstance();

利用反射分析类:java.lang.reflect包存在了三个类:Field/Method/Constructor;Modifiers用于判断修饰符的;以下是常用方法:用于得到类的各种信息

  • Field(域):getName();getType();getModifiers();
  • Method(方法):getName();getModifiers();
  • Constructor(构造器):getName();getModifiers();
  • Modifiers:isPublic();isFinal();isPrivate();toString();
  • Class类:getFields();getMethods();getConstructors();getDeclareFields();getDeclareMethods();getDeclaredConstructors();

在运行时使用反射分析对象

  • 利用反射得到相应域的时候,如果域为私有的,则默认受限于JAVA的访问控制,只能查看有哪些域,不能得到相应的值;但是如果java程序没有受到安全管理器的控制,就可以覆盖访问控制,可以调用AccessibleObject类的setAccessible()方法,这个类是上面Field/Method/Constructor的超类,所以也可以调用该方法;
  • 使用setAccessible(true);之后,就可以访问私有域的值了;也就是修改了访问控制;

下面是个通用的toString()方法:

 1 import java.lang.reflect.AccessibleObject;
 2 import java.lang.reflect.Field;
 3 import java.lang.reflect.Modifier;
 4 
 5 
 6 public class ObjectAnalyzer {
 7     public String toString(Object obj){
 8         Class cl = obj.getClass();
 9         String r = cl.getName();
10         do{
11             r +="[";
12             Field[] fields = cl.getDeclaredFields();
13             AccessibleObject.setAccessible(fields, true);
14             for(Field f : fields){
15                 if(!Modifier.isStatic(f.getModifiers())){
16                     {
17                         if(r.endsWith("[")) r+=",";
18                         r += f.getName() + "=";
19                         try{
20                             Object val = f.get(obj);
21                             r += toString(val);
22                         }catch(Exception e){e.printStackTrace();}
23                     }
24                 }
25                 r += "]";
26                 cl = cl.getSuperclass();
27             }
28         }while(cl!=null);
29         return r;
30     }
31 }

常用方法

  • AccessibleObject:setAccessible(boolean flag)/isAccessible()/setAccessible(AccessibleObject[] array,boolean flag)
  • Class:getField(String name);getField();getDeclaredField(String name);getDeclaredFields();
  • Field:get(Object obj)/set(Object obj,Object newValue);

▲用反射编写泛型数组

声明泛型数组的时候,需要制定数组的数据类型,否则后面进行“强转”的时候是不成功的;最开始声明为Object数组 ,后面是无法进行强转的;

反射扩展普通泛型数组:该方法使用的时候参数Object可以为任何类型的数组,但是必须声明为Object,而不是Object[];

 1 //该方法可以用于扩展任意类型的数组
 2 public static Object goodCopyof(Object a,int newlength){
 3     Class cl = a.getClass();
 4     if(!cl.isArray()) return null;
 5     Class componentType = cl.getComponentType();
 6     int length = Array.getLength(a);
 7     Object newArray = Array.newInstance(componentType, newlength);
 8     System.arraycopy(a, 0, newArray, 0, Math.min(length, newlength));
 9     return newArray;
10 }

 常用方法:java.lang.reflect.Array:get();getXxx();set();setXxx();getLength();newInstance();

调用任意方法:

使用Method类中的invoke方法:

Object invoke(Object obj,Object... args)

 Method的获得方法:

Method getMethod(String name,Class... parameterType),例如:

Method m1 = Employee.class.getMethod("raiseSalary",double.class);

八、继承的设计技巧

  • 将公共类和域放在超类
  • 不要使用受保护的域(会破坏封装性)
  • 有“is-a”关系才使用继承,否则乱用会更加复杂
  • 除非所有继承的方法都有意义,否则不要使用继承
  • 覆盖方法时,不要改变预期的行为,否则重写没有意义
  • 使用多态来减少(if-else)判断,例如:
1 if(x is type of Type1){
2     action1(x)
3 }else if(x is type of Type2){
4     action2(x);
5 }

上面代码中,如果action1与action2是相同的概念,可以使用多态优化为:x.action;

 

posted @ 2018-05-16 14:54  无言火  阅读(157)  评论(0编辑  收藏  举报