一.
开发的时候,到底什么时候写静态?
首先要明确的是static是修饰符,它只能修饰一个东西,就是成员(成员分两种,变量和方法)。
static可以修饰两种成员,现在讨论的是,它什么时候可以修饰这两种成员的问题。
静态什么时候用?
推理分析:
1. 静态变量。
先要知道加static和不加static的区别在哪?也是问静态变量和成员变量的区别。→成员变量一词固定为不加static的变量了么?
不加static是在对象里边,加static是在方法区里面。
不加static,对象特有。加了static对象共有。
什么时候变量需要静态化。→首先是这样的,堆内存是存储对象的,如果有一个成员变量对于所有对象来说都是一样的(比如相同国籍的人),在class类的成员变量的描述中写成String country="China"。这样,堆内存中的对象每一个里面都有一个country="China",如果对象很多的话,那么country="China"就会变的很占内存。为了缓解内存占用的问题,提出了静态变量的解决方法。这也是变量静态化的起源。
静态修饰可以将country="China"抽离出堆内存,那么抽离出来的数据放置在内存中哪里?方法区里,静态数据。
我们说对象里面都会持有相同的属性,因为根据同一个类创建的,创建完以后都具备相同属性,都有相同值,因为它们都有默认初始化值。默认完以后,它还在具备相同的效果时,它不需要做改变,只要拿过来用的话,这个值就是静态值,如果对象需要做改变,或者说有一个对象的值是可以改变的,那就不能静态。这就是我们说的共有数据和特有数据的区别。对象都用同一个数据,这个数据就是共有数据。
描述事物的时候,我们要分析事物的属性,如果我们发现事物这个属性的值全都是一样的,而且一般不变化,这个属性就被定义为静态属性。判别是共有的还是特有的,再做是否是可以用static修饰。
2. 静态函数
函数是对外提供的,我提供给对方的函数到底是静态的呢?还是非静态的呢?因为函数可以直接被用户所调到,这个时候考虑,我们是加还是不加。
函数就是功能,函数是每个对象都具备的功能。那么这个功能是用对象来调用,还是用类来调用,区分就只有一个,就是这个函数里面有没有访问到对象中的特有数据。看着是,函数访问了对象中的非静态变量,那么它就是非静态函数。为什么?有什么用?
对2.静态函数的解析,第一句是按内存讲的,第二句是按源代码讲的。
对第一句从内存开始分析。对speak方法,先用静态试试,如果出错,再将static去掉。
定义了一个speak()方法,它的功能是说出年龄。static是修饰符,加或不加,都不会改变它的功能。
我们要从内存开始分析。如果在主函数中真的创建一个对象,并调用speak()方法。意味着堆里面有了一个对象Demo,对象里面有一个age变量(默认初始化为0),没有num,num变量是static修饰的,是共享数据,不在对象里,在方法区内静态数据。speak()方法要使用age变量,但是age是对象的特有数据,这个特有数据不是指age,而是age的数值0。new Demo.speak()就是在调用speak(),speak()方法在执行的时候,用到了age,只能是非静态的了。在描述类中,先有变量的描述,接着是方法的描述。变量根据是特有还是共享,来确定其是静态还是非静态。然后,方法再依据使用的变量是静态还是非静态,来确定自己是否要用static修饰。
如果在这里非要给speak()方法,加上static修饰符,那就意味着没对象这方法就能执行。但是没有对象,就没有age,而且age前面其实是this.age,那么this也不能写。因为static修饰的方法不需要访问对象,
对speak()方法中的输出再次进行一个修改,再判定方法是否用static修饰。
如果speak()方法的输出语句中,输出的是“哈哈”。对于“哈哈”而言,在描述类中是没有定义字符串类型的变量的。(我觉着方法既然没有访问对象,那就可以用static修饰)→答案:加或不加都一样,既能编译又能运行(这里面为什么会存在一个只用java工具运行的情况?),(不加static,加了之后生命周期变长。→生命周期一样,因为方法,方法中都是一样的,只有所属不一样,一个所属this,一个所属类名?)。
静态和非静态都可以用,把内存图一画,就明白了。
不加static,看能不能用,将new.Demo().speak()进行了修改,改为了Demo d=new Demo(),d.speak(),虽然将一句拆成了两句,但本质是一样的。speak()方法没有访问特有数据,可是我们不加static(因为也没有使用静态变量,不是说没有使用特有变量,就必须加上static。应该这么说方法使用到了静态变量,就要加static修饰)。
我们从内存角度剖析这个程序,主函数中创建了一个对象,那么堆内存中就要开辟一个空间作为对象,同时分配地址编号,对象进行默认初始化(是不是每一个新建对象都有默认初始化,和构造函数初始化?)。先是栈中载入主函数,主函数中载入变量d,然后才有堆中对象的创建。在堆中对象初始化完毕后,将其地址赋值给d,从而d可以指向堆中的对象。主函数中的第二句是d.speak(),这意味对象调用了无参数的speak()方法,也就是说speak方法开始进栈。这时,speak方法中要指明是哪一个对象的调用,即用this来指代对象的地址编号,将对象的地址编号0x0045赋值给this(这个this是可以隐藏的么?是每一个方法都会存在的么?)。这样speak方法就指向了对象,接着speak方法运算完,输出“哈哈”,然后弹栈。从头到尾,新建的对象没有任何作用。
接着对程序进行一下修改,对对象进行一些确定的构造函数初始化,如下图所示。对象一初始化,就有年龄。对象一构造完,就从0变成了30。哪怕这里进行了特定的构造函数初始化,但是初始化都属于创建对象的过程,创建好对象后,该对象没有进行任何操作,程序就结束了。这里创建的对象除了浪费堆内存空间外,还要加载构造函数进栈出栈,没有其他的作用了(这里又想到关于堆内存中,无用数据的处理问题)。
这里其实就是想调用speak方法,输出“哈哈”,其实可以写成Demo.speak()。→匿名对象知识点不熟悉。
对象是封装特有数据的,没有访问特有数据,为什么要创建对象?
如果方法没有访问特有数据,对象创建是没有意义的,是在浪费空间。
因此本题是要加上static,是静态的,也就是通过类名来调用speak方法,只要能输出“哈哈”即可,而且也没有创建对象。设置成静态函数的目的就是为了,避开对象,通过类来调用方法。只要是非静态的方法,一调用,必然是通过对象来实现的。
只要方法访问的不是特有数据,就用static静态修饰方法。
对象在默认初始化之后,可以选择各种各样的构造初始化,如果不选择或者没有构造函数的话,系统会有自动的构造函数进行初始化。我这里想说的是,为什么新建对象后面的括号里输入的数据就代表着特定的构造函数,同时也能给构造函数赋值?