一文带你搞懂Java中的继承&多态&static

一文带你搞懂Java中的继承&多态&static

static:

当你在一个标准的JavaBean类中使用static去修饰一个数据时候他就变成共享数据了 共享的是地址 所以你new多少次创建了多少对象都是共享一个static静态数据;

演示:

public class MyBean {
    private int nonStaticVar;
    private static int staticVar;
    
    public MyBean(int nonStaticVar, int staticVar) {
        this.nonStaticVar = nonStaticVar;
        MyBean.staticVar = staticVar;
    }
    
    public void printVars() {
        System.out.println("nonStaticVar = " + nonStaticVar);
        System.out.println("staticVar = " +   );
        System.out.println();
    }
}
// 创建两个MyBean对象
MyBean bean1 = new MyBean(10, 20);
MyBean bean2 = new MyBean(30, 40);

// 输出两个对象的成员变量
bean1.printVars();
bean2.printVars();

// 修改静态变量的值
MyBean.staticVar = 50;

// 再次输出两个对象的成员变量,静态变量的值已经被修改,且两个对象的静态变量值相同
bean1.printVars();
bean2.printVars();

输出结果:

nonStaticVar = 10
staticVar = 20

nonStaticVar = 30
staticVar = 20

nonStaticVar = 10
staticVar = 50

nonStaticVar = 30
staticVar = 50

static修饰的数据是随着对象的加载而加载你只要是加载了就里面初始化 不管你有没有new;

static有两种调用方式 :

public class MyClass {
    static int myStaticVariable = 10;
    int myInstanceVariable = 20;
    
    static void myStaticMethod() {
        System.out.println("This is a static method in MyClass.");
    }
    
    void myInstanceMethod() {
        System.out.println("This is an instance method in MyClass.");
    }
}
  1. 使用类名调用:

    MyClass.myStaticVariable = 30;
    MyClass.myStaticMethod(); // 输出 "This is a static method in MyClass."
    
  2. 使用对象调用:

    MyClass obj = new MyClass();
    obj.myStaticVariable = 50; // 也可以这样使用对象来访问静态变量
    obj.myInstanceVariable = 40;
    obj.myStaticMethod(); // 输出 "This is a static method in MyClass."
    obj.myInstanceMethod(); // 输出 "This is an instance method in MyClass."
    

static注意事项:

1.静态方法中,只能访问静态;

2.非静态方法可以访问所有;

3.静态方法中没有this关键字;

  • 演示:(静态方法中只能访问静态成员(变量和方法),不能直接访问非静态成员。下面是一个演示:)–对标1&2
public class MyClass {
    static int staticVar = 10;
    int nonStaticVar = 20;

    static void myStaticMethod() {
        System.out.println("In myStaticMethod:");
        System.out.println("Static variable: " + staticVar);
        // System.out.println("Non-static variable: " + nonStaticVar); // 静态方法中 无法 直接访问非静态变量!
        myAnotherStaticMethod();
        // myAnotherNonStaticMethod(); // 静态方法中无法直接访问非静态方法!
        System.out.println();
    }

    static void myAnotherStaticMethod() {
        System.out.println("In myAnotherStaticMethod:");
        System.out.println("Static variable: " + staticVar);
        // System.out.println("Non-static variable: " + nonStaticVar); // 静态方法中无法直接访问非静态变量
        System.out.println();
    }

    void myNonStaticMethod() {
        System.out.println("In myNonStaticMethod:");
        System.out.println("Static variable: " + staticVar);
        System.out.println("Non-static variable: " + nonStaticVar);
        myAnotherStaticMethod();
        myAnotherNonStaticMethod();
        System.out.println();
    }

    void myAnotherNonStaticMethod() {
        System.out.println("In myAnotherNonStaticMethod:");
        System.out.println("Static variable: " + staticVar);
        System.out.println("Non-static variable: " + nonStaticVar);
        System.out.println();
    }
    
    public static void main(String[] args) {
        MyClass.myStaticMethod(); // 调用静态方法
        MyClass obj = new MyClass();
        obj.myNonStaticMethod(); // 调用非静态方法
    }
}

输出结果:

In myStaticMethod:
Static variable: 10

In myAnotherStaticMethod:
Static variable: 10

In myNonStaticMethod:
Static variable: 10
Non-static variable: 20

In myAnotherStaticMethod:
Static variable: 10

In myAnotherNonStaticMethod:
Static variable: 10
Non-static variable: 20

总结:

我们可以看到,在静态方法myStaticMethod()中,我们无法直接访问非静态成员变量myNonStaticMethod和非静态方法myAnotherNonStaticMethod。而在非静态方法myNonStaticMethod()中,我们既可以访问静态成员变量和静态方法,也可以直接访问非静态成员变量和非静态方法。

  • 演示:
public class MyClass {
    int var = 10;

    void myMethod() {
        System.out.println("In myMethod:");
        System.out.println("Instance variable: " + var);
        System.out.println("Instance variable using this: " + this.var);
        System.out.println();
    }

    static void myStaticMethod() {
        System.out.println("In myStaticMethod:");
        // System.out.println(this.var); // 静态方法中没有this关键字
        System.out.println();
    }
    
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.myMethod(); // 调用非静态方法
        MyClass.myStaticMethod(); // 调用静态方法
    }
}

输出:

In myMethod:
Instance variable: 10
Instance variable using this: 10

In myStaticMethod:In myStaticMethod:
10

总结:

我们可以看到,在非静态方法myMethod()中,我们可以使用this关键字来访问实例变量,并且可以通过this.var来访问实例变量。而在静态方法myStaticMethod()中,我们尝试使用this.var来访问实例变量,但系统编译时会提示错误信息,因为在静态方法中this关键字是不可用的。

继承

概念不用说!那么子类到底能继承父类中的哪些内容呢?

1.构造方法

  • 非私有 ==》不能继承

  • private ==》不能继承

  • 演示:

    class Animal {
        private String name;
      
        public Animal(String name) {   // 父类构造方法
            this.name = name;
        }
      
        public String getName() {
            return name;
        }
    }
    
    class Dog extends Animal {
        private int age;
      
        public Dog(String name, int age) {  // 子类构造方法
            this.age = age;
        }
      
        public int getAge() {
            return age;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog("Bobby", 3);
            String name = dog.getName();
            int age = dog.getAge();
            System.out.println("Name: " + name + ", Age: " + age);
        }
    }
    

    总结:

    在上面的示例代码中,Animal是父类,Dog是子类,Animal类有一个构造方法,用于初始化name成员变量,Dog类有一个构造方法,用于初始化age成员变量,并调用了父类的构造方法。

    我们在主方法中创建了一个Dog对象,这里我们传递了两个参数(“Bobby”, 3),它们分别被用于初始化nameage成员变量。接着我们调用getName()方法和getAge()方法分别获取对象的名字和年龄,并打印输出。

    运行程序后,你会发现会抛出一个编译错误,提示我们没有在Dog类中找到符合参数列表(String, int)的构造方法。这是因为我们在Dog类中没有显式地定义一个构造方法,编译器会自动生成一个无参构造方法,但是这个无参构造方法并没有调用父类的构造方法,所以会导致错误。

    因此,结论是:子类不能继承父类的构造方法,但是子类中的构造方法可以调用父类的构造方法来完成对父类成员变量的初始化工作。在子类中调用父类的构造方法,需要使用super()关键字来实现。

    改进一下加上super:

    class Animal {
        private String name;
      
        public Animal(String name) {   // 父类构造方法
            this.name = name;
        }
      
        public String getName() {
            return name;
        }
    }
    
    class Dog extends Animal {
        private int age;
      
        public Dog(String name, int age) {  // 子类构造方法
            super(name);  // 调用父类的构造方法
            this.age = age;
        }
      
        public int getAge() {
            return age;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog("Bobby", 3);
            String name = dog.getName();
            int age = dog.getAge();
            System.out.println("Name: " + name + ", Age: " + age);
        }
    }
    

    总结:

    在上述示例中,我们在Dog类的构造方法中添加了一行super(name),来调用父类的有参构造方法来初始化name成员变量。这样,在创建Dog对象的时候,会先调用父类的构造方法来初始化父类的成员变量,再调用子类的构造方法来初始化子类的成员变量。

    运行程序后,会输出如下结果:

    Name: Bobby, Age: 3
    

    因此,我们可以得出结论:在子类的构造方法中,使用super()关键字可以调用父类的构造方法来完成对父类成员变量的初始化工作,进而完成子类的初始化。

    另外你需要注意:

    如果父类的构造方法是私有的,子类是无法直接调用父类的构造方法的,包括使用super()关键字或者显示调用父类构造方法的方式都不行。

    私有构造方法是不可继承的,子类是无法直接访问或调用父类的私有构造方法的。这是因为私有构造方法的访问权限只限于父类本身,在其他类中是无法访问的。

    因此,如果父类的构造方法是私有的,子类不能直接调用父类的构造方法,也不能使用super()关键字来调用父类的构造方法。这种情况下,子类只能通过其他可访问的方式来初始化父类的私有成员变量,例如通过提供一个公共的方法来间接访问和设置父类的私有成员变量==>这个方法就是set方法;

2.成员变量

  • 非私有 ==》能继承

  • private ==》能继承

    演示:

    class Animal {
        public String publicName;
        protected String protectedName;
        String defaultName;
        private String privateName;
    
        public Animal() {
            publicName = "Public Animal";
            protectedName = "Protected Animal";
            defaultName = "Default Animal";
            privateName = "Private Animal";
        }
         public String getPrivateName() {  // 定义公共方法访问私有成员变量
            return privateName;
        }
    }
    
    class Dog extends Animal {
        public void printNames() {
            System.out.println(publicName);      // 直接访问父类的public成员变量
            System.out.println(protectedName);   // 直接访问父类的protected成员变量
            System.out.println(defaultName);     // 直接访问父类的default成员变量
            // System.out.println(privateName);  // 无法直接访问父类的private成员变量
            // 通过调用父类的公共方法来间接访问父类的private成员变量
            System.out.println(getPrivateName());
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog();
            dog.printNames();
        }
    }
    

    总结:

    父类Animal的成员变量包括4个,无论是非私有还是私有的成员变量,子类Dog都能够继承和访问它们。子类Dog直接访问并输出了父类的public、protected和default成员变量,而对于父类的私有成员变量,子类定义了一个私有方法getPrivateName(),通过间接访问来输出。

    运行程序后,会输出如下结果:

    Public Animal
    Protected Animal
    Default Animal
    Private Animal
    

    因此,在Java中,无论是非私有成员变量还是私有成员变量,子类都能够继承父类的成员变量。

3.成员方法

  • 非私有 ==》 能继承

  • private ==》不能继承

    演示:

    class Animal {
        public void publicMethod() {
            System.out.println("Public method in Animal");
        }
    
        protected void protectedMethod() {
            System.out.println("Protected method in Animal");
        }
    
        void defaultMethod() {
            System.out.println("Default method in Animal");
        }
    
        private void privateMethod() {
            System.out.println("Private method in Animal");
        }
    }
    
    class Dog extends Animal {
        public void dogMethod() {
            System.out.println("Own method in Dog");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Dog dog = new Dog();
            dog.publicMethod();   // 继承并调用父类的public方法
            dog.protectedMethod(); // 继承并调用父类的protected方法
            dog.defaultMethod();  // 继承并调用父类的default方法
            // dog.privateMethod();  // 无法继承和调用父类的private方法
            dog.dogMethod(); // 调用子类自己的方法
        }
    }
    

    总结:

    在上述示例中,父类Animal定义了4个成员方法,包括public、protected、default和private方法。子类Dog继承了父类,并可以通过实例化对象调用父类继承的方法。公共、受保护和默认的父类方法都能够被子类继承,并且子类可以直接调用它们。但是,在子类中无法继承和调用父类的私有方法。

    运行程序后,会输出如下结果:

    Public method in Animal
    Protected method in Animal
    Default method in Animal
    Own method in Dog
    

    因此,子类继承父类时可以继承和调用父类的非私有成员方法,但无法继承和调用父类的私有成员方法。

    注意:

    你真的以为java中的继承方法是沿着继承链去一代一代找是否有需要调用的方法吗==》错

    在java中 当你使用继承的时候jvm虚拟机会创建一个虚方法表 这个虚方法表会收集一些常用的方法(非private修饰的方法、非static修饰的方法、非final修饰的方法)这些方法都会被收集到虚方法表中 后面我们继承完毕当你调用某个方法的时候java会去虚方法表中查询如果有就调用。

多态⚠️

概念:

在Java中,对象的多态是指一个对象可以被赋值给它的父类类型的变量,并且可以根据实际的对象类型来调用相应的方法。具体来说,在多态中,如果一个类A是另一个类B的子类,那么类A的对象可以被赋值给类B的变量。通过这种方式,我们可以用一个统一的父类引用来引用不同的子类对象,并且根据实际对象的类型来调用相应的方法。

演示:

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("狗发出汪汪声");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("猫发出喵喵声");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        Animal animal1 = new Animal(); // 创建一个Animal对象
        Animal animal2 = new Dog(); // 创建一个Dog对象,并将其赋值给Animal类型的变量
        Animal animal3 = new Cat(); // 创建一个Cat对象,并将其赋值给Animal类型的变量
        
        animal1.sound(); // 调用Animal类的sound方法
        animal2.sound(); // 调用Dog类的sound方法
        animal3.sound(); // 调用Cat类的sound方法
    }
}

总结:

这个例子中,我们创建了一个父类Animal和两个子类DogCat。父类Animal有一个sound方法,而子类DogCat分别覆盖了这个方法。在PolymorphismDemomain方法中,我们创建了一个Animal对象和两个子类对象,并且分别赋值给了Animal类型的变量animal1animal2animal3。当我们调用animal1.sound()时,由于animal1引用的是Animal对象,所以会调用父类Animal中的sound方法。当我们调用animal2.sound()animal3.sound()时,由于animal2animal3引用的是子类对象,所以会根据实际的对象类型来调用对应子类的sound方法。这就是多态,同一个方法在不同的对象上展现出不同的行为。

注意:访问成员变量和成员方法的规则!

在多态中,如果方法重写(override)了父类的方法,则在使用父类引用变量引用子类对象时,运行时会调用到子类的方法,这个原则称为动态多态性(Dynamic Polymorphism)。在这种情况下,编译器在编译时只能根据引用变量的类型,即左边的类型来作为判断和选择的依据,所以这个过程称为静态多态性(Static Polymorphism)。具体来说,当使用父类引用变量引用子类对象时,如果访问成员变量,则编译器只会根据引用变量的类型来决定成员变量的类型,从而确定使用哪个成员变量,即编译看左边,在运行时也看左边。而如果访问成员方法,则编译器同样只能根据引用变量的类型来决定调用哪个方法,在运行时根据对象实际类型来确定调用的是哪个方法,即编译看左边,运行看右边。

总结两句话:

  • 调用成员变量的特点是:编译看左边,运行也看左边;

  • 调用成员方法的特点:编译看左边,运行看右边;

演示:

class Animal {
    public String name = "animal";

    public void say() {
        System.out.println("我是一只动物");
    }
}

class Dog extends Animal {
    public String name = "dog";

    @Override
    public void say() {
        System.out.println("我是一只狗");
    }
}

public class PolymorphismDemo {
    public static void main(String[] args) {
        Animal animal1 = new Animal(); // 创建一个Animal对象
        Animal animal2 = new Dog(); // 创建一个Dog对象,并将其赋值给Animal类型的变量
        
        System.out.println(animal1.name); // 输出"animal",因为animal1是Animal类型的变量
        System.out.println(animal2.name); // 输出"animal",因为animal2是Animal类型的变量
        
        animal1.say(); // 输出"我是一只动物",因为animal1是Animal类型的变量
        animal2.say(); // 输出"我是一只狗",因为animal2是Dog类型的变量
    }
}

总结:

在这个例子中,我们创建了一个Animal类和一个Dog类。在主方法中,我们创建了一个Animal类型的变量animal1和一个Dog类型的变量animal2,并且分别输出它们的名称(name)和调用它们的say方法。我们可以发现,无论是animal1还是animal2,它们的名称都是从所属的类中继承而来的,而say()方法的调用结果则由对象实际类型决定,即animal1调用的是父类Animal中的say()方法,而animal2调用的是子类Dog中的say()方法,这就体现了在Java多态中,访问成员变量的特点是编译看左边,运行也看左边,而访问成员方法的特点是编译看左边,运行看右边。

你可能感兴趣的:(java,python,开发语言)