继承中构造方法
子类中所有的构造方法默认都会访问父类中空参数的构造方法,子类每一个构造方法的第一条语句默认都是:super();
。因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
父类有无参构造
class Father {
int age;
public Father() {
System.out.println("Father的无参构造方法");
}
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
// super();
System.out.println("Son的无参构造方法");
}
public Son(String name) {
// super(name);
System.out.println("Son的带参构造方法");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son("林青霞");
}
}
输出:
Father的无参构造方法
Son的无参构造方法
------------
Father的无参构造方法
Son的带参构造方法
如果代码20行注释放开,打印“Father的带参构造方法”。不指定父类构造方法时,默认使用父类空参数的构造方法 super(); 。
默认都会访问父类中空参数的构造方法。
注:如果类只有无参构造方法,那类的属性怎么赋值?
实例化类对象时,如果只有无参构造方法,类的属性会被赋默认值。(不管程序有没有显式的初始化,Java 虚拟机都会先自动给它初始化为默认值。)
- Boolean默认 false。
- 整数类型(byte、short、int、long)的基本类型变量的默认值为0。
- 单精度浮点型(float)的基本类型变量的默认值为0.0f。
- 双精度浮点型(double)的基本类型变量的默认值为0.0d。
- 字符型(char)的基本类型变量的默认为 /u0000。
- 引用类型的变量是默认值为 null。
- 数组引用类型的变量的默认值为 null。当数组变量的实例后,如果没有显式的为每个元素赋值,Java 就会把该数组的所有元素初始化为其相应类型的默认值。
父类无无参构造
如果父类没有无参构造方法,那么子类的构造方法就会报错。
class Father {
/* 方法一:父类提供无参构造
public Father() {
System.out.println("Father的无参构造方法");
}
*/
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
// 方法三:子类通过this去调用本类的其他构造方法
this("任意");
System.out.println("Son的无参构造方法");
}
public Son(String name) {
// 方法二:子类使用super关键字去显示的调用父类的带参构造方法
super(name);
System.out.println("Son的带参构造方法");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son("lili");
}
}
解决方案:
- 父类提供无参构造方法
- 子类使用super关键字去显示的调用父类的带参构造方法
- 子类通过this去调用本类的其他构造方法
总之,子类中一定要有一个去访问了父类的构造方法,否则父类数据就没有初始化。
子类重写父类构造方法
如果子类重写了父类的有参构造方法,就必须重写父类的无参构造方法。
class Father {
public Father() {
System.out.println("Father的无参构造方法");
}
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
/*
public Son() {
}
*/
public Son(String name) {
super(name);
System.out.println("Son的带参构造方法");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
//创建对象
Son s = new Son();
System.out.println("------------");
Son s2 = new Son("lili");
}
}
报错:27行报错无法找到对应的构造方法。
解决方法:Son子类编写空的无参构造方法,默认去调用父类空参数构造方法。
子类重写父类成员方法
class Phone {
public void call(String name) {
System.out.println("给"+name+"打电话");
}
}
class NewPhone extends Phone {
public void call(String name) {
super.call(name);
System.out.println("可以听天气预报了");
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
NewPhone np = new NewPhone();
np.call("jack");
}
}
输出:
给jack打电话
可以听天气预报了
当子类需要父类的功能,而子类有自己特有内容时,可以重写父类中的方法。继承重写父类方法,即沿袭了父类的功能,又定义了子类特有的内容。
继承中代码块
class Fu {
static {
System.out.println("静态代码块Fu");
}
{
System.out.println("构造代码块Fu");
}
public Fu() {
System.out.println("构造方法Fu");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("构造代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
class ExtendsTest2 {
public static void main(String[] args) {
Zi z = new Zi();
}
}
输出
静态代码块Fu
静态代码块Zi
构造代码块Fu
构造方法Fu
构造代码块Zi
构造方法Zi
1、子类初始化之前先会进行父类的初始化。
2、静态的内容是随着类的加载而加载,静态代码块的内容会优先执行。
继承中分层初始化
class Y {
Y() {
System.out.println("Y");
}
}
class X {
Y b = new Y();
static {
System.out.println("X 静态代码块");
}
{
System.out.println("X 构造代码块");
}
X() {
System.out.println("X");
}
}
public class Z extends X {
Y y = new Y();
static {
System.out.println("Z 静态代码块");
}
Z() {
//super()
System.out.println("Z");
}
public static void main(String[] args) {
new Z();
}
}
输出:
X 静态代码块
Z 静态代码块
Y
X 构造代码块
X
Y
Z
1、分层初始化:先进行父类初始化,然后进行子类初始化。
2、类的初始化顺序:
父类–静态变量
父类–静态初始化块
子类–静态变量
子类–静态初始化块
子类main方法
父类–属性对象、变量 (执行顺序看代码位置由上到下)
父类–构造代码块
父类–构造函数
子类–属性对象、变量 (执行顺序看代码位置由上到下)
子类–构造代码块
子类–构造函数
总之,静态优先执行,属性优先执行。
注:
构造代码块和属性对象、变量的执行顺序看代码位置。
上述代码将属性对象Y y = new Y();
放在构造代码块下面。Y y = new Y();
放在构造方法下面。
class Y {
Y() {
System.out.println("Y");
}
}
class X {
static {
System.out.println("X 静态代码块");
}
{
System.out.println("X 构造代码块");
}
/* 属性对象放在构造代码块下面*/
Y y = new Y();
X() {
System.out.println("X");
}
}
public class Z extends X {
static {
System.out.println("Z 静态代码块");
}
Z() {
//super()
System.out.println("Z");
}
/* 属性对象放在构造方法下面*/
Y b = new Y();
public static void main(String[] args) {
new Z();
}
}
输出:
X 静态代码块
Z 静态代码块
X 构造代码块
Y
X
Y
Z
结果可见:
1、构造代码块和属性对象、变量的执行顺序看 代码位置。
2、属性对象、变量的执行 优先 构造方法。
构造方法特征
- 构造方法的名称必须与类名相同。
- 构造方法可以带参数。
- 构造方法没有返回值,返回类型不是void。如果在构造函数前面写上void ,那么构造函数就变成无效的了。
- 构造方法不能在方法中用 return 语句返回一个值。
- 当自定义了构造方法后,编译器将不再自动创建不带参数的构造方法 。
- 构造方法访问修饰符和其他方法一样,可以是public protected default private,访问范围也和其他方法一样。比如,protected修饰,其他包访问需要继承;default修饰,只能本包访问。