第7章 复用类
一,基类的初始化
当创建一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与用基类直接创建的对象一样,只是一个是外部的,一个是包含在导出类的对象中。
java会自动在导出类的构造器中插入基类的构造器的调用,如果想调用一个带参数的基类构造器,就必须根据参数列表用关键字super显示地编写调用语句。
class Game{
public Game(){
System.out.println("Game default constructor!");
}
public Game(int i){
System.out.println("Game constructor!");
}
}
class BoardGame extends Game{
BoardGame(){
System.out.println(" BoardGame default constructor!");
}
BoardGame(int i){
System.out.println(" BoardGame constructor!");
}
}
public class Chess extends BoardGame {
Chess(){
super(11);//调用带参数的构造器
System.out.println(" Chess default constructor!");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Chess x=new Chess();
}
}
结果:
Game default constructor!
BoardGame constructor!
Chess default constructor!
可以看出构造过程是从根基类开始,即使没有构造器,编译器也会提供一个默认构造器。
二,基类与导出类之间的方法重载
导出类可以继承基类中原有的方法,重载基类的方法即使该方法已经在基类中重载多次,最后就是覆盖基类中原有的方法。
class Homer{
/**
* 基类的doh()已经重载
* */
char doh(char c)
{
System.out.println("doh(char c)");
return c;
}
float doh(float f)
{
System.out.println("doh(float f)");
return 0.1f;
}
}
class Milhouse{}
class Bart extends Homer{
@Override
/**覆盖某方法,使用@Override注释
* 覆盖基类的char doh(char c)方法,
* 使用与基类完全相同的特征签名以及返回类型来覆盖具有相同名称的方法
* */
char doh(char c) {
System.out.println("Bart doh(char c)");
return c;
}
/**
* 导出类重载doh()
* */
void doh(Milhouse m){
System.out.println("doh(Milhouse m)");
}
}
public class Hide {
public static void main(String[] args) {
Bart b=new Bart();
b.doh(new Milhouse());//调用导出类重载方法
b.doh('a');//调用导出类的char doh(char c),基类已经被覆盖;
b.doh(0.2f);//调用基类方法
b.doh(1);
}
}
结果:
doh(Milhouse m)
Bart doh(char c)
doh(float f)
doh(float f)
三,protected关键字
protected修饰的域和方法,对于继承与此类的导出类(即使不在同一个包内)或者其他任何位于同个包的类来说,都是可以访问的。
package other;
/**
* 在other包中创建Villain
* */
public class Villain{
private String name;
protected void setName(String name){
this.name=name;
}
public Villain(){
}
@Override
public String toString() {
return "I'm a villain and my name is "+name;
}
}
import other.Villain;
/**
* 在默认包创建Orc,继承Villain
* */
public class Orc extends Villain {
private int orcNumber;
public Orc(String name, int orcNumber){
setName(name);//即使不在同一个包内,子类可以有访问基类的protected权限
this.orcNumber=orcNumber;
}
public void change(String name,int orcNumber){
super.setName(name);
this.orcNumber=orcNumber;
}
@Override
public String toString() {
return "Orc "+orcNumber+":"+super.toString();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Orc orc=new Orc("yang",20);
System.out.println(orc);
orc.setName("tong");
System.out.println(orc);
}
}
package other;
/**
*在other中创建Test6,并创建Villain对象,调用protected方法
* */
public class Test6 {
Villain v=new Villain();
public static void main(String[] args) {
// TODO Auto-generated method stub
Test6 t6=new Test6();
t5.v.setName("yang");//同一个包中的类。可以访问protected权限
System.out.println(t5.v);
}
}
import other.Villain;
/**
*在默认包中创建Test5,并创建Villain对象,不能调用protected方法
* */
public class Test5 {
Villain v=new Villain();
public static void main(String[] args) {
// TODO Auto-generated method stub
Test5 t5=new Test5();
t5.v.setName("yang");//不在同一个包内,无法访问protected权限
System.out.println(t5.v);
}
}
四,final关键字
package other;
import java.util.Random;
class Value{
private int i;
public Value(int i){
this.i=i;
}
public int getI(){
return i;
}
public void setI(int i){
this.i=i;
}
}
public class FinalData {
private static Random rand=new Random();
private String id;
private Value value1=new Value(1);
private final int j;//空白final
/*将valueOne,i3定义为常量,值无法修改*/
private final int valueOne=9;
private final int i3=rand.nextInt(20);
/*final定义value2,使引用不变,一旦引用初始化指向一个对象,就无法再把它改为指向另一个对象,对于数组也是同样,数组名也是引用*/
private final Value value2=new Value(2);
private final int[] a={1,2,3};
/*static与final定义编译器常量,只初始化一次,并且值恒定不变*/
private static final int VALUE_Two=99;
private static final int INT_4=rand.nextInt(20);
private static final Value VALUE_3=new Value(3);
public FinalData(String id){
/**
* 必须在域的定义出或者每个构造器中用表达式对final进行赋值;
* */
j=1;//The blank final field j may not have been initialized
this.id=id;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return id+"\n"+"valueOne="+valueOne+"\n"+
"VALUE_Two="+VALUE_Two+"\n"+
"i3="+i3+"\n"+
"INT_4="+INT_4+"\n"+
"value1="+value1.getI()+"\n"+
"value2="+value2.getI()+"\n"+
"VALUE_3="+VALUE_3.getI()+"\n";
}
public static void main(String[] args) {
FinalData fd1=new FinalData ("fd1");
System.out.println(fd1);
//!fd1.valueOne++;//The final field FinalData.valueOne cannot be assigned
fd1.value1=new Value(10);
//!fd1.value2=new Value(10);//The final field FinalData.value2 cannot be assigned
fd1.value2.setI(10);
//!fd1.a=new int[3];//The final field FinalData.a cannot be assigned
System.out.println(fd1);
FinalData fd2=new FinalData ("fd2");
System.out.println(fd2);
}
}
结果:
fd1
valueOne=9
VALUE_Two=99
i3=15
INT_4=12
value1=1
value2=2
VALUE_3=3
fd1
valueOne=9
VALUE_Two=99
i3=15
INT_4=12
value1=10
value2=10
VALUE_3=3
fd2
valueOne=9
VALUE_Two=99
i3=5
INT_4=12
value1=1
value2=2
VALUE_3=3
五,初始化过程
1,编译生成.class文件。
2,访问main方法,于是加载器开始启动加载.class里的编译代码。只要有extends关键字,编译器便将基类一层一层的加载进来,即使不打算创建基类对象,也会进行加载。
3,加载到根基类,根基类中的static开始初始化,然后是下一个导出类中的,以此类推下去。
4,必要的类加载完成后,对象可以被创建,对象中的基本类型会被赋予默认值,对象引用为null。
5,基类和导出类的构造器被调用,调用完成后,实例变量按其次序被初始化。