Yes, that is weird. In order to shed some light on this behaviour, it may be helpful, first of all, to recap how the protected modifier works in the absence of the static keyword, and why it works the way it does.
If class T declares a protected member m, then T and any class belonging to the same package as T can access the member, i.e. can say t.m; the type of the reference (t) must be T or a subclass of T. Additionally, any subclass U of T outside T's package can say t.m; in this case, the type of t must be U or a subclass of U.
The final part of this statement contains an important restriction. Its motivation is succintly explained in section 3.5 of The Java Programming Language (fourth edition) by Arnold, Gosling and Holmes:
The reasoning behind the restriction is this: Each subclass inherits the contract of the superclass and expands that contract in some way. Suppose that one subclass, as part of its expanded contract, places constraints on the values of protected members of the superclass. If a different subclass could access the protected members of objects of the first subclass then it could manipulate them in a way that would break the first subclass's contract -- and this should not be permissible.
Let's try to better understand this explanation by putting your Car and BMW classes to work. The following is a modified version of Car. I replaced the carNo field with an equally protected, but non-static, color field. The class also declares an obvious public getter/setter pair.
package com.car;
import java.awt.Color;
public class Car {
protected Color color;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
}
Here is the BMW class, which at the moment does nothing but inherit Car's members.
package com.bmw;
import com.car.Car;
public class BMW extends Car {
}
Finally, let's add another subclass of Car.
package com.ferrari;
import com.car.Car;
import java.awt.Color;
public class Ferrari extends Car {
public Ferrari() {
color = Color.RED;
}
@Override
public void setColor(Color color) {
log("Nope. I'm proud of my color.");
}
...
}
As you can see, in our application Ferrari objects show an exclusive preference for a certain color (which is almost true in the real world, too). The color field is set in the constructor, and made read-only by a straightforward override of setColor(). Notice, by the way, that direct access to the color protected member is permitted here because the reference (an implicit this) is of the right type, that of the accessing subclass (Ferrari plays the role of U in the above description).
Now, suppose that BMW objects want to demonstrate their superiority over other cars, so they ask the class's programmer to be enhanced by means of a bold overtake() method. The programmer obliges.
...
public class BMW extends Car {
public void overtake(Car car) {
log("Wow! Become green with envy!");
car.setColor(Color.GREEN);
}
...
}
But, reading the application logs, the programmer soon discovers that, while this works fine with other cars, Ferrari objects stubbornly resist any insolence. He then, urged by BMW objects to find a solution, tries to by-pass the setColor() method...
...
public class BMW extends Car {
public void overtake(Car car) {
log("Wow! Become green with envy!");
car.color = Color.GREEN; // <-
}
...
}
... which is exactly what we can't do in Java. The Ferrari subclass's expanded contract places a constraint on the value of the color protected member. If the BMW subclass could directly access the color field through a Car (or Ferrari) reference, it would be able to break that contract. Java does not allow this.
So, this is why the protected modifier behaves the way it does when applied to non-static members. With protected static members, things change altogether. If the color field were static, any method inside BMW could directly access it. In your code, the BMW class accesses the carNo field without a hitch.
In the example above, the Ferrari class can restrict the possible values of the color field by overriding the instance setColor() method, which effectively amounts to changing, without violating, the superclass's contract.
Now, Java is, by design, a class-based object-oriented language, which does not have a concept of class object in the same sense as, for example, Objective-C. In Objective-C, classes are, literally, objects, and class methods (analogous, but non identical, to Java static methods) are, so to speak, instance methods of the class object -- with all the consequences of this fact: in particular, they can be overridden and used as polymorphic operations, the array class method in NSArray and NSMutableArray being an obvious example.
In Java, there is no class object -- an instance of java.lang.Class is by no means the same thing as an Objective-C class object. Static methods are, in essence, functions with an associated namespace. Most importantly, they can be inherited, but can't be overridden -- only hidden, just like static and non-static fields. (By the way, this means that invoking a static method is more efficient than calling an instance method, because not only its form, but also its implementation can be chosen at compile-time.)
But, and this is the end of the story, if static members cannot be overridden, they cannot really change the superclass's contract either. And, if they cannot change the superclass's contract, a subclass cannot break the contract of a different subclass by only accessing the latter's static members. If we recall that avoiding this kind of violations was precisely the reason of the restriction concerning protected non-static members, we can now understand why the designers of Java ended up lifting that restriction for protected static members. Once again, we can find a concise allusion to this line of thought in a short passage from section 3.5 of The Java Programming Language:
Protected static members can be accessed in any extended class... This is allowed because a subclass can't modify the contract of its static members because it can only hide them, not override them -- hence, there is no danger of another class violating that contract.