Java面向对象特性

news2025/1/11 2:44:27

 Java继承:

继承的概念:

在Java中,继承(inheritance)是面向对象编程的一个重要概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以拥有父类的属性和方法,并且可以扩展或修改这些属性和方法,实现代码的重用和扩展。

父类怎么形成的?

当我们定义了多个类,发现每个类中的属性重复,我们就可以把这些重复属性抽取出来,形成父类,其它的类直接继承这个父类即可。

继承其实更贴合于设计这方面的感觉,因为我们写代码的时候,不可能写了好多类之后,然后发现好多类的属性重复,再去抽取,这样就很慢,而是在设计的时候就提前想好。

在Java中,使用关键字extends来实现继承,语法如下: 

class 子类名 extends 父类名 {
    // 子类的属性和方法
}

继承的基本使用:

1:首先定义一个父类(people)
package Testextends;

public class People {
    String name;
    int age;
    public void work(){
        System.out.println("工作");
    }
    private void eat(){
        System.out.println("吃饭");
    }
}
2:定义若干子类(teacher,manager)
package Testextends;

public class Manager extends People{
}
package Testextends;

public class Teacher extends People{

}

同时继承父类people,不过在子类中不写任何代码

3:在main方法中创建对象
package Testextends;

public class Test01 {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        
    }
}

如图,通过java的反射机制,我们就可以看出teacher继承了people的方法,

不过我们仔细看,私有方法eat并没有显示出来。

说明子类使用不了父类的私有方法和变量,只能使用非私有方法和变量

继承中成员变量的使用:

 1:继承中成员变量不重名:

首先我们需要明白一个规则,我创建了一个对象,如果我想调用这个对象里面的属性,我先是在这个对象的范围里面查找,找不到,再去查父类

明白了这个规则

我们看下面的代码:

在子类中创建一个变量zi = 10

package Testextends;

public class Teacher extends People{
    int zi = 10;
}

在父类中创建一个变量fu = 100 

package Testextends;

public class People {
    String name;
    int age;
    private int gender;
    int fu = 100;
    public void work(){
        System.out.println("工作");
    }
    private void eat(){
        System.out.println("吃饭");
    }
}

在主函数中创建父类对象 

 仔细看上面的代码:

父类调用子类的特有变量(zi)报错,这也印证了上面的规则。

2:继承中成员变量重名:

重名的话,很好理解,就是直接覆盖

 继承中成员方法的使用:

其实和上面的差不多,如果不重名,则这个方法就算子类的特有方法

如果重名,那这个就算子类重写了父类的方法,直接覆盖掉了

注意点:

如果这个时候来一行代码:

使用父类来创建一个子类的对象

People people1 = new Teacher();

这个时候的成员变量和成员方法的结果就需要特别注意了:

package Testextends;

public class Teacher extends People{
    int age = 18;
    public void work(){
        System.out.println("教师工作");
    }
}
package Testextends;

public class People {
    String name;
    int age = 100;
    private int gender;
    public void work(){
        System.out.println("工作");
    }
    private void eat(){
        System.out.println("吃饭");
    }
}
        People people1 = new Teacher();
        System.out.println(people1.age);
        people.work();
        teacher.work();
        people1.work();

输出结果:

100
工作
教师工作
教师工作

我们可以发现,对于成员变量来说,age的值还是父类的值,对于成员方法来说,work方法的内容却变成了子类重写过的内容

所以我们可以总结:

 继承中:

成员变量访问特点:看等号左边是谁,先调用谁中的成员变量

如上面People people1 = new Teacher();等号左边是父类(people),那成员变量的值就是左边

成员方法访问特点:看new的是谁,先调用谁中的方法

还是上面这个例子,调用的方法就是Teacher重写过的方法。

方法重写: 

方法重写(Method Overriding)是指子类重写(覆盖)其父类中具有相同名称相同参数列表返回类型的方法。

检测是否为重写方法:在该方法上写

  @Override

方法重写和方法重载的区别:

方法重载(Method Overloading)指的是在同一个类中可以有多个同名的方法,但这些方法的参数列表必须不同(包括参数的类型、顺序或个数)。在调用这些同名方法时,编译器会根据传入的参数类型来确定调用哪个重载的方法。

这是方法的重载

public class OverloadExample {
    public void printInfo(int num) {
        System.out.println("Number: " + num);
    }

    public void printInfo(String str) {
        System.out.println("String: " + str);
    }

    public void printInfo(int num1, int num2) {
        System.out.println("Numbers: " + num1 + " and " + num2);
    }

    public static void main(String[] args) {
        OverloadExample example = new OverloadExample();
        example.printInfo(10); // 调用第一个printInfo方法
        example.printInfo("Hello"); // 调用第二个printInfo方法
        example.printInfo(5, 8); // 调用第三个printInfo方法
    }
}

这是方法的重写:

class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.sound(); // 输出:Animal makes a sound

        Dog dog = new Dog();
        dog.sound(); // 输出:Dog barks
    }
}

@Override判断此方法是否是重写方法,如果不是就会报错

重写的注意事项:

 1:子类重写父类方法,权限必须要保证大于等于父类权限,这里的权限指的是访问权限

(子类中重写的方法不能使用比父类中被重写的方法更严格的访问修饰符)

权限排序:public > protected > 默认(什么都不加)> private 

2:父类私有方法,构造方法,静态方法

私有方法不能继承,也不能重写

构造方法不可以继承,不能重写

静态方法可以继承,不能重写

3:子类重写父类方法之后,返回值得是父类方法返回值得子类类型

重写的使用场景:

  1. 实现多态性:方法重写是实现运行时多态性的一种重要手段。通过子类重写父类方法,可以在运行时根据对象的实际类型来调用相应的方法,实现动态绑定。

  2. 修改父类方法的行为:有时候子类可能需要修改父类方法的行为或者提供更具体的实现。通过重写父类方法,子类可以根据自己的需求来实现特定的逻辑。

  3. 扩展父类方法的功能:在某些情况下,子类可能需要在父类方法的基础上添加额外的逻辑或功能。通过重写父类方法并在其中调用父类的原方法,可以实现逻辑的扩展。

  4. 更好地适应子类的需求:通过重写父类方法,子类可以更好地适应自身的特性和需求,从而提高代码的灵活性和可维护性。

(from GPT)

super和this:

先来看一个案例:

我在这里只初始化了一个子类对象,只调用了子类的构造方法。

可是连通父类的构造方法一起调用了。

所以,我们可以得出:

1:new子类对象时,会先初始化父类(父类的无参构造方法)

2:原因:

        每个构造方法的第一行,默认会有一个super(),jvm自动提供

        super代表父类的无参构造

 了解了这个案例之后:

super的使用:

1:概述:代表的是父类的引用
2:使用:

        a.调用父类的构造方法:在子类的构造方法中使用super关键字可以调用父类的构造方法。这样可以确保在子类对象被实例化时,父类的构造方法也会被执行。如果不显式使用super关键字调用父类构造方法,Java编译器会默认插入调用父类的无参构造方法。

        super() -> 调用父类无参构造

        super(实参)->调用父类有参构造

         b.调用父类的成员变量和方法:在子类中使用super关键字可以访问调用父类的成员变量和方法

        super.成员变量名

        super.成员方法名(实参)

public class Fu {
    int num = 10;
    public Fu(){
        System.out.println("我是父类中的无参构造");
    }

    public Fu(int data){
        System.out.println("我是父类中的有参构造");
    }

    public void method(){
        System.out.println("我是父类中的method方法");
    }
}

```

```java
public class Zi extends Fu{
    int num = 100;
    public Zi(){
        super();//调用父类中的无参构造
        System.out.println("我是子类中的无参构造");
    }

    public Zi(int num){
        super(10);//调用父类的有参构造
        System.out.println("我是子类中的有参构造");
    }

    public void method(){
        super.method();//调用父类的method方法
        System.out.println("我是子类中的method方法");
        System.out.println(num);//子类自己的
        System.out.println(super.num);//调用父类的num
    }
}

```

```java
public class Test01 {
    public static void main(String[] args) {
        Zi zi = new Zi();
        System.out.println("============");
        Zi zi1 = new Zi(10);
        System.out.println("============");
        Zi zi2 = new Zi();
        zi2.method();

    }
}

this的使用:

1.this概述:代表的是当前对象(哪个对象调用的this所在的方法,this就代表哪个对象)

当前对象:指的是指定上下文中的实例化对象

public class MyClass {
    private int num;

    public void setNum(int num) {
        this.num = num; // 在这里,this表示当前对象的引用,指正在调用setNum方法的对象
    }
}

MyClass obj = new MyClass();
obj.setNum(10);
// 当调用setNum方法时,this表示obj这个实例化的对象

这里面的obj就是当前对象

2.作用:

  a.区分重名的成员变量和局部变量

  b.调用当前对象中的成员

3.使用:
  a.调用当前对象的构造:在构造中写

    this():调用当前对象的无参构造

    this(实参):调用当前对象的有参构造

public class MyClass {
    private int num;

    public MyClass() {
        this(0); // 在这里,this表示当前对象的引用,指构造当前对象的实例
    }

    public MyClass(int num) {
        this.num = num;
    }
}

MyClass obj = new MyClass();
// 当创建MyClass对象时,this表示的是正在实例化的MyClass对象

当在主函数中实例化一个对象后,就算你调用的是一个无参构造方法, 在无参构造中调用了this(0)

还是会用那个有参的构造方法实例化。

  b.调用当前对象的成员变量:

    this.成员变量名

public class MyClass {
   
    int m;
    public void setNum(int num){
        this.num = num;
        System.out.println(this.num);
    }
    private int num;
    public void method(){
        int num = 100;
        System.out.println(num);
        System.out.println(this.num);
    }
}

 当全局变量有一个和方法中的变量重名的时候(这段代码中的num),我们就可以用this来区分全局变量和局部变量

  c.将当前对象作为参数传递给其他方法:
public class MyClass {
    private int num;

    public void method() {
        newMethod(this); // 在这里,this表示当前对象的引用,指调用method方法的对象
    }

    public void newMethod(MyClass obj) {
        // 在这里,obj参数接收到的是this所指向的对象
    }
}

MyClass obj = new MyClass();
obj.method();
// 当调用method方法时,this表示的是obj这个实例化的对象
4:注意点:

不管是super还是this,只要在构造中使用,都必须在第一行,所以二者不能同时手写出来

继承的特点:

1.继承只支持单继承,不能多继承

  public class A extends B,C{}  -> 错误

2.继承支持多层继承

  public class A extends B{}

  public class B extends C{}

3.一个父类可以有多个子类

  public class A extends C{}

  public class B extends C{}

 继承(为父类私有属性赋值)

首先我们知道,父类的私有属性,子类是没有办法访问的,那这个时候如果我们还是要去修改值怎么办呢?

两种方法:

1:利用set赋值

其实这个就是去构造一个javabean对象,然后通过get和set方法对这个变量进行赋值

public class Employee {
    private String name;
    private int age;

    public Employee() {
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void work(){
        System.out.println("工作");
    }
}
Teacher teacher = new Teacher();
        teacher.setName("张三");
        teacher.setAge(18);
        System.out.println(teacher.getName()+"..."+teacher.getAge());
2:利用构造方法赋值

在子类的构造方法中利用super(参数)来构造

其实利用构造方法赋值本质上也是利用了set方法。

public class Manager extends Employee{
    public Manager() {
    }

    public Manager(String name, int age) {
        super(name, age);
    }
}
Manager manager = new Manager("金莲", 24);
        System.out.println(manager.getName()+"..."+manager.getAge());

继承中的抽象类: 

首先我们设想一个场景:三角形类,正方形类,圆形类,他们中都有一个方法是求面积

根据我们之前学习的继承,我们很容易想到将这个求面积的方法抽象出来成一个形状类

不过我们再看,三个形状求面积的公式不同,我们不能想之前那样抽象成一个方法。

所以就需要我们的抽象类。

抽象类的定义:

Java中的抽象类是指不能被实例化的类,通常用于定义一些共同的方法和属性,让子类来实现具体的逻辑。抽象类通常包含抽象方法(没有具体实现的方法)和非抽象方法(有具体实现的方法)。

 抽象类案例:

public abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    // 抽象方法
    public abstract void sound();

    // 非抽象方法
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

在上面的例子中,Animal类是一个抽象类,包含一个抽象方法sound()和一个非抽象方法sleep()。任何继承Animal类的子类都必须实现sound()方法,否则会被标记为抽象类。

要实现一个继承自抽象类的子类,需要在子类中实现所有的抽象方法,如下所示:

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void sound() {
        System.out.println(name + " is barking");
    }
}

在这个例子中,Dog类继承自Animal类,并且实现了抽象方法sound()。这样,Dog类就可以被实例化并调用其方法了。

特点:
  1. 继承了抽象类,就必须重写所有的抽象方法,由此我们也可以得出,抽象方法不能用private,final 或者static关键字来修饰,这是因为定义抽象方法的目的就是想将方法的具体实现延迟到子类,最终是要被子类重写的,而private,final,static这几个关键字都和“方法重写”的目的背道而驰。
  2. 拥有抽象方法的类一定是抽象类;但是抽象类不一定有抽象方法。这个如何理解,如果一个类你一开始定义为普通类,后面想用抽象方法,就会报错,必须加上abstract,但是一开始,如果你定义了一个抽象类,你可以不写抽象方法,后面再补也许
  3. 抽象类不能被实例化,只能创建其非抽象子类对象 。既然抽象类不能实例化,那抽象类的构造方法用处是什么呢?我们之前讲过继承可以为父类私有属性赋值,同时我们也可以利用这一点

package Testextends;

public class Manager extends Person{
    public Manager(String name, int age) {
        super(name, age);
    }
}


package Testextends;

public abstract class Person {
    String name;
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}




package Testextends;

public class Test01 {
    public static void main(String[] args) {
        Manager manager = new Manager("张三",18);
        System.out.println(manager.getName()+manager.getAge());
    }
}

在这个案例中,Manager继承了抽象类Person,并且super(name, age);

就和上面那张图一样,将抽象类中的name和age属性赋上了值。

Java接口:

接口的定义:

在Java中,接口(Interface)是一种抽象数据类型,它定义了一组方法的签名,但并没有提供方法的具体实现。接口可以看作是一种约定或契约,让类来实现这些方法以确保特定的行为或功能。

来举一个生活中例子把:

当我们订购外卖时,我们可以将食品平台比作接口,而餐厅则是实现了这个接口的具体类。在这个例子中,食品平台定义了一组规范,比如订单方法、支付方法等,餐厅则按照这些规范实现了具体的行为。

接口代表食品平台提供的服务,而餐厅作为实现类,提供了具体的食品和服务。我们可以通过食品平台下单、支付等方法与不同的餐厅交互,而不需要关心具体餐厅是如何处理订单和支付的。

这样的设计使得我们可以更加灵活地选择不同的餐厅进行订餐,并且不需要知晓每个餐厅的具体细节,只需按照规范进行操作就可以了。

接口的理解(特点):

  1. 接口可以理解为一个抽象类,实现这个接口的类必须重写这个接口里面的所有方法,但是接口和抽象类不同的是:接口可以实现多实现,这就和之前的继承不同了,也算弥补了java不能多继承的缺点,子类可以继承一个父类的同时实现一个或者多个接口
  2. 接口里面的方法默认都是abstract的,在JDK8中添加了default和static,在JDK9中添加了private,如果在接口中不去特意定义default,private和static这三个关键字的话,那是不是接口中的方法默认都是abstract
  3. 接口中的变量默认都是静态常量,即使用 public static final 修饰的常量。
  4. 同时接口也不能被实例化,都只能通过子类来new

接口的使用:

关键字:
   a.interface 接口
      public interface 接口名{}
   
  b.implements 实现
      实现类 implements 接口名{}

第一个案例:最简单的一个接口的使用
public interface USB {
    public abstract void open();
    public abstract void close();
}


public class Mouse implements USB{
    @Override
    public void open() {
        System.out.println("鼠标打开");
    }

    @Override
    public void close() {
        System.out.println("鼠标关闭");
    }
}


public class Test01 {
    public static void main(String[] args) {
        Mouse mouse = new Mouse();
        mouse.open();
        mouse.close();
    }
}
第二个案例:定义了默认方法:
public interface Greetable {
    // 默认方法
    default void greet() {
        System.out.println("Hello, nice to meet you!");
    }
}

public class Person implements Greetable {
    public static void main(String[] args) {
        Person p = new Person();
        p.greet(); // 调用接口中的默认方法
    }
}

对于默认方法,需要通过接口的实例来调用

第三个案例:定义了静态方法:
public interface Calculator {
    // 静态方法
    static int add(int a, int b) {
        return a + b;
    }
}

public class TestCalculator {
    public static void main(String[] args) {
        int sum = Calculator.add(10, 20); // 调用接口中的静态方法
        System.out.println("Sum: " + sum);
    }
}

静态方法直接用接口名就可以调用

第四个案例:定义了私有方法
public interface Calculation {
    default int add(int a, int b) {
        return performAddition(a, b); // 调用私有方法
    }

    private int performAddition(int a, int b) {
        return a + b;
    }
}

public class TestCalculation implements Calculation {
    public static void main(String[] args) {
        TestCalculation calc = new TestCalculation();
        int sum = calc.add(10, 20); // 调用接口中的默认方法
        System.out.println("Sum: " + sum);
    }
}

在这个例子中,接口 Calculation 中定义了一个默认方法 add,并在该默认方法中调用了一个私有方法 performAddition。私有方法被用来实现具体的逻辑,但只能在接口内部使用,外部无法访问。

Java多态:

多态是指同一个方法调用,在不同对象上有不同的表现形式。

这样讲其实蛮难理解:

举一个例子:

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }
    void lookDoor(){
        System.out.println("Dog lookDoor");
    }
}

class Cat extends Animal {
    void makeSound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog(); // 编译时类型为Animal,运行时类型为Dog
        animal.makeSound(); // 运行时调用Dog类的makeSound方法
        Animal animal = new Cat(); // 运行时类型为Cat
        animal.makeSound(); // 运行时调用Cat类的makeSound方法
    }
}




首先创建一个动物类Animal类,狗dog类,猫cat类

dog和cat同时继承Animal,并且重写了父类的makeSound,父类引用指向子类对象

这是我们就会发现控制台输出了:

Dog barks

Cat meows

这就是多态,相同的引用到不同的对象上,有不同的表现形式。

注意点:由父类引用指向子类对象创建出来的对象不能调用自己的特有方法

比如用Animal animal = new Dog();创建了一个对象animal,这个animal就不能调用Dog类中特有的方法lookDoor

 多态的前提:

  1.   必须有子父类继承或者接口实现关系
  2.   必须有方法的重写(没有重写,多态没有意义),多态主要玩儿的是重写方法
  3.   new对象:父类引用指向子类对象,Fu fu = new Zi() -> 理解为大类型接收了一个小类型的数据 ->比如  double b = 10
  4.   多态下不能直接调用子类特有功能(这一点和继承一样)

多态的条件下成员的访问特点

 看new的是谁,先调用谁中的成员方法,子类没有,找父类

这一句话和继承的时候学的是一样的

比如:

成员变量的使用:
public class Fu {
    int num = 1000;
}
public class Zi extends Fu{
    int num = 100;
}
public class Test01 {
    public static void main(String[] args) {
        Fu fu = new Zi();
        System.out.println(fu.num);
    }
}

new的是子类,所以输出的就是子类的成员变量

成员方法的使用:
public class Fu {
    int num = 1000;
    public void method(){
        System.out.println("我是父类中的method方法");
    }
}

public class Zi extends Fu{
    int num = 100;

    public void method(){
        System.out.println("我是子类中的method方法");
    }
}

public class Test01 {
    public static void main(String[] args) {
        Fu fu = new Zi();
        System.out.println(fu.num);//父类中的num
        fu.method();//子类中重写的method方法
    }
}

这里也是同理。

多态的优点:

要讲多态的优点,我们先来举个例子吧,这个例子的目的其实也不是说能说明多态的优点,就是做一个对比,已多态的方式创建对象和普通的方式创建对象

问题描述:


  如果使用原始方式new对象(等号左右两边一样),既能调用重写的,还能调用继承的,还能调用自己特有的成员
  但是多态方式new对象,只能调用重写的,不能直接调用子类特有的成员,那为啥还要用多态呢?

既然我们学了,那肯定就有学的用处:

先来看一下优缺点吧:

多态方式和原始方式new对象的优缺点:


  原始方式:
    a.优点:既能调用重写的,还能调用父类非私有的,还能调用自己特有的
    b.缺点:扩展性差
        
  多态方式:
    a.优点:扩展性强
    b.缺点:不能直接调用子类特有功能       

这样讲肯定太宽泛了:

具体的代码案例:

package Testduotai;

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    void makeSound() {
        System.out.println("Cat meows");
    }
}
public class Test01 {
    public static void main(String[] args) {
        //不用多态,用普通的方法
        Dog dog = new Dog();
        method01(dog);
        Cat cat = new Cat();
        method02(cat);
    }
    public static void method01(Dog dog){
        dog.makeSound();
    }
    public static void method02(Cat cat){
        cat.makeSound();
    }
}

这段代码是用普通的创建对象的方式:

这段代码中:method01 和method02这两个方法的参数分别是dog和cat,

因为Dog和Cat两个对象之间没有什么关系,所以,我们需要建两个方法,方法的参数分别是dog和cat

但是如果我们用了多态的创建对象的方式:

public static void main(String[] args) {
        //不用多态,用普通的方法
        Dog dog = new Dog();
        method01(dog);
        Cat cat = new Cat();
        method02(cat);
        //用多态的方式
        Animal animal = new Dog();
        method(animal);
        Animal animal1 = new Cat();
        method(animal1);
    }
    public static void method(Animal animal){
        animal.makeSound();
    }

我们用一个方法就可以实现上面的功能,并且如果动物的种类多了,用这种方式可以更好的实现代码的复用性

总结起来就是:

形参传递父类类型,调用此方法父类类型可以接收任意它的子类对象
传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法

 

多态中的转型

一:向上转型:

        1.父类引用指向子类对象
          好比是: double b = 1;

二:向下转型:

        1.向下转型:好比强转,将大类型强制转成小类型
        2.表现方式:
          父类类型 对象名1 = new 子类对象() -> 向上转型 -> double b = 1
          子类类型 对象名2 = (子类类型)对象名1 -> 向下转型 -> int i = (int)b
        3.想要调用子类特有功能,我们就需要向下转型 

具体讲一下第三点调用子类特有功能:

看代码:

public abstract class Animal {
    public abstract void eat();
}
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    //特有方法
    public void catchMouse(){
        System.out.println("猫会捉老鼠");
    }
}
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗啃骨头");
    }

    //特有方法
    public void lookDoor(){
        System.out.println("狗会看门");
    }
}
public class Test01 {
    public static void main(String[] args) {
        //多态new对象  向上转型
        Animal animal = new Dog();
        animal.eat();//dog重写的
        //animal.lookDoor();//多态不能调用子类特有功能

        //向下转型
        Dog dog = (Dog) animal;
        dog.eat();
        dog.lookDoor();
    }
}

这段代码中,猫和狗都有一个自己特定的方法捉老鼠和看门,

父类对象animal如果想要调用这两个方法,就需要向下转型

转型时会遇到的问题:

如果等号左右两边类型不一致,会出现类型转换异常(ClassCastException)

在我们进行类型转换的时候难免会发生这个异常

比如我们去调用一个别人写好的方法,我们不知道类型,我们直接进行强转,就很容易错

解决办法:


  在向下转型之前,先判断类型 关键字:instanceof
  判断结果是boolean型
  对象名 instanceof 类型 -> 判断的是关键字前面的对象是否符合关键字后面的类型

具体看代码:

public static void method(Animal animal){
        if(animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.lookDoor();
        }
        if(animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }

判断传进来的类型再进行输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1873495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java养老护理助浴陪诊小程序APP源码

💖护理助浴陪诊小程序💖 一、引言:养老新趋势🌱 在快节奏的现代生活中,养老问题逐渐成为了社会关注的焦点。如何为老年人提供便捷、贴心的服务,让他们晚年生活更加安心、舒适,是我们每个人都需…

BUUCTF--WEB

首頁 - OWASP Top 10:2021 [极客大挑战 2019]EasySQL 类型:sql注入 使用万能密码 flag{f580db5b-c0c9-4b13-bfb6-adfa525c93f5} [极客大挑战 2019]Havefun 类型:代码审计 F12打开浏览器控制台 GET请求,在url添加参数/?cat=dog访问 返回flag{f60c7d5c-9f44-4e92-88c0…

驱动LSM6DS3TR-C实现高效运动检测与数据采集(6)----FIFO数据读取与配置

驱动LSM6DS3TR-C实现高效运动检测与数据采集.6--FIFO数据读取与配置 概述视频教学样品申请源码下载主要内容生成STM32CUBEMX串口配置IIC配置CS和SA0设置ICASHE修改堆栈串口重定向参考驱动程序FIFO参考程序初始化管脚获取ID复位操作设置量程BDU设置设置速率FIFO读取程序设置FIFO…

鸿蒙开发 之 健康App案例

1.项目介绍 该项目是记录用户日常饮食情况,以及针对不同食物摄入营养不同会有对应的营养摄入情况和日常运动消耗情况,用户可以自己添加食品以及对应的热量。 1.1登陆页 1.2饮食统计页 1.3 食物列表页 2.登陆页 2.1自定义弹框 import preferences from oh…

词向量模型

文章目录 RNN词向量模型模型整体框架训练数据构建CBOW与Skip-gram模型负采样 RNN 卷积神经网络(CNN)主要应用计算机视觉,而递归神经网络(RNN)主要应用于自然语言处理。 递归神经网络会涉及处理之前所有的数据&#x…

Linux高级编程——线程

pthread 线程 概念 :线程是轻量级进程,一般是一个进程中的多个任务。 进程是系统中最小的资源分配单位. 线程是系统中最小的执行单位。 优点: 比多进程节省资源,可以共享变量 进程会占用&am…

【漏洞复现】金和OA 未授权访问

【产品介绍】 金和OA协同办公管理系统C6软件(简称金和OA),本着简单、适用、高效的原则,贴合企事业单位的实际需求,实行通用化、标准化、智能化、人性化的产品设计,充分体现企事业单位规范管理、提高办公效…

ubuntu22.04编译安装tesseract

1、 为什么用自己编译安装,而不采用apt安装? 由于tesseract有很多依赖包,直接用deb包或者rpm包等安装包安装很复杂,不一定能成功安装。 2、安装基本的依赖包 sudo apt update sudo apt install g autoconf automake libtool pkg…

如何利用ChatGPT寻找科研创新点?分享5个有效实践技巧

欢迎关注:智写AI,为大家带来最酷最有效的智能AI学术科研写作攻略。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥交流:yida985 地表功能最强大的高级学术专业版已经开放,拥有全球领先的GPT学术科研应用,有兴趣…

44 mysql batch insert 的实现

前言 我们这里 来探讨一下 insert into $fields values ($values1), ($values2), ($values3); 的相关实现, 然后 大致来看一下 为什么 他能这么快 按照 我的思考, 应该里里面有 批量插入才对, 但是 调试结果 发现令我有一些意外 呵呵 果然 只有调试才是唯一的真理 相比于 …

如何用一个二维码实现企业固定资产管理?

固定资产管理中普遍存在盘点难、家底不清、账实不一致、权责不清晰等问题。如果平时不规范化执行,年终面对上上下下、大大小小、成百上千件物资要进行盘点整理的时候,会是十分痛苦且低效的事情。 今天这篇文章就来给大家推荐几家便宜好用的二维码固定资…

学校选用SOLIDWORKS教育版进行授课的理由

在当代的工程与技术教育领域,计算机辅助设计软件(CAD)已经变成了一个不可缺少的教学辅助工具。SOLIDWORKS作为一个功能齐全且用户友好的CAD软件,其教育版本在学校教学环境中受到了广泛的欢迎。本文将对学校教学中选用SOLIDWORKS版…

最实用的美国TikTok选品策略之跟卖亚马逊

美国电商市场,TikTok好比是一个快速成长的大龄儿童,亚马逊(Amazon)则是一个历经风雨的成熟中年人。TikTok现阶段还处于大量招商,引入优质品牌、卖家初期,许多品类并没有太多优质的商品售卖,竞争…

华为HCIA综合实验(结合前几期所有内容)

第一章 实验目的 (1)配置Telnet,要求所有网络设备支持远程管理,密码为admin(2)配置Trunk,交换机之间的链路均为Trunk模式(3)配置VLAN,在SW2和SW3上创建相关…

前端开发的工厂设计模式

在前端开发中,工厂设计模式(Factory Pattern)是一种非常有用的设计模式,能够帮助我们在创建对象时减少代码的重复性和复杂性。 一、工厂设计模式概述 工厂设计模式是一种创建型设计模式,主要目的是定义一个用于创建对…

探索绿色消费新纪元:消费增值模式

大家好!我是来自一家备受瞩目的科技公司的产品经理,我叫吴军。今天,我非常荣幸能与大家分享一种正在市场上引起广泛关注的创新商业模式——消费增值模式。 近年来,随着环保意识的日益增强,绿色消费逐渐成为了新时代的消…

MySQL中的存储引擎

介绍 存储引擎就是存储数据,建立索引,更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可以称为表类型(即一个数据库下的表可以选择不同的存储引擎)。 1. 如何查看一个…

一看就会的Jmeter分布式压测实战技巧详解

一、什么是jmeter分布式压测? jmeter分布式压测:指将需要模拟的大量并发用户数分发到多台压力机,使jmeter拥有更大的负载量,满足真实业务场景(高并发场景)。可以理解为通过一个Jmeter控制台来远程控制多个…

C++项目实践学习笔记---DLL

linux守护进程 守护进程或精灵进程(Daemon):以后台服务方式运行的进程,它们不占用终端(Shell),因此不会受终端输入或其他信号(如中断信号)的干扰守护进程有如下特点。 &…

【计算机毕业设计】084基于微信小程序大学生心理健康服务

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…