类的高级概念
static
static关键字 -- 是一个可选修饰符,它本身的含义叫做“静态”。 它不仅只能修饰方法,
它也可以修饰:属性、代码块、内部类 。
那么static可以修饰其中两个:属性和方法。不能修饰构造方法。能够在类里面书写的两
个内容:代码块(初始化块)以及内部类。
类里面总共可以书写的内容是5个:属性、构造、方法、初始化块和内部类。其中:前
三个是最常操作的。初始化块在大型项目当中可能会在部分关键类里面出现。
static 与 非static 的属性
在一个类当中,属性也可以选择使用static进行修饰。
A、语法上的对比:
有static
1、有static修饰的属性可以用"类名."的方式直接访问,也可以通过“对象.”的方式
进行访问(但第二种方式,不推荐)。
2、用static修饰的属性,是全类的所有对象共享一个值,无论是该类的哪个对象把这
个值改了,那么大家的值都跟着变。
非static
1、非static修饰的属性是只能用“对象.”的方式访问;
2、非static修饰的属性是每个对象身上有一个该属性的变量,存放了该对象该属性的
值。从两者的第2点语法对比,我们领会Java语法设计者的设计意图。之所以让
static修饰的属性能够用“类名.”的方式去访问,不是为了让我们方便,而是为了体现
这个属性是全类共享的。而非static修饰的属性用"对象."的方式去访问,是为了清楚
的表达到底要操作是哪个对象的该属性。
在一个类当中,能够用static修饰的属性是不多见的,通常都需要在某个特定的问题
域限制当中才找得到。static才是特例,通常都是非static的属性。根据经验,只有
常量,我们能够直接设计为static
原因:
1、常量属性的值是在类的定义中给定的,每个对象身上都是同样的值(常量也不能
改);所以没有必要在每个对象身上存放一份,只需要全类共享一个就可以了。
2、常量属性的值在类定义中给定以后,外部也没有更改的权利,所以可以不用保密,
直接public公布给外部看;
3、所以,通常常量属性的修饰符是:public static final的。
千万不要为了方便去把属性设计为static,一定要找到全类共享一个值的属性才做这
种设计。
B、内存上的区别:
1、存放的位置不同
非static修饰的属性是在对象本身身上,也就是在堆当中;
static修饰的属性没有存在对象身上,也就不是在堆当中;而是单独作为类的共
享信息存放在一个叫做“静态区”的内存空间里;一个类的一个static属性只划分
一个存放空间。
2、该属性在内存中产生的时机不同:
非static属性是在产生对象的时候,在内存中产生;
static的属性是在 *加载期* 产生的于内存中。
两个凡是:
1、凡是用static修饰的内容,都是表示它与对象无关,只与类相关;
所以:用static修饰的属性,我们又叫做“类属性”/“静态属性”,
没有static修饰的属性,我们一般叫做“成员属性”
2、凡是用static修饰的内容,都会在加载期搞事情。
static 和 非static修饰的 方法
A、在语法上的对比:
有static的方法:
1、在调用该方法的时候,可以通过“类名.”的方式来访问,也可以通过"对象."的方式
来访问;只不过第2种方式不推荐,但不会报错;
2、在方法的内部实现中,静态方法只能调用本类的静态属性 或 静态方法;
之所以不能调用的原因:
原因1:静态方法是通过"类名."的方式调用,这个时候方法内部是没有当前对象这一目
标,也就是说没有“this”,所以无法确认这里访问的非静态属性或方法到底是哪个对
象的。这是站在面向对象的设计思想来表达的;
原因2:当我们在进行类的编译和加载的时候,在进行加载的顺序划分时,JVM都是首先
加载静态的,然后再加载非静态的。如果在加载静态内容时,发现里面有非静态的调
用,这个时候JVM是不认识这些非静态内容的,因此报错。反过来,在加载非静态的方
法时,由于静态内容已经加载过了,所以JVM认识,因此通过。
非static的方法:
1、在调用该方法的时候,只能通过“对象.”的方式访问。
2、在方法的实现内部,即可以操作本类的静态属性和静态方法,也可以调用到本类的
非静态属性和非静态方法;
这样的设计同样表明了用static修饰的方法是跟对象无关的;非static的方法是一定
要确定到底是哪个对象去做这件事情。所以,用static修饰的方法又被称之为“类方
法”或“静态方法”;非static修饰的方法被称为“成员方法”。
B、设计上的区别:
我们要把工具类的工具方法,设计为static的;其他的都是非static的。
对初始化块的简单认知
1、初始化块是我们可以在一个类里面定义的第4个内容。它的语法非常简单,就是直接在类
里面,打上一对"{}",然后在"{}"里面书写代码。
2、它从外观形式上非常像一个没有方法声明,只有方法实现的特殊语句块。在它内部,我
们可以书写任意指令语句,包括:输入输出、变量声明、运算表达、流程控制;
3、由于这个代码块是没有方法声明的,也不能接受参数和返回,所以很明显它是不能够在
类的外部随意调用的。它的执行只能是在一个特定的时间点运行。 什么时候呢? 产生对象
的时候,所以它也被称之为“实例初始化块”。每产生一个对象,就会被执行一次。
实例初始化块到底在初始化一些什么呢?如果初始化对象的话,那么它的作用就和我们的构
造方法重叠了。
1、先后问题
首先调用到构造方法,构造方法就会默认执行三个动作,
先在内存中划分空间(对象存放的空间);
然后在该空间划分属性;
接下来就会先执行我们在实例初始化块当中书写的代码;
再对属性进行初始化(这里的初始化是指有没有在声明属性的时候用代码赋初始值);
完了以后再执行我们在构造方法当中书写的代码。
2、哪些初始化动作放在初始化块,哪些动作是放在构造方法里面做?
构造方法里面的初始化,主要做的事通过外部调用者传入的参数,给当前对象的
属性赋值;
如果某些属性不需要从外部接受参数赋初始值,那么我们更多的选用的是,在声
明属性的时候直接使用“=”去赋值;
而实例初始化块更多的作用不是用来给对象中的属性赋初始值的,而是用来在一
些特殊场景当中执行非赋值的初始代码,比如:开启资源,开启通讯管道,开启
线程、开启文件等等。
初始化块也分static 和 非static的
前者也叫“静态初始化块”,后者叫做“实例初始化块”
"静态初始化块"是在加载类的时候被执行的,而一个类在JVM当中只需要加载一次,所
以它是首先被执行,且以后无论产生多少对象,甚至不产生对象,也不再被执行。
“实例初始化块”是在产生对象的时候被执行,且产生多少个对象就会被执行多少次。
如果说“实例初始化块”的执行时机和次数与构造方法有一定的重叠,导致它的使用量很少。
但是“静态初始化块”由于其独特的执行时机和执行次数,导致它是没有被替换的可能的。
很多资源的操作,包括开启呀,预加载呀,都是一次性完成的,没有必要每new一个对象就
做一次,而且很多时候我们会把做这些动作放到程序的加载期而不是运行期,这是为了提升
程序的运行速度。
静态初始化块也满足两个凡是:
1、与类有关;
与对象无关;
对象与否无关。
2、加载期搞事情
内部类
故名思义 -- 就是在一个类A的内部定义另外一个类B,那么类A就是外部类,类B就是A的内
部类。
首先需要明确:
1、内部类一定要定义在外部类的"{}"里面,而不是简单的写在一篇java文件当中;写
在一篇java文件当中,它们两个是平行关系,不是嵌套关系。
2、内部类除了定义的位置有特殊性以外,它在本质上和普通的类没有区别;
包括:
a、仍然拥有属性、构造、行为、初始化块、甚至是它的内部类;
b、内部类虽然和外部类在同一篇java文件当中定义,但是编译以后它拥有自己独
立的class文件。
c、内部类的类名,不是我们书写的简单名,而是和外部类的类名共同组成的。
内部类的分类
首先根据内部类书写的位置,分为两大类:
1、成员内部类;
如果一个内部类是直接定义在外部类的“{}”当中,它的位置和外部类的属性、方法、
构造,都处于平行位置,那么就叫做“成员内部类”。
当然作为外部类的成员,它是可以有访问修饰符的,用来控制这个内部类是否可以被外
部使用。
成员内部类编译后的class文件名:外部类名字$内部类的名字
2、局部内部类;
如果一个内部类是定义在外部类的某个方法当中,它的位置和这个方法的局部变量保持
一致,那么就叫做“局部内部类”。
作为方法的内部定义,跟局部变量一样出了方法就认为消失不在了,所以它只能在这个
方法内部使用,不能有访问修饰符。
局部内部类编译后的class文件名:外部类的名$序号内部类名字
这里序号从1开始,是同名局部内部类编译的顺序序号。
再根据语法特殊性,对“成员内部类”和“局部内部类”再分类:
1、成员内部类分为:普通成员内部类 和 静态内部类;
区别就在于 加不加 static 关键字;
2、局部内部类分为:普通的局部内部类 和 匿名内部类
所谓“匿名”就是没有给这个类起类名,匿名内部类是在定义的时候马上产生对象,只
能使用一次。
四种内部类的使用语法
1、普通的成员内部类
要想用普通的成员内部类,首先要先产生外部类的对象,然后用"外部类对象.new" 的语法
去产生内部类对象。
```java
OutClass1 out = new OutClass1();
OutClass1.InnerClass1 inner1 = out.new InnerClass1();
```
2、静态内部类
要用静态内部类,由于有static修饰符,所以这个静态内部类与它所属的外部类对象无
关,因此可以不需要产生外部类对象,直接用外部类类名访问。
```java
OutClass1.InnerClass2 inner2 = new OutClass1.InnerClass2();
```
3、局部内部类
局部内部类只能在定义它的方法之内使用。
```java
InnerClass3 inner3 = new InnerClass3()
```
内部类当中书写this,代表的是当前的内部类对象,如果要表示它所关联的外部类对象,那
么要写成“外部类类名.this”
局部内部类的方法里面,能够操作到外部类的属性,但是不能操作到所属方法的局部变量。
4、匿名内部类
匿名内部类是在new对象的同时,去定义这个对象里面的属性和行为,然后由于没有给这个
类型取名字,所以只能用这一次。
```java
new Object(){
private int a;
public void test(){
}
}.test();
在new对象的同时,去定义这个对象里面有哪些属性和行为。由于这个对象有了新增的属性
和行为,所以它不属于new关键字后面那个类型了,而是属于一种新的具有a属性和test方
法的类型,但这种类型没有取名字。