JavaSE 08 面向对象(中)

来源:互联网 发布:网络数据机房维护方案 编辑:程序博客网 时间:2024/06/17 01:01

1、继承

⑴ 概念


一个类没有定义自己的成员(属性或方法等),就能具备另外一个类的成员(属性或方法等)。
被继承的类:父类、基类或超类。
继承其他类的类:子类或派生类。

⑵ 好处


① 提高了代码的重用性。② 提高了代码的维护性和扩展性。 ③ 为多态奠定了基础。

⑶ 语法


修饰符 class 子类 extends 父类{
    子类成员..
}

⑷ 子类继承了父类的什么


① 子类继承了父类的所有的属性和方法。但是应当注意父类成员(属性和方法)的访问修饰符,具体场合具体分析,因为访问父类的属性或方法时遵循属性和方法的修饰符权限
② 可以通过set或get方法访问属性。
③ 不能继承父类的构造器。
继承

⑸ 再说protected修饰符


可以让本包中的所有类访问,或其他包中的子类访问到。但在其他包中的类中,所创建的子类对象,不能用“.”的方式访问到。
示例:
package com.test1;
public class A{
protected String name;
}

package com.test2;
import com.test1.A;
public class B extends A{
}
package com.test3;
import com.test2.B;
public class Test{
    public static void main(String[] args){
          B b = new B();
       b.name = “姓名”; // 无法访问到父类(A)的name属性【错误: name可以在A中访问protected】
    }
}

它只能在子类中访问。
示例:
package com.test2;
import com.test1.A;
public class B extends A{
  public void testMethod(){
    name = “姓名”;
  }
}

⑹ 继承的注意事项


① Java支持单继承,一个子类只能直接继承一个父类。
② 不能滥用继承,父类和子类之间应当满足一个:子类 是一个 父类的 …
③ Java中所有的类都直接或间接地继承了Object类。
Object类是所有类的祖先。
Object类中的方法【Object类的成员只有方法】,所有类都具备。

⑺ 继承中有关构造器的注意事项


① 子类不能继承父类的构造器,但是必须调用父类的构造器。
子类如果没有显式地调用父类的构造器,则默认调用的是父类的无参构造器。
③ 如果父类没有无参的构造器,即提供了有参的构造器同时没有提供无参的构造器。则子类需要显式地调用父类的有参构造器。【通过super关键字来调用:super(实参列表);】
④ 构造器的调用可以一直往类的父类追述,直到Object类。

2、super关键字

super可以访问父类的属性、方法和构造器。

语法:⑴ 访问属性:super.属性名 = 值;
⑵ 访问方法:super.方法名(实参);
⑶ 访问构造函数:super(实参);

应用场合:当子类中定义的属性或方法与父类中的属性或方法重名时,而又想访问父类的属性或方法时,就需要添加super关键字【因为根据就近原则,不加super关键字,则会访问子类中的属性或方法】。

在构造器中使用super关键字时的注意事项:
⑴ super(实参)且只能有一句,只能放在构造函数的第一行。
⑵ super(实参)不能和this(实参)关键字同时使用。

3、方法的重写

概念:当子类中定义了和父类有着相同方法名的方法时,但方法体不同。在调用方法时,会执行子类的方法。

要求:
⑴ 发生在子类和父类之间。
⑵ 方法的名字和形参列表必须相同。
⑶ 返回值类型相同;或为父类返回值类型的子类型【JDK5.0的新特性】
示例:
public class A{
   public Object myMethod(){ // 返回值类型:Object
       return null;
   }
}

public class B extends A {
    @Override
    public String myMethod(){ // 返回值类型:String为Object的子类
       return “”;
    }
}

⑷ 子类的访问修饰符的访问权限不能比父类的访问修饰符的访问权限小。

注意:没有属性的重写!

4、方法重写和方法重载的对比

         方法的重写Override     方法的重载Overload

【发生范围】           父子类                                 本类
【访问修饰符】 相同或不能比父类严格           没要求
【返回值类型】 相同或为父类的子类型           没要求
【方法名】              必须相同                           必须相同
【形参列表】          必须相同                           必须不同

5、多态

⑴ 概念


一种方法或一个对象具有多种表现形式。

⑵ 对象的转型


对象的编译类型和运行类型不一致时,就出现了多态。
编译类型:赋值符号(=)左边的类型
运行类型:赋值符号右边的类型

编译时类型由声明该变量的类型决定,而运行时类型由实际赋给该变量的类型决定。

示例: Person person = new Student( );
    编译类型:Person     运行类型:Student

父类的引用指向子类的对象。
父类的引用指向子类对象

⑶ 对象转型的分类


造型:对象类型的转型
前提条件:必须具有继承关系的对象才可以进行造型(对象类型的转换)。

① 向上转型【一般称此为多态】


子类型向父类型转换。类型转换自动进行。

特点:⒈ 编译时看赋值符号左边的类型(编译类型),而运行时看赋值符号右边的类型(运行类型)【编译看左边,运行看右边】。
⒉ 属性或方法能否调用,需要看是否在编译类型中定义过,如果没有定义,则编译报错。
⒊ 方法的实际运行的结果,则需要看运行类型中定义的同名的方法(重写过的方法)。
⒋ 属性没有多态的概念【属性不能被重写】。属性调用的结果,需要看编译类型。

注意:⒈ 创建子类对象时,需要先加载所有父类。
⒉ 子类对象中,包含了父类的属性和方法的引用以及子类本身的属性和方法。
⒊ 如果父子类有重名的属性,则父类和子类的属性为两个独立的空间。
⒋ 如果父子类有重名的方法,则子类的方法会覆盖父类的方法。

示例:

public class Test {  public static void main(String[] args) {    Person p = new Man();    p.hi(); // 我是男人 【运行结果,要看运行类型中的方法】    // p.hello(); // 错误: 找不到符号 【只能调用编译类型中定义过的方法】    System.out.println(p.name); // 人类 【属性没有多态的概念,只需看编译类型中定义的属性】  }}class Person {  String name = "人类";  public void hi() {    System.out.println("我是人类");  }}class Man extends Person {  String name = "男人";  public void hi() {    System.out.println("我是男人");  }  public void hello() {    System.out.println("我是一个男人");  }}

多态的方法和属性的调用

② 向下转型


父类型向子类型转换。类型转换通过造型来实现,需要进行强制类型转换。

特点:⒈ 不能强转为父类的对象,只能强转为父类的引用。
错误:Man m = (Man) new Person(); // java.lang.ClassCastException: Person cannot be cast to Man
正确:Person p = new Man( );
           Man m = (Man) p;
理解:男人是人;但人不一定是男人。
⒉ 不仅可以调用从父类继承过来的方法,同时也可以调用子类自身定义的方法。

注意:没有继承关系的引用数据类型之间的转换是非法的!

⑷ 本态调用和多态调用


① 本态调用,即正常方法调用。
Person person = new Person( );
Person.hi( ); // 我是人类

② 多态调用,又称虚拟方法的调用。
Person p = new Man( );
p.hi( ); // 我是男人

编译时,p为Person类型,而hi( )方法调用时是在运行时确定的,所以调用的是Man类中的hi( )方法。【动态绑定】

⑸ 多态数组


在引用数据类型的数组中,数组元素的类型为数组类型的子类型。
示例:

public class Test {  public static void main(String[] args) {          Animal[] animals = new Animal[3]; // 对象数组的声明并开辟长度    animals[0] = new Dog("旺旺", 2, "黄色"); // 添加数组元素【多态形式】    animals[1] = new Dog("小白", 1, "白色");    animals[2] = new Dog("大王", 1, "黑色");   for (int i = 0; i < animals.length; i++) { // 遍历数组元素     System.out.println(animals[i]);   }  }}class Animal {  String name;  int age;  public Animal(String name, int age) {    this.name = name;    this.age = age;  }}class Dog extends Animal {  String color;  public Dog(String name, int age, String color) {    super(name, age);    this.color = color;  }  public String toString() {    return "这是一只" + age + "岁的,名叫" + name + "的," + color +  "的狗。";  }}

⑹ 多态参数


调用方法时,传入的实参为形参的子类型。
示例:

public class Test {  public static void main(String[] args) {    Animal animal = new Dog("旺旺"); // 向上转型【多态】    Man man = new Man();    man.feed(animal); // 旺旺吃肉 【实参是形参的子类型】  }}class Man {  public void feed(Animal animal) {    System.out.println(animal.name + "吃肉");  }}class Animal {  String name;  public Animal(String name) {    this.name = name;  }}class Dog extends Animal {  public Dog(String name) {    super(name);  }}

6、instanceof 关键字

语法: 引用名 instanceof 类型
返回值:true 或 false

用于判断左边的引用类型是否属于右边的引用类型,或为右边引用类型的子类。

示例:
String str = “abc”;
boolean flag = str instanceof String;
System.out.println(flag); // true

7、 Object类

⑴ 介绍


① 包名:java.lang 系统默认导入
② 说明:Object类是所有类的祖先类,根类。
③ 构造函数:new Object( ); 很少使用
④ 常见方法:toString( );
                       equals(Object obj);
                       hashCode( );
                       finalize( );

⑵ toString


public String toString(){
    return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
返回值类型:字符串
方法功能:返回该对象的字符串形式

特点:
① Object类中的toString 方法,默认返回的是:包名.类名 + @ + 此对象哈希码的无符号十六进制
② 一般来讲,子类会重写toString 方法,用于返回对象的属性信息。简单明了。

好处:
① 打印方法中,默认调用对象的toString 方法。
System.out.println(obj); // 相当于System.out.println(obj.toString( ));
② 拼接字符串时,默认调用对象的toString 方法。
String str = obj + “Hello”; // 相当于String str = obj.toString( ) + “Hello”;

⑵ equals


public boolean equals(Object obj) {
    return (this == obj);
}
返回值类型:布尔类型
方法功能:判断其他某个对象是否与此对象“相等”。

特点:
① Object类中的equals 方法判断的是地址号。
② 子类往往会重写equals 方法,用来判断两个对象的内容是否相等。重写过的子类:String等。

重写equals 方法的步骤

① 判断传入的对象的地址号是否和调用equals 方法的对象的地址号相等。
② 判断传入的对象的值(地址号)是否为null(空)。
③ 判断传入的对象的运行时类是否为调用equals 方法的对象的运行时类。
④ 依次判断传入的对象的属性是否和调用equals 方法的对象的属性值相等,一旦不等就返回false。
⑤ 如果属性值都相等,则返回true。

示例:

public class Test {  public static void main(String[] args) {    Person p1 = new Person("张三");    Person p2 = new Person("张三");    System.out.println(p1.equals(p2)); // true    Person p3 = new Man("张三");    System.out.println(p1.equals(p3)); // false  }}class Person {  private String name;  public Person(String name) {    this.name = name;  }  @Override  public boolean equals(Object obj) {    if (this == obj) { // 判断地址号      return true;    }   if (null == obj) { // 判断引用是否为空     return false;   }   if (getClass() != obj.getClass()) { // 判断所属类型     return false;   }   Person person = (Person) obj; // 向下转型   if (name != person.name) { // 判断属性是否相等    return false;   }   return true;   }}class Man extends Person {  public Man(String name) {    super(name);  }}

== 和 equals的对比

== 可以判断基本数据类型和引用数据类型
    判断基本数据类型时,判断的是值是否相等。
    判断引用数据类型时,判断的是地址号是否相等。

equals 只能判断引用数据类型
    在Object类中,用于判断地址号是否相等。
    在子类中,可以通过重写equals 方法来判断内容(对象的属性)是否相等。

⑶ hashCode


public native int hashCode();

哈希值:哈希值是根据对象的属性、方法和地址号等信息推算得出的。
哈希值的特点:⑴ 同一个对象的哈希值肯定一样。
                        ⑵ 不同对象的哈希值可能一样。

hashCode方法的好处:提高了集合中查找元素的效率,减少了equals方法判断的次数。
子类中通常会重写hashCode方法。

⑷ finalize


protected void finalize() throws Throwable { }

当对象被创建时,系统将自动启动垃圾回收机制,检测该对象的大小或者是否被引用。一旦该对象没有被引用,就变成了无用对象,也就是垃圾。垃圾回收机制将会自动回收该对象。但是垃圾回收机制回收垃圾具有不确定性,即垃圾回收机制不是即时回收垃圾的。

一般加速垃圾回收机制回收垃圾的方法有2种:
⑴ 代码唤醒垃圾回收机制。
⑵ 有新的对象需要占用堆空间(很大)时。

当垃圾回收机制回收某对象时,将自动调用该对象的finalize方法。如果有需要,则会在子类中重写此方法,来进行一些清除或关闭操作。

示例:

public class Test {  public static void main(String[] args) {    Book book = new Book();    book = null; // book对象【不可达】    System.gc();    System.out.println("a"); // 被回收!\n a  }}class Book { @Override protected void finalize() throws Throwable {   System.out.println("被回收!"); }}
1 0