methods
A class can contain multiple fields. For example, we define two fields for the Person class:
class Person {
public String name;
public int age;
}
Copy the code
However, exposing the field directly to the outside world as public might break encapsulation.
Obviously, manipulating field directly is a logical mess. To prevent external code from accessing the field directly, we can decorate the field with private to deny external access:
class Person { private String name; private int age; } // private field public class Main { public static void main(String[] args) { Person ming = new Person(); ming.name = "Xiao Ming"; Age = 12; }} class Person {private String name; private int age; }Copy the code
Is there a compilation error? Remove the assignment statement that accesses the field and it will compile normally.
Change fields from public to private. External code can’t access these fields, so what’s the use of defining them? How do I give it a value? How can I read its value?
So we need to use methods to allow external code to modify the field indirectly:
// private field public class Main { public static void main(String[] args) { Person ming = new Person(); ming.setName("Xiao Ming"); // Set name ming.setage (12); Println (ming.getName() + ", "+ ming.getage ()); // Set age system.out.println (ming.getName() + "," + ming.getage ()); } } class Person { private String name; private int age; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public int getAge() { return this.age; } public void setAge(int age) { if (age < 0 || age > 100) { throw new IllegalArgumentException("invalid age value"); } this.age = age; }}Copy the code
Although external code cannot modify the private field directly, it can modify the private field indirectly by calling the methods setName() and setAge(). Inside the method, we have the opportunity to check that the parameters are correct. SetAge (), for example, checks for incoming arguments that are out of range and reports an error. That way, there is no opportunity for external code to set age to an unreasonable value.
The setName() method can also be checked, for example, to disallow null and empty strings:
public void setName(String name) { if (name == null || name.isBlank()) { throw new IllegalArgumentException("invalid name"); } this.name = name.strip(); // remove the first and last Spaces}Copy the code
Similarly, external code cannot read the private field directly, but it can get the value of the private field indirectly through getName() and getAge().
So, by defining methods, a class can expose interfaces to external code, while maintaining logical consistency internally.
The syntax for calling methods is instance variables. Method name (parameter); . A method call is a statement, so don’t forget to add ‘at the end; ‘. For example: ming.setname (“Xiao Ming”); .
Define methods
As you can see from the code above, the syntax for defining a method is:
Modifier method return type Method name (method parameter list) {several method statements; Return Method returns a value; }Copy the code
The return value of the method is realized by the return statement. If there is no return value, the return type is set to void and the return can be omitted.
Private methods
Where there are public methods, there are private methods. Just like private fields, private methods are not allowed to be called externally, so why should we define private methods?
The reason for defining private methods is that internal methods can call private methods.
Methods can encapsulate a class’s external interface without the caller knowing or caring whether the Person instance has an age field inside it.
This variable
Inside a method, you can use an implicit variable this, which always points to the current instance. Thus, the fields of the current instance can be accessed through this.field.
** If there are no naming conflicts, omit this **
class Person { private String name; public String getName() { return name; // the same as this.name}}Copy the code
** However, if a local variable has the same name as a field, the local variable has a higher priority and must be added with this **
class Person { private String name; public void setName(String name) { this.name = name; }}Copy the code
The method parameters
Methods can have zero or any arguments. Method parameters are used to receive variable values passed to the method. ** when calling a method, you must pass ** exactly as the parameters are defined. Such as:
class Person { ... public void setNameAndAge(String name, int age) { ... }}Copy the code
The setNameAndAge() method must take two arguments, and the first argument must be String and the second argument must be int:
Person ming = new Person(); ming.setNameAndAge("Xiao Ming"); SetNameAndAge (12, "Xiao Ming"); // Compilation error: the parameter type is incorrectCopy the code
Variable parameter
** Variable parameters with type… Define ** as an array type:
class Group { private String[] names; public void setNames(String... names) { this.names = names; }}Copy the code
SetNames () above defines a mutable parameter. When called, you can write:
Group g = new Group(); g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // Pass three strings g.setnames ("Xiao Ming", "Xiao Hong"); // Pass 2 String g.setnames ("Xiao Ming"); // Pass in 1 String g.setnames (); // Pass in 0 stringsCopy the code
** Can be written as String[] : **
class Group { private String[] names; public void setNames(String[] names) { this.names = names; }}Copy the code
However, the ** caller needs to construct String[] ** itself. Such as:
Group g = new Group(); g.setNames(new String[] {"Xiao Ming", "Xiao Hong", "Xiao Jun"}); // Pass in 1 String[]Copy the code
Another problem is that callers can pass null:
Group g = new Group();
g.setNames(null);
Copy the code
Mutable arguments guarantee that null cannot be passed in, because when zero arguments are passed in, the actual value received is an empty array, not NULL.
Parameter binding
When a caller passes an argument to an instance method, the values passed during the call are bound by the argument position.
So what is parameter binding?
Let’s look at the passing of a primitive type parameter:
Public class Main {public static void Main (String[] args) {Person p = new Person(); int n = 15; // the value of n is 15 p.stage (n); System.out.println(p.gettage ())); // Pass the value of n system.out.println (p.gettage ()); // 15 n = 20; // change n to 20 system.out.println (p.gettage ()); // 15 or 20? } } class Person { private int age; public int getAge() { return this.age; } public void setAge(int age) { this.age = age; }}Copy the code
P < p style = “color: RGB (51, 51, 51); color: RGB (51, 51, 51); color: RGB (51, 51, 51);
** Conclusion: Primitive type arguments are passed as copies of caller values. Subsequent modifications of both parties shall not affect each other. 台湾国
Let’s look at another 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); // Pass the fullname array system.out.println (p.getname ()); // "Homer Simpson" fullname[0] = "Bart"; // Change the first element of the fullname array to "Bart" system.out.println (p.get_name ()); // "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; }}Copy the code
Notice that the argument to setName() is now an array. > > < p style = “max-width: 100%; clear: both; clear: both;
** Conclusion: The passing of a reference type parameter, the caller’s variable, and the receiver’s parameter variable point to the same object. Changes made by either party to this object affect the other. 台湾国
With this conclusion in mind, let’s look at another example:
Public class Main {public static void Main (String[] args) {Person p = new Person(); String bob = "Bob"; p.setName(bob); // Pass in the Bob variable system.out.println (p.goetname ()); // "Bob" bob = "Alice"; // Bob renamed Alice system.out.println (p.get_name ()); // "Bob" or "Alice"? } } class Person { private String name; public String getName() { return this.name; } public void setName(String name) { this.name = name; }}Copy the code
Don’t doubt the mechanism for referencing parameter bindings, and try to explain why the code above prints “Bob” both times.
Static field
Fields defined in a class are called instance fields. The feature of instance fields is that each instance has an independent field, and the fields with the same name of each instance do not affect each other.
A static field is a static field.
Instance fields have their own separate “space” in each instance, whereas static fields have only one shared “space” that is shared by all instances. Here’s an example:
class Person { public String name; public int age; Public static int number; }Copy the code
Let’s look at the following code:
public class Main { public static void main(String[] args) { Person ming = new Person("Xiao Ming", 12); Person hong = new Person("Xiao Hong", 15); ming.number = 88; System.out.println(hong.number); hong.number = 99; System.out.println(ming.number); } } class Person { public String name; public int age; public static int number; public Person(String name, int age) { this.name = name; this.age = age; }}Copy the code
For static fields, the effect is the same regardless of which instance’s static field is modified: the static field is modified for all instances because the static field does not belong to the instance:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ Ming ─ ─ > │ Person instance │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ name = "Xiao Ming" │ │ age = 12 │ │ number ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ Person class │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ ├ ─ ─ ─ ─ ─ > │ │ number = 99 ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ hong ─ ─ > │ Person instance │ │ ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ name = "Xiao hong" │ │ │ age = 15 │ │ │ number ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘Copy the code
While instances can access static fields, they actually point to static fields of the Person class. So, all instances share a static field.
Therefore, instance variables are not recommended. Static fields access static fields because in Java programs, instance objects do not have static fields. In code, instance objects can access static fields only because the compiler can automatically convert them to class names based on the instance type. Static fields to access static objects.
Class names are recommended for accessing static fields. Static fields can be thought of as fields (not instance fields) that describe the class itself. For the above code, it would be better to write:
Person.number = 99;
System.out.println(Person.number);
Copy the code
A static method
Where there are static fields, there are static methods. Methods decorated with static are called static methods.
Instance methods must be called through an instance variable, whereas static methods need no instance variable and can be called through the class name. Static methods are similar to functions in other programming languages. Such as:
public class Main { public static void main(String[] args) { Person.setNumber(99); System.out.println(Person.number); } } class Person { public static int number; public static void setNumber(int value) { number = value; }}Copy the code
Because a static method belongs to a class and not an instance, it cannot access the this variable or the instance field inside the static method. It can only access the static field.
Static methods can also be called from instance variables, but the compiler automatically overwrites the instance to the class name for us.
Typically, accessing static fields and methods through instance variables will result in a compile warning.
Static methods are often used in utility classes.
Arrays.sort()
Math.random()
Copy the code
Static methods are also often used for auxiliary methods. Notice that the Java program entry main() is also a static method.
A constructor
When creating an instance, we often need to initialize the fields of the instance at the same time, for example:
Person ming = new Person(); Ming. Elegantly-named setName (" Ming "); ming.setAge(12);Copy the code
It takes three lines of code to initialize an object instance, and if you forget to call setName() or setAge(), the state inside the instance is incorrect.
Can you initialize all the internal fields to the appropriate values when you create an object instance?
Absolutely.
At this point, we need constructors.
When you create an instance, you actually initialize it through the constructor. Let’s define a constructor that initializes a Person instance by passing name and age at once:
Public class Main {public static void Main (String[] args) {Person p = new Person("Xiao Ming", 15); System.out.println(p.getName()); System.out.println(p.getAge()); } } class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return this.name; } public int getAge() { return this.age; }}Copy the code
Because the constructor is so special, its name is the class name. Constructor parameters are unlimited, and you can write arbitrary statements inside a method. However, in contrast to normal methods, constructors return no value (and no void), and constructors must be called with the new operator.
Default constructor
Any class has a constructor. If a class does not define a constructor, the compiler automatically generates a default constructor for us, with no arguments and no execution statements, like this:
class Person {
public Person() {
}
}
Copy the code
If we customize a constructor, then the compiler no longer automatically creates the default constructor.
If we want to be able to use the constructor with arguments and keep the constructor without arguments, we can only define both constructors:
When a field is not initialized in a constructor, the ** reference field defaults to NULL, the numeric field defaults to 0, the int value defaults to false, and the Boolean value defaults to false:
class Person { private String name; // Default to null private int age; Public Person() {}}Copy the code
It is also possible to initialize a field directly:
class Person {
private String name = "Unamed";
private int age = 10;
}
Copy the code
Initializing a field and initializing a field in a constructor:
class Person { private String name = "Unamed"; private int age = 10; public Person(String name, int age) { this.name = name; this.age = age; }}Copy the code
When we create the object, new Person(“Xiao Ming”, 12) gets the object instance. What is the initial value of the field?
In Java, object instances are initialized in the following order:
Initialize the field first, for example, int age = 10; Double salary; String name; Indicates that the reference type field is initialized to NULL by default;
The code that executes the constructor is initialized.
Therefore, the constructor code runs later, so the field values of new Person(“Xiao Ming”, 12) are ultimately determined by the constructor code.
Multiple construction method
Multiple constructors can be defined, and when called through the new operator, the compiler automatically distinguishes them by the number, position, and type of constructor arguments:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
this.age = 12;
}
public Person() {
}
}
Copy the code
If you call new Person(“Xiao Ming”, 20); Constructor public Person(String, int).
If you call new Person(“Xiao Ming”); Is automatically matched to the constructor public Person(String).
If you call new Person(); Is automatically matched to the constructor public Person().
A constructor can call other constructors for code reuse purposes. The syntax for calling other constructors is this(…). :
class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name) { this(name, 18); Person(String, int)} public Person() {this("Unnamed"); Call another constructor Person(String)}}Copy the code
summary
- When an instance is created, the new operator calls its corresponding constructor, which initializes the instance.
- When no constructor is defined, the compiler automatically creates a default no-argument constructor.
- Multiple constructors can be defined, and the compiler automatically determines according to the parameters;
- You can call one constructor from within another constructor for easy code reuse.
Method overloading
Within a class, we can define multiple methods. If you have a list of methods that all do the same thing, except for different parameters, you can name the set of methods with the same name. For example, in the Hello class, define multiple Hello () methods:
class Hello { public void hello() { System.out.println("Hello, world!" ); } public void hello(String name) { System.out.println("Hello, " + name + "!" ); } public void hello(String name, int age) { if (age < 18) { System.out.println("Hi, " + name + "!" ); } else { System.out.println("Hello, " + name + "!" ); }}}Copy the code
This method has the same name but different parameters. It is called method Overload.
Note: The return value type for method overloads is usually the same.
The purpose of method overloading is that methods with similar functions using the same name are easier to remember and, therefore, easier to call.
For example, the String class provides multiple overloaded methods, indexOf(), to find substrings:
Int indexOf(int ch) : search by character's Unicode code; Int indexOf(String STR) : search by String; Int indexOf(int ch, int fromIndex) : searches by character, but specifies the starting position; Int indexOf(String STR, int fromIndex) searches by String, but specifies the starting position.Copy the code
Give it a try:
// String.indexOf() public class Main { public static void main(String[] args) { String s = "Test string"; int n1 = s.indexOf('t'); int n2 = s.indexOf("st"); int n3 = s.indexOf("st", 4); System.out.println(n1); System.out.println(n2); System.out.println(n3); }}Copy the code
summary
- Method overloading is when multiple methods have the same method name but different parameters.
- Overloaded methods should do something similar, refer to String indexOf();
- Overloaded methods should return values of the same type.
inheritance
Define the Person class:
class Person { private String name; private int age; public String getName() {... } public void setName(String name) {... } public int getAge() {... } public void setAge(int age) {... }}Copy the code
Now, suppose we need to define a Student class with the following fields:
class Student { private String name; private int age; private int score; public String getName() {... } public void setName(String name) {... } public int getAge() {... } public void setAge(int age) {... } public int getScore() {... } public void setScore(int score) {... }}Copy the code
On closer inspection, the Student class contains the same fields and methods as the Person class, except for the score field and the corresponding getScore() and setScore() methods.
Can we not write duplicate code in Student?
This is where inheritance comes in handy.
Inheritance is a very powerful mechanism in object-oriented programming, and it allows you to reuse code first. When we let Student inherit from Person, Student gets all of Person’s functionality, and we just need to write the new functionality for Student.
Java uses the extends keyword to implement inheritance:
class Person { private String name; private int age; public String getName() {... } public void setName(String name) {... } public int getAge() {... } public void setAge(int age) {... }} class Student extends Person {private int score; private int score; Public int getScore() {... } public void setScore(int score) {... }}Copy the code
As you can see, with inheritance, the Student only needs to write additional functionality and no longer needs to repeat the code.
Note: Subclasses automatically obtain all fields of the parent class. Do not define fields with the same name as the parent class! In OOP terms, we refer to Person as a super class, parent class, base class, and Student as a subclass, extended class.
Inheritance tree
Notice that when we define Person, we don’t write extends. In Java, when there is no class that explicitly writes extends, the compiler automatically adds extends Object. So, any class, except Object, inherits from some class. The inheritance tree for Person and Student is as follows:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Object │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Person │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Student │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘Copy the code
Java allows only one class to inherit from a class, so a class has one and only one parent class. Only Object is special, it has no parent class.
Similarly, if we define a Teacher that inherits from Person, their inheritance tree relationship is as follows:
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Object │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Person │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring bring │ │ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Student │ │ the Teacher │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘Copy the code
protected
Inheritance is characterized by the fact that subclasses cannot access private fields or private methods of their parent class. For example,
Student cannot access the name and age fields of the Person class:
class Person { private String name; private int age; } class Student extends Person { public String hello() { return "Hello, " + name; // compiler error: cannot access name field}}Copy the code
This makes inheritance less useful. In order for subclasses to access the fields of their parent class, we need to change private to protected. Protected fields can be accessed by subclasses:
class Person { protected String name; protected int age; } class Student extends Person { public String hello() { return "Hello, " + name; // OK! }}Copy the code
Therefore, the protected keyword keeps access to fields and methods within the inheritance tree. A protected field or method can be accessed by its subclasses and their subclasses, as we’ll explain later.
super
The super keyword represents the parent class (superclass). A subclass can use super.fieldName when referring to a field of its parent class. Such as:
class Student extends Person { public String hello() { return "Hello, " + super.name; }}Copy the code
In fact, using super.name, or this.name, or name will have the same effect. The compiler automatically locates the name field of the parent class.
However, at some point, you have to use super. Let’s look at an example:
// super public class Main { public static void main(String[] args) { Student s = new Student("Xiao Ming", 12, 89); } } class Person { protected String name; protected int age; public Person(String name, int age) { this.name = name; this.age = age; } } class Student extends Person { protected int score; public Student(String name, int age, int score) { this.score = score; }}Copy the code
Running the code above gives you a compilation error to the effect that the constructor for Person cannot be called in the Student constructor.
This is because in Java, the first line of any class constructor must be the constructor that calls the parent class. If we don’t explicitly call the parent constructor, the compiler automatically adds a super() for us; So, the Student class constructor actually looks like this:
class Student extends Person { protected int score; public Student(String name, int age, int score) { super(); // Automatically call the parent constructor this.score = score; }}Copy the code
However, the Person class does not have a parameterless constructor, so the compilation fails.
The solution is to call one of the existing constructors of the Person class. Such as:
class Student extends Person { protected int score; public Student(String name, int age, int score) { super(name, age); Person(String, int) this.score = score; }}Copy the code
This will compile normally!
Therefore, we conclude that if the parent class does not have a default constructor, the subclass must explicitly call super() with arguments to allow the compiler to locate an appropriate constructor for the parent class.
In passing, there is another problem: subclasses do not inherit any of their parent class’s constructors. The default constructor for subclasses is automatically generated by the compiler, not inherited.
Stop the inheritance
Normally, any class can inherit from a class that does not have a final modifier.
As of Java 15, it is allowed to use sealed to specify a subclass name with permits that can be inherited from the class.
For example, define a Shape class:
public sealed class Shape permits Rect, Circle, Triangle {
...
}
Copy the code
The Shape class above is a sealed class that only allows the specified three classes to inherit it. If you write:
public final class Rect extends Shape {... }Copy the code
There is no problem, because Rect appears in the permits list of Shape. However, an error is reported if an Ellipse is defined:
public final class Ellipse extends Shape {... } // Compile error: class is not allowed to extend sealed class: ShapeCopy the code
The reason is that Ellipse does not appear in the Permitage list of Shape. This sealed class is mainly used in some frameworks to prevent inheritance from being abused.
Sealed class is currently in the preview state in Java 15. To enable it, you must use the parameters –enable-preview and –source 15.
upcasting
If a reference variable is of type Student, it can point to an instance of type Student:
Student s = new Student();
Copy the code
If a reference type variable is Person, it can point to an instance of Person:
Person p = new Person();
Copy the code
Now the question is: if Student is inherited from Person, can a variable referring to type Person point to an instance of type Student?
Person p = new Student(); // ???
Copy the code
If you test it out, this pointing is allowed!
This is because Student inherits from Person, so it has all the functionality of Person. A variable of type Person that points to an instance of type Student is fine to operate on!
This assignment, which safely converts a subclass type to a parent type, is called upcasting.
An upcast actually converts a subtype safely to a more abstract parent type:
Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok
Copy the code
Notice that the inheritance tree is Student > Person > Object, so you can convert the Student type to Person or a higher level Object.
Downward transition
Downcasting is the opposite of upcasting, when a parent type is forced to subclass. Such as:
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
Copy the code
If you test the code above, you can see that:
Person type P1 actually points to the Student instance, and Person type variable P2 actually points to the Person instance. In the downward transition, converting P1 to Student will succeed because P1 does refer to Student. Converting P2 to Student will fail because p2 is of type Person. You cannot change the parent class to subclass because subclass has more functions than parent class.
So the downward transition is likely to fail. On failure, the Java virtual machine reports a ClassCastException.
To avoid downcasting errors, Java provides the instanceof operator, which determines whether an instance is of a type:
Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false
Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true
Student n = null;
System.out.println(n instanceof Student); // false
Copy the code
Instanceof actually determines whether the instance to which a variable points is of a specified type, or a subclass of that type. If a reference variable is null, then any instanceof judgment is false.
Instanceof can be used to determine the following before downward transformation:
Person p = new Student(); If (p instanceof Student) {// If (p instanceof Student) = (p instanceof Student); // Will be successful}Copy the code
Starting from Java 14, instanceof can be directly converted to a specified variable after judging it, avoiding forced transformation again. For example, for the following code:
Object obj = "hello";
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.toUpperCase());
}
Copy the code
It can be rewritten as follows:
// instanceof variable: public class Main { public static void main(String[] args) { Object obj = "hello"; If (obj instanceof String s) {// We can use the variable s: system.out.println (s.toupperCase ()); }}}Copy the code
The instanceof method is much cleaner.
Distinguish between inheritance and composition
When using inheritance, we need to pay attention to logical consistency.
Consider the following Book class:
class Book { protected String name; public String getName() {... } public void setName(String name) {... }}Copy the code
The Book class also has a name field, so can we make Student inherit from Book?
class Student extends Book {
protected int score;
}
Copy the code
Obviously, logically this does not make sense, and Student should inherit not from Book but from Person.
The reason is that Student is a type of Person, they are an IS relation, and Student is not Book. Student has a relationship with Book.
Instead of using inheritance, you should use composition to have a has relationship, that is, Student can hold an instance of Book:
class Student extends Person { protected Book book; protected int score; } Therefore, inheritance is the IS relation, composition is the HAS relation.
summary
Inheritance is a powerful way to reuse code in object-oriented programming.
- Java only allows single inheritance, and the ultimate root of all classes is Object;
- Protected Allows subclasses to access fields and methods of their parent class;
- The constructor of a subclass can call the constructor of its parent class through super();
- You can safely transition up to a more abstract type;
- Downward transformation can be forced, preferably by instanceof judgment;
- The relationship between a subclass and its parent class is, and has cannot be inherited.
polymorphism
In an inheritance relationship, a subclass is called Override if it defines a method whose signature is exactly the same as the parent class’s method.
For example, in the Person class, we define the run() method:
class Person { public void run() { System.out.println("Person.run"); }}Copy the code
In subclass Student, override the run() method:
class Student extends Person { @Override public void run() { System.out.println("Student.run"); }}Copy the code
The @Override annotation is a Java built-in annotation mechanism, similar to the ES6 decorator, that does nothing but prompt the Java virtual machine to check that the method format is overridden correctly
Distinguish overloading from overwriting
The difference between Override and Overload is, if the method signature is different, it’s Overload, so the Overload method is a new method; If the method has the same signature and returns the same value, it is Override.
Note: The same method name, the same method parameters, but the method return value is different, are also different methods. In Java programs, when this happens, the compiler will report an error.
Class Person {public void run() {... }} class Student extends Person {public void run(String s) {public void run(String s) {... } public int run() {public int run() {... }}Copy the code
The addition of @override allows the compiler to help check that the correct Override is being done. The compiler will report an error if you wish to overwrite but accidentally write the wrong method signature.
public class Main {
public static void main(String[] args) {
}
}
class Person {
public void run() {}
}
public class Student extends Person {
@Override // Compile error!
public void run(String s) {}
}
Copy the code
But @override is not required.
We already know that the declared type of a reference variable may not match its actual type, for example:
Person p = new Student(); Now, let’s consider a case where a subclass overrides a parent class’s method:
// override public class Main { public static void main(String[] args) { Person p = new Student(); p.run(); }} class Person {public void run() {system.out.println (" person.run "); } } class Student extends Person { @Override public void run() { System.out.println("Student.run"); }}Copy the code
So, if a variable of type Student refers to type Person and calls its run() method, does that call Person or Student’s run()?
Running the code above shows that the method actually called is Student’s run() method. Therefore, it can be concluded that:
Java instance method calls are dynamic calls based on the actual type of the runtime, not the declared type of the variable.
This very important property is called polymorphism in object-oriented programming. The English spelling is very complex: Polymorphic.
polymorphism
Polymorphism is a method call for a type where the actual method executed depends on the actual type of method at runtime.
Person p = new Student(); p.run(); // Cannot determine which run() method is called at runtimeCopy the code
Student run(); Student run();
But suppose we write a method like this:
public void runTwice(Person p) {
p.run();
p.run();
}
Copy the code
It passes in an argument of type Person, and we have no way of knowing whether the argument is actually of type Person, Student, or some other subclass of Person, and therefore whether the run() method defined by the Person class is called.
So, the nature of polymorphism is that the runtime dynamically decides which subclass methods to call. When a method is called on a type, the actual method executed may be an override method of a subclass. What exactly does this nondeterministic method call do?
Let’s go back to chestnuts. Suppose we define an Income and need to file a tax return for it, then define an Income class:
class Income { protected double income; Public double getTax() {return income * 0.1; // Tax rate 10%}}Copy the code
For wage Income, we can subtract a base, so we can derive SalaryIncome from Income and overwrite getTax() :
class Salary extends Income { @Override public double getTax() { if (income <= 5000) { return 0; } return (income-5000) * 0.2; }}Copy the code
If you receive a State Department special allowance, it is exempt from taxes:
class StateCouncilSpecialAllowance extends Income { @Override public double getTax() { return 0; }}Copy the code
Now, we are going to write a financial software to tax all the income of a person, you can say:
public double totalTax(Income... incomes) {
double total = 0;
for (Income income: incomes) {
total = total + income.getTax();
}
return total;
}
Copy the code
It all reads as follows:
Public static void Main (String[] args) {// For a person with ordinary income, salary income and special allowance from The State Council: Income[] incomes = new Income[] { new Income(3000), new Salary(7500), new StateCouncilSpecialAllowance(15000) }; System.out.println(totalTax(incomes)); } public static double totalTax(Income... incomes) { double total = 0; for (Income income: incomes) { total = total + income.getTax(); } return total; } } class Income { protected double income; public Income(double income) { this.income = income; } public double getTax() {return income * 0.1; }} class Salary extends Income {public Salary(double Income) {super(Income); } @Override public double getTax() { if (income <= 5000) { return 0; } return (income-5000) * 0.2; } } class StateCouncilSpecialAllowance extends Income { public StateCouncilSpecialAllowance(double income) { super(income); } @Override public double getTax() { return 0; }}Copy the code
Observe totalTax () method: using the polymorphism, totalTax () method only needs to deal with Income, it is completely don’t need to know the existence of Salary and StateCouncilSpecialAllowance can correctly calculate the total tax. If we want to add a new kind of contribution Income, we simply derive from Income and overwrite the getTax() method correctly. Passing the new type into totalTax() requires no code changes.
As you can see, a very powerful feature of polymorphism is that it allows you to extend functionality by adding subclasses of more types without having to modify the superclass-based code.
Overrides the Object method
Because all classes ultimately inherit from Object, Object defines several important methods:
- ToString () : prints instance as String; (Instance refers to instance object)
- Equals () : determines whether two instances are logically equal;
- HashCode () : Evaluates the hash value of an instance.
We can override these methods on Object if necessary. Such as:
class Person { ... @override public String toString() {return "Person:name=" + name; } @override public Boolean equals(Object o) {// If and only if o is of type Person: if (o instanceof Person) { Person p = (Person) o; // Return true: return this.name.equals(p.name); // Return this.name.equals(p.name); } return false; } // Calculate hash: @override public int hashCode() {return this.name.hashCode(); }}Copy the code
Call the super
In a subclass overriding method, if you want to call the overridden method of the parent class, you can call it through super.
class Person { protected String name; public String hello() { return "Hello, " + name; }} Student extends Person {@override public String hello() {return super.hello() + "!" ; }}Copy the code
final
If you don’t want a method to be overridden, use final
Inheritance allows subclasses to override methods of their parent class. If a parent class does not allow subclasses to overwrite one of its methods, mark that method as final. Methods decorated with final cannot be Override:
class Person { protected String name; public final String hello() { return "Hello, " + name; }} Student extends Person {// compile Error: @override public String hello() {}}Copy the code
You can also use final if you don’t want the class to be inherited
If a class does not want any other classes to inherit from it, the class itself can be marked as final. Classes modified with final cannot be inherited:
final class Person { protected String name; } // compile Error: Not allowed to inherit from Person Student extends Person {}Copy the code
You can also use final if you do not want the property to be modified
You can also use final for instance fields of a class. Fields decorated with final cannot be modified after initialization. Such as:
class Person {
public final String name = "Unamed";
}
Copy the code
Reassignment of final fields returns an error:
Person p = new Person();
p.name = "New Name"; // compile error!
Copy the code
Final fields can be initialized in the constructor
class Person { public final String name; public Person(String name) { this.name = name; }}Copy the code
This approach is more common because it guarantees that once an instance is created, its final fields cannot be modified.
summary
A subclass can Override a method of its parent class, which changes the behavior of the parent method in the subclass.
- Java method calls always act on the actual type of the runtime object, a behavior called polymorphism;
- Final modifiers have several functions:
- The final modifier prevents overwriting;
- A final class prevents inheritance;
- Fields that are final must be initialized when the object is created and cannot be subsequently modified.
An abstract class
Because of polymorphism, each subclass can override the methods of its parent class.
Class Person {public void run() {... }} class Student extends Person {@override public void run() {... }} class Teacher extends Person {@override public void run() {... }}Copy the code
Both Student and Teacher derived from the Person class can override the run() method.
If the run() method of the parent Person class doesn’t make sense, can I get rid of the method’s execution statement?
class Person {
public void run(); // Compile Error!
}
Copy the code
The answer is no, which will result in a compilation error, because when you define a method, you must implement its statements.
Can we get rid of the run() method of the parent class?
Again, no, because by removing the run() method from the parent class, you lose the polymorphic nature. For example, runTwice() does not compile:
public void runTwice(Person p) { p.run(); // Person does not have a run() method, which results in a compilation error p.run(); }Copy the code
Abstract methods
If the parent class’s method itself does not need to implement any function, but simply defines the method signature so that subclasses can override it, then we can declare the parent class’s method as abstract:
class Person {
public abstract void run();
}
Copy the code
Declaring a method abstract means that it is an abstract method that does not implement any method statements itself. Because the abstract method itself cannot be executed, the Person class cannot be instantiated. The compiler will tell us that the Person class cannot be compiled because it contains abstract methods.
The Person class itself must also be declared abstract to compile it properly:
abstract class Person {
public abstract void run();
}
Copy the code
This defines an abstract class
An abstract class
If a class defines a method but does not execute the code, it is an abstract method, which is decorated with abstract.
Because an abstract method cannot be executed, this class must also be declared an abstract class.
Classes that use the abstract modifier are abstract classes. We cannot instantiate an abstract class:
Person p = new Person(); // Error compilingCopy the code
What’s the use of abstract classes that can’t be instantiated?
Because abstract classes themselves are designed to be used only by inheritance, abstract classes can force subclasses to implement their defined abstract methods, or compile an error. Therefore, abstract methods are in effect defining “specifications”.
For example, the Person class defines the abstract method run(), so when subclass Student implements it, we must override the run() method:
// abstract class public class Main { public static void main(String[] args) { Person p = new Student(); p.run(); } } abstract class Person { public abstract void run(); } class Student extends Person { @Override public void run() { System.out.println("Student.run"); }}Copy the code
Abstract oriented programming
When we define the abstract class Person and the concrete Student/Teacher subclasses, we can refer to instances of the concrete subclasses by using the abstract class Person type:
Person s = new Student();
Person t = new Teacher();
Copy the code
The beauty of this reference to an abstract class is that we make method calls to it and don’t care about the specific subtypes of the Person variable:
// do not care about the specific subtype of the Person variable: s.run(); t.run();Copy the code
In the same code, if we refer to a new subclass, we still don’t care about the type:
// Also don't care how the new subclass implements the run() method: Person e = new Employee(); e.run();Copy the code
This approach of trying to refer to high-level types and avoiding actual subtypes is called abstract oriented programming.
The essence of abstract-oriented programming is:
- The upper layer only defines the specification (for example, Abstract class Person);
- Business logic can be implemented without subclasses (properly compiled);
- The specific business logic is implemented by different subclasses and does not concern the caller.
summary
- The method defined by abstract is an abstract method, which has only definition and no implementation. Abstract methods define the interface specifications that subclasses must implement;
- Classes that define abstract methods must be defined as abstract classes, and subclasses that inherit from abstract classes must implement abstract methods.
- If no abstract method is implemented, the subclass is still an abstract class;
- Abstract oriented programming makes the caller care only about the definition of the abstract method and not about the concrete implementation of the subclass.
interface
In an abstract class, the abstract method is essentially defining the interface specification: that is, specifying the interface of the higher-level class to ensure that all subclasses have the same interface implementation, so that polymorphism can come into play.
If an abstract class has no fields, all methods are abstract methods:
abstract class Person {
public abstract void run();
public abstract String getName();
}
Copy the code
You can rewrite the abstract class as interface.
In Java, you can declare an interface using interface:
interface Person {
void run();
String getName();
}
Copy the code
An interface is a purely abstract interface that is more abstract than an abstract class because it cannot even have fields. Because all methods defined by the interface are public abstract by default, these two modifiers do not need to be written (it makes no difference whether they are written or not).
The implements keyword is used when a specific class implements an interface. Here’s an example:
class Student implements Person { private String name; public Student(String name) { this.name = name; } @Override public void run() { System.out.println(this.name + " run"); } @Override public String getName() { return this.name; }}Copy the code
We know that in Java, a class can only inherit from another class, not from multiple classes. However, a class can implement more than one interface, for example:
Class Student implements Person, Hello {// Implements two interfaces... }Copy the code
The term to distinguish
Java interface specifically refers to the definition of interface, which represents an interface type and a set of method signatures, while programming interface generally refers to interface specifications, such as method signatures, data formats, network protocols, etc.
Abstract classes and interfaces
A comparison of abstract classes and interfaces is as follows:
type | abstract class | interface |
---|---|---|
inheritance | Can extends only one class | You can implements multiple interfaces |
field | Instance fields can be defined | Instance fields cannot be defined |
Abstract methods | Abstract methods can be defined | Abstract methods can be defined |
Nonabstract method | Non-abstract methods can be defined | You can define the default method |
Interface inheritance | ||
An interface can inherit from another interface. An interface inherits from an interface using extends, which extends the interface’s methods. Such as: | ||
` ` ` | ||
interface Hello { |
void hello();
Copy the code
} interface Person extends Hello { void run(); String getName(); }
At this point, the Person interface inherits from the Hello interface, so the Person interface now actually has three abstract method signatures, one of which comes from the inherited Hello interface. Design the interface and Abstract class inheritance relationship properly, can be fully reusable code. In general, ** common logic fits in abstract class **, concrete logic in subclasses, and ** interface hierarchy represents the level of abstraction **. Consider the inheritance of Java's collection classes for a set of interfaces, abstract classes, and concrete subclasses:Copy the code
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ Iterable │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ │ Object │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ Collection │ bring └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ bring bring ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ AbstractCollection │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ List are bring └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ bring ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ AbstractList │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ bring bring │ │ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ArrayList │ │ LinkedList │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
When used, the instantiated object can only be a concrete subclass, but it is always referred to through an interface, which is more abstract than an abstract class:Copy the code
List list = new ArrayList(); // Use the List interface to reference instances of concrete subclasses Collection coll = List; Iterable it = coll; // Transition up to Iterable interface
In the interface, you can define the default method. For example, change the run() method of the Person interface to the default method:Copy the code
public class Main { public static void main(String[] args) { Person p = new Student(“Xiao Ming”); p.run(); } } interface Person { String getName(); default void run() { System.out.println(getName() + ” run”); } } class Student implements Person { private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
Copy the code
}
** Implementation classes do not have to override the default method **. The purpose of the default method is that when we need to add a new method to the interface, it involves modifying all of the subclasses. If the default method is added, the subclass does not have to change all the methods, but simply overrides the new method where it is needed. There is a difference between the default method and the ordinary method of an abstract class. Because interface has no instance field, the **default method cannot access the instance field, whereas the ** ordinary method of the abstract class can access the instance field. Because Interface is a purely abstract class, it cannot define instance fields. However, interfaces can have static fields, and static fields must be final:Copy the code
public interface Person { public static final int MALE = 1; public static final int FEMALE = 2; }
In fact, since the interface field can only be of public static final type, we can remove all these modifiers. The above code can be shortened to:Copy the code
Public interface Person {// The compiler automatically adds public static final: int MALE = 1; int FEMALE = 2; }
The compiler automatically changes this field to the public static final type. Java interfaces define a purely abstract specification. A single class can implement multiple interfaces. * Interfaces are also data types, suitable for transitions up and down; * All methods of an interface are abstract methods, and the interface cannot define instance fields; * The interface can define default methods (JDK>=1.8). In the previous code, we named our classes and interfaces with simple names like Person, Student, Hello, and so on. In reality, if Ming writes a Person class and Hong writes a Person class, now Bai wants to use both Ming's Person and Hong's Person, what happens? If Xiaojun writes an Arrays class and the JDK also comes with an Arrays class, how to resolve the class name conflict? In Java, we use package to resolve name conflicts. Java defines a namespace called a package. ** A class always belongs to a package **, the class name (such as Person) is just a shorthand, ** the true full class name is the package name. The name of the class * *. For example, Ming's Person class is stored under the package Ming, so the full class name is ming.person. The Person class is stored under the package Hong, so the full class name is hong.person; The Arrays class of Xiaojun is stored under the package Mr. Jun. The JDK's Arrays classes are stored under the java.util package, so the full class name is java.util. When defining a class, we need to declare in the first line which package the class belongs to. Xiaoming's Person. Java file:Copy the code
// declare the package name Ming package Ming; public class Person { }
Arrays.java file from Xiaojun:Copy the code
Jun package Mr. Jun; public class Arrays { }
When the Java Virtual machine executes, the **JVM only looks at the full class name **, so as long as the package name is different, the class is different. ** packages can be multi-layer structures, using. Separated by * *. For example: java.util. Special note: the package has no parent-child relationship. Java.util and java.util. Zip are different packages and there is no inheritance relationship between the two. ** There is no class with a package name defined. It uses the default package, which is very likely to cause name conflicts. Therefore, it is not recommended to write no package name **. ** We also need to organize the above Java files according to the package structure **. Assuming package_sample as the root directory and SRC as the source directory, all files are structured as follows: TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT TXT ** that is, all Java files correspond to the same directory level as the package level **. Compiled. Class files also need to be stored according to the package structure. If you use an IDE and put the compiled.class file in the bin directory, the compiled file structure looks like this: Package_sample ├─ hong │ ├─ activities. class │ ├─ activities. class │ ├─ activities. class │ ├─ activities. class We need to execute the javac command in the SRC directory ** :Copy the code
javac -d .. /bin ming/Person.java hong/Person.java mr/jun/Arrays.java
In the IDE, all Java source is automatically compiled against the package structure, so you don't have to worry about complex commands compiled from the command line. Class in the same package that has access to package-scoped fields and methods **. ** Fields and methods that are not public, protected, or private are package scoped. For example, the Person class is defined under the Hello package:Copy the code
package hello; Public class Person {// package scope: void hello() {system.out.println (“Hello!”) ); }}
The Main class is also defined under the Hello package:Copy the code
package hello; public class Main { public static void main(String[] args) { Person p = new Person(); p.hello(); // Can be called because Main and Person are in the same package}}
## import Within a class, we always refer to other classes. For example, Xiaoming's Ming. Person class, if you want to reference Xiaojun's Mr. Jun. Arrays class, he has three ways to write: The first way is to directly write the complete class name, for example:Copy the code
package ming; public class Person { public void run() { mr.jun.Arrays arrays = new mr.jun.Arrays(); }}
Obviously, writing the full class name each time is a pain. Therefore, the second way is to import Xiaojun's Arrays with an import statement and write the simple class name:Copy the code
package ming; // Import Mr. Jun. Arrays;
public class Person { public void run() { Arrays arrays = new Arrays(); }}
** We can use * to import all classes under the package (but not the subpackage classes) ** :Copy the code
package ming; // Import all classes of Mr. Jun package: import Mr. Jun.*; public class Person { public void run() { Arrays arrays = new Arrays(); }}
We generally don't recommend this because it's hard to see which package the Arrays class belongs to after importing multiple packages. There is also the syntax for importing static fields and methods of a class:Copy the code
package main; // Import all static fields and methods of the System class: import static java.lang.system.*; Public class Main {public static void Main (String[] args) {// Call system.out.println (… out.println(“Hello, world!” ); }}
**import static rarely uses **. The Java compiler ends up compiling.class files using only the full class name, so in code, when the compiler encounters a class name: 1. If it is a full class name, look up the class by the full class name. 2. If it is a simple class name, search in the following order: * Find if the class exists in the current package; * Find if the package imported contains this class; * Find out if the java.lang package contains this class. If the class name cannot be determined according to the above rules, the compilation error is reported. Let's look at an example:Copy the code
package test; import java.text.Format; public class Main { public static void main(String[] args) { java.util.List list; -> java.util.List Format Format = null; // ok, use the import class -> java.text.Format String s = “hi”; // ok, use java.lang package String -> java.lang.String system.out.println (s); // ok, use java.lang package System -> java.lang.System MessageFormat mf = null; MessageFormat: MessageFormat cannot be resolved to a type}}
Therefore, when writing a class, the compiler automatically does two import actions for us: * By default, it automatically imports other classes of the current package; * Automatically import java.lang.* by default. Note: ** automatically imports the java.lang package, but packages like java.lang.Reflect still need to be manually imported. ** If you have two classes with the same name, for example, Mr. Jun. Arrays and Java.util. Arrays, then you can only import one of them, and the other must write the full class name **. To avoid name conflicts, we need to determine a unique package name. The recommended practice is to use inverted domain names to ensure uniqueness. Such as:Copy the code
org.apache org.apache.commons.log com.liaoxuefeng.sample
Subpackages can then be named according to the functionality. Be careful not to have the same name as the java.lang package class, that is, do not use these names for your own classes:Copy the code
String System Runtime …
Do not use the same name as a common JDK class:Copy the code
java.util.List java.text.Format java.math.BigInteger …
## Summary * Java's built-in package mechanism is designed to avoid class naming conflicts; * The core classes of the JDK use the java.lang package, which the compiler automatically imports; * Other common JDK classes are defined in java.util.*, java.math.*, java.text.*... ; * You are advised to use an inverted domain name, such as org.apache. In Java, we often see modifiers like public, protected, and private. In Java, these modifiers can be used to restrict access scope. Public class; interface; public class;Copy the code
package abc; public class Hello { public void hi() { } }
The Hello above is public and, therefore, can be accessed by other package classes:Copy the code
package xyz; Class Main {void foo() {// Main can access Hello Hello h = new Hello(); }}
Fields and methods defined as public can be accessed by other classes if they have access to the class first:Copy the code
package abc; public class Hello { public void hi() { } }
The hi() method above is public and can be called by other classes, provided that the Hello class is accessible first:Copy the code
package xyz; class Main { void foo() { Hello h = new Hello(); h.hi(); }}
Private fields and methods cannot be accessed by other classes:Copy the code
package abc; Public class Hello {// Cannot be called by other classes: private void hi() {} public void Hello () {this.hi(); }}
In fact, to be precise, private access is restricted inside the class and is independent of the method declaration order. ** It is recommended to leave the private method behind ** because the public method defines the functionality provided by the class. When reading the code, you should focus on the public method first:Copy the code
package abc; public class Hello { public void hello() { this.hi(); } private void hi() { } }
Since Java supports nested classes, if a class has nested classes defined inside it, then the nested class has access to private:Copy the code
public class Main { public static void main(String[] args) { Inner i = new Inner(); i.hi(); } // private static void hello() {system.out.println (“private hello!”) ); } // static class Inner {public void hi() {main.hello (); }}}
Classes defined inside a class are called nested classes, and Java supports several nested classes. ## protected protected applies to inheritance. ** Protected fields and methods are accessible by subclasses, and subclasses of subclasses:Copy the code
package abc; Public class Hello {// protected method: void hi() {}}
The protected methods above can be accessed by inherited classes:Copy the code
package xyz; Class Main extends Hello {void foo() {// You can access protected methods: hi(); }}
Finally, a package scope is a class that allows access to classes that are not public or private, and fields and methods that are not public, protected, or private of the same package.Copy the code
package abc; Class Hello {// package permission method: void hi() {}}
As long as you are in the same package, you can access the class, field, and method permissions of the package:Copy the code
package abc; Class Main {void foo() {// Can access the package permissions of the class: Hello h = new Hello(); // You can call the package permission method: h.hi(); }}
Note that the package names must be exactly the same, there is no parent-child relationship between the packages, com.apache and com.apache. ABC are different packages. Variables defined inside a method are called local variables. Local variables are scoped from the declaration of the variable to the end of the corresponding block. Method parameters are also local variables.Copy the code
package abc; Public class Hello {void hi(String name) {// ① String s = name.tolowerCase (); // ② int len = s.length(); // ③ if (len < 10) {// ④ int p = 10; // ⑤ for (int I =0; i<10; I++) {// ⑥ system.out.println (); // ⑧} // ⑨} // ⑩}
The hi() method argument is a local variable that is scoped to the entire method, from ① to ⑩. * the scope of variable s is from definition to method end, i.e. ② ~ ⑩; Len is scoped from the definition point to the end of the method, i.e. ③ ~ ⑩; * the scope of the variable p is defined until the end of the if block, i.e. ⑤ ~ ⑨; * The scope of variable I is for loop, i.e. ⑥ ~ ⑧. When using local variables, you should minimize the scope of ** local variables and delay declaration of local variables ** if possible. ## final Java also provides a final modifier. Final does not conflict with access rights, and it has many uses. 1. Using final to prevent a class from being inherited:Copy the code
package abc; Public final class Hello {private int n = 0; protected void hi(int t) { long i = t; }}
2. Final method is used to prevent subclasses from overwriting:Copy the code
package abc; Public class Hello {// cannot be overridden: protected final void hi() {}}
3. Modifying a field with a final modifier prevents reassignment:Copy the code
package abc; public class Hello { private final int n = 0; protected void hi() { this.n = 1; // error! }}
4. Modifying local variables with final prevents reassignment:Copy the code
package abc; public class Hello { protected void hi(final int t) { t = 1; // error! }}
## Best practice * Do not declare public if you are not sure whether you need it, that is, expose as few external fields and methods as possible. * Defining methods as package permissions helps testing because the test class and the class under test can access the package permission methods of the class under test as long as they are in the same package. * A. Java file can contain only one public class, but can contain multiple non-public classes. If there is a public class, the filename must be the same as the name of the public class. ## Summary * Java built-in access rights include public, protected, private, and package; * Java defines variables inside methods as local variables whose scope begins with the variable declaration and ends with a block; * Final modifiers are not access rights; they can modify class, field, and method; * A. Java file can contain only one public class, but can contain multiple non-public classes.Copy the code