类的初始化顺序是老生长谈的问题。但是它仍然是面试中常见的,也可以说是不可缺少的题目。因为在项目开发过程中,经常会因为这个出现BUG,且这样的BUG一般很难查。
从C语言转用java语言的人常常会这样写代码:
public class Sample {
private String str = null; // str = "";
private boolean flag = false;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
// Other code
// ........
}
他们总是喜欢在声明的时候初始化,我在学java之初,也有人告诉我这样写,说这是好习惯。后面的工作中,发现这是很大的错误,我们的项目中,因为这种写法,出现了很多BUG。看一下下面这个简单的例子:
class A {
public A() {
initAttrs();
}
public void initAttrs() {
System.out.println("initAttrs in A");
}
}
class B extends A {
public String s = null;
public B() {
super();
}
public void initAttrs() {
s = "aaa";
}
}
public class InheritProblem {
public static void main(String[] args) {
B b = new B();
System.out.println(b.s);
}
}
在这段代码,其实是一个小的框架思想,initAttrs方法是用来初始化成员属性用的,为什么不用B类的构造方法去做呢?因为如果有很多初始化的事情要做,全部都放在构造器中做,就会显得很复杂,不利于维护。团队开发时,也不太方便。且如果放在构造器中来写,子类继承时,将不能重写其代码。写成这样,子类仅需要关注自己要初始化的东西就好了。
这段代码运行的结果出乎一般人的意料。大多数人会回答结果是:
aaa
但是结果却是:
null
为什么会这样呢?这就需要细说一下类的初始化顺序和方法的重写了。
先从简单的讲起。方法的多态有两种,一种是重载,一种是重写。重载没有什么好说的,两个方法有一样的名字,参数和返回值有些差异(其实这里面有点小小的问题,我会在别的文章再说)。重写就不一样了,重写是方法的参数必须是一样的,但返回值可以不一样,子类方法的返回值要和父类方法返回值一样或是其子类,重写必须存在于父类和子类之间,子类重写父类的方法。这时,用子类对象去访问该方法,永远调用到的是子类的方法,即使是强行转化为父类的引用,就像父类的方法不存在一样。看下面的例子:
class A {
public String str;
public A() {
str = "a";
}
public String getAStr() {
return str;
}
public String print() {
return "A Print";
}
}
class B extends A {
public String str;
public B() {
str = "b";
}
public String getBStr() {
return str;
}
public String print() {
return "B Print";
}
}
public class InheritProblem {
public static void main(String[] args) {
A a = new B();
System.out.println("a.str=" + a.str);
System.out.println("a.getAStr()=" + a.getAStr());
System.out.println("a.print()=" + a.print());
System.out.println("--------------------------------");
B b = new B();
System.out.println("b.str=" + b.str);
System.out.println("b.getAStr()=" + b.getAStr());
System.out.println("b.getBStr()=" + b.getBStr());
System.out.println("b.print()=" + b.print());
System.out.println("--------------------------------");
A c = b;
System.out.println("c.str=" + c.str);
System.out.println("c.getAStr()=" + c.getAStr());
System.out.println("c.print()=" + c.print());
System.out.println("--------------------------------");
}
}
未完,待续
分享到:
相关推荐
在Java编程语言中,理解成员变量的初始化顺序对于正确地设计和实现类结构至关重要。通过本实验,我们将深入了解成员变量的初始化顺序,以及如何通过实际代码示例来验证这些概念。 #### 实验步骤解析 ##### 步骤一...
Java 类的初始化顺序是一个关键的编程概念,尤其对于理解和避免潜在的运行时错误至关重要。在 Java 中,类的元素初始化顺序遵循以下规则: 1. **静态变量与静态初始化块**: 首先,Java 解释器会执行类中的静态...
"子类--构造器"); } public static void main(String[] args) { new SubClass(); } } 当我们运行这段代码时,会看到以下输出...所以,对于Java程序员和工程师来说,掌握类的初始化顺序是非常基础且重要的知识点。
多态在构造方法中的体现较少,但重要的是理解,尽管构造方法不能被重写,它们可以被子类覆盖以初始化特定状态,从而支持更复杂的构造逻辑。 #### 六、利用继承设计:纯粹继承与扩展 继承不仅是代码重用的一种手段...
子类构造方法是一个特殊的方法,用于初始化新创建的对象。在创建子类对象时,会先调用父类的构造方法,然后执行子类的构造方法。这可以通过使用`super`关键字来完成,`super()`或者`super(参数列表)`用于调用父类的...
### 继承与多态知识点总结 ...总结而言,接口和抽象类都是为了提高代码的可重用性和可扩展性而设计的,它们共同支撑起了Java面向对象编程中的多态特性。正确理解和使用这些概念对于开发高质量的Java应用程序至关重要。
因为构造器是在对象初始化时被调用的,所以构造器内部的多态方法的行为可以影响对象的初始化和行为。例如,在基类的构造器内部调用某个动态绑定的方法,该方法是被导出类覆盖的,那么这便可能产生难以预料的后果,...
4. **成员变量初始化**:尽管在类声明时已经初始化了`name`和`age`,但在构造器中`age`被重新赋值。 5. **静态方法执行**:返回到`main`方法,在第9行,打印出`name`和`age`的值。 6. **输出结果**:最终输出为`Java...
此外,`super`还可以在子类构造方法中调用父类的构造方法,确保初始化顺序正确。 总的来说,封装、继承和多态是Java面向对象编程的基石,它们帮助我们构建模块化、可扩展和易于维护的代码。通过封装,我们可以保护...
这意味着一个类可以有多个构造函数,每个构造函数用于初始化对象的不同方式。 7. 编译时多态与运行时多态 编译时多态主要是通过方法的重载实现,Java在编译期间就能确定调用哪个重载方法。而运行时多态体现在方法...
这些问题展示了Java中继承与多态的基本概念,包括类的继承、构造器的调用顺序、方法的重写、访问控制和静态关键字的使用等。了解这些基础知识对于编写健壮的Java程序至关重要。在实际开发中,我们需要熟练运用这些...
子类构造器可以通过`super`关键字调用父类的构造函数,确保初始化过程正确进行。 6. **内部类与匿名类** 内部类是定义在另一个类内部的类,可以是静态的或非静态的。非静态内部类可以访问外部类的所有成员,包括...
它包括了从类的初始化顺序、String对象的创建数量、变量的覆盖、final关键字、传值和传引用的区别、字符串的处理、日期和时间的处理、基本类型的细节、继承和多态、多线程以及运算符相关的面试题目。下面将详细解析...
- 3.3 构造方法:构造方法不参与多态,构造过程遵循“先父后子”的顺序,先执行父类构造器,然后执行子类构造器的其他代码,完成子类成员变量的初始化。 - 3.4 静态成员:静态成员属于类,不参与多态,多态调用时...
总的来说,Java类的构造方式是一个涉及内存分配、初始化、继承和多态的重要主题。理解和掌握这一过程对于编写高效、可靠的Java代码至关重要。通过实例和字节码分析,程序员能够更好地理解类实例是如何被构建和初始化...
Java 学习路线基础篇!...5. 抽象类和接口:了解什么是抽象类和接口,掌握抽象类和接口的定义格式、静态初始化、动态初始化。 通过掌握这些基础知识,你将轻松精通 Java,拥有基础牢固的编程能力。
类的初始化 成员初始化 构造器初始化初始化顺序 数组初始化 对象的销毁 对象作用域 this 和 super 访问控制权限继承 多态组合代理 向上转型static final 接口和抽象类接口 抽象类异常 认 识 Exception 什么是 ...
- **初始化顺序**:Java对象的初始化有三种方式:构造器、初始化块(实例初始化块和静态初始化块)以及定义变量时的初始值。初始化顺序遵循以下规则: - 首先执行静态初始化块,只在类第一次加载时运行一次。 - ...
`static`关键字用于修饰成员变量和方法,使其成为类变量和类方法,不依赖于实例即可访问。静态代码块只在类加载时执行一次,常用于初始化静态变量。 `final`关键字用于声明常量或防止修改,它可以修饰类、方法、...