当前位置: 首页 > news >正文

Java【13_2】多态、根父类

1. 多态 ★
面向对象三大特性之一!
1.1 定义
是指同一行为,具有多个不同表现形式(重载和重写)

1.2 前提
① 父子类【继承父类或者实现接口】
② 必须有重写
③ 父类引用指向子类对象

1.3 多态的语法
父类引用指向子类对象
举例:Person per1 =new Employee();
    per1.eat();//从名义上是调用的Person类的eat方法,但是实际是员工餐

1.4 多态的注意事项
使用不了子类独有的内容!
因为名义是父类类型,就只能调用到父类中存在的,如果子类有重写
这个是可以调用到子类的方法的!如果没有重写就是父类的
总结:多态只能调用父类的内容!
成员变量
子类独有的访问不到,如果有重名属性,调用属性看类型!

1.5 多态的用途
① 多态参数 ★
② 多态数组 -> 对象数组的升级

2. 父子类之间的类型转换
2.1 自动转换(向上转型-多态的提现)  小->大
2.2 强制转换(向下转型) 大->小
    因为多态,调用不到子类独有的内容!我就是想调用(很少见)
    向下转型是有风险!所以以后能少强转就少强转!
语法:子类类型 对象名=(子类类型)父类引用;
强转之前一定要做判断!
    instanceof   判断前面的对象是否属于后面的类型
举例:boolean flag=employee instanceof Employee;
练习:
创建若干个Student或者Employee对象,存储起来!
    循环遍历每个成员,调用其eat方法!
    如果是员工就调用working
    如果是学生就调用学习方法

3. native 本地的、原生的
只能修饰方法
追踪源码的时候,会见到这个关键字!方法不是java代码实现,是c语言实现
native修饰的方法,是可以被重写的!

3.1 不能和abstract一起使用的修饰符?
    (1)final:和final不能一起修饰方法和类
    (2)static:和static不能一起修饰方法
    (3)native:和native不能一起修饰方法 记住
    (4)private:和private不能一起修饰方法
3.2 static和final一起使用:
    (1)修饰方法:可以,因为都不能被重写
    (2)修饰成员变量:可以,表示静态常量
    (3)修饰局部变量:不可以,static不能修饰局部变量
    (4)修饰代码块:不可以,final不能修饰代码块
    (5)修饰内部类:可以一起修饰成员内部类,不能一起修饰局部内部类(后面讲)

4. Object   根父类
默认是类的父类!该类中所有的方法都可以被任意对象继承下去!
根据api去学!
构造器:
    Object();
方法:
① toString();
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
② Class getClass();反射位置,会遇到此方法!
    功能:返回对象的运行时类型(类加载的时候,会创建一个Class对象)
③ int hashCode();
    将当前对象,通过哈希算法,得到一个int值
    两对相同对象(地址一样的):经过相同的hash算法,得到的int值肯定是一样的!
    两个不同的对象:经过相同的hash算法,得到的int值,也有可能一样,大概率是不一样的
        集合-->HashMap集合的时候,会遇到hashCode方法!
④ finalize()
    java存在垃圾回收机制的!(自动的机制)
    java中什么样的对象,会被认为是垃圾?
        没有引用的对象
    java中被认为是垃圾的对象,什么时候会被垃圾回收机制回收?
        不定时回收
    垃圾对象被回收的时候,是会默认调用该对象的finalize方法!
        目的:是让这个对象做一个临终遗言(这个方法不是回收的代码)
⑤  equals(Object obj)
    判断两个对象是否一致!
    == 主要是判断两个地址是否一致!
    源码:和==没有区别
    public boolean equals(Object obj) {
        return (this == obj);
    }

5. String类的equals方法
两个对象,this/anObject对比内容是否一致
    public boolean equals(Object anObject) {
        //地址是否一样,如果地址一样,内容肯定一样
        if (this == anObject) {
            return true;
        }
        //判断anObject是否是String类型,如果不是String类型,直接返回false
        if (anObject instanceof String) {
            //向下转型
            String anotherString = (String)anObject;
            //char[] value; n->this的字符串长度
            int n = value.length;
            //判断长度是否一样,如果长度不一样,返回false
            if (n == anotherString.value.length) {
                //v1是this的字符串内容
                char v1[] = value;
                //v2是参数字符串的内容
                char v2[] = anotherString.value;
                //挨个字符的对比,从头到尾,中途有不一样的,返回false
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                //从头到尾都没有不一样的,说明每个字符都一样(内容是一样的)
                return true;
            }
        }
        return false;
    }

练习:
创建两个Person对象,我认为,只要name和id都一样,就是同一个人!
在Person类中,重写equals方法!(对比内容,id和name)

6. 接口
usb接口、type-c接口...
usb接口的规范又是哪来的?有人定制了这个规范,然后厂商按照这个规范生产
接口-->定义规则
6.1 接口的语法
    【修饰符】interface 接口名{}   -->类似于类的语法
        定义接口的修饰符和类的修饰符知识点一样!

6.2 分类
    ① JDK1.8之前
        a. 接口中所有的成员变量,默认都是公有的静态常量(不允许修改访问修饰符)
        b. 接口中所有的成员方法,默认都是公有的抽象方法
        c. 接口中是没有构造器的
        d. 没有初始化块
    ② JDK1.8的新特性
        默认方法和静态方法

6.3 特点:
    a. 接口不能实例化对象
    b. 只能作为父级(父接口)
        子类继承父类
        实现类实现父接口
    c. 实现类实现父接口,并实现父接口中所有的抽象方法
        public class Aircraft implements Fly {抽象方法实现}
    d. 如果类有父接口,还可以有父类吗?可以的!
        类只能有一个父类,可以有多个父接口!
        public class Aircraft extends Person implements Fly,Attack  {

6.4 关系
    a. 类与类之间:单继承
    b. 类与接口之间:多实现
    c. 接口与接口之间:多继承

讲课顺序:
JDK1.8新特性
冲突问题如何解决
介绍经典的接口(感受接口的用途)  好处:解耦合


示例:1. 多态 ★ 
//是指同一行为,具有多个不同表现形式(重载和重写)
//使用不了子类独有的内容!

public class Person {
    public int id;
    public String name="父亲";

    public Person() {
        super();//父类中没有这个构造器
    }

    public void eat(){
        System.out.println("吃饭");
    }

    public void run(){
        System.out.println("奔跑");
    }
}

public class Employee extends Person {
    public double salary=2000;
    public String name="打工人";

    @Override
    public void eat() {
        System.out.println(name+"吃员工餐"+salary);
    }

    public void working(){
        System.out.println("打工人在工作");
    }

    /*public String toString(){
        return name+"\t"+salary;
    }*/

    //必须要调用才可以!
    public String getInfo(){
        return name+""+salary;
    }

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

public class Student extends Person {
    public double score;

    @Override
    public void eat() {
        System.out.println("吃食堂。。。");
    }

    public void study(){
        System.out.println("在学习");
    }
}

public class Demo1 {
    public static void main(String[] args) {
        //父类的引用指向子类的对象
        //per1的类型是Person,但是实际类型是Employee
        Person per1 =new Employee();
        per1.eat();//从名义上是调用的Person类的eat方法,但是实际是员工餐
        //per1.working();//【报错】因为是子类独有方法,调用不到
        //per1.run();

        //System.out.println(per1.id);
        //System.out.println(per1.name);//name调用的是谁的?
        //System.out.println(per1.salary);//【报错】这也是子类中的,调用不到
        //属性看类型  方法看对象


        //per2的类型是Person,但是实际类型是Student
        Person per2=new Student();
        per2.eat();//名义上是Person,实际是食堂
        //per2.study();//【报错】……
    }
}

D:\javademo\day13_am\Demo>javac Demo1.java
D:\javademo\day13_am\Demo>java Demo1
打工人吃员工餐2000.0
吃食堂。。。


示例:延续上面代码,多一个Demo2.java测试
【04】38:30 通过传参体现的
//person.eat();//同一个行为,具有不同的体现形式

public class Demo2 {
    public static void main(String[] args) {
        Employee employee=new Employee();
        method(employee);//Employee对象是可以通过参数传递给,Person类型的

        Student student=new Student();
        method(student);//Student对象也是可以通过参数传递给Person类型的
    }

    public static void method(Person person){//Person person=new Employee();
        person.eat();//同一个行为,具有不同的体现形式
    }
    /*
       安排所有人吃饭
     */
    /*public static void method1(Employee employee){
        employee.eat();
    }
    public static void method2(Student stu){
        stu.eat();
    }*/

}

【08】00:20
属性、静态方法没有多态性
因为多态前提:有重写,属性是没有覆盖的。属性只看它前面的类型是谁。
因为静态方法不允许 重写。


示例:为什么 str.toString() 不是返回地址呢?
【路径:D:\javademo\day13_pm\Employee.java】
package com.atguigu.bean;

public class Employee {
    public double salary=2000;
    public String name="打工人";

    /*@Override
    public String toString() {
        return "Employee{" +
                "salary=" + salary +
                ", name='" + name + '\'' +
                '}';
    }*/
}

【路径:D:\javademo\day13_pm\Demo5.java】
package com.atguigu.demo;

import com.atguigu.bean.Employee;

public class Demo5 {
    public static void main(String[] args) {
        Employee employee=new Employee();
        Object obj=new Object();//这个对象没有什么意义(供子类使用)

        String s=employee.toString();
        System.out.println(s);//输出的是地址经过处理的!地址
        System.out.println(employee);
    }
}


D:\javademo\day13_pm>javac -d . Employee.java
D:\javademo\day13_pm>javac -d . Demo5.java
D:\javademo\day13_pm>java com.atguigu.demo.Demo5
com.atguigu.bean.Employee@15db9742
com.atguigu.bean.Employee@15db9742


【idea软件中】上面代码中,按住ctrl点击 toString() 跳到源代码如下:
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

【结论】所有的对象,在输出或者拼接的时候,都会默认调用该对象的toString方法!(你不写toString,也会调)
该toString方法,把当前对象的地址值算成“哈希值”,然后再转成16进制,
"@" 前面是对象运行时类型
"@" 后面可以看成是地址值


下面,来试试String类的toString
public class Demo5 {
    public static void main(String[] args) {
        //创建String对象
        String str=new String("abc");
        System.out.println(str.toString());
        System.out.println(str);//为什么输出的不是地址呢?输出的是值
    }
}

D:\javademo\day13_pm>java com.atguigu.demo.Demo5
abc
abc


【此时】按住ctrl点击 toString() 跳到源代码如下:
    public String toString() {
        return this;
    }
【思考】为什么调用的是自己的toString呢?为什么不是Object中的toString?
【因为】因为重写了!!!
【因为】因为String觉得Object类中的toString不能满足我的需求,所以重写!

粗略看一下String这个类:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ……
    public String toString() {
        return this;
    }
    ……
}


【上上面employee对象】
直接输出employee对象的时候,输出其所有属性信息!该如何操作呢?
Object中toString不能满足我的要求,所以我也要重写!
【上上面的灰色注释去掉,相当于定义自己的重写】

D:\javademo\day13_pm>java com.atguigu.demo.Demo5
Employee{salary=2000.0, name='打工人'}
Employee{salary=2000.0, name='打工人'}


【上上面Object的toString中getClass() 转到定义】
public final native Class<?> getClass();

这个是用 native修饰的,没有方法体,但它并不是抽象方法,它是通过C语言去实现的,这种方法只能记住方法的功能就可以了。没办法代码追踪了。
getClass() 
功能:返回对象的运行时类型(类加载的时候,会创建一个Class对象)


示例:getClass()  返回类型【反射时会用到】,对象== 判断的是地址
【路径:D:\javademo\day13_pm\Demo6.java】
package com.atguigu.demo;

import com.atguigu.bean.Employee;

public class Demo6 {
    public static void main(String[] args) {
        Employee employee=new Employee();
        System.out.println(employee.getClass());//返回employee对象在运行是的类型

        Employee employee1=new Employee();
        System.out.println(employee==employee1);//false
        System.out.println(employee1.getClass());
        System.out.println(employee.getClass()==employee1.getClass());//true
    }
}

D:\javademo\day13_pm>java com.atguigu.demo.Demo6
class com.atguigu.bean.Employee
false
class com.atguigu.bean.Employee
true


【上上面Object的toString中hashCode() 转到定义】也是 native修饰的
public native int hashCode();
将当前对象,通过哈希算法,得到一个int值

示例:两对相同对象(地址一样的):经过相同的hash算法,得到的int值肯定是一样的!
   【两个不同的对象:经过相同的hash算法,得到的int值,也有可能一样,大概率是不一样的】
public class Demo6 {
    public static void main(String[] args) {
        Employee employee=new Employee();
        System.out.println(employee.hashCode());
        Employee employee1=employee;
        System.out.println(employee1.hashCode());

        Employee employee2=new Employee();
        System.out.println(employee2.hashCode());
    }
}

D:\javademo\day13_pm>java com.atguigu.demo.Demo6
366712642
366712642
1829164700


示例:finalize() 垃圾回收
【路径:D:\javademo\day13_pm\Student.java】
package com.atguigu.bean;

public class Student {
    public String name;

    @Override
    protected void finalize() throws Throwable {
        System.out.println(this.name+"被回收了!");
    }
}


【路径:D:\javademo\day13_pm\Demo6.java】
package com.atguigu.demo;

import com.atguigu.bean.Employee;
import com.atguigu.bean.Student;

public class Demo6 {
    public static void main(String[] args) {
        Student student=new Student();
        student.name="张三";

        Student student1=new Student();
        student1.name="李四";

        student=new Student();//王五的地址将张三的地址给覆盖掉了!
        student.name="王五";

        student1=new Student();//没有姓名的学习将李四的地址给覆盖了!

        //张三就是垃圾对象,他就会被垃圾回收机制回收!
        //在回收的时候,会自动调用张三的finalize方法!但是Object类继承下来的方法内
        //什么都没有写,如果我想查看是不是张三被回收!
        //Student类重写finalize方法,重写之后,调用的就是重写之后的!

        System.gc();//通知垃圾回收机制来回收垃圾!(也不会立刻来回收垃圾对象!)

        //程序会睡眠2秒钟,这个时间段内,垃圾回收机制就会运行!然后就可以看到效果
        //【这边垃圾回收挺快的,几十毫秒吧】
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("睡醒了");
    }
}

D:\javademo\day13_pm>javac -d . Student.java
D:\javademo\day13_pm>javac -d . Demo6.java
D:\javademo\day13_pm>java com.atguigu.demo.Demo6
李四被回收了!
张三被回收了!
睡醒了


示例:String之==的内存图
public class Demo7 {
    public static void main(String[] args) {
        String str1="java";
        String str2="java";
        String str3=new String("java");
        System.out.println(str1==str2);//判断两个对象是否相等   true
        System.out.println(str1==str3);//判断两个对象是否相等   false

        /* 同理,用这种方式输入“java”判断也是 false
        java.util.Scanner input=new java.util.Scanner(System.in);
        String str3=input.next();//输入:java
        System.out.println(str1==str3);//false */
    }
}


D:\javademo\day13_pm>javac Demo7.java
D:\javademo\day13_pm>java Demo7
true
false


为什么呢?看看内存图!


常量池-【jdk不同版本,在不同的位置】
1.6方法区内
1.7堆内存
1.8元空间

因为方法区太小,放常量池里面会有很多常量,会放不下;
然后堆内存很大,东西也多呀,后来就研发出元空间。


示例:equals(Object obj)
public class Person { }

public class Demo7 {
    public static void main(String[] args) {
        Person per1=new Person();
        Person per2=per1;
        Person per3=new Person();
        System.out.println(per2==per1);//true
        System.out.println(per1==per3);//false   per1和per3的地址是不一样的

        System.out.println(per1.equals(per3));//判断per1和per3是否一致  false

        System.out.println("------------");
        String str1="java";
        String str2="java";
        String str3=new String("java");
        System.out.println(str1==str2);//判断两个对象是否相等   true
        System.out.println(str1==str3);//判断两个对象是否相等   false

        System.out.println("String-equals:"+str1.equals(str3));//true对比的是内容!
        //这是为什么?此equals非彼equals!该equals是String重写后的!
    }
}

D:\javademo\day13_pm>javac Demo7.java
D:\javademo\day13_pm>java Demo7
true
false
false
------------
true
false
String-equals:true


【分析】
per1.equals 点进去看看 equals 源码【跟==没有区别】:
    public boolean equals(Object obj) {
        return (this == obj);
    }

str1.equals 点进去看看 equals 源码:【该equals是String重写后的!】

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }


下面一行行解释:
    public boolean equals(Object anObject) {
        //地址是否一样,如果地址一样,内容肯定一样
        if (this == anObject) {
            return true;
        }
        //判断anObject是否是String类型,如果不是String类型,直接返回false
        if (anObject instanceof String) {
            //向下转型
            String anotherString = (String)anObject;
            //char[] value; n->this的字符串长度
            int n = value.length;
            //判断长度是否一样,如果长度不一样,返回false
            if (n == anotherString.value.length) {
                //v1是this的字符串内容
                char v1[] = value;
                //v2是参数字符串的内容
                char v2[] = anotherString.value;
                //挨个字符的对比,从头到尾,中途有不一样的,返回false
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                //从头到尾都没有不一样的,说明每个字符都一样(内容是一样的)
                return true;
            }
        }
        return false;
    }

【练习】
创建两个Person对象,我认为,只要name和id都一样,就是同一个人!
在Person类中,重写equals方法!(对比内容,id和name)

public class Person {
    public int id;
    public String name;

    @Override
    public boolean equals(Object obj) {
        if(this==obj)
            return true;
        if(obj instanceof Person){
            Person person=(Person)obj;
            if(this.id==person.id&&this.name.equals(person.name))
                //此处的equals并不是我们正在重写的equals
                return true;
        }
        return false;
    }
}

public class Demo7 {
    public static void main(String[] args) {
        Person per4=new Person();
        per4.id=1;
        per4.name="张三";

        Person per5=new Person();
        per5.id=1;
        per5.name="张三";

        System.out.println(per4==per5);
        System.out.println(per4.equals(per5));
    }
}

D:\javademo\day13_pm\Demo>javac Demo7.java
D:\javademo\day13_pm\Demo>java Demo7
false
true


示例:接口继承多接口【接口更加纯粹的抽象类】
public interface Fly extends Aa,Bb,Cc{
    int id=1;//公有静态常量

    //public Fly(){}  没有构造器
    //static{}

    void method();//公有的抽象方法

    int function(int a,String b,Person p);//公有的抽象方法
}

interface Aa{
    void aa();
}
interface Bb{
    void bb();
}
interface Cc{
    void cc();
}

public class Demo8 {
    public static void main(String[] args) {
        System.out.println(Fly.id);
        //接口名.调用到id,说明id是静态的
        //跨包调用,说明id是公有的
        //Fly.id=10;
        //不可以修改id的值,说明是常量

        //Fly fly=new Fly();//接口是不能实例化对象的
    }
}


D:\javademo\day13_pm>javac Demo8.java
D:\javademo\day13_pm>java Demo8
1

http://www.xdnf.cn/news/490087.html

相关文章:

  • 【已解决】Parsing error: No Babel config file detected for E:\
  • MCP概述及MCP Server的使用和实现(谷歌ADK使用MCP Server)
  • 如何在 Windows 上安装类似 Synaptic 的 Chocolatey GUI 包管理器
  • 哈希表的实现02
  • java18
  • 理解位图算法:使用 C++ 实现高效数据查重
  • 4.1 多层感知机 MLP 笔记
  • C语言学习记录--深入理解指针(5)(qsort的练习)
  • Linux基础开发工具大全
  • 连续隐马尔可夫离散隐马尔科夫模型的MATLAB实现
  • falsk-ORM的使用-数据库表的创建
  • 【Linux】动静态库链接原理
  • nnUNet V2代码——图像增强(三)
  • 【数据结构】线性表--栈
  • 金属加工液展|切削液展|2025上海金属加工液展览会
  • 使用unsloth对Qwen3在本地进行微调
  • 一个批量文件Dos2Unix程序(Microsoft Store,开源)1.1.0 编码检测和预览
  • 淘宝扭蛋机系统开发前景分析:解锁电商娱乐化新蓝海
  • HOW - React NextJS 的同构机制
  • Dify中使用插件LocalAI配置模型供应商报错
  • Spring Cloud深度实践:从服务发现到弹性智能API网关全景解析
  • Day29 -JS开发02 -两个实例:dom树(存在dom-xss) 加密及基础的js逆向(明文加密)
  • SAP-ABAP:SAP DMS(文档管理系统)的详细说明,涵盖其核心功能、架构、配置及实际应用
  • spring学习->sprintboot
  • Room数据库
  • Matrix-Game:键鼠实时控制、实时生成的游戏生成模型(论文代码详细解读)
  • Java并发编程-线程池(四)
  • Reth(冗余以太网接口) 和Bridge-Aggregation(链路聚合接口)区别
  • 一个进程中可以有多个 WebView2 控件,它们各自有独立的用户数据目录,COOKIE共享
  • 内存泄漏系列专题分析之十六:高通相机CamX内存泄漏内存占用分析--chi-cdk部分ION内存拆解方法