Java 18 / JDK 18: General Availability
mail.openjdk.java.net
mail.openjdk.java.net
It sounds like a good idea, but I can imagine lots of downstream breakage, some of it not immediately obvious, with apps that make bad assumptions.
Edit: The risks section of the linked doc above does explain some of that, and there is some notable risk.
You're not wrong, which is probably why they did it in this release.
JDK 17 was an LTS release, and 18 isn't. The next LTS will be JDK 21 in September 2023:
* https://www.oracle.com/java/technologies/java-se-support-roa...
This presumably gives folks times to adjust before jumping between LTSes in production.
It's an Oracle term, and I expect the majority of OpenJDK committers and decision-makers are still from Oracle.
It's a thriving ecosystem (which is great!) but yes, Oracle does contribute a significant portion [3].
[1] https://mreinhold.org/blog/forward-faster [2] https://mail.openjdk.java.net/pipermail/discuss/2017-Septemb... [3] https://inside.java/2022/03/22/the-arrival-of-java18/
E.g. look on the things introduced in JDK 16 and JDK 17.
LTS is a good gimmick for decision makers in companies, but developers also don't care about it (it is not LTS as in e.g. Ubuntu).
It doesn’t mean enabling Java 18 on Production on day 1, but certainly it means adding a Java 18 compiler to your CI, running tests using Java 18, etc.
[1] https://www.reddit.com/r/java/comments/o0m6g8/the_state_of_p...
Even if you don't use any of the new features, there may be deprecation warnings and such that one should be aware of sooner rather than later. Tweaking early to ensure compatibility with current-LTS and future-LTS versions is worth some cycles: if you don't, you'll pay for it eventually when old(er)-LTS is inevitably retired.
I get that not everyone has spare time and energy, but it doesn't take that long to grab a beta, put it on a device (or load up the simulator) and find out. Leaving it until the last minute just forces you into a panic, and seems irresponsible when you had months to just test your product.
[1]: https://tschatzl.github.io/2022/03/14/jdk18-g1-parallel-gc-c...
This changes the default charset of the Java APIs to UTF-8.
I read that the Java 8's JVM's internal string representation is UTF-16 [0][1]. Is that still the case after JEP400?
[0] https://docs.oracle.com/javase/8/docs/technotes/guides/intl/...
I generally like the idea to use UTF-8 strings, but if they didn't want to break string indexing, the indexing would take O(str.length)...
JEP400 is about I/O, previous to this change when you create something like a FileWriter without specifying the charset the platform default would be used. For a long time this has been recognized as a common foot gun, hence this change to a default that is more likely to be what the developer actually wants.
> ...JVM's internal string representation is UTF-16
Hasn't been try for a while. They switched to using a byte array internally for storage, plus an encoding. Currently that's either UTF-16 or Latin 1, unless compact strings are disabled in which case it's all UTF-16.
Latin 1 has the special property that each of its fixed-width code units maps onto a single UTF-16 code unit. It is for that reason alone that CharSequence implementors can use it as an alternative to UTF-16. Imagine trying to implement `char charAt(int index)` if you're backed by a UTF-8 byte array (or UTF-32, for that matter)!
From a programmer's perspective, Java is pretty much as UTF-16 as ever.
UTF-32 (with no BMP concept) doubles the memory usage of most international text and quadruples the memory usage of ASCII text (which is the most common), yet characters outside the BMP are barely used outside of emoji.
Native UTF-8 in memory makes character indexing a non-constant time operation, which would bite people badly in cases where they've written a loop over the indexes. This is of course the point at which you say, ah but what is a character exactly. If you go down this route you end up with Swift and Emoji Flag Calculus classes. The string APIs become incredibly convoluted or inefficient for the common cases. It hardly seems worth any kind of backwards compatibility break for this.
So Java does the pragmatic thing: String can switch between 8 or 16 bits per "character" and this is basically always good enough. If you care about woring with emoji or Egyptian hieroglyphs in memory, then you either have to deal with combining characters or just bite the bullet and decode to UTF-32.
The only reason that Java's UTF-16 has constant time indexing is because they use a braindead definition of character which is "UTF-16 codepoint".
If you want constant time character indexing you need to go UTF-32. But obviously the downsides are too great for most users. So in practice everyone uses UTF-8 because it is usually the most memory efficient.
Plus it turns out that character indexing isn't actually that common of an operation, so it is really the right move for almost every application.
edit: per comments below, Oracle's Java is open-source too these days.
[1] https://www.oracle.com/java/technologies/javase/jdk-faqs.htm...
Oracle also has their own build of the JDK which is not GPL. That is free to use in commercial software as well, under their "No-Fee Terms and Conditions"
For reference I double checked here: https://www.oracle.com/java/technologies/downloads/
https://blog.netwrix.com/2021/12/02/oracle-java-license-chan...
https://itassetalliance.com/blog-posts/what-do-oracles-2021-... says there was a change in 2019 that made critical security updates a paid feature.
The 2021 license change reverted this somewhat, by giving you one year of security fixes for free in the long term support versions (I’m sure I’m missing some detail, if not completely misinterpreting things, here)
Side note: Avoid EBS at all costs if you can, unless you like spending money on consultants and like crappy obfuscated processes.
* Oracle products in general (in my experience)
My first boss wrote the TCP stack for a long defunct mainframe, which was distributed via a “shared source” program that his employer paid for and eventually was licensed to his employer at significant expense.
Most of the big vendors are running the TCK tests to certify compatibility as well.
https://en.wikipedia.org/wiki/Google_LLC_v._Oracle_America,_....
OpenJDK is reference one build from the source which is then taken by vendors and tweaked and built.
https://www.azul.com/downloads/?package=jdk#download-openjdk
I think it'll be very atypical for a companies who doesn't use any of the Oracle product to use proprietary Oracle Java and pay for the support
its fully certified and production ready. And most importantly - official Docker image https://hub.docker.com/_/amazoncorretto
Even oracle JDK does not come with a supported, official image
I'm speaking as someone who spent the last 6 years focused on frontend web technology.
IMO
if (str1 == str2) {
// oops
}This is consistent with the rest of the language, but I'd bet one rarely wants referential equality when comparing strings.
People commenting "yeah but that's because you are a newbie / you are comparing references and you should know better" miss the point I think. I can easily see myself making this mistake while fully understanding what's going on.
Basically the tooling around Java is very evolved and Java static type system helps the IDEs a lot. One of the main reason enterprises prefer Java. Also majority of the tools and IDEs is free for commercial use.
We are expected to learn about our tools.
I hope making mistakes is ok though?
I learnt C years before Java, it's also the first language I properly learnt. And a little bit of C++ too, so I've known the concepts of pointers and references way before learning Java. In C, you also either do pointer equality or explicit value equality (with strcmp) for string equality tests. I never had any issue understanding how things work in Java. It's not about learning anything. It's about doing mistakes. And I won't take "you should know better" as a counter argument to a mention of some rough edge in an API or a programming language. It's like Javascript: you might know the differences between == and === in Javascript full well, but still happen to write == by mistake because you just wrote some C/Java/Python code just before. I'll probably not forget about .equals(...) in Java most of the time but the fact that == would be silently accepted and not do what is wanted is still a bit concerning (and .equals(...) is ugly, too).
A linter will do indeed and that's a fair point. I'd argue that linters are there to work around rough edges of programming languages, but that's fair enough too, every programming language has its gotchas and I'll accept considering a programming language + its linter(s) instead of a programming language alone. However, that's still not ideal because you might have to work on non linted code and deal with these gotchas without any safety net.
I hadn't thought about number comparisons (mentioned in other comments) but that's even worse indeed.
It's no wonder Kotlin and Groovy both chose value comparison for ==.
There's a reason newer JVM languages like Kotlin use == as an alias for equals instead of reference equality.
Sometimes, it happens in core Java libraries themselves: https://bugs.openjdk.java.net/browse/JDK-8274779
https://github.com/openjdk/jdk/blob/722d639fad2e4fc6eb2aabd4...
Curiously, I don't do a lot of value-comparisons. Based on grepping in the repo for my search engine, I appear to do one every 1000 lines of code. May just be how I write code though. Some people seem to do that a lot more.
edit:
Kotlin uses `===`, which is another good option.
Look, languages are moving away from permitting constructs like "if (x = 1)" because it's difficult to distinguish from "if (x == 1)". Adding "if (x === 1)" into the mix is arguably a step backwards in terms of clarity.
The good news is that a linter trivially detects this bug.
I mean by that same logic, Javascript will confuse you as well because `{} != {}`, or C++ because of pointer mechanisms and custom operators.
The tricky one is numbers.
Integer == int // fine
int == int // fine
numVarOne == numVarTwo // Uh-oh
In that third example you better make sure they aren’t both boxed objects or BigDecimals.
I’ve almost never seen the string one for some reason. It’s always boxed numbers.
Again, a good IDE is worth a ton.
Boolean.getBoolean, however, now that is a proper Java land mine.
Though I’m not sure why wasn’t two new i’s added instead of modifying the latin i? It is a different letter after all in this case.
From a business standpoint, there are lots and lots of Java programmers out there. Of course that doesn't reflect anything about the average skill of those programmers..
If you're like me and you sort of get sad when your program uses more than 64mb when idle because it feels wasteful, then yeah that overhead is pretty gnarly.
"But we have servers with hundreds of gigs of ram!" you might respond - and sure, we do, but having resources doesn't mean you have to use them. It is better to write small fast efficient programs than extravagant enterprise stuff.
Sure, there are use cases where it is not a reasonable tradeoff, but for CRUD business apps, it is perfectly fine. Especially given that not all cores can be used uniformly.
Some complain about verbosity. It's really a non-issue particularly in a modern IDE and not worth making a language decision over.
Not everything has to be new and sexy.
I understand though, that in more recent versions of Java this is taken care of for you.
myService.getSomething().var
and press tab, it will autocomplete it to
Something something = myService.getSomething()
I wish.
As opposed to what developers that aren't cheap? Node.js? PHP? C#? Python? I have no idea what developer you're talking about here. Do you think JEE developers are cheap?
No language is perfect of course, the point isn't to start a war. I don't personally use Java for my own projects anymore but I can see why it's very attractive.
1. Catch, log and rethrow, or
2. Wrap as a runtime exception (so you don’t have to change every method signature up to main())
Sure, there’s option 3: catch and handle… but this is used 1/50 times, and the ergonomics of (2) overwhelm the utility of this.
BTW, vavr is nice. Just, “run it all” and optimize for the golden path. If there’s an error account for it locally.
Compared to "SEGFAULT at 0x000000 in some xxx.dll" having to handle exception and having readable stack traces is a productivity win.
That's true, but that doesn't mean we should pretend the problem does not exist. Using unchecked exceptions is sweeping things under the carpet. Generally unchecked exceptions should be used only for bugs (array index out of bounds) or unrecoverable situations (JVM crash, no memory). I/O does not belong there.
One of biggest troubles with checked exceptions is that in Java it is impossible to generify them the same way you can do with argument and return types. You can't write a generic filter or map function that throws the same checked exceptions as the lambda (or any other interface object) that gets passed as an argument. This makes it impossible to use code that throws checked exceptions in some contexts and forces developers to wrap them in RuntimeException.
And because those situations are extremely common these days, especially after some FP techniques influenced how Java code is being written, almost noone uses checked exceptions and you typically get runtime exceptions flying around the whole codebase. Which are even worse - they turn very quickly into an unmaintainable mess - because now any function can throw just anything at any time, and that's not even explicitly visible.
And here we get to the next big problem with exceptions (not just checked exceptions): they add a high number of alternative control flows you need to analyze in addition to the main "happy path" of the program. An exception can happen anywhere and it might leave the data in incorrect, partially updated state. In reality many devs simply pretend the problem doesn't exist, and they check the happy path only. In languages with no exceptions, a function can exit only by an explicit return - and that is way more readable and easier to analyze.
And the last thing which seems to be more a cultural problem, rather than a Java problem, is that somehow most Java programs communicate typical, expected problems with ugly and mostly useless exception stacktraces. Can't connect to a host? I get a stacktrace. File not found? Two screens of stacktraces... That's IMHO a terrible approach to error handling. As a user I am totally uninterested in what code was being executed when something failed (that should be reported only for bugs so the developers can fix). I'm interested in getting a human-readable message with context helping me understand what went wrong and how to fix it.
map it to another checked exception and you now have a "blue" function. and, you also have another layer of abstraction in your errors.
if you can't actually handle the error, skip the circus and just let the exception propagate to a general error boundary (alongside all those RuntimeExceptions you're unaware of).
It's a deep ocean, your defense mechanism of guarding against Checked Exceptions is a small fraction of all exceptions.
To be fair, I did really like the idea of checked functions when I first started using Java. But in practice, it's just too hard to tell what operations should be Checked / Unchecked (sometimes).
This is not as useless as it sounds. You can analyse the logs and find out why these exceptions are occurring. A lot of times these lead you to subtle bugs which you might not have noticed otherwise.
``` Try.success(foo. .map(...) .mapTry(...) .map(...) .mapTry(...) .onFailure(e -> LOGGER.warn("Failed %s", e) .get() ```
Of course you catch and handle errors. Or at the very least you must think about whether it makes sense to handle an error on the current abstraction level that you are on, which means you must always consider option 3. Only if you decide that it's not possible or doesn't make sense to handle it ("I am too lazy" is not a reason to not handle it!) you decide whether you need to throw it one level up the stack. And if doing that is the sensible thing, you think about whether you want to make the exception part of the API of whatever you currently write or not. If you want to, you throw the exception without doing anything (most of the time you don't even log it, because you understand that the receiver at the end, the one who finally handles it, is responsible of logging it, otherwise you get multiple logs of the same exception written out just because someone sprinkled logging into several abstraction layers). If you want to hide the exception type somehow, you wrap it in a more fitting exception that's explicitly or implicitly part of the API of whatever you're writing, and throw that one. That can, but doesn't have to be, a runtime exception.
I really don't get what's so hard about exception handling. Java's exceptions are a pretty good analog to how one must model proper error handling in multi-layered software applications anyway, regardless of the programming model used to implement it.
looks like the "true scotsman problem", though. i'm using state of the art packages like jgrapht which throw runtime exceptions, and i'm manually catching those runtime exceptions, pattern matching on the exception messages, and trying to handle.
when the community doesn't use the API, it's a tragedy of the commons problem. there's a lot of grey area between what constitutes a runtime exception vs. a checked exception. it's all very subjective!
Returning an Either<Result, Error> is pretty much the same thing conceptually. Either you propagate the error or you handle it.
I have no problem with Either<Error, Result> though.
Using the JVM doesn't mean you're limited to Java anymore, and hasn't for a long time. I have built software for the JVM for the past 6+ years nearly exclusively in Clojure with only a few moments where I needed to dip down into Java proper.
It also has an ecosystem of tooling to monitor and profile built both into the JDKs and by teams across Silicon Valley (eg first class bazel support).
I’m in one of these companies now, and while I’m certainly productive using Java, it’s also got huge downside in the community which these companies don’t really care about.
Never have I seen so much cargo cult practice. The ecosystem isn’t great, with hard to google docs and everything pointing you to a poorly written baeldung article.
Java gets no one excited. And, it gets no one fired.
I think you’ve got to meet your customer where they are, and if you can ship Java desktop apps, and they’re happy… keep going.
But you’re not shipping apps to their iPhones using Java. It’s either JS or Swift.
And, on Android it’s JS or Kotlin.
So the common target is JS.
It's a combination of JavaFX, a custom controls library that implements Material Design and GraalVM native image. Whether you want a non-native UI toolkit on iOS is a separate matter, but it can be done technically.
In fact, you can ship JavaFX apps to the web too. It doesn't compile to megabytes of JS either - instead, the JavaFX app runs on the server and the UI is projected to divs and SVG elements on the fly. The browser provides native scrolling and text selection. If you have low latency to the server (e.g. same continent) it can work remarkably well. Check out https://www.jpro.one/ - the entire website is a JavaFX app.
But yeah, I guess i first started using Django ~2008 if I recall correctly, and switched over to Pyramid in 2010. Since then, I've used many other frameworks – in Python, Ruby, JS, C#, Java, and Go. Regardless of the language, I always try and go closer to the metal when I can.
I think your assessment about the others being potentially worse is fair: they're all bad in their own ways. I think it just takes time (and experience across many different teams) to develop good taste.
There are 3rd party libraries and integrations for _everything_ and it's comparatively easy to find skilled developers with years of experience.
Personally I prefer c#, but a lot of places already were using Java in significant ways before c# became good (IMO 2.0ish) and saw no reason to change.
For a simple hello world it is below 0.1s.
Slow startup time is a problem in automated testing of big codebases - when you aim to test the whole codebase, but each test alone does not stress any code path hard enough for the hotspot compiler to kick in. So basically most of your Java code in testing runs in interpreted mode, which is like 20x-100x slower than properly compiled and optimized C/C++/Rust.
If no code path gets run enough times, then frankly, it simply doesn’t matter, they could have been written in bash and still be “fast enough”.
Like, I can’t fathom a program that doesn’t have loops where the actually important part happens. You simply can’t write enough “linear” code to make a modern CPU sweat even just a little. And if you do have said loop, than it will be JIT compiled.
JVM performance is generally fine, and the language prevents you from introducing whole classes of memory safety bugs. You can still shoot yourself in the foot with native (JNI) extensions, but the decent performance of the JVM makes developers use JNI far less frequently than, say, Python or Ruby devs reach for compiled libraries.
JVM byte code decompilation is fairly trivial, so tools for the security scanning of artifacts are cheaper and more fully featured than similar tools for scanning machine code.
Then why do I have to have multiple JVMs installed, which I need to go through via trial-and-error, if I'm given some random java application?
There are occasionally breaking changes but most old jars out there will run fine in the latest runtime, so the answer to your question is going to depend on how squirrely your use case is.
You don't. Almost any Java program written ever will run on Java 18.
This is just how things are. It's Java fanfiction to say this isn't the case, and necessitates having multiple JVMs installed.
https://www.azul.com/downloads/?version=java-17-lts&package=...
There are two kinds of backwards compatibility at play: BC for compiled artifacts (let's call this Java ABI compatibility) and BC for language APIs (Java API compatibility). I have run into issues trying to compile an application with one JDK version and then run it on another (specifically, I believe JVMs will generally not run artifacts compiled with a later JDK version), but API backwards incompatibility is really rare in Java.
I do recall having to do a bit of cleanup when a previous project moved from Java 8 to Java 11, since a ton of deprecated APIs were pruned in Java 9, but those APIs were published by the JDK authors as standalone JARs, so I could remediate by adding a maven dependency. JVM backwards compatibility isn't perfect, but it's a much more stable target than other languages I've worked in.
this has been the case in the past, but Java is gritting its teeth and making some breaking changes in the JDK11/16/18 era.
libraries that mess with the unsafe/internal namespaces are the most prominent example - there are a ton of legacy libraries that are going to break from that change unless updated - but there's also a big push for modules and for classes to encapsulate themselves more strongly.
- Java has great ecosystem support
- JVM is very performant
- Java has well understood quirks
- Reasonably mature build system
- Good deploy story
I wouldn’t use Java for a web server but I like it very much for many other things.
I think Scala + Play does a good job for JVM web frameworks but I’d use a Typescript thing (or just Rails)
There are downsides to Java. Bit and byte manipulation are annoying, for instance. But I’m still happy with it.
The competition includes languages like Rust, Go, Haskell or Erlang. Most of these lack the languages that you'll find on the JVM, so it really is a question of what you are trying to do. Go is certainly interesting in some places, especially if you want a binary that is entirely self sufficient. Each of these languages is interesting, though of course one has to wonder about hiring, whereas finding developers who've worked on the JVM is easy enough.
Java had so many marketing crazes that it's crazy. I still remember the "Java mobile game" fad from a dozen of years ago - Java this, Java that, Java for phone, Java for toaster...
At some point, the public mind starts to associate "programming" with Java. Since there were (and probably always will be, unless we evolve into utopia) hordes of job-desperate people who would do anything to feed themselves and their families, a lot of them hear about Java, start programming in Java and develop their whole world around Java. Java helps them with this by providing this huge, Java-centric ecosystem which gives you everything you ever need to do whatever you want to do.
Of course, there is also the fact that Java is a managed environment and makes it a lot harder for programmers to write broken code, which makes large-scale programming possible even if all you have is a horde of mediocre programmers. But then again, that property is not unique to Java.
People laugh at the cute "Java runs on four billion devices!" tagline but back in 1995 if you weren't doing Java, you were a last-century coder. It was so pervasive Brendan Eich even renamed his strange scheme-smalltalk mashup "JavaScript".
I suppose there are many Java enthusiasts on here.
I don't think that is true. Windows is still around.
Twenty-five years ago
Hype is not reason why "a major Silicon Valley company that's migrating their backend to it".
I'm not bashing the language - it certainly has its defenders. Some people regard simplicity as a virtue (although I'd question how simple Java is nowadays). But I think even ardent Java fans will concede, Java has some deficiencies as a language. The main reason people use Java, is that people know Java. And the main reason they know Java, is because it is used.
It takes a long time to kill a network effect.
(Also, Scheme-Self, not Scheme-Smalltalk.)
Java also supposedly runs on everything. And, you could as well write Android apps in it.
But to throw another onto the pile, Java being statically compiled makes it a lot easier to comprehend a larger codebase that you didn't necessarily write yourself. The history of dynamically-typed languages is basically getting to a certain critical size and realizing there's some merit to a well-defined, compiler-checkable code interface and then retconning it in - this happened with Facebook (Hack-lang), with Typescript/CoffeeScript (Google), and I think there's one for Python now too.
So yeah, basically people like Java because it's fast and versatile, because it's got a Pythonesque amount of stuff already written for it, and because it's got features that are oriented towards bigger teams with more turnover/etc that make it practical for business reasons.
cough Microsoft did Typescript, actually
That's kind of downplaying it right? Outside of "native" languages like C, C++, Rust, fortran, Java blows everything else out of the water.
But that is what I think OP means. It isn't top tier performance, but it is in the second tier which I wouldn't argue for qualifying for "relatively good".
FWIW I find that languages tend to fall into a couple rough performance groups:
- Native at about x1 performance: C, C++, Rust, Fortran
- Compiled GC at about 2x performance: Java, Go, Haskell, Lisp
- Dynamic at 20-50x native performance: Python, Ruby...
JS is a weird one that varies a lot depending on your workload and can sometimes look similar to compiled GC performance.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Also, 'performance' isn't really an issue once you are past 'good enough'.
I don't think most devs are worried about Java/Node/Python/Golang performance on the backend for most apps. Obviously not always the case, but in most instances, it's not a primary concern.
Frankly, these days, when I think about 'performance' I think about 'developer performance!'. Java downsides in that its bureaucratic by convention, but at least it's super solid and reliable. I really, really wish it were a bight lighter however.
Source? I constantly see companies switching from the likes of python and javascript to compiled languages after they reach a certain scale.
I find Java to be extremely developer friendly, especially nowadays with features like pattern matching, records, switch expressions, and the upcoming project Loom
Modern frameworks like quarkus.io are also a step in that direction. You might also want to check out https://jodd.org/.
But the parent poster is right that the two teams did choose a different workload to optimize for, V8 being fast faster while java taking more time to JIT compile a region, but that is mostly due to the difference between their most-common uses, running websites’ code as soon as they load vs mostly running on huge—ass servers with sometimes terabytes of RAM.
I've built V8 instances and run some tests, but you don't need to do that.
Just write some roughly equivalent code and run it in Chrome vs. JVM and see for yourself. It's not nuance or controversial, it will really 'stick out'.
In basically all cases, V8 will 'get fast very quickly', it reaches a high level of performance on the second or 3rd iteration of a bit of code, whereas Java takes thousands of iterations.
That's a very, very broad and crude generalisation, but you'll see it very clearly if you just try it yourself.
It actually does make a difference depending on what you are doing as well - if you're running as server that uses the same code paths all day long, and executes them 1K times per minute, well then Java is fine. But for things like UI I particular where yo need the code to react it's a problem.
'Developer friendly' and 'modern frameworks' are a another question.
Go ahead and run some code on V8 and the equivalent Java.
You'll notice V8 is 'extremely fast' at getting up and running, while Java takes quite some time - often thousands of iterations of a specific bit of code before it optimises.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Those tests ultimately measure 'peak performance'.
Java takes much longer to reach that level than V8.
Given the fact that a lot of software does not run for a long period of time, and especially UI/Web code (and possibly serverless), the net result is that V8 is basically faster. It gets too near 100% performance after 1 or 2 iterations. Java takes 1000's of iterations.
For many applications, the 'advantage of being fast soon' considerably outweighs any kind of incremental performance benefit that Java might offer over time.
This is mostly true for UI software.
V8 'is fairly quick, and right away'. Java is 'sticky and slow for a bit, and then a bit faster over time but it's not noticeable'.
V8 is a regular car: stops and starts as needed. Java is a race-track stock car - accelerates up to a high speed and stays there. You don't need a race-track car to do food delivery, because the added peak performance is pointless.
Yeah, I'd agree (with the sibling as well). Obviously native code is faster but Java is about as fast as it gets for a managed language. It's massively fast (these days) in comparison to, say, Ruby. Python manages to do relatively OK by throwing away any sense of threading and the synchronization of internals that might entail but it's still significantly slower than Java.
Speaking of which that's another advantage of Java. Threading works basically as expected, and now it's even got modern promise/future APIs. Python's "just clone all your memory and do everything through IPC" is super clunky in comparison.
In many cases, that's also inherited by other JVM languages, and interop is really cool there too. You can use Java libraries in your Scala/Kotlin/etc, or call those routines from your Java code. It's not just one language, it's all the languages on one engine, and they all run decently fast for what they are.
C#/DotNet is really fast too (I would say faster than Java if anything) and it's pretty similar to Java in most ways. Bytecode, JITed, static typing, etc. They also got the second-mover advantage, to see all the things Java did wrong and fix them - like checked exceptions, or the clone() interface. On the other hand, at least as of 10 years or so ago, the tooling was absolutely primitive compared to what's out there for JVM stuff (and at the time it was all tied to Windows). Maybe that's changed with Roslyn but they're coming from behind, and Java has been around for a long time and it's tough to overcome that inertia.
So it boils down to: do the people like static typing or not.
I work in Go currently and although I see the positives, I'd be less inclined to unleash a bunch of mid-level devs using Go to create a huge codebase.
If I could have my way, personally, I'd go for one of the other fun JVM langs out there... but that's just me having fun.
Why?
For most traditional Java frameworks (like Spring or Quarkus), Kotlin has long been a first class citizen (support, documentation, custom Kotlin extension functions, etc.) and probably the easier language to use when you are starting out with those frameworks. And of course on Android, it has long replaced Java as the default language.
On the server server-side, Spring has done so much work on integrating with Kotlin in the last five years that you are probably not doing yourself any favors if you choose not to use that. Kotlin DSL support is just a killer feature here. Spring has really embraced that and they've added nice Kotlin DSLs for pretty much everything that matters. No more builders. No more endless function chaining. No more annotation magic (or at least a lot less of it).
https://www.infoq.com/news/2022/03/jrebel-report-2022
Hardly a successor of anything outside Google's ecosystem.
You can see that in the statistics for Java 8 in the link you provided. 37% is still stuck on Java 8. That's quite old. We're talking about projects that have not updated anything in close to ten years. And some even use older versions than that apparently.
That's not because those versions were that good but because some people just are that conservative. Java was always popular with conservative companies like banks. Companies like that are likely not using anything released in the last six years. According to Jrebel, that's about half of the survey. You can read into that what you want. But it's kind of meaningless for new projects.
Kotlin had their 1.0 release only six years ago. Spring boot 2.0 followed two years later and that was the first version to incorporate a lot of Kotlin and add explicit support for it.
It would be more interesting to get some statistics for Spring Boot 2 projects and Kotlin usage. Or even break them down by point release (2.7 is coming out soon) and there should be a version 3 by november as well. I'd expect a jump in Kotlin users with each of those point releases. JDK 17 / Kotlin 1.6 are going to be the minimum supported version for v3.
So, the percentage of users of anything over java 12 vs kotlin users is kind of suggestive: 12% vs 8%. Modern Java seems to be not that much more popular than Kotlin. And of course there's a difference between people that have upgraded their JVM and those that are actually actively using the new language features in Java.
It would also be interesting to compare those Spring Kotlin numbers against the versions back when Spring was equally motivated to support Scala and Groovy a decade ago.
In any case it hardly matters when the platform only supports Java out of the box on the JDK, without extra tooling, and anything besides InteliJ is a second class experience.
Intellij is written in Kotlin. It does Kotlin really well. Better than Java even.
InteliJ is partially written in Kotlin, unless they decided to port everything on top of Kotlin/Native.
As for why Kotlin exists at all,
> And while the development tools for Kotlin itself are going to be free and open-source, the support for the enterprise development frameworks and tools will remain part of IntelliJ IDEA Ultimate, the commercial version of the IDE. And of course the framework support will be fully integrated with Kotlin.
https://blog.jetbrains.com/kotlin/2011/08/why-jetbrains-need...
- Compiler performance, especially of mixed Java/Kotlin compilation, is spotty.
- Support and tooling maturity is way behind Java. I'm trying to use it with Bazel, but the support lags far behind the Java equivalents.
- Interop with Java libraries that use annotations or interfaces is maddening.
- Perhaps a matter of taste, but the `var` keyword is a blight, even in Java.
- for all the complaints about "== vs .equals" and that sort of thing, these are by now incredibly well understood pitfalls that are generally checked by static analysis, sometimes even at compile time now by e.g. ErrorProne
In the early days, when it seemed like Java development might stall because of legal hell, Kotlin was a breath of fresh air. But today now, Java is clearly on a path to cannibalize Kotlin's most valuable features. Record classes and pattern matching obviate the biggest draws of Kotlin, while maintaining the buttoned-up, no-fun-but-extremely-pragmatic rigor of the Java language.
Once the glow started to fade, I was left with a very boring, familiar question: what am I really gaining by using this new tool? Is it worth the cost? and on my list of problems I deal with daily as a Java developer, the problems addressed by Kotlin are now so far down on the list that it's not worth dealing with even the tooling friction imo. For me, anyways.
* It's very fault tolerant. In almost every case C++ would mushroom cloud, Java can walk it off. It's easy to write code that cleans itself up on error. try-with-resources is great.
* Modern JVM JIT and GC are pretty damn good, and the tooling is great.
* It's very boring and stable. I see this as the killer feature. Cool is a conserved quantity. It's extremely hard to write cool software in a cool language, as that attracts cool developers too busy getting cool jobs to maintain their cool framework you depended on. There's a lot of mature high quality libraries available. APIs are stable, stuff very seldom breaks. You can often use code that's 10-15 years old just fine. This has changed a bit lately, but it's still extremely reliable.
There are drawbacks as well, primarily memory mapping files is difficult, some of the aforementioned libraries are a bit bloated as well and overall the language sort of encourages large complicated solutions to simple problems.
1. GC is an enormous productivity improvement for applications whose performance characteristics can afford it. Seriously, I can't think of a single programming language feature/concept/technology that makes a bigger difference in developer velocity than memory safety and GC.
2. Static types are also a large productivity improvement for developers comfortable using them and the kinds of projects that benefit from them (large, multi-dev, multi-year).
3. Imperative and object-oriented. Java is imperative in the small, which is familiar to most developers and seems to strike a balance of letting developers write correct code with good performance. Java is OOP in the large and for many classes of applications, that's been a wildly successful way of organizing code and lowering the cognitive overhead.
4. Path dependence. The past largely determines the present. We don't reset all of our technology choices and start from scratch at the beginning of each fiscal quarter, so the languages that were widely used tend to stay the languages widely used.
Java was the first heavily engineering and marketed language that featured GC, static types, and OOP, so it got big and continues to stay big.
I also think it's a pretty decent language. The things people don't criticize about Java are mostly complaints about a certain style of programming in the 90s and less the language itself.
(assuming that's a typo there, the things people don't like/the things people criticize?)
I disagree on that point, I think the language itself was a problem. Java 6 and prior were very very verbose and very very slow. I think that really changed with JDK 7/8, but the java we know today isn't the java of the 90s.
Even then, to some extent that problem hasn't gone away, it's just been papered over with frameworks and library code. Like honestly even as a java dev, I couldn't tell you off the top of my head the exact code to open a file, read each of the lines, and then close everything, or the right way to open a JNP connection to RabbitMQ or something. Anything touching files or sockets is still an absolute nightmare of boilerplate code, inputstreams, filestreams, printwriters, try/catch, and mother. fucking. IOException. thrown everywhere. But you don't care, because you just call Apache commons-io or Spring RestTemplate.
And that has contributed to some criticism of its own. Spring tends to turn into architecture-astronaut crap, like everyone's favorite AbstractSingletonProxyFactoryBean.
> Java was the first heavily engineering and marketed language that featured GC, static types, and OOP, so it got big and continues to stay big.
The other quiet boost that people don't talk about is Android. Tons of libraries get written with the intent of being used in some android app somewhere, but you can use that equally much on desktop/server. It really really helps the rapid-prototyping aspect of Java that ~half the smartphones in the world are running Android.
var lines = Files.lines(Paths.get("file.txt"));
or if you want to be extremely cautious: try (var lines = Files.lines(Paths.get("file.txt"))) {
}
I agree about IOException though. It's the bane of my existence :) The amount of times I have to rethrow UncheckedIOException is far too high.Oops, yes, sorry.
> Java 6 and prior were very very verbose and very very slow.
It's kind of verbose, when looking at it from today's perspective, but at the time I didn't think it was that bad. I think they deliberately tried to be less cryptic and more wordy than C/C++ (which can often be impenatrable). I think they overcorrected for that, but I don't think the language is interolerably verbose.
Performance was definitely bad until Lars Bak and company showed up and wrote HotSpot. But almost every managed language at the time had poor performance. It was certainly painfully slow compared to C and C++ but... what wasn't?
> I couldn't tell you off the top of my head the exact code to open a file, read each of the lines, and then close everything, or the right way to open a JNP connection to RabbitMQ or something.
I mean, I couldn't tell you how to do that off the top of my head in just about any language. Except C. But, of course, the code I would write for C off the top of my head is simple and wrong because it doesn't handle all of the various ways IO can fail.
Networking and file systems are honestly just kind of grungy. You can paper over it with simple APIs but what you end up with is code that looks pretty but can fail in obscure ways.
> try/catch, and mother. fucking. IOException. thrown everywhere.
Yeah, checked exceptions were simply a mistake. I don't fault them for trying to fit exceptions into the static type system. It was a cool, ambitious idea. At the time, no one really had a sense of how the ecosystem would settle around using exceptions. In practice, it ended up being more trouble than it's worth.
> The other quiet boost that people don't talk about is Android.
Android is definitely keeping it popular these days, but Java was huge because of server development before Android came along.
Slow compared to Smalltalk. Slow compared to Lisp.
But even fast Smalltalks weren't very fast relative to C, just relative to most other dynamic languages. And, of course, the people who made Smalltalk fast ended up making Java even faster.
Seemed like you'd shifted your comparison to "almost every managed language."
So Java at-the-time compared to Smalltalk and Lisp at-the-time.
I suppose Haskell at-the-time, OCaml at-the-time :-)
Were there fast managed languages? Yes, a couple.
Were there fast managed object-oriented languages? Yes, Self and a Smalltalk or two.
Were there fast managed statically typed languages? Yes, SML and Haskell.
Where there fast, managed, statically typed, and object-oriented languages? Not many. Maybe Eiffel, but that was shackled by DbC dogmatism and probably some amount of Eurocentricity. I don't think Ada had GC back then and if your complaint is that Java is too verbose, you sure as heck aren't gonna like Ada.
Free-as-in-beer?
Contrary to the Silicon Valley popular opinion, these languages are incredibly productive and run much of the world, with code running in all of the Fortune 500 companies down to small businesses and even your phones.
Python and node are both great but mostly just wrap C libraries, so If openssl or your database driver has a bug your fancy interpreted/sandboxed language does not help.
Java has existed a long time and developers have had time to reimplement many popular protocols and libraries. This can sometimes cause few % points of performance, but you get stability and the excellent monitoring&debugging.
For example, if I need to accept images from the internet I would use Java since there are pure java image libraries that cannot be exploited with unexpected data. At most it fails and everything is cleaned up.
A trivially easy replacement would be Kotlin, it compiles to the same VM (and additionally webassembly), it is designed and implemented by one of the leading IDE vendors, it can trivially call and be called from Java, it has tons of zero-cost syntactic sugar that covers the inhumane verbosity that Java buries you under till your eyes bleed and your hands scream. A well-designed language, what should have been all along.
I think at least some of my dependencies still rely on reflection features that are limited by Java 9.
Not OP by the way
If you want to write shared Java libs that work on both client/server, you need to stick to Java 8.
Plus most code probably handles this poorly. I suspect it will often result in exceptions opening new files or connection which are just caught and retried forever, resulting in your server locking up after some period of time. If you are lucky it just crashes and restarts. (The ultimate garbage collection.)
The only really issue is that you effectively need to litter your code with queue checks, or dedicate a thread to watching them.
https://download.java.net/java/early_access/jdk18/docs/api/j...
But I agree with you, it is anecdotically a problem for me right now as well: intellij under osx can’t index/import a bigger work project due to getting too many files IO exceptions all around, even though I tried increasing both the system limit, the java vm flag, everything..
Examples of things that have been or are being removed more important than finalizers:
- As noted, SecurityManager deprecation
- JavaFX. It was bundled in Java 8, lots of apps were written on that assumption, then it was removed. They all had to be re-packaged.
- Web Start.
- Removal of access to JDK internals in general, which lots of stuff depended on.
- Removal of various Oracle Java specific stuff, e.g. the resource management/control API.
- Java EE stuff. Annoying because a lot of stuff used it only for a few utilities, and because it actually got renamespaced so you couldn't even just add in the packages from elsewhere.
And there were lots of other backwards compatibility breaks, e.g. when they changed the format of the version number a lot of stuff broke.Probably more that I've forgotten.
Java is a great platform and I use it all the time, but Java's backwards compatibility is highly overrated. They don't care about real apps at all, probably because they hardly use any. Their backwards compatibility is defined relative to their own specification, not working software, so important apps have broken repeatedly over time.
"I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. In recent years, a number of program analysers like PREfix and PREfast in Microsoft have been used to check references, and give warnings if there is a risk they may be non-null. More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965." -- Tony Hoare
Let's give credit to C# and Dart for fixing this, and bring shame to Java and Go for not doing so.
* https://www.oracle.com/java/technologies/java-se-support-roa...
I believe JetBrains use Swing and their stuff is pretty great.
Currently fully FRP UI is quite popular (Jetpack Compose, ReactJS etc) but frankly whenever I've read such codebases I've been somewhat unimpressed. You can make JavaFX work that way with a bit of extra utility code, and there are places where that approach makes sense, but fundamentally a well engineered OOP UI toolkit is in its element. The functional reactive approach seems to create quite a few problems. Whether it creates as many as it solves, I reserve judgement as I haven't had a chance to write a large GUI app since they came out. Still, I've written a lot of them in the past and would definitely be tempted to stick with JavaFX despite having worked through the Jetpack Compose tutorials.
(note: jfx-central is itself a JavaFX app, again running with JPro to be projected over the web, but you can also download it and run it locally).
Java 9-17 adds some major language features, but they're not so major that you'd really need a whole book. You could look up the individual JDK release notes and see what each release changed.
[0] https://www.oreilly.com/library/view/effective-java/97801346...
Hopefully Panama delivers one day.
Android 13 is surprisingly adopting more Java 11 features, most likely because they need compatibility with Java libraries that have moved beyond the Android Java subset.
static void testStringOrNull(Object o) {
switch (o) {
case null, String s -> System.out.println("String: " + s);
}
}
I would never have believed you. And while it's not Elixir-level pattern matching, it's not toothless either. This is valid pattern matching code too: static void testTriangle(Shape s) {
switch (s) {
case Triangle t && (t.calculateArea() > 100) ->
System.out.println("Large triangle");
default ->
System.out.println("A shape, possibly a small triangle");
}
}
[1]: https://openjdk.java.net/jeps/420Java's Project Loom will be superior to C#'s async/await.
[1]: https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...