目录
1.继承
1.1继承的定义与目的
我们在利用类进行实例化时,实例之间总会有一些相同的成员变量或方法。就比如我创建了一个 Dog 和 Cat 的类,他们都需要 name 和 age 这两个成员变量。如果每次创建类似的类时都要写一遍的话,未免太过麻烦,那么我们能不能将这类共性提取出来呢。
答案是可以的,我们这里就需要用到继承。我们再新建一个 animal 类,在里面添加 name 和age 这两个成员变量,这便是 Dog 和 Cat 的共性,那么我们如何让Dog 和 Cat 继承到Animal的成员变量呢。
1.2继承的语法
在Java中,使用extends
关键字来实现继承。如图:
这样 Dog 类就继承了 Animal 类,由于Animal类是被继承的,因此Animal类又被称为父类,而Dog类则被称之为子类, 这样子类就可以使用父类中的成员变量和方法了。
public class Test1 {
public static void main(String[] args) {
Dog dog =new Dog();
dog.bark();
}
}
public class Animal {
String name = "小白";
int age = 10;
public void eat(){
}
}
public class Dog extends Animal{
@Override
public void eat(){
System.out.println(name+"在吃狗粮");
}
public void bark(){
System.out.println(name+"在狗叫");
}
}
这几段代码的运行结果是 “小白在狗叫”,我们是可以发现Dog类中并没有 name 变量,它所使用的就是其父类Animal中的变量,当然父类中的方法也可以使用。
值得注意的是:成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
那如果在父类和子类都有同一个变量但我就是要访问父类的变量那该怎么办。这里我们就需要用到super操作符了
1.3super 和 this
super的作用很简单,就是在子类中访问父类的内容。而 this 就是访问当前类中内容
public class Test2 {
public static void main(String[] args) {
B b=new B();
b.method();
}
}
public class A {
String s="父类中的s";
public void print(){
System.out.println("父类的打印");
}
}
public class B extends A{
String s="子类中的s";
@Override
public void print() {
System.out.println("子类的打印");
}
public void method(){
System.out.println(this.s);//子类打印
print();//子类打印
System.out.println(super.s);//父类打印
super.print();//父类打印
}
}
<---打印结果
1.4构造方法
父类,子类肯定是先有父再有子,所以在构造子类对象时候 ,先要调用父类的构造方法,将从父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整
class Parent {
Parent() {
System.out.println("Parent Constructor");
}
Parent(String message) {
System.out.println(message);
}
}
class Child extends Parent {
Child() {
// 隐式调用 super();
// 注意子类构造方法中默认会调用父类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
System.out.println("Child Constructor");
}
Child(String message) {
super(message + ", then Child"); // 显式调用父类的有参构造方法
System.out.println("Child Constructor with Message");
}
}
public class Test {
public static void main(String[] args) {
new Child(); // 输出: Parent Constructor, Child Constructor
new Child("Parent first, "); // 输出: Parent first, then Child, Child Constructor with Message
}
}
注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
2. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
3. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。
4. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
当创建子类对象时,构造方法的执行顺序如下:
1.父类的静态成员和静态初始化块(按它们在代码中出现的顺序执行,仅执行一次,因为它们是类级别的)。
2.子类的静态成员和静态初始化块(同样,仅执行一次)。
3.父类的构造方法(显式或隐式调用)。
4.子类的构造方法。
2.多态
所谓多态就是去完成某个行为时,当不同的对象去完成时会产生出不同的状态,就像Dog和Cat,我都使用一个eat方法,但一个是吃狗粮,一个是吃猫粮。同一个方法发生在不同对象上,产生了不同效果。
public class Test1 {
public static void main(String[] args) {
Animal s1=new Animal();
s1.eat();
Animal s2=new Dog();
s2.eat();
Animal s3=new Cat();
s2.eat();
}
}
public class Animal {
String name = "小白";
int age = 10;
public void eat(){
System.out.println(name+"在吃饭");
}
}
public class Cat extends Animal{
public String name="小猫";
public int age=10;
@Override
public void eat() {
System.out.println(name+"在吃猫粮");
}
public void mew(){
System.out.println(name+"在猫叫");
}
}
public class Dog extends Animal{
public String name="小狗";
public int age=10;
@Override
public void eat(){
System.out.println(name+"在吃狗粮");
}
public void bark(){
System.out.println(name+"在狗叫");
}
}
<----(打印结果)这段代码实现的就是多态
2.1多态的实现方法
多态的实现主要依赖于以下几个关键点:
-
继承:多态性发生在有继承关系的子类和父类之间。子类继承了父类,并可以重写父类的方法。
-
方法重写:子类对父类中的方法进行重写,即子类可以提供一个特定签名的方法,该方法在父类中也有定义但具有不同的实现。
-
向上转型:父类类型的引用指向子类对象。这种转型是自动的,也称为隐式类型转换。
-
向下转型(可选):子类类型的引用指向父类类型的对象,但需要进行显式类型转换(即强制类型转换),并且可能面临
ClassCastException
的风险。
我们一个个来看
2.1.1继承
只有在继承体系下才能实现多态 。因为只有继承了才能进行下一步的 “重写” 操作
2.1.2方法重写
简单来说,就是在子类里写一个父类中已经有的方法。其返回值,名字和形参都不能改变,改变的只有方法的内容,且重写的访问权限不能比父类中被重写的方法的访问权限更低,其中被static、private修饰的方法无法重写。在上面子类代码的eat方法上方中,我们可以看@Override 的字样,这就代表这个方法被重写了。那么重写与重载又什么区别呢?
重写(覆写) | 重载 | |
参数列表 | 必须和父类中的方法一致 | 必须不同 |
返回值 | 必须和父类中的方法一致 | 无所谓 |
访问修饰符 | 访问权限不能比父类方法更低 | 无所谓 |
多态性 | 子类和父类的多态表现(运行时多态性, 动态绑定) | 一个类的多态表现(编译时多态性,静态绑定) |
2.1.3向上转型
向上转型指的是将子类的实例赋值给其父类(或任何父类)类型的引用变量。
就比如这段代码:
Animal s2=new Dog();
将子类Dog的实例,赋值给了父类Animal。
语法格式:父类类型 对象名 = new 子类类型()
向上转型的特点
- 自动性:不需要显式的类型转换语句,编译器会自动处理。
- 安全性:由于子类继承父类,所以子类实例可以视为父类的一个特例,因此向上转型是安全的,就像动物父类下有很多子类,狗也是其中一个,因此说狗也是动物是完全没有问题的。
- 限制:通过父类引用调用方法时,只能访问父类中定义的方法(除非这些方法在子类中被重写)。如果子类有新增的方法,则不能通过父类引用来调用这些方法。就像上面Dog类中的bark方法,此时所 s2 就是无法调用的,因为这是Dog类特有的。
2.1.4 向下转型
与向上转型相反,向下转型是指将父类类型的引用变量强制转换为子类类型的引用变量,和类型强制转换类似
Dog s2=new (Dog)Animal();
向下转型的特点
- 显式性:需要显式地使用类型转换操作符。
- 潜在风险:向下转型是不安全的,建议少使用,毕竟狗可以是动物,但动物却不一定是狗。
- 用途:当你确实知道父类引用实际上指向的是一个子类实例,并且你需要访问子类特有的方法或属性时,才会使用向下转型。