十二、字段不存在多态特征
通过对象调用字段,在编译时期就已经决定了调用那一块内存空间的数据。
即:字段不存在覆盖的概念,在多态时,不能有多态特征(在运行时期体现子类特征)。只有方法才有覆盖的概念。
十三、代码块
在类或者方法中,直接使用{}
括起来的一段代码,表示一块代码区域,代码块里的变量属于局部变量,只在自己的区域里有效。
根据代码块定义的位置,我们把代码块分成三种形式:
1.局部代码块:直接定义在方法内部的代码块:
一般的,我们不会直接使用局部代码块的,只不过我们会使用if,while,for,try等关键字结合;
2.初始化代码——构造代码块:直接定义在类中
每次创建对象的时候都会执行初始化代码块(每次创建对象之前会调用构造器,在调用构造器之前,会先执行本类中的初始化代码块):
public class CodeBlockDemo
{
{
System.out.println("初始化代码块");
}
CodeBlockDemo()
{
System.out.println("构造器...");
}
public static void main(String[] args)
{
System.out.println("进入main方法");
new CodeBlockDemo();
new CodeBlockDemo();
new CodeBlockDemo();
}
}
运行结果如下:
进入main方法
初始化代码块
构造器...
初始化代码块
构造器...
初始化代码块
构造器...
大家会很好奇,为什么会这样呢,当我们将字节码文件反编译之后,会发现实际上的代码是这样的:
public class CodeBlockDemo
{
CodeBlockDemo()
{
System.out.println("初始化代码块");
System.out.println("构造器...");
}
public static void main(String[] args) {
System.out.println("进入main方法");
new CodeBlockDemo();
new CodeBlockDemo();
new CodeBlockDemo();
}
}
但是…但是…但是…我们一般不用初始化代码块,一般在构造器中初始化。如果初始化操作很多,构造器结构很乱,我们可以重新定义一个方法,再在构造器中调用这个方法。
3.静态代码块:使用static修饰的初始化代码块:
在主方法之前执行静态代码块,而且只执行一次。
static
{
System.out.println("初始化代码块");
}
CodeBlockDemo()
{
System.out.println("构造器...");
}
public static void main(String[] args)
{
System.out.println("进入main方法");
new CodeBlockDemo();
new CodeBlockDemo();
new CodeBlockDemo();
}
输出结果为:
初始化代码块
进入main方法
构造器...
构造器...
构造器...
main方法是程序的入口点,那为什么静态代码块优先于main方法执行呢?
因为:静态成员随着字节码的加载也加载进JVM,此时main方法还没有执行,因为方法需要JVM调用。也就是先把字节码加载进JVM,然后JVM再调用main方法。
一般的,我们用来做初始化操作,加载资源,加载配置文件等等。。。(不同于初始化代码块对于每一个对象的初始化操作)
十四、一道有趣的面试题
问以下代码的执行顺序:
public class App {
private static App d = new App();
private SubClass t = new SubClass();
static{
System.out.println(3);
}
public App(){
System.out.println(4);
}
public static void main(String[] args) {
System.out.println("Hello");
}
}
class SuperClass{
SuperClass(){
System.out.println("构造SuperClass");
}
}
class SubClass extends SuperClass{
static{
System.out.println(1);
}
SubClass(){
System.out.println(2);
}
}
运行结果为:
1):1 //APP类依赖于SubClass类,因此要先把SubClass类加载进JVM
2):构造SuperClass //非static字段的初始化其实都是在构造器中优先执行的
3):2 //static对象的初始化实际都是在静态代码块中执行的
4):4
5):3
6):Hello
十五、final修饰符
为什么需要使用final修饰符:
因为继承关系最大的弊端是破坏封装,子类能访问父类的实现细节,而且可以通过方法覆盖的形式修改实现细节。
多个修饰符之间是没有先后关系的:public static final 和 pubic final static 和 final static public 都是可以的。final可以修饰非抽象类,非抽象方法和变量。但是构造方法不能用final修饰,因为构造方法不能被继承。
当满足以下条件就可以把类设计成final类:
1.某个类不是专门为继承而设计;
2.出于安全考虑,类的实现细节不允许改动;
3.确信该类不会被拓展
什么时候的方法要用final修饰:
1.在父类中提供的统一的方法骨架,不准子类通过方法覆盖来修改,此时使用final修饰;(模板方法设计模式)
2.构造器中调用的方法(初始化方法),此时一般用final修饰。
注意:final修饰的方法,子类可以调用,但是不可以覆盖。
final修饰的变量:表示常量,只能赋值一次,不能再次赋值。
1.final变量必须显示置顶初始值,系统不会为final字段初始化;
2.final变量一旦赋值,就不能再次赋值、
常量名规范:常量名符合标识符,单词全部大写,单词间使用下划线分隔。
比如:final int MAX_VALUE = 2147483647;
补充概念:全局静态变量:public static final修饰的变量,直接用类名调用。
面试题:final修饰的引用类型变量到底表示引用的地址不能改变,还是引用地址里的数据不能改变?
final修饰基本类型变量:表示该变量的值不能改变,即不能用“=”重新赋值;
final修饰引用类型变量:表示该变量的引用地址不能改变,而不是引用地址里的内容不能变。
final是唯一可以修饰局部变量的修饰符,目的何在?(内部类将介绍,局部内部类只能访问final修饰的局部变量)
什么时候使用常量:当在程序中,多个地方使用到共同的数据,而且数据不会改变,此时我们专门定义全局的变量。
一般的,我们在开发中会专门定义一个常量类,专门用来存储常量数据。