第四讲
面向对象(下)
类的继承
● 通过继承可以简化类的定义
● Java只支持单继承,不允许多重继承。
● 可以有多层继承,即一个类可以继承某一个类的子类,如类B继承了类A,类C又可以继承类B,那么类C也间接继承了类A。
● 子类继承父类所有的成员变量和成员方法,但不继承父类的构造方法。在子类的构造方法中可使用语句super(参数列表)调用父类的构造方法。
● 如果子类的构造方法中没有显式地调用父类的构造方法,也没有使用this关键字调用重载的其它构造方法,则在产生子类的实例对象时,系统默认调用父类无参数的构造方法。
class Person
{
private String name="unknown";
private int age=0;
public Person()
{
}
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
void getInfo()
{
System.out.println("姓名:"+name+"\n"+"年龄:"+age);
}
}
class Student extends Person
{
String school="unknown";
public Student()
{
//super();
super("张三",18);
}
}
class TestStudent
{
public static void main(String[]args)
{
Student st=new Student();
st.school="小学";
System.out.println("学校:"+st.school);
st.getInfo();
st.school="中学";
System.out.println("学校:"+st.school);
new Person("李四",20).getInfo();
}
}
运行结果:
学校:小学
姓名:张三
年龄:18
学校:中学
姓名:李四
年龄:20
子类对象的实例化过程
● 分配成员变量的存储空间并进行默认的初始化,就是用new关键字产生对象后,对类中的成员变量按第三章的表3.1中的对应关系对对象中的成员变量进行初始化赋值。
● 绑定构造方法参数,就是new Person(实际参数列表)中所传递进的参数赋值给构造方法中的形式参数变量。
● 如有this()调用,则调用相应的重载构造方法(被调用的重载构造方法双从步骤2开始执行这些流程),被调用的重载构造方法的执行流程结束后,回到当前构造方法,当前构造方法直跳转到步骤6执行。
● 显式或隐式追溯调用父类的构造方法(一直到Object类为止,Object是所有Java类的最顶层父类,在本章后面部分有详细讲解),父类的构造方法双从步骤2开始对父类执行这些流程,父类的构造方法的执行流程结束后,回到当前构造方法,当前构造方法继续往下执行。
● 进行实例变量的显式初始化操作,也就是执行在定义成员变量时就对其进行赋值的语句。、
● 执行当前构造方法的方法体中的程序代码
class Person
{
public String name="unknown";
public int age=0;
public Person()
{
}
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
void getInfo()
{
System.out.println("姓名:"+name+"\n"+"年龄:"+age);
}
}
class Student extends Person
{
String school="unknown";
public Student()
{
//super();
super("张三",18);
}
public Student(String name,int age)
{
super(name,age);
}
public Student(String name,int age,String school)
{
//super(name,age);
this(name,age);
this.school=school;
}
public void getInfo()
{
System.out.println("姓名:"+name+" 年龄:"+age+" 学校:"+school);
}
}
class TestStudent
{
public static void main(String[]args)
{
new Student().getInfo();
Student st=new Student("李四",16);
st.getInfo();
new Student("王二麻子",21,"清华").getInfo();
}
}
运行结果:
姓名:张三 年龄:18 学校:unknown
姓名:李四 年龄:16 学校:unknown
姓名:王二麻子 年龄:21 学校:清华
注意看下面代码的粗体部分及运行结果:
class Person
{
public String name="unknown";
public int age=0;
public Person()
{
}
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
void getInfo()
{
System.out.println("姓名:"+name+"\n"+"年龄:"+age);
}
}
class Student extends Person
{
String school="unknown";
public Student()
{
//super();
super("张三",18);
}
/*public Student(String name,int age)
{
super(name,age);
}*/
public Student(String name,int age,String school)
{
super(name,age);
//this(name,age);
this.school=school;
}
public void getInfo()
{
System.out.println("姓名:"+name+" 年龄:"+age+" 学校:"+school);
}
}
class TestStudent
{
public static void main(String[]args)
{
new Student().getInfo();
Student st=new Student("李四",16);
st.getInfo();
new Student("王二麻子",21,"清华").getInfo();
}
}
姓名:张三 年龄:18 学校:unknown
姓名:unknown 年龄:0 学校:unknown
姓名:王二麻子 年龄:21 学校:清华
注意:super()和this()调用语句不能同时在一个构造函数中出现。super()和this()调用语句只能作为构造函数中的第一句出现。
覆盖父类的方法
● 覆盖方法必须和被覆盖的方法具有相同的方法名称,参数列表和返回值类型。
● 如果在子类中想调用父类中的那个被覆盖的方法,我们可以用super.方法的格式。
● 覆盖方法时,不能使用比父类中被覆盖的方法更严格的访问权限。
class Person
{
public String name="unknown";
public int age=-1;
public Person()
{
}
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
public void getInfo()
{
System.out.println("name="+name+",age="+age);
}
}
class Student extend Person
{
public String school="unknown";
public Student()
{
//super();
super("wangwu",18);
}
public Student(String name,int age)
{
super(name,age);
}
public Student(String name,int age,String school)
{
//super(name,age);
this(name,age);
this.school=school;
}
public void getInfo()
{
System.out.print(/*"name="+name+",age="+age+*/"school="+school);
super.getInfo();//调用父类的成员方法
}
public void study()
{
}
}
class TestStudent
{
public static void main(String[]args)
{
Student st=new Student("zhangsan",18,"大学");
/*st.name="zhangsan";
st.age=20;*/
st.getInfo();
}
}
Final关键字
● 在Java中声明类、属性和方法时,可使用关键字final来修饰。
● final标记的类不能被继承。
● final标记的方法不能被子类重写。
● final标记的变量(成员变量或局部变量)即成为常量,只能赋值一次。
● 方法中定义的内置类只能访问该方法内的final类型的局部变量,用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期,将一个形参定义成final也是可以的,这就限定了我们在方法中修改形式参数的值。
● public static final共同标记常量时,这个常量就成了全局的常量。
抽象类
java中可以定义一些不含方法体的方法,它的方法体的实现交给该类的子类根据自己的情况实现,这样的方法就是抽象方法,包含抽象方法的类就叫抽象类。
● 抽象类必须用abstract关键字来修饰;抽象方法也必须用abstract来修饰。
● 抽象类不能被实例化,也就是不能用new关键字去产生对象。
● 抽象方法只需声明,而不需实现。
● 含有抽象方法的类必须被声明为抽象类,抽象类的子类必须覆盖所有的抽象方法后才能被实例化,否则这个子类还是个抽象类。
接口(interface)
如果一个抽象类中的所有方法都是抽象的,我们就可以将这个类用另外一种方式来定义,就是接口的定义。接口是抽象方法和常量值的定义的集合,从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
● 接口中的成员都是public访问类型的,接口里的变量默认是用public static final标识的。
● 我们可以定义一个新的接口用extends关键字去继承一个已有的接口
● 我们也可以定义一个类用implements关键字去实现一个接口中的所有方法,我们还可以定义一个抽象类用implements关键字去实现一个接口中定义的部分方法。
● 一个类可以继承一个父类的同时,实现一个或多个接口,extends关键字必须位于implements关键字的之前。
interface Runner
{
/*public static final*/ int ID=05;
void run();
}
interface Animal extends Runner
{
public void breathe();//必须为public类型,以下如同
}
abstract class LandAnimal implements Animal
{
public void breathe()
{
System.out.println("LandAnimal ......");
}
}
class Fish implements Animal
{
public void run()
{
System.out.println("The fish jogs the way is the swimming");
}
public void breathe()
{
System.out.println("The fish breath way is the bubbling");
}
}
class TestAnimal
{
public static void main(String[]args)
{
Fish f=new Fish();
f.run();
f.breathe();
int j=0;
//f.ID=2;无法为最终变量 ID 指定值
System.out.println(f.ID);
System.out.println(Fish.ID);
//LandAnimal la=new LandAnimal(); LandAnimal 是抽象的;无法对其进行实例化
}
}
对象的类型转换
● 子类对象可以自动转换成父类
● 父类转换成子类必须使用强制转换
● instanceof操作符可以用来判断一个实例对象是否属于一个类。
class A
{
public void func1()
{
System.out.println("A fun1 is calling");
}
public void func2()
{
func1();
}
}
class B extends A
{
public void func1()
{
System.out.println("B func1 is calling");
}
public void func3()
{
System.out.println("B func3 is calling");
}
}
class C
{
public static void main(String[]args)
{
B b=new B();
callA(b);
A a=b;
callA(new A());
}
public static void callA(A a)
{
if(a instanceof B)//如果a是B类的一个实例对象
{
B b=(B)a;
b.func1();
b.func2();
b.func3();
}
else
{
a.func1();
a.func2();
}
}
}
● Object类及equals方法
class Student
{
String name;
int age;
public Student(String name,int age)
{
this.name=name;
this.age=age;
}
public boolean equals(Object obj)
{
Student st=null;
if(obj instanceof Student)
{
st=(Student)obj;
if(st.name==name && st.age==age)
return true;
else
return false;
}
else
return false;
}
public static void main(String[]args)
{
Student stud1=new Student("zhangsan",20);
Student stud2=new Student("zhangsan",18);
if(stud1.equals(stud2))
System.out.println("equals");
else
System.out.println("not equals");
}
}
接口实例:
interface PCI
{
void start();
void stop();
}
class SoundCard implements PCI
{
public void start()
{
System.out.println("hollo");
}
public void stop()
{
System.out.println("ByeBye");
}
}
class NetWork implements PCI
{
public void start()
{
System.out.println("send...");
}
public void stop()
{
System.out.println("stop...");
}
}
class MainBoard
{
public void usePCICard(PCI p)
{
p.start();
p.stop();
}
}
class Assembler
{
public static void main(String[]args)
{
MainBoard mb=new MainBoard();
NetWork nw=new NetWork();
SoundCard sc=new SoundCard();
mb.usePCICard(nw);
mb.usePCICard(sc);
}
}
匿名内置类
interface PCI
{
void start();
void stop();
}
class SoundCard implements PCI
{
public void start()
{
System.out.println("hollo");
}
public void stop()
{
System.out.println("ByeBye");
}
}
class NetWork implements PCI
{
public void start()
{
System.out.println("send...");
}
public void stop()
{
System.out.println("stop...");
}
}
class MainBoard
{
public void usePCICard(PCI p)
{
p.start();
p.stop();
}
}
class Assembler
{
public static void main(String[]args)
{
MainBoard mb=new MainBoard();
NetWork nw=new NetWork();
SoundCard sc=new SoundCard();
mb.usePCICard(nw);
mb.usePCICard(sc);
mb.usePCICard(new PCI()
{
public void start()
{
System.out.println("test start");
}
public void stop()
{
System.out.println("test stop");
}
});
}
}
异常
● 异常定义了程序中遇到的非致命的错误,而不是编译时的语法错误,如程序要打开一个不存在的文件、网络连接中断、操作数越界、装载一个不存在的类等。
● try,catch语句
● throws关键字
● 自定义异常与throw关键字
● 如何对多个异常作出处理
● 我们可以在一个方法中使用throw,try…catch语句业实现程序的跳转
● 一个方法被覆盖时,覆盖它的方法必须扔出相同的异常或异常的子类。
● 如果父类扔出多个异常,那么重写(覆盖)方法必须扔出那些异常的一个子集,也就是说不能扔出新的异常
class Test
{
public int devide(int x,int y) throws DevideByMinusException,ArithmeticException
{
if(y<0)
throw new DevideByMinusException("devide is "+y);
return x/y;
}
//以下是实现程序的跳转功能。
/*void fun()
{
try
{
if(x==0)
throw new XxxException("xxx");
else
throw new YxxException("yyy");
......
}
catch(XxxException e)
{
}
catch(YyyException e)
{
......
}
}*/
}
class DevideByMinusException extends Exception
{
public DevideByMinusException(String msg)
{
super(msg);
}
}
class TestException
{
public static void main(String[]args)
{
Test tt=new Test();
try
{
tt.devide(4,0);
}
catch(ArithmeticException e)
{
System.out.println("Program is running into Arithmetic");
e.printStackTrace();
}
catch(DevideByMinusException e)
{
System.out.println("Program is running into DevideByMinusException");
e.printStackTrace();
}
catch(Exception e)
{
System.out.println(e.getMessage());
}
finally
{
System.out.println("finally");
}
System.out.println("The program is running here.");
}
}
访问控制

类本身也有访问控制,即在定义类的class关键字前加上访问控制符,但类本身只有两种访问控制,即public和默认,父类不能是private和protected,否则子类无法继承。Public修饰的类能被所有的类访问,默认修饰(即class关键字前没有访问控制符)的类,只能被同一包中的所有类访问。