文章目录
基于已有的类创建新的类。
继承已存在的类就是复用这些类的方法,并且可以增加一些新的字段和方法,使新类能够适应新的情况。
类、超类和子类
"is-a"关系是继承的一个明显特征。
定义子类
Java中使用关键字extends表示继承。
该关键字表明正在构建的新类派生于一个已经存在的类。这个已经存在的类称为父类,新派生出的类称为子类。
子类继承父类后,有三个特点:
- 子类继承了父类的所有非private的属性和方法
- 子类可以添加新的属性和方法
- 子类可以覆盖从父类继承而来的方法
综上所述,子类拥有比父类更多的功能。
使用extends关键字来定义子类时,只需在子类中指出子类和父类之间的不同即可。
/**
* 父类
*/
public class FatherDemo {
public void method1(){
System.out.println("method1");
}
}
/**
* 子类
*/
public class SonDemo extends FatherDemo{
@Override
public void method1() {
// 调用父类的该方法
super.method1();
System.out.println("重写的新内容");
}
public void method2(){
System.out.println("子类新增的method2");
}
}
覆盖方法
又叫重写。子类中,如果想对父类的现有方法进行功能修改,可以覆盖父类的方法。
覆盖的方法,有如下特点:
- 覆盖方法与父类的现有方法的方法名、参数、返回值均相同,只改变方法的具体实现
- 覆盖方法不能低于父类现有方法的可见性:比如父类方法是public,子类方法必须是public。
- 子类中的覆盖方法,使用@Override注解标明
- 子类中若想使用父类的方法,可使用super关键字。可理解为,this为当前类的隐藏参数,super为父类的隐藏参数。
子类的构造器
子类的构造器可使用super关键字调用父类的构造器,并且super语句必须是子类构造器方法的第一条语句。
⭐️当子类进行实例化的时候,会自动调用父类的无参构造器。若父类没有无参构造器,并且子类也没有显示的调用父类的有参构造器,Java编译器就会报错。
多态
对于继承了父类的所有子类,子类的每个对象也是父类的对象。也就是说,程序中出现的出现父类对象都可以使用子类替换。
// 子类对象赋予父类变量
FatherDemo fatherDemo = new SonDemo();
在Java这种面向对象的设计语言,对象变量是多态的。
阻止继承:final类和方法
在开发过程中,有时候我们可能并不希望其他人利用某个类来定义子类。这时我们可以使用final关键字。
- final类不能被继承
- final方法被子类不能重写
强制类型转换
有继承关系的类中,可以使用强制类型转换将父类强行转换成子类。
使用强制类型转换的唯一原因是:需要暂时忽略对象的实际类型之后使用对象的全部功能。
- 只能在继承层次内进行强制类型转换
- 在进行强制类型转换之前,必须使用instanceof进行检查。
抽象类
抽象类其实本质就是一个类,可以有,但是在类中有一个或者多个抽象方法。
抽象类或者抽象方法都使用abstract关键字进行修饰。
抽象方法就是只有方法的定义,并没有实现。
/**
* 抽象类
*/
public abstract class AbstractDemo {
private String name;
public AbstractDemo(String name) {
this.name = name;
}
public void method1(){
System.out.println("这是正常方法");
}
/**
* 抽象方法
*/
public abstract void method2();
}
抽象类就是用来给子类继承的,子类继承抽象类,然后对抽象方法进行重写即可使用。
有抽象方法的类一定是抽象类,但是抽象类可以没有抽象方法。
Object,所有类的父类
Object类是Java中所有类的始祖,每个Java类都扩展了Object。所以每个类都有Object的如下方法:
- equals
- hashcode
- toString
equals
equals方法用于检测一个对象是否等于另一个对象。
public boolean equals(Object obj) {
return (this == obj);
}
从源码我们可以看到,Object类的equals就是==,也就是说equals方法判断两个对象是否相等就是判断两个对象引用是否相等。但是在开发过程中,一般需要对equals进行重写。
Java语言规范要求equals方法具有提下特性:
- 自反性:对于任何非空引用x,x.equals(x)返回为true
- 对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)返回true
- 传递性:对于任何引用x、y和z,如果x.equals(y)为true,并且y.equals(z)为true,那么x.equals(z)也为true
- 一致性:如果x和y引用的对象没有变化,反复调用x.equals(y)应返回相同的结果
- 对于任意非空引用x,x.equals(null)返回false
hashCode
散列码(hash code)是由对象导出的一个整型值。并且没有规律。
⭐️hashCode方法与equals方法由密切关系:
- equals方法与hashCode方法定有必须相同:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()返回相同的值
- 如果重写了equals方法,必须重写hashCode方法
toString
toString方法会返回表示对象值的一个字符串。
一般在开发中,都会进行重写。
泛型数组列表
见集合章节
对象包装器与自动装箱
Java中,所有的基本类型都有一个与之对应的类,这些类被称为包装器。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Char |
- 包装器是不可变的。一旦构造了包装器,就不允许更改包装器中的值
- 包装器类使用final修饰,无法派生他们的子类
在日常使用过程中,基本数据类型与包装类可以自动相互转化,无需手动操作。这被称为自动拆、装箱
可变长参数
Java方法支持可变长参数。即方法中可传入任意个参数。
public void method1(int a,String... str){
for (String s : str) {
System.out.println(s);
}
System.out.println("多参数");
}
上面的方法中,我们除了可以传一个int类型的参数外,还可以传入多个String类型的参数。
使用可变长参数,这种参数必须放在参数列表的最后。Java编译器会这种参数视为一个数组。
枚举
之前曾经学习过枚举,这里主要列出关于枚举的几个重要特点:
- 枚举类只有几个特定的实例,无法构造新的实例
- 对比两个枚举类的值时,使用==即可
- 枚举的构造器总是private,所以可以省略
- 所有枚举类都是Enum类的子类
- 每个枚举类都有一个values方法,它将返回一个包含所有枚举值的数组。
反射
正在建设中
继承的设计技巧
- 将公共操作和字段放在父类种
- 不要使用受保护的字段
- 使用继承实现“is-a”关系
- 除非所有的继承方法都有意义,否则不要使用继承。
- 覆盖方法时,不要改变预期的行为。
- **使用多态,而不要使用类型信息。**便于维护和扩展。
- 不要滥用反射。