Breaking

Post Top Ad

Your Ad Spot

viernes, 14 de junio de 2019

Modificadores sin acceso en Java

Introducción

Los modificadores son palabras clave que nos permiten ajustar el acceso a nuestra clase y sus miembros, su alcance y comportamiento en ciertas situaciones. Por ejemplo, podemos controlar qué clases / objetos pueden tener acceso a ciertos miembros de nuestra clase, si una clase puede ser hereditaria o no, si podemos reemplazar un método más tarde, ya sea que deberíamos reemplazar un método más tarde, etc.
Las palabras clave modificadoras se escriben antes del tipo y nombre de variable / método / clase (retorno), por ejemplo, private int myVarpublic String toString().
Los modificadores en Java se dividen en uno de dos grupos: acceso y no acceso :
nativeno se trata con más detalle a continuación, ya que es una palabra clave simple que marca un método que se implementará en otros idiomas, no en Java. Funciona junto con la interfaz nativa de Java (JNI). Se usa cuando queremos escribir secciones críticas de código de rendimiento en idiomas más amigables con el rendimiento (como C).
¿Desea obtener más información sobre los modificadores de acceso , en lugar de no acceder? Si es así, echa un vistazo a nuestro artículo Modificadores de acceso en Java .

Modificadores sin acceso

Estos tipos de modificadores se utilizan para controlar una variedad de cosas, como las capacidades de herencia, ya sea que todos los objetos de nuestra clase compartan el mismo valor de miembro o tengan sus propios valores de esos miembros, si un método se puede anular en una subclase, etc.
Una breve descripción de estos modificadores se puede encontrar en la siguiente tabla:
Nombre del modificadorVisión general
estáticoEl miembro pertenece a la clase, no a los objetos de esa clase.
finalLos valores de las variables no se pueden cambiar una vez asignados, los métodos no se pueden sobrescribir, las clases no se pueden heredar.
resumenSi se aplica a un método, debe implementarse en una subclase, si se aplica a una clase, contiene métodos abstractos
sincronizadoControla el acceso de hilos a un bloque / método.
volátilEl valor de la variable siempre se lee desde la memoria principal, no desde la memoria de un hilo específico.
transitorioEl miembro se omite al serializar un objeto.

El modificador estático

El staticmodificador hace que un miembro de clase sea independiente de cualquier objeto de esa clase. Hay algunas características a tener en cuenta aquí:
  • Las variables declaradas staticse comparten entre todos los objetos de una clase (ya que la variable pertenece esencialmente a la clase en este caso), es decir, los objetos no tienen sus propios valores para esa variable, en su lugar, todos comparten uno solo.
  • staticSe puede acceder a las variables y los métodos declarados a través del nombre de la clase (en lugar de la referencia de objeto habitual, por ejemplo, MyClass.staticMethod()MyClass.staticVariable), y se puede acceder a ellos sin que se cree una instancia de la clase .
  • staticlos métodos solo pueden usar staticvariables y llamar a otros staticmétodos, y no pueden referirse thissuperde ninguna manera (es posible que una instancia de objeto ni siquiera exista cuando llamamos un staticmétodo, por thislo que no tendría sentido).
Nota : es muy importante tener en cuenta que las staticvariables y los métodos no pueden acceder a las variables y métodos que no son static(de la instancia). Por otro lado, las no staticvariables y los métodos pueden acceder a las staticvariables y los métodos.
Esto es lógico, ya que los staticmiembros existen incluso sin un objeto de esa clase, mientras que los miembros de la instancia existen solo después de que una clase haya sido instanciada.

Variables estáticas

Para las variables, usamos staticsi queremos que la variable sea común / compartida para todos los objetos.
Echemos un vistazo a cómo las staticvariables se comportan de manera diferente de las variables de instancia regulares:
class StaticExample {  
    public static int staticInt = 0;
    public int normalInt = 0;

    // We'll use this example to show how we can keep track of how many objects
    // of our class were created, by changing the shared staticInt variable
    public StaticExample() {
        staticInt++;
        normalInt++;
    }
}
// No instances of StaticExample have been created yet
System.out.println(StaticExample.staticInt); // Prints: 0  
// System.out.println(StaticExample.normalInt); // this won't work, obviously

// Let's create two instances of StaticExample
StaticExample object1 = new StaticExample();  
// We can refer to static variables via an object reference as well, 
// however this is not common practice, we usually access them via class name
// to make it obvious that a variable/method is static
System.out.println(object1.staticInt); // Prints: 1  
System.out.println(object1.normalInt); // Prints: 1

StaticExample object2 = new StaticExample();  
System.out.println(object2.staticInt); // Prints: 2  
System.out.println(object2.normalInt); // Prints: 1

// We can see that increasing object2's staticInt 
// increases it for object1 (and all current or future objects of that class)

object1.staticInt = 10;  
object1.normalInt = 10;  
System.out.println(object2.staticInt); // Prints: 10  
System.out.println(object2.normalInt); // Prints: 1 (object2 retained its own value for normalInt as it depends on the class itself)  

Métodos estáticos

El ejemplo más común de uso statices el main()método, se declara staticporque se debe llamar antes de que exista cualquier objeto. Otro ejemplo común es la Mathclase, ya que usamos los métodos de esa clase sin hacer una instancia de ella primero (como Math.abs()).
Una buena manera de pensar acerca de los staticmétodos es "¿Tiene sentido usar este método sin crear primero un objeto de esta clase?" (Por ejemplo, no es necesario crear una instancia de la Mathclase para calcular el valor absoluto de un número).
Los métodos estáticos se pueden utilizar para acceder y modificar los staticmiembros de una clase. Sin embargo, se utilizan comúnmente para manipular los parámetros del método o para calcular algo y devolver un valor.
Estos métodos se conocen como métodos de utilidad :
static int average(int num1, int num2) {  
    return (num1+num2)/2;
}
Este método de utilidad se puede usar para calcular el promedio de dos números, por ejemplo.
Como se mencionó anteriormente, la Mathclase se utiliza a menudo para los staticmétodos de llamada Si miramos el código fuente, podemos notar que en su mayoría ofrece métodos de utilidad:
public static int abs(int i) {  
    return (i < 0) ? -i : i;
}

public static int min(int a, int b) {  
    return (a < b) ? a : b;
}

public static int max(int a, int b) {  
    return (a > b) ? a : b;
}

Bloques estáticos

También hay un staticbloque. Un staticbloque se ejecuta solo una vez cuando se crea una instancia de la clase por primera vez (o staticse ha llamado a un miembro, incluso si la clase no está instanciada), y antes del resto del código.
Agreguemos un staticbloque a nuestra StaticExampleclase:
class StaticExample() {  
    ...
    static {
        System.out.println("Static block");
    }
    ...
}
StaticExample object1 = new StaticExample(); // "Static block" is printed  
StaticExample object2 = new StaticExample(); // Nothing is printed  
Independientemente de su posición en la clase, los staticbloques se inicializan antes que cualquier otro bloque no estático, incluidos los constructores:
class StaticExample() {  
    public StaticExample() {
        System.out.println("Hello from the constructor!");
    }

    static {
        System.out.println("Hello from a static block!");
    }
}
La creación de esta clase generaría:
Hello from a static block!  
Hello from the constructor!  
Si hay varios staticbloques presentes, se ejecutarán en su orden respectivo:
public class StaticExample {

    static {
        System.out.println("Hello from the static block! 1");
    }

    public StaticExample() {
        System.out.println("Hello from the constructor!");
    }

    static {
        System.out.println("Hello from the static block! 2");
    }
}
La creación de esta clase generaría:
Hello from the static block! 1  
Hello from the static block! 2  
Hello from the constructor!  

Importaciones estáticas

Como ya se mencionó, es mejor llamar a los staticmiembros prefijados con el nombre de la clase, en lugar del nombre de la instancia. Además, en algunos casos, nunca instanciamos realmente una clase con staticmétodos, como la Mathclase, que ofrece numerosos métodos de utilidad con respecto a las matemáticas.
Dicho esto, si utilizamos los staticmiembros de una clase a menudo, podemos importar miembros individuales o todos ellos utilizando a static importEsto nos permite omitir el prefijo de sus llamadas con el nombre de la clase:
package packageOne;

public class ClassOne {  
    static public int i;
    static public int j;

    static public void hello() {
        System.out.println("Hello World!");
    }
}
package packageTwo;

static import packageOne.ClassOne.i;

public class ClassTwo {  
    public ClassTwo() {
        i = 20;
    }
}
O, si nos gustaría importar todos los staticmiembros de ClassOne, podríamos hacerlo así:
package packageTwo;

import static packageOne.ClassOne.*;

public class ClassTwo {  
    public ClassTwo() {
        i = 20;
        j = 10;
    }
}
Lo mismo se aplica a los métodos:
package packageTwo;

import static packageOne.ClassOne.*;

public class ClassTwo {  
    public ClassTwo() {
        hello();
    }
}
Ejecutar esto daría como resultado:
Hello World!  
Esto puede no parecer tan importante, pero ayuda cuando llamamos a muchos staticmiembros de una clase:
public int someFormula(int num1, int num2, int num3) {  
    return Math.ceil(Math.max(Math.abs(num1), Math.abs(num2))+Math.max(Math.abs(num2), Math.abs(num3)))/(Math.min(Math.abs(num1), Math.abs(num2))+Math.min(Math.abs(num2), Math.abs(num3)));
}

// Versus...
import static java.lang.Math.*;  
public int someFormula(int num1, int num2, int num3) {  
    return ceil(max(abs(num1), abs(num2))+max(abs(num2), abs(num3)))/(min(abs(num1), abs(num2))+min(abs(num2), abs(num3)));
}

El modificador final

La palabra clave finalpuede tener uno de tres significados:
  • para definir constantes nombradas (variables cuyos valores no pueden cambiar después de la inicialización)
  • para evitar que un método sea anulado
  • para evitar que una clase sea heredada

Constantes nombradas

Agregar el finalmodificador a una declaración de variable hace que esa variable no se pueda cambiar una vez que se haya inicializado.
El finalmodificador a menudo se usa junto con el staticmodificador si estamos definiendo constantes. Si solo aplicamos statica una variable, todavía se puede cambiar fácilmente. También hay una convención de nombres vinculada a esto:
static final double GRAVITATIONAL_ACCELERATION = 9.81;  
Variables como estas a menudo se incluyen en clases de utilidad, como la Mathclase, acompañadas de numerosos métodos de utilidad.
Aunque, en algunos casos, también garantizan sus propias clases, tales como Constants.java:
public static final float LEARNING_RATE = 0.3f;  
public static final float MOMENTUM = 0.6f;  
public static final int ITERATIONS = 10000;  
Nota : cuando se utiliza finalcon variables de referencia de objetos, tenga cuidado con el tipo de comportamiento que espera. Considera lo siguiente:
class MyClass {  
    int a;
    int b;

    public MyClass() {
        a = 2;
        b = 3;
    }
}
    final MyClass object1 = new MyClass();
    MyClass object2 = new MyClass();
La variable de referencia object1es de hecho finaly su valor no puede cambiar, pero ¿qué significa eso para las variables de referencia de todos modos? Esto significa que object1 no se puede cambiar cuál es el objeto que está apuntando a más, pero nosotros podemos cambiar el objeto en sí. Esto es algo que a menudo confunde a la gente:
    // object1 = object2; // Illegal!
    object1.a = 5; // Perfectly fine
Los parámetros del método también pueden ser declarados finalEsto se utiliza para asegurarse de que nuestro método no cambie el parámetro que recibe cuando se llama.
Las variables locales también pueden ser declaradas finalEsto se utiliza para asegurarse de que la variable recibe un valor solo una vez.

Prevención de anular

Si especifica el finalmodificador al definir un método, cualquier subclase futura no puede anularlo.
class FinalExample {  
    final void printSomething() {
        System.out.println("Something");
    }
}
class ExtendsFinalExample extends FinalExample {  
    // This would cause a compile-time error
    //void printSomething() {
    //  System.out.println("Some other thing");
    //}

    // However, you are perfectly free to overload this method
    void printSomething(String something) {
        System.out.println(something);
    }
}
Una pequeña ventaja de declarar métodos verdaderamente finales finales un ligero aumento de rendimiento cuando llamamos a este método. Por lo general, Java resuelve las llamadas de métodos dinámicamente en tiempo de ejecución, pero con los métodos declarados final, Java puede resolver una llamada en el momento de la compilación, o si un método es realmente pequeño, simplemente puede incluir llamadas a ese método ya que "sabe" que no será anulado Esto elimina la sobrecarga asociada con una llamada de método.

La prevención de la herencia

Este uso de finales bastante sencillo, una clase definida con finalno se puede heredar. Esto, por supuesto, también declara implícitamente todos los métodos de esa clase final (no se pueden anular si la clase no se puede heredar en primer lugar).
final class FinalExample {...}  

El modificador abstracto

El abstractmodificador se utiliza para definir métodos que se implementarán en una subclase más adelante. La mayoría de las veces se usa para sugerir que alguna funcionalidad se debe implementar en una subclase o, por alguna razón, no se puede implementar en la superclase. Si una clase contiene un abstractmétodo, también debe ser declarado abstract.
Nota : No puede crear un objeto de una abstractclase. Para hacer eso, necesita proporcionar una implementación para todos los abstractmétodos.
Un ejemplo sería si tuviéramos una clase simple llamada Employeeque encapsula datos y métodos para un empleado. Digamos que no a todos los empleados se les paga de la misma manera, algunos tipos de empleados se pagan por hora y otros se les paga un salario fijo.
abstract class Employee {  
    int totalHours; // In a month
    int perHour;    // Payment per hour
    int fixedRate;  // Fixed monthly rate
    ...
    abstract int salary();
    ...  
}
class Contractor extends Employee {  
    ...
    // Must override salary if we wish to create an object of this class
    int salary() {
        return totalHours*perHour; 
    }
    ...
}
class FullTimeEmployee extends Employee {  
    ...
    int salary() {
        return fixedRate; 
    }
    ...
}
class Intern extends Employee {  
    ...
    int salary() {
        return 0; 
    }
    ...
}
Si una subclase no proporciona una implementación a todos los abstractmétodos de la superclase, también debe declararse abstracty no se puede crear un objeto de esa clase.
Nota : abstractse utiliza en gran medida con el polimorfismo, por ejemplo, decíamos ArrayList<Employee> employees = new ArrayList();, y añadimos ContractorFullTimeEmployeeInternse opone a ella. Aunque no podemos crear un objeto de la Employeeclase, podemos usarlo como un tipo de variable de referencia.

El modificador sincronizado

Cuando dos o más subprocesos necesitan usar el mismo recurso, de alguna manera necesitamos asegurarnos de que solo uno de ellos tenga acceso a él a la vez, es decir, necesitamos sincronizarlos .
Esto se puede lograr de varias maneras, y una forma simple y legible (aunque con un uso algo limitado) es usando la synchronizedpalabra clave.
Un concepto importante a comprender antes de ver cómo usar esta palabra clave es el concepto de monitor. Cada objeto en Java tiene su propio monitor implícito asociado. Un monitor es un bloqueo "mutuamente exclusivo", lo que significa que solo un subproceso puede "ser dueño" de un monitor a la vez. Cuando un hilo entra en el monitor, ningún otro hilo puede ingresar hasta que sale el primer hilo. Esto es lo que synchronizedhace.
Los hilos están más allá del alcance de este artículo, por lo que me centraré solo en la sintaxis synchronized.
Podemos sincronizar el acceso a los métodos, y los bloques de código. La sincronización de bloques de código funciona al proporcionar una instancia de objeto con la que queremos sincronizar el acceso y el código que queremos realizar relacionado con ese objeto.
class SynchronizedExample {

    ...
    SomeClass object = new SomeClass();
    ....

    synchronized(object) {
         // Code that processes objects
         // only one thread at a time
    }

    // A synchronized method
    synchronized void doSomething() {
         ...
    }
    ...
}

El modificador volátil

El volatilemodificador le dice a Java que una variable puede ser cambiada inesperadamente por alguna otra parte del programa (como en la programación multiproceso), por lo que el valor de esa variable siempre se lee desde la memoria principal (y no desde la memoria caché de la CPU), y que cada cambio a la volatileLa variable se almacena en la memoria principal (y no en la memoria caché de la CPU). Teniendo esto en cuenta, volatilesolo debe usarse cuando sea necesario, ya que leer / escribir en la memoria cada vez es más costoso que hacerlo con el caché de la CPU y solo leer / escribir en la memoria cuando sea necesario.
En términos simplificados: cuando un hilo lee un volatilevalor variable, se garantiza que leerá el valor escrito más recientemente. Básicamente, una volatilevariable hace lo mismo que los synchronizedmétodos / bloques, simplemente no podemos declarar una variable como synchronized.

El modificador transitorio

Cuando se declara una variable como transient, eso significa que su valor no se guarda cuando el objeto se almacena en la memoria. 
transient int a;significa que cuando escribimos el objeto en la memoria, el contenido de "a" no se incluirá. Por ejemplo, se utiliza para asegurarse de que no almacenemos información privada / confidencial en un archivo.
Cuando intentamos leer un objeto que contiene transientvariables, todos los transientvalores de las variables se establecerán en null(o valores predeterminados para los tipos primitivos), sin importar qué eran cuando escribimos el objeto en el archivo. Otro ejemplo de uso sería cuando el valor de una variable debería derivarse en función de otros datos (como la edad actual de alguien) y no forma parte del estado de objeto persistente.
Nota : Algo muy interesante sucede cuando lo usamos transientfinaljuntos. Si tenemos una transient finalvariable que se evalúa como una expresión constante (cadenas o tipos primitivos), la JVM siempre la serializará, ignorando cualquier posible transientmodificador. Cuando transient finalse usa con variables de referencia, obtenemos el comportamiento predeterminado esperado de transient.

Conclusión

Los modificadores son palabras clave que nos permiten ajustar el acceso a nuestra clase y sus miembros, su alcance y comportamiento en ciertas situaciones. Proporcionan rasgos fundamentales para nuestras clases y sus miembros. Todos los desarrolladores deben conocerlos a fondo para hacer el mejor uso de ellos.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

Post Top Ad

Your Ad Spot

Páginas