使用继承时子类的实例的初始化
子类实例初始化时,如果我们不显示调用父类的构造函数,编译器会先调用父类的默认的构造函数,顺着这个继承链向上一直到Object。如果子类的某个祖先类的构造函数被有参数的替换了,也没有提供无参的,那么必须在子类显示调用父类的有参数的构造函数,否则编译器会报错。
public class Test18 { public static void main(String[] args) { Cat cat = new Cat(); /× Animal Cat ×/ }}class Animal{ Animal(){ System.out.println("Animal"); }}class Cat extends Animal{ Cat(){ System.out.println("Cat"); }}
在子类中overload父类的方法
在java中子类中overload父类的方法,如果父类的该方法本身有overload的方法时,在子类中这些方法还在;而在C++中父类的这些overload的方法不能访问了。
public class Test19 { public static void main(String[] args) { Man m = new Man(); m.eat(); m.eat(1); m.eat(1.2); m.eat("23"); } }class People{ public void eat(){ } public void eat(int a){ } public void eat(String b){ }}class Man extends People{ public void eat(double d){ }}
#includeUpcastingusing namespace std;class Father{public: void eat(){ } void eat(int a){ }};class Son:public Father{public: void eat(double d){ cout< <
Upcasting指的是父类的引用指向子类的对象:
void test(Father f){}Son s = new Son();test(s);//Upcasting当我们在Composition和Inheritence之间选择时,只需要判断需不需要用到Upcasting,如果需要则使用继承。
final关键字
final data
final data可以分为两类:
1.编译时常量 public static final double PI = 3.14;//命名规范是全部大写,下划线分隔
2.运行时初始化的常量 public final int a = new Random(47).nextInt(20);
final修饰基础类型时表示基础类型变量的值不能变,修饰对象的引用时表示引用本身不能变,不能再指向其他的实例,而引用指向的对象的值是可以改变的(包括数组)。
public class Test20 { public static void main(String[] args) { // TODO Auto-generated method stub final Teacher t = new Teacher(); t.age = 10;//right t = new Teacher();//wrong }}class Teacher{ int age;}Blank finals
blank finals指的是被声明为final但是没有赋初始值的字段,这些final字段在使用前必须赋值,编译器会保证这一点。这些blank finals必须在各个构造函数中初始化。
综上所述,final字段或者在声明时初始化,或者在各个构造函数中初始化,这样才能保证fianl字段在使用前一定被初始化。
final arguments
当final修饰基础类型参数时,表示在方法中不能改变这个基础类型的值,当修饰对象引用时,表示在方法中不能改变这个引用的值,不能指向新的对象,但是对象的值是可以改变的。
final method
使用final method的原因有两个:
1.子类不能override父类的final method
2.在过去,使用final提示编译器将某个方法当作内联方法处理。在后面的java版本中,虚拟机使用hotspot技术可以自动检测需要使用内联函数的情形并做优化,因此现在不需要使用final提示编译器。
private的方法默认就是final的,虽然在子类中override父类的private方法看上去没错,但实际上已经不是override,因为子类根本看不到父类的perivate方法。
public class Test1 { public final void go(){ System.out.println("Test1 go"); }}class SubTest1 extends Test1{ public void go(){//Cannot override the final method from Test1 } public int go(int i){//correct return i*i; }}如上代码所示,虽然不能override,但是可以overload。
我们可以用@Override来表示对父类方法的override,这样如果不是override的话(比如子类对父类private方法的重写)就会报错。
final classes
使用final classes时,表示这个类不能被继承,final类中的方法不管加不加final关键字修饰都是final的,因为不可能被override了,而final类中的属性字段可以是final的也可以不是,这个看需要来定,final的不能改,不是final的可以改变值。
切记不要认为final类的字段也是final的,那就大错特错了。
ArrayList与Vector的区别
ArrayList中的方法没有synchronized关键字修饰,而Vector有,前者效率高些,非线程安全,后者效率低,线程安全。提倡使用前者,由程序员控制同步逻辑。
Hashtable与HashMap的区别
Hashtable中的方法有synchronized关键字修饰,HashMap没有,因此前者效率低,线程安全,后者效率高,非线程安全。HashMap的键和值允许为null,Hashtable不能。提倡使用HashMap替代Hashtable,同步逻辑由程序员控制。
public class Test4 { public static void main(String args[]){ Hashtable如上代码所示,如果Hashtable键为null,虽然编译不会出错,但是运行时会报空指针错误。而HashMap轻松无压力。可以说,Hashtable写的很差劲,因此要用HashMap取代它。hTable = new Hashtable (); HashMap hMap = new HashMap (); //hTable.put(null, "1"); //System.out.println(hTable.get(null)); hMap.put(null, "1"); System.out.println(hMap.get(null)); }}
初始化和类加载
传统语言(C++)程序被加载一次,接着初始化,然后开始执行。
在java中由于每个类的class code在各自的文件中,因此可以在某个类真正需要使用的时候再加载和初始化,即当一个实例被创建,或者类的静态的字段或方法被访问时。
初始化的顺序,就按照静态对象初始化的代码中的顺序,或者静态初始代码块中的顺序。当然,静态的东西只初始化一次。
带继承时候的初始化顺序
class Insect { private int i = 9; protected int j; Insect() { System.out.println("i = " + i + ", j = " + j); j = 39; } private int p=print(" Insect.p initialized"); private static int x1 =print("static Insect.x1 initialized"); static int print(String s) { System.out.println(s); return 47; }}public class Beetle extends Insect { private int k = print("Beetle.k initialized"); public Beetle() { System.out.println("k = " + k); System.out.println("j = " + j); } private static void dd() { System.out.println("dd "); } private static int x2 = print("static Beetle.x2 initialized"); public static void main(String[] args) { System.out.println("Beetle constructor"); Beetle b = new Beetle(); }}该程序输出:
static Insect.x1 initialized
static Beetle.x2 initializedBeetle constructor Insect.p initializedi = 9, j = 0Beetle.k initializedk = 47j = 39当运行Beetle时,访问它的静态的main方法,这个时候Beetle类会被加载,加载完后发现还有父类,因此接着加载父类,直到顶层。
接着父类的static初始化执行,然后是子类的static初始化。
接着执行System.out.println("Beetle constructor");,然后是 Beetle b = new Beetle();。
创建对象时,Beetle的内置类型(包括继承来的)初始化为默认值,引用类型初始化为null。
紧接着父类的非静态成员被初始化,构造函数被调用,然后子类的非静态成员被初始化,接着子类的构造函数被调用。