day6(多态、接口)

多态

 概述

是指同一行为,具有多个不同表现形式。

生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也 是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。

 概念及语法

概念

多态性,是面向对象中最重要的概念,在Java中的体现:父类的引用指向子类的对象

  • 可以直接应用在抽象类和接口上

  • Java引用变量有两个类型:编译时类型运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。

    • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)

    • 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

语法格式

父类类型 引用 = new 子类对象;
引用.方法名();

示例代码

复制代码
public abstract class Animal { 
    public abstract void eat(); 
}
​
class Cat extends Animal { 
    public void eat() { 
        System.out.println("吃鱼"); 
    }
}
​
class Dog extends Animal { 
    public void eat() { 
        System.out.println("吃骨头");
    }
}
​
public class Test { 
    public static void main(String[] args) { 
        // 多态形式,创建对象
        Animal a1 = new Cat(); 
        // 调用的是 Cat 的 
        eat a1.eat(); 
        // 多态形式,创建对象 
        Animal a2 = new Dog(); 
        // 调用的是 Dog 的 
        eat a2.eat();
    } 
}
复制代码

 

1.3 实现原理

1需要存在继承或者实现关系

2有方法的重写

虚拟方法调用(virtual method invocation)

正常的方法调用

Person e = new Person(); 
e.getInfo(); 
Student e = new Student(); 
e.getInfo();

 

多态情况下

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。这也叫做方法的动态绑定

Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法

编译时类型和运行时类型

编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定

左边类型是编译时类型,右边类型是运行时类型,调用方法要通过编译,所以父类的必须有调用方法,否者instanceof强转运行时类型调用运行时类型的方法

但是父类是左边可以确定,实例化具体的子类对象不能确定,这就是多态,动态绑定技术。类似于给你一个字,喊你说出这个字开头的成语,成语却不止一种。

内存图

 

 

属性不具有多态性

多态的好处

  • 可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。

  • 可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。

  • 接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。

  • 灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。

  • 简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

instanceof操作符

x instanceof A:检验x是否为类A的对象,返回值为boolean型。

  • 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。

  • 如果x属于类A的子类B,x instanceof A值也为true。

 对象类型转换

对Java对象的强制类型转换称为造型(Casting)

  • 从子类到父类的类型转换可以自动进行(多态)

  • 从父类到子类的类型转换必须通过造型(强制类型转换)实现

  • 无继承关系的引用类型间的转换是非法的

  • 在造型前可以使用instanceof操作符测试一个对象的类型

 

 

示例代码

复制代码
public class Test {
    public void method(Person e) { 
        // 设Person类中没有getschool() 方法
        // System.out.pritnln(e.getschool()); //非法,编译时错误
        if (e instanceof Student) {
            Student me = (Student) e; // 将e强制转换为Student类型
            System.out.pritnln(me.getschool());
        }
    }
    
    public static void main(String[] args){
        Test t = new Test();
        Student m = new Student();
        t.method(m);
    } 
}
复制代码

 

代码块

  • 代码块(或初始化块)的作用:

    • 对Java类或对象进行初始化

  • 代码块(或初始化块)的分类:

    • 一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块(static block),没有使用static修饰的,为非静态代码块。

  • static代码块通常用于初始化static的属性

复制代码
class Person { 
​
    public static int total; 
​
    static {
​
        total = 100;//为total赋初值
​
    }
​
    …… //其它属性或方法声明 
​
}
复制代码

 

  • 静态代码块:用static修饰的代码块

    1. 可以有输出语句。

    2. 可以对类的属性、类的声明进行初始化操作。

    3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。

    4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。

    5. 静态代码块的执行要先于非静态代码块。

    6. 静态代码块随着类的加载而加载,且只执行一次。

  • 非静态代码块:没有static修饰的代码块

    1. 可以有输出语句。

    2. 可以对类的属性、类的声明进行初始化操作。

    3. 除了调用非静态的结构外,还可以调用静态的变量或方法。

    4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。

    5. 每次创建对象的时候,都会执行一次。且先于构造器执行。

示例代码

  • 变量赋值总结

 

 

Object

 介绍

  • Object类是所有Java类的根父类

  • 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object

public class Person {
...
}
等价于:
public class Person extends Object {
...
}

主要结构

 

 

 equals()方法

  • equals():所有类都继承了Object,也就获得了equals()方法。还可以重写。

    • 只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。

    • 格式:obj1.equals(obj2)

  • 特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对象;

    • 原因:在这些类中重写了Object类的equals()方法。

  • 当自定义使用equals()时,可以重写。用于比较两个对象的“内容”是否都相等

  • 重写equals()方法的原则

    • 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。

    • 自反性:x.equals(x)必须返回是“true”。

    • 传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。

    • 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。

    • 任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。

面试题:==和equals的区别

  1. ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址

  2. equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中** 用的比较多,久而久之,形成了equals是比较值的错误观点。

  3. 具体要看自定义类里有没有重写Object的equals方法来判断。

  4. 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。

 

toString()方法

  • toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。

  • 在进行String与其它类型数据的连接操作时,自动调用toString()方法

    Date now=new Date();
    System.out.println(“now=+now);相当于System.out.println(“now=+now.toString());
  • 可以根据需要在用户自定义类型中重写toString()方法。如String类重写了toString()方法,返回字符串的值。

    s1=“hello”;
    System.out.println(s1);//相当于System.out.println(s1.toString());
  • 基本类型数据转换为String类型时,调用了对应包装类的toString()方法

    int a=10; System.out.println(“a=+a);

接口

 概述

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。

  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都 支持USB连接。

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。

  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。

 

 语法

  • 接口(interface)是抽象方法和常量值定义的集合。

  • 接口的特点:

    • 用interface来定义。

    • 接口中的所有成员变量都默认是由public static final修饰的。

    • 接口中的所有抽象方法都默认是由public abstract修饰的。

    • 接口中没有构造器。

    • 接口采用多继承机制。

  • 定义Java类的语法格式:先写extends,后写implements

    class SubClass extends SuperClass implements InterfaceA{ }  
  • 一个类可以实现多个接口,接口也可以继承其它接口。

  • 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。

  • 接口的主要用途就是被实现类实现。(面向接口编程)

  • 与继承关系类似,接口与实现类之间存在多态性

  • 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。

接口与抽象类

 

 

 

 接口的改进

Java 8中,可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

  • 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

  • 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。

 内部类

 概述

  • 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

  • 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

  • Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。

    • Inner class的名字不能与包含它的外部类类名相同;

  • 分类:

    • 成员内部类(static成员内部类和非static成员内部类)

    • 局部内部类(不谈修饰符)、匿名内部类

  • 成员内部类作为类的成员的角色:

    • 和外部类不同,Inner class还可以声明为privateprotected

    • 可以调用外部类的结构

    • Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;

  • 成员内部类作为类的角色:

    • 可以在内部定义属性、方法、构造器等结构

    • 可以声明为abstract类 ,因此可以被其它的内部类继承

    • 可以声明为final

    • 编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

注意

  1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。

  2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式

  3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据

  4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

示例代码

复制代码
class Outer {
    private int s;
    public class Inner {
        public void mb() {
            s = 100;
            System.out.println("在内部类Inner中s=" + s);
        } 
    }
    
    public void ma() {
        Inner i = new Inner();
        i.mb();
    } 
}

public class InnerTest {
    public static void main(String args[]) {
        Outer o = new Outer();
        o.ma();
    } 
}
复制代码

 

 内部类的使用语法

如何实例化成员内部类

如何在成员内部类中调用外部类的结构

//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();

 

 匿名内部类

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

格式:

复制代码
new 父类构造器(实参列表)|实现接口(){ 

    //匿名内部类的类体部分 

}

new Animal(){
    // 方法重写
}
复制代码

 

匿名内部类的特点

  • 匿名内部类必须继承父类或实现接口

  • 匿名内部类只能有一个对象

  • 匿名内部类对象只能使用多态形式引用

示例代码

复制代码
interface A{
    public abstract void fun1();
}

public class Outer{
    public static void main(String[] args) {
        new Outer().callInner(new A(){
        //接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名
            public void fun1() {
                System.out.println(“implement for fun1");
            }
        });// 两步写成一步了
    }
            
    public void callInner(A a) {
        a.fun1();
    }
}
复制代码

我的练习:

复制代码
package Demo1.JavaSe.NeusoftEdu.zuoye;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @program: FirstDemo
 * @description: 东软实训第六天
 * @author: GuoTong
 * @create: 2020-08-18 08:43
 **/
public class day6 extends TestDay6 {
    public static void main(String[] args) {
        day6 day6 = new day6();
        //Class 代表.class文件加载到内存中代表这个类的对象的类型。
        Class<? extends Demo1.JavaSe.NeusoftEdu.zuoye.day6> aClass = day6.getClass();
        //包装类
        int i = Integer.parseInt("349");
        Integer integer = Integer.valueOf(10);
        Integer dsfs = Integer.getInteger("dsfs");

        //instanceof 关键字:在运行时(动态监测)所属对象
        System.out.println(day6 instanceof TestDay6);
        //配合多态
        TestDay6 testDay6 = new day6();
        System.out.println(testDay6 instanceof day6);
    }


}

class TestDay6 {
    private String name;
    private int number;


    public static void main(String[] args) {
        /*String str1= “abc”; 在编译期,JVM会去常量池来查找是否存在“abc”,
        如果不存在,就在常量池中开辟一个空间来存储“abc”;
        如果存在,就不用新开辟空间。
        然后在栈内存中开辟一个名字为str1的空间,来存储“abc”在常量池中的地址值。
        String str3 = new String("abcd")的实现过程:直接在堆中创建对象。
        如果后来又有String str4 = new String("abcd"),str4不会指向之前的对象,
        而是重新创建一个对象并指向它,所以如果此时进行str3==str4返回值是false,
        因为两个对象的地址不一样,如果是str3.equals(str4),返回true,因为内容相同。
         *
         */
        //String 重写了equals
        String index = "123";
        String index1 = "123";
        System.out.println(index == index1);//true
        System.out.println(index.equals(index1));//true

        String index3 = new String("123456");
        String index4 = new String("123456");
        System.out.println(index3 == index4);//false
        //==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
        System.out.println(index3.equals(index4));//true
    }

}

class TestClassNX {
    public static void main(String[] args) {
        int it = 65;
        float fl = 65.0f;
        System.out.println("65和65.0f是否相等?" + (it == fl));//true
        char ch1 = 'A';//ASCII:65
        char ch2 = 12;
        System.out.println("65和'A'是否相等?" + (it == ch1));//true
        System.out.println("12和ch2是否相等?" + (12 == ch2));//true
        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println("str1和str2是否相等?" + (str1 == str2));//比较地址:false
        System.out.println("str1是否equals str2?" + (str1.equals(str2)));//true,String 重写过equals
        // System.out.println("hello" == new java.util.Date());//编译报错
        System.out.println("hello" .equals(new java.util.Date()) );
    }
}

@Data
@AllArgsConstructor
class GeometricObject {
    private String color;
    private double weight;

    public double findArea() {
        return 1;
    }

    public static void main(String[] args) {
        GeometricObject geometricObject = new Circle11("红色", 10, 20);
        GeometricObject geometricObject1 = new MyRectangle("绿色", 10, 10, 10);
        boolean b = equalsArea(geometricObject, geometricObject1);
        System.out.println(b);
    }

    public static boolean equalsArea(GeometricObject geometricObject, GeometricObject geometricObject1) {
        double area = 0;
        double area1 = 0;
        if (geometricObject instanceof Circle11) {
            area = ((Circle11) geometricObject).findArea();
            System.out.println(area);
        }
        if (geometricObject1 instanceof MyRectangle) {
            area1 = ((MyRectangle) geometricObject1).findArea();
            System.out.println(area1);
        }
        return area == area1;
    }

}

class Circle11 extends GeometricObject {
    private double redius;

    public Circle11(String color, double weight, double redius) {
        super(color, weight);
        this.redius = redius;
    }

    @Override
    public double findArea() {
        System.out.println("圆形");
        return Math.PI * redius * redius;
    }


}

class MyRectangle extends GeometricObject {
    private double width;
    private double height;

    public MyRectangle(String color, double weight, double width, double height) {
        super(color, weight);
        this.width = width;
        this.height = height;
    }

    @Override
    public double findArea() {
        System.out.println("矩形");
        return width * height;
    }
}

class Personx {
    protected String name = "person";
    protected int age = 50;

    public String getInfo() {
        return "Name: " + name + "\n" + "age: " + age;
    }
}

class Studentx extends Personx {
    protected String school = "pku";

    public String getInfo() {
        return "Name: " + name + "\nage: " + age + "\nschool: " + school;
    }
}

class Graduatex extends Studentx {
    public String major = "IT";

    public String getInfo() {
        return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;
    }
}

class InstanceTest {
    public static void method(Personx e) {
        System.out.println(e.getInfo());
        if (e instanceof Personx)
            System.out.println("a person");
        if (e instanceof Studentx)
            System.out.println("a student");
        if (e instanceof Graduatex)
            System.out.println("a graduated student");
    }

    public static void main(String[] args) {
        Personx e = new Personx();
        method(e);
        System.out.println("===========");
        Personx e1 = new Studentx();
        method(e1);
        System.out.println("===========");
        Personx e2 = new Graduatex();
        method(e2);
    }

}
复制代码

 

 

 

posted on   白嫖老郭  阅读(117)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示