If we call the getPackage method on the Class object, we can get the Package object describing the package where the class is located (the Package class is defined in java.lang). We can also use the package name to obtain the Package object by calling the static method getPackage or calling the static method getPackages (which returns an array composed of all known packages in the system). The getName method can return the full name of the package.
The use of Package objects is completely different from other reflection types, i.e. we cannot create or manipulate packages at runtime. We can use Package objects to get information about packages, such as the purpose of the package, who created the package, the version of the package, etc. We will postpone these contents until we will discuss them in detail later.
Naming of the package
Package names should avoid conflicts with other packages, so choosing a name that is both meaningful and unique is an important aspect of package design. However, programmers around the world are developing packages, and there is no way to know who used what package name, so choosing the only package name is a problem. If we determine that a package is only used within our organization, we can have an internal arbiter to ensure that there is no name conflict between projects.
But for the whole world, this approach is not practical. Package identifiers are all simple names, and a better way to ensure that the package name is to use an Internet domain name. If the company we are working in is Magic.lnc and the company's domain name is magic c.com, then the declaration of the attribute package should be:
package com.magic.attr;
Note that the domain name constituent elements here are arranged in reverse order of the conventional domain name.
If we adopt this idiom, the package names we use will not conflict with anyone else's except for the possible conflict within our organization. If there is indeed a conflict within our organization (probably a large enterprise), then we can use more specific domain names to further qualify. Many large companies have internal subdomains, such as east and europe, which can be used to further qualify the name of the package:
package corn.magic.japan.attr;
Using this solution may make the package name very long, but it is relatively safe. Programmers using this technique will not choose the same package name, and programmers who do not use this technique will not choose the name we use.
Package content
The contents of the package should be carefully designed so that they only include functionally relevant classes and interfaces. Classes in the package can freely access non-private members of other classes in the package, and some classes may even have sufficient permissions to access internal details of other classes. In order to avoid such classes from misoperating class members, we need to protect class members. Any member not declared as private can be accessed by all other types in the same package, so any unrelated classes may be more likely to be more coordinated than we would expect.
Packages also provide logical grouping for programmers looking for useful interfaces and classes. Packages composed of irrelevant classes make it difficult for programmers to tell which interfaces and classes are useful, and logical grouping of classes can help programmers reuse code because programmers can find what they need more easily through logical grouping. If the package only contains related, tightly coupled type sets, it means that we can give the type some more intuitive names to avoid name conflicts.
Packages can be nested. For example, java.lang is a nested package where the package Lang is nested in larger package java, while the package j ava also contains some other packages. Nesting makes the related packages form a naming system with hierarchical structure.
For example, to create a set of packages for adaptive systems such as neural networks and genetic algorithms, we can name packages with dot-separated names to create nested packages:
package adaptive. neural Net;
The source file containing the above declaration statement is located in the adaptive.neuralNet package, and the adaptive.neuralNet package itself is a subpackage of the adaptive package. The adaptive package may contain some classes related to general adaptive algorithms, such as generalization problem statement classes or benchmark classes. Packages that are in a deeper position in the hierarchy (eg adaptive.neu-ralNet or adaptive.genetic) contain classes related to a specific type of adaptive algorithm.
The nesting of packages is just a tool for organizing related packages, and it does not provide any special access rights between packages.
The class code in the adaptive.genetic package cannot access the members in the adaptive or adaptive.neuralNet package that have package access rights, and the package scope is only applicable to specific packages. The nesting of packages can group relevant packages and help programmers find the desired class in the logical level more conveniently, but beyond that, it does not bring any other benefits.
Package notes
The package can also have annotations. But the problem is that since packages are an organizational structure without source code entities and they have no actual definition, they cannot be annotated like classes or methods, so package annotation can only be achieved by annotating the package's declaration statement in the source file. However, there can only be one package declaration in each package that can have annotations that act on it.
So how do you annotate packages? In fact, Java does not force programmers to use some way to deal with the "single annotated package statement" rule. The recommended way is to create a file named package-i nfo.java in the package directory, in which only the package statements and the annotations of the package are stored without placing anything else. For example, the package info.java file for the attr package looks like this:
@PackageSpec(name two"Attr Project", version="1.0" @DevelopmentSite("attr.project.org") @DevelopmentModel("open-source") package attr;Packagespec, Developmentsite and Development opmentmodel are used to modify annotation types. Of course, they have runtime saving strategies. package-info.java file should be compiled with other source files in the package.
We recommend placing all package-related information in the package-info.java file. If you do this, you can place document comments at the beginning of the file, so that these documents are annotated as package documents.
Package access
When declaring accessibility of top-level classes and top-level interfaces in packages, there are two options: package access (package) and public access (public). Classes or interfaces modified with public can be accessed by out-of-package code, while types not decorated with public have package scope: they can be accessed by other codes in the same package; but they are hidden for out-of-package codes, even in subpackage codes. When declaring types, we should only declare those types that other programmers need to use as public, and hide those types that belong to the implementation details of the package. This technology provides us with great flexibility, and since programmers do not rely on these types of implementation details that they cannot access, we can freely change them when we want to change implementation details.
Class members that are not declared as public, protected or private can be accessed directly by any code inside the package, but are hidden from outside the package. In other words, the default access modifier is "package", with the exception of the members of the interface, and their default access modifier is "public".
Fields or methods that are not declared private within a package can be accessed by all other code in that package, so classes in the same package are considered "friendly" or "trusted". This allows us to define an application framework that combines predefined code and placeholder code, where the placeholder code is overridden by a subclass of the framework class. Predefined codes can use package access modifiers so that other collaborative codes within the package can directly access them, but for out-of-package users, these codes are inaccessible. However, the subpackages of the packages where these codes are located are not trusted and vice versa. For example, code modified with the package access modifier in the package dit cannot be accessed by the code in its child package dit.dat, and vice versa.
Therefore, each type defines three different contracts:
.publi. Contract: defines the main function of the type.
.protected contract: defines the functions available to subclasses for specialization purposes.
.package contract: defines the functions that can be obtained by other code in the package to achieve collaboration between types in the package. All of these contracts require careful consideration and design.
Accessibility and cover method
Only methods that are accessible in superclasses can be overwritten in subclasses. If a method in the superclass cannot be accessed, the method cannot be overridden in the subclass even if the method in the subclass has the same name as the method. When a method is called at runtime, the system considers its accessibility and thus determines which implementation of it is running.
The following specially constructed example is explained more clearly. Suppose we declare an Abstract-Base class in the P1 package:
package P1; { Ab Ab AbAb public abstract class AbstractBase private void pri() { print( " stractBase.pri()"):} void pac () { print(" stractBase.pac() " ); } protected void pro() { print( " stractBase.pro()" ); } public void pub() { print (" stractBase.pub()");} public final void show() pri(); pac(); pro(); pub(); } }In this class, we define 4 methods, each with a different access modifier, and the body of the method only identifies itself. The method show calls these 4 methods on the current object in turn. When applying this method to different subclass objects, it can explain which implementation of these methods is called.
Now, we define the class Concretel, which extends the AbstractBase class, but is located in the P2 package:
package P2; import P1.AbstractBase public class Concretel extends AbstractBase{ public void pri(){print("Concretel.pri()");} public void pac(){print("Concretel.pac()");} public void pro(){print("Concretel.pro()");} public void pub(){print("Concretel.pub()");} }The 4 methods in the superclass are redeclared in this class and their implementations are changed, which are reporting that they belong to the Con-cretel class. At the same time, their access rights have been changed to public for other code to access. Execute the following code
new Concretel().show():
The following output will be generated:
AbstractBase.pri() AbstractBase.pac() Concretel.pro() Concretel.pub()
Because the private method pri cannot be accessed by subclasses (or other classes), the show method always calls the implementation of the pri method in the AbstractBase class. The pac method with package access permissions in the AbstractBase class cannot be accessed by Concretel, so the implementation of the pac method in the Concretel class cannot override the definition in the AbstractBase class, so the show method calls the AbstractBase.pac method. The pro method and the pub method are both accessible in the Concretel class and can also be overwritten, so the show method calls the implementation of these two methods in the Concretel class.
Follow our foot meaning class Concrete2 to extend the class Concretel, and then we put it in the same package P1 as the AbstractBase class':
package P1; import P2.Concretel public class Concrete2 extends Concretel{ public void pri(){print("Concrete2.pri()");} public void pac(){print("Concrete2.pac()");} public void pro(){print("Concrete2.pro()");} public void pub(){print("Concrete2.pub()");} }Because methods in Concretel have public access rights, they can be accessed in Concrete2, and each method in Concrete2 covers its corresponding methods separately. In addition, since Concrete2 and AbstractBase are in the same package, the method AbstractBase.pac can also be accessed in Concrete2, and the method Concrete2.pac can be overridden. Call the show method on the Concrete2 object, and the print result is as follows:
AbstractBase.pri() Concrete2.pac() Concrete2.pro() Concrete2.pub()
Finally, we define the class Concrete3 to extend the class Concrete2 and put it in package P3:
package P3 import P1.Concrete2; public class Concrete3 extends Concrete2{ public void pri(){print("Concrete3.pri()");} public void pac Q{print("Concrete3.pac()");} public void pro(){print("Concrete3.pro()");} public void pub(){print("Concrete3.pub()");} }Call the show method on the Concrete3 object, and the print result is as follows:
AbstractBase.pri() Concrete3.pac() Concrete3.pro() Concrete3.pub()
Here method Concrete3.pac looks like it overrides the inaccessible AbstractBase.pac method, but in fact, method Concrete3.pac overrides the method Concrete2.pac, and method Concrete2.pac overrides the method AbstractBase.pac, so method Concrete3.pac indirectly overrides the method AbstractBase.pac. By redeclaring the pac method in class Concrete2 as having public access permissions, it can be accessed and overwritten by any subclass.
Package Objects and Specifications
Packages usually implement some specification, and are usually from an organization. Package objects are different from other reflection types and cannot be used to create or operate packages, but can only serve as a knowledge base for providing information, which provides information about the specifications implemented by the package (the title, vendor, and version number) and information about the implementation of the package itself (the title, vendor, and version number of the package). Although packages usually come from individual organizations, the specifications it implements (such as statistical analysis libraries) may be defined by other organizations. Programs using packages may need to know the version of the specification implemented by the package, so that functions defined only in a certain version can be used. Similarly, these programs may also need to know which implementation version is provided to it, mainly to deal with possible flaws in different versions. Some of the main methods of the Package class allow access to this information:
・public Stri ng getName (): Returns the name of the package.
.public string getspecificationTitle p: Returns the title of the specification implemented by the package. If the title is unknown, return null,
.public string getspecificationversion(): Returns a string describing the version information of the specification implemented by the package. If the version information is unknown, return null,
.public string getspecificationvendor Q: Returns the name of the supplier, which owns and maintains the specifications implemented by the package. If the supplier is unknown, return null,
.public string getImplerentationTitle(): Returns the title of the implementation provided by the package. If the title is unknown, it returns null, ・public string getImplementationversion(): Returns a string describing the version information of the implementation provided by the package. If the version information is unknown, it returns null,
・public string getImplementationvendor(): Returns the name of the organization (vendor) that provides the implementation. If the organization is unknown, return null,
For example, if we extract these information from the java.lang package in our system, we will get the following results:
Specification Title: Java Platform API Specification Specification Version: 1.4 Specification Vendor:Sun Microsystems, Inc. Implementation Title:Java Runtime Environment Implementation Version:1.5.0_02 Implementation Vendor: Sun Microsystems, Inc.
The canonical version number consists of non-negative numbers separated by period delimiters, such as ''2.0'' or '11.0.12'. This pattern allows us to call the iscompatiblewith method to compare the version number that follows this pattern with the version number of the package. If the version number of the package is greater than or equal to the version number of the successor, then the method returns true. This comparison only compares a period-separated number at a time. If any of these numbers is smaller than the corresponding position in the passed version number, then the two versions are incompatible. If one of the version numbers is longer than the other, the missing part in the short version numbers will be considered zero. For example, if the package's canonical version number is "1.4" and we compare it with "1.2", "1.3.1'. or ".1.81., then true will be returned; but if compared with "1.4.2'. or ".5", then false will be returned. This conclusion is drawn because this comparison mechanism assumes that the specification version is backward compatible.
There is no specified format for the implementation version number, because different organizations that provide the implementation will define the implementation version differently. The only comparison that can be made between implementation versions is to test whether the version is the same, where there is no assumption of backward compatibility.
The package can be sealed, which means that classes can no longer be added to the package. Unsealed packages can contain classes from multiple different locations in the class search path, while the contents of the sealed package must come from the same location—either a specific archive or a location specified by a URL. There are two ways to determine if a package is sealed:
.public boolean issealed p: Return trueo if the package is sealed
.public boolean issealed(URL url): Return true if the package is sealed for the given URL, that is, the classes in the package can be loaded from this given URL. If the class in the package cannot be loaded from a given URL, or the package is not sealed, then false is returned, and the specification and implementation information of the package are usually provided as part of the manifest file stored with the package - for example as part of the manifest file in a Java archive (jar), as described in Section 25.9.2, “Archive file java.util.jar”. When a class in a package is loaded, this information is read by the person. A ClassLoader can dynamically define a Package object for the class it wants to load:
.protected Package deNePackage (String name, string specTitle, String specversion, string specvendor, String implTitle, string implversion, string implvendor, uRL sealBase): This method will return a Package object with the given package name and specification and implementation value set by the corresponding quote. If the parameter sealBase is null, then the package is not sealed, otherwise the package is sealed for this URL: the class's Package object must be defined before the class is defined, and the package name must be unique in the class loader. If the package name is repeated with the existing name, the work 11ega1ArgumentException will be thrown.
We can call the getPackage method of the Class object of the given class to get the Package object of this class. We can also call the static Package.getPackage with the given package name to get the Package object, or call the static Package.getPackages, which will return the Package array composed of all packages currently known to the class loader. Both methods are related to the class loader that calls their code, because these codes will call the get-Package or getPackages methods of their class loader. These class loaders' methods will search for a specific class loader and all its parent class loaders, and if no settings are made for the current class loader, the system class loader will be used at this time. Note that if the package is unknown, the classloader method will return null because no type in the package has been loaded at this time.