高校网站推广方案seo培训优化
news/
2025/9/29 7:14:19/
文章来源:
高校网站推广方案,seo培训优化,网站备案信息的核查方式,php 文档系统wordpress提前讲的重要知识点
一个类在没有父类的情况下默认有一个父类为Object类。
而当在有父类情况下#xff0c;如果你那父类没有父类#xff0c;则其父类的父类默认为object类#xff0c;所以即使一个类有父类#xff0c;其内部还是有object类。
object类都是隐藏起来的如果你那父类没有父类则其父类的父类默认为object类所以即使一个类有父类其内部还是有object类。
object类都是隐藏起来的你不会看到但是能用。
多态 前言
当父类引用所引用的子类对象不一样时。调用重写的方法所表现出来的行为是不一样的我们把这种思想叫做多态。
其中上面所说的可能大家会觉得有点抽象所以且听我慢慢道来大家就懂了。
多态的基础是动态绑定所以要了解多态前提我们还要了解动态绑定。 了解动态绑定的前提
要想实现动态绑定我们需要满足以上几个条件
1.要发生向上转型
2.有发生重写子类和父类有同名的方法
3.使用父类对象的引用去调用重写方法
完成了这三部分就会发生动态绑定。
从而用该父类对象的引用调用子类和父类都有的方法时调用的是子类方法而不是正常来说的父类方法。
而在这里出现了重写以及向上转型这些概念。所以我们得先了解它们才能再去了解动态绑定。 向上转型
向上转型实际就是创建一个子类对象将其当成父类对象来使用。
语法格式父类类型 对象名 new 子类类型()
Animal animal new Cat(元宝,2); //cat是子类animal是父类 虽然它们类型不相同但由于它们是继承关系子类类型可以隐式转换为父类类型小范围能隐式转换为大范围所以能实现该代码。
向上转型有以上三种方式
1. 直接赋值
2. 方法传参
3. 方法返回
public class TestAnimal {// 2. 方法传参形参为父类型引用可以接收任意子类的对象public static void eatFood(Animal a){a.eat();}// 3. 作返回值返回任意子类对象public static Animal buyAnimal(String var){if(狗.equals(var) ){return new Dog(狗狗,1);}else if(猫 .equals(var)){return new Cat(猫猫, 1);}else{return null;}}public static void main(String[] args) {Animal cat new Cat(元宝,2); // 1. 直接赋值子类对象赋值给父类对象Dog dog new Dog(小七, 1);eatFood(cat);eatFood(dog);Animal animal buyAnimal(狗);animal.eat();animal buyAnimal(猫);animal.eat();}
}
向上转型的优点让代码实现更简单灵活。
向上转型的缺陷不能调用到子类特有的方法。 虽然我们创造的对象是子类对象但引用是父类的引用所以用发生向上转型的父类引用其不能调用子类的特有方法如果是子类和父类都有名字相同的方法此时用该引用调用相同的方法调用的是子类的方法 重写
重写(override)也称为覆盖
重写是对重写的父类方法是有限制的等会来讲父类方法的实现过程进行重新编写放在子类中。 【方法重写的规则】
1.子类在重写父类的方法时一般必须与父类方法原型一致 返回值类型 方法名 (参数列表) 要完全一致 2.当然有个特殊点被重写的方法返回值类型可以不同但是必须是具有父子关系的这里的父子关系指的是在父类中返回值必须要为父类类型在子类中方法必须为子类类型相反则会报错。 3.访问权限不能比父类中被重写的方法的访问权限更低。例如如果父类方法被public修饰则子类中重写该方法就不能声明为 protected。 注意在重写的方法里不能出现private这个访问权限修饰符
访问权限大小比较privatedefaultprotectedpublic 4.父类中被static或private或final修饰的方法以及构造方法都不能被重写。 5.在子类中重写的方法, 可以使用 Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法 构成重写 注意重写只能应用于成员方法不能应用于成员变量。 动态绑定和静态绑定
静态绑定也称为前期绑定(早绑定)即在编译时根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
动态绑定也称为后期绑定(晚绑定)即在编译时不能确定方法的行为需要等到程序运行时才能够确定具体调用哪个类的方法。
所以说正是因为动态绑定我们才能实现多态。
在实现重写和向上转型这两个前提后用该父类对象的引用调用子类和父类都存在且同名的方法时就发生了动态绑定使运行时期确认调用的是子类同名方法而不是正常情况下应该调用的父类同名方法。
从而利用这个动态绑定作为基础去实现多态。
多态的实现 多态具体点就是去完成某个行为时当不同的对象去完成时会产生出不同的状态。代码如下 此时在上述代码中不同对象用同一个方法时产生了不同的结果这种就可以认为是多态。 多态的优缺点
class Shape {//属性....public void draw() {System.out.println(画图形);}
}
class Rect extends Shape{Overridepublic void draw() {System.out.println(♦);}
}
class Cycle extends Shape{Overridepublic void draw() {System.out.println(●);}
}class Flower extends Shape{Overridepublic void draw() {System.out.println(❀);}
}【使用多态的好处】
1. 能够降低代码的 圈复杂度, 避免使用大量的 if - else
什么叫 圈复杂度 ? 圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂. 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 圈复杂度. 如果一个方法的圈复杂度太高, 就需要考虑重构. 不同公司对于代码的圈复杂度的规范不一样. 一般不会超过 10 .
例如我们现在需要打印的不是一个形状了, 而是多个形状. 如果不基于多态, 实现代码如下:
public static void drawShapes() {Rect rect new Rect();Cycle cycle new Cycle();Flower flower new Flower();String[] shapes {cycle, rect, cycle, rect, flower};for (String shape : shapes) {if (shape.equals(cycle)) {cycle.draw();} else if (shape.equals(rect)) {rect.draw();} else if (shape.equals(flower)) {flower.draw();}}
}如果使用使用多态, 则不必写这么多的 if - else 分支语句, 代码更简单.
public static void drawShapes() {// 我们创建了一个 Shape 对象的数组. Shape[] shapes {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()};for (Shape shape : shapes) {shape.draw();}
} 2. 可扩展能力更强 如果要新增一种新的形状, 使用多态的方式代码改动成本也比较低.
class Triangle extends Shape {Overridepublic void draw() {System.out.println(△);}
}对于类的调用者来说(drawShapes方法), 只要创建一个新类的实例就可以了, 改动成本很低. 而对于不用多态的情况, 就要把 drawShapes 中的 if - else 进行一定的修改, 改动成本更高. 多态缺陷代码的运行效率降低。
1. 属性没有多态性 当父类和子类都有同名属性的时候通过父类引用只能引用父类自己的成员属性属性即成员变量
2. 构造方法没有多态性 向下转型
讲了向上转型之后就有必要延伸讲一下向下转型了。
注意向下转型跟向上转型的来源不一样向下转型来源并不是跟向上转型一样通过创建对象去形成的。
我们看看如果向下转型跟向上转型一样通过创建对象去形成会产生什么现象。 发生了类型转换异常。这可能是因为d引用在这时能访问的空间只有B类所创建的空间d引用本身还能访问除了B类空间的其他空间但我们只创建了B类空间其他本身能访问的空间并没有创建出来所以因为不能访问整个完整的D类空间从而发生错误。使用d时无论调用谁都会报错。 所以向下转型不是跟向上转型一样通过创建对象去发生的它的基础是要在发生了向上转型后才能发生向下转型如下
将一个子类对象经过向上转型之后当成父类方法使用再无法调用子类的方法但有时候可能需要调用子类特有的方法注意特有此时将父类引用再还原为子类对象即可即向下转换。
代码如下
public class TestAnimal {public static void main(String[] args) {Cat cat new Cat(元宝,2);Dog dog new Dog(小七, 1);// 向上转型Animal animal cat;animal.eat();animal dog;animal.eat();// animal.bark();// 编译失败编译时编译器将animal当成Animal对象处理// 而Animal类中没有bark方法因此编译失败
//所以我们需要将其向下转换向下转换需要强制转换// animal本来指向的就是狗因此将animal强制还原为狗也是安全的
//只要dog能访问到自己本身就能访问的全部空间代码就算成功。 dog (Dog)animal;dog.bark();}
}
而会出现以下代码就是不安全的了
public class TestAnimal {public static void main(String[] args) {Cat cat new Cat(元宝,2);Dog dog new Dog(小七, 1);// 向上转型Animal animal cat;animal.eat();animal dog;animal.eat();cat (Cat)animal;cat.mew();
// 现在要强制还原为猫无法正常还原运行时抛出ClassCastException
//发生了类型转换异常这是因为cat引用不能访问自己本身就能访问的全部空间
//这个跟我最前面讲为什么向下转换不能通过创造对象去形成一样的道理
}} 向下转型用的比较少而且不安全万一转换失败运行时就会抛异常。Java中为了提高向下转型的安全性引入了instanceof 如果该表达式为true则可以安全转换。
public class TestAnimal {public static void main(String[] args) {Cat cat new Cat(元宝,2);Dog dog new Dog(小七, 1);// 向上转型Animal animal cat;animal.eat();animal dog;animal.eat();if(animal instanceof Cat){cat (Cat)animal;cat.mew();}if(animal instanceof Dog){dog (Dog)animal;dog.bark();}}
}instanceof操作符
如果右边类的类型为左边引用所指向的对象的类型或者右边类的类型为其父类则返回true。不是则返回false。
如上代码animal指向dog对象dog对象的类型为Dog所以右边类型只有Dog或animal才能返回true其他都是false。 避免在构造方法中调用重写的方法
一段有坑很好玩的代码 我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func。
看看打印结果
class B {public B() {// do nothingfunc();}public void func() {System.out.println(B.func());}
}class D extends B {private int num 1;Overridepublic void func() {System.out.println(D.func() num);}
}public class Test {public static void main(String[] args) {D d new D();}
}// 执行结果
D.func() 0
对于这结果你们肯定很有疑惑且听我们慢慢道来
构造 D 对象的同时, 会调用 B 的构造方法. B 的构造方法中调用了 func 方法, 此时因为构造方法中隐含着this而在父类构造方法中隐藏着 B this 这个引用变量接收的引用却是D类型引用所以发生了向上提升而在父类与子类中又发生了重写且还用到了func这个重写方法在func前面隐藏着this. 此时this代表着B这个父类引用所以导致触发动态绑定, 会调用到 D 中的 func 。
而我们还要知道一个很重要的一点在构造方法完成后才会对成员变量进行初始化在构造方法完成前成员变量的值都是基础值。
所以因为我们在构造方法还没完成时就用了成员变量该成员变量还未进行初始化变为1依然是保持基础值所以打印时其num值为基础值0 这就是打印出该结果的原因。
结论: 用尽量简单的方式使对象进入可工作状态, 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题 。所以在构造函数内尽量避免使用实例方法我们用它去进行初始化成员变量就可以了。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/921508.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!