Background

Java (and arguably other languages and platforms) ``solve'' some of the easy reuse problems:

Packaging
Objects, classes, components, packages

Portability
Bytecodes, unicode, transports

Extensibility
Subclassing, interfaces, class loaders

Safety
Virtual machine, GC, verifiers

But this seems not to make reuse in most projects much easier. One reason for this is that just about every Java project involves one or more aspects [#!kiczales97!#] of software design that become widespread when developers no longer have to wrestle so hard with the easy stuff:

Concurrency
Use of threads, locks, and monitors to support multiprocessing, and/or to improve availability in reactive systems.

Distribution
Use of RMI, CORBA, etc., to support fault tolerance, resource sharing, mobility, and communication in open systems.

Persistence
Use of serialization, JDBC, etc., to support versioning, recovery, and transactionality in systems in which object lifetimes exceed program lifetimes.

Security
Use of security managers, protection domains, and encryption to support privacy and commerce in ubiquitous computing systems.