1 面向对象
1.1 对象概述
1.1.2:什么是对象
对象是真实世界中的物体在人脑中的映象,包括实体对象和逻辑对象。
实体对象指的是我们能在现实生活中能看得见、摸得着,实际存在的东西,比如:人,桌子,椅子等。
逻辑对象是针对非具体物体,但是在逻辑上存在的东西的反映,比如:人与人的关系。
1.1.2:对象的基本构成
静态属性和动态的功能。
1.1.3:如何进行对象抽象
抽象是在思想上把各种对象或现象之间的共同的本质属性抽取出来而舍去个别的非本质的属性的思维方法。
也就是说把一系列相同或类似的实体对象的特点抽取出来,采用一个统一的表达方式,这就是抽象。
如人:
对象{
静态属性:
属性1;
属性2;
动态动作:
动态1;
动态2;
}
1.1.4:抽象对象和实体对象的关系
抽象对象——“人”,具体的实体对象:“张三”、“李四”。你会发现,抽象对象只有一个,实体对象却是无数个,通过对抽象对象设置不同的属性,赋予不同的功能,那么就能够表示不同的实体对象,在需要具体的实体的时候,分别设置不同的值就可以表示具体对象了。
1.1.5:Java 中的类和对象
1.1.5.1:Java 中的类
把抽象出来的对象使用 Java 表达出来,那就是类 class。
1.1.5.2:Java 中的对象
Java 中的对象是在 Java 中一个类的实例,也称实例对象。实例就是实际例子。
类可被认为是一个模板—你正在描述的一个对象模型。一个对象就是你每次使用的时候创建的一个类的实例的结果。
1.2 对象三大特点
三大特性:封装、继承、多态。
1.2.1 封装
封装是 JAVA 面向对象的特点的表现,封装是一种信息隐蔽技术。
- 1 ) 即把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位;
- 2 ) 尽可能隐藏对象的内部结构,使代码更好维护;用户去使用一个界面访问数据
1.2.2:继承
“继承”是面向对象软件技术当中的一个概念.
继承可以使得子对象具有父对象的各种属性和方法,而不需要再次编写相同的代码。在令子对象继承父对象的同时,可以重新定义某些属性,并重写某些方法,即覆盖父对象的原有属性和方法,使其获得与父对象不同的功能。
1.2.3:多态
同一行为的多种不同表达,或者同一行为的多种不同实现就叫做多态。
2 java类组成
2.1 概述
一个完整的 Java 类通常由下面六个部分组成:
包定义语句
import 语句
类定义{
成员变量
构造方法
成员方法
}
其中:只有类定义和“{}”是不可或缺的,其余部分都可以根据需要来定义。
2.2 包
2.2.1:定义
在Java 中,包是类、接口或其它包的集合,包主要用来将类组织起来成为组,从而对类进行管理,以解决命名冲突、引用不方便、安全性。
简而言之:从逻辑上讲,包是一组相关类的集合;从物理上讲,同包即同目录。
2.2.2:用途
包对于下列工作非常有用:
(1):包允许您将包含类代码的文件组织起来,易于查找和使用适当的类。
(2):包不止是包含类和接口,还能够包含其它包。形成层次的包空间。
(3):它有助于避免命名冲突。当您使用很多类时,确保类和方法名称的唯一性是非常困难的。包能够形成层次命名空间,缩小了名称冲突的范围,易于管理名称。
使用 import 导入包
2.3 访问修饰符
修饰符访问 | 同类 | 同包 | ==子类(可不同包)== | 任何场合 |
---|---|---|---|---|
private | Yes | |||
default | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
protected 方法或变量可以被同包访问,也可以子类访问(包括其它包)。
2.4 类定义
类的定义形式如下:
<权限修饰符> [一般修饰符] class <类名> {
[<属性定义>]
[<构造方法定义>]
[<方法定义>]
}
2.5 构造方法
2.5.1 什么是构造方法
类有一个特殊的成员方法叫作构造方法,它的作用是创建对象并初始化成员变量.
在创建对象时,会自动调用类的构造方法。
2.5.2 构造方法定义规则
Java 中的构造方法必须与该类具有相同的名字,并且没有方法的返回类型(包括没有void)。另外,构造方法一般都应用 public 类型来说明,这样才能在程序任意的位置创建类的实例--对象。
2.5.3 说明
每个类至少有一个构造方法。如果不写一个构造方法,Java 编程语言将提供一个默认的,该构造方法没有参数,而且方法体为空。
注意:如果一个类中已经定义了构造方法则系统不再提供默认的构造方法。
2.6 析构方法
析构方法 finalize 的功能是:当对象被从内存中删除时,该成员方法将会被自动调用。
通常,在析构方法内,你可以填写用来回收对象内部的动态空间的代码。
特别注意:当我们去调用析构方法的时候,并不会引起该对象实例从内存中删除,而是不会起到任何作用。
在 Java 编程里面,一般不需要我们去写析构方法,了解一下就可以了。
2.7 属性
2.7.1 属性是什么
简单点说,属性就是对象所具有的静态属性。
2.7.2:属性定义规则
Java 类中属性的声明采用如下格式:
访问修饰符 修饰符 类型 属性名称=初始值;
访问修饰符:可以使用四种不同的访问修饰符中的一种,包括
public(公共的)、
protected(受保护的),
无修饰符
private(私有的)。
public 访问修饰符表示属性可以从任何其它代码调用。private 表示属性只可以由该类中的其它方法来调用。protected 将在以后的课程中讨论。
修饰符:是对属性特性的描述,例如后面会学习到的:static、final 等等。
类型:属性的数据类型,可以是任意的类型。
属性名称:任何合法标识符
初始值:赋值给属性的初始值。如果不设置,那么会自动进行初始化,基本类型使用缺省值,对象类型自动初始化为 null。
2.7.3:说明
属性有时候也被称为成员变量、实例变量、域,它们经常被互换使用。
2.8 方法
2.8.1:方法是什么
方法就是对象所具有的动态功能。
2.8.2:方法定义规则
Java 类中方法的声明采用以下格式:
访问修饰符 修饰符 返回值类型 方法名称 (参数列表) throws 异常列表 {
方法体
}
访问修饰符:public、protected(受保护的),无修饰符和 private。
修饰符:是对方法特性的描述:static、final、abstract、 synchronized 等等。
返回值类型:表示方法返回值的类型或void(空)。
方法名称:可以是任何合法标识符
参数列表:允许将参数值传递到方法中。列举的元素由逗号分开,而每一个元素包含一个类型和一个标识符。
throws 异常列表:子句导致一个运行时错误(异常)被报告到调用的方法中,以便以合适的方式处理它。
花括号内是方法体,即方法的具体语句序列。
2.8.3:示例
2.8.4:形参和实参
形参:就是形式参数的意思。是在定义方法名的时候使用的参数,用来标识方法接收的参数类型,在调用该方法时传入。
实参:就是实际参数的意思。是在调用方法时传递给该方法的实际参数。
形参和实参有如下基本规则:
(1):形参和实参的类型必须要一致,或者要符合隐含转换规则
(2):形参类型不是引用类型时,在调用该方法时,是按值传递的。在该方法运行时,形参和实参是不同的变量,它们在内存中位于不同的位置,形参将实参的值复制一份,在该方法运行结束的时候形参被释放,而实参内容不会改变。
(3):形参类型是引用类型时,在调用该方法时,是按引用传递的。运行时,传给方法的是实参的地址,在方法体内部使用的也是实参的地址,即使用的就是实参本身对应的内存空间。所以在函数体内部可以改变实参的值。
2.8.5:参数可变的方法
从JDK5.0 开始,提供了参数可变的方法,传入参数的类型必须是一致的,究其本质,就是一个数组。
5.0 以前版本,多个参数放在一个数组或者对象集合中作为参数来传递:
int sum(Integer[] numbers){…}
//在别处调用该方法
sum(new Integer[] {12,13,20});
而在 5.0 版本中可以写为:
int sum(Integer... numbers){//方法内的操作}
注意:方法定义中是三个点
//在别处调用该方法
sum(12,13,20);//正确
sum(10,11); //正确
3. 使用 Java 类
3.1 new 关键字
//有个类:
class MyDate {
int day;
int month;
int year;
}
可以使用变量之前,实际内存必须被分配。通过使用关键字 new 来实现的。
//使用1
MyDate today;
today = new MyDate();
//使用2
或
MyDate today = new MyDate();
使用1:
第一个语句(声明)仅为引用分配了足够的空间,而第二个语句则通过调用对象的构造方法为构成 MyDate 的三个整数分配了空间。对象的赋值使变量 today 重新正确地引用新的对象。这两个操作被完成后,MyDate 对象的内容则可通过 today 进行访问。
关键字 new 意味着内存的分配和初始化,new 调用的方法就是类的构造方法。
使用2:
使用一个语句同时为引用 today 和由引用 today 所指的对象分配空间也是可能的。
3.2:如何使用对象中的属性和方法
要调用对象中的属性和方法,使用“.”操作符。
例如:
today.day = 26;
today.month = 1;
today.year = 2018;
3.3:==this 关键字==
关键字 this 是用来指向当前对象或类实例的,this的用途如下:
3.3.1:点取成员
this.day 指的是调用当前对象的 day 字段,示例如下:
public class MyDate {
private int day, month, year;
public void tomorrow() {
this.day = this.day + 1;
//其他代码
}
}
Java自动将所有实例变量和方法引用与this关键字联系在一起,因此使用关键字在某些情况下是多余的。下面的代码与前面的代码是等同的。
public class MyDate {
private int day, month, year;
public void tomorrow() {
day = day + 1; // 在 day 前面没有使用 this
//其他代码
}
}
3.3.2:区分同名变量
也有关键字 this 使用不多余的情况。如,需要在某些完全分离的类中调用一个方法,并将当前对象的一个引用作为参数传递时。
Birthday bDay = new Birthday (this);
还有一种情况,就是在类属性上定义的变量和方法内部定义的变量相同的时候,到底是调用谁呢?例如:
public class BasicTest {
int i = 2;
public static void main(String[] args) {
BasicTest t = new BasicTest();
t.m();
}
/*
实例变量 i=2
方法内部的变量 i=3
*/
public void m(){
int i = 3; //跟属性的变量名称是相同的
System.out.println("实例变量 i="+ this.i);
System.out.println("方法内部的变量 i="+ i);
}
}
运行结果:
实例变量 i=2
方法内部的变量 i=3
也就是说:“this.变量”调用的是当前属性的变量值,直接使用变量名称调用的是相对距离最近的变量的值。
3.3.3:作为方法名来初始化对象
也就是相当于调用本类的其它构造方法,它必须作为构造方法的第一句。示例如下:
public class Test {
public Test(){
this(3);//在这里调用本类的另外的构造方法
}
public Test(int a){
}
public static void main(String[] args) {
Test t = new Test();
}
}
4. 引用类型
4.1:引用类型是什么
一般引用类型(reference type)指向一个对象,不是原始值,指向对象的变量是引用变量。
在Java 里面除去基本数据类型的其它类型都是引用数据类型。
Java程序运行时,会为引用类型分配一定量的存储空间并解释该存储空间的内容。
示例如下:
public class MyDate{
private int day=8;
private int month=8;
private int year=2008;
public MyDate(int day, int month, int year){
}
public void print(){
}
}
public class TestMyDate{
public static void main(String args[]){
MyDate today = new MyDate(23,7,2008);//这个 today 变量
//就是一个引用类型的变量
}
}
4.2:引用类型的赋值
用类的一个类型声明的变量被指定为引用类型,这是因为它正在引用一个非原始类型,这对赋值具有重要的意义。请看下列代码片段:
int x = 7;
int y = x;
String s = “Hello”;
String t = s;
四个变量被创建:两个原始类型 int 和两个引用类型 String。x 的值是 7,而这个值被复制到 y;x 和 y 是两个独立的变量且其中任何一个的进一步的变化都不对另外一个构成影响。
至于变量 s 和 t,只有一个 String 对象存在, 它包含了文本“Hello”,s 和 t 均引用这个单一的对象。
将变量 t 重新定义为:t=”World”; 则新的对象 World 被创建,而 t 引用这个对象。上述过程被描述如下
4.3:按值传递还是按引用传递
(1):按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
(2):在 Java 里面只有基本类型和按照双引号定义字符串指定String(如:String str = “Java”;) 是按值传递,其它的都是按引用传递
==值传递与引用传递
是java中是没有指针的,java中只存在值传递,只存在值传递!!! 然而我们经常看到对于对象(数组,类,接口)的传递似乎有点像引用传递,可以改变对象中某个属性的值。但是不要被这个假象所蒙蔽,实际上这个传入函数的值是对象引用的拷贝,即传递的是引用的地址值,所以还是按值传递。==
5.变量
5.1:实例变量和局部变量
在方法外定义的变量主要是实例变量,它们是在使用 new Xxxx()创建一个对象时被分配内存空间的。
每当创建一个对象时,系统就为该类的所有实例变量分配存储空间;创建多个对象就有多份实例变量。
通过对象的引用就可以访问实例变量。
在方法内定义的变量或方法的参数被称为局部(local)变量,有时也被用为自动(automatic)、临时(temporary)或栈(stack)变量。
方法参数变量定义在一个方法调用中传送的自变量,每次当方法被调用时,一个新的变量就被创建并且一直存在到程序的运行跳离了该方法。
当执行进入一个方法遇到局部变量的声明语句时,局部变量被创建,当执行离开该方法时,局部变量被取消,也就是该方法结束时局部变量的生命周期也就结束了。
因而,局部变量有时也被引用为“临时或自动”变量。在成员方法内定义的变量对该成员变量是“局部的”,因而,你可以在几个成员方法中使用相同的变量名而代表不同的变量。
该方法的应用如下所示:
public class BasicTest2 {
private int i; // Test类的实例变量
public int firstMethod() {
int j = 1; // 局部变量
//这里能够访问i和j
System.out.println("firstMethod 中 i="+i+",j="+j); return 1;
} // firstMethod()方法结束
public int secondMethod(float f) { //method parameter f
int j = 2; //局部变量,跟firstMethod()方法中的j是不同的
//这个j的范围是限制在secondMethod(),可以同时访问i,j,f
System.out.println("secondMethod 中 i="+i+",j="+j+",f="+f); return 2;
}
/*
firstMethod 中 i=0,j=1
secondMethod 中 i=0,j=2,f=3.0
*/
public static void main(String[] args) {
BasicTest2 t = new BasicTest2();
t.firstMethod();
t.secondMethod(3);
}
}
运行结果:
firstMethod 中 i=0,j=1
secondMethod 中 i=0,j=2,f=3.0
5.2:变量初始化
在 Java 程序中,任何变量都必须经初始化后才能被使用。
当一个对象被创建时,实例变量在分配内存空间时按程序员指定的初始化值赋值,否则系统将按下列默认值进行初始化
类型 | 初始化值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | ‘\u0000’ |
boolean | false |
所有引用类型 | null |
注意 — 一个具有空值“null”的引用不引用任何对象。试图使用它引用的对象将会引起一个异常。异常是出现在运行时的错误,这将在模块“异常”中讨论。
在方法外定义的变量被自动初始化。
*局部变量必须在使用之前做“手工”(由程序员进行)初始化。
如果编译器能够确认一个变量在初始化之前可能被使用的情形,编译器将报错。
public class Test {
private int i; //Test类的实例变量
public void test1() {
int x = (int) (Math.random() * 100);
int y;
int z;
if (x > 50) {
y = 9;
}
z = y + x; // 将会引起错误,因为y可能还没有被初始化就使用了
}
public static void main(String[] args) {
Test t = new Test();
t.test1();
}
}
5.3:变量的范围(scope)
Java 变量的范围有四个级别:类级、对象实例级、方法级、块级
(1):类级变量又称全局级变量,在对象产生之前就已经存在,就是后面会学到的 static 变量。
(2):对象实例级,就是前面学到的属性变量
(3):方法级:就是在方法内部定义的变量,就是前面学到的局部变量。
(4):==块级==:就是定义在一个块内部的变量,变量的生存周期就是这个块,出了这个块就消失了。
示例如下:
public class BasicTest3 {
private static String name = "Java";//类级
private int i; // 对象实例级,BasicTest3类的实例变量
{//属性块,在类初始化属性时候运行
int j = 2;//块级
int jj = 2;//块级
//可以访问类的
System.out.println("block name="+name+",i="+i+",j="+j+",jj="+jj);
}
public void test1() {
int j = 3;//方法级
if (j == 3) {
int k = 5;//块级
}
//这里不能访问块级的变量,块级变量只能在块内部访问
System.out.println("test1 name=" + name + ",i=" + i + ",j=" + j);
//System.out.println("name="+name+",i="+i+",j="+j+",jj="+jj);
}
public static void main(String[] args) {
BasicTest3 t = new BasicTest3();
t.test1();
//只能访问name,i不是静态的,j访问不到
System.out.println("main name=" + name );
//System.out.println("name="+name+",i="+i+",j="+j+",jj="+jj);
}
}
运行:
block name=Java,i=0,j=2,jj=2
test1 name=Java,i=0,j=3
main name=Java
运行结果:
name=Java ,i=0,j=3
5.3.1:==访问说明==
(1):方法内部除了能访问方法级的变量,还可以访问类级和实例级的变量,方法不能访问快中的变量
(2):块内部能够访问类级、实例级变量,如果块被包含在方法内部,它还可以访问方法级的变量。快中的变量不能被方法访问.
(3):变量当然是要在被访问前被定义和初始化,不能访问后面才定义的变量。
6.包装类
虽然 Java 语言是典型的面向对象编程语言,但其中的 8 种基本数据类型并不支持面向对象的编程机制,
基本类型的数据不具备“对象”的特性—-不携带属性、没有方法可调用。沿用它们只是为了迎合人类根深蒂固的习惯,并的确能简单、有效地进行常规数据处理。
这种借助于非面向对象技术的做法有时也会带来不便,比如引用类型数据均继承了 Object 类的特性,要转换为 String 类型(经常有这种需要)时只要简单调用 Object 类中定义 的toString()即可,而基本数据类型转换为 String 类型则要麻烦得多。为解决此类问题,Java语言引入了封装类的概念,在 JDK 中针对各种基本数据类型分别定义相应的引用类型,并称之为包装类(Wrapper Classes)。
下表描述了基本数据类型及对应的包装类
基本数据类型 | 对应的包装类 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
每个包装类的对象可以封装一个相应的基本类型的数据,并提供了其它一些有用的功能。包装类对象一经创建,其内容(所封装的基本类型数据值)不可改变。
7. 类型转换
在赋值的信息可能丢失的地方,编译器需要程序员用类型转换(type cast)的方法确认赋值。Java 中的类型转换分成:强制类型转换、自动升级类型转换和后面将会学习到的向上造型。
7.1:强制类型转换
把某种类型强制转换成另外一种类型就叫做强制类型转换。
long bigValue = 99L;
int squashed = (int)(bigValue);
在上述程序中,期待的目标类型被放置在圆括号中,并被当作表达式的前缀,该表达式必须被更改。一般来讲,建议用圆括号将需要转型的全部表达式封闭。否则,转型操作的优先级可能引起问题。
注意:强制类型转换只能用在原本就是某个类型,但是被表示成了另外一种类型的时候,可以把它强制转换回来。强制转换并不能在任意的类型间进行转换。
比如上面的例子:99 这个数本来就是一个 int 的数,但是它通过在后面添加 L 来表示成了一个 long 型的值,所以它才能够通过强制转换来转换回 int 类型。
7.2:升级和表达式的类型转换
当没有信息丢失时,变量可被自动升级为一个较长的形式(如:int 至 long 的升级)
long bigval = 6; // 6 是 int 类型, OK
int smallval = 99L; // 99L 是 long 型, 非法
double z = 12.414F; // 12.414F 是 float 型, OK
float z1 = 12.414; // 12.414 是 double 型, 非法
一般来讲,如果变量类型至少和表达式类型一样大(位数相同),则你可认为表达式是赋值兼容的。
7.3:表达式的升级类型转换
对 + 运算符来说,当两个操作数是原始数据类型时,其结果至少有一个int,并且有一个通过提升操作数到结果类型,或通过提升结果至一个较宽类型操作数而计算的值,这可能会导致溢出或精度丢失。例如:
short a,b,c
a=1;
b=2;
c= a+b; //出错
上述程序会出错是因为在执行“+”操作前,a 和 b 会从 short 提升至 int,两个 int 相加的结果也是 int,然后把一个 int 的值赋值给 c,但是 c 是 short 型的,所以出错。如果 c 被声明为一个 int,或按如下操作进行类型转换:
c = (short)(a+b);
则上述代码将会成功通过。
尤其在四则运算表达式里面,如果不强制进行类型转换,那么运算最后的结果就是精度最高的那个操作数决定的。
比如:
3*5.0 的结果就是 double 型的,应该定义成为:double a = 3 * 5.0;
7.4:自动包装和解包
自动包装:就是把基础数据类型自动封装并转换成对应的包装类的对象。
自动解包:就是把包装类的对象自动解包并转换成对应的基础数据类型。
示例如下:
public class Test {
public static void main(String args[]) {
Integer a1 = 5;//自动包装
int a2 = new Integer(5);//自动解包
System.out.println("a1="+a1+",a2="+a2);
}
}
运行结果:a1=5,a2=5
8. Java 类的基本运行顺序
8.1 构造方法
public class ExeSequence { //第1行
private String name = "ExeSequenceName"; //第2行,4-1
private int age = 18; //第3行,4-2
public ExeSequence(){ //第4行,3
age = 100; //第5行,5
} //第6行,6
public static void main(String[] args) { //第7行,1
ExeSequence t = new ExeSequence(); //第8行,2
System.out.println(t.name+"的年龄是"+t.age+"岁");//第9行,7
} //第10行,8
} //第11行
//ExeSequenceName的年龄是100岁
/*
运行的基本顺序是:
(1) :先运行到第 7 行,这是程序的入口
(2) :然后运行到第 8 行,这里要 new 一个 ExeSequence,就要调用 ExeSequence 的构造方法
(3) :就运行到第 4 行,注意:可能很多人觉得接下来就应该运行第 5 行了,错!初始化一
个类,必须先初始化它的属性
(4) :因此运行到第 2 行,然后是第 3 行
(5) :属性初始化完过后,才回到构造方法,执行里面的代码,也就是第 5 行
(6) :然后是第 6 行,表示 new 一个 ExeSequence 实例完成
(7) :然后回到 main 方法中执行第 9 行
(8) :然后是第 10 行
*/
```
运行的结果是:Java的年龄是 100
## 8.2 父子类构造方法
public class ExeSequenceChild extends ExeSequenceParent { //第1行
private String name = “childName”; //第2行,8-1
private int age = 10; //第3行,8-2
public ExeSequenceChild() { //第4行,3
age = 50; //第5行,9
} //第6行,10
public static void main(String[] args) { //第7行,1
ExeSequenceChild t = new ExeSequenceChild(); //第8行,2
System.out.println(t.name + “的岁龄是” + t.age + “岁”); //第9行,11
} //第10行,12
} //第11行
class ExeSequenceParent { //第12行
private int num = 30; //第13行,5
public ExeSequenceParent() { //第14行,4
System.out.println(“现在初始化父类”); //第15行,6
} //第16行,7
public void test() { //第17行
System.out.println(“这是父类的test方法”); //第18行
} //第19行
} //第20行
/* 上述类的基本运行顺序是:
(1) :先运行到第 7 行,这是程序的入口
(2) :然后运行到第 8 行,这里要 new 一个 ExeSequenceChild,就要调用 ExeSequenceChild 的构造方法
(3) :就运行到第 4 行,注意:初始化子类必先初始化父类
(4) :要先初始化父类,所以运行到第 14 行
(5) :然后是第 13 行,初始化一个类,必须先初始化它的属性
(6) :然后是第 15 行
(7) :然后是第 16 行,表示父类初始化完成
(8) :然后是回到子类,开始初始化属性,因此运行到第 2 行,然后是第 3 行
(9) :子类属性初始化完过后,才回到子类的构造方法,执行里面的代码,也就是第 5 行
(10) :然后是第 6 行,表示 new 一个 ExeSequenceChild 实例完成
(11) :然后回到 main 方法中执行第 9 行
(12) :然后是第 10 行
运行结果是:
现在初始化父类
childName的岁龄是50岁
*/
## 8.3 父子类构造方法静态代码块
public class ExeSequenceChildAndStatic extends ExeSequenceParent2 { //第1行
static {
System.out.println(“ExeSequenceChildAndStatic static”);
}
private String name = “childName”; //第2行,8-1
private int age = 10; //第3行,8-2
public ExeSequenceChildAndStatic() { //第4行,3
System.out.println(“现在初始化子类构造方法”);
age = 50; //第5行,9
} //第6行,10
public void test() { //第17行
super.test();
System.out.println(“这是子类的test方法”); //第18行
super.test();
}
public static void main(String[] args) { //第7行,1
ExeSequenceChildAndStatic t = new ExeSequenceChildAndStatic(); //第8行,2
System.out.println(t.name + “的岁龄是” + t.age + “岁”); //第9行,11
t.test();
} //第10行,12
} //第11行
class ExeSequenceParent2 { //第12行
static {
System.out.println(“ExeSequenceParent2 static”);
}
private int num = 30; //第13行,5
public ExeSequenceParent2() { //第14行,4
System.out.println(“现在初始化父类构造方法”); //第15行,6
} //第16行,7
public void test() { //第17行
System.out.println(“这是父类的test方法”); //第18行
} //第19行
} //第20行
/* 上述类的基本运行顺序是:
(1) :先运行到第 7 行,这是程序的入口
(2) :然后运行到第 8 行,这里要 new 一个 ExeSequenceChild,就要调用 ExeSequenceChild 的构造方法
(3) :就运行到第 4 行,注意:初始化子类必先初始化父类
(4) :要先初始化父类,所以运行到第 14 行
(5) :然后是第 13 行,初始化一个类,必须先初始化它的属性
(6) :然后是第 15 行
(7) :然后是第 16 行,表示父类初始化完成
(8) :然后是回到子类,开始初始化属性,因此运行到第 2 行,然后是第 3 行
(9) :子类属性初始化完过后,才回到子类的构造方法,执行里面的代码,也就是第 5 行
(10) :然后是第 6 行,表示 new 一个 ExeSequenceChild 实例完成
(11) :然后回到 main 方法中执行第 9 行
(12) :然后是第 10 行
运行结果是:
ExeSequenceParent2 static
ExeSequenceChildAndStatic static
现在初始化父类构造方法
现在初始化子类构造方法
childName的岁龄是50岁
这是父类的test方法
这是子类的test方法
这是父类的test方法
加上静态代码块之后:
会先初始化父类的静态代码快,然后初始化子类的静态代码块,
然后才是父类的构造方法,然后再是子类的构造方法.
*/
“`