看think in java 随笔

java的方法是运行期动态绑定上去的,可以根据自己真正实例化的类来判断调用哪个方法,比如子类重写了父类方法,会调用子类方法。
而利用final关键字可以让方法不能重写,就可以在编译期就绑定,这样就可以提高执行效率。
 
private 方法 默认就是final的
 
final 的类 里面所有方法 默认都是final的

==================================================================== 

虚函数

  1. 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数 [1]
  2. 语法:virtual 函数返回类型 函数名(参数表) { 函数体 }
  3. 用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数,也就是允许子类override父类同名方法。

虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型(也就是完全相同的方法,不能只是函数名相同。)。以实现统一的接口,不同的定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。

当程序发现虚函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。虚函数是C++多态的一种表现。动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式:
  1. 指向基类的指针变量名->虚函数名(实参表)
  2. 基类对象的引用名. 虚函数名(实参表)
使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 ,例如:virtual void fun() = 0,我们把这样的函数(方法)称为纯虚函数。如果一个类包含了纯虚函数,称此类为抽象类。

总结:如果一个子类想要重写父类的方法,那么父类的这个成员方法必须是virtual的,也就是这个方法必须是虚函数。

java

在java中,所有方法默认就是虚拟的,只要方法不是声明为final类型的,那么肯定就是虚函数,不用为方法显示声明为virtual。 在<core java2 :volum I>中提到:"In Java, you do not need to declare a method as virtual. Dynamic binding is the default behavior. If you do not want a method to be virtual, you tag it as final"。所以我们发现,在java中,子类可以重写(override)父类的方法,而父类没有声明


--------------

1. Java虚函数

虚函数的存在是为了多态。

C++中普通成员函数加上virtual关键字就成为虚函数

Java中其实没有虚函数的概念,它的普通函数就相当于C++的虚函数,动态绑定是Java的默认行为。如果Java中不希望某个函数具有虚函数特性,可以加上final关键字变成非虚函数

PS: 其实C++和Java在虚函数的观点大同小异,异曲同工罢了。

 

2. Java抽象函数(纯虚函数)

   抽象函数或者说是纯虚函数的存在是为了定义接口。

   C++中纯虚函数形式为:virtual void print() = 0;

   Java中纯虚函数形式为:abstract void print();

   PS: 在抽象函数方面C++和Java还是换汤不换药。

 

3. Java抽象类

抽象类的存在是因为父类中既包括子类共性函数的具体定义,也包括需要子类各自实现的函数接口。抽象类中可以有数据成员和非抽象方法。

C++中抽象类只需要包括纯虚函数,既是一个抽象类。如果仅仅包括虚函数,不能定义为抽象类,因为类中其实没有抽象的概念。

Java抽象类是用abstract修饰声明的类。

PS: 抽象类其实是一个半虚半实的东西,可以全部为虚,这时候变成接口。

 

4. Java接口

接口的存在是为了形成一种规约。接口中不能有普通成员变量,也不能具有非纯虚函数。

C++中接口其实就是全虚基类。

Java中接口是用interface修饰的类。

PS: 接口就是虚到极点的抽象类。

 

5. 小结

C++虚函数    ==  Java普通函数

C++纯虚函数  ==  Java抽象函数

C++抽象类    ==  Java抽象类

C++虚基类    ==  Java接口

====================================================================

设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调
用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于private
方法,它们自动具有final 属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。(参见thinkInjava 7.73/204页)


Java 中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final。它可有效地“关闭”动态绑定,或者告诉编译器不需要进行动态绑定。这样一来,编译器就可
为final 方法调用生成效率更高的代码。

常数分组类型安全可以这样编程:
public final class Month2 {
private String name;
private Month2(String nm) { name = nm; }
public String toString() { return name; }
public final static Month2
JAN = new Month2("January"),
FEB = new Month2("February"),
MAR = new Month2("March"),
APR = new Month2("April"),
MAY = new Month2("May"),
JUN = new Month2("June"),
JUL = new Month2("July"),
AUG = new Month2("August"),
SEP = new Month2("September"),
OCT = new Month2("October"),
NOV = new Month2("November"),
DEC = new Month2("December");
public final static Month2[] month = {
JAN, JAN, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC
};
public static void main(String[] args) {
Month2 m = Month2.JAN;
System.out.println(m);
m = Month2.month[12];
System.out.println(m);
System.out.println(m == Month2.DEC);
System.out.println(m.equals(Month2.DEC));
}
}

==================================================================== 

普通类只能是public 和 默认友好的,内部类可以是四种全部访问权限
内部类访问外部类:外部类名.this
 
由于java动态绑定方法,所以会出现一个初始化的bug,这是程序设计上为了动态绑定,无法调和的一个矛盾,变量声明和赋值其实是两个步骤,哪怕在声明的时候就赋值也是这样,如果你的程序在初始化赋值之前就提前调用,就会出现这个bug.见think in java 第4版 203页。所以我们在构造器里尽量少调用方法,调用final和private的方法绝对不会有这种潜在隐患,因为它们不能被覆盖,不参与动态绑定。
 
java不支持参数化类型,c++用template关键字支持,java 保留了generic关键字,以便以后需要的时候可以扩展支持。
 
c++ 破坏器,java没有,缺陷。
 
==================================================================== 
线程:
synchronized方法,默认将此对象为锁。在任何时刻,只可有一个线程调用特定锁的同步方法(尽管那个线程可以调用多个锁的同步方法)。也就是说这个线程拥有这把锁,就可以调用以这把锁为synchronized的代码块。
static synchronized 默认将此类Class对象作为锁。
 
一个线程可以有四种状态:
(1) 新(New):线程对象已经创建,但尚未启动,所以不可运行。
(2) 可运行(Runnable ):意味着一旦时间分片机制有空闲的CPU 周期提供给一个线程,那个线程便可立即
开始运行。因此,线程可能在、也可能不在运行当中,但一旦条件许可,没有什么能阻止它的运行——它既
没有“死”掉,也未被“堵塞”。
(3) 死(Dead):从自己的run()方法中返回后,一个线程便已“死”掉。亦可调用stop()令其死掉,应该尽可能地避免调用它,根本不会解除对象的锁定。
(4) 堵塞(Blocked):线程可以运行,但有某种东西阻碍了它。若线程处于堵塞状态,调度机制可以简单地
跳过它,不给它分配任何CPU 时间。除非线程再次进入“可运行”状态,否则不会采取任何操作。 
 
堵塞的原因:
(1) 调用sleep(毫秒数),使线程进入“睡眠”状态。在规定的时间内,这个线程是不会运行的。不放弃锁,
(2) 用suspend()暂停了线程的执行。除非线程收到resume()消息,否则不会返回“可运行”状态。不放弃锁,且需要另一线程持有本线程的句柄,另一线程调用本线程的resume()才能继续。
(3) 用wait()暂停了线程的执行。除非线程收到nofify()或者notifyAll()消息,否则不会变成“可运行”。放弃锁。
(4) 线程正在等候一些IO(输入输出)操作完成。
(5) 线程试图调用另一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。
 
死锁:
一个对象等待另一个对象的锁,这个“等候”链最可怕的情形就是进入封闭状态——最后那个对象等候的是第一个对象!此时,所有线程都会陷入无休止的相互等待状态,大家都动弹不得。我们将这种情况称为“死锁”。
Java 1.2 “反对”使用Thread 的stop(),suspend(),resume()以及destroy()方法,以减少出现死锁的可能。
如果要退出循环线程,应该用一个标志告诉线程什么时候自己结束。如果一个线程被堵塞,比如在它等候输入的时候,不能轮询标志。但在这些情况下,我们仍然不该使用stop(),而应换用由Thread 提供的interrupt()方法。

看think in java 随笔

上一篇:RichTextBox 清空


下一篇:程序员,你可以更优秀些