继承(is a的关系)
首先请看这两个代码:
eg:
class Student{
String name;
int age;
public Student(){
}
//getXxx()/setXxx()
public void eat(){
System.out.println("吃饭");
}
}
class Teacher{
String name;
int age;
public Teacher(){
}
//getXxx()/setXxx()
public void eat(){
System.out.println("吃饭");
}
}
- 上面两个代码的name、age、getXxx()/setXxx()、eat()等都是相同的,如果后面继续定义的类也具备这些内容,每一次定义类都要将这些重复的内容重新定义一次,使得代码变的很冗杂;
为了解决这一问题,Java中提出了继承这一概念。
继承:多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,那么多个类无需再定义这些属性和行为,只需要继承那个类即可;
class 父类名 {
}//父类、基类、超类
class 子类名 extends 父类名{
}//子类、派生类
//类2继承于类1
extends 是关键字;
由此,前面的两个代码就可以写为:
class Person{
String name;
int age;
public Person(){
}
//getXxx()/setXxx()
public void eat(){
System.out.println("吃饭");
}
}
class Student extends Person{
public Student(){
}
}
class Teacher extends Person{
public Teacher(){
}
}
继承的优点:
- 提高了代码的复用性;
- 提高了代码的维护性;
- 让类与类之间产生关系,是多态的前提;
extends 关键字
- final class Animal —>类被final修饰后不能被继承
- final可以修饰常量、属性、类
class Animal{
public String name;
public int age;
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(this.name + "eat()");
}
}Animal为父类/基类/超类
class Cat extends Animal{ //关键字extends
public String sex;
public Cat(String name,int age,String sex){
super(name,age); //super显示调用父类的构造方法
this.sex= sex;
}
public void jump(){
System.out.println(this.name + "跳");
}
}Cat为子类/派生类
public class TestDemo {
public static void main(String[] args){
Animal animal = new Animal("动物",10);
Cat cat = new Cat("豆豆",1,"women");
cat.jump();
cat.eat();
}
}
多态
向上转型
三种方式:
- 直接赋值
Animal animal = new Animal("动物",10);
Cat cat = new Cat("豆豆",1,"women");
animal = cat; //将子类对象赋值给父类对象
可以写成:
Animal animal = new Cat("豆豆",1,"women");
//父类引用 子类对象 ---》向上转型
- 方法传参
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args){
Cat cat = new Cat();
func(cat);
}
- 方法返回
public static Cat func(){
Cat cat = new Cat();
return cat;
}
public static void main(String[] args){
Animal animal = func();
}
- Animal只能调用父类自己的方法或者成员属性;
- 如果子类中重写了父类中的方法,调用的依然是父类的该方法,但是由于在该过程中会发生运行时多态,所以运行的结果为子类中该方法的结果;
invokespecial:调用构造方法
invokevirtual:调用非静态方法 实例方法
class Animal{
public String name;
public int age;
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(this.name + "Animal::eat()");
}
}
class Cat extends Animal{ //关键字extends
public String sex;
public Cat(String name,int age,String sex){
super(name,age); //super显示调用父类的构造方法
this.sex= sex;
}
public void jump(){
System.out.println(this.name + "跳");
}
public void eat(){
System.out.println(this.name + "Cat::eat()");
}//重写
}
重写:方法名相同 参数列表相同 返回值相同 ——》override
重写的注意事项:
- 方法不能是private;
- 子类的方法权限一定要大于父类方法的权限;
- 不能是一个静态的方法;
重写和重载的区别:
- 重载是方法名相同,参数列表不同;重写是方法名相同,返回值类型相同,参数列表相同;
- 重载是一个类中,重写是继承关系上;
- 重载没有权限要求,重写有权限要求;
this 和 super 的区别:
- this访问本类中的属性和方法,super由子类访问父类中的属性和方法;
- this先查找本类,如果本类没有就调用父类;super不查找本类,直接调用父类;
- this表示当前对象的引用;
发生多态——运行时多态:
- 继承——》父类需要引用子类对象(向上转型);
- 通过父类的引用去调用子类和父类同名的覆盖方法;
- 一个类型就有一个方法表和class对象;
(反射:获取class对象; 三种方式)
向下转型
class Bird extends Animal{
public String color;
public void fly(){
}
public static void main(String[] args){
Animal animal = new Bird();
animal.eat();
Bird bird = (Bird)animal; //将父类对象给子类对象,需要强转
animal.fly();
}
向下转型的前提是必须先进行一次向上转型;
Animal animal = new Cat("小猫");
Bird bird = (Bird)animal;
bird.fly();
执行结果, 抛出异常:Exception in thread"main"java.lang.ClassCastException: Cat cannot be cast to Bird at Test.main(Test.java:35)
(animal本质上引用的诗意个Cat对象,运行时就会抛出异常;)
为了让向下转型更安全,我们需要先判断animal是不是一个Bird实例再进行转换;
instanceof 关键字
Animal animal = new Cat("小猫");
if (animal instanceof Bird) {
Bird bird = (Bird)animal;
bird.fly();
}//如果是Bird的实例,进入if语句
在构造器中调用重写的方法(这是一个大坑)
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
执行结果:D.func() 0
- 构造 D 对象的同时,会调用 B 的构造方法.
- B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
- 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0.
- Q:构造方法内是否能够发生动态绑定?
- 可以;
抽象类
abstract 抽象方法
- 包含抽象方法的类叫做抽象类;
- 抽象类不能被实例化;
- 抽象类当中可以有抽象方法,也可以有非抽象方法或者成员变量;
- 抽象类的产生就是为了继承;
public abstract void Shape{
}//抽象类
如果一个类里面的方法没有具体的实现,可以将它作为一个抽象方法,一旦这个抽象方法放到这个类中,此类必须是一个抽象类;
abstract class Shape{
public abstract void draw();
}
- 抽象类只要被继承,一定要重写抽象方法;
- 如果抽象类A继承了抽象类B,那么抽象类A可以选择重写或者不重写抽象类B中的方法;
注意:
- 抽象类不能直接实例化;(只能创建该抽象类的子类,然后让子类重写抽象方法)
- 抽象类不能是 private 的;
- 抽象类中可以包含其他的非抽象方法,也可以包含字段;
Q:普通的类也可以被继承, 也可以被重写, 为啥非得用抽象类和抽象方法呢?
使用抽象类相当于多了一重编译器的校验。
本文详细介绍了Java中的继承、多态和抽象类。通过讲解`extends`关键字、向上转型与向下转型、多态的概念及其实现、`instanceof`关键字的应用,以及抽象方法和抽象类的特性,帮助读者深入理解Java面向对象编程的核心概念。同时,文中还探讨了在构造器中调用重写方法的潜在问题及其原因。
973

被折叠的 条评论
为什么被折叠?



