These are very different languages, particularly in this area. Let's say you have a class. In this case, we'll make it a user control, something like a textbox. Call it UIControl. Now we want to put this in another class. In this case, since we're using a UI for our example, we'll call it the CleverPanel class. Our CleverPanel instance will want to know about things happening to its UIControl instance for various reasons. How to do this?
In C#, the basic approach is to check for various Events, setting up methods that will execute
when each interesting event is triggered. In Java, which lacks events, the usual solution is to pass an object with various "event" handling methods to a UIControl method:
boolean stillNeedIt = ... ;
uiControl.whenSomethingHappens( new DoSomething() {
public void resized( Rectangle r ) { ... }
public boolean canICloseNow() { return !stillNeedIt; }
public void closed() { ... }
...
} );
So far, the difference between C# and Java is not profound. However, we have the DoSomething interface that is not needed in C#. Also, this interface might include a lot of methods that are not needed most of the time. In C#, we just don't handle that Event. In Java, we create a class that provides a null implementation for all the interface methods, DoSomethingAdapter. Now we replace DoSomething with DoSomethingAdapter and we don't need to write any methods at all for a clean compile. We end up just overriding the methods we need to make the program work right. So we end up needing an interface and using inheritance in Java to match what we did with events in C#.
This is an example, not a comprehensive discussion, but it gives the basics of why there's so much inheritance in Java as opposed to C#.
Now, why does Java work this way? Flexibility. The object passed to whenSomethingHappens could have been passed to CleverPanel from somewhere else completely. It might be something several CleverPanel instances should pass to their UIControl-like objects to aid a CleverWindow object somewhere. Or the UIControl could hand it off to one of its components.
In addition, instead of an adapter there might be a DoSomething implementation somewhere that has thousands of lines of code behind it. We could create a new instance of that and pass it. We might need to override one method. A common trick in Java is to have a large class with a method like:
public class BigClass implements DoSomething {
...many long methods...
protected int getDiameter() { return 5; }
}
Then in CleverlPanel:
uiControl.whenSomethingHappens( new BigClass() {
@Override
public int getDiameter() { return UIPanel.currentDiameter; }
} );
The open source Java Platform does a lot of this, which tends to push programmers to do more--both because they follow it as an example and simply in order to use it. I do think the basic design of the language is behind Sun's framework design and behind Java programmer's using the techniques when not using the framework.
It's real easy to create a class on the fly in Java. The class, anonymous or named, need only be referenced in one small block of code buried deep in one method. It can be created completely new or by slight modifications to a very large, existing class. (And the existing class can be top-level in it's own file, or nested in a top-level class, or defined only within a single block of code). The new class instance can have full access to all the creating object's data. And the new instance can be passed and used all over the program, representing the object that created it.
(As an aside, note that a big use of inheritance here--as in other places in Java--is simply for DRY purposes. It lets different classes reuse the same code. Note also the ease of inheritance in Java that encourages this.)
Again, this is not a comprehensive discussion; I'm just scratching the surface here. But yes, there is a startling difference in how inheritance is used between Java and C#. They are, in this respect, very different languages. It's not your imagination.