Java
Java is the language everyone loves to hate.
It was designed to be the safety version of C++, like those rounded scissors children have to use in school.
Java is an object-oriented (OO) programming language with verbose syntax, redundant code, and repetitive structure: it features static data typing, access modifiers, extensive libraries, data abstraction, structured class definitions, whiny compilers, and semicolons for days: all to ensure you don't screw up your own code.
Jokes aside, Java is a powerful language that does not compile to machine code but to Java Bytecode, which is then run by a Java Virtual Machine, allowing Java to be written anywhere, compiled anywhere, and run anywhere.
Your Friend the JVM
Java is one of the most well-known languages, primarily because it's available on everything: computers, phones, printers, toasters, you name it.
The reason for this is because Java's compiling is platform independent.
Most compiled languages compile to assembler code or machine code, instructions for the CPU to perform specific tasks, like read from a particular memory address or perform an arithmetic operation on some bytes of data.
Assembler code and machine code are architecture-specific, meaning they are tied to the motherboard, graphics card, CPU, RAM, and everything else in your computer.
Because of this, some programs written in compiled languages need to be compiled for each computer they run on: this is where Java shines.
Java does not compile to machine or assembler, it compiles to Java bytecode, which is run by a Java Virtual Machine (JVM).
A virtual machine is a program that emulates a physical computer all the way down to the architectural level.
A JVM is just that, but follows the
Java Virtual Machine Specification, an abstract definition of a virtual machine where the Java bytecode is its machine code.
The Many "Safety Features" of Java
Java was designed in the aftermath of C++ to contain all the object-orientedness of C++ without all the dangerous, abusable capabilities like operator overloading, lazy evaluation, inferred data typing, and functional programming.
Java instead has numerous features to reduce the chances of rapid and spontaneous code destruction.
Strict Data Typing Rules
Java is what is called strongly typed.
The actual definition of strongly typed is nonexistent, but it's used colloquially often enough it's understood by pretty much everyone in programming.
Strongly typed really just means "more strict than weakly typed" and weakly typed really just means "less strict than strongly typed": there are no absolutes, only comparisons.
The basic idea behind a strongly typed language is that objects, when passed around, can reliably be compatible with an expected data type: that is, an object passed to a function will be of a compatible type to the type expected by the receiving function.
In Java you declare the types of each parameter and can expect either an object of that type or a subtype, which brings me to the key features in Java's type system: manifests and names
Java uses both a Manifest Type system and a Nominative Type system.
The Manifest Type system enforces that programmers explicitly declare the type of the variable at some point during or before initialization: in double size;
and Color color;
both double and Color are the manifests.
In the Nominative Type system, objects are considered compatible if they share the same class, not just share the same structure.
In Java, an ArrayList is a List, so an object of type ArrayList is compatible where either an ArrayList or List is expected.
In contrast, some languages just expect certain methods or fields to be present to establish compatibility.
For example, an object of type ArrayList and another object of type HashMap have similar methods (clear
, isEmpty
, size
).
In a dynamic language, like Lua or Python, both objects could be cleared since they both have the clear
method, but in Java, since the two objects do not have the same classes they are incompatible.
Access Control
All languages have their own way to do scoping: the "visibility" of the values of variables in certain parts of code.
Inside methods, variables in Java are only visible inside the block they were declared and inside any nested blocks, but not visible outside of that.
Class-level variables (fields and methods) have a different set of visibility rules; because there are no "blocks" that these variables are declared in the previous scoping rules aren't applicable.
For these variables Java has Access Modifiers, of which there are four types.
The most visible access modifier is public, denoted by the public
keyword.
Public lets any and every class in a program access that particular class-variable; thus, in most Java programs, the only public variables are finals or methods: non-final fields should not be public as their globalness can cause unpredictable results.
Underneath public is protected, denoted by protected
.
Protected variables in a class A are visible to only other classes in the same package as class A and subclasses of class A regardless of the package the subclasses are in.
More restrictive than protected is package-private, which is indicated by no explicit modifier keywords before the variable name.
Package-private lets only the classes in the same package see that variable: not even subclasses can see it.
The most restrictive is private, denoted by private
, which restricts the visibility of the variable to only inside that class.
The following is a table comparing the visibilities of each access level:
Level | Class | Package | Subclass | Global |
public | yes | yes | yes | yes |
protected | yes | yes | yes | no |
package-private | yes | yes | no | no |
private | yes | no | no | no |
Something That Bugs Me
One thing that bugs me about Java's selection of access modifiers is that there is not a subclass-private modifier that limits visibility to only a class and its subclasses.
I somewhat understand the reasoning behind package-private variables being open to only those classes in the package, but I don't understand why protected variables are also visible everyone else in the package.
To me, it seems that subclasses would need higher privacy than package classes, but that's just me.
I would propose a new access modifier for subclass-private with keyword parental
(parental to keep with the P theme the access modifiers have, and also to indicate it's a parent variable and only accessible by the children).
Consider the following as an addition to the table above:
Level | Class | Package | Subclass | Global |
subclass-private | yes | no | yes | no |
An Addendum to Above
After some Search Engine Result Analysis (looking it up on Google), I understand why there is not a subclass-private access modifier.
The earlier versions of Java had it (it was called private protected
), which gave access to just the class and subclasses and nothing else.
It wasn't used very much, never worked, and the developers thought it was too confusing to use, so they dropped it.
Historically, it existed but was taken out for fear of confusion in usage, however from a developer's perspective, it makes sense to not include it.
Developers group similar purpose classes into packages and not families, thus it makes sense that a package-private access is available.
Though a subclass outside a package can access all protected and public values of its parent, the other classes in the package still have access to those same values.
It's easy enough to enforce classes in the same package to not purposefully access certain values, so a subclass-private access is redundant when a protected modifier could be used and respected instead.