问:Java和JavaScript有什么关系?
答:Java和JavaScript的关系与雷锋和雷峰塔的关系一样的。
雷锋和雷峰塔只是名字相似,但Java和JavaScript就不只是命名上的相似了。JavaScript借鉴了很多Java的思想和语法(同时还有C\C++),Java早时曾被叫做C加加减减,而JavaScript则被称作C减减,可见JavaScript与众多高级语言有很多相似之处。
但是,由于JavaScript是一种基于对象(object-based)的语言而不是严格意义上的面向对象(object-oriented)语言,在进行编程时则与Java有很大区别。在这篇文章里,我将把Java和JavaScript在面向(基于)对象编程方面进行简单的对比。
实例属性(变量)的定义与调用
Java:
public class ClassA {
public int a = 1;
public int b = 2;
}
ClassA obj = new ClassA();
System.out.println(obj.a); //输出:1
System.out.println(obj.b); //输出:2
JavaScript:
function ClassA {
this.a = 1; //第一种:调用this指针
};
ClassA.prototype.b = 2; //第二种:调用prototype(对象原型)
obj = new ClassA();
console.log(obj.a); //输出:1
console.log(obj.b); //输出:2
可以发现,在JavaScript中对象的属性定义有两种方法,其中调用this指针的方法与Java最为相似。
实例方法(函数)的定义与调用
Java:
public class ClassA {
public int a() {
return 1;
}
public int b() {
return 2;
}
}
ClassA obj = new ClassA();
System.out.println(obj.a()); //输出:1
System.out.println(obj.b()); //输出:2
JavaScript:
function ClassA() {
this.a = function() {
return 1;
};
};
ClassA.prototype.b = function() {
return 2;
}
obj = new ClassA();
console.log(obj.a()); //输出:1;
console.log(obj.b()); //输出:2;
方法和属性的定义、使用是非常相似的,没有太多变化。
类属性(静态属性)的定义与调用
Java:
public class ClassA {
public static int A = 1;
}
System.out.println(ClassA.A); //直接调用类,输出:1
ClassA obj = new ClassA();
System.out.println(obj.A); //调用由类产生的实例,输出:1
JavaScript:
function ClassA() {
} //首先初始化对象
ClassA.A = 1; //定义“类变量”(本质是函数的变量)
console.log(Class.A); //直接调用类,输出:1
obj = new ClassA();
// 不能通过用new产生的实例调用类属性
//console.log(obj.A);
在Java中,我们可以通过由类产生的实例调用静态属性,但JavaScript中不允许这样做,这和JavaScript中使用函数表示对象的设计有关。定义类方法
与此类似,不在赘述。
子类的定义与调用
Java:
public class ClassA {
public int x;
public ClassA(int a) {
this.x = a;
} //构造器
public void print() {
System.out.println("x=" + this.x);
}
} //定义类ClassA
public class ClassB extends ClassA {
public int y; //定义新变量
public ClassB(int a, int b) {
super(a); //调用父类的构造器
this.y = b;
} //重写构造器
@Override
public void print() {
super.print(); //调用父类的方法
} //重写父类方法
public int sum() {
return this.x + this.y;
} //定义新方法
} //定义继承于ClassA的类ClassB
ClassA objA = new ClassA(1);
objA.print(); //调用ClassA的实例的print方法,输出:x=1
ClassB objB = new ClassB(2,3);
objB.print(); //调用ClassA的实例的print方法,输出:x=2\ny=3
System.out.println('x+y=' + objB.sum()); //输出:x+y=5
JavaScript:
function ClassA(a) {
this.x = a;
this.print = function() {
console.log('x=' + this.x);
};
} //定义类ClassA
ClassB.prototype = new ClassA(1);
function ClassB(a, b) {
ClassB.prototype.constructor.apply(this, arguments); //调用父类的构造器
this.y = b;
this.print = function() {
ClassB.prototype.print.call(this); //调用父类的方法
console.log('y=' + this.y);
}
this.sum = function () {
return this.x + this.y;
}
} //定义继承于ClassA的类ClassB
var objA = new ClassA(1);
objA.print(); //输出:x=1
var objB = new ClassB(2,3);
objB.print(); //输出:x=2\ny=3
console.log('x+y=' + objB.sum()); //输出:x+y=5
JavaScript中类的继承比较复杂,这是因为JavaScript是基于对象的语言而不是面向对象的,在JavaScript中并没有“类”的概念,一切皆是函数。在这里我只是采用了一种最为通用的子类使用方法,除此之外还有很多方法,这篇文章的最后链接了很多解决方案。
最后,我在node.js的源码中找到了util.inherits的定义,如果使用node.js环境开发可进行调用。
/**
* Inherit the prototype methods from one constructor into another.
*
* The Function.prototype.inherits from lang.js rewritten as a standalone
* function (not on Function.prototype). NOTE: If this file is to be loaded
* during bootstrapping this function needs to be revritten using some native
* functions as prototype setup using normal JavaScript does not work as
* expected during bootstrapping (see mirror.js in r114903).
*
* @param {function} ctor Constructor function which needs to inherit the
* prototype.
* @param {function} superCtor Constructor function to inherit prototype from.
*/
exports.inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
我只是来留言的 表示什么都看不懂
public ClassB(int a, int b) {
super(a); //调用父类的构造器
this.y = b;
} //重写构造器
重写构造器?这句值得商榷,构造器不能被重写吧。
构造器可以
重写,而且重写之后不能通过父类的构造器构造了,也就是说ClassB必须使用两个参数的构造器进行构造。不对吧 。子类不能重写父类的构造方法。
public class A {
public A() {
System.out.println(“A”);
}
}
public class B extends A {
public B() {
System.out.println(“B”);
}
public static void main(String[] args) {
B b = new B();
}
}
最后打印的结果是:A,B 说明先实例化父类 然后再实例化子类的
如您所说,“重写”这个词用得确实不是很恰当,更为恰当的说法是“重载”。但Java的构造函数重载非常复杂,一般情况下如果不显式的调用父类的构造函数,则Java会自己调用。更特别的,如果没有显式调用父类的构造函数,当父类“重载”了无参数的默认构造函数时候,Java会去调用super();当父类没有“重载”无参数的默认构造函数,而是“重载”了其他形式比如super(int),子类会产生一个编译错误:Implicit super constructor ClassFather() is undefined. Must explicitly invoke another constructor。
所以,您说得对。
Java和JavaScript对重写、重载构造方法的处理不是完全一样的,我在写的时候更多的是按照更为熟悉的Java的思路写的,私以为Java思路更为严谨,您觉着呢?
的确是这样的 。
跟楼主讨论,我也发现了很多以前没有想过问题。楼主功力深厚。