- 技術 💻
オブジェクトとは?クラスとの違いと参照の落とし穴を3言語の実行結果で
結論
オブジェクトとは、プログラムの中で「データ(属性)」と「そのデータを扱う操作(メソッド)」を1つにまとめたものです。クラスが設計図、そこから作った1つ1つの実体がインスタンスで、ふだん「オブジェクト」と呼ぶのはこのインスタンスです。
この記事でわかること
- オブジェクト・クラス・インスタンスの違いを1つの表で
- 同じ「商品」をPython・Java・JavaScriptで作った実行結果
- is と ==・参照の共有・JSONとの違い・1個のメモリ量の実測
オブジェクトは「もの」という意味の英語 object から来た言葉です。プログラミングでは、名前・値段・在庫のようなデータと、「合計金額を出す」「売る」のような操作をひとまとめにして、1つの「もの」として扱います。
flowchart TD
C[クラス Product<br>設計図:name・price・stock<br>total・sell] -->|new / 呼び出し| A[インスタンス1<br>ボールペン 120円 10本]
C -->|new / 呼び出し| B[インスタンス2<br>ノート 200円 5冊]
A --> O[どちらもオブジェクト<br>データと操作のまとまり]
B --> O
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| オブジェクトとは | データ(属性)と操作(メソッド)を1つにまとめたもの | オブジェクトとは(意味) |
| オブジェクトとクラスの違い | クラスは設計図、オブジェクトは設計図から作った実体 | クラス・インスタンスとの違い |
| オブジェクトとインスタンスの違い | ほぼ同じもの。「どのクラスから作ったか」を言うときにインスタンスと呼ぶ | クラス・インスタンスとの違い |
| オブジェクト プログラミングでの作り方 | Python は Product(...)、Java は new Product(...)、JS は {...} か new |
Python・Java・JavaScript の各節 |
| 同じ中身なのに == が false | 別々に作ったオブジェクトは「別もの」。中身の比較は自分で決める | 同一性と等価 |
| 片方を変えたらもう片方も変わった | 代入はコピーではなく、同じオブジェクトを指す参照の共有 | 参照の共有の落とし穴 |
| JavaScript のオブジェクトと JSON の違い | JSON は文字列の形式。キーは二重引用符、関数や undefined は入らない | オブジェクトと JSON の違い |
| オブジェクト指向との違い | オブジェクトは「もの」、オブジェクト指向はそれを組み合わせて作る考え方 | オブジェクト指向との関係 |
確認環境:Ubuntu 24.04.5 LTS(x86-64)の上で、同じ「商品(Product)」オブジェクトを Python 3.13.18・OpenJDK 21.0.12.1(javac・java)・Node.js 22.23.4 で書いて実行し、属性とメソッド、同一性と等価、参照の共有、コピー、ミュータブルとイミュータブル、エラー文の原文を出力しました。メモリの大きさは Python の sys.getsizeof と tracemalloc、Java は Runtime のヒープ使用量(SerialGC)、Node.js は --expose-gc を付けた process.memoryUsage() で、10万個〜100万個作ったときの差から1個あたりを求めた目安です。「オブジェクトファイル」の例は gcc 13.3.0 で確かめました。定義は Python 言語リファレンス(データモデル)、Java SE 21 の API 仕様(java.lang.Object)、MDN(Object の用語集・JSON)で確かめています。図は mermaid で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「オブジェクトとは?クラスとの違いと参照の落とし穴を3言語の実行結果で」
オブジェクトとは(意味を一言で)
この節では、プログラミングでいうオブジェクトの意味と、ほかの場面で使われる「オブジェクト」との違いが分かります。
プログラミングのオブジェクトは、「状態(データ)」と「ふるまい(操作)」を持った1つのまとまりです。 データの部分を属性(プロパティ・フィールド・メンバー変数)、操作の部分をメソッドと呼びます。呼び方は言語で少し違いますが、指しているものは同じです。
| 部分 | 役割 | 「商品」の例 | 言語ごとの呼び方 |
|---|---|---|---|
| 属性(データ) | そのオブジェクトの今の状態 | 名前・値段・在庫数 | Python:属性/Java:フィールド/JS:プロパティ |
| メソッド(操作) | 状態を使う・変える処理 | 合計金額を出す・売って在庫を減らす | どの言語でもメソッド |
| 同一性 | ほかのオブジェクトと区別する印 | 同じボールペンでも「棚のこの1本」 | Python:id()/Java:参照/JS:参照 |
Python の言語リファレンスは、オブジェクトを次のように説明しています。
オブジェクト は、Python がデータを抽象化したものです。 Python プログラム内のすべてのデータは、オブジェクトまたはオブジェクト間の関係として表されます。(中略)すべてのオブジェクトは、同一性 (identity)、型、値をもっています。
JavaScript については、MDN の用語集が「JavaScript では、オブジェクトはプロパティの集合と見なすことができます」と説明しています。言語によって言い方は違いますが、「中身(値)を持ち、ほかと区別でき、決まった操作ができるもの」という点は共通しています。
パソコンにたとえると
「パソコン」という機種名は1つでも、店に並ぶパソコンは1台ずつ製造番号で区別されます。プログラミングのオブジェクトは、この製造番号の付いた1台にあたります。CPU・メモリの容量は属性、「起動する」「終了する」はメソッドです。同じ機種の2台は仕様が同じでも別の1台で、片方の電源を切っても、もう片方は動き続けます。この「同じ中身でも別もの」という点が、後の節の is と == の話につながります。
ほかの場面の「オブジェクト」
同じ言葉が、IT の中でも別の意味で使われます。検索するときに混ざりやすいので、違いを並べます。
プログラミングの部品
この記事の主題。データと操作のまとまり。クラスから作る。
Python・Java・JS など
画面の上の部品
図形描画ソフトで置いた四角形、3DCG の立体、スライドの図形やアイコン。選んで動かせる1つの単位。
Office・CAD・3DCG
オブジェクトファイル
コンパイルで作られる機械語のファイル(.o・.obj)。object は「目的」の意味とされ、プログラミングの部品とは別の話。
C・C++ のビルド
保存の単位
Amazon S3 などのオブジェクトストレージでは、1つのファイルとその情報をオブジェクトと呼ぶ。
クラウドの保存先
オブジェクトファイルが本当に別物であることは、C のソースをコンパイルするだけで分かります。中身は ELF という形式の機械語でした。
$ printf 'int add(int a, int b) { return a + b; }\n' > add.c
$ gcc -c add.c -o add.o
$ file add.o
add.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ nm add.o
0000000000000000 T add
$ od -A x -t x1z -N 16 add.o
000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 >.ELF............<
IT 以外では、美術の「オブジェ(立体作品)」や、英語の「目的・対象」の意味でも使われます。
オブジェクトとクラス・インスタンスの違い
この節では、オブジェクト・クラス・インスタンスという3つの言葉の関係が、表とたとえで分かります。
クラスは「どんな属性とメソッドを持つか」を決めた設計図で、クラスから作った実体がインスタンスです。 インスタンスとオブジェクトはほぼ同じものを指し、「Product クラスのインスタンス」のように、作った元のクラスを言うときにインスタンスと呼びます。
| 言葉 | 意味 | 「商品」の例 | 数 |
|---|---|---|---|
| クラス | 設計図。属性とメソッドの形を決める | Product(name・price・stock と total・sell を持つ) |
1つ |
| インスタンス | クラスから作った1つの実体 | ボールペン 120円 10本、ノート 200円 5冊 | いくつでも |
| オブジェクト | データと操作のまとまり全般。インスタンスとほぼ同じ意味で使う | 上の2つ。Python や JS では数値・関数・クラス自体もオブジェクト | いくつでも |
| インスタンス化 | クラスからインスタンスを作ること | new Product("ノート", 200, 5) |
― |
たい焼きにたとえると
見分ける問い:「それを1つ、2つと数えられ、別々に中身を変えられるか」
「メッセージを送る」という言い方
オブジェクト指向の説明では、「オブジェクトにメッセージを送ると、オブジェクトがメソッドを実行する」と書かれることがあります。pen.sell(3) は「pen に『3本売れた』というメッセージを送る」と読めます。呼び出す側は、在庫がどう減るかを知らなくても、メッセージを送るだけで済みます。これが、データと操作をまとめる理由です。
Java でクラスを書いて new する手順は、関連記事「Java クラスとインスタンスとは|newでの作り方とフィールド・メソッド」で扱っています。
Pythonでオブジェクトを作る(属性とメソッド)
この節では、Python で「商品」オブジェクトを作り、属性とメソッドを使う流れと、Python では何でもオブジェクトであることが実行結果で分かります。
Python では、クラス名を関数のように呼ぶとインスタンスが作られ、__init__ で属性に初期値が入ります。 メソッドの最初の引数 self には、呼び出したオブジェクト自身が入ります。
class Product:
def __init__(self, name, price, stock=0):
self.name = name # 属性(データ)
self.price = price
self.stock = stock
def total(self): # メソッド(操作)
return self.price * self.stock
def sell(self, n):
if n > self.stock:
raise ValueError(f"在庫が足りません: 残り{self.stock}")
self.stock -= n
pen = Product("ボールペン", 120, 10)
note = Product("ノート", 200, 5)
print(pen.name, pen.price, pen.stock, pen.total())
print(note.name, note.price, note.stock, note.total())
print("vars(pen) =", vars(pen))
print("type(pen) =", type(pen), " isinstance:", isinstance(pen, Product))
pen.sell(3)
print("sell(3) 後の在庫:", pen.stock)
ボールペン 120 10 1200
ノート 200 5 1000
vars(pen) = {'name': 'ボールペン', 'price': 120, 'stock': 10}
type(pen) = <class '__main__.Product'> isinstance: True
sell(3) 後の在庫: 7
同じクラスから作った pen と note は、それぞれ自分の属性を持っています。pen.sell(3) で減ったのは pen の在庫だけです。vars() を使うと、そのオブジェクトが持つ属性を辞書で見られます。
Python では数値も関数もクラスもオブジェクト
Python では、自分で作ったインスタンスだけでなく、あらゆる値がオブジェクトです。いくつかの値の型を調べました。
1 type=int isinstance(object)=True
'abc' type=str isinstance(object)=True
[1, 2] type=list isinstance(object)=True
<built-in function len> type=builtin_function_or_method isinstance(object)=True
<class '__main__.Product'> type=type isinstance(object)=True
None type=NoneType isinstance(object)=True
数値の 1 も、関数の len も、クラスの Product も、None も、すべて object のインスタンスでした。 だから "abc".upper() のように、文字列にもメソッドがあります。クラス Product 自身の型は type で、クラスも「type クラスのインスタンス」というオブジェクトです。
print したときの表示
何も書かないクラスのオブジェクトを print すると、クラス名とメモリ上の場所が出ます。__repr__ を書くと、中身が読める表示に変えられます。
<__main__.P object at 0x7f05238df230>
Q(name='ペン', price=120)
__init__ と self の動きをもっと詳しく追うなら、関連記事「Pythonクラスの使い方|__init__とselfを実行結果つきで図解」で扱っています。
Javaでオブジェクトを作る(newとObjectクラス)
この節では、Java で同じ「商品」オブジェクトを new で作り、すべてのクラスの親である Object クラスから何を受け継ぐのかが実行結果で分かります。
Java では new クラス名(...) でオブジェクトを作り、変数にはオブジェクトそのものではなく「オブジェクトへの参照」が入ります。
class Product {
String name;
int price;
int stock;
Product(String name, int price, int stock) {
this.name = name; this.price = price; this.stock = stock;
}
int total() { return price * stock; }
}
Product pen = new Product("ボールペン", 120, 10);
System.out.println(pen.name + " " + pen.price + " " + pen.total());
System.out.println("toString: " + pen);
System.out.println("hashCode: " + pen.hashCode() + " (0x" + Integer.toHexString(pen.hashCode()) + ")");
System.out.println("getClass: " + pen.getClass().getName()
+ " / super: " + pen.getClass().getSuperclass().getName());
ボールペン 120 1200
toString: Product@d716361
hashCode: 225534817 (0xd716361)
getClass: Product / super: java.lang.Object
extends を書いていない Product の親クラスは java.lang.Object でした。Java SE 21 の API 仕様は「Class Object is the root of the class hierarchy. Every class has Object as a superclass.」としています。toString・equals・hashCode は、Object から受け継いだメソッドです。
Product@d716361は「クラス名+@+ハッシュコードの16進数」です。 API 仕様は既定の toString をgetClass().getName() + '@' + Integer.toHexString(hashCode())と同じ値だと説明しています。- 同じプログラムを2回実行すると、1回目は
Product@4dc63996、2回目はProduct@d716361でした。API 仕様にも、hashCode は実行のたびに同じである必要はない、と書かれています。表示されたハッシュコードを、保存や比較のキーに使ってはいけません。
record で「値を表すオブジェクト」を短く書く
Java 16 からの record を使うと、値を入れるだけのクラスに、equals・hashCode・toString が自動で付きます。
record Item(String name, int price) {}
Item i1 = new Item("ノート", 200), i2 = new Item("ノート", 200);
System.out.println(i1 + " equals: " + i1.equals(i2) + " ==: " + (i1 == i2)
+ " hash同じ: " + (i1.hashCode() == i2.hashCode()));
Item[name=ノート, price=200] equals: true ==: false hash同じ: true
中身が同じなら equals は true、別々に new したので == は false です。この違いは次の節以降で詳しく扱います。
JavaScriptのオブジェクト(リテラル・クラス・プロパティ)
この節では、JavaScript でオブジェクトを作る2つの方法と、プロパティの決まりが実行結果で分かります。
JavaScript では、クラスを書かなくても { }(オブジェクトリテラル)でその場でオブジェクトを作れます。 同じ形のものをたくさん作るときは class を使います。
const pen = { name: "ボールペン", price: 120, stock: 10,
total() { return this.price * this.stock; } };
console.log(pen.name, pen["price"], pen.total());
class Product {
constructor(name, price, stock = 0) { this.name = name; this.price = price; this.stock = stock; }
total() { return this.price * this.stock; }
get label() { return `${this.name}(${this.price}円)`; }
}
const note = new Product("ノート", 200, 5);
console.log(note, note.total(), note.label);
console.log("typeof:", typeof pen, typeof note, typeof null, typeof [], typeof Product);
console.log("Object.keys(note):", Object.keys(note));
pen.color = "黒"; delete pen.stock;
console.log("追加と削除のあと:", Object.keys(pen));
const sym = Symbol("id"); pen[sym] = 1; pen[1] = "one";
console.log("キーの型:", Object.keys(pen).map(k => typeof k), Object.getOwnPropertySymbols(pen));
ボールペン 120 1200
Product { name: 'ノート', price: 200, stock: 5 } 1000 ノート(200円)
typeof: object object object object function
Object.keys(note): [ 'name', 'price', 'stock' ]
追加と削除のあと: [ 'name', 'price', 'total', 'color' ]
キーの型: [ 'string', 'string', 'string', 'string', 'string' ] [ Symbol(id) ]
- プロパティは後から足せて、消せます。
pen.color = "黒"で増え、delete pen.stockで消えました。Java のように、クラスで決めた形に縛られません。 - キーは文字列かシンボルだけです。
pen[1]と数値で書いても、キーは文字列の"1"になりました。MDN も、プロパティのキーは文字列値かシンボル値のどちらかだと説明しています。 typeof nullは"object"です。 null はオブジェクトではありませんが、言語の初期からこの結果のままです。配列も"object"、クラスは"function"と出ました。labelは値を持つデータプロパティではなく、読むたびに関数が動くアクセサープロパティ(getter)です。クラスに書いた getter はインスタンスではなくプロトタイプにあるため、Object.keys(note)には出ませんでした。
キーの一覧・コピーなどの操作をまとめて見たいときは、関連記事「JavaScriptオブジェクトのコピー|structuredClone入門」で扱っています。
3言語の対応表
| やりたいこと | Python | Java | JavaScript |
|---|---|---|---|
| 作る | Product("ペン", 120) |
new Product("ペン", 120, 0) |
{ name: "ペン" }・new Product(...) |
| 自分自身 | self(引数に書く) |
this(書かなくてよい) |
this(呼び方で変わる) |
| 属性を後から足す | できる(__slots__ で禁止できる) |
できない(コンパイルエラー) | できる(Object.freeze で禁止) |
| 同じオブジェクトか | a is b |
a == b |
a === b |
| 中身が等しいか | a == b(__eq__) |
a.equals(b) |
標準の演算子は無い(自分で比べる) |
| 識別の番号 | id(a) |
System.identityHashCode(a) |
無い |
| 文字列にした表示 | __repr__・__str__ |
toString() |
toString()・JSON.stringify |
同一性と等価の違い(is・==・equals・===)
この節では、「同じオブジェクトか」と「中身が同じか」の2つの比較の違いが、3言語の実行結果で分かります。
オブジェクトの比較には「同一性(同じ1つのものか)」と「等価(中身が等しいか)」の2種類があります。 中身がまったく同じ商品を2回作り、3つ目の変数には1つ目を代入して比べました。
a = Product("ボールペン", 120, 10)
b = Product("ボールペン", 120, 10)
c = a
print("a == b:", a == b, " a is b:", a is b)
print("a == c:", a == c, " a is c:", a is c)
print("id(a) =", id(a), hex(id(a)))
print("id(b) =", id(b), hex(id(b)))
print("id(c) =", id(c))
a == b: False a is b: False
a == c: True a is c: True
id(a) = 139819557829072 0x7f2a4713b9d0
id(b) = 139819560401840 0x7f2a473afbb0
id(c) = 139819557829072
| 比べたもの | Python | Java | JavaScript |
|---|---|---|---|
| 別々に作った同じ中身(a と b)・同一性 | a is b → False |
a == b → false |
a === b → false |
| 別々に作った同じ中身・等価(何も書かない) | a == b → False |
a.equals(b) → false |
a == b → false |
| 代入した c と a | is も == も True | == → true |
=== → true |
| 等価を自分で定義したあと | __eq__ で True |
equals で true |
自分で関数を書く |
何も書かなければ、3言語とも「中身が同じでも、別々に作ったものは等しくない」と判定しました。 Python の == も、__eq__ を書いていないクラスでは is と同じ比較になります。CPython の id() は、Python 言語リファレンスのとおりメモリ上のアドレスで、c と a は同じ値でした。
等価を定義すると、ハッシュの決まりも付いてくる
「名前と値段が同じなら同じ商品」と決めて、等価を定義しました。ここで多くの人がつまずきます。
# Python:__eq__ だけ定義した Product2
__eq__ 定義後 x == y: True x is y: False
TypeError: unhashable type: 'Product2'
# Java:equals だけ上書きして hashCode を書かない
x.equals(y): true set.contains(y): false hash x/y: 980546781/2061475679
hashCode も書くと contains: true Product3[name=ボールペン, price=120]
- Python は
__eq__だけを書くと、そのクラスのオブジェクトが集合(set)や辞書のキーに使えなくなりました。 等しいもの同士はハッシュ値も等しい、という決まりを守れなくなるためです。使うなら__hash__も書きます。 - Java は equals だけを上書きしてもコンパイルは通り、HashSet の
containsが false を返しました。 API 仕様は「equals で等しい2つのオブジェクトの hashCode は同じ整数を返さなければならない」としています。Objects.hash(name, price)で hashCode を足すと true になりました。record ならこの2つが自動で揃います。
文字列と数値の比較で起きること
文字列や数値も、オブジェクトとして比べると、思わぬ結果になります。
# Java
リテラル同士 ==: true new String ==: false equals: true
Integer 127 ==: true 128 ==: false 128 equals: true
# Python
256 is: True
257 is: False ==: True
Java の Integer は -128〜127 を使い回すため127では == が true、128では false でした。Python も小さな整数を使い回すため、int("256") は同じオブジェクト、int("257") は別のオブジェクトでした。さらに、同じファイルに x = 257 と x is 257 を書くと、警告が出たうえで結果は True でした。
/dev/shm/t98/warn.py:2: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
print(x is 257)
True
同じ値でも、書き方や実装で is の結果が変わります。 Python 言語リファレンスも、a = 1; b = 1 のあとに a と b が同じオブジェクトを指すかは実装によって異なり、頼るべきではないとしています。値の比較には ==(Java は equals)、is は None との比較などに限って使います。
参照の共有の落とし穴(代入はコピーではない)
この節では、変数への代入が「オブジェクトのコピー」ではなく「同じオブジェクトを指すこと」であるために起きる不具合が、実行結果で分かります。
変数はオブジェクトそのものではなく、オブジェクトを指す名札(参照)です。 c = a は、a が指しているオブジェクトに c という2つ目の名札を付けるだけで、商品が2つに増えるわけではありません。
flowchart LR
a[変数 a] --> P[Product<br>ボールペン 150円]
c[変数 c] --> P
b[変数 b] --> Q[Product<br>ボールペン 120円]
# Python
c.price=150 のあと a.price = 150
# Java
c.price=150 のあと a.price = 150
# JavaScript
c.price=150 のあと a.price = 150
3言語とも、c を通して値段を変えると、a から見た値段も変わりました。同じ仕組みで、次の3つの不具合が起きます。
| 書き方 | 期待したこと | 実際の結果 |
|---|---|---|
Python [Product("消しゴム", 80, 1)] * 3 の1つ目だけ在庫99 |
[99, 1, 1] | [99, 99, 99](3つとも同じ id) |
Python 内包表記 [Product(...) for _ in range(3)] |
[99, 1, 1] | [99, 1, 1] |
JS Array(3).fill({ stock: 1 }) の1つ目だけ99 |
[99, 1, 1] | [ 99, 99, 99 ] |
Python def add_tag(tag, tags=[]) を2回呼ぶ |
['新商品'] と ['セール'] | どちらも ['新商品', 'セール'] |
[obj] * 3とfill(obj)は、1つのオブジェクトへの参照を3つ並べます。1つずつ作るには、内包表記やArray.from({length: 3}, () => ({stock: 1}))を使います。- Python の引数の既定値は、関数を定義したときに1回だけ作られ、呼び出しのたびに同じリストが使われます。既定値は
Noneにして、関数の中で[]を作ります。
「勝手に値が変わった」と思ったら id を見る
Python は id(a) == id(c)、Java は a == c、JS は a === c で、2つの変数が同じオブジェクトを指していないかを確かめる。
どこで同じ参照を渡したかを探す
代入・関数の引数・リストへの追加・既定値・* 3・fill のどれかで、同じオブジェクトが2か所から使われている。
必要な所だけコピーするか、変えられない形にする
別々に変えたいならコピーを作る。共有してよいなら、イミュータブル(変更できない)オブジェクトにして、うっかり変更を防ぐ。
Java のメソッドに渡したオブジェクトが呼び出し元でも変わる理由は、関連記事「Javaは値渡し?参照渡し?引数の変更が反映される条件」で扱っています。
ミュータブルとイミュータブル(変えられるオブジェクトとコピー)
この節では、作ったあとに中身を変えられるオブジェクト(ミュータブル)と変えられないオブジェクト(イミュータブル)の違いと、浅いコピー・深いコピーの違いが分かります。
ミュータブルは「同じオブジェクトのまま中身が変わる」、イミュータブルは「変えるたびに新しいオブジェクトができる」ものです。 Python 言語リファレンスは、数値・文字列・タプルはイミュータブル、辞書とリストはミュータブルだとしています。
str += で id が変わる: True
list += で id が変わる: False
tuple の中の list を変更: ([1, 99], 2)
TypeError: 'str' object does not support item assignment
FrozenInstanceError: cannot assign to field 'yen'
| 操作 | 結果 | 読み取れること |
|---|---|---|
文字列 s += "cil" |
id が変わった | 新しい文字列オブジェクトができ、s がそちらを指した |
リスト l += [2] |
id は同じ | 同じリストの中身が増えた |
| タプルの中のリストに append | ([1, 99], 2) |
タプル自体は変えられないが、中のリストは変えられる |
"pen"[0] = "P" |
TypeError | 文字列は一部を書き換えられない |
@dataclass(frozen=True) の属性に代入 |
FrozenInstanceError | 自作クラスもイミュータブルにできる |
JS Object.freeze した値段に代入 |
TypeError(ESM は strict mode) | freeze で変更を止められる |
| JS freeze したオブジェクトの中の配列に push | tags: [ '新', 'セール' ] |
freeze は1段目だけ。中のオブジェクトは変えられる |
JavaScript の freeze で出たエラーの原文です。
TypeError: Cannot assign to read only property 'price' of object '#<Object>'
freeze 後: { price: 100, tags: [ '新', 'セール' ] }
浅いコピーと深いコピー
中に別のオブジェクトを持つオブジェクトをコピーすると、「外側だけ新しく、中身は共有」の浅いコピーになることがあります。セット商品(中に商品の配列を持つ)をコピーし、元の中の商品の値段を999に変えました。
# Python
浅いコピーの price: 999 深いコピーの price: 1
# JavaScript
スプレッドの price: 999 structuredClone の price: 1
浅いコピー(中身は共有)
Python:copy.copy()・list(x)・x[:]
JS:{ ...obj }・Object.assign
中のオブジェクトを変えると、元にも出る
深いコピー(中身まで新しく)
Python:copy.deepcopy()
JS:structuredClone()
中まで別々。そのぶん時間とメモリを使う
JavaScript の structuredClone は、クラスのインスタンスを渡すとただのオブジェクトになり(instanceof Product が false)、メソッドを値に持つリテラルは複製できませんでした。
クラスの複製: { name: 'ノート', price: 200, stock: 5 } false
DataCloneError: total() { return this.price * this.stock; } could not be cloned.
JavaScriptのオブジェクトとJSONの違い
この節では、見た目が似ている JavaScript のオブジェクトと JSON の違いと、JSON.parse で出るエラーの原文が分かります。
JavaScript のオブジェクトはメモリの中の「もの」で、JSON はそれを書き出すための「文字列の形式」です。 JSON は JavaScript の書き方を元にしていますが、同じではありません。MDN は、プロパティ名は二重引用符の文字列であること、末尾のカンマや数値の先頭のゼロ、NaN・undefined が使えないことを違いとして挙げています。
const obj = { name: "ペン", price: 120, made: new Date(Date.UTC(2026, 9, 29)),
memo: undefined, total() { return 1; }, [sym]: 1, nan: NaN };
const json = JSON.stringify(obj);
console.log(json);
console.log(JSON.parse(json));
{"name":"ペン","price":120,"made":"2026-10-29T00:00:00.000Z","nan":null}
{ name: 'ペン', price: 120, made: '2026-10-29T00:00:00.000Z', nan: null }
| 元のオブジェクトの値 | JSON.stringify の結果 | parse で戻したとき |
|---|---|---|
メソッド total() |
消える | 無い |
undefined |
キーごと消える | 無い |
| シンボルのキー | 消える | 無い |
NaN |
null |
null |
Date |
ISO 形式の文字列 | 文字列のまま(Date に戻らない) |
BigInt 1n |
TypeError | ― |
| 自分自身を指すオブジェクト | TypeError | ― |
JSON を通すと、メソッドとクラスの情報は残りません。 戻ってくるのはデータだけのオブジェクトで、オブジェクトの「操作」の部分は送れないと考えます。
JSON.parse のエラー原文
JavaScript のオブジェクトの書き方のまま JSON.parse に渡すと、次のエラーになりました(Node.js 22.23.4)。
{name: "ペン"} -> SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
{'name': 'ペン'} -> SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
{"name": "ペン",} -> SyntaxError: Expected double-quoted property name in JSON at position 14 (line 1 column 15)
{"price": 0120} -> SyntaxError: Unexpected number in JSON at position 11 (line 1 column 12)
{"memo": undefined} -> SyntaxError: Unexpected token 'u', "{"memo": undefined}" is not valid JSON
TypeError: Do not know how to serialize a BigInt
TypeError: Converting circular structure to JSON
JSON を手で書くとき
{name: 'ペン', price: 120,} をそのまま貼るJSON.stringify(obj, null, 2) で書き出したものを使う。キーは二重引用符、末尾のカンマなし見分ける問い:「それはメモリの中のものか、保存・送信する文字列か」
オブジェクトのメモリ上の大きさ(実測)
この節では、オブジェクト1個がメモリをどれくらい使うのかと、Python の sys.getsizeof が示す数字の意味が、実測で分かります。
同じ「名前・値段・在庫」の3つを持つ商品オブジェクト1個は、手元の環境で数十バイトでした。 10万個〜100万個作り、作る前後のメモリの差を個数で割った目安です。
Python の sys.getsizeof で、いろいろなオブジェクトの大きさを出しました。
int 1 28 bytes
int 10**30 40 bytes
str '' 41 bytes
str 'pen' 44 bytes
str 'ペン' 62 bytes
list [] 56 bytes
list [1,2,3] 88 bytes
dict {} 64 bytes
tuple () 40 bytes
Product 48 bytes
__slots__ 版 56 bytes
dataclass 版 48 bytes
- 数値の
1でも28バイトあります。 Python の値はすべてオブジェクトなので、型の情報や参照の数を一緒に持っています。 sys.getsizeofは、そのオブジェクトが指す先(属性の値など)を数えません。 Product は48バイトと出ましたが、tracemalloc で10万個作った実際の増え方は1個あたり96バイトでした。__slots__版は getsizeof が56バイト、実際も56バイトでした。- 手元の Python 3.13 では、作った直後のオブジェクトは属性の値を本体の近くに並べて持ち、
p.__dict__を読んだときに辞書が作られる動きでした。読んだあとは、辞書だけで296バイトと出ました。 - Java の約28バイトは、オブジェクトの頭の部分(ヘッダー)と3つのフィールド、配列に入れた参照を合わせた目安です。JVM の設定で変わります。
- Node.js では、クラスで作っても
{ }で作っても約56バイトで同じでした。空の{}は約64バイトと、かえって大きく出ました(後からプロパティが足されるのに備えた余白のため)。
何百万個も作るときは、__slots__ やタプル・配列で持つと減らせます。数千個までなら、読みやすさを優先して普通のクラスで書くのが向いています。
オブジェクト指向・メソッド・抽象化との関係
この節では、「オブジェクト」と「オブジェクト指向」の違いと、次に何を学ぶとよいかが分かります。
オブジェクトは「データと操作のまとまり」という部品そのもので、オブジェクト指向はその部品を組み合わせてプログラムを作る考え方です。 カプセル化・継承・ポリモーフィズム(多態性)は、オブジェクト指向の側の言葉です。Python・Java・JavaScript・C#・Ruby・PHP・C++ など、多くの言語がオブジェクトを扱えます。
- 1オブジェクト(この記事)属性とメソッド・クラスとインスタンス・同一性と等価・参照
- 2メソッド関数との違い・インスタンスメソッド・クラスメソッド・static
- 3オブジェクト指向カプセル化・継承・ポリモーフィズム・コンポジション
- 4抽象化インターフェース・抽象クラスで共通部分を取り出す
- 5設計の原則とパターンSOLID・デザインパターン
オブジェクトを組み合わせて作る考え方(カプセル化・継承・ポリモーフィズム)は、関連記事「オブジェクト指向とは?コードでわかる基本と使いどころ」で扱っています。メソッドの種類は同時期に公開予定の記事「メソッドとは?関数との違いとクラスメソッド・staticを実行例で」で、インターフェースや抽象クラスへの進み方は「抽象化とは?意味とプログラミングの例|3言語で段階実装し差分を比較」で扱います。
オブジェクトのよくあるエラーとつまずき(原文つき)
この節では、オブジェクトを使い始めたときによく出るエラーの原文と、その意味・直し方が分かります。
# Python 3.13.18
pen.color -> AttributeError: 'Product' object has no attribute 'color'
Product('鉛筆') -> TypeError: Product.__init__() missing 1 required positional argument: 'price'
None.name -> AttributeError: 'NoneType' object has no attribute 'name'
Product.total() -> TypeError: Product.total() missing 1 required positional argument: 'self'
# Java 21(javac と実行時)
Err.java:4: error: constructor Product in class Product cannot be applied to given types;
Err.java:5: error: cannot find symbol
symbol: variable color
NullPointerException: Cannot read field "name" because "none" is null
# Node.js 22.23.4
TypeError: Cannot read properties of null (reading 'name')
TypeError: Cannot read properties of undefined (reading 'price')
TypeError: Class constructor Product cannot be invoked without 'new'
| エラー(要点) | 意味 | 直し方 |
|---|---|---|
object has no attribute 'color' |
そのオブジェクトに無い属性を読んだ | 綴りを確かめる。vars(obj) で持っている属性を見る |
'NoneType' object has no attribute |
オブジェクトのつもりの変数が None だった | None を返した関数を探す(戻り値の無い関数など) |
missing 1 required positional argument: 'price' |
作るときに必要な値が足りない | __init__ の引数に合わせて渡す |
missing ... argument: 'self' |
インスタンスを作らずにクラスからメソッドを呼んだ | Product(...) で作ってから pen.total() |
Java cannot find symbol |
クラスに無いフィールド・メソッドを使った | クラスの定義を確かめる。Java は後から足せない |
Java Cannot read field "name" because "none" is null |
参照が null のままフィールドを読んだ | new し忘れ・null を返す処理を探す |
JS Cannot read properties of undefined (reading 'price') |
メソッドを取り出して呼び、this が外れた | note.total() の形で呼ぶか、bind・アロー関数を使う |
JS cannot be invoked without 'new' |
クラスを関数として呼んだ | new Product(...) と書く |
- Java の NullPointerException は、
javac -g(デバッグ情報つき)でコンパイルすると変数名"none"が出ました。付けないと"<local20>"のような番号の表示になりました。 - JavaScript で無いプロパティを読んでもエラーにならず、
undefinedが返りました(note.color→ undefined)。エラーになるのは、その undefined からさらにプロパティを読んだときです。
- 1値の比較に is・== を取り違えていないPython は値なら ==、Java は equals、JS は自分で比べる関数
- 2同じオブジェクトを2か所で使っていない[obj] * 3・fill・既定値の []・代入をコピーのつもりで使う
- 3等価を定義したらハッシュも揃えたPython は __hash__、Java は hashCode。record・dataclass なら自動
- 4None・null・undefined のままでない作り忘れ・戻り値の無い関数・見つからなかった検索結果
- 5JSON にメソッドやクラスが残ると思っていないJSON はデータだけ。戻したら必要に応じて new し直す
どの形でオブジェクトを作るか(判断表)
この節では、辞書・クラス・dataclass・record・オブジェクトリテラルのどれを選ぶかが、場面ごとに分かります。
「操作(メソッド)を持たせたいか」「中身を変えたいか」「たくさん作るか」の3つで決まります。
| 場面 | Python | Java | JavaScript |
|---|---|---|---|
| その場で一時的にまとめたいだけ | 辞書 {"name": ...} |
record | オブジェクトリテラル |
| 値を入れて比べたい・変えたくない | @dataclass(frozen=True) |
record | Object.freeze |
| 状態を持ち、メソッドで変える | 普通のクラス | 普通のクラス | class |
| 同じ形を何百万個も作る | __slots__・タプル |
普通のクラス(小さい) | class か配列 |
| 保存・送信する | 辞書にして json | Jackson などで JSON | JSON.stringify |
| キーが実行中に増える | 辞書 | Map | Map |
1まずはクラスで書く
名前の付いた属性とメソッドがあると、読む人に「これは商品だ」と伝わる。辞書のキーの打ち間違いもエラーで気づける。
2値ならイミュータブル
金額・日付・座標のような「値」は変えられない形にすると、参照の共有の不具合が起きない。
3境界ではデータに戻す
ファイル・API・DB とのやりとりは JSON などのデータで行い、中に入ってからオブジェクトに戻す。
よくある質問
Q. オブジェクトとは、簡単に言うと何ですか? A. プログラムの中で、データ(属性)とそのデータを扱う操作(メソッド)を1つにまとめた「もの」です。商品なら、名前・値段・在庫がデータ、「合計を出す」「売る」が操作です。
Q. オブジェクトとインスタンスは同じですか?
A. ほぼ同じものを指します。「Product クラスのインスタンス」のように、どのクラスから作ったかを言うときにインスタンスと呼びます。Python や JavaScript では、クラスを使わずに作った値(数値や {})もオブジェクトと呼びます。
Q. オブジェクトとクラスの違いは何ですか? A. クラスは設計図、オブジェクトは設計図から作った実体です。1つのクラスから、中身の違うオブジェクトをいくつでも作れます。
Q. オブジェクトとオブジェクト指向の違いは何ですか? A. オブジェクトは部品そのもの、オブジェクト指向はその部品を組み合わせてプログラムを作る考え方です。カプセル化・継承・ポリモーフィズムはオブジェクト指向の言葉です。
Q. 中身が同じなのに == が False になるのはなぜですか?
A. 何も定義していないクラスでは、== が「同じ1つのオブジェクトか」で比べるためです。Python なら __eq__、Java なら equals と hashCode を書くか、dataclass・record を使います。
Q. Python の is と == はどう使い分けますか?
A. 値が等しいかは ==、同じオブジェクトかは is です。is は x is None のような比較に使い、数値や文字列には使いません。手元では int("257") 同士の is が False、同じファイルに書いた 257 同士は警告付きで True と、書き方で結果が変わりました。
Q. 代入したらコピーされますか?
A. されません。代入は同じオブジェクトに別の名前を付けるだけです。別々に変えたいときは、Python は copy.copy・copy.deepcopy、JavaScript はスプレッド構文・structuredClone でコピーします。
Q. JavaScript のオブジェクトと JSON は同じものですか? A. 違います。オブジェクトはメモリの中の値、JSON は文字列の形式です。JSON ではキーは二重引用符が必要で、関数・undefined・シンボルのキーは書き出されません。
Q. JavaScript でオブジェクト同士の中身を比べる方法はありますか?
A. 標準の演算子はありません。比べたいプロパティを並べて比べる関数を書くか、テストでは Node.js の assert.deepStrictEqual などを使います。JSON.stringify の文字列を比べる方法は、キーの順番が違うと一致しないので注意します。
Q. Java の Product@d716361 のような表示は何ですか? A. toString を書いていないクラスの既定の表示で、「クラス名+@+ハッシュコードの16進数」です。メモリの番地ではなく、実行のたびに変わることがあります。toString を上書きするか record を使うと、中身が表示されます。
Q. オブジェクトはどれくらいメモリを使いますか?
A. 手元の環境で、属性3つの商品オブジェクト1個は Python の普通のクラスで約96バイト、__slots__ 版で56バイト、Java 21 で約28バイト、Node.js 22 で約56バイト(どちらも配列の参照込み)でした。言語・版・設定で変わる目安です。
Q. 画面の図形や S3 の「オブジェクト」もこれと同じ意味ですか? A. 「選んで扱える1つの単位」という点は似ていますが、別の使い方です。図形描画ソフトの図形、オブジェクトストレージのファイル、コンパイルでできるオブジェクトファイルは、プログラミングのクラスから作るオブジェクトとは別のものです。
まとめ
- オブジェクトは、データ(属性)と操作(メソッド)を1つにまとめたもの。クラスは設計図、そこから作った実体がインスタンスで、ふだん「オブジェクト」と呼ぶのはインスタンス。
- Python・Java・JavaScript で同じ商品オブジェクトを作ると、作り方と self・this の書き方は違うが、属性とメソッドを持つ点は同じだった。Python では数値・関数・クラスもオブジェクト。
- 比較には「同じ1つのものか(is・Java の ==・===)」と「中身が等しいか(== と __eq__・equals)」の2種類がある。何も書かなければ、別々に作った同じ中身は等しくないと判定された。等価を定義したらハッシュも揃える。
- 代入はコピーではなく参照の共有。
[obj] * 3・fill(obj)・既定値の[]は、3言語の実行で同じ不具合を起こした。別々に変えるならコピー、共有するならイミュータブルにする。 - JSON はオブジェクトを書き出す文字列の形式で、メソッドやクラスは残らない。属性3つのオブジェクト1個は、手元で数十バイト(Python 96・__slots__ 56・Java 約28・Node.js 約56)だった。
次に読むとよい記事です。オブジェクトを組み合わせる考え方は関連記事「オブジェクト指向とは?コードでわかる基本と使いどころ」で、Java のクラスの書き方は関連記事「Java クラスとインスタンスとは|newでの作り方とフィールド・メソッド」で扱っています。
参考・出典
確認日はいずれも 2026年10月29日です。
- Python 言語リファレンス「3. データモデル」3.1 オブジェクト、値、および型:https://docs.python.org/ja/3/reference/datamodel.html
- Python 標準ライブラリ「copy --- 浅いコピーおよび深いコピー操作」:https://docs.python.org/ja/3/library/copy.html
- Python 標準ライブラリ「dataclasses --- データクラス」:https://docs.python.org/ja/3/library/dataclasses.html
- Oracle「Java SE 21 API Class Object」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Object.html
- MDN「Object (オブジェクト)」用語集:https://developer.mozilla.org/ja/docs/Glossary/Object
- MDN「JSON」(JavaScript と JSON の違い):https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON
- MDN「structuredClone()」:https://developer.mozilla.org/ja/docs/Web/API/Window/structuredClone
- MDN「Object.freeze()」:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「オブジェクトとは?クラスとの違いと参照の落とし穴を3言語の実行結果で」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。