9.接口、继承与duo’t
继承,复用已定义的类,减少重复代码的编写
多态,动态调整对象的调用,降低对象之间依存关系
接口,优化继承和多态,建立类和类之间关联的标准
9.1继承
基于父类制造出一个新的子类,子类可以继承父类原有的属性和方法,增加原来父类不具有的属性和方法,或者直接重写父类中某些方法
举例1:
public class Text {
public Text() {
//构造方法
}
protected void doSomeThing() {
System.out.println("调用Text().doSomeThing()");
}
protected Text doll() {
System.out.println("返回Text()类型");
return new Text();
//返回类类型的成员函数
}
public static void main(String[] args) {
//一个类文件中有多个类时,主函数应该在与类文件名相同的类中,否则需要自行指明主函数位置
new Text2().doll();
//子类可以直接调用父类的非privated方法
}
}
class Text2 extends Text {
//extends关键字用来继承父类
public Text2() {
super();
//supper():可以调用父类构造方法,根据父类构造方法是否有参数再携带参数
super.doSomeThing();
//super关键字可以调用父类的非private方法
}
protected void doSomeThing() {
// 重写构造方法
System.out.println("Text调用函数dosomething");
}
public void doSomeThingNew() {
// 新增成员方法
}
protected Text2 doll2() {
// 重写返回类类型的父类方法
return new Text2();
}
}
要点:
1.extends()关键字用来声明继承关系,子类利用super()关键字调用父类方法
2.子类方法可以直接调用父类非private方法
3.重写父类方法,可以在子类中修改方法内容,更改方法存储权限,修改方法返回类型(J2SE5.0以上提供,重写后的返回值类型必须是父类中同名方法返回值类型的子类)
4.重构,特殊的重写方式,父类与子类方法返回值、方法名、参数表完全相同,方法实现内容不同
5.重写时,修改方法的修饰权限只能从小范围到大范围,不能降低方法修饰权限范围
举例2:
class Parent {
Parent() {
System.out.println("调用Parent()构造方法");
}
}
class SubParent extends Parent {
SubParent() {
System.out.println("调用SubParent()构造方法");
}
}
public class Subroutine extends SubParent {
Subroutine() {
System.out.println("调用Subroutine()构造方法");
}
public static void main(String[] args) {
new Subroutine();
}
}
要点:
1.实例化子类对象时,首先要实例化父类对象,java编辑器会在子类构造方法中自动调用父类无参数构造方法
2.若想在子类构造方法中调用父类有参构造方法,只能依赖于super关键字显式调用
3.如果使用finalize()方法清理对象,确保finalize()最后一个动作是调用父类的finalize()方法,否则部分对象不能被正常终止
9.2Object类
Object类是所有类的父类,Java类层最高层类,自定义类也继承自Object类,所以在定义类时,省略了extends Object关键字
Object类中有很多方法,如clone()、finalize()、equals()、toString()等,任何类都可以重写Object类中的方法,但getClass()、notify()、notifyAll()、wait()等被定义为final类型的方法除外
1.getClass()方法
该方法将返回对象执行时的Class实例,可以通过此实例调用getName()方法获得类名称,语法为:getClass().getName()
可以将getClass()放在重写的toString()方法中使用
2.toString()方法
该方法将一个对象以字符串的形式返回。实际应用中通常重写toString()方法,修改输出内容,当该类转换为字符串或者与字符串连接时,将自动调用重写的toString()方法。
public class ObjectInstance {
public String toString() {
//重写toString()方法
return "在 " + getClass().getName() + " 类中重写toString()方法";
}
public static void main(String[] args) {
System.out.println(new ObjectInstance());
//打印该类对象时将自动调用toString()方法
}
}
3.equals()方法
注意equals()和”==”的区别。
在基本数据类型中,equals()是比较两个对象实际内容是否相同,而”==”是比较两个对象的引用是否相等
在自定义类中,equals()默认实现方法是调用”==”比较两个两个对象引用地址是否相同,若想要比较两个自定义对象内容,需要重写equals()
class V {
String abc;
V(String abc) {
this.abc = abc;
}
}
class OverWriteEquals {
public static void main(String[] args) {
String s1 = "123";
String s2 = "123";
System.out.println(s1.equals(s2));
V v1 = new V("123");
V v2 = new V("123");
System.out.println(v1.equals(v2));
}
}
9.3对象类型的转换
9.3.1向上转型
把子类对象转换为父类对象,进行操作,这是一个由具体类到简单类的转换,安全
举例如下:
class Quadrangle {
// 四边形类
public static void draw(Quadrangle q) {
// dosomeThing
}
}
public class Parallelogram extends Quadrangle {
// 平行四边形类,继承自四边形类
public static void main(String[] args) {
Parallelogram q = new Parallelogram();
draw(q);
/*
* 子类对象转换为父类对象,作为参数,调用父类方法,相当于:
* Quadrangle q=new Parallelogram();
* draw(q);
*/
}
}
如果在父类方法中根据不同的子类设置不同处理,可以在父类中定义一个方法完成各个功能,这就是多态的基本思想
9.3.2向下转型
向下转型是将具体类转换成抽象类,必须显示、强制转换(告知编译器)
class Quadrangle {
// 四边形类
public static void draw(Quadrangle q) {
// dosomeThing
}
}
public class Parallelogram extends Quadrangle {
// 平行四边形类,继承自四边形类
public static void main(String[] args) {
draw(new Parallelogram());
// 向上转换,安全,相当于:
Quadrangle q = new Parallelogram();
// 该语句相当于,父类是子类
/*
* 错误举例1:
* Parallelogram p=q;
* 该语句相当于说,子类是父类,即四边形是平行四边形,显然不正确,所以要强制转换如下:
*/
Quadrangle p = (Quadrangle) q;
/*
* 错误举例2:
* Parallelogram p=new Quadrangle();
* 错误原因相同,修改如下:
*/
Parallelogram s = (Parallelogram) new Quadrangle();
}
}
9.4instanceof判断对象类型
向下转型操作时,如果父类对象不是子类对象实例,就会发生ClassCastException异常,所以在向下转型之前用instanceof操作符检查,语法如下:
myobject instanceof ExampleClass
该表达式返回一个布尔值,myobject是每个对象的引用,ExampleClass是某个类
举例如下:
class Quadrangle{
public static void draw(Quadrangle q){
//SomeSentence
}
}
class Square extends Quadrangle{
//SomeSentence
}
class Anything{
//SomeSentence
}
public class Parallelogram extends Quadrangle{
public static void main(String[] args) {
Quadrangle q =new Quadrangle();
if(q instanceof Parallelogram){
Parallelogram p=(Parallelogram)q;
}
if(q instanceof Square){
Square s=(Square)q;
}
// q对象不为Anything类的对象,该语句错误
// System.out.println(q instanceof Anything);
}
}
当被测类为对象的父类实例、实例、子类实例时,均返回true
9.5重载
重载是指不同的方法使用相同的方法名
重载依照方法中参数表的参数类型、参数个数、参数顺序的不同,区分不同方法
主函数调用重载函数时仅提供参数表,不涉及返回类型,因此仅仅返回值不同不足以区分两个方法的重载
举例如下:
public class OverLoadTest {
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
public static int add(int a) {
return a;
}
public static int add(double a, int b) {
return 1;
}
public static int add(int a, double b) {
return 2;
}
public static void main(String[] args) {
System.out.println(add(1, 2));
System.out.println(add(1.2, 3.4));
System.out.println(add(1, 2.3));
System.out.println(add(2.3, 1));
}
}
不定长参数方法,在方法中可以接受不确定个数的参数,可配合重载使用,语法如下:
返回值 方法名(参数数据类型 … 参数名)
此时,该不定长参数被视为一个数组,举例如下:
public class OverLoadTest2 {
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
public static int add(int a) {
return a;
}
public static int add(double a, int b) {
return 1;
}
public static int add(int a, double b) {
return 2;
}
public static int add(int...a){
int s=0;
for(int i=0;i<a.length;i++)
s+=a[i];
System.out.print("调用不等长参数方法:");
return s;
}
public static void main(String[] args) {
System.out.println(add(1, 2));
System.out.println(add(1.2, 3.4));
System.out.println(add(1, 2.3));
System.out.println(add(2.3, 1));
System.out.println(add(1,2,3,4,5,6));
System.out.println(add(1));
}
}
9.6多态
在父类方法中做出限定,根据调用时不同的子类对象,进行不同的处理
多态使程序具有更好的延展性,且避免了大量重复代码的开发
该思想依据是向上转型,即将子类对象当作父类对象处理
举例如下:
public class Quadrangle {
private Quadrangle[] qtest=new Quadrangle[6];
//被实例化的数组,用于保存四边形对象
private int nextIndex=0;
public void draw(Quadrangle q){
//将参数对象加入数组,索引指向数组的下一位
if(nextIndex<qtest.length){
qtest[nextIndex]=q;
System.out.println(nextIndex);
nextIndex++;
}
}
public static void main(String[] args) {
Quadrangle q=new Quadrangle();
//通过父类对象调用draw()方法
q.draw(new Square());
//qtest[0]指向该对象
q.draw(new Parallelogramgle());
//qtext[1]指向该对象
}
}
class Square extends Quadrangle{
public Square(){
System.out.println("正方形");
}
}
class Parallelogramgle extends Quadrangle{
public Parallelogramgle(){
System.out.println("平行四边形");
}
}
9.7抽象类和抽象方法
9.7.1抽象类
无法用语言描述的类,被称为抽象类,java抽象类不能实例化对象
在多态机制中,父类不需要初始化对象,故常常将父类定义为抽象类
关键字abstract定义了抽象类和抽象方法,举例如下:
public abstract class Test{
abstract void testAbstract();
//定义抽象方法,抽象方法无方法体
//继承抽象方法时,要保证相同的方法名称、参数列表和返回值类型
}
抽象方法只能在抽象类中定义
抽象类和抽象方法除了被继承无任何意义
继承抽象方法时需要将抽象类中的全部抽象方法覆盖,带入太多冗余代码,此时需要用接口解决
9.7.2接口
1.接口简介
接口是抽象类的延伸,所有方法都没有方法体,可以看作是纯粹的抽象类。
接口中任意字段都是static和final的。
在接口中定义的方法必须被定义为public(默认, 不可修改)和abstract形式。其他修饰权限不被认可。
关键字interface用来定义接口,语法如下:
public interface drawTest{
//public:接口的权限修饰符
//drawTest:接口名称
void draw();
//接口内的方法,省略abstract关键字
}
一个类实现一个接口可以使用implements关键字,举例如下:
interface drawTest{ //定义接口
public void draw(); //定义方法
}
//定义平行四边形类,该类继承四边形类,实现drawTest接口
class ParallelogramgleUseInterface extends QuadrangleUseInterface
implements drawTest{
public void draw(){
//该类实现了接口,需要覆盖接口内方法
System.out.println("平行四边形.draw()");
}
public void doAnyThing(){
//SomeSentence
System.out.println();
}
}
//定义正方形类,该类继承四边形类,实现drawTest接口
class SquareUseInterface extends QuadrangleUseInterface
implements drawTest{
public void draw(){
System.out.println("正方形.draw()");
}
public void doAnyThing(){
//someSentence
}
}
//定义四边形类
public class QuadrangleUseInterface{
public void doAnyThing(){
//SomeSentence
}
public static void main(String[] args) {
drawTest[] d={ //接口进行向上转型操作
new SquareUseInterface(),new ParallelogramgleUseInterface()};
for(int i=0;i<d.length;i++){
d[i].draw();
}
}
}
在java中,子类无论是向上转型为父类对象,还是向上转型为抽象父类对象,还是向上转型为该类实现接口,都是合法的
2.接口和继承
Java不允许一个类继承多个父类,但是允许一个类同时实现多个接口,多重继承可以用接口实现
用接口实现多重继承是需要实现接口中所有方法,可能产生庞大的代码量,多重继承语法如下:
class 类名 implements 接口1,接口2,...,接口n
定义接口时,可以使该接口继承另外一个接口
interface intf1{
}
interface intf2 extends intf1{
}