java里多态和继承的本质区别(你真的熟悉Java中的继承与多态?给你几分钟能回答上来?)java基础 / Java继承与多态...

wufei123 发布于 2024-06-17 阅读(4)

一. 继承1.介绍多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可此处的多个类称为子类(此类,派生类,拓展类),单独的这个类称为父类(基类 ,超类)。

可以理解为:“子类 is a 父类”类继承语法规则: class Subclass extends SuperClass{ };以下几点需要注意:子类继承了父类所有可以访问的数据域和方法父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。

只因为封装性的影响,使得子类不能直接调用父类的结构而已如果父类中定义了公共的访问器 / 修改器,那么可以通过这些公共的访问器 / 修改器来访问和修改它们子类并不是父类的一个子集实际上,一个子类通常比它的父类包含更多的信息和方法。

在 Java 中是不允许多重继承的一个 Java 类只可能直接继承自一个父类这种限制称为单一继承( single inheritance)如果使用 extends 关键字来定义一个子类,它只允许有一个父类。

然而,多重继承是可以通过接口来实现2.super关键字在Java类中使用super来调用父类中的指定操作:用于访问父类中定义的属性(属性没有私有化)用于调用父类中定义的成员方法用于在子类构造器中调用父类的构造器

2.1 调用父类的构造方法构造方法用于构建一个类的实例不同于属性和普通方法,父类的构造方法不会被子类继承它们只能使用关键字 super 从子类的构造方法中调用调用父类构造方法的语法是: super()或者super(parameters);。

语句 super() 调用父类的无参构造方法,而语句 super(arguments) 调用与参数匹配的父类的构造方法语句 super() 和 super (arguments) 必须出现在子类构造方法的第一行。

,这是显式调用父类构造方法的唯一方式子类中所有的构造器默认都会访问父类中空参数的构造器如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错.所以呢,一般情况下,最好能为每个类提供一个无参构造方法,以便于对该类进行扩展,同时避免错误。

特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则。

必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法publicclassPerson{ private String name; privateint age; 。

private Date birthDate; publicPerson(String name, int age, Date d){ this.name = name;

this.age = age; this.birthDate = d; } publicPerson(String name, int age){ this(name, age,

null); } publicPerson(String name, Date d){ this(name, 30, d); } publicPerson

(String name){ this(name, 30); } } publicclassStudentextendsPerson{ private String school;

publicStudent(String name, int age, String s){ super(name, age); school = s; } public

Student(String name, String s){ super(name); school = s; } // 编译出错: no super(),系统无法调用父类无参数的构造器。

//There is no default constructor available in chapter01.PersonpublicStudent(String s){ school = s; } }

2.2 构造方法链在任何情况下,构造一个类的实例时,将会调用沿着继承链的所有父类的构造方法当构造一个子类的对象时,子类构造方法会在完成自己的任务之前,首先调用它的父类的构造方法如果父类继承自其他类,那么父类构造方法又会在完成自己的任务之前,调用它自己的父类的构造方法。

这个过程持续到沿着这个继承体系结构的最后一个构造方法被调用为止这就是构造方法链(constructor chaining)publicclassFacultyextendsEmployee { 。

publicstaticvoidmain(String[] args) { Faculty faculty = new Faculty(); } publicFaculty

() { System.out.println("(4) Performs Facultys tasks"); } } classEmployeeextendsPerson {

publicEmployee() { this("(2)Invoke Employees overloaded constructor"); System.out.println(

"(3)Performs Employees tasks "); } publicEmployee(String s) { System.out.println(s); } }

classPerson { publicPerson() { System.out.println("(1) Performs Persons tasks"); } }

结果:

在第3 行,new Faculty() 调用Faculty 的无参构造方法由于 Faculty 是 Employee 的子类,所以,在Faculty 构造方法中的所有语句执行之前,先调用 Employee 的无参构造方法。

Employee 的无参构造方法调用Employee 的第二个构造方法(第13 行)由于 Employee 是 Person 的子类,所以,在 Employee 的第二个构造方法中所有语句执行之前,先调用 Person 的无参构造方法。

2.3 调用父类的方法super.方法名(参数);3.方法重写子类从父类中继承方法有时,子类需要修改父类中定义的方法的实现,这称作方法重 写(method overriding),:要重写一个方法,需要在子类中使用和父类一样的签名以及一样的返回值类型

来对该方法进行定义注意以下几点:仅当实例方法是可访问时,它才能被覆盖因为私有方法在它的类本身以外是不能访问的,所以它不能被覆盖如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系子类不能用语法 super.super.toStringO 访问父类的父类中的toString ,这是一个语法错误。

与实例方法一样,静态方法也能被继承但是,静态方法不能被覆盖如果父类中定义的静态方法在子类中被重新定义,那么在父类中定义的静态方法将被隐藏可以使用语法:父类名 .静态方法名(SuperClassName.staticMethodName) 调用隐藏的静态方法。

这个再次详细说明一下publicclassStaticExtends { publicstaticvoidmain(String[] args) { //声明为Father类,son1静态方法和Father类绑定

Father son = new Son(); son.method(); son.staticMethod(); Son son2 =

new Son(); son2.method(); son2.staticMethod(); } } classFather { voidmethod

() { System.out.println("父类方法"); } staticvoidstaticMethod() { System.out.println(

"父类静态方法"); } } classSonextendsFather { @Override voidmethod() { System.out.println(

"子类方法"); } staticvoidstaticMethod() { System.out.println("子类静态方法"); } } 输出结果:

在子类中重写父类的static方法,是不会报错的,编译也可以通过,但是在通过一个声明为父类,实际类型为子类的引用变量调用该方法时,发现被调用的仍是父类中原本以为会被覆盖的方法,不具有“多态”特性所以呢,父类的static方法是不会被重写的。

4.Object类及其常用方法Java 中的所有类都继承自 java.lang.Object 类,如果在定义一个类时没有指定继承性,那么这个类的父类就被默认为是 Object。

4.1 toString()方法Object 类中toString()方法的默认实现是:public String toString(){ return getClass().getName() +

"@" + Integer.toHexString(hashCode()); } 调用一个对象的 toString() 会返回一个描述该对象的字符串默认情况下,它返回一个由该对象所属的类名、at 符号(@)以及该对象十六进制形式的内存地址组成的字符串。

这个信息不是很有用,所以重写像String、Date、File、包装类等都重写了Object类中的toString()方法使得在调用对象的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); 4.2 equals()方法Object 类中 equals 方法的默认实现是:

publicbooleanequals(Object obj){ return (this obj); } 说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体。

像String、Date、File、包装类等都重写了Object类中的equals()方法重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同通常情况下,我们自定义的类如果使用equals()的话,也。

通常是比较两个对象的"实体内容"是否相同那么,我们 就需要对Object类中的equals()进行重写重写的原则:比较两个对象的实体内容是否相同tips:==和equals的区别:1 = 既可以比较基本类型也可以比较引用类型。

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

3 具体要看自定义类里有没有重写Object的equals方法来判断4 通常情况下,重写equals方法,会比较类中的相应属性是否都相等二. 多态5.1 介绍首先呢,我们知道继承关系使一个子类继承父类的特征,并且附加一些新特征。

子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来就不成立例如:每个圆都是一个几何对象,但并非每个几何对象都是圆因此,总可以将子类的实例传给需要父类型的参数使用父类对象的地方都可以使用子类的对象。

这就是通常所说的多态简单来说,多态意味着父类型的变量可以引用子类型的对象5.2 动态绑定我们都知道方法可以在父类中定义而在子类中重写(方法可以在沿着继承链的多个类中实现JVM 决定运行时调用哪个方法)那么。

Object o = new SonObject(); System.out.println(o.toSting); 这里的 o 调用哪个 tostring() 呢? 我们首先介绍两个术语:声明类型和实际类型

一个变量必须被声明为某种类型变量的这个类型称为它的声明类型(declared type)这里,o 的声明类型是 Object一个引用类型变量可以是一个 null 值或者是一个对声明类型实例的引用实例可以使用声明类型或它的子类型的构造方法创建。

变量的实际类型(actual type) 是被变量引用的对象的实际类这里,o 的实际类型是SonObject, 因为 o 指向使用 new SonObject() 创建的对象o 调用哪个toString() 方法由 o 的实际类型决定。

这称为动态绑定(dynamic binding) 也就是多态情况下,==编译时,看左边;运行时,看右边== “看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)。

态绑定工作机制如下:假设对象 o 是类 Cl, C2, … ,Cn-1, Cn 的实例,其中 C1是 C2的子类,C2 是 C3 的子类,… ,Cn-1是 Cn 的子类也就是说,Cn 是最通用的类,C1是最特殊的类。

在 Java 中,Cn 是 Object 类如果对象 o 调用一个方法 p, 那么JVM 会依次在类 Cl,C2, … ,Cn-1,Cri 中查找方法 p 的实现,直到找到为止一旦找到一个实现,就停止査找,然后调用这个首先找到的实现。

看以下代码:publicclassDynamicBindDemo{ publicstaticvoidmain(String[] args){ m(new GraduateStudent()); m(

new Student()); m(new Person()); m(new Object()); } publicstaticvoidm(Object x)

{ System.out.println(x.toString()); } } classGraduateStudentextendsStudent{ } classStudent

extendsPerson{ @Overridepublic String toString(){ return"Student"; } } classPerson{

@Overridepublic String toString(){ return"Person"; } } 输出结果:

5.3 对象转换和instanceof()运算符5.3.1 对象转换对象的引用可以类型转换为对另外一种对象的引用,这称为对象转换Object o = new Student(); m(o); Student 的实例也是 Object 的实例。

,所以,语句 Object o = new StudentO 是合法的,它称为隐式转换(implicit casting) 但是Student b = o;将会发生编译错误,原因是 Student 对象总是 Object 的实例,但是,Object 对象不一定是 Student 的实例。

即使可以看到 0实际上是一个 Student 的对象,但是编译器还没有聪明到知道这一点为了告诉编译器o就是一个 Student 对象,就要使用显式转换( explicit casting)Student b = (Student)o;

// Explicit casting总是可以将一个子类的实例转换为一个父类的变量,称为向上转换(upcasting),因为子 类的实例永远是它的父类的实例当把一个父类的实例转换为它的子类变量(称为向下转换 (downcasting)) 时,必须使用转换记号 “(子类名)” 进行显式转换,向编译器表明你的意图。

为使转换成功,必须确保要转换的对象是子类的一个实例如果父类对象不是子类的一个 实例,就会出现一个运行异常 ClassCastException5.3.2instanceof运算符在尝试转换之前确保该对象是另一个对象的实例,可以利用运算符instanceof 来实现的。

Objecto = new Circle();if(o instanceof Circle){Circlec = ((Circle)o;System.out.println("Thecircle diameter is + o.getDiameter());

① a instanceof A:判断对象a是否是类A的实例如果是,返回true;如果不是,返回false② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。

③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误变量 myObject 被声明为 Object声明类型 决定了在编译时匹配哪个方法使用 myObject.getDiameter()会引起一个编译错误,因为Object 类没有 getDiameter 方法。

编译器无法找到和 myObject.getDiameter()匹配的方法所以,有必要将 myObject 转换成 Circle 类型,来告诉编译器 myObject 也是 Circle 的一个实例同时要注意,引用变量 o和 c指向同一个对象,而在进行基本数据类型转换时,会创建一个新的对象。

为什么没有在一开始就把 myObject定义为 Circle 类型呢?为了能够进行通用程序设计,一个好的经验是把变童定义为父类型,这样,它就可以接收任何子类型的值。

注意::对象成员访问运算符( .)优先于类型转换运算符使用圆括号保证在点运算符( .)之前进行转换,例如:((Circle)object).getArea();最后大家看完有什么不懂的可以在下方留言讨论.。

谢谢你的观看。觉得文章对你有帮助的话记得关注我点个赞支持一下!链接:https://juejin.im/post/6864894838878240782

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻12049