【关于为什么普通内部类中不能定义static修饰的方法】

相信大家在初学Java类这一章节时,遇到内部类、外部类的内容会感觉比较绕,尤其是遇到一些语法规定时会觉得难记,其实这些都是有原理的,如果我们能摸清其中的机制,便可以得心应手的使用了。

1.关于static

  特性:在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,而是所有对象所共有的。

所以它并不是随着实例对象的出现被加载的,而是在类被加载的时候就被加载了。所以它只会被加载一次,因为它是随类加载的。

举个例子:

比如我们要一个关于【三班学生的类】,字段包括班级、姓名、年龄。因为班级是所有学生对象共有的特性(所有学生都是3班),所以我们可以用static修饰班级,表示这是个静态成员,即所有对象所共有的。而姓名跟年龄,则是某一个具体的实例对象拥有的特性。

public class Class3Student {
    public static int clas  = 3; //班级
    public String name; //姓名
    public int age;//年龄

    public Class3Student(String name, int age){
        this.name = name;
        this.age = age;
    }


    public static void main(String[] args) {
        Class3Student student1 = new Class3Student("小王",12);
        Class3Student student2 = new Class3Student("小李", 13);
        Class3Student student3 = new Class3Student("小周",14);
        System.out.println(student1.clas+" "+student1.name); //3 小王
        System.out.println(student2.clas+" "+student2.name);//3 小李
        System.out.println(student3.clas+" "+student3.name);//3 小周
    }
}

但是上诉代码中会出现黄色警告,因为我们使用了实例对象.静态成员去访问静态成员。正确访问静态成员的方式应该是【类名.静态成员】(即:Class3Student.clas)。因为它属于类成员,并不单独的属于某一个实例对象,所以应该用类名的方式去访问更为准确。

既然static修饰的成员表示类成员,那么是不是就表示我们不需要new一个对象就可以实现静态成员的访问呢?

 

 

 运行的结果证实了我们的猜想。

1.1总结及扩展

1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中
4. 类变量存储在方法区当中
5. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

2.普通内部类(非静态内部类)

定义:定义在一个类中的非静态类(没有用static修修饰的类)称为普通内部类。

举个例子:

class Outer{
    public int a = 1;
    public int b = 2;
    public static int data = 3;
    private int c = 4;

    public void func(){
        System.out.println("这是外部的成员方法"+a);
    }

    public static void func2(){
        System.out.println("这是外部的静态成员方法");
    }
    class Inner{
        public int a = 10;
        public void func(){
            System.out.println("这是内部的成员方法"+a);
            System.out.println("这是内部的成员方法"+Outer.this.a);
        }
    }
}


public class Test {
    public static void main(String[] args) {
        Outer.func2();
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        outer.func();
        inner.func();
    }
}

上述代码中,我们先用了外部类名去访问它的静态方法,为第一点讲的static做一个回顾,我们不需要实例化对象,就可以调用静态成员方法。Outer.func2();

通过实例化对象outer,我们调用了Outer类中的普通成员方法。

Outer outer = new Outer();     outer.func();

通过实例化外部类(Outer类),我们实例化了Inner类, Outer.Inner inner = outer.new Inner();

这里需要注意当内部类成员变量和外部类成员变量同名时,需要通过外部类名.this.成员变量 的方式访问外部的成员(本例中写法是:Outer.this.a )。


=========================================================================

现在我们来思考一个问题,是否能在外部类的方法中访问到内部类的成员变量呢?在刚刚的例子中我们已经知道了在内部类是可以通过Outer.this.a 的方法成功访问到外部类的成员变量的。那反过来呢?我们用代码实验一下:

我们可以看到这样会使编译出错。

那怎么样才能访问到呢?在func方法中创建一个内部类对象去引用它的成员变量d即可。

 public void func(){
        System.out.println("这是外部的成员方法" + a );
        System.out.println( "内部的成员变量:"+ new Inner().d);
    }

我们在main方法中试一下是否可以成功运行。

 public static void main(String[] args) {
        Outer outer = new Outer();
        outer.func();
    }

 

========================================================================= 

我们试着来梳理一下为什么这样是可行的,为什么不能直接访问,而是需要实例出一个Inner对象才能访问。而为什么Inner的方法中就可以直接访问Outer类的成员?

首先Inner类是一个在Outer类内部定义的一个类,它也可以被认为是Outer类中的一个特殊的成员变量,只不过这个变量是类而已。但是这个类是普通类,并不被static所修饰。这就是说Inner类只有在Outer类实例出一个具体的对象的时候才会存在。

我们可以将其与一开始讲述的static成员联系,之前讲了被static修饰的成员是类成员,只要类加载了,我们不需要实例对象(也就是不需要new一个对象),我们就可以直接用【类名.静态成员】访问使用。那反过来不被static修饰的成员,就是需要实例对象才能访问的成员,它是随着你的对象被创建出来(new出来),它才被加载的。

总结:

被static修饰的成员,随类加载,可以不创建对象直接使用。不被static所修饰的成员,必须创建对象,才能使用。

一些疑惑:

为什么随类加载可以直接使用?========》因为在你创建对象之前,你的这个变量就已经随着类加载进来了,机器已经知道你这个东西了,并且开辟了内存给你放好了,你直接用就是了,根本就不用再创建对象。而不被static修饰的成员变量,只有你new了对象,机器才知道原来还有这些变量存在,然后再给你开辟空间储存。

扩展:

一个类中,被static修饰的成员存储的区是方法区,不被static修饰的成员存储的区是堆。


问题:【为什么不能直接访问,而是需要实例出一个Inner对象才能访问。而为什么Inner的方法中就可以直接访问Outer类的成员?】

首先这是Outer类中普通的成员方法(不被static修饰),所以说明要调用这个方法本身就需要new一个Outer对象。然后在看内部类中的成员变量d,它也没有被static所修饰,所以我们也需要new一个Inner对象才能访问。但是因为本身能进到这个方法里面(能调用这个方法)就已经说明了我们已经创建了Outer对象,所以我们只需要在里面创建一个Inner对象就可以访问到Inner中的d变量了。

 那如果是我们Outer中静态方法func2调用内部类的d是不是就需要new Outer对象了呢?

我们可以看到一开始没有new Outer()会报错,加了new Outer之后就不会报错了。因为static修饰的方法是可以没有实例对象的,所以这里编译会报错,我们必须要确定有一个实例对象才能对非静态变量成员进行访问。其中Inner是Outer的一个特殊的非静态成员变量,d是Inner一个非静态成员变量。


3.为什么普通内部类不能有static修饰的成员变量或方法?

如果你对上述内容已经理解,相信你心里多多少少已经有答案了。

普通内部类(没有被static修饰的类)======》外部类中特殊的普通成员变量======》普通成员变量必须依赖实例对象(必须new一个对象)======》普通内部类依赖于外部类的实例对象

结论:

普通内部类依赖于外部类的实例对象。

那么如果我们内部类有static(静态)修饰的成员变量或方法会怎么样呢?

内部类中的静态成员变量/方法======》内部类不用实例对象就可以直接访问======》【内部类.成员变量名】直接使用。

但是我们可以直接使用吗?我们之前得出结论普通内部类依赖于外部类的实例对象 。那么我们怎么可以直接用【内部类.成员变量名】呢,我们必须要依附于外部类的实例对象创建才可以。所以这是与我们的初衷相悖的。所以这是矛盾的,不可行。

从机制上来讲,static成员是类成员,随着类被加载而加载,但是没有实例化外部类,内部类未加载,却试图在内存中创建static的属性和方法,这当然是错误的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值