面向对象3

面向对象3

1.多态

概念

多态是继封装,继承之后,面向对象的第三大特性。

多态(多种形态)是同一个行为具有多个不同表现形式或形态的能力,多态就是同一个接口,使用不同的实例而执行不同操作。

1. 父类类型 变量名 = new 子类对象();
2. 变量名.方法名();
3. 父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误,如果有则调用子类重写的方法。 实际多态的好处是:可以使程序有良好的扩展性,并且可以对所有类的对象进行通用处理。

class Cat extends Animal{
    //新增属性
    private String color;

    @Override
    public void run() {
        System.out.println("猫跑起来了。。。");
    }

    //子类新增方法
    public void catchMouse(){
        System.out.println("抓老鼠!!!");
    }
}

class Dog extends Animal{
    //新增的属性
    private String kind;

    @Override
    public void run() {
        System.out.println("狗跑起来了。。。。");
    }

    //子类新增方法
    public void lookDoor(){
        System.out.println("看门!!!");
    }
}

public class Animal {
    //成员变量
    private String name;
    private int age;

    public void run(){
        System.out.println("动物跑起来。。。。");
    }
}


public class TestAnimal {

    public static void show(Animal animal){
        animal.run();
        //没有办法访问子类新增的方法
//        animal.lookDoor();
//        animal.catchMouse();
    }

    //多态测试类
    public static void main(String[] args) {
//        Animal animal = new Animal();
//        animal.run();
//
//        System.out.println("-----------------");
//        Cat cat = new Cat();
//        cat.run();

        Animal animal1 = new Cat();
        //animal1.run(); //cat的run方法
        Animal animal2 = new Dog();
        //animal2.run(); //dog的run方法
        Animal animal3 = new Animal();
        //animal3.run(); //animal的run方法

        System.out.println("--------------------");
        show(animal1);
        show(animal2);
        show(animal3);
    }
}

引用类转换

父子对象之间的转换分为向上转型和向下转型,他们区别如下:

向上转型:通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换

​ 注意:其意义在于当我们需要多个同父的对象调用某个方法时。通过向上转换后,则可以确定参数的统一,方便程序设计

​ 对象的向上转型(安全的):会失去子类新增。

​ 子类对象,被看做了父类的类型,那么就不能访问子类的新增,只能访问父类的属性和方法,以及子类重写。

向上转型的案例

/**
 * 向上转型的实例
 */
   Animal cat = new Cat();//向上转型
   cat.run();
   System.out.println("-------");

向下转型:通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换

为什么要转型

​ 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果有则编译报错,也就是说,不能调用子类拥有,而父类没有的方法,编译都会错误,更别说运行了。所以,要想调用子类特有的方法,必须向下转型。

instanceof关键字

在Java中向下转型是为了,通过父类强制转换成子类,从而来实现调用子类独有的方法。通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,如果不是则返回false。

instanceof:判断某个对象是否是某个类的实例:类以及继承的父类。

向下转型的案例

        Animal animalObj = new Cat();
        Cat catObj = (Cat) animalObj; //向下转型
        catObj.catchMouse();

2.Object类

概述

所有的类,都是以继承结构存在的,如果没有显示的父类,默认继承Object类。

class Person{}

相当于

class Person extends Object{}

class Student extends Person{} //学生类继承Person类。

class Animal{}

相当于

class Animal extends Animal{}

超类,基类,所有类的直接或间接父类,位于继承树的最顶层。

任何类如果没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承。

Object类中所有定义的方法,是所有对象都具备的方法。

Object类型可以存储任何对象

作为参数,可以接受任何对象。

作为返回值,可返回任何对象。

常用方法

getClass()

public final Class<?> getClass(){...}
返回引用中存储的实际对象类型。
应用:通常用于判断两个引用中实际存储对象类型是否一致。

hashCode()方法

public int hsahCode(){...}
返回该对象的十进制的哈希码值。
哈希算法根据对象的地址或字符串数字计算出来的int类型的数值

toString()方法

public String toString(){...}
返回该对象的字符串表示(表现形式)
可以根据程序需求覆盖方法,如:展示对象各个属性值。

equals()方法

public boolean equals(Object obj){...}
默认实现为(this == obj)。比较两个对象地址是否相同。
可进行覆盖,比较两个对象的内容是否相同。

equals重写步骤

比较两个引用是否指向同一个对象。

比较obj是否为null。

判断两个引用指向的实际对象类型是否一致。

强制类型转换。

依次比较各个属性值是否相同。

示例:

class Student extends Person{

}

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}



public class PersonTest {
    public static void main(String[] args) {
        Person person = new Person("zhangsan",23);
        Class<? extends Person> aClass = person.getClass(); //返回引用中存储的实际对象类型。
        System.out.println("aClass:"+aClass);

        Person person1 = new Person();
        Class<? extends Person> aClass1 = person1.getClass();
        System.out.println("aClass1:"+ aClass1);

        Student student = new Student();
        System.out.println(student.getClass());

        System.out.println("-----------------------");
        System.out.println("person.hashCode()):"+person.hashCode()); //是不是相同的内存地址
        System.out.println("person1.hashCode():"+person1.hashCode());

        System.out.println("-------------------------");
        System.out.println("person:"+person.toString()); //有时候我们不止想看这个对象的全类名。我们想看这个对象的成员变量的值

        System.out.println("--------------------------");
        Person person2 = new Person("zhangsan", 24);
        Person person3 = new Person("zhangsan", 24);
        System.out.println("person2 == person3:"+ (person2 == person3)); // ==比较的是内存地址
        System.out.println("person2.equals(person3):"+ person2.equals(person3)); //我们比较两个对象,其实我们是想比较一下对象的成员变量的值是否相等
    }
}

结果:
aClass:class com.lanou.part3.Person
aClass1:class com.lanou.part3.Person
class com.lanou.part3.Student
-----------------------
person.hashCode()):-1461067292
person1.hashCode():961
-------------------------
person:Person{name='zhangsan', age=23}
--------------------------
person2 == person3:false
person2.equals(person3):true

finalize()方法

当对象被判定为垃圾对象时,有JVM自动调用此方法。用以标记垃圾对象,进入垃圾回收队列。

垃圾对象:没有有效引用指向此对象时,为垃圾对象。

垃圾回收:有GC销毁垃圾对象,释放数据存储空间。

自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象。

手动回收机制:使用System.gc();通知JVM执行垃圾回收。

示例:

class Student extends Person{

    @Override
    protected void finalize() throws Throwable {
        System.out.println("Student finalize()方法!");
    }
}

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}



public class TestFinalize {
    public static void main(String[] args) {
        new Student();
        new Student();
        new Student();

        System.out.println("-----------");

        //手动gc  (手动回收机制)
        System.gc();
        System.out.println("回收垃圾!");
    }
}

结果:
    -----------
Student finalize()方法!
Student finalize()方法!
Student finalize()方法!

3.抽象类

程序是用来模拟现实世界,解决现实问题的;

现实世界中存在的都是“动物”具体的子类对象,并不存在“动物”对象,所以,Animal不应该独立创建成对象。

将子类的共同特征进行抽取,抽象成一个父类。如果有的功能在父类中定义了,但是没有具体的实现,那么可以定义成抽象的。等待子类实现即可。

作用:

​ 可被子类继承,提供共性属性和方法。

​ 可声明为引用,更自然的使用多态。

抽象类:包含抽象方法的类;

​ 被abstract修饰的类,称为抽象类。

​ 抽象类意为不够完整的类、不够具体的类。

抽象方法:没有方法体的方法。

抽象类与抽象方法的定义

抽象方法定义的格式

public abstract 返回值类型 方法名(参数);

抽象类定义的格式

public abstract class 类名{
}

示例:

class Cat extends Animal{

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

abstract class Dog extends Animal{ //如果不想实现抽象方法,那么可以自己定义成抽象类。

//    @Override
//    public void eat() {
//        System.out.println("狗吃肉!");
//    }
}
public abstract class Animal { //有抽象方法的类一定要定义成抽象类
    //定义一个抽象方法
    public abstract void eat();
}

注意事项

抽象类和抽象方法都需要被abstract修饰,抽象方法一定要定义在抽象类中。

抽象类不可以直接创建对象,原因:调用抽象方法没有意义。

只有覆盖了抽象类中所有得到抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类

之所以继承抽象类,更多的实在思想,是面对共性类型操作会更简单。

抽象类不允许实例化,因为可能包含有抽象方法,必须等待子类来继承,并且实现抽象方法,子类积极的实现父类中的抽象方法,但是如果没有全部实现,那么子类也是抽象的,要在等子类来继承,并且实现抽象方法。

重要的结论:抽象类和类作比较,除了不能实例化外,没有任何区别。

抽象类一定是一个父类,抽象类中可以不定义抽象方法,抽象类的意义是:不让该类创建对象,方法可以直接让子类去使用。

abstract关键字:不能和static,private,final等关键字搭配使用。

abstract与static
abstract用来声明抽象方法,抽象方法没有方法体,不能被直接调用,必须在子类overriding后才能使用,
static用来声明静态方法,静态方法可以被类及其对象调用
static和abstract不能同时使用
用static声明的方法表示这个方法在不生成类的实例时可直接被类调用,而abstract方法不能被调用,两者矛盾。

4.接口

概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量,构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK7以前),默认方法和静态方法(JDK8),私有方法(JDK9)。

接口的定义和类的定义方法类似,但是使用interface关键字,他会编译成一种class文件,但是一定要明确它并不是一个类,而是另一种引用数据类型。

​ 引用数据类型:数组,类,接口

接口是一种约定,锲约,一种规范。

接口的定义

含有抽象方法

抽象方法:使用abstract关键字修饰,可以省略,没有方法体。该方法提供子类实现使用。

代码如下:

public interface InterFaceName{
	public abstract void method();
}
含有默认方法和静态方法

默认方法用abstract来修饰,可以省略,没有方法体,该方法提供子类实现使用。

静态方法用static修饰,供接口直接调用。

代码如下:

public interface InterFaceName{
	public default void method1(){
		//执行语句
	}
	public static void method2(){
		//执行语句
	}
}
含有私有方法和私有静态方法

私有方法:使用private修饰,供接口中的默认方法或静态方法调用。

代码如下:

public interface InterFaceName{
	private default void method1(){
		//执行语句
	}
}

示例:

public interface Test{
	public static final double PI=3.14;//定义的变量会默认加上public static final
	public abstract void test1();
	public void test2();//默认加上public abstract
	public default void test3(){ //显示声明一个default
		System.out.println("wowowo");
	}
}

接口实现

接口要有实现类来实现接口中的抽象方法

子类和抽象父类的关系是:extends

实现类和接口的关系:implements

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。

非抽象子类实现接口:

1.必须重写接口中所有抽象方法。

2.继承了接口的默认方法,即可以直接调用,也可以重写。

实现格式:

class 类名 implements 接口名{
	//重写接口中抽象方法(必须)
	//重写接口中默认方法(可选)
}

注意:

​ 接口中抽象方法必须全部实现

​ 接口中默认方法可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。

​ 接口中静态方法只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。

示例:

class Mouse implements Usb{

    @Override
    public void start() {
        System.out.println("鼠标开始工作!");
    }

    @Override
    public void stop() {
        System.out.println("鼠标结束工作!");
    }

    //重写
    @Override
    public void fun1() {
        System.out.println("鼠标的默认方法!");
    }
}

class Keyboard implements Usb{

    @Override
    public void start() {
        System.out.println("键盘开始工作!");
    }

    @Override
    public void stop() {
        System.out.println("键盘开始工作!");
    }
}


public interface Usb { //接口
    //开始工作
    public abstract void start();

    //结束工作
    public abstract void stop();

    //默认方法
    public default void fun1(){
        System.out.println("默认方法。。。。。");
    }
    //静态方法
    public static void fun2(){
        System.out.println("静态方法。。。。");
    }
    
}
一个类可以实现多个接口

​ 接口最重要的体现:解决多继承的弊端,将多继承这种机制在Java中通过多实现完成了

interface Fu1{
    public void fun1();
}

interface Fu2{
    public void fun2();
}

/**
 * 一个类可以实现多个接口
 */
public class Usb2 implements Fu1,Fu2{
    @Override
    public void fun1() {

    }

    @Override
    public void fun2() {

    }
}

怎么解决多继承的弊端?

弊端:多继承时,当多个父类中有相同的功能时,子类调用会产生不确定性,其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行那个主体内容。

为什么实现能解决

解决:因为接口中的功能没有方法体,由子类来明确。

一个类继承类同时可以实现接口

接口和类之间可以通过实现产生关系,同时也学习了类与类之间可以通过继承产生关系。当一个类继承另一个父类,他又需要扩展额外的功能,这时接口就派上用场了。

子类通过继承父类来实现扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能,这时通过接口来实现。

示例:

public class Fu {
    public void show(){

    }
}

public interface Inter {
    public abstract void show2();
}

public class ZI extends Fu implements Inter{
    @Override
    public void show2() {

    }
}

接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能

接口的多继承

多个接口之间可以使用extends进行继承

示例:

public interface Fu1{
	void show1();
}

public interface Fu2{
	void show2();
}

public interface Fu3{
	void show3();
}

public interface Zi extends Fu1,Fu2,Fu3{
	void show();
}

示例2

public interface IFinger{
	void finger();//指纹识别
}

public interface IFace{
	void face();//人脸识别
}

public interface IDoor extends IFace,IFinger{
	//开门
	public void open();
	//关门
	public void close();
}

在开发过程中如果多个接口中存在相同方法,这时若有个类实现了这些接口,那么就要实现接口中的方法,由于接口的方法是抽象方法,子类实现后也不会发生调用的不确定性。

和抽象类的区别

相同点:

​ 都存在抽象方法

​ 不能创建对象,不能实例化。

​ 可以作为引用类型。

​ 具备Object类中所定义的方法。

不同点:

​ 所有属性都是公开静态常量,隐式使用public static final修饰。

​ 所有方法都是公开抽象方法,隐式使用public abstract修饰。

​ 没有构造方法,动态代码块,静态代码块。

接口的好处

接口的出现降低了耦合性

设计与实现完全分离,更容易更具体内容。

更容易搭建程序框架。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值