5 分でプロトタイプ モードをマスターする
今日は プロトタイプ パターンを共有します # # デザインパターンにあります。適切なライフ ストーリーや実際のプロジェクト シナリオを使用してデザイン パターンについて話し、最後にデザイン パターンを一文にまとめます。
ストーリー 職場で、私は偶然、比較的美しいプログラマーの履歴書テンプレートをインターネットで見つけたので、クラス全員がその履歴書 (U ディスク) を狂ったようにコピーし始めました。一方で、冗談もあって、過去の履歴書を内容も名前も変えずにコピーした学生が何人かいて、期限内に面接官(学内採用面接官)に提出しました。全員がインターンシップに行き、中にはまだ仕事を探している人もいました。 その後、会社の面接官や他のクラスメートからのフィードバック: 全く同じ履歴書を何通か受け取りました。戻ってきたら、話し合った後、問題が何であるか全員が理解していました。私はそれをコピーしたことを認め、有害で恥ずかしいものです。
履歴書のコピーには 2 種類あります:
1 つは、履歴書をコピーして自分の情報に変更する方法です。 もう 1 つは、内容を変更せずに履歴書をコピーする方法です。
プロトタイプ パターン定義
プロトタイプ パターンの定義プロトタイプ インスタンスを使用して作成するオブジェクトの種類、およびこのプロトタイプに対処して新しいオブジェクトを作成する
大まかな意味: プロトタイプ インスタンスを使用して作成するオブジェクトの種類を指定し、これらのプロトタイプをコピーして新しいオブジェクトを作成する。
プロトタイプ パターン: プロトタイプ パターン
。これは作成パターンです。
呼び出し元は作成の詳細を知る必要はなく、オブジェクトを作成するためにコンストラクターを呼び出す必要もありません。
使用シナリオ
プロトタイプ モードには次の使用シナリオがあります:
- #クラスの初期化はより多くのリソースを消費します #new によって生成されたオブジェクトには非常に面倒なプロセス (データの準備、アクセス許可など) が必要です
- コンストラクターはより複雑です
- ループ本体で多数のオブジェクトが生成される場合
- In Spring では、プロトタイプ パターンが広く使用されています。例:
- scope='prototype'
が提供する Cloneable
インターフェイスを使用して、高速コピーを実現することもできます。 オブジェクトを作成する 4 つの方法:
このようなよくある状況に遭遇したことはありませんか? プロジェクトでは、データベース テーブルにマッピングされたエンティティ クラスをフロントエンドに返せないと規定されているため、通常は次のものが返されますフロントエンドへ: XxxVO、XxxBO、XxxDTO...
このとき、次のようなシーンが表示されますが、誰もが想像しているかもしれません。
以下は、データベース テーブルにマップされた
UserEntity エンティティ クラスです。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>public class UserEntity {
private Long id;
private String name;
private Integer age;
//....可能还有很多属性
//省略getter setter
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
フロントエンドまたは呼び出し元に返される UserVO エンティティ クラス。
public class UserVO { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }
現時点では、データベースから見つかった UserEntity を UserVO に変換して、フロントエンド (または呼び出し元) に返す必要があります。
public class ObjectConvertUtil { public static UserVo convertUserEntityToUserVO(UserEntity userEntity) { if (userEntity == null) { return null; } UserVo userVo = new UserVo(); userVo.setId(userEntity.getId()); userVo.setName(userEntity.getName()); userVo.setAge(userEntity.getAge()); //如果还有更多属性呢? return userVo; } }
从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是有点恐怖?
于是,我们通常都会使用一些工具类来处理,比如常见有以下:
BeanUtils.copy(); JSON.parseObject() Guava工具类 .....
这些工具类就用到了原型模式。
通过一个对象,创建一个新的对象。
也把原型模式称之为对象的拷贝、克隆。
其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
我们先来聊聊浅克隆,都喜欢由浅入深。
浅克隆
比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息UserAddress
属性。
以下是代码的实现:
//用户地址信息 public class UserAddress implements Serializable{ private String province; private String cityCode; public UserAddress(String province, String cityCode) { this.province = province; this.cityCode = cityCode; } } //用户信息 public class User implements Cloneable { private int age; private String name; //用户地址信息 private UserAddress userAddress; //getter setter 省略 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } //测试 public class UserTest { public static void main(String[] args) throws Exception { User user = new User(); user.setAge(20); user.setName("田维常"); UserAddress userAddress = new UserAddress("贵州", "梵净山"); user.setUserAddress(userAddress); User clone = (User) user.clone(); System.out.println("克隆前后UserAddress比较:" + (user.getUserAddress() == clone.getUserAddress())); } }
输出结果
克隆前后 UserAddress 比较:true
两个对象属性 UserAddress
指向的是同一个地址。
这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。
关系如下:
深克隆
关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成n多个孙悟空,手里都会拿着一个金箍棒。
按照前面的浅克隆,结果就是:孙悟空倒是变成很多孙悟空,但是金箍棒用的是同一根。
深克隆的结果是:孙悟空变成了很多个,金箍棒也变成很多个根。
下面我们用代码来实现:
//猴子,有身高体重和生日 public class Monkey { public int height; public int weight; public Date birthday; }
孙悟空也是猴子,兵器 孙悟空有个金箍棒:
import java.io.Serializable; //孙悟空的金箍棒 public class JinGuBang implements Serializable{ public float h=100; public float d=10; //金箍棒变大 public void big(){ this.h *=10; this.d *=10; } //金箍棒变小 public void small(){ this.h /=10; this.d /=10; } }
齐天大圣孙悟空:
import java.io.*; import java.util.Date; //孙悟空有七十二变,拔猴毛生成一个金箍棒 //使用JDK的克隆机制, //实现Cloneable并重写clone方法 public class QiTianDaSheng extends Monkey implements Cloneable, Serializable { public JinGuBang jinGuBang; public QiTianDaSheng() { this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() throws CloneNotSupportedException { return this.deepClone(); } //深克隆 public QiTianDaSheng deepClone() { try { //内存中操作完成、对象读写,是通过字节码直接操作 //与序列化操作类似 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream bis = new ObjectInputStream(bais); //完成一个新的对象,底层是使用new创建的一个对象 //详情可以了解readObject方法 QiTianDaSheng qiTianDaSheng = (QiTianDaSheng) bis.readObject(); //每个猴子的生日不一样,所以每次拷贝的时候,把生日改一下 qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } catch (Exception ex) { ex.printStackTrace(); return null; } } //浅克隆,就是简单的赋值 public QiTianDaSheng shalllowClone(QiTianDaSheng target) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); qiTianDaSheng.height = target.height; qiTianDaSheng.weight = target.weight; qiTianDaSheng.jinGuBang = target.jinGuBang; qiTianDaSheng.birthday = new Date(); return qiTianDaSheng; } }
接着我们就来测试一下:
public class DeepCloneTest { public static void main(String[] args) { QiTianDaSheng qiTianDaSheng = new QiTianDaSheng(); try { QiTianDaSheng newObject = (QiTianDaSheng) qiTianDaSheng.clone(); System.out.print("深克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } catch (Exception ex) { ex.printStackTrace(); } QiTianDaSheng newObject=qiTianDaSheng.shalllowClone(qiTianDaSheng); System.out.print("浅克隆后 "); System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang)); } }
输出结果为:
深克隆后 金箍棒是否一直:false 浅克隆后 金箍棒是否一直:true
结论
深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。
总结
切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。
为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。
深いコピー: 履歴書をコピーし、履歴書の情報を自分のものに変更します。 - ##浅いコピー: 履歴書をコピーし、履歴書の内容はまったく変更しません
利点:
- Java プロトタイプ モードはメモリ バイナリ ストリーム コピーに基づいており、直接新しいものよりも優れたパフォーマンスを発揮します。 。
- ディープ クローン作成を使用すると、オブジェクトの状態を保存し、古いコピーを保存して (クローンを作成して)、それを変更することができます。これは元に戻す機能として機能します。
欠点:
- clone メソッドを構成する必要があり、変換中に既存のクラスを変更する必要があるため、違反します。閉鎖の「オープン」原則」。
- オブジェクト間に複数のネストされた参照がある場合、各レイヤーを複製する必要があります。
以上が5 分でプロトタイプ モードをマスターするの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。
