Java笔记整理——接口、继承与多态

一、类的继承

在Java中使用extents关键字来标识两个类的继承关系。在子类中,可以联通初始化父类构造方法来完成子类初始化操作,既可以在子类的构造方法中使用super()语句调用父类的构造方法,也可以在子类中使用super关键字调用父类的成员方法等,但是子类没有权限调用父类中被修饰为private的方法,只可以调用父类中修饰为public或protected的成员方法。
在继承中还有一种特殊的重写方式,子类与父类的成员方法返回值、方法名称、参数类型及个数完全相同,唯一不同的是方法实现内容,这种特殊重写方式被称为重构。
注意:当重写父类方法时,修改方法的修饰权限只能从小的范围到大的范围改变。
若子类重写父类的方法,需要修改返回值,这种重写方式需要遵循一个原则,即重写的返回值类型必须是父类中同一方法返回值类型的子类。
子类调用构造方法的顺序,首先是顶级父类,然后是上级父类,。。。,最后是子类。也就是说实例化子类对象时首先要实例化父类对象,然后再实例化子类对象,所以在子类构造方法访问父类的构造方法之前,父类已经完成实例化操作。
说明:在实例化子类对象时,父类无参构造方法将被自动调用,但有参构造方法并不能被自动调用,只能依赖于super关键字显式地调用父类的构造方法。
技巧:如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收对象占用内存时,对象的所有部分都能被正常终止。

二、Object类

在Java中,所有的类都直接或间接继承了java.lang.Object类。Object类是所有类的父类,是Java类层中的最高层类。
注意:Object类中除了getClass()、notify()、notifyAll()、wait()等方法不能被重写,因为这些方法被定义为final类型,其他的都可以被重写。
1、getClass()方法
getClass()方法是Object类定义的方法,它会返回对象执行时的Class实例,然后使用此实例调用getName()方法可以去的类的名称。语法如下:
getClass().getName();
2、toString()方法
toString()方法的功能是将一个对象返回为字符串形式。在实际应用中通常从写toString()方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString()方法。
示例如下:
import java.lang.Object;
public class ObjectInstance {
 public String toString() {
  return "在" + getClass().getName() + "类中重写toString()方法";
 }

 public static void main(String[] args) {
  System.out.println(new ObjectInstance());
 }
}
运行结果:
在ObjectInstance类中重写toString()方法
3、equals()方法
“==”运算符和equals()方法对比,“==”运算符比较的是两个对象的引用是否相等,而equals()方法比较的是两个对象的实际内容。
示例如下:
class V {
 
}
public class OverWriteEquals {
 public static void main(String[] args) {
  String str1 = "123";
  String str2 = "123";
  System.out.println(str1.equals(str2));
  V v1 = new V();
  V v2 = new V();
  System.out.println(v1.equals(v2));
 }
}
运行结果:
true
false
从上面的例子的运行结果中可以看出,在自定义的类中使用equals()方法进行比较时,将返回false,因为equals()方法的默认实现是使用“==”运算符比较两个对象的引用地址,而不是比较对象的内容,所以要想真正做到比较两个对象的内容,需要在自定义类中重写equals()方法。

三、对象类型的转换

1、向上转型
一个子类对象可以看作是父类的对象。
示例如下:
class Quadrangle {                                    // 四边形类
 public static void draw(Quadrangle q){ // 四边形类中的方法
  // SomeSentence
 }
}
public class Parallelogram extends Quadrangle { // 平行四边形类,继承了四边形类
 public static void main(String[] args) {
  Parallelogram p = new Parallelogram();              // 实例化平行四边形类对象引用
  draw(p);                                                                  // 调用父方法
 }
}
在父类中存在draw()方法,参数是Quadrangle类型,但是子类调用时可以传入Parallelogram类型。这就是说平行四边形也是一种类型的四边形,所以可以将平行四边形类的对象看作是一个四边形类的对象,就相当于"Quadrangle obj = new Parallelogram();",就是把子类对象赋值给父类类型的变量,这种技术被称为“向上转型”。
若在父类对象的draw()方法中根据不同的图形对象设置不同的处理,就可以做到在父类对象中定义一个方法完成各个子类的功能,这样可以使同一份代码毫无差别地运用到不同类型至上,这就是多台机制的基本思想。
2、向下转型
示例如下:
class Quadrangle {
 public static void draw(Quadrangle q){
  // SomeSentence
 }
}
public class Parallelogram extends Quadrangle {
 public static void main(String[] args) {
  draw(new Parallelogram());
  // 将平行四边形对象看作是四边形对象,称为向上转型操作
  Quadrangle q = new Parallelogram();
  // Parallelogram p = q;
  // 将父类对象赋予子类对象,是错误的
  // 将父类对象赋予子类对象,并强制转换为子类对象,是正确的
  Parallelogram p = (Parallelogram)q;
 }
}
上例可以看出不能将父类直接赋予子类,会产生编译错误。但是可以将父类对象强制转换为某个子类对象,这种方式称为显式类型转换。当在程序中使用向下转型技术时,必须使用显示类型转换。

四、使用instanceof操作符判断对象类型

当执行向下转型操作时,若父类对象不是子类对象的实例,就会发生ClassCastException异常,所以在执行向下转型之前,一定要先判断父类对象是否是子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是否一个类实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。instanceof语法如下:
myobject instanceof 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();
  // 判断父类对象是否为Parallelogram的一个实例
  if(q instanceof Parallelogram) {
   Parallelogram p = (Parallelogram)q;
  }
  // 判断父类对象是否为Parallelogram的一个实例
  if(q instanceof Square) {
   Square s = (Square)q;
  }
  // 由于q对象不为Anything类的对象,所以下面的语句是错误的
  //System.out.println(q instanceof Anything)
 }
}

五、方法的重载

示例如下:
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(int a, double b) {
  return 1;
 }
 public static int add(double b, int a) {
  return 1;
 }

 public static void main(String[] args) {
  System.out.println("调用add(int,int)方法:" + add(1, 2));
  System.out.println("调用add(double ,double)方法:" + add(2.1, 3));
  System.out.println("调用add(int)方法:" + add(1));
 }
}
运行结果:
调用add(int,int)方法:3
调用add(double ,double)方法:1
调用add(int)方法:1
注意:虽然在方法重载中可以使两个方法的返回值不同,但只有返回值不同并不足以区分两个方法的重载,还需要通过参数的个数以及参数的类型来设置。
在谈到参数个数可以确定两个方法是否具有重载关系时,会想到定义不定长参数方法。
示例如下:
public static int add(int...a) { // 定义不定长参数方法
    int s = 0;
    for(int i = 0; i < a.length; i++) {
    s += a[i];                            // 做参数累计操作
    return s;                             // 将结果返回
    }
}
不定长方法的语法如下:
返回值  方法名(参数数据类型...参数名称)
在参数列表中使用“...“形式定义不定长参数,其实这个不定长参数a就是一个数组,编译器会将(int...a)这种形式看作是(int[]a),所以在add()方法体做累加操作时使用了for循环。

六、多态
利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。
示例如下:
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();
  q.draw(new Square());
  q.draw(new Paralle());
 }
}
class Square extends Quadrangle {
 Square() {
  System.out.println("正方形");
 }
}
class Paralle extends Quadrangle {
 Paralle() {
  System.out.println("平行四边形");
 }
}
运行结果:
正方形
0
平行四边形
1
从本实例的运行结果中可以看出,以不同类对象为参数调用draw()方法可以处理不同的图形问题。使用多态节省了开发和维护事件,因为程序员无须在所有的子类中定义执行相同功能的方法,避免了大量重复代码的开发,同时,只要实例化一个集成父类的子类对象即可调用相应的方法,这里只要维护父类中的这个方法即可。

七、抽象类与接口

1、在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。抽象类的语法如下:
public abstract class Test {
    abstract void testAbstract(); // 定义抽象方法
}
使用abstract关键字定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法,抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被集成,实际上抽象类除了被继承之外没有任何意义。反过来讲,如果声明一个抽象的方法,就必须将承载这个抽象方法的类定义为抽象类,不可能在非抽象类中获取抽象方法。换句话说,只要类中有一个抽象方法,此类就被标记为抽象类。
2、接口
1)简介
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。可以将一个方法封装到一个接口中,使需要这个方法的类实现这个接口,也同时继承抽象类,这就是接口存在的必要性。接口的存在避免了子类继承抽象父类时,必须每次都实现抽象父类中的抽象方法的问题。可以减少代码的冗余。
接口使用interface关键字进行定义,语法如下:
public interface drawTest {
    void draw(); // 接口内的方法,省略abstract关键字
}
一个类实现一个接口可以使用implements关键字,如下示:
public class Parallelogram extends Quadrangle implements drawTest {
    ... //
}
注意:在接口中定义的方法必须定义为public或abstract形式,其他修饰权限不能被Java编译器认可,即使不将该方法声明为public形式,也是public。
说明:在接口中定义的任何字段都自动是static和final的。
示例如下:
interface drawTest {     // 定义接口
 public void draw();  // 定义方法
}
class ParallelogramgleUseInterface extends QuadrangleUseInterface
implements drawTest {
 public void draw() {
  System.out.println("平行四边形.draw()");
 }
 void doAnything() { // 覆盖父类方法
  // SomeSentence
 }
}
class SquareUseInterface extends Quadrangle
implements drawTest {
 public void draw() {
  System.out.println("正方形.draw()");
 }
 void doAnything() {
  // SomeSentence
 }
}
class AnythingUseInterface extends Quadrangle {
 void doAnything() {
 
 }
}
public class QuadrangleUseInterface { // 定义四边形类
 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();
  }
 }
}
运行结果:
正方形.draw()
平行四边形.draw()
2)接口与继承
Java中不允许有多重继承,但使用接口就可以实现多重继承,因为一个类可以同时实现多个接口,这样可以将所有需要继承的接口放置在implements关键字后并使用都好隔开,但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。
多重继承的语法如下:
class 类名 implements 接口1, 接口2, …,接口n
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章