7. Embalaje (2)

Resumen del capítulo

  • modificador de acceso
    • acceso al paquete
    • público: permisos de acceso a la interfaz
    • paquete predeterminado
    • privado: no puedes acceder
    • protegido: derechos de acceso heredados
    • Derechos de acceso a paquetes versus constructor público
  • interfaz e implementación
  • acceso a clase

modificador de acceso

Los modificadores de acceso de Java public , protected y private se colocan antes del nombre de la clase, el nombre de la propiedad y el nombre del método definidos. Cada modificador de acceso solo puede controlar el objeto que modifica.

Si no se proporciona ningún modificador de acceso, significa "acceso al paquete". De todos modos, todo tiene algún tipo de control de acceso. En las siguientes secciones, aprenderá sobre los distintos tipos de permisos de acceso.

acceso al paquete

Todos los ejemplos anteriores de este capítulo utilizaron el modificador de acceso público o no utilizaron el modificador ( acceso predeterminado) . El acceso predeterminado no tiene palabras clave y a menudo se denomina acceso a paquetes (a veces también se le llama amigable ). Esto significa que todas las demás clases del paquete actual pueden acceder a ese miembro. Para las clases fuera de este paquete, este miembro aparece como privado . Dado que una unidad de compilación (es decir, un archivo) solo puede pertenecer a un paquete, todas las clases ubicadas en la misma unidad de compilación son accesibles entre sí a través de permisos de acceso al paquete.

Los permisos de acceso a paquetes pueden agrupar clases relacionadas en un paquete para que puedan acceder fácilmente entre sí. Las clases de un paquete otorgan a los miembros de su paquete permisos de acceso para acceder entre sí, por lo que usted es "dueño" del código del programa en el paquete. Tiene sentido acceder únicamente a otro código que posee a través del código que posee. La creación de un mecanismo de acceso a paquetes es una de las razones importantes para agrupar clases en paquetes. En muchos lenguajes, la forma de organizar las definiciones en un archivo es arbitraria, pero en Java estás obligado a organizarlas de forma sensata. Además, puede excluir del paquete clases que no deberían tener acceso a las clases del paquete actual.

Una clase controla qué código tiene acceso a sus miembros. El código de otros paquetes no puede simplemente aparecer y decir "¡Hola, soy amigo de Bob !" y luego querer ver los miembros privados , de acceso a paquetes y protegidos de Bob . La única forma de obtener acceso a un miembro es:

  1. Hacer público el miembro . Entonces, no importa quién seas ni dónde estés, podrás acceder a él.
  2. Otorgue a los miembros acceso predeterminado al paquete sin agregar ningún modificador de acceso y luego coloque otras clases en el mismo paquete. De esta forma, otras clases pueden acceder al miembro.
  3. Como verá en el capítulo "Reutilización", las clases heredadas pueden acceder tanto a miembros públicos como protegidos (pero no a miembros privados ). Solo cuando dos clases están en el mismo paquete puede acceder a miembros con permisos de acceso al paquete. Pero no te preocupes por la herencia y la protección ahora .
  4. Proporciona métodos de acceso y mutador (a veces llamados métodos "get/set") para leer y cambiar valores.

público: permisos de acceso a la interfaz

Cuando usa la palabra clave public , significa que los miembros declarados inmediatamente después de public están disponibles para todos, especialmente para los programadores cliente que usan la biblioteca de clases. Supongamos que se define un paquete de postres que contiene la siguiente unidad de compilación :

// hiding/dessert/Cookie.java
// Creates a library
package hiding.dessert;

public class Cookie {
    
    
    public Cookie() {
    
    
        System.out.println("Cookie constructor");
    }
    
    void bite() {
    
    
        System.out.println("bite");
    }
}

Recuerde que los archivos de clase generados por el archivo Cookie.java deben ubicarse en un subdirectorio llamado postre oculto (que indica el capítulo "Encapsulación" de este libro), que debe estar varios directorios debajo de CLASSPATH. No cometa el error de pensar que Java siempre considerará el directorio actual como uno de los puntos de partida para el comportamiento de búsqueda. Si no tiene un . en su CLASSPATH , Java no buscará en el directorio actual.

Ahora, crea un programa usando cookies :

// hiding/Dinner.java
// Uses the library
import hiding.dessert.*;

public class Dinner {
    
    
    public static void main(String[] args) {
    
    
        Cookie x = new Cookie();
        // -x.bite(); // Can't access
    }
}

Producción:

Cookie constructor

Puedes crear un objeto Cookie porque su constructor y su clase son públicos . (Más conceptos públicos se verán más adelante ) Sin embargo, no se puede acceder al método en el objeto Cookie en Dinner.java , porque solo se proporciona acceso al paquete, por lo que no se puede acceder a él fuera del paquete de postre y el compilador le prohíbe usarlo. .bite()bite()

paquete predeterminado

Quizás le sorprenda saber que el siguiente código, a pesar de que parece infringir las reglas, aún se compila:

// hiding/Cake.java
// Accesses a class in a separate compilation unit
class Cake {
    
    
    public static void main(String[] args) {
    
    
        Pie x = new Pie();
        x.f();
    }
}

Producción:

Pie.f()

Segundo archivo en el mismo directorio:

// hiding/Pie.java
// The other class
class Pie {
    
    
    void f() {
    
    
        System.out.println("Pie.f()");
    }
}

Al principio puede parecer que estos dos archivos no están relacionados, pero en Cake puedes crear un objeto Pie y llamar a sus f()métodos. (Tenga en cuenta que debe haber un . en su CLASSPATH para que el archivo pueda compilarse). Generalmente se considera que Pie tiene   permisos de acceso a paquetes, por lo que Cakef() no puede acceder a ellos . Tienen acceso a paquetes, lo cual es parcialmente cierto. Cake.java puede acceder a ellos porque están en el mismo directorio y no se dieron nombres de paquetes explícitos. Java considera que dichos archivos pertenecen al paquete predeterminado del directorio, por lo que proporcionan acceso al paquete a todos los demás archivos del directorio.

privado: no puedes acceder

La palabra clave privado significa que ninguna otra clase puede acceder a este miembro, excepto la clase que contiene el miembro. Otras clases del mismo paquete no pueden acceder a miembros privados , por lo que esto equivale a aislarse. Por otro lado, es posible que muchas personas colaboren en la creación de un paquete. Con private , puedes modificar libremente ese miembro modificado sin preocuparte de afectar otras clases bajo el mismo paquete.

El acceso predeterminado a los paquetes generalmente proporciona una ocultación adecuada; recuerde, los programadores cliente que usan la clase no tienen acceso a los miembros de acceso a los paquetes. Esto está bien porque el acceso predeterminado es el que usamos todo el tiempo (y el que obtenemos automáticamente cuando olvidamos agregar algún acceso). Por lo tanto, lo que generalmente se considera es qué miembros deben declararse públicos para que los utilicen los programadores del cliente. Por lo tanto, la palabra clave privado no se utiliza con frecuencia inicialmente porque el programa puede funcionar normalmente sin ella. Sin embargo, el uso de privado es muy importante, especialmente en un entorno de subprocesos múltiples. (Verá esto en el capítulo "Programación concurrente").

A continuación se muestra un ejemplo del uso de privado :

// hiding/IceCream.java
// Demonstrates "private" keyword

class Sundae {
    
    
    private Sundae() {
    
    }
    static Sundae makeASundae() {
    
    
        return new Sundae();
    }
}

public class IceCream {
    
    
    public static void main(String[] args) {
    
    
        //- Sundae x = new Sundae();
        Sundae x = Sundae.makeASundae();
    }
}

Lo anterior muestra dónde entra en juego lo privado : controlar cómo se crean los objetos y evitar que otros accedan directamente a un constructor específico (o a todos los constructores). En el ejemplo, no puede crear un objeto Sundae a través del constructor , pero debe llamar makeASundae()al método para crear el objeto.

Cualquier método "auxiliar" que seguramente sea solo esa clase, puede declararse privado para garantizar que no se utilice incorrectamente en ninguna otra parte del paquete y para evitar que usted lo cambie o elimine. Declarar un método privado garantiza que tenga esta opción.

Lo mismo ocurre con las propiedades privadas en clases . A menos que deba exponerse la implementación subyacente (lo cual es poco común), declare la propiedad como privada . Sin embargo, sólo porque una referencia a un objeto en una clase sea privada no significa que otros objetos no puedan tener una referencia pública al objeto (consulte el Apéndice: Paso y retorno de objetos).

protegido: derechos de acceso heredados

Para comprender los derechos de acceso protegidos , debemos dar un pequeño salto en el contenido. En primer lugar, no es necesario que comprenda realmente el contenido de esta sección antes de presentar el capítulo "Reutilización" de este libro. Pero para completar, aquí hay una breve introducción y un ejemplo del uso de protected .

La palabra clave protected trata con el concepto de herencia, que le permite aprovechar una clase existente (lo que llamamos una clase base) y luego agregar nuevos miembros a la clase existente sin tocar la clase existente. También podemos cambiar el comportamiento de los miembros existentes de una clase. Para heredar de una clase, necesitas declarar la nueva clase para extender una clase existente, así:

class Foo extends Bar {
    
    }

El resto de la definición de clase tiene el mismo aspecto.

Si crea un nuevo paquete y hereda una clase de otro paquete, lo único a lo que se puede acceder son los miembros públicos de la clase heredada. (Si hereda del mismo paquete, puede operar en todos los miembros con acceso al paquete). A veces, el creador de una clase base querrá que un miembro específico sea accesible para la clase heredera, pero no para otras clases. En este momento necesitas usar protected . protected también proporciona acceso a paquetes, es decir, otras clases dentro del mismo paquete pueden acceder a elementos protegidos .

Volviendo al archivo anterior Cookie.java , la siguiente clase no puede llamar al método de acceso al paquete bite():

// hiding/ChocolateChip.java
// Can't use package-access member from another package
import hiding.dessert.*;

public class ChocolateChip extends Cookie {
    
    
    public ChocolateChip() {
    
    
        System.out.println("ChocolateChip constructor");
    } 
    
    public void chomp() {
    
    
        //- bite(); // Can't access bite
    }
    
    public static void main(String[] args) {
    
    
        ChocolateChip x = new ChocolateChip();
        x.chomp();
    }
}

Producción:

Cookie constructor
ChocolateChip constructor

Si existe un método en la clase Cookiebite() , entonces hay un método en cualquiera de sus subclases bite(). Pero como bite()tiene acceso al paquete y está en otro paquete, no podemos usarlo en este paquete. Podrías declararlo público , pero entonces todos tendrían acceso a él, lo que probablemente no sea lo que deseas. Si cambia la cookie para que se vea así:

// hiding/cookie2/Cookie.java
package hiding.cookie2;

public class Cookie {
    
    
    public Cookie() {
    
    
        System.out.println("Cookie constructor");
    }
    
    protected void bite() {
    
    
        System.out.println("bite");
    }
}

De esta forma, bite()todas las clases que heredan Cookie son accesibles:

// hiding/ChocolateChip2.java
import hiding.cookie2.*;

public class ChocolateChip2 extends Cookie {
    
    
    public ChocoalteChip2() {
    
    
        System.out.println("ChocolateChip2 constructor");
    }
    
    public void chomp() {
    
    
        bite(); // Protected method
    }
    
    public static void main(String[] args) {
    
    
        ChocolateChip2 x = new ChocolateChip2();
        x.chomp();
    }
}

Producción:

Cookie constructor
ChocolateChip2 constructor
bite

Aunque bite()también tiene acceso a paquetes, no es público .

Derechos de acceso a paquetes versus constructor público

Cuando defines una clase con acceso a paquetes, puedes definir un constructor público en la clase y el compilador no se quejará:

// hiding/packageaccess/PublicConstructor.java
package hiding.packageaccess;

class PublicConstructor {
    
    
    public PublicConstructor() {
    
    }
}

Existe una herramienta Checkstyle, puede ejecutar el comando gradlew hide:checkstyleMain para usarla, le indicará que este escrito es falso y técnicamente incorrecto. De hecho, no puedes acceder a este constructor público desde fuera del paquete:

// hiding/CreatePackageAccessObject.java
// {WillNotCompile}
import hiding.packageaccess.*;

public class CreatePackageAcessObject {
    
    
    public static void main(String[] args) {
    
    
        new PublicConstructor();
    }
}

Si compila esta clase, recibirá un mensaje de error de compilación:

CreatePackageAccessObject.java:6:error:
PublicConstructor is not public in hiding.packageaccess;
cannot be accessed from outside package
new PublicConstructor();
^
1 error

Por lo tanto, definir un constructor público en una clase con acceso a paquetes en realidad no hace que el constructor sea público y debe marcarse como un error en tiempo de compilación cuando se declara.

interfaz e implementación

El control de acceso a menudo se denomina ocultación de implementación. Encapsular datos y métodos en clases y ocultar la implementación se denomina encapsulación. El resultado es un tipo de datos con características y comportamiento.

El control de acceso traza límites dentro de los tipos de datos por dos razones importantes. La primera razón es establecer los límites de lo que los programadores cliente pueden y no pueden usar. Puede construir sus propios componentes internos en la estructura sin tener que preocuparse de que los programadores del cliente ocasionalmente hagan que la implementación interna forme parte de una interfaz que pueden usar.

Esto lleva directamente a la segunda razón: separar la interfaz de la implementación. Si la interfaz se usa en un conjunto de programas y el programador cliente solo puede enviar mensajes a la interfaz pública , entonces es libre de modificar cualquier cosa que no sea pública (como permisos de acceso a paquetes, cosas protegidas o modificadas de forma privada). pero no romperá el código del cliente.

Para mayor claridad, puede utilizar un estilo de creación de clases: los miembros públicos se colocan al principio de la clase, seguidos por los miembros protegidos , los miembros con acceso a paquetes y, finalmente, los miembros privados . La ventaja de esto es que los usuarios de la clase pueden leer desde el principio y ver primero las partes más importantes para ellos (miembros públicos, porque se puede acceder a ellos desde fuera del archivo) y dejar de leer cuando se encuentran con miembros no públicos. de la siguiente manera Se implementa internamente:

// hiding/OrganizedByAccess.java

public class OrganizedByAccess {
    
    
    public void pub1() {
    
    /* ... */}
    public void pub2() {
    
    /* ... */}
    public void pub3() {
    
    /* ... */}
    private void priv1() {
    
    /* ... */}
    private void priv2() {
    
    /* ... */}
    private void priv3() {
    
    /* ... */}
    private int i;
    // ...
}

Hacerlo sólo hace que el programa sea un poco más fácil de leer, ya que la implementación y la interfaz todavía están mezcladas. Es decir, todavía puedes ver el código fuente, la parte de implementación, porque está justo dentro de la clase. Además, la función de documentación de anotaciones proporcionada por javadoc reduce la importancia de la legibilidad del código del programa para los programadores del cliente. En realidad, la tarea del navegador de clases es presentar la interfaz a los usuarios de la clase. El navegador de clases mostrará todas las clases disponibles y le dirá cómo usarlas (por ejemplo, qué miembros están disponibles). En Java, la documentación JDK actúa como un navegador de clases.

acceso a clase

Los modificadores de acceso también se pueden utilizar para determinar qué clases de una biblioteca de clases están disponibles para los usuarios de la biblioteca de clases. Si desea que los programadores cliente utilicen una clase, aplique la palabra clave pública a la definición de toda la clase. Esto incluso controla si los programadores clientes pueden crear objetos de la clase.

Para controlar el acceso a una clase, los modificadores deben aparecer antes de la palabra clave class :

public class Widget {
    
    

Si el nombre de su biblioteca de clases está oculto , cualquier programador cliente puede acceder al widget mediante la siguiente declaración :

import hiding.Widget;

o

import hiding.*;

Hay algunas restricciones adicionales aquí:

  1. Sólo puede haber una clase pública por unidad de compilación (es decir, por archivo) . Esto significa que cada unidad de compilación tiene una interfaz pública representada por una clase pública . La interfaz puede contener muchas clases que admitan el acceso a paquetes. Una vez que aparezca más de una clase pública en una unidad de compilación, la compilación informará un error.
  2. El nombre de la clase pública debe ser el mismo que el nombre del archivo que contiene la unidad de compilación, incluido el caso. Entonces, para Widget , el nombre del archivo debe ser Widget.java , no widget.java o WIDGET.java . Nuevamente, si los nombres no coinciden, el compilador se quejará.
  3. Aunque no es muy común, es posible que no haya clases públicas en una unidad de compilación. En este punto, puede nombrar el archivo como desee (aunque un nombre arbitrario confundirá a los lectores y mantenedores del código).

Si es una clase en el paquete oculto , que solo se usa para completar las tareas que deben realizar otras clases públicas en el widget o paquete oculto , ¿cómo configurar sus derechos de acceso? No quiere tomarse la molestia de crear documentación para los programadores del cliente y cree que no pasará mucho tiempo antes de que el esquema se cambie por completo y la versión anterior se elimine y se reemplace por una nueva. Para preservar esta flexibilidad, debe asegurarse de que los programadores del cliente no confíen en ningún detalle específico oculto en el hide , así que elimine la palabra clave pública de la clase, déle acceso al paquete y estará listo.

Cuando crea una clase con acceso a paquetes, aún tiene sentido declarar las propiedades de la clase como privadas ; todas las propiedades deben declararse lo más privadas posible , pero los métodos generalmente se declaran con la clase (acceso a paquetes). Los mismos derechos de acceso son también razonable. Una clase con derechos de acceso al paquete sólo se puede utilizar dentro del paquete a menos que se obligue a declarar públicos ciertos métodos , en cuyo caso el compilador se lo informará.

Tenga en cuenta que una clase no puede ser privada (de modo que ninguna clase excepto la propia clase pueda acceder a ella) ni puede estar protegida . Por lo tanto, solo hay dos opciones para los permisos de acceso a clases: permisos de acceso a paquetes o públicos . Para evitar que el mundo exterior acceda a la clase, puede declarar todos los constructores como privados , de modo que solo usted pueda crear objetos (en los miembros estáticos de la clase):

// hiding/Lunch.java
// Demonstrates class access specifiers. Make a class
// effectively private with private constructors:

class Soup1 {
    
    
    private Soup1() {
    
    }
    
    public static Soup1 makeSoup() {
    
     // [1]
        return new Soup1();
    }
}

class Soup2 {
    
    
    private Soup2() {
    
    }
    
    private static Soup2 ps1 = new Soup2(); // [2]
    
    public static Soup2 access() {
    
    
        return ps1;
    }
    
    public void f() {
    
    }
}
// Only one public class allowed per file:
public class Lunch {
    
    
    void testPrivate() {
    
    
        // Can't do this! Private constructor:
        //- Soup1 soup = new Soup1();
    }
    
    void testStatic() {
    
    
        Soup1 soup = Soup1.makeSoup();
    }
    
    void testSingleton() {
    
    
        Soup2.access().f();
    }
}

Los objetos se pueden crear a través del método estático como [1] , o se puede crear primero un objeto estático como [2], y se puede devolver una referencia al objeto cuando el usuario necesite acceder a él.

La mayoría de los métodos hasta ahora devuelven tipos primitivos o vacíos, por lo que la definición en [1] puede parecer confusa a primera vista. Soup1 delante del nombre del método ( makeSoup ) indica el tipo que devuelve el método. Hasta ahora, aquí suele ser nulo , es decir, no devuelve nada. Sin embargo, también es posible devolver una referencia al objeto, como aquí. Este método devuelve una referencia al objeto de clase Soup1 .

Soup1 y Soup2 muestran cómo evitar la creación directa de objetos de una clase declarando privados todos sus constructores. Recuerde, si no crea explícitamente un constructor, el compilador creará automáticamente un constructor sin argumentos (un constructor sin parámetros). Si escribimos un constructor sin parámetros, el compilador no creará automáticamente el constructor. Declare el constructor como privado , entonces nadie podrá crear objetos de esta clase. Pero ahora, ¿cómo pueden otros usar esta clase? El ejemplo anterior ofrece dos opciones. En Soup1 , existe un método estático , su función es crear un nuevo objeto Soup1 y devolver una referencia al objeto. Esto es útil si desea realizar algunas operaciones adicionales en Soup1 antes de devolver la referencia o registrar cuántos objetos Soup1 se crearon (lo que se puede usar para limitar el número).

Soup2 utiliza el llamado patrón de diseño. Este patrón se llama singleton porque permite crear solo un objeto de la clase. El objeto de la clase Soup2 se crea como un miembro privado estático de Soup2 , por lo que solo hay uno y solo puede acceder a este objeto a través del método público modificado . access()

おすすめ

転載: blog.csdn.net/GXL_1012/article/details/132153130