访问修饰符
概念:用来定义属性的行为,用于在特定的情况下可以访问
类的每一个成员(包括成员变量、方法和构造器)都有一个访问修饰符,用来判断谁可以访问该成员。访问修饰符允许我们在设计程序的时候,就考虑到谁可以访问一个对象的不同属性和行为。
Java为类中的成员提供了四种访问级别,按照访问权限由大到小排列如下:
- 公开访问级别:使用public关键字修饰。用public关键字修饰的成员对外公开,即公开成员可以被任何其它对象访问。
- 受保护访问级别:使用protected关键字修饰。受保护的成员可以被同一包中的类所访问,还可以被类的子类所访问,不管子类是在哪个包中。
- 默认访问级别:没有访问修饰符。默认访问级别的成员可以被同一包中的其它类所访问。
- 私有访问级别:使用private关键字修饰。它是四种访问修饰符中级别最低的。私有成员只有类本身可以访问,不对外公开。
- 注:受保护和默认访问是相似的,因为二者都授予可以访问同一包中的其它类。受保护访问实际上比默认访问的限制更小,因为受保护访问也授予访问在包外的子类。
- 注:
访问级别:private-->default-->protected-->public
封装
封装是使类中的成员变量都是private,并提供public方法访问这些成员变量的技术。如果一个成员变量被声明为private,那么它就不能被其它类访问,从而隐藏了类中的成员变量。因此,封装也被称为数据隐藏。
- 注:继承、封装、多态、抽象是面向对象的四大基本概念
封装有很多好处,包括:
- 类的成员变量可以成为只读或者只写的。例如,SalesPerson类中的成员变量id是只读的,在SalesPerson对象实例化后,id成员变量就无法改变了。
- 类可以对存储在其成员变量中的内容有一个整体的控制。例如,SalesPerson类中的成员变量commissionRate的值只能在0.0和 0.20之间。
- 类的用户不需要知道类是如何存储数据的。类可以改变一个成员变量的数据类型,而类的用户不需要改变其代码。
理解静态成员
关键字static允许成员变量或方法不与类的特定实例关联。通过关键字static声明的成员变量或方法可以被认为是全局的,任何其它类可以直接访问静态成员变量或调用静态方法,而不需要该类的一个实例。
类的静态成员经常被称为类成员,因为静态成员与类相关,而不是类的某个实例。非静态的成员变量和方法经常被称为实例成员,因为非静态的成员变量和方法只存在于类的实例中。
类的非静态成员变量和方法在类没有实例化之前是不存在的。但是,静态成员是与类相关联的。JVM在加载类到内存后,就给静态成员变量和方法分配了内存。也就是说,类一旦加载后,我们就可以马上使用静态成员变量和方法了。
访问静态变量和方法
静态成员变量和方法不能使用引用来访问,因为引用是指类的实例,而我们不需要类的实例来访问静态成员。访问静态成员要使用类名。
//例如:
public class persion{
public static String username = “张三”;
Public static void printf(){
System.out.println(“你好吗”);
}
}
调用username是不需要new persion()再来调用,直接用类名调用persion.username调用,同样的,调用printf方法也是persion.printf()。
我们已经使用了静态成员变量和静态方法。如果我们看到一个成员变量或方法是通过类名来访问的,那么我们就可以说该成员变量或者方法是静态的。例如,在本书中我们已经用过很多次System.out。
静态方法不能访问实例成员(不能访问非静态成员)
- 静态方法只能访问静态成员,不能访问非静态成员,非静态成员函数可以访问静态成员
静态初始化块
除了声明静态成员变量和方法外,关键字static还有另外一种用法。Java类可以包含一个静态初始化块,静态初始化块是一组当类被JVM的类加载器加载时执行的语句。
类被类加载器加载一次,静态初始化块的作用是允许类执行任何所需的只需要发生一次的设置任务。
静态初始化块通过如下的语法形式定义:
| static { //语句出现在这里。 } |
- 类的静态初始化块只被运行一次。
- 静态初始化块在构造器执行之前加载
实例初始化块
用法:
{
//内容
}
实例初始化块与静态初始化块类似,它在类的对象每次实例化时执行一次。实例初始化块和构造器二者之间的不同之处在于实例初始化块在构造器调用之前执行。
实例初始化块的语法形式很简单,只需要在类中用大括号括起代码块即可。注意,实例初始化块不使用任何关键字,也没有名称。
实例初始化放在属性之下,构造之上
实例初始化块中的语句在任何父类构造器调用之后,在子类构造器调用之前执行。当对象被实例化,并且类包含有实例初始化块时,下面的事件按顺序发生:
- 子类中相应的构造器被调用。
- 执行对super的调用,控制流程跳转到相应的父类构造器。
- 父类构造器执行完,控制流程跳转回子类构造器。
- 在该子类构造器中的super()后面的任何语句执行前,实例初始化块执行。
- 最后,执行子类构造器中super()后面的语句。
实例初始化块在每次新建对象时都调用
在生成对象的时候,执行顺序是:
1、父类静态初始化快
2、子类静态初始化块
3、父类实例初始化块
4、父类构造函数中的代码
5、子类实例化初始化块
6、子类构造函数中的代码(super()后面的代码块)
现在已经不怎么用实例初始化块,用有参构造代替了代码块,相当于整合了无参构造和代码块
New出一个对象在调用构造函数做的事:
- 在堆中生成一个对象,为其分配空间
- 为成员变量分配存储空间
- 为成员变量赋初值
- 执行构造函数中的代码
- 实例初始化块在3-4之间执行
内部类
在类内部除了可以定义成员变量与方法。在Java中,还可以在类的内部定义类。这种在类的内部定义的类,称为内部类。内部类所在的类称为外部类。
Java中的内部类可以分为四种,分别是:静态内部类、成员内部类、局部内部类、匿名内部类。
内部类
类型 | 说明 |
静态内部类 | 作为类的静态成员,存在于某个类的内部 |
成员内部类 | 作为类的成员,存在于某个类的内部 |
局部内部类 | 存在于某个方法内部的类 |
匿名内部类 | 存在于某个类的内部,但是无类名的类 |
静态内部类
静态内部类的定义方法如下:
| class Outer { static int f(){ } static class Inner { public void g(){ f(); } } } |
注意:静态内部类调用外部类的方法只能调用静态的。
这里,Inner类存在于Outer类的内部,作为Outer的静态成员。由于静态成员可以在不创建类的情况下被使用,因此,我们可以不创建Outer类的对象,而直接引用Inner类。
同java文件中其他类调用这个静态内部类方式:
| Inner ob = new Inner(); //直接调用静态内部类 ob.g(); |
|
如果要在其他java文件中调用这个静态内部类,就写成: |
| Inner ob = new Outer.Inner(); //直接调用静态内部类 ob.g(); |
- 分析结论:静态内部类虽然是外部类的成员,但是在未创建外部类的对象的情况下,可以直接创建静态内部类的对象。此外,静态内部类可以引用外部类的静态成员变量和静态方法,但是不能引用外部类的普通成员。例如,如果在静态内部类中引用外部类的普通成员,就会出现编译错误。
成员内部类
成员内部类的定义方法如下:
| class Outer { private int i; Private int t; class Inner { public int i; Public int j; Public void g(){ this.i = Outer.this.i; j = t; } } } |
注意:在内部类的方法中调用外部类成员变量的时候,如果调用的变量名和本内部类的变量名不存在二义性,可以直接调用外部成员变量,如果名字存在二义性,那么就要写成(外部类名.this.成员名),方法的调用是一样的。调用内部类的成员那么可以直接用(this.成员名)调用。
这里,Inner类存在于Outer类的内部,作为Outer的成员。与静态内部类不同的是,Inner类定义时没有static修饰符,同时,只有创建了Outer类的实例对象后,才能使用Inner类的对象。
调用方式:
| InnerDemo out = new InnerDemo (); //实例化一个InnerDemo类的对象 Inner in = out.new Inner(); //Inner类的对象通过外部类的对象创建 in.g(); |
分析结论:成员内部类可以调用外部类的所有成员。但是只有创建了外部类的对象后,才能引用外部的成员。
局部内部类
局部内部类是在类的方法内部定义的类。局部内部类只能在方法内部中使用。一旦方法执行完毕,局部内部类就会从内存中被清除。
代码清单演示了局部内部类的使用。
| /*代码清单 LocalDemo.java 演示局部内部类的使用 */
public class LocalDemo { int a = 10; void f() { class Inner { int b = 20; void g() { System.out.println(a); System.out.println(b); } } Inner in = new Inner(); in.g(); }
public static void main(String[] args) { LocalDemo local = new LocalDemo(); local.f(); } } |
在LocalDemo程序中,方法f()内定义了一个局部内部类Inner,这个内部类只能在f()方法内使用。在第15行,我们实例化了内部类Inner,然后在第16行执行局部内部类的方法g()。
必须注意的是,如果局部内部类中要使用它所在的方法中的局部变量,那么就需要将这个局部变量定义为final的。
匿名内部类
匿名内部类是一种特殊的内部类,这种类没有名字。匿名内部类的定义与对象的创建合并在一起,整个动作看起来像产生对象似的。匿名内部类是为唯一对象而定义的类。当我们只需要创建一个类的对象,而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。
匿名内部类一般通过如下形式定义,并且在定义类的同时进行对象实例化:
| new 类或者接口的名字() { //匿名内部类的主体 } |
new后面是一个类或者接口,然后是一个大括号。在大括号中是匿名内部类的主体,这个主体就是类或者接口的实现。如果是类,那么匿名内部类是该类的子类。如果是接口,匿名内部类需要完成对接口的实现。一般用与对类里面的方法进行重写。
总结
本章内容总结如下:
- Java为类中的成员提供了四种访问级别,按照访问权限由大到小排列,分别是:公有的(public)、受保护的(protected)、私有的(private)和默认的(无访问修饰符)。
- 通过用private访问修饰符隐藏类中的成员变量,称为封装。
- 静态成员变量或静态方法并不与类的每个实例关联。静态成员变量或者静态方法只有单个实例,它们对类的所有实例以及程序中的其它类是共享的。
- 静态初始化块在类装载时执行。实例初始化块在类被实例化时执行,在构造器调用之间调用。
- 内部类是在类的内部定义的类。在Java中,内部类有四种形式,分别是静态内部类、成员内部类、局部内部类、匿名内部类。
java文件与类文件的关系
1、java文件中,包含几个类,就对应的产生几个相应的类文件
2、java文件的名称,不一定和类文件的名称保持一样
.1)兄弟类类文件的命名规则就是“兄弟类.class”
.2)成员内部类类文件的命名规则就是“外部类$内部类.class”
.3)局部内部类类文件的命名规则就是“外部类$内部类出现的次数 + 内部类.class”
3、一个java文件中,只允许出现一个public修饰的外部类,其他的而外部类都是default