多态

,运行根据对象类型进行绑定=“后期绑定”。

上面这段话先放一边,看起来有点专业化看不太懂,下面先放上一段代码:

class Instrument {
  public void play(Note n) {
    print("Instrument.play()");
  }
}
class Stringed extends Instrument {
  public void play(Note n) {
    print("Stringed.play() " + n);
  }
}

class Brass extends Instrument {
  public void play(Note n) {
    print("Brass.play() " + n);
  }
}
public class Wind extends Instrument {
  // Redefine interface method:
  public void play(Note n) {
    System.out.println("Wind.play() " + n);
  }
} ///:~

public class Music2 {
  public static void tune(Wind i) {
    i.play(Note.MIDDLE_C);
  }
  public static void tune(Stringed i) {
    i.play(Note.MIDDLE_C);
  }
  public static void tune(Brass i) {
    i.play(Note.MIDDLE_C);
  }
  public static void main(String[] args) {
    Wind flute = new Wind();
    Stringed violin = new Stringed();
    Brass frenchHorn = new Brass();
    tune(flute); // No upcasting
    tune(violin);
    tune(frenchHorn);
  }
} 

输出结果:

Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C

Wind,Stringed,Brass都继承与Instrument ,这种写法更清晰能看到Wind传入Wind并使用自己的方法更容易符合我们人的思想对不对,但是这样写现在是3个导出类(继承Instrument 这三个类),那万一上百个,是不是要写上百个tune()方法是不是不太合理。

那现在我们把改成这样

 public static void tune(Instrument i) {
    i.play(Note.MIDDLE_C);
  }

Wind,Stringed,Brass三个类型的参数是不是可以传到上面的这个方法,就不需要写三个方法了。但是这样的话,编译器又不是我们人脑,它怎么知道你传这个参数我到底给你调用那个方法。这就涉及到方法调用绑定。

程序运行前绑定=“前期绑定”这是默认方式,而想解决上面我们那种疑问就需要到“后期绑定”又叫运行时绑定,动态绑定。

运行根据对象类型进行绑定=“后期绑定”

public class Shape {
  public void draw() {}
  public void erase() {}
} ///:~

public class Circle extends Shape {
  public void draw() { print("Circle.draw()"); }
  public void erase() { print("Circle.erase()"); }
} ///:~
public class Square extends Shape {
  public void draw() { print("Square.draw()"); }
  public void erase() { print("Square.erase()"); }
} ///:~
public class Triangle extends Shape {
  public void draw() { print("Triangle.draw()"); }
  public void erase() { print("Triangle.erase()"); }
} ///:~

public class RandomShapeGenerator {
  private Random rand = new Random(47);
  public Shape next() {
    switch(rand.nextInt(3)) {
      default:
      case 0: return new Circle();
      case 1: return new Square();
      case 2: return new Triangle();
    }
  }
} ///:~
public class Shapes {
  private static RandomShapeGenerator gen =
    new RandomShapeGenerator();
  public static void main(String[] args) {
    Shape[] s = new Shape[9];
    // Fill up the array with shapes:
    for(int i = 0; i < s.length; i++)
      s[i] = gen.next();
    // Make polymorphic method calls:
    for(Shape shp : s)
      shp.draw();
  }
} /* Output:
Triangle.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Circle.draw()
*///:~

像上面这样写RandomShapeGenerator 就是一个工厂分配给你是Circle、Triangle还是Square,这就是工厂模式。

而return处就发生了向上转型,return返回之后就是Sharp s = new Circle/Triangle/Square,而产生向上转型,那么根据后期绑定,就可以执行到不同的draw()方法,也就没了我刚开始那样的Circle/Triangle/Square的draw()方法我要三个,这样就省去一部分的代码对不对。(其实这里我还是没理解后期绑定底层是如何运作的,书中也没写,后续看到我会补充)

而到这里可能大家还是一知半解,就省去一部分代码,多态到底好在哪。

将上面Music2代码改动下

public class Music2 {
  public static void tune(Instrument i) {
    i.play(Note.MIDDLE_C);
  }
 public static void tuneAll(Instrument[] e) {
    for(Instrument i : e)
      tune(i);
  }	
 
  public static void main(String[] args) {
    Wind flute = new Wind();
    Stringed violin = new Stringed();
    Brass frenchHorn = new Brass();
    tune(flute); // No upcasting
    tune(violin);
    tune(frenchHorn);
  }

乍一看好像没什么,不就是缩减成一个方法了吗。但是看到里面tuneALL()里面用到    for(Instrument i : e) tune(i) 这里Instrument是不是父类类型,所以可以适用于Wind/Stringed/Percussion三种类型,而如果没有后期绑定也就是多态,上面这个方法按照我们最开始那样是不是要写3个tune()方法外加3个for(Wind/Stringed/Percussion i : e) tune(i)加起来6个方法,而且假设我又增加一个类型继承Instrument,我还要用到tuneALL(),我是不是又要改动代码,费时费力又不讨好。那有了多态,你属于Instrument类型,根据后期绑定帮你找到你对应的方法执行,是不是又省去代码,又不用修改代码,不会影响到你其他的代码上!而这才是多态的关键之处。

但是多态还是有缺陷,缺陷一、非private修饰的方法才能被重载。

public class PrivateOverride {
  private void f() { print("private f()"); }
  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
  }
}

class Derived extends PrivateOverride {
  public void f() { print("public f()"); }
} /* Output:
private f()
*///:~

按照这个逻辑在我们的个人想法中,应该输出public f(),但是private修饰的发f()没办法在Derived 中重载,后期绑定无法找到对应的Derived 的方法执行,所以只有输出基类(PrivateOverride )中的发f()方法。

 缺陷一、域和静态方法。

运行根据对象类型进行绑定=“后期绑定”,那这两个都是在运行前做好的,肯定没办法多态咯。

构造器与多态(刚开始我的疑问,怎么突然冒出来个构造器,就是因为多态是根据运行时对象来的,构造器可以当做是static来看的,所以在多态之前,我们有必要了解下构造器):

一、顺序(看起来有点懵,其实也是跟之前构造器说的那块,先最底层然后变量定义,到构造器,没啥区别)

二、继承和清理(这里就举个例子,在构造一个对象时其实就是从最底层到最外层对吧,那举个例子,我们穿衣服就是先穿内衣到外套,那我们脱下来也是先外套再内衣对吧,所以在IO的时候我们关闭流,其实也就是这个道理)

垃圾回收让我们省去了释放对象,而当我们要有一些需求时,导出类(继承别的类)重写了基类(被继承类)的清理方法,记得需要super中的清理方法,不然的话,基类就不会清理,这点需要特殊注意。而在此基础上,我们可以根据多态提高的好处,在清理时设置static修饰变量,因为static上面说了是跟类级别,而多态是根据对象运行时,那就可以用来计数防止溢出(这个看起来有点难懂配合书中的代码看吧,)。

三、构造器内部的多态方法的行为(这里根据下面代码解释下,刚开始我也有点懵。我们知道多态是在运行根据对象类型进行绑定,那当你启动下面这个程序时,按照我们之前说的,那就是先执行基类的变量定义,到构造器,这时候还没有执行到main里面的new RoundGlyph(5),没有触发多态,所以这时候编译器会给变量先赋值二进制的0,对象就是null。所以我们正常的理解应该只有输出RoundGlyph.RoundGlyph(), radius = 5,但是多了一句RoundGlyph.draw(), radius = 0,理解这个可以对我们有时候有些莫名其妙的问题有一点危机意识)

class Glyph {
  void draw() { print("Glyph.draw()"); }
  Glyph() {
    print("Glyph() before draw()");
    draw();
    print("Glyph() after draw()");
  }
}	

class RoundGlyph extends Glyph {
  private int radius = 1;
  RoundGlyph(int r) {
    radius = r;
    print("RoundGlyph.RoundGlyph(), radius = " + radius);
  }
  void draw() {
    print("RoundGlyph.draw(), radius = " + radius);
  }
}	

public class PolyConstructors {
  public static void main(String[] args) {
    new RoundGlyph(5);
  }
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~

协议返回类型WheatMill重写process方法返回值Wheat类型继承Grain所以可以这样写,也是重写,java5之前不行。

基类与导出类拥有完全相同的接口方法称为纯继承,而继承往往是导出类“is-like-a”像一个基类,是基类的扩展,导出类有基类的所有非private方法,我们在上面可以知道导出类是可以向上转型,而向下转型则需要强转并且转型成功才可以调用导出类的方法,否则就报错ClassCastExceotion。

 

最后总结(再次偷懒):

.

 

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值