An in depth look at the package package structure in the Java code organization

  • 2020-05-07 19:36:02
  • OfStack

If we call the getPackage method on the Class object, we get the Package object that describes the package of the class (the Package class is defined in java.lang). We can also get the Package object with the package name by calling the static method getPackage or the static method getPackages(which returns an array of all known packages in the system). The getName method returns the full name of the package.

The use of Package objects is completely different from other reflection types in that we cannot create or manipulate packages at run time. We can use the Package object to get information about the package, such as the purpose of the package, who created the package, the version of the package, and so on. We will defer these to later when we discuss the package in more detail.

Name the package
The name of the
package should be avoided from conflicting with other packages, so choosing a meaningful and one-only name is an important aspect of package design. But programmers around the world are developing packages, and there is no way to know who is using what package name, so choosing a package name with only 1 is a challenge. If we determine that a package is only used within our organization, then we can have an internal mediator (internal arbiter) to ensure that there are no name conflicts between projects.

But this approach is impractical for the world as a whole. Package identifiers are simple names, and a good way to ensure that package names are only 1 is to use the Internet domain name. If the name of the company we work for is Magic.lnc, and the domain name of the company is magi c.com, then the property package statement should be:


  package com.magic.attr; 

Note that the domain name constituent elements here are in reverse order of a regular domain name.

If we adopt this usage, the package name we adopt will not conflict with anyone else's package name except for the possible conflict within our organization. If we do have a conflict within our organization (probably a large enterprise), we can use a more specific domain name to further qualify. Many large companies have internal subdomains, such as east and europe, which you can use to further qualify the package name:


  package corn. magic.japan.attr;

Using this scheme can make the package name very long, but it is relatively safe. Programmers who use this technique do not choose the same package name, and programmers who do not use this technique do not choose the name we use.

The contents of the package
The content of the
package should be carefully designed to include only functionally relevant classes and interfaces. Classes in the package are free to access non-private members of other classes in the package, and some classes may even have sufficient permissions to access the internal details of other classes. In order to prevent such classes from mistakenly manipulating class members, we need to protect class members. Any member that is not declared as private can be accessed with all other types in the same package, so the degree of coupling between any unrelated classes is likely to be higher than expected.

Packages also provide logical grouping for programmers looking for useful interfaces and classes. Packages of unrelated classes make it difficult for programmers to tell which interfaces and classes are useful, and logical grouping of classes helps programmers reuse code because programmers can find what they need more easily through logical grouping. If the package contains only a set of related, compactly coupled types, that means we can give the type a more intuitive name to avoid name collisions.

Packages can be nested. For example, java.lang is a nested package, where the package Lang is nested in the larger package java, while the package j ava contains some other packages. Nesting causes related packages to form a hierarchical naming system.

For example, to create a set of packages for adaptive systems such as neural networks and genetic algorithms, we can create nested packages by naming packages with polka-dotted names:


  package adaptive. neural Net;

The source file containing the above declaration is in the adaptive.neuralNet package, and the adaptive.neuralNet package itself is a subpackage of the adaptive package. The adaptive package may contain classes related to generic adaptive algorithms, such as the generalization problem statement class or benchmark class. Packages that are deeper in the hierarchy (for example, adaptive.neu-ralNet or adaptive.genetic) contain classes related to a particular type of adaptive algorithm.

Package nesting is just one tool for organizing related packages, and it does not provide any special access between packages.

The class code in the adaptive.genetic package cannot access the members of the adaptive or adaptive.neuralNet package with package access rights, and the package scope only applies to specific packages. The nesting of packages provides no benefit beyond the ability to group related packages and help the programmer find the desired class more easily at the logical level.

Package of annotation

Packages can also be annotated. The problem, however, is that because packages are an organizational structure, there are no source code entities, and they have no actual definition, they cannot be annotated like classes or methods, so they can only be annotated by annotating the package's declaration statements in the source file. However, only 1 package declaration can have annotations that act on it in each package.

So how exactly do you annotate packages? In fact, the Java language does not force programmers to use a certain way to handle the "single 1-annotated package statement" rule. The recommended approach is to create a file called package1i nfo.java in the package directory, where you store only the package statements and annotations for the package, and nothing else. For example, the package1info.java file for the attr package looks like this:


  @PackageSpec(name2 " Attr Project" . version="1.0"

  @DevelopmentSite("attr.project.org")

  @DevelopmentModel("open1source")

  package attr;

Packagespec,Developmentsite, and Devel opmentmodel are used to modify the annotation types, of course, they have runtime save policies. The package 1es1010en.java file should compile with the other source files in the package.

We recommend that all package-related information be placed in the package1info.java file. If you do this, then you can place a document comment at the beginning of the file so that the document is commented into a package document.

Access to the package
When
declares the accessibility of top-level classes and top-level interfaces in packages, it has two choices: package access (package) and public access (public). Classes or interfaces decorated with public can be accessed by code outside the package, while types not decorated with public have package scope: they can be accessed by other code in the same package. But for code outside of the package, or even within the subpackage, it is hidden. When we declare types, we should declare only those types that other programmers need to use as public, and hide those types that are part of the package's implementation details. This technique gives us a great deal of flexibility, because programmers are not dependent on the type of implementation details they cannot access, so we are free to change the implementation details when we want to.

Class members that are not declared public,protected, or private can be accessed directly by any code in the package, but are hidden from code outside the package. In other words, the default access modifier is "package", except for members of the interface whose default access modifier is" public".

No field or method declared as private in the package can be accessed by all the other code in the package, so the classes in the same package are considered "friendly" or "trustworthy." This allows us to define an application framework that combines predefined code (predefined code) and placeholder code (placeholder code), where the placeholder code is overridden by a subclass of the framework class. Predefined code can use package access modifiers so that other collaborating code within the package can access them directly, but is not accessible to users outside the package. However, the subpackage of the package in which the code resides is not trusted, and vice versa. For example, code decorated with the package access modifier in package dit cannot be accessed by code in its child package dit.dat, and vice versa.

Therefore, three different contracts are defined for each type:

publi. Contract: defines the main functionality of the type.

.protected contract: defines the functionality available to subclasses for specialized purposes.

.package contract: defines the functionality available to other code within the package to enable collaboration between types within the package. All of these contracts require careful consideration and design.

Accessibility and and cover methods

Only methods that are accessible in a superclass can be overridden in a subclass. If a method in a superclass cannot be accessed, it cannot be overridden in a subclass even if the method in the subclass has the same name as the method. When a method is called at run time, the system considers its accessibility to determine which specific implementation to run.

The following deliberately constructed example explains it 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 four methods, each with a different access modifier, and the body of the method identifies itself only. The method show calls each of the four methods in turn on the current object, and when the method is applied to different subclass objects, it indicates which implementation of the method was called.

Now, we define class Concretel, which extends the AbstractBase class but is 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()");}

  }

In this class, four methods in the superclass were redeclared and their implementations changed, which reported that they belonged to the Con-cretel class. At the same time, their access rights were changed to public for other code to access. Execute the following code


  new Concretel().show():

The following output will be produced:


  AbstractBase.pri()

  AbstractBase.pac()

  Concretel.pro()

  Concretel.pub ()

Because the private method pri cannot be accessed by a subclass (or any other class), the show method always calls the implementation of the pri method in the AbstractBase class. The pac method with package access 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 pub method are both accessible and overridden in the Concretel class, so the show method calls the implementation of both methods in the Concretel class.

We extend Concretel with Concrete2, and then we put it in the same package as AbstractBase ':


  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 all the methods in Concretel have access to public, they are accessible in Concrete2, and each method in Concrete2 overrides its corresponding method. In addition, because Concrete2 and Ab-stractBase are in the same package, the method AbstractBase.pac is also accessible in Concrete2, and the method Concrete2.pac can be overridden. Call the show method on the Concrete2 object and print the result as follows:


  AbstractBase.pri()

  Concrete2.pac()

  Concrete2 .pro()

  Concrete2.pub()

Finally, we define class Concrete3 to extend class Concrete2 and place it in package P3:


  package corn. magic.japan.attr;

0

Call the show method on the Concrete3 object and print the result as follows:


  package corn. magic.japan.attr;

1

Here the method Concrete3.pac appears to override the inaccessible AbstractBase.pac method, but in fact, the method Concrete3.pac overrides the method Concrete2.pac, and the method Concrete2.pac overrides the method AbstractBase2.pac, so the method Concrete3.pac indirectly overrides the method AbstractBase.pac. By redeclaring the pac method as having public access in class Concrete2, it can be accessed and overridden by any subclass.


package objects and specifications

Packages usually implement some specification and are usually from an organization. Unlike other reflection types, the Package object cannot be used to create or manipulate packages, but can only serve as a repository for information about the specification the package implements (the specification title, vendor, and version number) and about the package implementation itself (package title, vendor, and version number). Although a package usually comes from a single organization, it may implement specifications (such as a statistical analysis library) that have been defined by other organizations. A program that USES a package may need to know the version of the specification that the package implements so that it can use functionality that is defined only in one version. Similarly, these programs may also need to know which implementation version is being provided to them, primarily to address possible defects 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 or, if the title is unknown, null,

.public string getspecificationversion(): returns a string describing the version information of the specification implemented by the package. If version information is unknown, returns null,

public string getspecificationvendor Q: returns the name of the vendor who owns and maintains the specification implemented by the package, returns null if the vendor is unknown,

.public string getImplerentationTitle(): returns the title of the implementation provided by the package, if the title is unknown, returns null, · public string getImplementationversion(): returns a string describing the version of the implementation provided by the package, if the version information is unknown, returns null,

· public string getImplementationvendor(): returns the name of the organization (vendor) that provided the implementation, or null if the organization is unknown,

For example, if we extract this information from the java.lang package in our system, we will get the following results:


  package corn. magic.japan.attr;

2

The specification version number consists of non-negative digits separated by a period separator, such as' '2.0 "or" 11.0.12". This pattern allows us to call the iscompatiblewith method to compare the version number that follows the pattern with the version number of the package. This method returns true if the package version number is greater than or equal to the descendant version number. This comparison compares only one number separated by a period at a time, and if any of these Numbers is less than the value of the corresponding position in the version number passed in, the two versions are incompatible. If one version number is longer than the other, the missing part of the short version number is considered to be zero. For example, if the package specification version number is "1.4" and we compare it with" 1.2","1.3.1'. However, if compared to '1.4.2' or '.5 ', false is returned. This is because the comparison mechanism assumes that the specification version is backward compatible.

There is no specified format for the version number of the implementation, because the implementation version is defined differently by the different organizations that provide the implementation. The only comparison that can be made between implementation versions is whether the test version is the same, with no assumption of downward compatibility.

Packages can be sealed, which means you can't add classes to the package. An unsealed package can contain classes from several different locations in the class search path, and the contents of the sealed package must come from the same location -- either a specific archive or a location specified by URL. There are two ways to determine if a package is sealed:

public boolean issealed p: if the package is sealed, return trueo

public boolean issealed(URL url): if the package is sealed for a given URL, true is returned, that is, the classes in the package can be loaded from the given URL. If in class can't from the given URL loaded, or package is not sealed, it returns false, specification and implementation of the package information is usually as stored in 1 listing file with package and provide - for example as Java 1 part of the archive file (jar) 1 part of the listing of files, like 25.9.2 section "archive file java. util. jar" described in it. This information is read when the classes in the package are loaded. The class loader (ClassLoader) dynamically defines an Package object for the class it is loading:

protected Package de nePackage The object has a given package name and specification and implementation values set by the corresponding arguments. If the parameter sealBase is null, then the package is not sealed, otherwise the package is sealed for the URL: the Package object of the class must be defined before the class is defined, and the package name must be unique in the classloader. If the package name is repeated with the existing name, the worker 11ega1ArgumentException exception is thrown.

We can call the getPackage method of the Class object of a given class to obtain the Package object of that class. We can also call the static side Package.getPackage to get the Package object with the given package name, or the static side Package.getPackages, which will return an Package array of all packages currently known to the class loader. Both methods are related to the classloader of the code that invokes them, because the code will call the get-Package or getPackages methods of its classloader. The methods of these classloaders search for a particular classloader and all of its superloaders, and if nothing is set for the current classloader, then the system classloader is used. Note that if the package is unknown, the classloader method returns null, because no type in the package has been loaded yet.


Related articles: