前言——
今天是军训的第5天,昨天打了一天的军体拳(哈哈哈,好好看本篇博文,不然博主一套军体拳下去你可能会残疾 :)
明天是最后一天了,今晚继续做自己手头上的事,避免碌碌无为。今天讲解的内容——类和继承,也是跟上一篇一样,基础且重要。
第5天没把这篇文章写完,今天第六天,早上考了军体拳,下午考队列了,趁午休时间赶完这篇文章,晚上可能要去看戏曲表演,不过感觉看戏曲好无聊。。。
欢迎转载,转载请注明来处。
目录
Java中的类
a.Java的类文件
Java中的类是一个.java后缀的class文件。一个class文件最多只允许有一个public类,如果含有public类的话,那么这个class文件名就必须要和public类的类名一致;如果这个类中没有public类,都是包访问权限的类的话,那么class文件名可以随意。如下图所示:
b.类的加载
当我们要用到某个类,或者不严谨地说,当我们new一个类的对象时,如果代码中还未曾使用过该类,那么应该先进行该类的加载,然后再生成对象。类的加载会加载该类中的static变量或者方法,或者static代码块。当类被加载过后,第二次用到该类时就无需再加载了,可以直接生成对象。
如下:
这边Test4类的main方法中第一次用到Code类,所以要先加载Code类,所以会先加载static成员coderName,然后加载static代码块,这边会发现"Loading Jian"只打印一次。
运行结果如下:
loading Jian
I am going to code
c.生成对象时发生了什么
1.首先Java会对类中每个成员进行一个初始化,如果成员变量在定义时没有进行显示地初始化,那么会被默认初始化。
对于 char、short、byte、int、long、float、double等基本数据类型的变量来说会默认初始化为0(boolean变量默认会被初始化为false);对应引用类型的变量,则会被赋值为null。
这时在Code类当中,coderName这个成员没有被显示初始化,由于其是属于引用类型的变量,所以被默认初始化为null。
2.执行构造函数。如果这个类中没有自定义构造函数,那么编译器会自动生成无参构造函数;如果自定义了构造函数,那么编译器就不会自动生成无参构造函数。
会发现,Code类中自定义了Code(String name)的构造函数后,在Test4的main方法中调用无参构造函数时会报错,此时找不到对应的无参构造函数。
二.继承
在Java中用extends关键字表示继承,而且只允许单继承。一个子类只能继承一个父类,但是一个父类却可以被多个子类继承。
a.子类继承父类的哪些东西?
1.肯定能继承父类的public和protected变量和方法。
2.肯定无法继承父类的private变量和方法。
3.对于父类的包访问权限的变量和方法,如果子类和父类在同一个包下,则子类可以继承,否则子类不能继承。
如下:
在这边Bird类是Pet类的子类,但是二者不在同一个包当中,而父类Pet 中的getType()方法是一个包访问权限的方法,很明显,此时Bird类是没有继承这个getType()方法的。
4.对于子类可以继承的变量和方法,如果子类内部出现了同名的变量或者方法就会发生覆盖。此时如果在子类中想访问父类中的变量或者方法的话,应该用super关键字。
运行结果:
In Bird
In Pet
b.super关键字
有两种用法。
1.super.父类的成员/父类的方法
2.super(参数), 这个语句放在子类的构造函数中的第一行;除了一定得放在第一行之外,这个语句只能在子类的构造函数中出现一次。
在这边,父类由于自定义了构造函数,导致了编译器不会自动添加无参构造函数,父类的构造函数又不能被继承,所以子类的无参的构造函数必须显示地添加super(参数)语句,这样才能调用父类的构造函数。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,因为会自动调用无参构造函数。
三.初始化一个子类对象时,发生了啥
这是从其他博客看到的一个笔试题,感觉特别不错。
public class Test {
public static void main(String[] args) {
new Circle();
}
}
class Draw {
public Draw(String type) {
System.out.println(type+" draw constructor");
}
}
class Shape {
private Draw draw = new Draw("shape");
public Shape(){
System.out.println("shape constructor");
}
}
class Circle extends Shape {
private Draw draw = new Draw("circle");
public Circle() {
System.out.println("circle constructor");
}
}
上面这段代码的运行结果是啥?
shape draw constructor
shape constructor
circle draw constructor
circle constructor
在这边,主要考察生成子类对象时构造函数和初始的顺序。可以理解为先生成父类部分,再生成子类部分。
父类部分是先初始化父类成员,再调用父类的构造函数;
子类部分是先初始化子类成员,再调用子类的构造函数。