Java中的非访问修饰符
修饰符是Java中存在的特定关键字,我们可以使用它来更改变量、方法或类的特征并限制其范围。 Java编程语言有一组丰富的修饰符。
Java中的修饰符分为两种类型—— 访问修饰符和非访问修饰符。
Java中的访问修饰符有助于限制变量、方法、类或构造函数的范围。 Public 、 Private 、 Protected和Default这四个访问修饰符在Java中存在。
非访问修饰符
非访问修饰符向 JVM 提供有关类、方法或变量的特征的信息。 Java中有七种类型的非访问修饰符。他们是 -
- 静止的
- 最终的
- 抽象的
- 同步的
- 易挥发的
- 短暂的
- 本国的
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);
}
}
错误 :
在这里,当我们试图扩展声明为final的final_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。