前言
最近在啃《Java编程思想》,其中有很多东西,以前学过,有些都忘了,于是乎,我就想着捡起来,看到第七章《复用类》,文章中提出通过组合、继承的方式来进行类的复用,当然还可以通过代理(虽然java不直接支持代理)。关于“组合”稍微提下,就是在新的类中产生现有类的对象,通俗点就是将一个的类对象作为新类的成员变量。关于"继承",也稍微提下,就是一个类使用extends 继承父类,其中继承的类具有父类所有非私有方法调用权限,和非私有成员变量的访问权限。关于访问权限分四种,private 、protected、包访问权限、public 权限。private 修饰词修饰变量或者是方法,表示只有当前类才有权限进行变量/方法的访问和修改。这个广泛应用构造函数和类成员变量。这个可以防止某些不正当的错误发生。protected修饰词,表示只有该类和该类的子类才有权限进行访问和修改。不加修饰词,默认就是包访问权限,就是同一个包下的所有类都具有对该变量/方法 权限访问。public 就是同一个项目工程所有的类都具有对该变量/方法权限。
相关概念
demo:
其中这下面的 static{ } 这块就是静态数据块。没有static修饰的{ }包裹的就是构造块。构造函数就是以类名字命名的方法.下面的就是Father()、Father(final Person p),两个构造函数.main方法就是下面的public static void main(String [] args)静态方法,这个是整个程序开始运行的入口.
public static class Father{
private Person p;
{
Person pp=new Person("Father ");
}
Father(){
}
Father(final Person p){
this.p=p;
System.out.println("this is Father Constructor!");
}
static {
Person person=new Person("father",44,"male");
int i=1;
}
public static void main(String[] args) {
Father father = new Father(new Person());
}
}
实验,观察运行顺序.
附上Person类
public class Person {
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
System.out.println("this is Person Constructor with All Arguments ");
}
public Person() {
super();
System.out.println("this is Person Constructor without Arguments!");
}
public Person(String name) {
super();
System.out.println("this is Person Constructor with One Arguments");
}
}
执行Father类的main方法。观看运行顺序.
public static class Father{
private Person p;
{
Person pp=new Person("Father "); // 第三步执行
}
Father(){
}
Father(final Person p){
this.p=p;
System.out.println("this is Father Constructor!"); // 第四步执行
}
static {
Person person=new Person("father",44,"male"); // 第一步执行
int i=1;
}
public static void main(String[] args) {
Father father = new Father(new Person()); //第二步执行
}
}
结果如下:
this is Person Constructor with All Arguments
this is PersonConstructor without Arguments!
this is Person Constructor with OneArguments
this is Father Constructor!
第一步,找到Father类,并运行其中的static数据块,有且只运行一次,后面在调用构造方法创建对象的时候,不会再次运行.
第二步:按照顺序运行mian方法的方法
第三步: 调用构造函数,先去执行构造块,这个构造块会在每次调用构造函数时,都会运行一次。
第四步; 这才是真正的执行构造函数了。
观察在无论创建多少个对象的时候static块只运行一次,以及构造块是不是每次在创建对象的时候,都会运行一次?
这里使用Father类来创建两个对象来观察结果.
public static class Father{
private Person p;
{
Person pp=new Person("Father ");
}
Father(){
}
Father(final Person p){
this.p=p;
System.out.println("this is Father Constructor!");
}
static {
Person person=new Person("father",44,"male");
int i=1;
}
public static void main(String[] args) {
Father father1 = new Father(new Person());
Father father2 = new Father(new Person());
}
}
结果如下:
this is Person Constructor with All Arguments // static数据块运行结果
this is Person Constructor without Arguments! //构造方法中参数运行结果
this is Person Constructor with One Arguments // 构造块运行结果
this is Father Constructor! // 构造函数运行结果
this is Person Constructorwithout Arguments! //构造方法参数运行结果
this is Person Constructor with One Arguments // 构造块运行结果
this is Father Constructor! // 构造函数运行结果
可以清晰的看到 static{ } 静态块中的内容只运行了一次。构造块运行了两次。
带有继承的类的构造函数的运行顺序。
以上讲述的都是单一的类的运行顺序,试想,如果一个带有extends关键字的类,它在运行构造函数创建对象的时候,是怎么运行的呢?这里我们以三级关系为例子,就是父类 、子类、 子类的子类。
demo:
先附上用于测试输出结果的Person类
package thinkingInJava;
public class Person {
private String name;
private int age;
private String sex;
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
System.out.println("this is Person Constructor with All Arguments "+name+" "+age+" "+sex);
}
public Person() {
super();
System.out.println("this is Person Constructor without Arguments!");
}
public Person(String name) {
super();
System.out.println("this is Person Constructor with One Arguments"+" "+name);
}
}
public static class Father{
{
Person pp=new Person("Father");
}
Father(){
}
Father(final Person p){
this.p=p;
System.out.println("this is Father Constructor!");
}
static {
Person person=new Person("father",44,"male");
}
}
public static class Son extends Father{
private final String ST="China";
private static final Person person=new Person("Son",21,"male");
Person p=new Person("Son");
Son(){
super();
}
// 构造方法是隐式的无返回值的静态方法
Son(Person p){
//父级的构造优先于该子类的构造方法执行。因此结果就是先执行父类的构造后在继续执行子类的构造方法。
super(p);
System.out.println("this is Son Constructor");
}
}
// 先去加载该类的所有父类,依次从该类的最高的父类的static静态数据/块依次执行到该类本身。
//调用了构造函数,由于里面调用了父类的构造函数,因此先执行完成父类的构造函数,在执行子类的构造函数.
// 所有的构造函数的运行都是先初始换成员变量或者是构造块。然后执行构造函数本身。
public static class GrandSon extends Son{
// 编译器在开发人员不写默认构造函数的时候,自动会创建一个无参构造函数.
// 注意,一旦开发人员书写了有参构造函数,没有书写无参构造函数,那么在调用无参构造函数来创建对象的时候就会出错!
GrandSon(){
super();
}
Person pp=new Person("GrandSon");
GrandSon(Person p){
super(p);
System.out.println("this is GrandSon Constructor");
}
static Person p=new Person("GrandSon",3,"female");
public static void main(String[] args) {
GrandSon grandSon = new GrandSon(new Person());
}
}
运行GrandSon 的main方法用于观察结果.其中我这里是GrandSon 继承于Son类,Son类继承于Father类。
先看运行结果来进行分析。
this is Person Constructor with All Arguments father 44 male // 父类的static 数据块运行结果
this is Person Constructor with All Arguments Son 21 male // 子类的static 数据块运行结果
this is Person Constructor with All Arguments GrandSon 3 female // 子类的子类的static 数据块运行结果
this is Person Constructor without Arguments! // 构造方法参数运行结果
this is Person Constructor with One Arguments Father // 父类构造块
this is Father Constructor! // 父类构造函数
this is Person Constructor with One Arguments Son // 子类构造块
this is Son Constructor // 子类的构造函数
this is Person Constructor with One Arguments GrandSon // 子类的子类构造块
this is GrandSon Constructor //子类的子类的构造函数
分析:调用构造函数,先去加载该类,如果发现该类有extends关键字,即有继承关系,则会继续向上寻找该类的父类,如果该类的父类也有extends关键字,继续递归寻找,直到找到没有extends的祖类。然后依次按照继承顺序,分别执行类的static数据块。以三级结构为例子,先执行祖父类的static数据块,然后执行父类的static数据块,最后才执行自己的数据块.调用构造函数的时候,如果子类构造调用了父类构造函数,则先去执行父类构造函数,如果父类构造调用父类的父类构造,则继续递归直到不在调用父类递归的构造为止,然后按照祖类 父类 子类的顺序一次执行构造函数。当然执行构造函数的时候,都是先去执行该类的构造块。
总结:
第一步===>加载类,执行类的static数据块【如果该类有父类,则优先执行该类的父类的static数据块】,其中static数据块只会运行一次,无论之后创建多少个类对象。
第二步===>执行main方法中的逻辑。
第三步===>执行构造函数,如果该构造调用父类的构造,则优先执行父类的构造函数,其中调用构造函数时,执行结构块,初始化成员变量。
第四部===>这才到了运行构造函数内部逻辑。
结束语:以上都是个人参考《Java编程思想》实验得出。如果问题,欢迎各位在下面留言,一起交流。