|
In our case, runtime class information gives power to the programmer because he or she has more information to work with. This is a good thing. Why is introspection necessary in order for a language to be considered a "systems" language? One part of the answer is fairly mundane: Getting from "nothing" (that is, an uninitialized VM) to "something" (that is, a running Java class) requires that some part of the system be able to inspect the classes to be run so as to figure out just what to do with them. The canonical example of this problem is simply the following: "How does a program, written in a language that cannot look 'inside' another language component, begin executing the first language component, which is the starting point of execution for all other components?" Because the more advanced JavaBean customization features are only necessary for more advanced beans, we'll start with a walk-through of ScatterPlot, a bean that displays a scatter plot of data it retrieves from another object. The ScatterPlot class will (perhaps predictably) provide us with some gnarly customization problems. Fortunately, we'll have the tools at hand to solve them, armed as we are with the customization interface of the JavaBeans API. We'll overcome some of the limitations of property sheets by writing and registering a property editor for a derived type, and we'll finish by writing a complete customizer class. The Java virtual machine has hooks in it to allow a user-defined class loader to be used in place of the primordial one. Furthermore, since the user class loader gets first crack at the class name, the user is able to implement any number of interesting class repositories, not the least of which is HTTP servers -- which got Java off the ground in the first place. While implementing a standalone SOAP server, I stumbled upon multiple pitfalls associated with posting to a URL, starting with the nonintuitive design of the URL-related classes and ending with specific usability pitfalls in the URLConnection class. In fact, if you are reading this article via a Java-capable browser, class files for the simulation applet at the end of the article are flying across the Internet to your computer right now. If you'd like to listen in on them (and your computer has audio capability), push the following button: Before I give my answer, I want to point out that your life with Java will be much easier if you get into the habit of writing strictly disk location-independent code. Load resources such as property and configuration files via Class.getResource() and ResourceBundle.getBundle() as much as possible and avoid java.util.File until absolutely necessary. Not only is this very Java 2 Platform, Enterprise Edition (J2EE)-friendly, you will be surprised how much you can load from the classpath and how convenient it is. In the summer of 1994, I was working in the Java group and building what is known as a "least privilege" security model for Java. I had just finished figuring out that what I really wanted to do was to look inside a Java class, excise those pieces that were not allowed by the current privilege level, and then load the result through a custom class loader. It was then that I discovered there weren't any classes in the main run time that knew about the construction of class files. There were versions in the compiler class tree (which had to generate class files from the compiled code), but I was more interested in building something for manipulating pre-existing class files. Certainly Booch deserves the eminent status he has attained in the object-oriented community. Nonetheless, I disagree with his assessment, though in fairness I should acknowledge that he speaks from a language-independent view. Fortunately for Java developers, the Java language has taken a positive step in facilitating a clearer distinction between type and class. To explore that distinction, we will examine type and class from a Java perspective. One choice I dismiss easily: the system classloader. This classloader handles -classpath and is programmatically accessible as ClassLoader.getSystemClassLoader(). All ClassLoader.getSystemXXX() API methods are also routed through this classloader. You should rarely write code that explicitly uses any of the previous methods and instead let other classloaders delegate to the system one. Otherwise, your code will only work in simple command-line applications, when the system classloader is the last classloader created in the JVM. As soon as you move your code into an Enterprise JavaBean, a Web application, or a Java Web Start application, things are guaranteed to break. Unfortunately, some problems exist with this "class-as-object" approach. Because class methods are statically bound, your class-as-object won't enjoy the flexibility benefits of polymorphism and upcasting. (For definitions of polymorphism and dynamic binding, see the Design Techniques article, Composition versus Inheritance.) Polymorphism is made possible, and upcasting useful, by dynamic binding, but class methods aren't dynamically bound. If someone subclasses your class-as-object, they will not be able to override your class methods by declaring class methods of the same name; they will only be able to hide them. When one of these redefined class methods is invoked, the JVM will select the method implementation to execute not by the class of an object at runtime, but by the type of a variable at compile time. For his "Java In Depth" column in the October '96 issue of JavaWorld, Chuck McManis discussed in "The basics of Java class loaders." Based on that column, this Java Tip demonstrates how to successfully use the Java 1.1 class loader and goes a lot further. Thus, we just need to find the set of classes currently loaded in the JVM. This, however, is the difficult part: The java.lang.ClassLoader API is not designed to provide any information about classes loaded by a given classloader instance, and Java classloaders are not required to register themselves in any programmatically accessible way. (A Sun Microsystems-compatible JVM will also show classes as they load if you use a -verbose:class JVM option. However, you cannot access this information programmatically.) As with last month's column, "Designing fields and methods," the principles discussed may be familiar to many readers, as they apply to just about any programming language. But given the vast quantity of code I have encountered in my career that didn't benefit from these basic principles, I feel it is an important public service to address the basics in the early installments of this column. In addition, I have attempted in this article to show how the basic principles apply in particular to the Java programming language. Laszlo, an XML platform for rich internet applications, recently went open source. This platform applies an architecture that is similar to XUL and XAML to accomplish programming on the browser side. Laszlo uses Macromedia Flash as its execution platform, resulting in unsurpassed browser compatibility without requiring Macromedia Flash executables or licensing for development. Java is an OO language, which means much of the functionality of a Java application is encapsulated into cohesive classes that can be instantiated and acted upon. Nevertheless, once in a while you end up with some functions that are applicable to more than one class. These functions don't really belong to any particular class, but to a sub-system or a package. Although one can express this grouping as a class by itself (represented by interfaces), it is just simpler to collect them as static functions in a class, when one doesn't need the sophistication of service-centric approach for these methods. For example, I have a class called ServletUtils (see Example 1 below). Since the symbolic references to classes, fields, and methods is coded with String constants, the Constant Pool, which contains these String constants in the Java Class file, is the biggest portion of a class file. This is thus an easy target for APIs like the BCEL to manipulate and analyze. Patching core Java classes can help in debugging and understanding the JVM. The 'Replacing and Patching Core Java Classes' chapter discusses why you might need to patch the core classes, looks at how to patch core Java classes using the boot class path, and shows you a simple patch to java.lang.Integer. One of the common means to address this problem, coming to us from the C/C++ world, is to embed a string in a compiled object file, which then gets loaded into the compiled executable. "In the field", developers and/or tech support staff can run a utility (usually the Unix "strings" command or some variant thereof) to see the exact versions of each .C/.CPP file used to build the code the customer is currently running. Unfortunately, because Java doesn't support static linking, the same approach doesn't work for us; however, we can adapt it (through one of two ways) to provide much the same level of support. Editor's note: Sometimes, the most interesting discussions begin when someone says, "This may be a stupid question, but ..." If the person asking the question has taken the time to think about the problem before asking, the question is often not stupid at all. Uncertainty points out an ambiguity in the specs, holes in the docs, or a search for how more experienced programmers might address a particular problem. From time to time, we will publish one of the "(Not So) Stupid Questions" we receive and invite our readers to answer the question in the feedback section. This one began in a different form as a suggestion from Vladimir V. Ostromensky. He sent us some sample code, somewhat like the code we present below, with a question about String equality. We have adapted and extended his initial question. Editor's note: Sometimes the most interesting discussions begin when someone says, "This may be a stupid question, but ...." If the person asking the question has taken the time to think about the problem before asking, the question is often not stupid at all. The uncertainty points out an ambiguity in the specs, holes in the docs, or a search for how more experienced programmers might address a particular problem. From time to time, we will print one of the "(Not So) Stupid Questions" we receive and invite our readers to answer the question in the feedback section. Before tackling those problems, let's look at a built-in Java language facility that can be used to simulate much of the functionality provided by C language interposition. J2SE v1.3 has introduced a dynamic proxy class in the Java Reflection package. A dynamic proxy class implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface. Proxy classes and instances are created using static methods of the java.lang.reflect.Proxy class. The Proxy.getProxyClass method returns the java.lang.Class object for a proxy class given a class loader and an array of interfaces. The proxy class will be defined in the specified class loader and will implement all of the supplied interfaces. Each proxy class has one public constructor that takes one argument, an implementation of the java.lang.reflect.InvocationHandler interface. Rather than having to use the reflection API to access the public constructor, a proxy instance can be also be created by calling the Proxy.newProxyInstance method, which combines the actions of calling Proxy.getProxyClass and invoking the constructor with an invocation handler. Occasionally, bundles of class methods or bundles of constants Use class members to control access to objects, provide utility methods, or offer bundles of constants. Examples of controlling access to objects, static factory methods, singleton pattern. Hmm, providing access to objects seems to all fall under the category of static factory methods. It's really a tight-knit family thing. In the Java programming language, people can always use inner classes, which is a short hand of using composition, to get around multiple inheritance of implementation. If it feels more like a small family, go for the abstract class. Java allows you to separate the interface and implementation of objects by providing you with access levels to attach to the instance variables and methods of a class. The access level of an instance variable or method determines what other classes of object, if any, can access that instance variable or method. Welcome to another installment of "Under the Hood." In last month's article I discussed the Java Virtual Machine, or JVM, the abstract computer for which all Java programs are compiled. If you are unfamiliar with the JVM, you may want to read last month's article before this one. In this article I provide a glimpse into the basic structure and lifestyle of the Java class file. Create a Java application named Counter that prints out the numbers between 1 and 100, inclusive. (i.e.: 1, 2, 3, ..., 99, 100) As an example of when an interface isn't exactly what you want, use LinkMap and Map. Couldn't use Map because of RemoteException. The JVM architecture gives us a convenient way of doing this -- by working with the classloader implementation. Using classloader hooks, you can intercept the process of loading classes into the JVM and transform the class representations before they're actually loaded. To illustrate how this works, I'm first going to demonstrate intercepting the classloading directly, then show how Javassist provides a convenient shortcut that you can use in your applications. Along the way I'll make use of pieces from the prior articles in this series. I'll start classworking coverage with the Javassist bytecode manipulation library. Javassist isn't the only library for working with bytecode, but it does have one feature in particular that makes it a great starting point for experimenting with classworking: you can use Javassist to alter the bytecode of a Java class without actually needing to learn anything about bytecode or the Java virtual machine (JVM) architecture. This is a mixed blessing in some respects -- I don't generally advocate messing with technology you don't understand -- but it certainly makes bytecode manipulation much more accessible than with frameworks where you work at the level of individual instructions. When the java.lang.ref package, which includes the SoftReference, WeakReference, and PhantomReference classes, was first introduced in the Java 2 platform, its usefulness was arguably over-hyped. The classes it contains can be useful, but they have certain limitations that narrow their appeal and make their application very specific to a defined set of problems. Want to gain control over the nuances of your Web-page design without a lot of painstaking hand coding? Then start using classes with your HTML style sheets. The abstract Buffer class does not provide methods for storing or retrieving data from a buffer. Rather, that capability is provided by ByteBuffer and other subclasses of Buffer. In the Object-Oriented theory, there come situations that demand that you should declare your class in such a way that it should not be inherited. Typically, it occurs if the functionality that is provided by the class should not be changed, or more appropriately, overridden. In this article, I discuss two ways to implement this behavior in the Java language, the official way and the unofficial way. HTTP is the protocol that makes the Web click. It is generic enough to carry any type of data including HTML, raw data, image files, and XML. There are times when the resource your application needs is available from a Web site. You can see it by pointing your browser to its URL. You just need to have your Java application access it. The java.net.HttpURLConnection class will help you do just that. By encapsulating the HTTP protocol in a class, your application can interact with any resource that has an HTTP URL associated with it. This includes posting HTML forms to servlets and JSPs as if they were filled via a browser. The class ttask inherits from TimerTask and implements a single run() method. Similar to threads, the code inside of the run() method is what is executed on a scheduled basis. In our case, we simply print out a message. Another method that you can implement is cancel(), which stops the task. You only need to call this method once, because after an event is cancelled, it doesn't make sense to cancel it again. You won't get an error, however, if you repeatedly call cancel. It just won't do anything. I explained how to download, install, and test the multimedia class library in an earlier lesson titled Multimedia Programming with Java, Getting Started (see Resources). In this lesson, I taught you how to write a program that modifies its fundamental behavior at runtime by dynamically modifying, compiling, loading, and reloading classes. To lay the groundwork for the discussion of types, I will explain what we mean by variables and objects, and the similarity between the two. Chapter 4 introduced you to classes and objects. In this chapter, you learn about inheritance, another fundamental concept of object-oriented programming. The idea behind inheritance is that you can create new classes that are built on existing classes. When you inherit from an existing class, you reuse (or inherit) its methods and fields and you add new methods and fields to adapt your new class to new situations. This technique is essential in Java programming. For maximum security, both the default mechanism for loading a class and a custom class loader need to work with a security manager class that controls what actions code can perform. You'll see in detail how to configure Java platform security. Most methods and constructors have some restrictions on what values may be passed into their parameters. For example, it is not uncommon that index values must be nonnegative and object references must be non-null. You should clearly document all such restrictions and enforce them with checks at the beginning of the method body. This is a special case of the general principle, and you should attempt to detect errors as soon as possible after they occur. Failing to do so makes it less likely that an error will be detected and makes it harder to determine the source of an error once it has been detected. |
w___w_w__.___j___a_va___2___s.___c__om___ | Contact Us |
Copyright 2009 - 12 Demo Source and Support. All rights reserved. |
All other trademarks are property of their respective owners. |