今天讲一下java的构造器与继承。
当我们创建一个类以后,即使我们没有显示的写构造器器编译器也会在编译的时候为我们写一个无参构造器,但是一旦我们自己写了一个构造器(不管是无参还是有参的)则编译器就不会再为我们提供一个无参构造器。
当我们继承一个基类后,初始化导出类前首先初始化的是基类,然后初始化的是导出类,其实我们导出类的构造器器(无论是有参还是无参)中如果不显示的调用父类构造器,编译器也会为我们默认的调用父类的无参构造器(注意是无参构造器)。下面我们来看几种情况:
package com.djk.jichu;
public class Test9
{
public static void main(String[] args)
{
A a = new B("msg");
}
}
class A
{
public A()
{
System.out.println("a");
}
}
class B extends A
{
public B(String msg)
{
System.out.println(msg);
}
}
此时结果是首先打印出a然后是打印出msg,因为B继承A,B的有参构造器虽然没有调用A的无参构造器,但是在编译的时候编译器会在B的有参构造器中调用A的无参构造器。
package com.djk.jichu;
public class Test9
{
public static void main(String[] args)
{
A a = new B();
}
}
class A
{
public A()
{
System.out.println("a");
}
}
class B extends A
{
public B()
{
System.out.println("b");
}
}
package com.djk.jichu;
public class Test9
{
public static void main(String[] args)
{
A a = new B("msg");
}
}
class A
{
public A()
{
System.out.println("a");
}
public A(String a)
{
System.out.println(a);
}
}
class B extends A
{
public B(String msg)
{
System.out.println(msg);
}
}
此时输出的结果是 a然后是msg,理由是调用B的有参构造器前首先调用了A的无参构造器,举这3个列子我想说明的是不管导出类(子类)的构造器是无参还是有参的,只要我们没有显示的去调用父类的构造器,则编译器帮我们默认的调用无参构造器。
接下来再看个列子:
class A
{
public A(String a)
{
System.out.println(a);
}
}
class B extends A
{
public B(String msg)
{
System.out.println(msg);
}
}
此时会发现B的有才参构造器会报错,这个报错正好说明了2个问题第一个是,不管导出类的无参构造器还是有参构造器没有显示的调用基类的构造器的话,编译器默认的调用基类的无参构造器,这里导出类B的有参构造器没有显示的调用A基类的构造器则默认调用A类的默认构造器,但是正如前面所说当我们在一个类中定义了构造器后编译器将不会再为我们提供一个默认的构造器,此时当B的有参构造器调用A的无参构造器时会发现A中没有无参构造器所以会报错,只要我们显示的在A类中增加一个无参构造器则错误就消失,或在B的无参构造器中显示的调用A的有参构造器错误也将消失。
只要我们记住1:初始化导出类首先初始化的是基类 (调用父类的构造器)
2:当我们为类提供构造器后则编译器就不会再为我们提供默认的构造器
记住这2点后上面的错误自然也就很好理解了....
3:以后运用增强for循环的时候要注意空指针的判断因为集合不为空,但是集合中的元素可能是为空的。
4:在输入参数为int类型的时候要注意临界值的问题,当int类型的最大值加上一个数字后得出来的是一个负数
5:用随机数的时候不要给随机种子,不然随机数就失去意义了,每次执行的结果都是一样的
6:序列化的时候对象里面的static修饰的和tr,,,修饰的不会被序列号,加序列号的目的是向前兼容。
7:静态变量是先分配空间然后再赋值,所以在java中静态变量先运算后声明是存在的
9:多态对于静态的方法或者变量不适用,对非静态的变量也不适用,多态只适用于非静态的方法
10:覆写对于静态的方法不适用,因为静态的方法是类的不是对象的
11:代码块不是在构造器执行之前自己执行的,而是它依托构造器的执行 ,可以把他看成构造器执行时候的第一个方法。。。
12:代码块是在每个构造器都有效果,当有N多个构造器要调用一个方法的时候 就可以吧这个方法用代码块代替,java编译器在编译的时候会将代码块插入每个构造
函数之前,注意的是 如果构造器里面this调用了另一个构造器,这有this的构造器里面就不会再插入代码块了。
13:重写equals 方法需要注意的是:自反性(返回自反性的列子:name.equlas(a.getName.trim())); 传递性:(继承的例子,继承重写equals方法 用instance of判断)
14:java汉字的排序使用Collator.getInstance(Local.CHINA) 进行
15:在对一组数据计算和的时候数组的效率比集合快,原因是集合涉及自动拆箱操作
16:需要注意的是数组的拷贝是浅拷贝,当数组是对象的时候要注意了 如果是基本类型则没问题 ,如果是对象则拷贝的是引用的地址 拷贝前和拷贝后都指向一个内存
17:List<Integer> list = Arrays.asList(1,2,3); 表明上看上去这种方式很好,其实这有个隐患就是这个list不是我们平常的list而是Arrays里面的一个内部类 他没有实现add和remove方法 所以调用add方法的时候会报错
18:注意List的subList方法 该方法得到的一个List其实是原List的一个视图,即改变subList得到后的List也会影响原List,还有如果一个List调用了subList方法则原List就不能
再添加元素否则会报错。
29:在多线程坏境中用Vector替换list ,用hashtable替换HashMap
30:java的泛型只在编译期间有效果,在运行期间无效会被擦除
31:一个类返回自己内部的一个对象的时候应该返回他的克隆对象,防止放回了一个引用被人改动
32:继承有一个陷阱,比如继承HashSet的add和addAll方法,就是超类的公开方法尽量不要调用公开的方法
33:超类的构造器中不能调用被子类覆盖的方法,有时候会导致空指针
34:
public final class MySingleton implements Serializable{
private MySingleton() { }
private static final MySingleton INSTANCE = new MySingleton();
public static MySingleton getInstance() { return INSTANCE; }
private Object readResolve() throws ObjectStreamException {
// instead of the object we're on,
// return the class variable INSTANCE
return INSTANCE;
}
}
单列类实现序列化 防止反序列化得时候生成多个对象
35:企图将一个不可实例化的类写成抽象类这样做是不对的。因为抽象类可以被继承 子类可以实例化。正确的做法应该是把构造器私有化
36:私有的构造器将会导致这个类不能被继承
37:继承的时候子类不应该覆盖父类的方法,而应该增加新的方法,覆盖父类的方法会违背里氏代换原则
38:继承的另一个坏处是 当子类加了一个新方法,后期父类和子类加了一个一样的方法,可能会编译报错
39:在类的方法参数或者返回值 如果是一个可变的对象 那么就要考虑返回一个保护性的拷贝,这样做的原因是 如果反回一个可变的对象那么类本身就失去了控制,如果返回的是一个不可变的类 则可以不返回拷贝,因为不可变的类状态不能变