This article records all the notes since learning Java and is continuously updated. I hope to keep it up~
This article records all the notes since learning Java and is continuously updated. I hope to keep it up~
Method Overloading and Overriding#
Method Overloading: In a class, multiple methods have the same name but different parameter lists (which can differ in number, type, or order). This allows the class to handle different data types in a unified way, deciding which method to call based on the number or type of parameters passed. Note that the method names are the same, but the parameter lists are different; the return types can be the same or different. The Java Virtual Machine determines the method to call based on the parameter list. Therefore, a unique parameter list is required.
Method Overriding: In a subclass, overriding an existing method from the superclass extends the functionality of the superclass method. Overriding is based on an inheritance relationship. The method signature of the overridden method must exactly match the method signature of the superclass (the method signature refers to the method name and parameter list). Overriding reflects Java's polymorphism.
Polymorphism is the ability of the same behavior to have multiple different forms or manifestations.
Polymorphism is the manifestation of an object in various forms.
In reality, for example, when we press the F1 key:
If currently in the Flash interface, the help document for AS 3 pops up;
If currently in Word, the Word help pops up;
In Windows, the Windows help and support pops up. The same event occurring on different objects produces different results. The three necessary conditions for polymorphism are: 1. Inheritance 2. Overriding 3. A superclass reference pointing to a subclass object. For a method call of a certain type, the actual method executed depends on the actual type at runtime.
When overriding, the access modifier can be modified, with the requirement that the access modifier of the subclass's overridden method should be greater than or equal to that of the superclass method. For example, if the superclass is protected, you can change it to public when overriding, or if the superclass has default access, you can also change it to public or protected. The access level can only be greater, not smaller.
When overriding, the return value type can be modified to a different type, with the requirement that this can only happen when the return value is a reference data type (the return value type must be a reference data type, not a basic data type like int). In this case, the return value type must be a subclass of the superclass method's return value type; otherwise, it should remain the same as the superclass return value type. For example, if the superclass's return value type is Object, then the subclass's return value type can be Object or any other class. It can only be smaller, not larger.
Access Modifiers#
Access Modifier | Same Class | Same Package | Subclass | Other |
---|---|---|---|---|
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
Parameter Binding#
When the caller passes parameters to an instance method, the values passed during the call are bound to the parameters by their position.
public class Main {
public static void main(String[] args) {
Person p = new Person();
int n = 15; // n's value is 15
p.setAge(n); // passing the value of n
System.out.println(p.getAge()); // 15
n = 20; // n's value is changed to 20
System.out.println(p.getAge()); // 15 or 20?
}
}
class Person {
private int age;
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
Running the code shows that modifying the external local variable n does not affect the instance p's age field. The reason is that the setAge() method receives a copy of n's value, so p.age and the local variable n do not affect each other.
Conclusion: The passing of basic type parameters is a copy of the caller's value. Subsequent modifications by either party do not affect the other.
Now let's look at an example of passing a reference parameter:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname); // passing the fullname array
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // modifying the first element of the fullname array to "Bart"
System.out.println(p.getName()); // "Homer Simpson" or "Bart Simpson"?
}
}
class Person {
private String[] name;
public String getName() {
return this.name[0] + " " + this.name[1];
}
public void setName(String[] name) {
this.name = name;
}
}
Notice that the parameter of setName() is now an array. Initially, the fullname array is passed in, and then when the contents of the fullname array are modified, the instance p's field p.name is also modified!
Conclusion: The passing of reference type parameters means that the caller's variable and the receiver's parameter variable point to the same object. Any modification by either party to this object will affect the other (because they point to the same object).
Now this example:
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // passing the bob variable
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // renaming bob to Alice
System.out.println(p.getName()); // "Bob" or "Alice"?
}
}
class Person {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Conclusion is clear:
-
Integers, floating-point numbers, and characters are basic types.
-
Strings and arrays are reference types (indexes of memory data).
-
The passing of basic type parameters is a copy of the caller's value. Subsequent modifications by either party do not affect the other.
-
The passing of reference type parameters means that the caller's variable and the receiver's parameter variable point to the same object. Any modification by either party to this object will affect the other.
In the three examples:
- The understanding of passing integer parameters is that they are copied, separated, and manage their own data, so the class reads the data unchanged.
- The understanding of passing string arrays is that they point to the same location; when one element of the array is modified, the data read by the class also changes (the class continues to point here).
- Strings are also reference parameters; why does the data read by the class remain unchanged? Because the entire string is rewritten (new memory and pointer, see the string modification section), the class still points to the previous memory block, so the data read by the class remains unchanged, same conclusion as 1. If only a character's value in the string's memory is modified, it leads to the same conclusion as 2.
In summary: For basic types, the class copies the data itself, creating new memory. For reference types, it copies the address pointer; when the memory data itself changes, the data read by the class changes accordingly. However, modifying a string creates new memory and a new pointer, which can no longer affect the class data.
(The above examples and summary are from Liao Xuefeng's blog and comments
Constructor#
Person p = new Person("Xiao Ming", 15);
The person after new refers to the constructor; calling the constructor must use the new operator.
In Java, when creating an object instance, the initialization is performed in the following order:
-
First, initialize fields, for example, int age = 10; indicates that the field is initialized to 10, double salary; indicates that the field is default initialized to 0, String name; indicates that the reference type field is default initialized to null;
-
Then, execute the code of the constructor for initialization.
The code of the constructor runs later.
Constructors also have polymorphism and can automatically match based on the parameter list;
Constructors can call other constructors using the this. keyword.
native Keyword#
I just looked at a question: If two objects have the same hashCode(), does that mean equals() must also be true? Checking the definition of hashCode(),
public native int hashCode();
This is the first time I saw the native keyword, and I was curious about what it is. After some research.
JNI#
First, it's important to understand that JNI stands for Java Native Interface, which allows Java to interact with C or C++. Wikipedia explains it this way: "When an application cannot be fully implemented in the Java programming language (for example, when specific platform features or libraries not supported by the standard Java class library are needed), JNI allows programmers to write native methods to handle such situations." This means that in a Java application, we can use the C++ libraries we need and directly interact with Java code, and within the callable C++ program, we can also call Java methods (callback functions). (That's pretty amazing...)
In summary, a function (method) modified by the native keyword indicates that this method is a native function, meaning it is implemented in C/C++ and compiled into a DLL, which is then called by Java. Done!
To supplement the implementation steps:
You can think of native methods as the interface between Java programs and C programs, with the implementation steps being:
-
Declare the native() method in Java and compile it;
-
Use javah to generate a .h file;
-
Write a .cpp file to implement the native export method, which needs to include the .h file generated in the second step (note that it also includes the jni.h file provided by the JDK);
-
Compile the .cpp file from the third step into a dynamic link library file;
-
In Java, use the System.loadLibrary() method to load the dynamic link library file generated in the fourth step, and this native() method can then be accessed in Java.
Further learning link: IBM Community's Implementation of Java Native Methods in WindowsWhy does polymorphism eliminate coupling between types?#
Telling you is pointless; you just need to have a rough idea. This requires a lot of code... Simply put, without polymorphism, whatever is on the left side of the equals sign must be on the right side, which is called coupling. With polymorphism, the left side is a superclass (or interface), and the right side is a subclass (or implementation class). I only need to call the methods in the interface; how you implement it is your business. If you need to modify the implementation, you just need to change the implementation class without changing any code on my side, thus decoupling.