Scrigroup - Documente si articole

Username / Parola inexistente      

Home Documente Upload Resurse Alte limbi doc  


AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml

AspAutocadCDot netExcelFox proHtmlJava
LinuxMathcadPhotoshopPhpSqlVisual studioWindowsXml

The final keyword

java

+ Font mai mare | - Font mai mic



DOCUMENTE SIMILARE

Trimite pe Messenger
Building a Java program
Common pitfalls when using operators
Controlling cloneability: The copy-constructor
Initialization and class loading
Standard Java exceptions: The special case of RuntimeException
Windowing Components And Layout Classes
Grouping constants
Shift operators
You never need to destroy an object
JSF Fundamentals


The final keyword

The final keyword has slightly different meanings depending on the context, but in general it says ďThis cannot be changed.Ē You might want to prevent changes for two reasons: design or efficiency. Because these two reasons are quite different, itís possible to misuse the final keyword.




The following sections discuss the three places where final can be used: for data, methods and for a class.

Final data

Many programming languages have a way to tell the compiler that a piece of data is ďconstant.Ē A constant is useful for two reasons:

It can be a compile-time constant that wonít ever change.

It can be a value initialized at run-time that you donít want changed.

In the case of a compile-time constant the compiler is allowed to ďfoldĒ the constant value into any calculations in which itís used; that is, the calculation can be performed at compile time, eliminating some run-time overhead. In Java, these sorts of constants must be primitives and are expressed using the final keyword. A value must be given at the time of definition of such a constant.

A field that is both static and final has only one piece of storage that cannot be changed.

When using final with object handles rather than primitives the meaning gets a bit confusing. With a primitive, final makes the value a constant, but with an object handle, final makes the handle a constant. The handle must be initialized to an object at the point of declaration, and the handle can never be changed to point to another object. However, the object can be modified; Java does not provide a way to make any arbitrary object a constant. (You can, however, write your class so that objects have the effect of being constant.) This restriction includes arrays, which are also objects.

Hereís an example that demonstrates final fields:

//: FinalData.java

// The effect of final on fields

class Value

public class FinalData ;

public void print(String id)

public static void main(String[] args)

Since i1 and I2 are final primitives with compile-time values, they can both be used as compile-time constants and are not different in any important way. I3 is the more typical way youíll see such constants defined: public so theyíre usable outside the package, static to emphasize that thereís only one, and final to say that itís a constant. Note that final static primitives with constant initial values (that is, compile-time constants) are named with all capitals by convention. Also note that i5 cannot be known at compile time, so it is not capitalized.

Just because something is final doesnít mean that its value is known at compile-time. This is demonstrated by initializing i4 and i5 at run-time using randomly generated numbers. This portion of the example also shows the difference between making a final value static or non-static. This difference shows up only when the values are initialized at run-time, since the compile-time values are treated the same by the compiler. (And presumably optimized out of existence.) The difference is shown in the output from one run:

fd1: i4 = 15, i5 = 9

Creating new FinalData

fd1: i4 = 15, i5 = 9

fd2: i4 = 10, i5 = 9

Note that the values of i4 for fd1 and fd2 are unique, but the value for i5 is not changed by creating the second FinalData object. Thatís because itís static and is initialized once upon loading and not each time a new object is created.

The variables v1 through v4 demonstrate the meaning of a final handle. As you can see in main( ), just because v2 is final doesnít mean that you canít change its value. However, you cannot re-bind v2 to a new object, precisely because itís final. Thatís what final means for a handle. You can also see the same meaning holds true for an array, which is just another kind of handle. (There is know way that I know of to make the array handles themselves final.) Making handles final seems less useful than making primitives final.



Blank finals

Java 1.1 allows the creation of blank finals, which are fields that are declared as final but are not given an initialization value. In all cases, the blank final must be initialized before it is used, and the compiler ensures this. However, blank finals provide much more flexibility in the use of the final keyword since, for example, a final field inside a class can now be different for each object and yet it retains its immutable quality. Hereís an example:

//: BlankFinal.java

// 'Blank' final data members

class Poppet

class BlankFinal

BlankFinal(int x)

public static void main(String[] args)

Youíre forced to perform assignments to finals either with an expression at the point of definition of the field or in every constructor. This way itís guaranteed that the final field is always initialized before use.

Final arguments

Java 1.1 allows you to make arguments final by declaring them as such in the argument list. This means that inside the method you cannot change what the argument handle points to:

//: FinalArguments.java

// Using 'final' with method arguments

class Gizmo

public class FinalArguments

void without(Gizmo g)

// void f(final int i) // Can't change

// You can only read from a final primitive:

int g(final int i)

public static void main(String[] args)

Note that you can still assign a null handle to an argument thatís final without the compiler catching it, just like you can with a non-final argument.

The methods f( ) and g( ) show what happens when primitive arguments are final: you can only read the argument, but you can't change it.

Final methods

There are two reasons for final methods. The first is to put a ďlockĒ on the method to prevent any inheriting class from changing its meaning. This is done for design reasons when you want to make sure that a methodís behavior is retained during inheritance and cannot be overridden.

The second reason for final methods is efficiency. If you make a method final, you are allowing the compiler to turn any calls to that method into inline calls. When the compiler sees a final method call it can (at its discretion) skip the normal approach of inserting code to perform the method call mechanism (push arguments on the stack, hop over to the method code and execute it, hop back and clean off the stack arguments, and deal with the return value) and instead replace the method call with a copy of the actual code in the method body. This eliminates the overhead of the method call. Of course, if a method is big, then your code begins to bloat and you probably wonít see any performance gains from inlining since any improvements will be dwarfed by the amount of time spent inside the method. It is implied that the Java compiler is able to detect these situations and choose wisely whether to inline a final method. However, itís better to not trust that the compiler is able to do this and make a method final only if itís quite small or if you want to explicitly prevent overriding.



Any private methods in a class are implicitly final. Because you canít access a private method, you canít override it (the compiler gives an error message if you try). You can add the final specifier to a private method but it doesnít give that method any extra meaning.

Final classes

When you say that an entire class is final (by preceding its definition with the final keyword), you state that you donít want to inherit from this class or allow anyone else to do so. In other words, for some reason the design of your class is such that there is never a need to make any changes, or for safety or security reasons you donít want subclassing. Alternatively, you might be dealing with an efficiency issue and you want to make sure that any activity involved with objects of this class is as efficient as possible.

//: Jurassic.java

// Making an entire class final

class SmallBrain

final class Dinosaur {

int i = 7;

int j = 1;

SmallBrain x = new SmallBrain();

void f()

//! class Further extends Dinosaur

// error: Cannot extend final class 'Dinosaur'

public class Jurassic

Note that the data members can be final or not, as you choose. The same rules apply to final for data members regardless of whether the class is defined as final. Defining the class as final simply prevents inheritance Ė nothing more. However, because it prevents inheritance all methods in a final class are implicitly final, since thereís no way to override them. So the compiler has the same efficiency options as it does if you explicitly declare a method final.

You can add the final specifier to a method in a final class, but it doesnít add any meaning.

Final caution

It can seem to be sensible to make a method final while youíre designing a class. You might feel that efficiency is very important when using your class and that no one could possibly want to override your methods anyway. Sometimes this is true.

But be careful with your assumptions. In general, itís difficult to anticipate how a class can be reused, especially a general-purpose class. If you define a method as final you might prevent the possibility of reusing your class through inheritance in some other programmerís project simply because you couldnít imagine it being used that way.

The standard Java library is a good example of this. In particular, the Vector class is commonly used and might be even more useful if, in the name of efficiency, all the methods hadnít been made final. Itís easily conceivable that you might want to inherit and override with such a fundamentally useful class, but the designers somehow decided this wasnít appropriate. This is ironic for two reasons. First, Stack is inherited from Vector, which says that a Stack is a Vector, which isnít really true. Second, many of the most important methods of Vector, such as addElement( ) and elementAt( ) are synchronized, which as you will see in Chapter 14 incurs a significant performance overhead that probably wipes out any gains provided by final. This lends credence to the theory that programmers are consistently bad at guessing where optimizations should occur. Itís just too bad that such a clumsy design made it into the standard library where we must all cope with it.

Itís also interesting to note that Hashtable, another important standard library class, does not have any final methods. As mentioned elsewhere in this book, itís quite obvious that some classes were designed by completely different people than others. (Notice the brevity of the method names in Hashtable compared to those in Vector.) This is precisely the sort of thing that should not be obvious to consumers of a class library. When things are inconsistent it just makes more work for the user. Yet another paean to the value of design and code walkthroughs.






Politica de confidentialitate



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 358
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2022 . All rights reserved

Distribuie URL

Adauga cod HTML in site