Java类访问权限

技术Java类访问权限 Java类访问权限目录1 类访问权限1.1 四种访问权限解析1.2 Protected分析1.3 private失效情况1.3.1 Java内部类
1 类访问权限
1.1 四种访

Java类访问权限

类别1访问权限1.1四个访问权限的分析1.2受保护的分析1.3私有的失败1.3.1 Java内部类

1 类访问权限

1.1 四种访问权限解析

Java有四种访问权限,其中三种有访问权限修饰符,即私有、公共和受保护,一种没有任何修饰符。

四种访问权限:

私有: Java语言中访问权限的最窄修饰符,通常称为私有。修改后的类、属性和方法只能被这个类的对象访问,它的子类不能被访问,更不用说跨包访问了。

默认:即没有任何访问修饰符,通常称为默认访问模式。在这种模式下,只允许在同一个包中访问。

Protect:是介于公共和私有之间的访问修饰符,通常称为保护形式。修改后的类、属性和方法只能由类本身的方法和子类访问,即使子类在不同的包中。

Public:Java语言中访问限制最广的修饰符,一般称为public。修改后的类、属性和方法不仅可以跨类访问,还可以跨包访问。

下表展示了四种访问权限的异同,会更加生动。表格如下:

同一个班

同一个包

不同包的子类

不同包的非子类

私人的

默认

保护

公众

1.2 Protected分析

假设accesscontrol包下有两个类,AccessControlDemo和Base,其中access control保护双倍价格;是基类的成员变量,因为两个类在同一个包中,所以可以在AccessControlDemo类中直接访问system . out . println(Base . price);例子如下:

accesscontrol。访问控制演示

包访问控制;

公共类AccessControlDemo {

公共静态void main(String[]args){ 0

//TODO自动生成的方法存根

基地基地=新基地(' 123-1 ',120.1);

system . out . println(base . price);

}

}

accesscontrol。基础

包访问控制;

公共类基础{

私有字符串isbn

受保护的双重价格;

//默认构造函数

公共基础(){}

//构造函数。如果只定义了带参数的构造函数而没有定义默认构造函数,那么基类的子类必须定义显式构造函数。

公共库(字符串isbn,双倍价格){ 0

this.isbn=isbn

this.price=价格;

}

公共字符串GetSbN(){ 0

return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

但是假如我们将AccessControlDemo这个类移到test这个包中,我们会发现eclipse中提示错误,编译无法通过,因为在test包中对protected类型的成员变量不可见。
假如我们在test包中创建一个Base类的子类Bulk,也就是说BulkBase类不同包的子类。那么在Bulk类中能够直接访问protected double price;这个基层自Base类的成员变量,实例如下:
test.AccessControlDemo

package test;
public class AccessControlDemo {
    public static void main(String[] args) {
        Bulk bulk=new Bulk("123-1",120.1);
        bulk.print();
    }
}

test.Bulk

package test;
import accesscontrol.Base;
public class Bulk extends Base {
    
    public Bulk() {
        super();
    }
    public Bulk(String isbn, double price) {
        super(isbn, price);
    }
    public void print()
    {
        System.out.println(this.price);
    }
}

1.3 private失效情况

Java编程中,使用private关键字修饰了某个成员,只有这个成员所在的类和这个类的方法可以使用,其他的类都无法访问到这个private成员。

1.3.1 Java内部类

Java中相信很多人都用过内部类,Java允许在一个类里面定义另一个类,类里面的类就是内部类,也叫做嵌套类。一个简单的内部类实现可以如下

class OuterClass {
    class InnerClass{
    }
}

一个我们在编程中经常用到的场景,就是在一个内部类里面访问外部类的private成员变量或者方法,这是可以的。如下面的代码实现。

public class OuterClass {
  private String language = "en";
  private String region = "US";
 
  public class InnerClass {
      public void printOuterClassPrivateFields() {
          String fields = "language=" + language + ";region=" + region;
          System.out.println(fields);
      }
  }
 
  public static void main(String[] args) {
      OuterClass outer = new OuterClass();
      OuterClass.InnerClass inner = outer.new InnerClass();
      inner.printOuterClassPrivateFields();
  }
}

这是为什么呢,不是private修饰的成员只能被成员所述的类才能访问么难道private真的失效了么

使用javap命令查看一下生成的两个class文件
OuterClass的反编译结果

$ javap -c  OuterClass
Compiled from "OuterClass.java"
public class OuterClass extends java.lang.Object{
public OuterClass();
  Code:
   0:  aload_0
   1:  invokespecial    #11; //Method java/lang/Object."init":()V
   4:  aload_0
   5:  ldc  #13; //String en
   7:  putfield #15; //Field language:Ljava/lang/String;
   10: aload_0
   11: ldc  #17; //String US
   13: putfield #19; //Field region:Ljava/lang/String;
   16: return
 
public static void main(java.lang.String[]);
  Code:
   0:  new  #1; //class OuterClass
   3:  dup
   4:  invokespecial    #27; //Method "init":()V
   7:  astore_1
   8:  new  #28; //class OuterClass$InnerClass
   11: dup
   12: aload_1
   13: dup
   14: invokevirtual    #30; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   17: pop
   18: invokespecial    #34; //Method OuterClass$InnerClass."init":(LOuterClass;)V
   21: astore_2
   22: aload_2
   23: invokevirtual    #37; //Method OuterClass$InnerClass.printOuterClassPrivateFields:()V
   26: return
 
static java.lang.String access$0(OuterClass);
  Code:
   0:  aload_0
   1:  getfield #15; //Field language:Ljava/lang/String;
   4:  areturn
 
static java.lang.String access$1(OuterClass);
  Code:
   0:  aload_0
   1:  getfield #19; //Field region:Ljava/lang/String;
   4:  areturn
 
}

OuterClass中我们并没有定义这两个方法

static java.lang.String access$0(OuterClass);
  Code:
   0:  aload_0
   1:  getfield #15; //Field language:Ljava/lang/String;
   4:  areturn
 
static java.lang.String access$1(OuterClass);
  Code:
   0:  aload_0
   1:  getfield #19; //Field region:Ljava/lang/String;
   4:  areturn
 
}

从给出来的注释来看,access$0返回outerClasslanguage属性;access$1返回outerClassregion属性。并且这两个方法都接受OuterClass的实例作为参数。这两个方法为什么生成呢,有什么作用呢我们看一下内部类的反编译结果就知道了。
OuterClass$InnerClass的反编译结果

$ javap -c OuterClass\$InnerClass
Compiled from "OuterClass.java"
public class OuterClass$InnerClass extends java.lang.Object{
final OuterClass this$0;
 
public OuterClass$InnerClass(OuterClass);
  Code:
   0:  aload_0
   1:  aload_1
   2:  putfield #10; //Field this$0:LOuterClass;
   5:  aload_0
   6:  invokespecial    #12; //Method java/lang/Object."init":()V
   9:  return
 
public void printOuterClassPrivateFields();
  Code:
   0:  new  #20; //class java/lang/StringBuilder
   3:  dup
   4:  ldc  #22; //String language=
   6:  invokespecial    #24; //Method java/lang/StringBuilder."init":(Ljava/lang/String;)V
   9:  aload_0
   10: getfield #10; //Field this$0:LOuterClass;
   13: invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;
   16: invokevirtual    #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19: ldc  #37; //String ;region=
   21: invokevirtual    #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24: aload_0
   25: getfield #10; //Field this$0:LOuterClass;
   28: invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;
   31: invokevirtual    #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   34: invokevirtual    #42; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   37: astore_1
   38: getstatic    #46; //Field java/lang/System.out:Ljava/io/PrintStream;
   41: aload_1
   42: invokevirtual    #52; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45: return
}

下面代码调用access$0的代码,其目的是得到OuterClasslanguage 私有属性。

13:   invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;

下面代码调用了access$1的代码,其目的是得到OutherClassregion 私有属性。

28:   invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;

注意:在内部类构造的时候,会将外部类的引用传递进来,并且作为内部类的一个属性,所以内部类会持有一个其外部类的引用。
this$0就是内部类持有的外部类引用,通过构造方法传递引用并赋值。

final OuterClass this$0;
 
public OuterClass$InnerClass(OuterClass);
  Code:
   0:  aload_0
   1:  aload_1
   2:  putfield #10; //Field this$0:LOuterClass;
   5:  aload_0
   6:  invokespecial    #12; //Method java/lang/Object."init":()V
   9:  return

这部分private看上去失效,可实际上并没有失效,因为当内部类调用外部类的私有属性时,其真正的执行是调用了编译器生成的属性的静态方法(即acess$0,access$1等)来获取这些属性值。这一切都是编译器的特殊处理。

如果说上面的写法很常用,那么这样的写法是不是很少接触,但是却可以运行。

public class AnotherOuterClass {
  public static void main(String[] args) {
      InnerClass inner = new AnotherOuterClass().new InnerClass();
      System.out.println("InnerClass Filed = " + inner.x);
  } 
  class InnerClass {
      private int x = 10;
  }
 
}

和上面一样,使用javap反编译看一下。不过这次我们先看一下InnerClass的结果

16:03 $ javap -c AnotherOuterClass\$InnerClass
Compiled from "AnotherOuterClass.java"
class AnotherOuterClass$InnerClass extends java.lang.Object{
final AnotherOuterClass this$0;
 
AnotherOuterClass$InnerClass(AnotherOuterClass);
  Code:
   0:  aload_0
   1:  aload_1
   2:  putfield #12; //Field this$0:LAnotherOuterClass;
   5:  aload_0
   6:  invokespecial    #14; //Method java/lang/Object."init":()V
   9:  aload_0
   10: bipush   10
   12: putfield #17; //Field x:I
   15: return
 
static int access$0(AnotherOuterClass$InnerClass);
  Code:
   0:  aload_0
   1:  getfield #17; //Field x:I
   4:  ireturn
 
}

又出现了,编译器自动生成了一个获取私有属性的后门方法access$0一次来获取x的值。
AnotherOuterClass.class的反编译结果

16:08 $ javap -c AnotherOuterClass
Compiled from "AnotherOuterClass.java"
public class AnotherOuterClass extends java.lang.Object{
public AnotherOuterClass();
  Code:
   0:  aload_0
   1:  invokespecial    #8; //Method java/lang/Object."init":()V
   4:  return
 
public static void main(java.lang.String[]);
  Code:
   0:  new  #16; //class AnotherOuterClass$InnerClass
   3:  dup
   4:  new  #1; //class AnotherOuterClass
   7:  dup
   8:  invokespecial    #18; //Method "init":()V
   11: dup
   12: invokevirtual    #19; //Method java/lang/Object.getClass:()Ljava/lang/Class;
   15: pop
   16: invokespecial    #23; //Method AnotherOuterClass$InnerClass."init":(LAnotherOuterClass;)V
   19: astore_1
   20: getstatic    #26; //Field java/lang/System.out:Ljava/io/PrintStream;
   23: new  #32; //class java/lang/StringBuilder
   26: dup
   27: ldc  #34; //String InnerClass Filed =
   29: invokespecial    #36; //Method java/lang/StringBuilder."init":(Ljava/lang/String;)V
   32: aload_1
   33: invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I
   36: invokevirtual    #43; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   39: invokevirtual    #47; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   42: invokevirtual    #51; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45: return
 
}

其中这句调用就是外部类通过内部类的实例获取私有属性x的操作

33:   invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I

java官方文档 有这样一句话

if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

意思是 如果(内部类的)成员和构造方法设定成了私有修饰符,当且仅当其外部类访问时是允许的。

那么如何让内部类私有成员不被外部访问,那就是使用匿名内部类。
如下,由于mRunnable对象的类型为Runnable,而不是匿名内部类的类型(我们无法正常拿到),而Runanble中没有x这个属性,所以mRunnable.x是不被允许的。

public class PrivateToOuter {
  Runnable mRunnable = new Runnable(){
      private int x=10;
      @Override
      public void run() {
          System.out.println(x);
      }
  };
 
  public static void main(String[] args){
      PrivateToOuter p = new PrivateToOuter();
      //System.out.println("anonymous class private filed= "+ p.mRunnable.x); //not allowed
      p.mRunnable.run(); // allowed
  }
}

最后总结private表面上看上去失效了,但实际上是没有的,而是在调用时通过间接的方法来获取私有的属性。

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/74739.html

(0)

相关推荐

  • 六允读什么,允字上部是什么字,读音是什么

    技术六允读什么,允字上部是什么字,读音是什么“允”字的拼音是yǔn六允读什么。
    1、允的解释:一是答应,认可;二是公平得当;三是信,实;四是宽容,理解;五是包容;六是大、极致。
    2、组词、解释及造句
    (1)允许(yǔnx

    生活 2021年10月25日
  • linux基础之字符处理命令

    技术linux基础之字符处理命令 linux基础之字符处理命令目录文件管理之:字符处理命令1.1 排序命令 sort2.1 去重命令 uniq3.1 截取字段命令 cut4.1 替换或删除命令 tr5

    礼包 2021年12月22日
  • 火星距离地球多远,火星距离地球的距离是多少光年

    技术火星距离地球多远,火星距离地球的距离是多少光年火星与地球的最近距离是5565万千米,即约0.00000588光年;最远约4亿千米,即约0.000042光年.火星距地球的距离火星距离地球多远,近距离约为5500万千米,

    生活 2021年10月27日
  • 怎么浅谈数据库优化方案

    技术怎么浅谈数据库优化方案今天就跟大家聊聊有关怎么浅谈数据库优化方案,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。下面给大家分析了数据库优化方案,具体内容如下1.

    攻略 2021年12月2日
  • R语言中的MongoDB的示例分析

    技术R语言中的MongoDB的示例分析今天就跟大家聊聊有关R语言中的MongoDB的示例分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。R利剑MongeDB,分

    攻略 2021年11月23日
  • Java规则引擎有什么优点

    技术Java规则引擎有什么优点本篇内容主要讲解“Java规则引擎有什么优点”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java规则引擎有什么优点”吧!一、概述1 规则引擎概

    攻略 2021年11月15日