📜  Java中的非访问修饰符

📅  最后修改于: 2022-05-13 01:54:32.954000             🧑  作者: Mango

Java中的非访问修饰符

修饰符是Java中存在的特定关键字,我们可以使用它来更改变量、方法或类的特征并限制其范围。 Java编程语言有一组丰富的修饰符。

Java中的修饰符分为两种类型—— 访问修饰符非访问修饰符

Java中的访问修饰符有助于限制变量、方法、类或构造函数的范围。 PublicPrivateProtectedDefault这四个访问修饰符在Java中存在。

非访问修饰符

非访问修饰符向 JVM 提供有关类、方法或变量的特征的信息。 Java中有七种类型的非访问修饰符。他们是 -

  1. 静止的
  2. 最终的
  3. 抽象的
  4. 同步的
  5. 易挥发的
  6. 短暂的
  7. 本国的

1.静态

static 关键字意味着应用它的实体在类的任何特定实例之外都可用。这意味着静态方法或属性是类的一部分,而不是对象。内存在类加载时分配给这样的属性或方法。静态修饰符的使用通过节省内存使程序更高效。静态字段存在于所有类实例中,无需创建类的对象,就可以调用它们。

示例 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); 
    }
}


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(); 
    }
}


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);
    }
}


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);
    }
}


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();
    }
}


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();
    }
}


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(); 
    }
}


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);
    }
}


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);
    }
}


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();
    }
}


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();
    }
}


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);
  
    }
}


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 ();
    }
}


输出
GeeksforGeeks

在上面的代码示例中,我们将 String 声明为 static,是static_gfg类的一部分。通常,要访问字符串,我们首先需要创建static_gfg类的对象,但由于我们已将其声明为静态,因此无需创建static_gfg类的对象即可访问字符串。我们可以使用className.variableName来访问它。

示例 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(); 
    }
}
输出
1
2

在上面的代码中, count变量是静态的,因此它不绑定到类的特定实例。因此,当调用 obj1.myMethod()时,它会将 count 的值增加 1,然后obj2.myMethod()再次增加它。如果它不是静态的,那么在这两种情况下我们都会得到 1 的输出,但是因为它是一个静态变量,所以 count 变量会增加两次,第二次我们会得到 2 作为输出。

2.决赛

final关键字表示特定类不能扩展或方法不能被覆盖。让我们通过一个例子来理解——

示例 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);
    }
}
输出
geek1
geek2

在上面的代码中, final_gfg类由extended_gfg类扩展,代码工作正常并产生输出。

但是在将final关键字与final_gfg类一起使用之后。该代码将产生错误。以下是相同的实现 -

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);
    }
}

错误 :

错误截图

在这里,当我们试图扩展声明为finalfinal_gfg类时,我们在编译中遇到了错误。如果一个类被声明为 final,那么我们就不能扩展它或从该类继承。

示例 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();
    }
}
输出
Overrides GeeksforGeeks

在上面的代码中,我们重写了myMethod() ,并且代码工作正常。

现在我们要将超类中的myMethod()声明为 final。以下是相同的实现 -

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();
    }
}

错误:

错误截图

上面的代码产生了一个错误,因为在这里,我们试图覆盖一个声明为 final 的方法。 final_gfg类中的myMethod()被声明为 final,我们试图从override_final_gfg类中覆盖它。 final 方法不能被覆盖;因此,代码片段在这里产生错误。

3. 摘要

abstract 关键字用于将类声明为部分实现意味着不能直接从该类创建对象。任何子类都需要实现抽象类的所有方法,或者它也应该是抽象类。 abstract 关键字不能与 static、final 或 private 关键字一起使用,因为它们会阻止覆盖,并且我们需要在抽象类的情况下覆盖方法。

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(); 
    }
}
输出
GeeksforGeeks

在上面的代码中, abstract_gfg是一个抽象类, myMethod()是一个抽象方法。因此,我们首先需要使用MyClass扩展我们在这里完成的abstract_gfg类。扩展后,我们还需要重写抽象方法,否则代码会出错。

4.同步

synchronized 关键字防止一个代码块同时被多个线程执行。这对于一些关键操作非常重要。让我们通过一个例子来理解——

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);
    }
}

输出

上面的代码应该是20000的输出值,因为两个线程将其递增 10000 次,并且 main 正在等待Thread1、Thread2完成。有时它可能不是真的。根据系统的不同,它可能不会给出 20000 作为输出。由于两个线程都在访问count的值,因此 Thread1 可能会获取 count 的值,并且在递增它之前,Thread2 会读取该值并递增该值。因此,结果可能小于 20000。为了解决这个问题,我们使用了 synchronized 关键字。如果在声明increment()方法时使用了 synchronized 关键字,那么一个线程需要等待另一个线程完成该方法的操作,然后只有另一个线程可以处理它。所以我们可以保证输出20000。下面是同步代码:

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);
    }
}
输出
200000

5.易挥发

volatile 关键字用于使类线程安全。这意味着如果一个变量被声明为 volatile,那么它可以被多个线程同时修改而不会出现任何问题。 volatile 关键字仅适用于变量。 volatile 关键字减少了内存不一致的机会。 volatile 变量的值总是从主内存中读取,而不是从本地线程缓存中读取,它有助于提高线程性能。让我们通过一个例子来理解:

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();
    }
}

输出

在上面的代码中,如果按下 Return Key/Enter,程序应该理想地停止,但是在某些机器中,可能会发生变量running被缓存,并且我们无法使用shutdown()方法更改其值。在这种情况下,程序将无限执行,无法正常退出。为了避免缓存并使其成为线程安全的,我们可以在声明运行变量时使用 volatile 关键字。

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();
    }
}

输出

在上面的代码中,使用 volatile 关键字后,我们可以使用 Return 键停止无限循环,程序正常退出,退出代码为 0。

6.瞬态

这需要在Java中序列化的先验知识。您可以参考以下文章:- Java中的序列化。

可以将transient关键字应用于类的成员变量,以指示在序列化包含类实例时不应序列化成员变量。序列化是将对象转换为字节流的过程。当我们不想序列化变量的值时,我们将其声明为瞬态。为了使其更加透明,让我们举一个需要接受 UserID 和 Password 的应用程序示例。那时,我们需要声明一些变量来获取输入并存储数据,但由于数据很容易受到影响,所以我们不想在工作完成后继续存储它。为此,我们可以使用瞬态关键字进行变量声明。该特定变量不会参与序列化过程,当我们反序列化它时,我们将收到该变量的默认值。让我们看一个相同的示例代码 -

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);
  
    }
}

输出

正如您从输出中看到的,在序列化之后,UserID 和 Password 的值不再存在。但是, ' a'的值, 这是一个正常的变量,仍然存在。

7. 本地人

native 关键字可以应用于方法以指示该方法是用Java以外的语言实现的。使用这个Java应用程序可以调用用 C、C++ 或汇编语言编写的代码。在这种情况下,需要共享代码库或 DLL。我们先看一个例子——

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 ();
    }
}

输出:

在上面的代码中,我们有一个本地方法。该方法以任何其他语言定义,由使用共享 DLL 文件的Java应用程序加载。 DLL文件的实现超出了本文的范围,如果想了解更多,可以参考这篇文章——多语言编程Java进程类、JNI和IO。