JavaとC++の比較
言語の特徴 > 意味論

C++はCとの互換性を保つため組込型の暗黙な型変換をある程度許可しているが、大抵のコンパイラでは警告を出す。また、複合型の暗黙型変換も定義できる。一方、Javaではプリミティブ型の暗黙的な変換として数値型の精度が大きくなる型変換（拡張変換）のみを許可する。数値型の精度が低下する暗黙的な型変換（縮小変換）は許可されず、明示的なキャストを要求する。
この影響はJavaとC++双方にある条件式（if、whileとforの脱出条件）にも現れる。C++では条件式の型は厳密にboolである必要はなく、文脈的に変換可能であればよい。Cとの互換性から、C++のboolは整数型の一種であり、他の整数型や浮動小数点数型、ポインタなどから暗黙変換することができ、ゼロあるいはゼロに相当する値の場合はfalseに、それ以外はtrueに変換され、またこの条件式の文脈における暗黙変換はコンパイラ警告の対象にはならない。一方、Javaでは条件式の型はbooleanである必要があるが、booleanは整数型ではなく、数値型からの暗黙的な型変換ができない。単純なキャスト構文による型変換もできず、booleanに変換するためには何らかの比較演算子による明示的な比較が必要となる。そのため、数値型の変数aに対してif (a = 5)のようなコードは、Javaではコンパイルエラーになる。これは、if (a == 5)のミスタイプに対する対策となるが、C/C++で変数xが非ゼロまたはnon-nullであることをチェックするif (x)のようなコードをJavaへ移植する際には、0またはnullとの明示的な比較が必要になる。
関数の引数を渡すとき、C++は参照渡しと値渡し両方をサポートする。Javaではすべての引数は値渡しであるが、オブジェクト（非プリミティブ変数）の引数は参照になり、これは間接参照が言語に備わっていることを意味する。
Javaのプリミティブ型は大きさと値の範囲が指定されている。一方、C++の組み込み型は最小限度が定められているものの、正確な大きさは定められておらず、環境によって異なる。また同じコンパイラでもバージョンが違えば型の大きさが異なることもあるし、コンパイラの設定で変更可能な場合もある。C++で値の範囲が指定されていないとは、仮に同じ16ビットの符号付き整数だとしても、ある環境では2の補数で[-32768, 32767]、別の環境では1の補数で[-32767, 32767]の範囲になるという例があるということである。C++11ではサイズと内部表現が規定された整数型（std::int32_t など）がオプションとして追加された。C++20では符号付き整数が2の補数であることが規定された。
Javaの文字型charは、内部表現がUTF-16であることが規定されている。文字列を表現するクラスとして、java.lang.Stringが用意されている。一方、C++の文字型にはcharとwchar_t（ワイド文字）の2種類が存在し、それぞれの文字列クラスとしてstd::stringおよびstd::wstringが定義されているが、文字型の幅（ビット数）や内部表現はプラットフォームに依存する。規定されているのはcharのサイズが1バイト (sizeof(char) == 1) ということのみである。さらに、1バイトのビット数が規定されておらず、8ビット以上であればよい、という制約しかない。例えば、charがシングルバイト文字であったとしても、ASCIIであるとはかぎらないし、マルチバイト文字であったとしても、日本語環境でShift_JISあるいはEUC-JPが使われたり、中国語環境でGB 2312あるいはBig5が使われたり、もしくはUTF-8であったりしてもかまわない。また、wchar_tの内部表現は、UTF-16もしくはUTF-32であってもかまわないし、あるいはUnicodeと無関係の内部表現であってもかまわない。C++11ではUTF-16/UTF-32対応の文字型char16_t/char32_tと文字列クラスstd::u16string/std::u32stringが追加された。
Javaの文字列リテラルはString型のオブジェクトである。C++の文字列リテラルはヌル終端文字列をぴったり格納する固定長配列型である。
C++の浮動小数点数の丸め誤差と精度と演算はプラットフォームに依存する。Javaは異なるプラットフォームでの一貫した結果を保証する高精度浮動小数点モデルを提供しているが、通常は最適な浮動小数点演算性能を得るためにより大雑把な演算モードが使われる。
C++ではポインタを使ってメモリアドレスを直接操作できる。Javaはメモリアドレスを直接操作できるポインタを持っていない（オブジェクトの参照と配列参照だけはポインタを持っているが、どちらもメモリアドレスの直接アクセスを許可しない）。C++ではポインタへのポインタを定義できるが、Javaではオブジェクトアクセスにだけ参照を用いる。
どちらの言語も配列は固定の長さをもつ。C++の配列はメモリ空間上のオブジェクトの連続であり、配列自身をオブジェクトのように扱うことはできない（第一級オブジェクトではない）。通例、C言語のように配列先頭要素へのポインタを用いて配列の受け渡しを行うが、C++ではサイズの指定された固定長配列への参照を定義することもできる。配列範囲外アクセスはチェックされない。なお、構造体あるいはクラスでラップするか、C++11で追加された標準ライブラリに含まれるstd::arrayを用いることで、固定長配列を第一級オブジェクトとして扱うことができる。Javaの配列は第一級オブジェクトであるが、配列の一部分のみを参照するサブ配列は定義できず、インデックス範囲を別途指定する必要がある。また、配列範囲外アクセスのチェックが強制される。
C++では関数へのポインタ（あるいは関数への参照）によって、関数あるいはメンバ関数を間接参照することができる。また、ポインタによって関数オブジェクトを指す方法もある。Java 7以前では代替方法としてインタフェースを利用するしかなかったが、Java 8ではメソッド参照の機能が追加された。
C++ではプログラマが演算子を多重定義できる（利用者定義演算子）。Javaは演算子多重定義をサポートしない。文字列型java.lang.Stringに対してのみ、文字列連結が可能な演算子+と+=が予め用意されているだけである。
Javaはリフレクションや任意に新しいコードを動的ロードする機能をサポートする標準APIを持つ。
Javaは1.5以降でジェネリクスをサポートする。ただし型引数に使えるのは参照型のみであり、intのようなプリミティブ型は使用できないため、java.lang.Integerのようなプリミティブラッパークラスでボックス化する必要がある。C++はテンプレートによりジェネリックプログラミングをサポートする。名前解決が失敗しない限り、型引数にはいかなる型でも使えるため、静的ダックタイピングに応用できる強力な柔軟性を持つが、適合しなかった場合のコンパイルエラーメッセージが膨大・難解になる欠点がある。この点に関してはC++20の「コンセプト」機能によってある程度の解消が図られている。
JavaとC++はいずれも、ネイティブ型（これらも"基本型"または"組込型"として知られる）とユーザー定義型（これも"複合型"として知られる）を区別する。Javaでは、ネイティブ型は値としての意味しかなく、複合型は参照としての意味だけを持つ。C++では、すべての型が値としての意味を持つが、任意の型の参照を作ることが可能である。
C++は任意のクラスの多重継承をサポートする。また、状態の多重継承にまつわる問題を回避するための機構として、仮想継承をサポートする。Javaは型の多重継承をインタフェースによりサポートするが、実装は単一継承のみをサポートする。Javaでは、サブクラスはただ1つのスーパークラスからのみ派生することができるが、クラスは複数のインタフェースを実装することができる。なお、Java 8ではインタフェースのデフォルトメソッドにより、実装の多重継承をサポートするようになったが、状態の多重継承は依然としてサポートしない。
Javaはインタフェースとクラスを明確に区別している。C++では、純粋仮想関数（抽象メソッドにあたる）を並べたクラスでインタフェースを表現し、複数のインタフェースの実装は多重継承によって模倣される。
Javaはマルチスレッドをサポートした標準ライブラリと言語機能を持つ。Javaのキーワードsynchronizedはマルチスレッドアプリケーションをサポートするシンプルでセキュアな相互排他ロック（mutex）を提供するが、synchronized セクションはLIFOオーダーで残されなければならない。Java 1.5では並行プログラミングのためのライブラリが強化され、java.util.concurrent.Semaphoreクラスをはじめとする、柔軟なMutexロックメカニズムを提供するための明示的な同期オブジェクトなどもサポートするようになった。一方、C++では長らくスレッドが標準化されていなかったため、プラットフォームごとのスレッド機能を使うか、Boost C++ライブラリなどを利用する必要があったが、C++11でスレッドが標準化された。