[Summary of java notes] Object-oriented programming - encapsulation, inheritance, polymorphism


foreword

Reference textbook: "Java Object-Oriented Programming Tutorial"
answer reference: chatgpt

1. Packaging

1.1, class modifier

In Java, class modifiers can be used to determine the access level and other properties of a class. These modifiers mainly include:

public: The class can be accessed by any other class.

private: The class cannot be accessed by other classes. This modifier cannot be applied directly to top-level classes, but can be applied to inner classes.

protected: This modifier cannot be applied directly to top-level classes, but can be applied to inner classes. The decorated inner class can be accessed by classes in the same package and any other class that inherits this class.

default (no modifier): If a class is not declared as public, private or protected, then it is package-private by default, that is, it can only be accessed by classes in the same package.

final: A class modified by final cannot be inherited.

abstract: abstract classes cannot be instantiated and usually contain one or more abstract methods (or no abstract methods).

static: The static modifier can only be used for inner classes, which means that the class is static and does not need to rely on instances of outer classes.

public class MyClass {
    
     // 这是一个公有类
    // 类体
}

final class MyFinalClass {
    
     // 这是一个不能被继承的类
    // 类体
}

abstract class MyAbstractClass {
    
     // 这是一个抽象类
    // 类体
}

class OuterClass {
    
    
    static class MyStaticInnerClass {
    
     // 这是一个静态内部类
        // 类体
    }
}

In C++, there is no concept similar to class modifiers in Java : In C++, access control of a class is achieved by using the public, private or protected keywords in the class body, which determine the class's members. access rights, not access rights for the entire class. In addition, there are keywords such as final and virtual in C++, but their meanings and uses are not exactly the same as those in Java.

1.2. Access rights - public, private, protected

In Java and C++, modifiers such as public, private, and protected are used to control access to class members. These members may include variables (properties), methods, inner classes, etc.
In Java:

Public members can be accessed from anywhere.

Private members can only be accessed within their own class.

protected members can be accessed in any class in the same package as well as in subclasses in different packages.

The default access level (with no modifiers) is package-level, which means that these members can be accessed from any class in the same package.

In C++:

Public members can be accessed from anywhere.

Private members can only be accessed within their own class.

Protected members can be accessed in their own class as well as in any derived classes (that is, subclasses).

Therefore, these modifiers are indeed for members of the class (that is, the properties and methods of the object). They are used to define under what circumstances these members can be accessed and modified.

1.3. Member variables

1.3.1, member variable modifier

In Java, member variables (fields) can use the following modifiers:

Access Control Modifier : Used to determine the level of access to this variable by other classes.

public: The variable is visible to all classes.

protected: The variable is only visible to classes and all subclasses in the same package.

private: The variable is only visible inside this class.

No modifiers (default): The variable is visible to classes within the same package.


static : Used to specify that the variable is static, which means that the variable belongs to the class, not an instance of the class.

final : The variable can only be assigned once and cannot be changed once assigned.

transient : The variable will not be serialized. If an object is serialized by Java's serialization mechanism, all non-transient variables will be serialized, and all transient variables will be ignored.

volatile : Tell the JVM that the variable may be accessed and modified by multiple threads at the same time, so the JVM cannot perform cache optimization on it. This helps ensure data consistency in a multi-threaded environment, but may degrade performance.

Note that, like method modifiers, not all of these modifiers can be used at the same time. For example, final and volatile cannot modify the same variable at the same time, because the value of final variables cannot be changed, and volatile is used to deal with variable values ​​that may change. changing circumstances.

1.4. Member method

1.4.1, member method modifier

In Java, member methods (including instance methods and class methods) can use the following modifiers:

Access Control Modifier : Used to determine the level of access to this method by other classes.

public: The method is visible to all classes.

protected: This method is only visible to classes and all subclasses in the same package.

private: The method is only visible inside this class.

No modifiers (default): The method is visible to classes within the same package.


static : Used to specify that the method is static, which means that the method belongs to the class, not an instance of the class.

final : The method cannot be overridden in subclasses.

abstract : This method has no specific implementation in this class and must be implemented in subclasses. If a class contains abstract methods, then the class must be declared as abstract.

synchronized : This method can only be accessed by one thread at a time.

native : The implementation of the method is provided by non-Java code such as C or C++ code.

strictfp : Used to specify that the method must follow the IEEE 754 standard to ensure accurate floating-point calculations on all platforms.

Note that not all of these modifiers can be used at the same time, for example, abstract and final cannot modify the same method at the same time, because abstract methods must be overridden in subclasses, while final methods cannot be overridden.

1.4.2. Construction method

In Java, a constructor is a special method that is used to initialize the state of an object. The constructor has the same name as the class and does not return any value (including void). Every time an object is created using the new keyword, the constructor is called.
Here is a simple example:

public class Student {
    
    
    String name;
    int age;

    // 这是一个构造方法
    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
}

In this example, the Student class has a constructor that takes two parameters: name and age. When we create a new object of the Student class, we can use this construction method, like this:
Student student = new Student("Zhang San", 20);

In this line of code, new Student("Zhang San", 20); is to call the constructor and create a new Student object.

1.4.3, class method and instance method

Class methods and instance methods are the two main types of methods in Java (and other object-oriented programming languages).

Instance Methods : An instance method is a method that an instance (object) of a class can call. These methods can access and modify the value of instance variables, and can also access other instance methods and class methods. Instance methods are defined in classes without using the static keyword. These methods are typically used to perform tasks associated with a particular object instance.
example:

public class Dog {
    
    
    private String name;

    // 这是一个实例方法
    public void bark() {
    
    
        System.out.println(name + " is barking!");
    }
}

Class Methods : Class methods, also known as static methods, are methods declared using the static keyword. These methods do not depend on an instance of a class, but are directly related to the class itself. They cannot access instance variables or instance methods of the class because they can be invoked without an instance of the class. Class methods are typically used to perform tasks associated with the class itself rather than a specific instance of the class.
example:

public class MathUtil {
    
    
    // 这是一个类方法
    public static int add(int a, int b) {
    
    
        return a + b;
    }
}

When invoked, instance methods require an instance of the class, and then invoke methods on the instance, such as dog.bark(). Whereas class methods are called directly on the class, eg MathUtil.add(5, 10).
Note: Although instance methods can access class methods, it is not allowed to access instance methods or instance variables in class methods, because instance methods and instance variables need a specific instance to refer to, and class methods can be accessed without an instance can be called.


The difference between C++ static methods and java static methods:
Yes, class methods (static methods) in Java and static methods in C++ are similar in many ways. In both languages, static methods are associated with classes, not instances of classes. This means you can call static methods without creating an instance of the class.
Static methods in neither Java nor C++ can access non-static fields of a class or call non-static methods because these are instance-specific. They are often used to implement functionality that does not directly depend on class instances.
Here's an example of a simple C++ static method:

class MyClass {
    
    
public:
    static void myStaticMethod() {
    
    
        cout << "This is a static method.";
    }
};

int main() {
    
    
    MyClass::myStaticMethod();  // 调用静态方法
    return 0;
}

In this example, myStaticMethod is a static method of MyClass, we can call it directly through the class name without creating an instance of MyClass.

1.5 packs

In Java, a package is a mechanism for organizing classes and interfaces. They are essentially namespaces that allow us to organize classes and interfaces. Use packages to avoid class name conflicts, provide access control, and efficiently manage large applications.
Here is an example of how to use the package:

package com.mycompany.myapp;

public class MyClass {
    
    
    //...
}

In this example, the MyClass class is placed in the package com.mycompany.myapp. We can access this class using the full package name and class name (also known as fully qualified name), such as com.mycompany.myapp.MyClass.
Besides that, packages are also used to import other classes and packages. For example:

import java.util.List;

Here, java.util is a package and List is an interface in this package. Using the import keyword, we can use List directly in the code without writing the fully qualified name java.util.List.


Let's continue with a few examples:

  1. Creating and using packages
    Suppose we create a package called com.myapp.shapes and define a Circle class in it.
// 文件路径: com/myapp/shapes/Circle.java

package com.myapp.shapes;

public class Circle {
    
    
    private double radius;

    public Circle(double radius) {
    
    
        this.radius = radius;
    }

    public double getArea() {
    
    
        return Math.PI * radius * radius;
    }
}

Now, we can use this Circle class in another class. Suppose this class is under the com.myapp.main package.

// 文件路径: com/myapp/main/Main.java

package com.myapp.main;

import com.myapp.shapes.Circle;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Circle circle = new Circle(5.0);
        System.out.println("The area of the circle is " + circle.getArea());
    }
}

In the Main class, we imported the com.myapp.shapes.Circle class, then created a Circle object in the main method, and called its getArea method.
2. Use package-level access control
In Java, you can use public, private, protected, and default (no modifiers) four access levels. If you don't explicitly specify modifiers on classes or members, they will have package-level access, that is, only other classes in the same package can access them.
For example, if we have a Rectangle class in the com.myapp.shapes package that has a width member with a default access level:

// 文件路径: com/myapp/shapes/Rectangle.java

package com.myapp.shapes;

class Rectangle {
    
    
    double width;
    double height;

    Rectangle(double width, double height) {
    
    
        this.width = width;
        this.height = height;
    }

    double getArea() {
    
    
        return width * height;
    }
}

Then we access the width member of Rectangle in another class Square in the same package:

// 文件路径: com/myapp/shapes/Square.java

package com.myapp.shapes;

public class Square {
    
    
    public static void main(String[] args) {
    
    
        Rectangle rectangle = new Rectangle(5.0, 5.0);
        System.out.println("The width of the rectangle is " + rectangle.width);
    }
}

In this example, the Square class can access the width member of the Rectangle because they are both in the com.myapp.shapes package. However, if you try to access the width member of Rectangle from a class in the com.myapp.main package, you will get a compile error because the width member is only visible to classes in the same package.

2. Inheritance and polymorphism

2.1 Concept

In Java, class inheritance is an important feature of Java object-oriented programming. It allows you to create new classes based on existing classes. A newly created class inherits the properties and methods of the base class (also known as the parent class or superclass), and can add new properties and methods, or override inherited methods. This mechanism can improve code reusability and maintainability.

Basic Syntax : In Java, we use the "extends" keyword to declare that a class extends another class. For example, if we have an "Animal" class, we can create a new "Dog" class where "Dog" inherits from "Animal":

public class Animal {
    
    
    public void eat() {
    
    
        System.out.println("Animal eats");
    }
}

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

In this example, the "Dog" class inherits the "eat()" method from the "Animal" class and adds its own "bark()" method.

Method coverage : Subclasses can override (override) the method of the parent class, that is, define a method in the subclass with the same name and the same parameter list as the parent class. After overriding, the subclass object calls the method defined in the subclass. For example:

public class Dog extends Animal {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("Dog eats");
    }

    public void bark() {
    
    
        System.out.println("Dog barks");
    }
}

In this example, the "Dog" class overrides the "eat()" method of the superclass "Animal", so when a "Dog" object calls the "eat()" method, it prints "Dog eats".

Restrictions on Inheritance : In Java, a class can only directly inherit from one parent class (called single inheritance). However, a class can indirectly inherit multiple parent classes, for example, if there is a class B that inherits class A, and then class C inherits class B, then class C indirectly inherits the properties and methods of class A.

Constructors and inheritance : Constructors cannot be inherited, but when creating an object of a subclass, the constructor of the parent class will be called first (if the constructor of the parent class is not explicitly called, the no-argument parameter of the parent class will be called automatically Constructor). If the parent class does not have a no-argument constructor, then other constructors of the parent class must be explicitly called in the constructor of the subclass, using the "super" keyword.

super keyword : In subclass methods, we can use the "super" keyword to call methods or properties of the parent class, including methods overridden by subclasses. This feature is very useful in dealing with some occasions that need to refer to parent class methods.

2.2 Hiding of member variables and method rewriting

Hiding of member variables : When the subclass and the parent class define member variables with the same name, the variables in the subclass will hide the variables in the parent class. This is called hiding of member variables, or variable shadowing. Their values ​​can be set independently without affecting each other. If you need to access the member variables of the parent class in the subclass, you can use the super keyword to access.
For example:

public class Parent {
    
    
    public int value = 1;
}

public class Child extends Parent {
    
    
    public int value = 2;
}

Child child = new Child();
System.out.println(child.value); // 输出2
System.out.println(((Parent)child).value); // 输出1

In this example, the value variable in the Child class hides the value variable in the Parent class.
This code mainly demonstrates variable hiding and type conversion. In this example, the Child class inherits from the Parent class, and they both define a value variable. When we create a Child object, it actually has two value variables, one is Child's own, and the other is inherited from Parent. The value variable of the Child class hides the value variable of the Parent class.
System.out.println(child.value); This code will print the value of the value variable of the Child class, which is 2.
System.out.println(((Parent)child).value); This code first converts the child object up to the Parent type, and then accesses the value variable. Since variables do not have the feature of dynamic binding, this accesses the value variable of the Parent class, which prints 1.
So, although the child object is actually an instance of the Child class, when you see it as an instance of the Parent class, you will see the value of the value variable of the Parent class, which is the effect of type conversion.

Method rewriting (Override) : When a subclass defines a method with the same signature as the parent class, the method in the subclass will override the method in the parent class. This is called method overriding or method overriding. When calling this method, the version in the subclass is called, not the version in the parent class. This is a form of polymorphism.
For example:

public class Parent {
    
    
    public void print() {
    
    
        System.out.println("Parent");
    }
}

public class Child extends Parent {
    
    
    @Override
    public void print() {
    
    
        System.out.println("Child");
    }
}

Child child = new Child();
child.print(); // 输出"Child"
((Parent)child).print(); // 也输出"Child"

In this example, the print() method in the Child class overrides the print() method in the Parent class.

The difference between method rewriting in java and virtual function in C++:
The method rewriting in Java is similar to the virtual function mechanism in C++, both of which are a manifestation of polymorphism in object-oriented programming. Here are some key points:

C++ virtual function: In C++, if you want a subclass to be able to override a method of the parent class, you need to declare this method as a virtual function (using the virtual keyword) in the parent class. This method can then be overridden in subclasses. If you call this method through a parent class pointer or reference, C++ will dynamically determine which version of the method to call, which is dynamic binding.

Java method rewriting: In Java, all non-static methods are virtual methods by default, and subclasses can freely rewrite parent class methods (unless the parent class method is declared final, then subclasses cannot rewrite the method ). If you call this method through a parent class reference, Java will dynamically decide which version of the method to call, which is also dynamic binding.

So, while C++ requires explicit specification of virtual functions, and Java assumes that all non-static methods can be overridden, the fundamentals of the two mechanisms are the same. Polymorphism is achieved by allowing subclasses to override parent class methods.

2.3 super keyword

In Java, the super keyword has the following main purposes:

Access the member variables of the parent class : If the subclass and the parent class have member variables with the same name, we can use the super keyword to access the member variables of the parent class.
For example:

public class Parent {
    
    
    protected int value = 1;
}

public class Child extends Parent {
    
    
    private int value = 2;

    public void printValue() {
    
    
        System.out.println(value); // 输出2
        System.out.println(super.value); // 输出1
    }
}

Call the method of the parent class : If the subclass overrides the method of the parent class, we can use the super keyword to call the method of the parent class.
For example:

public class Parent {
    
    
    public void print() {
    
    
        System.out.println("Parent");
    }
}

public class Child extends Parent {
    
    
    @Override
    public void print() {
    
    
        super.print();
        System.out.println("Child");
    }
}

Call the constructor of the parent class : In the constructor of the subclass, we can use the super keyword to call the constructor of the parent class. It should be noted that this super call must be the first statement of the subclass constructor.
For example:

public class Parent {
    
    
    public Parent() {
    
    
        System.out.println("Parent constructor");
    }
}

public class Child extends Parent {
    
    
    public Child() {
    
    
        super();
        System.out.println("Child constructor");
    }
}

In this example, creating a Child object will first call the Parent's constructor, and then call the Child's constructor.

2.4 Object upcast object

In Java, upcasting (Upcasting) is an operation that treats a child class object as a parent class object. This is an important feature of polymorphism. When you upcast, you can treat a reference to a particular class as a reference to its parent class. In the process of upward transformation, some information obtained from subclasses may be lost.
Suppose we have two classes: Animal (animal) class and Dog (dog) class, where the Dog class is a subclass of the Animal class. We can create a Dog object and upcast it to Animal as follows:

Dog myDog = new Dog();
Animal myAnimal = myDog; // 这就是向上转型

In this example, myDog is an instance of the Dog class, which we upcast to type Animal and store it in the myAnimal variable. Now, although myAnimal is actually a Dog instance, we only have access to the portion of its properties and methods that it has as an Animal object.
It's worth noting that even with upcasting, Java still remembers the actual object type. So, if the Dog class overrides a method of the Animal class, calling that method will call the Dog class's version even though the reference type is Animal, because Java's method calls are dynamically bound. For example:

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

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

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Dog myDog = new Dog();
        Animal myAnimal = myDog;
        myAnimal.makeSound(); // 输出 "The dog barks"
    }
}

In this example, even though myAnimal is of type Animal, the makeSound() method will still output "The dog barks" because the actual object type is Dog.

How to understand dynamic binding?
Java's method calls are dynamically bound" means that in Java, which version of the method will be called (that is, the method of the parent class or the method of the subclass) is determined at runtime. Not at compile time. This is a key feature of polymorphism.
For example, suppose we have a Animal parent class and a Dog subclass, both of which have a makeSound() method. In the Dog class, the makeSound() method overrides The makeSound() method of the Animal class.

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

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

Then we create a Dog object and cast it to Animal:

Animal myAnimal = new Dog();

When we call myAnimal.makeSound(), although the static type (compile-time type) of myAnimal is Animal, what is actually called is the makeSound() method of the Dog class, because the dynamic type (run-time type) of myAnimal is Dog:

myAnimal.makeSound(); // 输出 "The dog barks"

This is an example of dynamic binding. Although at compile time the compiler only knows that myAnimal is an Animal, at run time the Java Virtual Machine (JVM) looks at the actual object type (ie Dog) and calls the appropriate method. This mechanism of determining object method calls at runtime is called dynamic binding.

Is there a static binding as opposed to that?
Yes, Java has static binding too. Compared with dynamic binding, static binding is determined at compile time, and this process is also called early binding.
Static binding is mainly used in the following situations in Java:

Static methods: Static methods are resolved at compile time. This is because static methods do not depend on the instance, but on the class itself.

final method: final methods cannot be overridden by subclasses, so their behavior can be determined at compile time.

Private methods: Private methods can only be called within the class itself, so the compiler can determine their behavior at compile time.

Constructors: Constructors are resolved at compile time.

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

    static void staticMethod() {
    
    
        System.out.println("This is a static method in Animal class");
    }
}

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

    static void staticMethod() {
    
    
        System.out.println("This is a static method in Dog class");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        Animal myAnimal = new Dog();
        myAnimal.makeSound(); // 输出 "The dog barks"
        myAnimal.staticMethod(); // 输出 "This is a static method in Animal class"
    }
}

In this code, although myAnimal is an instance of the Dog class, when we call myAnimal.staticMethod(), the output is "This is a static method in Animal class". This is because static methods are not dynamically bound, they are resolved at compile time. Therefore, even though myAnimal is an instance of the Dog class, it still calls a static method in the Animal class. This is an example of static binding.


2.5 Abstract class

An abstract class is a special class that cannot be instantiated. The main purpose of an abstract class is to define a common, inheritable type for its subclasses. An abstract class can contain one or more abstract methods. These methods are declared but not implemented. Concrete implementations are provided by subclasses of the abstract class.
Here is an example of a simple abstract class:

// 定义抽象类 Animal
public abstract class Animal {
    
    
    // 抽象方法 makeSound
    abstract void makeSound();

    // 非抽象方法
    public void eat() {
    
    
        System.out.println("The animal eats");
    }
}

// Dog 类继承 Animal 类
public class Dog extends Animal {
    
    
    // 实现 makeSound 方法
    public void makeSound() {
    
    
        System.out.println("The dog barks");
    }
}

// Cat 类继承 Animal 类
public class Cat extends Animal {
    
    
    // 实现 makeSound 方法
    public void makeSound() {
    
    
        System.out.println("The cat meows");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 创建 Dog 对象
        Dog myDog = new Dog();
        myDog.makeSound(); // 输出 "The dog barks"
        myDog.eat(); // 输出 "The animal eats"

        // 创建 Cat 对象
        Cat myCat = new Cat();
        myCat.makeSound(); // 输出 "The cat meows"
        myCat.eat(); // 输出 "The animal eats"
    }
}

2.6 Interface

An abstract class is a special class that cannot be instantiated. The main purpose of an abstract class is to define a common, inheritable type for its subclasses. An abstract class can contain one or more abstract methods. These methods are declared but not implemented. Concrete implementations are provided by subclasses of the abstract class.
Here is an example of a simple abstract class:

// 定义接口 Animal
public interface Animal {
    
    
    // 抽象方法 makeSound
    void makeSound();
}

// Dog 类实现 Animal 接口
public class Dog implements Animal {
    
    
    // 实现 makeSound 方法
    public void makeSound() {
    
    
        System.out.println("The dog barks");
    }
}

// Cat 类实现 Animal 接口
public class Cat implements Animal {
    
    
    // 实现 makeSound 方法
    public void makeSound() {
    
    
        System.out.println("The cat meows");
    }
}

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 创建 Dog 对象
        Dog myDog = new Dog();
        myDog.makeSound(); // 输出 "The dog barks"

        // 创建 Cat 对象
        Cat myCat = new Cat();
        myCat.makeSound(); // 输出 "The cat meows"
    }
}

Difference Between Interfaces and Abstract Classes:
Both interfaces and abstract classes can be used to define abstract types in Java, but there are some key differences between them:

Instantiation: Neither abstract classes nor interfaces can be instantiated directly, they need to be instantiated through their subclasses or implementation classes.

Method definitions: Abstract classes can have non-abstract methods (methods with concrete implementations), whereas before Java 8, interfaces could only have abstract methods. Starting from Java 8, interfaces can have default methods and static methods.

Member variables: Abstract classes can have non-final and non-static variables, while interfaces can only have static and final variables.

Inheritance and implementation: A class can implement multiple interfaces, but can only inherit one abstract class.

Constructor: An abstract class can have a constructor while an interface cannot have a constructor.

Access modifier: The method in the interface is public by default, and the abstract class can have public, protected and private methods (private methods cannot be abstract).

In general, abstract classes are a good choice if you need to create a kind of "template" of objects and expect a lot of shared code. If you need to define a protocol of behavior, or your class needs to implement multiple unrelated properties, then interfaces may be a better choice.

Three, polymorphism

Summarize

Tip: Here is a summary of the article:
For example: the above is what I will talk about today. This article only briefly introduces the use of pandas, and pandas provides a large number of functions and methods that allow us to process data quickly and easily.

Guess you like

Origin blog.csdn.net/weixin_46274756/article/details/131242162