java基础-第5章-面向对象(三)

简介: 所有属性都是公开静态常量,隐式使用public static final修饰。所有方法都是公开抽象方法,隐式使用public abstract修饰。没有构造方法、动态代码块、静态代码块。接口的好处接口的出现降低了耦合性设计与实现完全分离,更容易更换具体实现。更容易搭建程序框架。

一、多态

概念

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

生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也

是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

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


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

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的重写方法。

使用多态的好处是,可以使程序有良好的扩展,并可以对所有类的对象进行通用处理

实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展

性与便利。


public class Animal {
    public void eat(){
        System.out.println("动物吃东西。。。。");
    }
}
class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼。。。");
    }
    public void work(){
        System.out.println("抓老鼠。。。");
    }
}
class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃肉。。。。");
    }
    public void work(){
        System.out.println("看家。。。。");
    }
}

package com.tuling.part1;


public class Demo_Polymorphic {
    public static void show(Animal a){
        会根据传入的参数的不同,调用不同的子类重写的方法,体现了多态的特征
        a.eat();//分别调用子类的重写,体现了多态的特征
    }
    public static void main(String[] args){
        show(new Animal());
        show(new Cat());//以Cat为对象调用show方法;
        show(new Dog());//以Dog为对象调用show方法;
    }
}

引用类型转换

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

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


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


对象的向上转型(安全的):会失去子类新增。 子类对象,被看做了父类的类型。那么就不能访问子类的新增, 只能访问父类的属性和方法。以及子类重写。


package com.funtl.oop.demo4;
/**
 * 向上转型案例
 */
public class HelloUp {
    public static void main(String args[]) {
        // 通过子类去实例化父类
        A a = new B();
        a.print();
    }
}
class A {
    public void print() {
        System.out.println("A:print");
    }
}
class B extends A {
    @Override
    public void print() {
        System.out.println("B:print");
    }
}

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

为什么要转型

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥

有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子

类特有的方法,必须做向下转型。

** instanceof关键字**

在 Java 中,向下转型则是为了,通过父类强制转换为子类,从而来调用子类独有的方法,为了保证向下转型的顺利完成,在 Java 中提供了一个关键字 instanceof, 通过 instanceof 可以判断某对象是否是某类的实例,如果是则返回 true, 否则为 false

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

// 向上转型 (B 是 A 的子类)
A a = new B();
// 返回 true
a instanceof A;
// 返回 true
a instanceof B;
// 返回 false
a instanceof C;
1
2
3
4
5
6
7
8
9
package com.funtl.oop.demo5;
/**
 * 向下转型
 */
public class HelloDown {
    public static void func(A a) {
        a.print();
        if (a instanceof B) {
            // 向下转型,通过父类实例化子类
            B b = (B) a;
            // 调用 B 类独有的方法
            b.funcB();
        } else if (a instanceof C) {
            // 向下转型,通过父类实例化子类
            C c = (C) a;
            // 调用 C 类独有的方法
            c.funcC();
        }
    }
    public static void main(String args[]) {
        func(new A());
        func(new B());
        func(new C());
    }
}
class A {
    public void print() {
        System.out.println("A:print");
    }
}
class B extends A {
    @Override
    public void print() {
        System.out.println("B:print");
    }
    public void funcB() {
        System.out.println("funcB");
    }
}
class C extends A {
    @Override
    public void print() {
        System.out.println("C:print");
    }
    public void funcC() {
        System.out.println("funcC");
    }
}

父类 对象1 = new 父类();

子类 对象2 = new 子类();

父类 对象3 = new 子类();

结论:

看一个对象能够访问哪些成员,看=左边是定义的是什么类型

父类类型:只能访问父类的属性和方法,

子类类型:可以访问父类的属性和方法,子类新增,子类重写

看一个对象最终访问哪个方法,看=右边是什么类型的对象

父类对象:父类属性和方法

子类对象:父类的属性和方法,以及子类重写的方法


二、Object类

概述

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

class Person{}

相当于

class Person extends Object{}

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

class Animal{}

相当于

class Animal extends Object{}

class Cat extends Animal{}

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

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

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

Object类型可以存储任何对象。

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

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


常用方法

getClass()

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

hashCode()方法


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

toString()方法


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

equals()方法

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

equals重写步骤:

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

判断obj是否为null。

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

强制类型转换。

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

示例:

public class Cat{
    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;
    }
//重写equals方法
   Cat cat1 = new Cat("wowo",23);
   Cat cat2 = new Cat("wowo",23);
    cat1.equals(cat2);
    //重写equals的意义:有时候我们不想判断内存是否相等,我们就想知道属性是否相同。
//    @Override
//    public boolean equals(Object obj) {//在这里的时候,会发生向上转型
//        if(this==obj){//step1:如果两个内存地址一样,那么我们认为内容是一样的。内存地址一样,是同一个对象
//            return true;
//        }
//        if(obj instanceof Cat){//step2:先判断obj是否是Cat类型,为向下转型准备
//            Cat cat = (Cat)obj;//step3:强制转换,向下转型,
//            if(this.getAge()==cat.getAge()&&this.getName().equals(cat.getName())){//step4:判断属性是否相等
//                return true;
//            }
//        }
//
//        return false;
//    }
//    public String toString(){
//        return "Cat {name="+name+",age="+age+"}";
//    }
}

 

Cat cat = new Cat();
        cat.setAge(20);
        cat.setName("机器猫");
        int hashCode = cat.hashCode();
        System.out.println("hashCode");
        Cat cat2 = new Cat();
        cat2.setAge(20);
        cat2.setName("机器猫");
        int hashCode2 = cat2.hashCode();
        System.out.println("hashCode2:"+hashCode2);
        Class c1 = cat2.getClass();//获取对象运行时所属的类
        System.out.println(c1);//
        //toString()方法
        System.out.println(cat.toString());
        System.out.println(cat2.toString());//调用默认的toString方法
        //
        //
        int i = 12;
        System.out.println(Integer.toBinaryString(i));//10进制转二进制
        System.out.println(Integer.toOctalString(i));//10进制转8进制
        System.out.println(Integer.toHexString(i));//10进制转16进制

finalize()方法

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

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

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

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

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

示例:

public class TestFinalize {
  public static void main(String[] args) {
//  Student s1=new Student("aaa", 20);
//  Student s2=new Student("bbb", 20);
//  Student s3=new Student("ccc", 20);
//  Student s4=new Student("ddd", 20);
//  Student s5=new Student("eee", 20);
  new Student("aaa", 20);
  new Student("bbb", 20);
  new Student("ccc", 20);
  new Student("ddd", 20);
  new Student("eee", 20);
  //回收垃圾
  System.gc();
  System.out.println("回收垃圾");
  }
}

三、抽象类

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

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

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

作用:

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

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

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

被abstract修饰的类,称为抽象类;

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

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


抽象类与抽象方法的定义

抽象方法定义的格式

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

抽象类定义的格式


public abstract class 类名 {
}

示例1:

//研发部员工 
public abstract class Developer {
  public abstract void work();//抽象函数。需要abstract修饰,并分号;结束
}
//JavaEE工程师
public class JavaEE extends Developer{
  public void work() {
  System.out.println("正在研发淘宝网站");
  }
}
//Android工程师
public class Android extends Developer {
  public void work() {
  System.out.println("正在研发淘宝手机客户端软件");
  }
}

示例2:

public abstract class Animal {
    private String name;
    private int age;
    public abstract void work();//动物的工作,规定我的子类必须完成一件事
    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 class Cat extends Animal {
    @Override
    public void work() {
        System.out.println("猫去抓老鼠。。。。");
    }
}
public class Dog extends Animal {
    String kind;
    @Override
    public void work() {
        System.out.println("狗去看门。。。。");
    }
}

注意事项

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

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

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

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

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


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


抽象类一定是个父类 ?

是的,因为不断抽取而来的。

抽象类中是否可以不定义抽象方法。

是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用

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


1、abstract与static

what abstract:用来声明抽象方法,抽象方法没有方法体,不能被直接调用,必须在子类overriding后才能使用。 static:用来声明静态方法,静态方法可以被类及其对象调用。


how static与abstract不能同时使用。


why 用static声明方法表明这个方法在不生成类的实例时可直接被类调用,而abstract方法不能被调用,两者矛盾。


四、接口

概述

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么

接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法

(JDK 9)。

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并

不是类,而是另外一种引用数据类型。


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


接口就是一种约定,契约。一种规范。

例如生活中的USB接口,鼠标:键盘,U盘等等只要遵循了USB接口规范,那么就可以正常使用;


接口定义

含有抽象方法

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

代码如下:

public interface InterFaceName {
    public abstract void method();
}

含有默认方法和静态方法

默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。

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

代码如下:

public interface InterFaceName {
    public default void method() {
        // 执行语句
    }
    public static void method2() {
        // 执行语句   
    }
}

含有私有方法和私有静态方法

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

代码如下:

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

示例:

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

接口实现

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

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

实现类和接口的关系:implements

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

非抽象子类实现接口:


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

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

实现格式:

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

注意:

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

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

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

示例:

public interface USB {
    public void start();//开始工作
    public void end();//结束工作
}
public class Mouse implements USB {
    @Override
    public void start() {
        System.out.println("鼠标开始工作。。。");
    }
    @Override
    public void end() {
        System.out.println("鼠标结束工作。。。。");
    }
}
public class Keyboard implements USB {
    @Override
    public void start() {
        System.out.println("键盘开始工作。。。。。");
    }
    @Override
    public void end() {
        System.out.println("键盘关闭。。。");
    }
}

测试:

USB usb = new Mouse();
usb.start();
usb.end();
1
2
3
一个类可以实现多个接口
接口最重要的体现:解决单继承的弊端。将多继承这种机制在Java中通过多实现完成了
public interface Fu1{
  void show1();
}
public interface Fu2{
  void show2();
}
public class Zi implements Fu1,Fu2{// 多实现,同时实现多个接口
    public void show1();
    public void show2();
}

怎么解决多继承的弊端?


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


为什么多实现能解决呢?


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


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

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

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


public class Fu{
    public void show(){
    }
}
public interface Inter{
  public abstract void show1();
}
public class Zi extends Fu implements Inter{
  public void show1();
}


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


接口的多继承

学习类的时候,知道类与类之间可以通过继承产生关系,接口和类之间可以通过实现产生关系,那么接口与接口之间会有什么关系

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

示例1:

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:

package com.qf.part4_interface.sub1;
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修饰。

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

接口的好处

接口的出现降低了耦合性

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

更容易搭建程序框架。

相关文章
|
15天前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
19 1
|
28天前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
24 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
36 2
|
3月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
3月前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
36 4
|
3月前
|
Java
接口和抽象类【Java面向对象知识回顾②】
本文讨论了Java中抽象类和接口的概念与区别。抽象类是不能被实例化的类,可以包含抽象和非抽象方法,常用作其他类的基类。接口是一种纯抽象类型,只包含抽象方法和常量,不能被实例化,且实现接口的类必须实现接口中定义的所有方法。文章还比较了抽象类和接口在实现方式、方法类型、成员变量、构造方法和访问修饰符等方面的不同,并探讨了它们的使用场景。
接口和抽象类【Java面向对象知识回顾②】
|
2月前
|
存储 Java 程序员
Java基础-面向对象
Java基础-面向对象
19 0
|
4月前
|
Java 数据处理 开发者
【Java基础面试十二】、说一说你对面向对象的理解
这篇文章阐述了面向对象是一种以类和对象为基础,通过封装、继承和多态等概念来模拟现实世界中的事物及其相互关系的程序设计方法,它强调以事物为中心进行思考和系统构造,与结构化程序设计相比,更符合人类的自然思维方式。
【Java基础面试十二】、说一说你对面向对象的理解
|
3月前
|
安全 Java Go
面向对象程序设计语言:Java
Java语言语法和C语言和C++语言很接近,很容易学习和使用,Java丢弃了C++中很少使用的、很难理解的、令人迷惑的特性,Java语言不使用指针,而是引用,并提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧
74 2
|
4月前
|
Java
【Java基础面试十三】、面向对象的三大特征是什么?
这篇文章介绍了面向对象程序设计的三大基本特征:封装、继承和多态,其中封装隐藏对象实现细节,继承实现软件复用,多态允许子类对象表现出不同的行为特征。
【Java基础面试十三】、面向对象的三大特征是什么?