
Hello 大家好,讲完了类与对象的两大基本特征,本文就让我们一起进入面向对象的第三大特征——多态,感受多态所带来的魅力🌹
重要又灵活的多态
所谓向上,用一句话来讲就是【父类引用指向子类对象】,对于上转型对象,它可以说是多态的雏形,要学好多态必须先了解上转型对象。好,废话不所说,直接上代码
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
void eat(){
System.out.println(this.name + "正在吃东西");
}
}
public class Cat extends Animal{
public Cat(String name) {
super(name);
}
@Override
void eat() {
System.out.println(this.name + "正在吃鱼");
}
}
public class test {
public static void main(String[] args) {
Animal animal = new Cat("咪咪");
animal.eat();
}
}

接下去我们来了解一下产生上转型对象的三种方式,可以可以帮我们在实例开发中快速的判断自己是否在使用上转型对象
这种的话就是我上面作为引入的案例
Animal animal = new Cat("咪咪");
animal.eat();
从下面的代码可以看出,是将一个本类方法的形参设置为父类的引用,然后将子类的对象作为实参传入,这也可以形成上转型对象
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("咪咪");
func(cat);
}
这种方式比较抽象而且难理解,因为是采用父类这个类型作为返回值,因为直接new一个子类的对象去返回,就自然地形成了上转型对象,这个确实不太好理解,大家要自己观察一下,就不给代码,给的指示图片
说完了三种上转型对象的方式,接下去我们说说使用在这个上转型对象时我们需要注意哪些
Animal animal;
Cat c = animal;
好,最后我们再来说一下上转型对象和接口回调的区别,接口的话会在下一篇文章讲【implements关键字】的时候讲到,这里先提一嘴
讲到了多态,也不能忘了继承,接下去就让我们来讲讲继承和多态之间的联系,以及它们都有哪些优点
然后来看一个小案例
class Animal{
void cry(){
}
}
class Dog extens Animal{
void cry(){
System.out.println("汪汪~~");
}
}
class Cat extens Animal{
void cry(){
System.out.println("喵喵~~");
}
}
class test{
public static void main(String[] args) {
Animal animal;
animal = new Dog();
animal.cry();
animal = new Cat();
animal.cry();
}
}
了解了多态的基本概念之后,我们要接下来说说要如何去实现多态
清楚了如何产生多态的条件,那我们就要去想,这个多态究竟有什么好处呢,为什么她是面向对象中最重要的一趴,接下来我们来说说这个
class Geometry{
void draw(){
System.out.println("画一个几何图形");
}
}
class Circle extends Geometry{
@Override
void draw() {
System.out.println("画一个⚪");
}
}
class Slice extends Geometry{
@Override
void draw() {
System.out.println("画一个♦");
}
}
class Triangle extends Geometry{
@Override
void draw() {
System.out.println("画一个▲");
}
}
public class test2 {
public static void main(String[] args) {
Circle circle = new Circle();
Slice slice = new Slice();
Triangle triangle = new Triangle();
String[] shapes = {"cycle","triangle","slice","cycle"};
for(String shape : shapes)
{
if(shape.equals("cycle")){
circle.draw();
}else if(shape.equals("triangle")){
triangle.draw();
}else if(shape.equals("slice")){
slice.draw();
}
}
}
}
OK,是时候展现真正的技术了😎
Geometry[] shapes = {new Circle(),new Triangle(),new Slice(),new Circle()};
for(Geometry shape : shapes){
shape.draw();
}
③对于最后这一点,还是比较好理解的,就是可扩展能力强【无限地增加继承的子类】
看完了上面这些,那您就算初步地了解了多态这个概念,但是并没有形成那个思维,只是一个引入,接下来我们便通过abstract这个关键字真正地进入多态的编程模式,感受面向抽象的编程思维🚶
class Geometry{
void draw(){
System.out.println("画一个几何图形");
}
}
abstract关键字即可,然后抹去这个方法的方法体,因为抽象方法是不可以有方法体的abstract void draw();
如果一个类中有抽象方法,那么这个类就必须是抽象类那这时候就有同学说,哇,这个关键字很厉害、很高级的感觉。是的,不然我不会前面铺垫那么多,才讲到这个关键字,厉害归厉害,但是在使用这个关键字的时候要注意的地方还是挺多的,让我们一起来看一下
1. 抽象类不能被实例化
Geometry shape = new Geometry();
2. 类内的数据成员,和普通类没有区别
abstract class Geometry{
private int num;
public void func(){
System.out.println("这是抽象类的一个实例方法");
}
abstract void draw();
}
3. 抽象类主要就是用来被继承的,所以不可以被final关键字修饰,抽象方法也是一样,需要被重写
4. 如果一个类继承了这个抽象类,那么这个类必须重写抽象类中的抽象方法
abstract class ff extends Geometry{
}
class gg extends ff{
@Override
void draw() {
}
}
5. 不可以用staic和private关键字修饰abstract方法
6. 抽象类中也可以由构造方法,方便子类调用
eat(),可以看到对于抽象类来说和普通类其实也差不多,这一点上面也说到过,我在里面也提供了构造方法,这是为了子类可以直接方便调用public abstract class Animal {
private String name;
private int age;
public Animal(String name, int age){
//提供构造方法,方便子类初始化
this.name = name;
this.age = age;
}
abstract void eat(); //抽象方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Rabbit类继承了这个抽象类,并且重写了抽象类里面的抽象方法,因为Rabbit类继承了动物类,所以可以看到直接很方便地调用了父类中的有参构造方法public class Rabbit extends Animal{
public Rabbit(String name, int age)
{
super(name, age);
}
@Override
void eat() {
System.out.println(this.getName() + " 不吃窝边草");
}
}
public class Test {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit("Black Rabbit", 100);
System.out.println("这只兔子的姓名为:" + rabbit.getName());
System.out.println("这只兔子的年龄为:" + rabbit.getAge());
rabbit.eat();
}
}

讲了这么多有关abstract关键字的注意事项,现在就让我们到实战中来看看它是具体怎么应用的吧👈
女朋友类(doge)
public abstract class GirlFriend {
abstract void speak();
abstract void cooking();
}
中国女朋友类(doge)
public class ChinaGirlFriend extends GirlFriend{
@Override
void speak() {
System.out.println("你好");
}
@Override
void cooking() {
System.out.println("会做水煮鱼");
}
}
美国女朋友类(doge)
public class AmericanGiralFriend extends GirlFriend{
@Override
void speak() {
System.out.println("Hello");
}
@Override
void cooking() {
System.out.println("Can make roast beef");
}
}
男孩类
public class Boy {
GirlFriend girlFriend;
void setGirlFriend(GirlFriend f){
girlFriend = f;
}
void showGirlFriend(){
girlFriend.speak();
girlFriend.cooking();
}
}
测试类
public class test {
public static void main(String[] args) {
GirlFriend girlFriend = new ChinaGirlFriend();
Boy boy = new Boy();
System.out.println("中国女朋友");
boy.setGirlFriend(girlFriend);
boy.showGirlFriend();
girlFriend = new AmericanGiralFriend();
System.out.println("--------------");
System.out.println("美国女朋友");
boy.setGirlFriend(girlFriend);
boy.showGirlFriend();
}
}
运行结果

在看完abstract关键字的应用之后,您对多态有没有形成一个概念了呢,其实要实现多态还是要有一个面向抽象的编程思维,这一点是很重要的🔑
———— 以上选段摘自耿祥义《Java2实用教程》
了解了面向抽象的编程思维,以及看了这么多的有关多态的小案例,接下来就让我们到实战中感受一下多态所带来的魅力吧🍁
//构造一个抽象几何形状类 —— 实现不同子类几何形状面积的求解
public abstract class Gemotrey {
public abstract double getArea();
}
//柱类 —— 面向抽象类Gemotrey,为具体底面几何图形提供总抽象类接口
public class Pillar {
Gemotrey bottom; //底面几何图形对象
double height; //柱体的高
//传入具体的底面几何图形和柱体的高
public Pillar(Gemotrey bottom, double height) {
this.bottom = bottom;
this.height = height;
}
//对外获取柱体体积
public double getVolume(){
if(bottom == null){
System.out.println("没有底,无法计算面积");
return -1;
}
return bottom.getArea() * height;
//通过具体的几何图形去重写抽象父类的获取面积方法
}
}
//圆类,继承自抽象类Gemotrey
public class Circle extends Gemotrey{
double r;
public Circle(double r) {
this.r = r;
}
@Override
public double getArea() {
return 3.14 * r * r;
}
}
//矩形类,继承自抽象类Gemotrey
public class Rectangle extends Gemotrey{
int a,b;
public Rectangle(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public double getArea() {
return a * b;
}
}
//测试类
public class test {
public static void main(String[] args) {
Gemotrey bottom; //几何形状底面对象
Pillar pillar; //柱类对象
int height = 50;
//1.无底的对象
bottom = null;
pillar = new Pillar(bottom,height);
System.out.println("-----------");
System.out.println("无底的对象面积为:" + pillar.getVolume());
//2.圆形底对象
bottom = new Circle(10);
pillar = new Pillar(bottom,height);
System.out.println("-----------");
System.out.println("圆形底对象面积为:" + pillar.getVolume());
//3.矩形底对象
bottom = new Rectangle(20,15);
pillar = new Pillar(bottom,height);
System.out.println("-----------");
System.out.println("矩形底对象面积为:" + pillar.getVolume());
}
}

public abstract class SIM {
public abstract void setNumber(String number); //设置电话号码
public abstract String getNumber(); //获取电话号码
public abstract String getCoreName(); //获取电话卡公司
}
public class MobileOfTelecom extends SIM{
String number;
@Override
public void setNumber(String number) {
this.number = number;
}
@Override
public String getNumber() {
return number;
}
@Override
public String getCoreName() {
return "中国电信";
}
}
public class SIMOfChinaMobile extends SIM{
String number;
@Override
public void setNumber(String number) {
this.number = number;
}
@Override
public String getNumber() {
return number;
}
@Override
public String getCoreName() {
return "中国移动";
}
}
public class SIMOfChinaUNnicom extends SIM{
String number;
@Override
public void setNumber(String number) {
this.number = number;
}
@Override
public String getNumber() {
return number;
}
@Override
public String getCoreName() {
return "中国联通";
}
}
public class MobilePhone {
SIM sim;
public void useISM(SIM sim){
this.sim = sim;
}
public void showMess(){
System.out.println("您使用的手机号码是:" + sim.getNumber());
System.out.println("您使用的手机卡公司是:" + sim.getCoreName());
}
}
public static void main(String[] args) {
SIM sim; //SIM电话卡类
MobilePhone mobilePhone = new MobilePhone(); //移动电话类
//1.中国电信
sim = new MobileOfTelecom();
sim.setNumber("18958473306"); //设置电话号码
mobilePhone.useISM(sim); //传入移动电话卡
mobilePhone.showMess(); //显示信息
System.out.println("------------");
//2.中国移动
sim = new SIMOfChinaMobile();
sim.setNumber("13955348743");
mobilePhone.useISM(sim);
mobilePhone.showMess();
System.out.println("------------");
//3.中国联通
sim = new SIMOfChinaUNnicom();
sim.setNumber("13284835562");
mobilePhone.useISM(sim);
mobilePhone.showMess();
}

所谓的开-闭原则(Open-Closed Principle),就是让设计的系统对扩展开发,对修改关闭
我们通过一张框架图来具体理解一下
本文,我们重点讲解了面向对象的第三大特征——多态,讲到了多态存在所必不可少的一块,也就是实现上转型对象,然后说道了继承和多态之间的关系,初步地了解了多态的概念,接着来到了主题部分,也就是关键abstract的讲解,继而引申出了抽象类和抽象方法,了解了这些之后呢,我们又形成了一个面向抽象的编程思维,在实战案例中感受到了如如何运用这种思维去实现一个多态,最后我们讲到了企业开发中所需要遵循的【开-闭】原则,提到了何处需要开,何处又需要关对多态也有了一个完整的思维体系🌳
好了,这就是本文要讲述的所有内容,感谢您对本文的观看,如果错误请于评论区或私信指出🌸

总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir