对象间相互请求或相互协作的途径。
对象接收它能识别的消息
拒绝它不能识别的消息
对象不可能在不可预知或事先不允许的方式下与其他对象交互
考虑对象A向对象B发送消息,也可以看成对象A向对象B请求服务
- 类型的检查是在运行时做的;
- 一般在变量使用之前不需要声明变量类型,而变量的类型通常是由被赋的值的类型决定。
- 优点是方便阅读,不需要写非常多的类型相关的代码
- 缺点是不方便调试,命名不规范时会造成读不懂,不利于理解等
- 如php、Python和Ruby。
面向对象静态
C++,Delphi pascal,Eiffel,Java
面向对象动态
Objective-c,Smalltalk,Dylan,Python
非面向对象静态
Ada,Algol,C,Fortran,Haskell,ML,Modula
非面向对象动态
APL,Forth,Lisp,Prolog,Snobol
动态类型语言与静态类型语言之间的差异在于变量或数值是否具备类型这种特性。
Java, C++: this
Eiffel: Current
Smalltalk, object-c: self
伪变量在使用时就好像是作为类的一个实例。
在一个类的方法或构造方法内部,可以使用“this.成员变量名”这样的格式来引用成员变量名,有些时候可以省略,有些时候不能省略
public class ReferenceVariable {
private int a;
public ReferenceVariable(int a){
this.a = a;
}
public int getA(){
return a;
}
public void setA(int a){
this.a = a;
}
}
java语言规定当变量作用范围重叠时,作用域小的变量覆盖作用域大的变量。所以在构造方法和setA方法内部,参数a起作用。
这样如果访问成员变量a则必须使用this进行引用。
public class ThisTest {
private int i=0;
// 第一个构造器:有一个String型形参
ThisTest(String s){
System.out.println("String constructor: "+s);
}
// 第二个构造器:有一个int型形参和一个String型形参
ThisTest(int i,String s){
this(s);//this调用第一个构造器
this.i=i++;//this以引用该类的成员变量
System.out.println("Int constructor: "+i+"/n"+"String constructor: "+s);
}
注意:
- 其他任何方法都不能调用构造器,只有构造方法能调用它。
- 就算是构造方法调用构造器,也必须位于其第一行,构造方法也只能调用一个且仅一次构造器!
public class ThisTest {
private int i=0;
//第一个构造器:有一个int型形参
ThisTest(int i){
this.i=i+1;//此时this表示引用成员变量i,而非函数参数i
System.out.println("Int constructor i——this.i: "+i+"——"+this.i);
System.out.println("i-1:"+(i-1)+"this.i+1:"+(this.i+1));
//从两个输出结果充分证明了i和this.i是不一样的!
}
public ThisTest increment(){
this.i++;
return this;//返回的是当前的对象,该对象属于(ThisTest)
}
public static void main(String[] args){
ThisTest t0=new ThisTest(10);
System.out.println(t0.increment().increment().increment().i);
//t0.increment()返回一个在t0基础上i++的ThisTest对象,
//接着又返回在上面返回的对象基础上i++的ThisTest对象!
}
}
Java:构造函数中,使用this区分参数和数据成员。
对象=状态(实例变量)+行为(方法)
public class ClassDemo02 {
public static void main(String args[]){
Person per = new Person() ;
}
}
public class ClassDemo03 {
public static void main(String args[]){
Person per = new Person() ;
per.name = "张三" ; // 为属性赋值
per.age = 30 ;
per.tell() ; // 调用类中的方法
}
}
public class ClassDemo04 {
public static void main(String args[]) {
Person per1 = null; // 声明per1对象
Person per2 = null; // 声明per2对象
per1 = new Person(); // 实例化per1对象
per2 = new Person(); // 实例化per2对象
per1.name = "张三"; // 设置per1对象的name属性内容
per1.age = 30; // 设置per1对象的age属性内容
per2.name = "李四"; // 设置per2对象的name属性内容
per2.age = 33; // 设置per2对象的age属性内容
System.out.print("per1对象中的内容 --> ") ;
per1.tell(); // per1调用方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell(); // per2调用方法
}
}
public class ClassDemo05 {
public static void main(String args[]) {
Person per1 = null; // 声明per1对象
Person per2 = null; // 声明per2对象
per1 = new Person(); // 只实例化per1一个对象
per2 = per1 ;// 把per1的堆内存空间使用权给per2
per1.name = "张三";// 设置per1对象的name属性内容
per1.age = 30; // 设置per1对象的age属性内容
// 设置per2对象的内容,实际上就是设置per1对象的内容
per2.age = 33;
System.out.print("per1对象中的内容 --> ") ;
per1.tell(); // 调用类中的方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell();
}
}
PlayingCard aCard = new PlayingCard(Diamond, 3);
C++:结合
对象使用缺省构造函数来初始化。
数组由对象组成,每个对象则使用缺省(即无参数)构造函数来进行初始化
PlayingCard *cardArray[52];
Java:new仅创建数组。数组包含的对象必须独立创建。
PlayingCard cardArray[ ] = new PlayingCard[13];
for (int i = 0; i < 13; i++)
cardArray[i] = new PlayingCard(Spade,i+1);
Java无指针?
对于堆分配的变量值,只要存在对它的引用,就会一直存在,因此,变量值的生存期一般长于创建该变量过程的生存期。
public class ClassDemo06 {
public static void main(String args[]) {
Person per1 = null; // 声明一个per1对象
Person per2 = null; // 声明一个per2对象
per1 = new Person(); // 实例化per1对象
per2 = new Person() ; // 实例化per2对象
per1.name = "张三"; // 设置per1的name属性内容
per1.age = 30; // 设置per1的age属性内容
per2.name = "李四" ; // 设置per2的name属性内容
per2.age = 33 ; // 设置per2的age属性内容
per2 = per1 ; // 将per1的引用传递给per2
System.out.print("per1对象中的内容 --> ") ;
per1.tell(); // 调用类中的方法
System.out.print("per2对象中的内容 --> ") ;
per2.tell();
}
}
C++:delete
Object Pascal:free
付出额外代价:
被一个类的所有实例共享的公共数据字段。
Java和C++使用修饰符static创建共享数据字段。
Java:静态数据字段的初始化是在加载类时,执行静态块来完成。
利用static关键字修饰的数据成员称为静态数据成员/静态数据字段/静态变量/类成员,也称为静态成员(全局成员)。
static 数据类型 数据成员名;
类名.静态成员;
static不能修饰局部变量!
利用static关键字修饰的成员方法为类方法/静态成员方法,类方法可以由类直接调用。
静态方法与非静态方法、静态数据成员与非静态数据成员之间的调用关系:
结论:静态成员不需要实例化就存在,而非静态成员是实例化后才有的成员,在没有实例化之前非静态成员并不存在。因此可以利用仅仅在某一时刻存在的对象访问普遍存在的对象;而不能用一个普遍存在的对象访问仅仅在某一时刻存在的对象。
对象本身不对共享字段初始化。内存管理器自动将共享数据初始化为某特定值,每个实例去测试该特定值。第一个进行测试的做初始化。
在类内可以访问所有的static数据成员,采用直接访问方式。
当创建和初始化分离时(当使用没有构造函数的编程语言时),程序员在创建新对象之后,很容易忘记调用初始化例程,这样通常会导致不良后果
class Complex { // complex numbers
public Complex (double rv) { realPart = rv; }
public double realPart = 0.0; // initialize data areas
public double imagPart = 0.0; // to zero
}
在构造函数的声明中一定要牢记以下几点:
class Person {
public Person(){ // 声明构造方法
System.out.println("一个新的Person对象产生。") ;
}
}
public class ConsDemo01 {
public static void main(String args[]) {
System.out.println("声明对象:Person per = null ;") ;
Person per = null ;// 声明对象时不调用构造方法
System.out.println("实例化对象:per = new Person() ;") ;
per = new Person();// 实例化对象时调用构造方法
}
}
每次在创建新的对象实例时,会强制执行一次来构造方法
在子类中调用父类的构造方法
缺省构造方法:没有参数的构造方法。如果程序员不提供任何来构造方法,则编译程序自动提供一个构造方法;只要程序员提供了一个构造方法,系统不再提供缺省构造方法
编译程序提供的缺省构造方法只做一件事:调用父类的缺省构造方法
Java:
PlayingCard cardSeven = new PlayingCard(); // Java
C++:
PlayingCard *cardEight = new PlayingCard; // C++
static final int i2 = 99;
class BlankFinal {
final int i = 0; // Initialized final
final int j; // Blank final
// Blank finals MUST be initialized // in the constructor:
BlankFinal() {
j = 1; // Initialize blank final
p = new Poppet();
}
BlankFinal(int x) {
j = x; // Initialize blank final
p = new Poppet();
}
public static void main(String[] args) {
BlankFinal bf = new BlankFinal();
}
}
final仅断言相关变量不会赋予新值,并不能阻止在对象内部对变量值进行改变。如对消息的响应。
class Box {
public void setValue (int v);
public int getValue () { return v; }
private int v = 0;
}
final Box aBox = new Box(); // can be assigned only once
aBox.setValue(8); // but can change
aBox.setValue(12); // as often as you like
END