• 在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。
  • 多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。
  • “封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节“私有化”把接口和实现分离开来,而多态的作用则是消除类型之间的耦合关系。

再论向上转型 & 转机

  • 对象既可以作为它自己本身的类型使用,也可以作为它的基类使用,而这种把某个对象的引用视为其基类的引用的做法被称为“向上转型”
  • 将一个方法调用同一个方法主体关联起来被称为绑定
    • 若在程序执行前进行绑定(如果有的话,由编译器和链接器实现),叫做前期绑定
    • 若在运行时根据对象的类型进行绑定,则叫做后期绑定,也叫做动态绑定运行时绑定
  • Java 中除了 static 方法和 final 方法(private 方法属于 final 方法)外,其他所有方法都是后期绑定的。
  • Java 用动态绑定实现了多态后,我们可以只编写与基类相关的代码,而这些代码可以对所有该基类的导出类正确运行。
  • 多态的例子可以参考练习2。
  • 在一个设计良好的 OOP 程序中,大多数或所有方法都只与基类接口通信。这样的程序是可扩展的,因为可以从通用的基类继承出新的数据类型,从而新添加一些功能。
  • 域没有多态。
  • 如果一个方法是静态的,那么它的行为就不具有多态性。静态方法是与类,而不是与单个对象相关联的。
  • 由于 final 方法是无法覆盖的,所以 private 也是无法覆盖的,因此没办法进行动态绑定。即只有非 private 方法可以覆盖,但是“覆盖”private 方法编译器不会报错,但运行结果往往与预期不符:
package com.company.ch08;

public class PrivateOverride {
private void func() {
System.out.println("private func()");
} public static void main(String[] args) {
PrivateOverride privateOverride = new Derived();
privateOverride.func();
}
} class Derived extends PrivateOverride {
public void func() { // 这里其实没有覆盖。
System.out.println("Derived func()");
}
}
// private func()

构造器和多态

构造器不具有多态性,它们实际上是 static 方法,只不过该 static 是隐式声明的。

构造器的调用顺序

  • 基类的构造器总是在导出类的构造过程中调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。
  • 在导出类的构造器主体中,如果没有明确指定调用某个基类构造器,它会默默地调用默认构造器。如果不存在默认构造器,编译器就会出错(如果某个类没有任何构造器,则编译器会给他添加一个默认构造器)

构造器的调用顺序:

  1. 调用基类构造器。
  2. 按照声明顺序调用成员的初始化方法。
  3. 调用导出类的构造器的主体。

继承与清理

Java 中通常不需要考虑清理的问题,垃圾回收机制会解决大部分问题,但是如果真的需要进行清理操作时,我们需要手动调用某个特定的函数进行清理操作。因为继承的原因,我们在覆盖基类的清理函数时,需要调用基类版本的清理函数。通常在导出类清理函数的末尾。同时如果成员对象也有需要清理的话,也需要在清理函数中调用该成员的清理函数。调用的原则就是:清理的顺序应该与初始化的顺序相同

如果某些成员对象存在于其他一个或多个对象共享的情况下,我们不能简单的调用其清理函数,我们可以使用“引用计数”来跟踪访问着共享对象的对象数量(就是C++中的shared_ptr)。

构造器内部的多态方法的行为:

如果在一个构造器的内部调用正在构造的对象的某个动态绑定方法,会发生什么?

package com.company.ch08;

class Glyph {
void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
} class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
} @Override
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
} public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
// Glyph() before draw()
// RoundGlyph.draw(), radius = 0
// Glyph() after draw()
// RoundGlyph.RoundGlyph(), radius = 5

从上面的输出可以看出,在基类中调用动态方法,的确会调用到对应导出类的方法,但是导出类的域却未完成初始化。

初始化实例的过程:

  1. 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
  2. 调用基类构造器
  3. 按声明顺序调用成员的初始化方法
  4. 调用导出类的构造器主题。

在构造器内唯一能够安全调用的那些方法是基类中 final 方法(private 方法属于 final 方法)

协变返回类型

Java SE5 中添加了协变返回类型,它表示在导出类中的被覆盖方法可以返回基类方法的返回类型的某种导出类型。

package com.company.ch08;

class Grain {
@Override
public String toString() {
return "Grain";
}
} class Wheat extends Grain {
@Override
public String toString() {
return "Wheat";
}
} class Mill {
Grain process() {
return new Grain();
}
} class WheatMill extends Mill {
@Override
Wheat process() { // 关键在这里,原本返回类型应该是 Grain,而这里使用了 Grain 的导出类 Wheat
return new Wheat();
}
} public class CovariantReturn {
public static void main(String[] args) {
Mill mill = new Mill();
Grain grain = mill.process();
System.out.println("grain = " + grain);
mill = new WheatMill();
grain = mill.process();
System.out.println("grain = " + grain);
}
}
// grain = Grain
// grain = Wheat

用继承进行设计

我们应该首先选择“组合”,尤其是不能十分确定应该使用哪种方法时。组合不会强制我们的程序谁叫进入继承的层次结构。而且,组合更加灵活,他可以动态选择类型。

package com.company.ch08;

class Actor {
public void act() {}
} class HappyActor extends Actor {
@Override
public void act() {
System.out.println("HappyActor");
}
} class SadActor extends Actor {
@Override
public void act() {
System.out.println("SadActor");
}
} class Stage {
private Actor actor = new HappyActor();
public void change() {
actor = new SadActor();
}
public void performPlay() {
actor.act();
}
} public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
// HappyActor
// SadActor

我们通过在运行时将引用与不同的对象重新绑定起来,可以让我们在运行期间获得动态灵活性(也称为“状态模式”)。

继承表示行为间的差异,字段表示状态上的变化

纯继承与扩展

  • is-a 关系(纯继承):只覆盖在基类中已有的方法,不对其进行扩展

    • 导出类和基类有完全相同的接口。
    • 只需要从导出类向上转型,永远不需要知道正在处理的对象的确切类型
  • is-like-a 关系:对基类进行了扩展
    • 导出类接口中扩展部分不能被基类访问。

向下转型与运行时类型识别

在 Java 中,所有转型都会得到检查。即使我们只是进行一次普通的加括弧形式的类型转换,在进入运行期时仍然会对其进行检查,如果不是我们想要转换的类型,那么会返回一个 ClassCastException。

练习

练习1

package com.company.ch08;

public class Cycle {
void run() {
System.out.println("Cycle run");
}
} class Unicycle extends Cycle {
@Override
void run() {
System.out.println("Unicycle run");
}
} class Bicycle extends Cycle {
@Override
void run() {
System.out.println("Bicycle run");
}
} class Tricycle extends Cycle {
@Override
void run() {
System.out.println("Tricycle run");
}
} class Test {
static void ride(Cycle c) {
c.run();
}
public static void main(String[] args) {
Unicycle unicycle = new Unicycle();
Bicycle bicycle = new Bicycle();
Tricycle tricycle = new Tricycle(); unicycle.run();
bicycle.run();
tricycle.run();
}
}
// Unicycle run
// Bicycle run
// Tricycle run

练习2

package com.company.ch08;

public class Shape {
public void draw() {}
public void erase() {}
}
package com.company.ch08;

public class Circle extends Shape{
@Override
public void draw() {
System.out.println("Circle draw");
} @Override
public void erase() {
System.out.println("Circle erase");
}
}
package com.company.ch08;

public class Square extends Shape{
@Override
public void draw() {
System.out.println("Square draw");
} @Override
public void erase() {
System.out.println("Square erase");
}
}
package com.company.ch08;

public class Triangle extends Shape {
@Override
public void draw() {
System.out.println("Triangle draw");
} @Override
public void erase() {
System.out.println("Triangle erase");
}
}
package com.company.ch08;

import java.util.Random;

// 工厂模式
public class RandomShapeGenerator {
private Random random = new Random(47);
public Shape next() {
switch (random.nextInt(3)) {
default:
case 0: return new Circle();
case 1: return new Square();
case 2: return new Triangle();
}
}
}
package com.company.ch08;

public class Shapes {
private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) {
Shape[] shapes = new Shape[9];
for (int i = 0;i < 9;i++) {
shapes[i] = randomShapeGenerator.next();
}
for (Shape shape: shapes) {
shape.draw();
}
}
}
// Triangle draw
// Triangle draw
// Square draw
// Triangle draw
// Square draw
// Triangle draw
// Square draw
// Triangle draw
// Circle draw

练习3

public class Shape {
public void draw() {}
public void erase() {}
public void info() {
System.out.println("Shape info");
}
}

即使导出类没有覆盖它,但是由于继承的原因,导出类任然会有该方法。

package com.company.ch08;

public class Circle extends Shape{
@Override
public void draw() {
System.out.println("Circle draw");
} @Override
public void erase() {
System.out.println("Circle erase");
} @Override
public void info() {
System.out.println("Circle info");
}
}
package com.company.ch08;

public class Shapes {
private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) {
Shape[] shapes = new Shape[9];
for (int i = 0;i < 9;i++) {
shapes[i] = randomShapeGenerator.next();
}
for (Shape shape: shapes) {
shape.info();
}
}
}
// Shape info
// Shape info
// Circle info
// Circle info
// Shape info
// Shape info
// Shape info
// Shape info
// Circle info

如果只有一个导出类Circle覆盖了该方法,只有在正式类型为Circle的Shape调用info时,才会调用到覆盖后的方法,而其余的则是调用到基类的方法。

练习4

class Line extends Shape {
@Override
public void draw() {
System.out.println("Line draw");
} @Override
public void erase() {
System.out.println("Line erase");
}
} public class Shapes {
private static RandomShapeGenerator randomShapeGenerator = new RandomShapeGenerator(); public static void main(String[] args) {
Shape[] shapes = new Shape[9];
for (int i = 0;i < 9;i++) {
shapes[i] = randomShapeGenerator.next();
}
for (Shape shape: shapes) {
shape.draw();
}
shapes[0] = new Line();
shapes[0].draw();
}
} //Shape info
//Shape info
//Circle info
//Circle info
//Shape info
//Shape info
//Shape info
//Shape info
//Circle info

练习5

package com.company.ch08;

public class Cycle {
void run() {
System.out.println("Cycle run");
}
int wheels() {
return 0;
}
} class Unicycle extends Cycle {
@Override
void run() {
System.out.println("Unicycle run");
} @Override
int wheels() {
return 1;
}
} class Bicycle extends Cycle {
@Override
void run() {
System.out.println("Bicycle run");
} @Override
int wheels() {
return 2;
}
} class Tricycle extends Cycle {
@Override
void run() {
System.out.println("Tricycle run");
} @Override
int wheels() {
return 3;
}
} class Test {
static void ride(Cycle c) {
c.run();
}
public static void main(String[] args) {
Unicycle unicycle = new Unicycle();
Bicycle bicycle = new Bicycle();
Tricycle tricycle = new Tricycle(); unicycle.run();
bicycle.run();
tricycle.run(); Cycle[] cycles = new Cycle[]{unicycle, bicycle, tricycle};
for (Cycle cycle: cycles) {
System.out.println("cycle.wheels() = " + cycle.wheels());
}
}
}
// Unicycle run
// Bicycle run
// Tricycle run
// cycle.wheels() = 1
// cycle.wheels() = 2
// cycle.wheels() = 3

练习6

package com.company.ch08;

enum Note {
MIDDLE_C, C_SHARP, B_FLAT;
} class Instrument {
void play(Note n) {
System.out.println("Instrument.play() n = " + n);
}
@Override
public String toString() {
return "Instrument";
}
void adjust() {
System.out.println("Adusting Instrument");
}
} class Wind extends Instrument {
@Override
void play(Note n) {
System.out.println("Wind.play() n = " + n);
}
} class Percussion extends Instrument {
@Override
void play(Note n) {
System.out.println("Percussion.play() n = " + n);
} @Override
public String toString() {
return "Percussion";
} @Override
void adjust() {
System.out.println("Adjusting Percussion");
}
} class Stringed extends Instrument {
@Override
void play(Note n) {
System.out.println("Stringed.play() n = " + n);
} @Override
public String toString() {
return "Stringed";
} @Override
void adjust() {
System.out.println("Adjusting Stringed");
} } class Brass extends Wind {
@Override
void play(Note n) {
System.out.println("Brass.play() n = " + n);
} @Override
void adjust() {
System.out.println("Adjusting Brass");
}
} class Woodwind extends Wind {
@Override
void play(Note n) {
System.out.println("Woodwind.play() n = " + n);
} @Override
public String toString() {
return "Woodwind";
}
} public class Music3 {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
} public static void tuneAll(Instrument[] instruments) {
for (Instrument instrument: instruments) {
tune(instrument);
}
} public static void main(String[] args) {
Instrument[] instruments = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind(),
};
tuneAll(instruments);
for (Instrument instrument: instruments) {
System.out.println(instrument);
}
}
}
// Wind.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Brass.play() n = MIDDLE_C
// Woodwind.play() n = MIDDLE_C
// Instrument
// Percussion
// Stringed
// Instrument
// Woodwind

练习7

class Piano extends Instrument {
@Override
void play(Note n) {
System.out.println("Piano.play() n = " + n);
} @Override
public String toString() {
return "Piano";
} @Override
void adjust() {
System.out.println("Adjusting Piano");
}
} public class Music3 {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
} public static void tuneAll(Instrument[] instruments) {
for (Instrument instrument: instruments) {
tune(instrument);
}
} public static void main(String[] args) {
Instrument[] instruments = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind(),
new Piano(),
};
tuneAll(instruments);
for (Instrument instrument: instruments) {
System.out.println(instrument);
}
}
}
// Wind.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Brass.play() n = MIDDLE_C
// Woodwind.play() n = MIDDLE_C
// Piano.play() n = MIDDLE_C
// Instrument
// Percussion
// Stringed
// Instrument
// Woodwind
// Piano

练习8

class InstrumentGenerator {
private Random random = new Random(42);
public Instrument next() {
switch (random.nextInt(6)) {
default:
case 0: return new Wind();
case 1: return new Percussion();
case 2: return new Stringed();
case 3: return new Brass();
case 4: return new Woodwind();
case 5: return new Piano();
}
}
} public class Music3 {
public static void tune(Instrument i) {
i.play(Note.MIDDLE_C);
} public static void tuneAll(Instrument[] instruments) {
for (Instrument instrument: instruments) {
tune(instrument);
}
} public static void main(String[] args) {
Instrument[] instruments = new Instrument[10];
InstrumentGenerator instrumentGenerator = new InstrumentGenerator();
for (int i = 0;i < 10; i++) {
instruments[i] = instrumentGenerator.next();
}
tuneAll(instruments);
for (Instrument instrument: instruments) {
System.out.println(instrument);
}
}
} // Stringed.play() n = MIDDLE_C
// Brass.play() n = MIDDLE_C
// Wind.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Wind.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Piano.play() n = MIDDLE_C
// Stringed.play() n = MIDDLE_C
// Percussion.play() n = MIDDLE_C
// Piano.play() n = MIDDLE_C
// Stringed
// Instrument
// Instrument
// Stringed
// Instrument
// Percussion
// Piano
// Stringed
// Percussion
// Piano

练习9

package com.company.ch08;

public class Rodent {
void eat() {
System.out.println("Rodent.eat()");
} public static void main(String[] args) {
Rodent[] rodents = new Rodent[] {
new Rodent(),
new Mouse(),
new Gerbil(),
new Hamster(),
};
for (Rodent rodent: rodents) {
rodent.eat();
}
}
} class Mouse extends Rodent {
@Override
void eat() {
System.out.println("Mouse.eat()");
}
} class Gerbil extends Rodent {
@Override
void eat() {
System.out.println("Gerbil.eat()");
}
} class Hamster extends Rodent {
@Override
void eat() {
System.out.println("Hamster.eat()");
}
}
// Rodent.eat()
// Mouse.eat()
// Gerbil.eat()
// Hamster.eat()

练习10

package com.company.ch08;

class Base {
void func1() {
func2();
}
void func2() {
System.out.println("Base");
}
} public class Ex10 extends Base {
@Override
void func2() {
System.out.println("Ex10");
} public static void main(String[] args) {
Base base = new Ex10();
base.func1();
}
}
// Ex10

因为func2既不是static也不是final,所以他是动态绑定的,因此基类的 func1 中调用 func2 方法也是调用到导出类的 func2。

练习11

package com.company.ch08;

class Meal {
Meal() {
System.out.println("Meal()");
}
} class Bread {
Bread() {
System.out.println("Bread()");
}
} class Cheese {
Cheese() {
System.out.println("Cheese()");
}
} class Lettuce {
Lettuce() {
System.out.println("Lettuce()");
}
} class Lunch extends Meal {
Lunch() {
System.out.println("Lunch()");
}
} class PortableLunch extends Lunch {
PortableLunch() {
System.out.println("PortableLunch()");
}
} class Pickle {
Pickle() {
System.out.println("Pickle");
}
} public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
private Pickle p = new Pickle();
public Sandwich() {
System.out.println("Sandwich()");
} public static void main(String[] args) {
new Sandwich();
}
}

练习12

package com.company.ch08;

public class Rodent {
Rodent() {
System.out.println("Rodent");
}
void eat() {
System.out.println("Rodent.eat()");
} public static void main(String[] args) {
Rodent[] rodents = new Rodent[] {
new Rodent(),
new Mouse(),
new Gerbil(),
new Hamster(),
};
for (Rodent rodent: rodents) {
rodent.eat();
}
}
} class Mouse extends Rodent {
Mouse() {
System.out.println("Mouse");
}
@Override
void eat() {
System.out.println("Mouse.eat()");
}
} class Gerbil extends Rodent {
Gerbil() {
System.out.println("Gerbil");
}
@Override
void eat() {
System.out.println("Gerbil.eat()");
}
} class Hamster extends Rodent {
Hamster() {
System.out.println("Hamster");
}
@Override
void eat() {
System.out.println("Hamster.eat()");
}
}

练习13

package com.company.ch08;

class Shared {
private int refcount = 0;
private static long counter = 0;
private final long id = counter++;
public Shared() {
System.out.println("Create " + this);
}
void addRef() {
refcount++;
}
protected void dispose() {
if(--refcount == 0)
System.out.println("Disposing " + this);
} @Override
public String toString() {
return "Shared{" +
"id=" + id +
'}';
} @Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
if (refcount != 0) {
System.out.println("refcount != 0");
}
super.finalize();
}
} class Composing {
private Shared shared;
private static long counter = 0;
private final long id = counter++;
public Composing(Shared shared) {
System.out.println("Creating " + this);
this.shared = shared;
this.shared.addRef();
}
protected void dispose() {
System.out.println("disposing " + this);
shared.dispose();
} @Override
public String toString() {
return "Composing{" +
"id=" + id +
'}';
}
} public class ReferenceCounting {
public static void main(String[] args) { // Shared shared = new Shared();
// Composing[] composings = {
// new Composing(shared), new Composing(shared),
// new Composing(shared), new Composing(shared),
// new Composing(shared)
// };
//
// for (Composing composing: composings) {
// composing.dispose();
// }
new Composing(new Shared());
System.gc();
}
}

练习14

练习15

package com.company.ch08;

class Glyph {
void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
} class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
} @Override
void draw() {
System.out.println("RoundGlyph.draw(), radius = " + radius);
}
} class RectangularGlygh extends Glyph {
private int length;
RectangularGlygh(int length) {
this.length = length;
System.out.println("RectanguarGlygh length = " + length);
} @Override
void draw() {
System.out.println("RectanguarGlygh.draw() length = " + length);
}
} public class PolyConstructors {
public static void main(String[] args) {
new RectangularGlygh(10);
}
}
// Glyph() before draw()
// RectanguarGlygh.draw() length = 0
// Glyph() after draw()
// RectanguarGlygh length = 10

练习16

package com.company.ch08;

class Status {
void func() {}
} class StatusA extends Status {
void func() {
System.out.println("Status A");
}
} class StatusB extends Status {
void func() {
System.out.println("Status B");
}
} class StatusC extends Status {
void func() {
System.out.println("Status C");
}
} class AlterStatus {
Status status = new StatusA();
void A() {
status = new StatusA();
}
void B() {
status = new StatusB();
}
void C() {
status = new StatusC();
}
void call() {
status.func();
}
} public class Starship {
public static void main(String[] args) {
AlterStatus alterStatus = new AlterStatus();
alterStatus.call();
alterStatus.B();
alterStatus.call();
alterStatus.C();
alterStatus.call();
alterStatus.A();
alterStatus.call();
}
}
// Status A
// Status B
// Status C
// Status A

练习17

package com.company.ch08;

public class Cycle {
void run() {
System.out.println("Cycle run");
}
int wheels() {
return 0;
}
} class Unicycle extends Cycle {
@Override
void run() {
System.out.println("Unicycle run");
} @Override
int wheels() {
return 1;
} void balance() {}
} class Bicycle extends Cycle {
@Override
void run() {
System.out.println("Bicycle run");
} @Override
int wheels() {
return 2;
} void balance() {}
} class Tricycle extends Cycle {
@Override
void run() {
System.out.println("Tricycle run");
} @Override
int wheels() {
return 3;
}
} class Test {
static void ride(Cycle c) {
c.run();
}
public static void main(String[] args) { Cycle[] cycles = new Cycle[]{new Unicycle(), new Bicycle(), new Tricycle()};
// for(Cycle cycle: cycles) {
// cycle.balance(); // 无法调用
// }
Unicycle unicycle = (Unicycle)cycles[0];
Bicycle bicycle = (Bicycle)cycles[1];
Tricycle tricycle = (Tricycle)cycles[2]; unicycle.balance();
bicycle.balance();
// tricycle.balance(); //无法调用
}
}

首发与Code & Fun

《 Java 编程思想》CH08 多态的更多相关文章

  1. 【学习笔记】JAva编程思想之多态

    1.如果java的基类拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法名称并不会屏蔽在基类的任何版本.因此,无论是在该层或者他的基类中对方法进行定义,重载机制都可以正常工作. 2.使用@O ...

  2. Java编程思想之八多态

    在面向对象的程序设计语言中,多态是继数据和继承之后的第三张基本特征 多态不但能够改善代码组织结构和可读性,还能够创建可扩展的程序--即无论在项目最初创建时还是在需要添加新功能时都可以"生长& ...

  3. 《java编程思想》多态与接口

    向上转型 定义:把某个对象的引用视为对其基类类型的引用的做法被称为向上转型方法调用绑定 将一个方法调用同一个方法主体关联起来被称作绑定. 前期绑定:程序执行前进行的绑定叫做前期绑定,前期绑定也是jav ...

  4. (七)《Java编程思想》——多态的缺陷

    1.不能“覆盖”私有方法 package chapter8; /** * 不能"覆盖"私有方法 */ public class PrivateOverride { private ...

  5. Java编程思想(四) —— 复用类

    看了老罗罗升阳的专訪,不由自主地佩服,非常年轻,我之前以为和罗永浩一个级别的年龄.也是见过的不是初高中编程的一位大牛之中的一个,专訪之后.发现老罗也是一步一个脚印的人. 别说什么难做,做不了.你根本就 ...

  6. Java编程思想学习(一)----对象导论中多态的理解

    1.1抽象过程 1)万物皆对象. 2)程序是对象的集合,他们通过发送消息来告知彼此所要求做的. 3)每个对象都有自己的由其他对象所构成的存储. 4)每个对象都拥有其类型. 5)某一特定类型的所有对象都 ...

  7. 《Java编程思想》学习笔记_多态

    多态 多态指一个行为产生多种状态,针对父类类型可接收其子类类型,最终执行的状态由具体子类确定,其不同子类可呈现出不同状态.例如人[父类]都会跑步[行为],但小孩[子类]跑步.成年人[子类]跑步.运动员 ...

  8. 《Java编程思想》学习笔记(二)——类加载及执行顺序

    <Java编程思想>学习笔记(二)--类加载及执行顺序 (这是很久之前写的,保存在印象笔记上,今天写在博客上.) 今天看Java编程思想,看到这样一道代码 //: OrderOfIniti ...

  9. Java编程思想重点笔记(Java开发必看)

    Java编程思想重点笔记(Java开发必看)   Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而 ...

  10. java编程思想-复用类总结

    今天继续读<java 编程思想>,读到了复用类一章,看到总结写的很好,现贴上来,给大家分享. 继承和组合都能从现有类型生成新类型.组合一般是将现有类型作为新类型底层实现的一部分来加以复用, ...

随机推荐

  1. Java之Object类用法总结

    Object类概述: 1.Object类是所有Java类的根父类. 2.如果在类的声明中未使用extends关键字指明其父类, 则默认父类为java.lang.Object类. Object类主要结构 ...

  2. 【Tool】---ubuntu18.04配置oh-my-zsh工具

    作为Linux忠实用户,应该没有人不知道bash shell工具了吧,其实除了bash还有许多其他的工具,zsh就是一款很好得选择,基于zsh shell得基础之上,oh-my-zsh工具更是超级利器 ...

  3. django 数据库中的表生成model

    https://blog.csdn.net/weixin_34405354/article/details/93582647 还没有证实是否有效

  4. 创建模仿存储库 Making a Mock Repository 精通ASP-NET-MVC-5-弗瑞曼 Listing 7-5

  5. ASENET MVC 5 with Bootstrap and Knockout.js 第一弹

     A Basic Example Now that the Knockout library is installed, let’s get right to an example of using ...

  6. JSON Web Token 是什么?

    免费获得官方JWT手册并深入学习JWT吧! 简介 JSON Web Token(缩写JWT),是一套开放的标准(RFC 7519),它定义了一种紧凑且自URL安全的方式,以JSON对象的方式在各方之间 ...

  7. CAS的ABA问题详解

    CAS的ABA问题详解 ABA问题 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下 1.线程1,期望值为A ...

  8. Selenium(六):截图

    截图 from selenium import webdriver driver = webdriver.Chrome() # 以PNG格式,保存浏览器截图,filename为截图文件绝对路径 dri ...

  9. SpringCloud与微服务Ⅳ --- Rest微服务构建案例工程模块

    一.父工程搭建 父工程pom: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=& ...

  10. JDK源码之Boolean类分析

    一 简介 boolean类型的封装类,将基本类型为boolean的值包装在一个对象中,实现序列化接口,和Comparable接口 额外提供了许多便捷方法,比较简单,直接贴代码分析 二 源码分析 //t ...