Java For C# Programmers
Java For C# Programmers
This page contains an overview of the differences between the Java and C# pro-
gramming languages, from the perspective of a C# programmer who is new to Java.
The comparison is not encyclopedic but rather highlights some fundamental points
that are potentially troublesome or otherwise remarkable. New features introduced
in the major revisions Java SE 5–11 are noted where appropriate.
Since both languages are closely tied to their respective runtimes, I’ll also
cover any relevant differences between the Java Virtual Machine (JVM) and the .NET
Framework, as well as basic library classes. I don’t cover the library frameworks for
networking, serialization, GUI, XML, and so on. The capabilities of both platforms
are broadly equivalent in these respects, but the implementations are quite
different.
C# 7 and later — After the release of C# 6, Microsoft moved the rapidly evolving
language to Github and apparently no longer bothers to write proper specifications.
Some of the new features are directly copied from Java (e.g. digit separators) but it’s
generally safe to assume that C# 7+ features won’t be available in Java. The rest of
this page only covers C# up to version 6.
Further Reading
See Compiling Java Code for a quick primer on how to compile and run Java
programs.
Oracle’s JDK 11 Documentation comprises all reference material, including
the Java Language and VM Specifications, the Java Platform SE 11 API Specification,
and the Java Tutorials. For a thorough performance analysis of Java language and li-
brary elements, see Mikhail Vorontsov’s Java Performance Tuning Guide.
The best printed introductions are Horstmann’s Core Java and Bloch’s
Effective Java, as well as Horstmann’s Core Java for the Impatient as a quick overview.
Please see Java Books for further recommendations. Andrei Rinea’s tutorial series,
Beginning Java for .NET Developers, covers selected topics in more detail.
Article Contents
1. Keywords
2. Primitives
3. Arithmetic
4. Control Flow
5. Arrays
6. Strings
7. Classes
8. Inheritance
9. Interfaces
10. Nested Classes
11. Lambda Expressions
12. Enumerations
13. Value Types
14. Packages & Modules
15. Exceptions
16. Generics
17. Collections
18. Annotations
19. Comments
1. Keywords
Java and C# use a very similar syntax and provide similar sets of keywords, both de-
rived from C/C++. Here I’ll briefly list some noteworthy differences that aren’t
mentioned in the following sections.
Java versions prior to SE 10 lacked var but offered type inference for lambda expres-
sions (Java SE 8) and for generic type arguments, including diamond notation for
generic constructors. Do not combine var with generic type inference, or the omit-
ted type variables may be inferred as Object ! See Stuart W. Marks’ style guidelines
for more information.
Missing Keywords
As an alternative to writing your own event infrastructure, consider using the “ob-
servable” objects defined in the JavaFX package javafx.beans and its sub-packages.
Such objects let you attach listeners that are notified on value changes – often the
intended use of events. (Note: JavaFX has become a separate download as of Java SE
11 and is now available here.)
Java has no equivalent to LINQ keywords. As of Java SE 8, lambda expres-
sions and stream libraries replicate the method-based incarnation of LINQ to
Objects, complete with lazy and/or parallel evaluation. Third-party libraries for
LINQ-like queries in Java include iciql, Jinq, jOOQ, and QueryDSL.
2. Primitives
Java and C# have equivalent sets of primitive types ( int etc.), with the following
exceptions:
Java only has signed numeric types, including byte . All unsigned variants are
missing. Java SE 8 added unsigned operations as library methods.
C# decimal is similar to the Java class BigDecimal which has no corresponding
primitive.
All Java primitives have equivalent library classes. However, those are not syn-
onyms as in C# but rather boxed versions. This is because Java does not support
value types in its class hierarchy.
For the same reason, Java has no nullable variants of primitives. Simply use the
equivalent library classes – they are all reference types and therefore nullable.
In addition to decimal, hexadecimal, and octal literals for numeric values, Java SE
7 added binary literals and underscore literals.
Note the distinction between primitive-equivalent classes in C# and Java. In C#, int
and System.Integer are synonyms: both represent an unboxed primitive value type.
You need an (Object) cast to explicitly wrap a reference around such values.
But in Java, only int is an unboxed primitive value whereas
java.lang.Integer represents the strongly-typed boxed version! C# programmers
must take care not to use Java primitives and the corresponding class names inter-
changeably. See autoboxing for more details. (Java SE 5)
Java automatically unboxes primitive values from their strongly-typed
wrapper objects as needed within the context of assignments, mathematical opera-
tions, or method invocations. Explicit casts to primitive types are only required
when unboxing from a more general class ( Number , Object ).
3. Arithmetic
4. Control Flow
Java reserves goto but does not define it. However, statement labels do exist, and in
a strange twist break and continue were enhanced to accept them. You can only jump
out of local blocks, but break operates on any block – not just loops. This makes Java
break almost the full equivalent of C# goto .
Java switch operates on (boxed or unboxed) primitives and enums, and
since Java SE 7 also on strings. You don’t need to qualify enum case values with the
enum type as in C#. Java allows fall-through from one case to the next, just like
C/C++. Use the compiler option -Xlint:fallthrough to warn against missing break
statements. There is no equivalent to C# goto targeting a case label.
5. Arrays
As in .NET, Java arrays are specialized reference types with automatic index check-
ing. Unlike .NET, Java supports only one array dimension directly. Multi-dimen-
sional arrays are simulated by nesting one-dimensional arrays. However, when all
dimensions are specified during initialization, all required nested arrays are allo-
cated implicitly. For example, new int[3][6] allocates both the outer array and all
three nested arrays of six integers, without the need for repetitive new statements.
Any number of rightmost dimensions can be left unspecified to later manually cre-
ate a ragged array.
Much array-related functionality is provided by the helper class Arrays:
comparison, conversion, copying, filling, hash code generation, partitioning, sort-
ing & searching, and output as a human-readable string which the instance method
Array.toString notably doesn’t do. Java SE 8 added several parallel… methods that
perform multithreaded operations if possible.
6. Strings
As in C#, Java strings are immutable sequences of UTF-16 code units which each fit
into one 16-bit char primitive. For strings containing any 32-bit Unicode code
points (i.e. real-world characters) that require surrogate pairs of two UTF-16 code
units, you cannot use the ordinary char indexer methods. Instead, use various
helper methods for code point indexing which Java defines directly on the String
class.
Java SE 9 introduced a compact representation for strings containing only
ISO-8859-1 (Latin-1) characters. Such strings use 8 rather than 16 bits per charac-
ter. This is an automatic and purely internal optimization that does not affect public
APIs.
Operators — Unlike C#, Java does not special-case the == operator for strings, so
this will only test for reference equality. Use String.equals or
String.equalsIgnoreCase to test for content equality.
Java does special-case the + operator for string concatenation (JLS §15.18.1).
Annoyingly, this performs JavaScript-like automatic conversion of all types to
String . As soon as one String operand is found, any existing intermediate sum to
the left and any remaining individual operands to the right are converted to String
and concatenated as such. As in C#, piecemal string concatenation can also be inef-
ficient. Use the dedicated StringBuilder class for better performance.
7. Classes
Java lacks C# static classes. To create a class that only contains static utility meth-
ods, use the old-fashioned approach of defining a private default constructor. Java
does feature a static class modifier, but only for nested classes and with very dif-
ferent semantics.
Class Objects contains some simple but useful helper methods for objects of
any type, including hash code generation for multiple objects and various null-safe
operations.
Destruction — Java supports finalizers that run before the garbage collector de-
stroys an object, but these are rather sensibly called finalize instead of C#’s mis-
leading C++ destructor syntax. Finalizer behavior is complex and problematic, so
you should generally prefer try/finally cleanup.
Java provides not only weak references like C# that may be collected at any
time, but also soft references that are only collected in response to memory
pressure.
8. Inheritance
Base classes are called superclasses and accordingly referenced with the keyword
super rather than C# base . When declaring derived classes, Java extends (for super-
classes) and implements (for interfaces) specify the same inheritance relationships
for which C# uses simple colons.
C# virtual is missing entirely because all Java methods are virtual unless
explicitly declared final . There is little performance penalty because the JVM
Hotspot optimizer, unlike the rather stupid .NET CLR optimizer, can dynamically
inline virtual methods when no override is detected at runtime.
Java SE 5 added covariant return types to support its type-erasing generics.
Covariance is achieved through compiler-generated bridge methods, so watch out
for versioning issues with subclasses.
9. Interfaces
Like C#, Java supports single class inheritance and multiple interface inheritance.
Interface names are not prefixed with I as in .NET, nor in any other way distin-
guished from class names.
Java does not support C# extension methods to externally attach imple-
mentation to an interface (or class), but it does allow implementation within an in-
terface. Java interfaces may contain constants, i.e. public static final fields. The
field values may be complex expressions which are evaluated when the interface is
first loaded.
Java SE 8 added static and default methods on interfaces, and Java SE 9 pri-
vate methods as well. Default methods are used in the absence of a normal class im-
plementation. This obviates the need for abstract default implementing classes, and
also allows extending interfaces without breaking existing clients.
Java does not support C# explicit interface implementation to hide inter-
face-mandated methods from public view. This is probably a good thing since the
access semantics for explicit interface implementations are notoriously error-prone
when multiple classes are involved.
Inner Classes — Non-static nested classes are inner classes which carry an implicit
reference to the outer class instance that created them, similar to the implicit this
reference of instance methods. You can also use outerObj.new InnerClass() to asso-
ciate a specific outer class instance. The inner class can access all private fields and
methods of its outer class, optionally using the prefix OuterClass.this for disam-
biguation. The static modifier on a nested class prevents this implicit association.
Local Classes — Inner classes may appear as local classes within methods. In addi-
tion to all private members of the implicitly associated outer class instance, local
classes also have access to all local variables that are in scope within the declaring
method, so long as they are effectively final .
12. Enumerations
2. Java enum types support arbitrary fields, methods, and constructors. This lets
you associate arbitrary data and functionality with each enum value, without re-
quiring external helper classes. (Each enum value is an anonymous subclass in-
stance in this case.)
One significant defect of Java is the lack of user-defined value types. When released
in an undecided future version, Project Valhalla should offer .NET-style value types
with generics support – see the proposals State of the Values and Minimal Value
Types for more details. Right now, the only value types offered by Java are its primi-
tives which live completely outside the class hierarchy. This section briefly describes
the impact on semantics, performance, and generics.
Semantics — Value types have two important semantic properties: they cannot be
null (i.e. have “no value”), and their entire contents are copied on each assignment,
making all copies independent of each other in terms of future mutations. The first
property is currently impossible to achieve for user-defined types in Java, and can
only be approximated by frequent null checking.
Surprisingly, the second property doesn’t matter because value types should
be immutable anyway, as Microsoft discovered the hard way. Value types in .NET are
mutable by default, and that caused no end of obscure bugs due to implicit copying
operations. Now the standard recommendation is to make all value types im-
mutable, and that’s also true for value-like Java classes such as BigDecimal . But once
an object is immutable the theoretical effects of mutation are irrelevant.
Performance — Value types store their contents directly on the stack or embedded
within other objects, without a reference or other metadata. This means they require
far less memory, assuming the contents aren’t much bigger than the metadata.
Moreover, the garbage collector’s workload is reduced, and no dereferencing step is
needed to access the contents.
Oracle’s Server VM is quite adept at optimizing small objects that C# would
implement as value types, so there’s no big difference in computational perfor-
mance. However, the extra metadata inevitably bloats large collections of small ob-
jects. You need complex wrapper classes to work around this problem, see e.g.
Compact Off-Heap Structures/Tuples In Java.
Generics — As outlined in Project Valhalla: Goals, the fact that primitives are not
classes means they cannot appear as generic type arguments. You must use equiva-
lent class wrappers instead (e.g. Integer for int ), resulting in expensive boxing op-
erations. The only way to avoid this is hard-coding variants of generic classes that
are specialized for primitive type arguments. The Java library is littered with such
specializations. There is currently no better solution for this, until Project Valhalla
delivers value types that integrate primitives into the class hierarchy.
Java packages are largely equivalent to C# namespaces, with some important differ-
ences. As of Java SE 9, modules provide additional features for dependency checking
and access control.
Storage Format
The Java class loader expects a directory structure that replicates the declared pack-
age structure. Fortunately, the Java compiler can automatically create that structure
( -d . ). Moreover, each source file can only contain one public class and must have
the name of that class, including exact capitalization.
These restrictions have an unexpected benefit: the Java compiler has an in-
tegrated “mini-make” facility. Since the locations and names of all object files are
exactly prescribed, the compiler can check automatically which files need updating
and recompiles only those.
For distribution, the entire directory structure of compiled Java class files
along with metadata and any required resources is usually placed in a Java archive
(JAR). Technically, this is simply an ordinary ZIP file. The extension .jar is the same
for both executables and libraries; the former are marked internally as having a
main class.
Unlike .NET assemblies, JAR files are completely optional and have no se-
mantic significance. All access control is achieved through package and (to a greater
degree) module declarations. In this regard, Java packages and modules combine the
functionality of .NET namespaces and assemblies.
Packages
Java packages are the basic way to organize classes. They are not quite expressive
enough for large projects, such as the JDK itself, which led to the development of a
new module system for Java SE 9. Non-modular packages continue to be supported,
however, and should be sufficient for smaller applications.
Storage — The directory holding a package’s source code may contain an optional
file package-info.java that is only used for documentation. In a non-modular appli-
cation, directory trees for the same package can occur multiple times in different
sub-projects. The contents of all visible occurrences are merged.
Visibility — The default visibility for classes, methods, and fields is package-inter-
nal. This is roughly equivalent to C# internal but refers to declared packages (C#
namespaces), not to physical deployment units (JAR files or .NET assemblies).
Outside code can therefore gain access to all default-visible objects in a deployment
unit, simply by declaring itself part of the same package. You must explicitly seal
your JAR files if you wish to prevent this, or else use modules (Java SE 9).
There is no dedicated keyword for the default visibility level, so it’s implied
if neither public , private , nor protected is present. C# programmers must take spe-
cial care to mark private fields as private to avoid this default! Moreover, Java pro-
tected is equivalent to C# internal protected , i.e. visible to derived classes and to all
classes in the same package. You cannot restrict visibility to subclasses only.
Finally, Java packages have no concept of “friend” access
( InternalsVisibleTo attribute) that provides elevated visibility to specific other
packages or classes. Package members that should be visible to any other package
must be public or protected .
Modules
Java SE 9 introduced modules that combine any number of packages with explicit
dependency and visibility declarations. Technically all code runs in modules now,
but for backward compatibility any non-modular code is treated like a module that
depends on all present modules and exports all its packages.
Oracle does not currently provide concise documentation for modules. You
can dig through Mark Reinhold’s link-studded announcement, consult chapter 7 of
the Java Language Specification, or buy Cay Horstmann’s Core Java 9 for the
Impatient. The following overview is non-exhaustive.
Declaration & Storage — Each module corresponds to one single directory with the
(arbitrary) module name, containing the file module-info.java and subdirectory
trees for all contained packages. The packages are declared as usual. All module dec-
larations reside in module-info.java , using special Java keywords valid only there.
Dependency — requires declares any modules required by the current module.
Optionally, transitive makes a required module an implicit requirement of anyone
using the current module. Non-required modules are unavailable to the current
module, even if they are present on the module path.
Visibility — exports declares any packages exported for use, and opens declares any
packages open to external reflection. Optionally, exports/opens can restrict visibility
to a given list of named modules. Any packages not made visible are inaccessible to
other modules. Thus, public members of non-exported packages are equivalent to
C# internal members.
While modules may have the same name as packages, all module names and
all visible package names within an application must be unique. It is therefore not
possible to augment a package declared in another module, fixing a strange loop-
hole of Java packages.
15. Exceptions
Java is somewhat infamous for its checked exceptions, i.e. exception types that must
be specified in a throws clause if a method throws but does not catch them. The value
of checked exceptions has long been debated, on the grounds of programmer psy-
chology (compiler errors are silenced by swallowing exceptions, which is worse than
not handling them) and component interaction.
For example, meaningless throws clauses may proliferate through worst-
case scenarios, or else the exceptions are handled at inappropriate locations to stop
that proliferation. Anders Hejlsberg famously rejected checked exceptions when de-
signing C#. Some programmers simply avoid them altogether by wrapping checked
exceptions in unchecked ones, although Oracle is not fond of that practice.
Conceptually, however, checked exceptions are quite simple: all exceptions
are checked unless derived from Error (serious internal errors) or RuntimeException
(typically programming errors). The usual suspects are I/O errors that are expected
during normal operation and must be handled accordingly.
Otherwise, Java exception handling is very similar to C#. Java SE 7 added
multiple exception types in one catch block, and a try version that replicates C# us-
ing . The try-with-resources statement relies on the (Auto)Closeable interface, just
like C# using relies on IDisposable . Java SE 9 allows try-with-resources to use effec-
tively final variables, too.
Assertion Errors — Java’s base class for all runtime errors is not Exception as in
.NET but rather Throwable from which both Exception and Error derive.
Unfortunately Java’s AssertionError , thrown by assert failures, is an Error rather
than an Exception . So if you wish to handle assertion errors along with exceptions,
for example on a background thread, you must catch Throwable rather than
Exception . See Catching Java Assertion Errors for details and links.
Jumps and finally — As in C#, exceptions thrown in finally clauses discard previ-
ously thrown exceptions in the associated try blocks. Unlike C#, which forbids
jumping out of finally , simply returning from a Java finally clause also discards all
previous exceptions! The reason for this surprising behavior is that all jump state-
ments ( break , continue , return ) classify as “abrupt completion” in the same sense
as throw . The finally clause’s abrupt completion discards the try block’s previous
abrupt completion.
An even weirder consequence, although less likely to occur in practice, is
that jumping out of finally overrides ordinary return statements in the associated
try block. See Jumping out of Java finally for examples and further information.
Enable the compiler option -Xlint:finally to check for this pitfall.
16. Generics
Java SE 5 introduced generics two years before Microsoft added them to .NET 2.
While both versions look similar in source code, the underlying implementations are
quite different. To ensure maximum backward compatibility, Sun opted for type era-
sure that eliminates type variables at runtime and replaces all generic types with
non-generic equivalents. This does allow seamless interoperation with legacy code,
including precompiled byte code, but at a huge cost to new development.
C# generics are simple, efficient, and nearly foolproof. Java generics resem-
ble C++ templates in their tendency to generate incomprehensible compiler errors,
yet don’t even support unboxed primitives as type arguments! If you want an effi-
cient resizable integer collection in Java, you cannot use any implementation of
List<T> etc. because that would force wasteful boxing on all elements.
Instead, you must define your own non-generic collection that hard-codes
int as the element type, just as in the bad old days of plain C or .NET 1. (Of course,
you could also use one of several third-party libraries.) Primitives in generics are
planned as part of Project Valhalla – see Value Types above and Ivan St. Ivanov’s ar-
ticle series Primitives in Generics (part 2, part 3).
Rather than attempting to explain the complex differences between Java
and C# generics, I point you to the sources cited above, and to Angelika Langer’s ex-
tremely comprehensive Java Generics FAQ. In the rest of this section I’ll cover just a
few noteworthy points.
Construction — Java lacks the C# new constraint, but nonetheless allows instantia-
tion of generic type arguments with the class literal trick. Supply the class literal
Class<T> c for the desired type argument T to a method, then within the method use
c.newInstance() to create a new instance of type T .
As of Java 8, you can also use method references to constructors which were
introduced along with lambda expressions and are described in that section.
Static Fields — Static fields are shared among all type instantiations of a generic
class. This is a consequence of type erasure which collapses all different instantia-
tions into a single runtime class. C# does the opposite and allocates new storage for
all static fields of each generic type instantiation.
Java disallows static fields and methods from using any generic type vari-
ables. Type erasure would produce a single field or method using Object (or some
more specific non-generic type) on the shared runtime class. Due to type erasure,
only instance fields and methods can be type-safe for distinct type arguments from
different type instantiations.
Type Bounds — Optional bounds for generic type variables (JLS §4.4) are similar to
C# but with a different syntax. Bounds consist of one principal type (class or inter-
face) and zero ore more interface bounds, appended with & . For example, <T extends
C & I> is equivalent to C# <T> where T: C, I . This ensures that the actual type of T is
some subtype of C that also implements interface I which C itself does not imple-
ment. Interestingly, Java also allows interface bounds in cast expressions (JLS
§15.16).
Void — Just as you cannot specify primitives as generic type arguments, you also
cannot specify the keyword void . Use the Void class for any type parameter of a
generic interface that your implementing class does not use.
Wildcards — Any generic type parameter that is never referred to may be simply
specified as ? , a so-called wildcard. Wildcards can also be bounded with extends or
super . This allows co- and contravariance like C# in/out but is not limited to inter-
faces. To reference a wildcarded type argument, capture it with a separate method
declaring a named type parameter.
There’s one neat trick related to wildcards. If a container holds some generic
element type with a wildcard, e.g. the collection returned by
TableView<S>.getColumns, then you can put instances with different concrete types
for the wildcard in the same container. This is not possible in C# where different
concrete type arguments produce incompatible classes.
17. Collections
The Java Collections Framework (tutorial) is much better designed than its .NET
equivalent. Collection variables are usually typed from a rich hierarchy of interfaces.
These are nearly as powerful as their implementing classes, so the latter are only
used privately for instantiation. Hence, most collection algorithms work on any
framework-conforming collection with appropriate semantics. This includes a vari-
ety of composable wrapper methods, such as dynamic subranges and read-only
views.
Some combinations of interface methods and concrete implementations
may perform poorly, e.g. indexing a linked list. Java prefers exposing a possibly slow
operation to not exposing the operation at all, which is generally the case with
.NET’s more restrictive collection interfaces.
Iterators — Java allows mutating a collection while iterating over its elements, but
only through the current iterator. Java also features a specialized ListIterator that
can return its element index. .NET allows neither mutation nor index retrieval when
using a collection iterator.
Java SE 5 added a for-each loop equivalent to the C# foreach statement, but
without a dedicated keyword. This loop does not expose the mutation and index re-
trieval facilities of Java collection iterators. As in C#, for-each loops over arrays are
special-cased to avoid creating iterator objects.
Streams — Java SE 8 added streams & pipelines that chain method calls for cumula-
tive operations, like the method-based version of C# LINQ. Streams can be created
from regular collections, but also from generator functions or external files.
Pipelines fetch new elements only as needed, and can process them either sequen-
tially or in parallel. Lambda expressions are used to customize pipeline operations.
Finally, a terminal operation converts the result into another regular Java object or
collection.
18. Annotations
Java SE 8 allows annotating type usage in addition to type declarations. You need
external tools to benefit from such annotations, though.
19. Comments
Like C#, Java defines a standard format for code comments on classes and methods
that can be extracted as formatted HTML pages. Unlike C#, the Javadoc processor
that ships with the JDK directly performs output formatting, so you don’t need an
external formatter such as NDoc or Sandcastle.
The capabilities are similar, although Javadoc lacks a compiler-checked way
to reference parameters within comment text. The syntax is quite different and
much more concise, as Javadoc mostly relies on implicit formatting and compact @
tags. HTML tags are used only where no appropriate @ tag exists.
If you need to convert large amounts of C# XML comments to Javadoc for-
mat you should check out my Comment Converter that does most of the mechanical
translation for you.
Name
Nico Jansen − ⚑
⏲ 9 years ago
Thanks for this awesome article and references to further readings. It is helping me a lot
with learning Java as a C# developer. In general thought, Java feels like a step back if you
know C#, but a minor one at that (nothing compared to PHP).
5 0 • Reply • Share ›
Kenneth Fiduk − ⚑
KF ⏲ 9 years ago
Great article, thanks!
2 0 • Reply • Share ›
xpaulbettsx
⏲ 9 years ago
− ⚑
Imad Marmoud − ⚑
⏲ 8 years ago
Good article, thank you !
1 0 • Reply • Share ›
anon9435939 − ⚑
A ⏲ 8 years ago
Thanks for the helpful concise information. I once studied the basics of Java about 10
years ago and hated it, and then I moved to C#.
If it were not for Android, I would not have tried to learn Java again. I just wish Google
had used its own new language instead of Java saving itself from all the lawsuits and
security problems caused by Java.
4 0 • Reply • Share ›
David Holt − ⚑
⏲ 8 years ago
Thanks for a great article. I found it very helpful as I begin my journey into Java from C#.
3 0 • Reply • Share ›
steben ⚑
steben − ⚑
S ⏲ 5 years ago
Great read. Good to see an unbiased comparison between both languages.
2 0 • Reply • Share ›
Daniel − ⚑
D ⏲ 5 years ago
Great article, it really helped me in the transition of learning java after a long time i've
been programming in c#
1 0 • Reply • Share ›
Daniel
⏲ 5 years ago
− ⚑
Shimmy − ⚑
⏲ 4 years ago
C# is today a far better language than Java, especially now that it's open source and
compiles on any platform, including web. Looking at this comparison I could clearly see
how far Java is from C# in almost any given aspect.
Thanks for sharing!
2 0 • Reply • Share ›
Neither DoI
⏲ 4 years ago
− ⚑
P.s.
I think that there should be a place for IDEs comparison and an explanation of Maven.
1 0 • Reply • Share ›
muhdAlavu − ⚑
⏲ 3 years ago
most usable article for someone from c# background.
0 0 • Reply • Share ›
D thFl ⚑
DarthFloopy − ⚑
D ⏲ 3 years ago
As in C#, piecemal string concatenation can also be inefficient. Use the dedicated
StringBuilder class for better performance