学习java也有两年了,对一些基础还是理解的不够深,上网一搜很的确有不少这样的文章。下面就整理下以免以后忘记了。(理解:java 构造方法不等于创建对象而是初始化对象,new 关键字分配内存和创建对象)如理解有误的话,还请指点!
一、这个文章理解的还是比较有趣的。(转)
http://zangxt.iteye.com/blog/472238
http://shukuiyan.iteye.com/blog/1007808
关于java的构造方法有几个简单的问题:
1.构造方法有返回值吗?
没有。构造方法没有任何返回类型,也不允许是void。比如:
Java代码
public class Test {
//这不是构造函数!
public void Test() {
System.out.println("void Test()");
}
public static void main(String[] args) {
Test test = new Test();
test.Test();
}
}
这里我们定义了一个返回类型为void的Test()方法,有了返回类型void,它就不是构造方法了。
Test test = new Test();
有人用上面的表达式来说明构造方法返回对象引用,这是明显错误的。new关键字有两个作用。一是分配内存,
创建对象。二是调用构造方法,完成对象的初始化工作。完成这两步之后,才算创建了一个完整的Java对象。我们
可以用Test test = new Test();的反编译代码来说明这个问题:
0: new #5; //class Test
3: dup
4: invokespecial #6; //Method "":()V
7: astore_1
原表达式被编译成四条bytecode指令。
new指令负责根据参数分配内存并创建Test对象,然后将新创建对象的引用置于栈顶。
dup指令复制栈顶的内容,记住,此时栈最顶端的两个单元都是新创建对象的引用。
接着是调用初始化方法,该方法是由构造方法编译而来,栈顶的引用作为此方法的参数消耗了。通过调用初始化方法完成
对象的创建过程。这里注意一下初始化方法Method "":()V,它是void类型的。
最后的astore_1指令将栈顶的对象引用赋给局部变量(前面说了,dup之后栈顶两个引用,一个给初始化方法吃掉了,一个留给astore_1操作用),也就是执行赋值操作。
因此,得到的引用是new指令的结果,不是构造方法的返回值。
有一点需要注意:new指令创建对象,同时使对象的各个字段得到其默认值,比如整数为0,浮点数为0.0,引用为null,boolean为false等。也就是说在构造方法执行之前,各个字段都有默认值了。这一点我们在第三条继续说明。
通过上面说明,我们明确了构造方法的职能(初始化new指令创建的对象,得到一个状态合法的对象,完成对象的
创建过程)。任何类都有构造方法,但是new指令只能创建非抽象类的对象。理解了这一点,也就不要再问"抽象类也有构造方法,为什么不能创建对象"之类的问题了。
2.构造方法是静态的?
错误。
这是《Thinking In Java》中的一个观点。书里有一段:
Even though it doesn't explicitly use the static keyword, the constructor is actually a static method. So the first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath.
《java编程思想》中文第四版96页:
总结一下对象的创建过程,假设有个名为Dog的类:
1.即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成
是静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件。
这里我并没有看明白作者为什么说构造器实际上是静态方法。但是我们知道,静态方法是不能使用this的。因此,"构造器实际上也是静态方法"这点很好否定。看下面例子:
Java代码
public class Test {
public Test() {
this.test2();
}
public static void test(){
this.test2();
}
public static void test2(){
}
}
test方法编译错误,因为静态方法中不能使用非静态的this,而构造方法使用this是没有问题的。
如果有C++经验的话,可以类比一下。C++里的new操作符有两个作用,调用operator new()来分配内存,然后调用构造函数来完成初始化。
而这里的operator new()是隐式静态的。参考《C++程序设计语言(特别版)》中文版的
比如这个例子:
Cpp代码
class Employee{
//...
public:
//....
void* operator new(size_t);
void operator delete(void* ,size_t);
}
成员operator new()和operator delete()默认为static成员,因为它们没有this指针,也不会修改任何对象。它们将提供一些存储,供构造函数去初始化,而后由析构函数去清理。
类比可知,静态的是负责分配内存的工具,而不是构造函数。 不知道《Thinking In Java》的作者是不是把这点弄混了?
3.父类的构造方法中调用被子类重写的方法有多态现象。
这句话很绕,直接看例子:
Java代码
class Father{
private int i = 5;
public Father() {
System.out.println("Father's i is " + this.i);
test();
}
public void test(){
System.out.println(this.i);
}
}
class Son extends Father{
private int i = 55;
public Son() {
System.out.println("Son's i is " + this.i);
}
@Override
public void test() {
System.out.println(this.i);
}
}
public class Test {
public static void main(String[] args) {
new Son();
}
}
结果是:
Father's i is 5
0
Son's i is 55
结合第一点,构造方法调用之前,首先是new指令创建了一个对象,并将各个成员初始化为其默认值。下面看构造方法的调用过程。
子类构造方法会调用父类构造方法,父类构造方法首先打印Father's i is 5。然后调用test()方法,注意,我们创建的是Son类的对象,所以test()方法调用的是Son类定义的test()方法,也就是说发生了多态。我们再去看Son类中test方法的实现,就是简单的输出this.i,为什么是0呢,别忘了我们还没有执行子类的构造方法啊,所以此时子类的i还是new指令初始化得到的0。好,test()方法执行完了,总算回到子类构造方法继续执行,先把i赋值为55,下面的输出语句Son's i is 55也就不难理解了。
在构造方法中调用方法要特别注意这种多态现象。
这种现象和c++里的现象是不同的。在C++中,如果在父类的构造函数中调用虚方法的话,调用的是父类定义的版本,不会发生多态现象。但一个有趣的现象是,C++的经典书籍和Java的经典书籍竟然都建议不要在构造方法里面调用多态方法,因为现象并不是你期待的!这就奇怪了,难道C++程序员和Java程序员天生就有相反的期待吗?
分享到:
相关推荐
答案是: 抽象类中允许有自己的构造方法,但是该构造方法并不能直接实例化自己的对象. 如果在抽象类中存在有参构造方法,则必须在子类中明确的使用super([参数列表])指明要调用父类中的哪个构造方法. 这里举例如下: ...
JAVA理论知识基础复习 构造方法 new关键字 方法的重载 特殊变量this 关键字static 定义常量 继承 方法的重写 特殊变量super 多态性 等相关知识点总结
2.构造器也是方法,满足方法的各种特征,无参数的构造器称为默认构造器,默认构造方法或者无参构造器。在没有其他构造方法的时候可以省略不写 需要注意的是,构造器虽然是方法但是它没有返回值。 3.使用构造器...
类名 变量 = 当前类的对象(new 构造方法) 父类 变量 = 子类的对象(new 构造方法) 接口 变量 = 实现类的对象(new 构造方法) (6)编写类的步骤和创建对象的方式 1.创建类 2.编写成员变量 3.生成构造方法(建议:无...
super:超类,用于调用父类的构造方法或方法。 switch:开关,用于switch语句中的不同情况。 synchronized:同步,用于实现线程同步的方法。 this:这个,用于引用当前对象。 throw:抛出,用于抛出一个异常。
} // 使用这个构造方法创建对象,要写成C c = new C(a); a是A的对象。 11、异常类 JAVA中除了RunTimeException 类,其他异常均须捕获或抛出。 本文来自CSDN博客,转载请标明出处:...
//创建对象需要调用类的构造方法 构造方法:类名() 特点:方法名和类名相同,没有返回值类型 public Person(){ private String name; public void setName(String name){ this.name = name; } } public ...
肯定是为了满足自己想要完成某些行为的语句,但是又用了super继承父类的构造方法。 那么以前所做的修改就都回到以前了,就是说又成了父类的构造方法了。 2. 在Java中,有时还会遇到子类中的成员变量或方法与...
new关键字的作用 缺省构造方法 方法的重载(overload) 特殊变量this 关键字static 常量的定义 类的继承 方法的覆盖(override) 特殊变量super 多态性 面向对象的程序设计
1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。 2. 在该类内部创建一个唯一的对象 3. 定义一个静态方法返回这个唯一对象。 #### 例设计模式的类型 根据实例化对象的时机单例设计模式又分为...
这可以通过使用new关键字和类构造函数来完成。 java Person person1 = new Person("John Doe", 30); 访问对象属性(Attributes):一旦对象被创建,你就可以通过使用.操作符来访问和修改对象的属性。 java System.ou
new 调用对象的构造方法,返 回对该对象的一个引用(即该对象所在的内存地址)。用 new 可以为一个类实例化, 多个不同的对象。这些对象分别占用不同的内存空间,因此改变其中一个对象的状 态不会影响其它对象的状态 。...
抽象类中有构造方法,但是没有抽象的构造方法。构造方法的存在完全是为了继承关系而存在。 与之形成对比的就是接口,接口中的所有方法要求定义成公开抽象方法,因为这些方法所表示的就是标准,标准的信息必须做到...
06 掌握构造方法(隐式、显式)的定义和使用,以及构造方法重载的概念。 07 掌握this关键字的使用。 08 了解基本数据类型变量和引用数据类型变量在数值传递上的区别。 《Java程序设计案例教程》教学课件05深入Java...
但不可用void 声明 C) Java 语言规定构造方法不可重载 D) Java 语言规定构造方法只能通过new自动调用 2、作为Java应用程序入口的main方法,其声明格式可以是( B ) A) public void main(String [] args) B) public ...
//将move定义为抽象方法 抽象方法目的就是为了实现一个接口多种方法的原理,即所有的子类对外都呈现一个相同名字的方法,抽象方法必须被重写,且构造方法、类方法不能声明为抽象方法。 面向对象程序设计与实践-Java...
欲构造ArrayList类的一个实例,此类继承了List接口,下列哪个方法是正确的 ? B A ArrayList myList=new Object(); B List myList=new ArrayList(); C ArrayList myList=new List(); D List ...
D) 该程序无法通过编译,因为在Test类中的构造方法被写成私有,那么Test t=new Test() 将无法找到合适的构造方法。 题目12:a 程序如下: class Test { private int day; private Test(int d) { day=d; } ...
除了使用new关键字创建对象意外,试列举另外三种以上创建实例的方式? 37.classloader中,JDK的API、Classpath中的同web-inf中的class加载方式有什么区别? 38.列举三种以上垃圾回收算法,并比较其优缺点? 39....
2.Java程序里,创建新的类对象用关键字new,回收无用的类对象使用关键字free. ( × ) 3.Java有垃圾回收机制,内存回收程序可在指定的时间释放内存对象. ( × ) 4.构造函数用于创建类的实例对象,构造函数名...