This is uttely false. I desume you haven't used C++ in the last 10 years, or at least you didn't delve deep enough into it to really understand how powerful (while bonkers) the C++ type system is.

C++ has a much, MUCH more stronger type system with true generics, value types, const-correctness, compile time reflection and dispatching, ...

In Java, everything is a reference, except when it's not (which is a design mistake that .NET fixed, IMHO). Some stuff in Java is plain "magic", like type erasure and boxing, while C++ might well be drowning in its own sea of utter madness but at least tries to be somewhat consistent (for instance, there are no "magic" types, when you do `int { 3U }` you are "constructing" an integer, when you do `bool x { 33 }` you applying the implicitly defined `bool(int)` constructor from bool. You can define your own conversions, and you can define your own custom types that behave and can be used like built-in ones (see smart pointers, iterators, ...).

Java _seems_ stronger typed because it generally doesn't allow integer promotions and implicit conversions, but these are concepts that are orthogonal to the type system, `bool` and `char` are different, distinct types and if you specialize a template for T<bool>, it won't apply to `char` unless a conversion happens, and if it does so, it is still operating on `bool`, not char - it is constructing a type from another, the fact this happens is simply hidden from you, like Java and boxing (which ironically is an implicit conversion).

The Java delegates pretty much everything to the JVM, and that's reflected in the language design. Java is a simple language that does not do a lot at compile time, relying on runtime facilities to mitigate these shortcomings. See for instance how everything can always decay to a reference to Object, implicitly, everywhere, requiring casts (i.e. runtime assertions) to restore type safety - that's basically a safer `void*`.

1995's Java was clearly too limited, I understand they wanted a fresh start from the ugliness of '90s C++, but they straight removed too much for the sake of simplicity. The current crop of languages, which largely rejected the Java model is kind of a symbol of what went wrong with Java, IMHO.

The fact certain features had been "hacked" on top of the language using what was already there (see generics, boxing, ...), often introducing features that act like "magic", and can't be overridden by the user is bad, and shows how limited the original language was. The same way you can't override operators, you can't define custom boxing rules for your types (mostly because you won't be able to define custom value types until Valhalla is released).

Modern C++ allows you to write safe and solid code using compile-time features and the type system. While stuff like <type_traits>, SFINAE, template metaprogramming and such are definitely not "nice", they are extremely powerful and if used correctly eliminate completely certain issues from ever happening. If you only use smart pointers, references, containers, moves and by-value semantics you won't get crashes from nulls, ever. You won't have memory leaks, and after lots of fighting with the compiler, there's a high chance your code will work straight away (unless you messed up the logic). This is not that far from Rust, as far as my experience goes - I still hope for Rust to mostly replace C++ in the end, but for now C++20 is a very solid substitute and a good choice (and feels much more modern and powerful than Java could or has ever been).