Modificadores de no acceso en Java

Los modificadores son palabras clave específicas presentes en Java con las que podemos realizar cambios en las características de una variable, método o clase y limitar su alcance. El lenguaje de programación Java tiene un rico conjunto de modificadores.

Los modificadores en Java se dividen en dos tipos: modificadores de acceso y modificadores de no acceso

Los modificadores de acceso en Java ayudan a restringir el alcance de una variable, método, clase o constructor. Público , Privado , Protegido y Predeterminado , estos cuatro modificadores de acceso están presentes en Java.

Modificadores de no acceso 

Los modificadores de no acceso proporcionan información sobre las características de una clase, método o variable a la JVM. Hay siete tipos de modificadores de no acceso presentes en Java. Están –

  1. estático
  2. final
  3. resumen
  4. sincronizado
  5. volátil
  6. transitorio
  7. nativo

1. estático

La palabra clave estática significa que la entidad a la que se aplica está disponible fuera de cualquier instancia particular de la clase. Eso significa que los métodos estáticos o los atributos son parte de la clase y no un objeto. La memoria se asigna a dicho atributo o método en el momento de la carga de la clase. El uso de un modificador estático hace que el programa sea más eficiente al ahorrar memoria. Existe un campo estático en todas las instancias de la clase y, sin crear un objeto de la clase, se pueden llamar.

Ejemplo 1:

Java

import java.io.*;
  
// static variable
class static_gfg {
    static String s = "GeeksforGeeks"; 
}
class GFG {
    public static void main(String[] args)
    {
        // No object required
        System.out.println(
            static_gfg.s); 
    }
}
Producción

GeeksforGeeks

En este ejemplo de código anterior, hemos declarado String como estático, parte de la clase static_gfg . Generalmente, para acceder a la string, primero necesitamos crear el objeto de la clase static_gfg , pero como lo hemos declarado como estático, no necesitamos crear un objeto de la clase static_gfg para acceder a la string. Podemos usar className.variableName para acceder a él.

Ejemplo 2:

Java

import java.io.*;
  
class static_gfg {
  
    // static variable
    static int count = 0; 
    void myMethod()
    {
        count++;
        System.out.println(count);
    }
}
class GFG {
    public static void main(String[] args)
    {
        // first object creation
        static_gfg obj1 = new static_gfg(); 
            
        // method calling of first object
        obj1.myMethod(); 
            
        // second object creation
        static_gfg obj2
            = new static_gfg(); 
        
        // method calling of second object
        obj2.myMethod(); 
    }
}
Producción

1
2

En el código anterior, la variable de recuento es estática, por lo que no está vinculada a una instancia específica de la clase. Entonces, mientras se llama obj1.myMethod() , aumenta el valor de count en 1 y luego obj2.myMethod() nuevamente lo aumenta. Si no era estático, obtendremos la salida como 1 en ambos casos, pero como es una variable estática, la variable de conteo se incrementará dos veces, y obtendremos 2 como salida la segunda vez.

2. definitivo

La palabra clave final indica que la clase específica no se puede ampliar o que no se puede anular un método. Entendamos eso con un ejemplo –

Ejemplo 1:

Java

import java.io.*;
  
class final_gfg { 
    String s1 = "geek1";
}
class extended_gfg extends final_gfg { 
                          
    String s2 = "geek2";
}
class GFG {
    public static void main(String[] args)
    {
      // creating object
      extended_gfg obj = new extended_gfg(); 
        
      System.out.println(obj.s1);
      System.out.println(obj.s2);
    }
}
Producción

geek1
geek2

En este código anterior, la clase final_gfg se amplía con la clase extended_gfg , y el código funciona bien y genera resultados.

Pero después de usar la palabra clave final con la clase final_gfg . El código producirá un error. A continuación se muestra la implementación de la misma – 

Java

import java.io.*;
  
// This class is final
final class final_gfg { 
    String s1 = "geek1";
}
// We are trying to inherit a final
class extended_gfg extends final_gfg { 
                          
    String s2 = "geek2";
}
class GFG {
    public static void main(String[] args)
    {
        // creating object
        extended_gfg obj
            = new extended_gfg();
        
          System.out.println(obj.s1);
          System.out.println(obj.s2);
    }
}

Error :

Captura de pantalla del error

Aquí estamos obteniendo errores en la compilación ya que estamos tratando de extender la clase final_gfg , que se declara como final. Si una clase se declara como final, no podemos extenderla ni heredar de esa clase.

Ejemplo 2:

Java

import java.io.*;
  
class final_gfg{
     void myMethod(){
        System.out.println("GeeksforGeeks");
    }
}
class override_final_gfg extends final_gfg{
    void myMethod(){ 
        System.out.println("Overrides GeeksforGeeks");
    }
}
  
class GFG{
    public static void main(String[] args) {
        override_final_gfg obj=new override_final_gfg();
        obj.myMethod();
    }
}
Producción

Overrides GeeksforGeeks

En el código anterior, estamos anulando myMethod() y el código funciona bien. 

Ahora vamos a declarar myMethod() en la superclase como final. A continuación se muestra la implementación de la misma –

Java

import java.io.*;
  
class final_gfg{
    final void myMethod(){
        System.out.println("GeeksforGeeks");
    }
}
class override_final_gfg extends final_gfg{
    // trying to override the method available on final_gfg class
    void myMethod(){ 
        System.out.println("Overrides GeeksforGeeks");
    }
}
class GFG{
    public static void main(String[] args) {
        override_final_gfg obj=new override_final_gfg();
        obj.myMethod();
    }
}

Error:

Captura de pantalla del error

El código anterior produce un error porque aquí estamos tratando de anular un método que se declara como final. myMethod() en la clase final_gfg se declara como final, y estamos tratando de anular eso de la clase override_final_gfg . Un método final no se puede anular; por lo tanto, el fragmento de código produce un error aquí.

3. resumen

La palabra clave abstracta se usa para declarar una clase como implementada parcialmente, lo que significa que no se puede crear un objeto directamente a partir de esa clase. Cualquier subclase debe implementar todos los métodos de la clase abstracta o también debe ser una clase abstracta. La palabra clave abstracta no se puede usar con palabras clave estáticas, finales o privadas porque evitan la anulación, y necesitamos anular los métodos en el caso de una clase abstracta.

Java

// abstract class
abstract class abstract_gfg{ 
        abstract void myMethod();
}
  
//extending abstract class
class MyClass extends abstract_gfg{ 
    
    // overriding abstract method otherwise
    // code will produce error
    void myMethod(){ 
        System.out.println("GeeksforGeeks");
    }
}
class GFG{
    public static void main(String[] args) {
        MyClass obj=new MyClass();
        obj.myMethod(); 
    }
}
Producción

GeeksforGeeks

En el código anterior, abstract_gfg es una clase abstracta y myMethod() es un método abstracto. Entonces, primero necesitamos extender la clase abstract_gfg que hemos hecho aquí usando MyClass . Después de extender, también debemos anular el método abstracto; de lo contrario, el código producirá errores.

4. sincronizado

La palabra clave sincronizada evita que un bloque de código se ejecute en varios subprocesos a la vez. Es muy importante para algunas operaciones críticas. Entendamos con un ejemplo:

Java

import java.io.*;
  
class Counter{
    int count;
    void increment(){
        count++;
    }
}
class GFG{
    public static void main(String[] args) throws InterruptedException {
        Counter c=new Counter();
        
        // Thread 1
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=10000;i++){
                    c.increment();
                }
            }
        });
        
        // Thread 2
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=10000;i++){
                    c.increment();
                }
            }
        });
  
        t1.start();
        t2.start();
        t1.join();
        t2.join();
  
        System.out.println(c.count);
    }
}

Producción

El código anterior debe tener un valor de salida de 20000 ya que dos subprocesos lo incrementan 10000 veces cada uno, y el principal está esperando que finalice Thread1, Thread2 . A veces puede que no sea cierto. Según el sistema, es posible que no proporcione 20000 como salida. Como ambos subprocesos acceden al valor de count , puede suceder que Thread1 obtenga el valor de count y, antes de que pueda incrementarlo, Thread2 lea el valor y lo incremente. Entonces, el resultado puede ser menor que 20000. Para resolver este problema, usamos la palabra clave sincronizada. Si se usa la palabra clave sincronizada al declarar el incremento()entonces un subproceso debe esperar a que otro subproceso complete la operación del método, luego solo otro puede trabajar en él. Entonces podemos obtener una salida garantizada de 20000. A continuación se muestra el código sincronizado:

Java

import java.io.*;
  
class Counter{
    int count;
    synchronized void increment(){
        count++;
    }
}
class GFG{
    public static void main(String[] args) throws InterruptedException {
        Counter c=new Counter();
        
        // Thread 1
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=100000;i++){
                    c.increment();
                }
            }
        });
        
        // Thread 2
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1;i<=100000;i++){
                    c.increment();
                }
            }
        });
  
        t1.start();
        t2.start();
        t1.join();
        t2.join();
  
        System.out.println(c.count);
    }
}
Producción

200000

5. volátil

La palabra clave volatile se usa para hacer que la clase sea segura para subprocesos. Eso significa que si una variable se declara como volátil, varios subprocesos pueden modificarla al mismo tiempo sin ningún problema. La palabra clave volátil solo se aplica a una variable. Una palabra clave volátil reduce la posibilidad de inconsistencia en la memoria. El valor de una variable volátil siempre se lee desde la memoria principal y no desde la memoria caché de subprocesos local, y ayuda a mejorar el rendimiento de los subprocesos. Entendamos con un ejemplo:

Java

import java.io.*;
import java.util.*;
  
class Geeks extends Thread{
    boolean running=true;
    @Override
    public void run(){
        while(running){
            System.out.println("GeeksforGeeks");
        }
    }
    public void shutDown(){
        running=false;
    }
}
class GFG{
    public static void main(String[] args) {
        Geeks obj = new Geeks();
        obj.start();
        Scanner input = new Scanner(System.in);
        input.nextLine();
        obj.shutDown();
    }
}

Producción

En el código anterior, el programa idealmente debería detenerse si se presiona la tecla Retorno/Intro, pero en algunas máquinas, puede suceder que la variable en ejecución se almacene en caché y no podamos cambiar su valor usando el método shutDown() . En tal caso, el programa se ejecutará infinitas veces y no podrá salir correctamente. Para evitar el almacenamiento en caché y hacerlo seguro para subprocesos, podemos usar palabras clave volátiles al declarar la variable en ejecución .

Java

import java.io.*;
import java.util.*;
  
class Geeks extends Thread{
    volatile boolean running=true;
    @Override
    public void run(){
        while(running){
            System.out.println("GeeksforGeeks");
        }
    }
    public void shutDown(){
        running=false;
    }
}
  
class GFG{
    public static void main(String[] args) {
        Geeks obj = new Geeks();
        obj.start();
        Scanner input = new Scanner(System.in);
        input.nextLine();
        obj.shutDown();
    }
}

Producción

En el código anterior, después de usar la palabra clave volatile, podemos detener el bucle infinito usando la tecla Retorno y el programa salió correctamente con el código de salida 0.

6. transitorio

Esto necesita conocimientos previos de serialización en Java. Puede consultar el siguiente artículo para eso: – serialización en java .

La palabra clave transitoria se puede aplicar a las variables miembro de una clase para indicar que la variable miembro no debe serializarse cuando se serializa la instancia de la clase contenedora. La serialización es el proceso de convertir un objeto en un flujo de bytes. Cuando no queremos serializar el valor de una variable, la declaramos transitoria. Para hacerlo más transparente, tomemos un ejemplo de una aplicación en la que necesitamos aceptar ID de usuario y contraseña. En ese momento, necesitamos declarar alguna variable para tomar la entrada y almacenar los datos, pero como los datos son susceptibles, no queremos mantenerlos almacenados después de que el trabajo haya terminado. Para lograr esto, podemos usar la palabra clave transient para la declaración de variables. Esa variable en particular no participará en el proceso de serialización, y cuando la deserialicemos, recibiremos el valor por defecto de la variable. Veamos un código de muestra para el mismo:

Java

import java.io.*;
  
class transient_gfg implements Serializable {
    // normal variable
    int a = 10; 
      
    // Transient variables
    transient String UserID="admin"; 
    transient String Password="tiger123";
  
}
class GFG{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        transient_gfg obj=new transient_gfg();
        
        // printing the value of transient
        // variable before serialization process
        System.out.println("UserID :"+obj.UserID);
        System.out.println("Password: "+obj.Password);
        System.out.println("a = "+obj.a);
  
        // serialization
        FileOutputStream fos = new FileOutputStream("abc.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
        
        // de-serialization
        FileInputStream fis = new FileInputStream("abc.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        transient_gfg output = (transient_gfg)ois.readObject();
        
        // printing the value of transient 
        // variable after de-serialization process
        System.out.println("UserID :"+output.UserID);
        System.out.println("Password: "+output.Password);
        System.out.println("a = "+obj.a);
  
    }
}

Producción

Como puede ver en el resultado, después de la serialización, los valores de ID de usuario y contraseña ya no están presentes. Sin embargo, el valor de ‘ a’ , que es una variable normal, todavía está presente.

7. nativo

La palabra clave native se puede aplicar a un método para indicar que el método se implementa en un lenguaje distinto de Java. El uso de esta aplicación Java puede llamar a código escrito en C, C++ o lenguaje ensamblador. En este caso, se requiere una biblioteca de código compartido o DLL. Veamos un ejemplo primero –

Java

import java.io.*;
  
class GFG
{
    // native method
    public native void printMethod (); 
    static
    {
        // The name of DLL file
        System.loadLibrary ("LibraryName");  
    }
    public static void main (String[] args)
    {
        GFG obj = new GFG ();
        obj.printMethod ();
    }
}

Producción:

En el código anterior, tenemos un método nativo. El método se define en cualquier otro idioma, cargado por una aplicación Java utilizando el archivo DLL compartido. La implementación del archivo DLL está fuera del alcance de este artículo, por lo que si desea obtener más información al respecto, puede consultar este artículo: Programación en varios idiomas: clase de proceso Java, JNI e IO .

Publicación traducida automáticamente

Artículo escrito por satyajit1910 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *