Java面向对象编程----面向对象(三)

123 阅读7分钟

继承

继承在一定程度上能实现代码重用,并且通过继承实现已有类的新特性扩充,让程序更具有可拓展性。

继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。

Java不支持多重继承,只能直接继承一个父类(单继承),但也能使用多层继承。

子类对象必须能够替换掉所有父类对象。 Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型

Animal animal = new Cat();

子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问, 要通过父类提供公共的方法去访问

子类必须调用父类的构造器, 完成父类的实例化 。当创建子类对象时,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作

super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

java 所有类都是Object 类的子类, Object 是所有类的基类

父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

方法重写

重写(Overriding)

重写一般用于重写(Overriding)父类的方法,但不改变参数列表和返回类型,保持相同的方法签名。 子类方法的访问权限必须大于等于父类方法,子类方法不能缩小父类的访问权限。 final关键字定义不能被重写的类或方法,或者定义不能被修改的常量(引用类型的被引用对象本身是可修改的)。定义全局常量名称必需全部大写。

final int x = 1; // x = 2; // cannot assign value to final variable 'x' final A y = new A(); y.a = 1;

private方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。

重载(Overloading) 与它类似的操作是重载(Overloading)。重载方法名称相同,但是参数列表不会相同(同名不同参)。

super与this

区别superthis
访问属性,方法子类查找,依次向父类查找从父类查找
调用构造器调用本类构造调用父类构造
其他当前对象子类访问父类对象

注解

Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 Java内置注解, 包括@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。

  • 准确覆写@Override
    表示当前的方法定义将覆盖父类中的方法
  • 过期声明@Deprecated
    表示代码被弃用,如果使用了被@Deprecated注解的代码则编译器将发出警告
  • 压制警告@SuppressWarnings
    表示关闭编译器警告信息
class A{
    public void test() {
    }
}

class B extends A{

    /**
        * 重载父类的test方法
        */
    @Override
    public void test() {
    }

    /**
        * 被弃用的方法
        */
    @Deprecated
    public void oldMethod() {
    }

    /**
        * 忽略告警
        * 
        * @return
        */
    @SuppressWarnings("rawtypes")
    public List processList() {
        List list = new ArrayList();
        return list;
    }
}

多态

方法多态性和对象多态性

方法多态性 方法重载、方法覆写

对象多态性

  • 对象向上转型 统一接收或返回的参数类型

class Instrument {
    public void play() {
        System.out.println("Instrument is playing...");
    }
}

class Wind extends Instrument {
    public void play() {
        System.out.println("Wind is playing...");
    }
}

class Percussion extends Instrument {
    public void play() {
        System.out.println("Percussion is playing...");
    }
}
public class PolymorphismDemo {
    public static void main(String[] args) {
        List<Instrument> instruments = new ArrayList<>();
        //向上转型
        instruments.add(new Wind());
        instruments.add(new Percussion());
        for (Instrument instrument : instruments){
            instrument.play();
        }

    }
}

  • 向下转型

产生向下转型前一定会发生向上转型,用于获取子类特殊的方法

        instrument.play();
        // 向下转型
        Percussion percussion = (Percussion) instrument

instanceof关键字

判断它左边的对象是否是它右边的类的实例.
上面的向下转型前可以通过instanceof判断是否进行过向上转型
if(instrument instanceof Percussion)

Object类

Java中唯一一个不存在继承关系的类
可以用来接收任意类的对象,包括数组那样的引用数据类型也可以 直接输出对象时会自动调用object类的toString方法,可以通过覆写该方法指定输出对象的信息

public class ObjectDemo {
    public static void main(String[] args) {
        Object obj = new int [] {1,2,3};// 向上转型
        if(obj instanceof int[]){
            int res[] = (int []) obj;// 向下转型
            System.out.println("向下转型"+ res);
            System.out.println("向下转型"+ res.toString());
        }
    }
}

quals()方法 Object类的equals方法

image.png

进行覆写

class EqualExample {
    private int x;
    private int y;
    private int z;

    public EqualExample(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public boolean equals(Object obj) {
        if(this == obj) return true;
        if(obj == null || !(obj instanceof EqualExample)) return false;

        EqualExample e = (EqualExample) obj;
        if(x != e.x) return false;
        if(y != e.y) return false;
        return  z == e.z;
    }
}

抽象类与接口

通过使用abstract关键字来定义抽象类和方法,抽象类和方法并不完善,只是约定结构。
子类必须强制性覆写父类抽象方法,除非它自己也声明为abstract类。
抽象类不能被实例化,抽象方法不能有主体,即不能实现
抽象类不一定要包含abstract方法。 也就是说,抽象类可以没有abstract方法,但是一旦类包含了abstract方法,则这个类必须声明为abstract
abstract只能修饰类和方法,不能修饰属性和其它的,抽象类可以有任意成员,也可以拥有自己的构造方法[抽象类本质还是类]。
抽象方法不能使用private、final 和static来修饰,因为这些关键字都是和重写相违背的。

public class AbstractClassDemo {
    public static void main(String[] args) {

        // AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated
        AbstractClassExample ac2 = new AbstractExtendClassExample();
        // static关键字 不受实例化对象或结构的限制
        AbstractClassExample ac3 = AbstractClassExample.getInstance();
        ac2.fun1();
        ac3.fun1();
    }
}

abstract class AbstractClassExample{
    protected int x;
    private int y;

    public abstract void fun1();

    public void fun2() {
        System.out.println("fun2");
    }
    public static AbstractClassExample getInstance(){
        return  new AbstractExtendClassExample();
    }
}

class AbstractExtendClassExample  extends  AbstractClassExample{
    @Override
    public void fun1() {
        System.out.println("func1");
    }
}

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造但子类总体上会保留抽象类的行为方式。

包装类

拆箱:从包装类中获取基本数据类型
装箱:将基本数据类型放入包装类中

基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
包装类对象都是引用类型,不会相等。1.5版本前只能通过手动方式实现装箱。

自动装箱都是通过包装类的 valueOf()方法来实现的.自动拆箱都是通过包装类对象的 xxxValue()来实现的

Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);// false
Integer x1 = Integer.valueOf(123);
Integer y1 = Integer.valueOf(123);
Integer x2 = Integer.valueOf(128);
Integer y2 = Integer.valueOf(128);
System.out.println(x1 == y1);// true
System.out.println(x2 == y2);// false

在 Java 8 中,Integer 缓存池的大小默认为 -128~127。所以会导致上面情况的产生
Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取同一个对象的引用

// 先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

包装对象的数值比较,不能简单的使用==,虽然-128 到 127 之间的数字可以使用缓存池,但是这个范围之外还是需要使用 equals 比较。
由于自动拆箱,如果包装类对象为 null,那么自动拆箱时就有可能抛出空指针异常NPE。 如果一个for循环中有大量拆装箱操作,也会浪费很多资源。