【Java】继承

类、超类、子类

Java中继承使用extends关键字,并且Java中所有的继承都是公有继承,而没有C++的私有继承和保护继承。

超类:就是父类。

重载:方法的方法名相同,参数列表不同,返回值无所谓。类的构造方法可以重载。

覆盖(重写):在子类中写父类的同名同参同返覆盖方法。允许子类将返回值定义为原方法返回类型的子类型(细品)。类的构造方法没有返回值,方法名也不同所以不会形成覆盖。

如果想要在子类中调用父类的方法可以使用super关键字

super还可以用来调用父类的构造函数。super(...)。并且使用super调用父类的构造函数的语句必须是子类构造函数的第一条语句。

Java中的多态不需要使用virtual。直接将子类对象给父类的引用即可。

Java不支持多继承。

动态绑定

如果采用了动态绑定,那么虚拟机会调用与所引用对象的实际类型最合适的那个类的方法。

例子:x的实际类型为B,B是A的子类。如果B定义了方法fun(),那么就直接调用它,如果没定义那就会去找A中的fun()方法。

注意上面的例子,发现如果每次都找,那么开销较大。所以虚拟机给每一个类创建了一个方法表(对应的就是C++中的虚基类表)

final类

  • 被final修饰的类不能有子类。
  • 被final修饰的方法在子类中也不能被覆盖。
  • 被final修饰的成员变量在构造对象后就不能改变其值了。
  • 对于final修饰的类,其中的方法也是final方法,但成员变量还是普通的成员变量。

final类早期是为了避免动态绑定给系统带来较大的开销,毕竟没有覆盖的方法,编译器可以对其直接优化处理,这个过程就是内联。但后来由于虚拟机中的计时编译器很厉害,它会自动的将方法很短,被频繁调用且没有覆盖的放法进行内联处理。

强制类型转化

将子类的引用给父类变量,是可以的。但将父类的引用给子类变量,就需要强制类型转换。

抽象类

用abstract声明。

  • 包含一个或多个抽象方法的类必须声明为抽象类。当然不包含抽象方法的类也可以是抽象类。

  • 抽象是可以包含具体数据和具体方法的。

  • 抽象方法不需要实现。

  • 抽象类不能实例化。


Object:所有类的父类

可以使用Object类型的变量引用任何类型的对象。

Java中只有基本数据类型不是对象。其他包括数组(基本类型的数组或对象数组)都扩展了Object类。

equals方法

Object中的equals方法用于检测一个对象是否等于另外一个对象。将判断两个对象是否具有相同的引用。

在子类中定义class方法时,首先调用父类的equals,如果失败对象就不可能相等。如果成功再比较子类中的成员变量即可。

hashCode方法

散列码(hash值)是由对象导出的一个整型值。

在Object类中有hashCode方法可以直接使用,但也可以通过重写的方法自己写该方法。

区别:原本的方法只要对象不一样,生成的hash值就不同。但自己写的就会根据写的函数体生成固定的hash值,导致虽然对象不同,也会生成相同的hash值。

注:
在java.util.Objects;包下Objects类(多了一个s)有自己的hash函数和hashCode函数,使用方法与Object略有不同。

  • Objects下的hashCode()更安全因为如果传的是null指针就会返回0。
  • Object下的hashCode()会报错。
  • Objects下的hash()函数可以接收多个参数,生成一个hash值
  • 同时要求Equals与hashCode定义必须一致。俨然就是如果equals返回为true,生成的hash值就需要一样。

toString方法

Object中的toString方法会返回表示对象值得字符串。

也可以在自己的类中重写toString方法。

一个常见的格式为:类名[成员变量 = 值,...]。类名可以使用getClass().getName()获得。

其实在使用System.out.println(x)得时候们就会直接调用x.toString方法。

注:
比较不方便的一点是,数组也继承了Object的toString方法,但它会用旧的格式打印生成的字符串是看不懂的。这时我们需要使用Arrays.toString(数组名)的方法进行打印。多维数组需要用Arrays.deepToString()方法。


泛型数组列表

在C++STL中有vector它可以自动扩容。

在Java中有ArrayList,也是具有自动调节数组容量的功能。

ArrayList<Integer> arr = new ArrayList<Integer>();//<>里不能出现int等基本数据类型
arr.add(harry);//添加元素
arr.remove(i);//删除元素
arr.ensureCapacity(100);//固定容量,就不需要重复扩容了
arr.size();//返回包含的实际元素数量
arr.set(i,harry);//相当于C++的arr[i] = harry,但是它只能替换不能添加
arr.get(i);//就是arr[i]

对象包装器与自动装箱

对象包装器类:Intager、Long、Float、Double、Short、Byte、Character、Void、Boolean前6个类派生于公共的父类Number。

对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。

在<>中的类型不能是基本类型。

Integer n = 1;//自动装箱
n++;//自动拆箱

如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会拆箱,提升为double,再装箱为Double:

装箱和拆箱是编译器认可的,而不是虚拟机。编译器在生成类的字节码时,插入必要的方法调用。虚拟机只是执行这些字节码。


继承的设计技巧

  1. 将公共操作和域放在父类
  2. 一般不要用protected
  3. 使用继承实现“is-a”的关系
  4. 除非所有的继承的方法都有意义否则不要用继承
  5. 在覆盖方法时,不要改变预期的行为
  6. 使用多态,而非类型信息
  7. 不要过多使用反射

Annotation

准确的覆盖:@Override

如果在写代码是没有写extends,或者覆盖的时候单词写错了,所以为了避免以上问题对的出现,追加@Override,明确标识该方法是覆盖的方法。

在编译期就能报错。

过期操作:@Deprecated

在开发过程中可能有某方法或某个类存在问题,导致新版本引用存在问题(老版本没事),这时当然不能直接删掉该操作,就可以采用过期声明。告诉新的用户不要在用了,老的用户可以用。

编译期会警告,但不影响运行。

压制警告:@SuppressWarings

我知道有编译器会给警告信息,但我不想看见,就可以用。

posted @ 2020-01-23 21:00  LampsAsarum  阅读(342)  评论(0编辑  收藏  举报