Java, J, and the .NET Framework

I l @ ve RuBoard

Java, J#, and the .NET Framework

J# includes independently developed libraries that provide the same functionality as that of the JDK 1.1.4 libraries, which makes it easier to take existing Java code and port it to .NET. If your Java application uses the Abstract Window Toolkit (AWT), JDBC or other features of the JDK 1.1.4 class libraries, you can compile your source code using the J# compiler and generate a .NET executable. However, there are a few features that Microsoft does not support, and there are some areas of overlap between the JDK 1.1.4 class libraries and the .NET Framework Class Library that might cause confusion. We'll discus these in the next few sections.

Packaging and Locating Classes

Java uses packages as containers for classes. In many implementations , a package is a directory structure held on disk or in a compressed ZIP folder. For example, in the JDK 1.1.4 under Windows, the compressed folder classes.zip contains the JDK library. Classes.zip is located in the \JDK1.1.4\lib folder by default. If you're using Windows XP, you can examine the contents of classes.zip. You'll see that the folder contains compressed folders called java, sun, and sunw. (The latter two folders contain extension classes provided by Sun Microsystems and are not strictly part of the standard JDK library.) If you open the java folder, you'll see a range of further folders corresponding to the various packages in the JDK library, as shown in Figure 3-5.

Figure 3-5. The JDK 1.1.4 class library

If you open one of these folders ”lang, for example ”you'll find all the classes in the java.lang package as well as a further folder called reflection, which contains the classes in the java.lang.reflection package.

In version 1.2 of the JDK, packages can also be held in a Java Archive (JAR) file, which is similar to a ZIP file except it contains a manifest describing the contents of the archive, which makes it quicker to search.

At run time, the class loader in the JVM uses the CLASSPATH environment variable to provide a list of locations used for resolving class references. These locations can be folders, references to ZIP files, or both. If the CLASSPATH variable does not exist, the JVM uses a default path that includes the classes.zip file in the lib subfolder, under the folder created for the JDK when it was installed. Each location in the CLASSPATH is examined in turn until the required package and class is located or the CLASSPATH is exhausted. If the class is found, the JVM will load it and use it. If not, it will generate a NoClassDefFoundError .

As you might recall, the mechanism used by the common language run ­time for locating and loading classes is quite different. For one thing, the common language runtime uses assemblies and namespaces rather than ZIP files and packages. Like Java, J# also uses the import statement, but you perform namespace resolution at compile time by specifying the assemblies being used with the /reference flag to the vjc compiler. At run time, the J# class loader uses the common language runtime mechanisms described in Chapter 2 to locate and load the correct assembly and class. J# does not use the CLASSPATH variable. The Class.forName methods , which you can use to load a class dynamically, also use the assembly search mechanisms of .NET rather than the CLASSPATH variable.

Incidentally, you'll find the Microsoft libraries that support the functionality of the JDK 1.1.4 libraries used by J# in the assembly Vjslib.dll located under \Windows\Microsoft Visual JSharp. NET\Framework\<version>. Figure 3-6 shows this assembly being examined using ILDASM.

Figure 3-6. The contents of the Vjslib.dll assembly

The Java Class Hierarchy

If you're familiar with the JDK, you know that it implements an object-oriented hierarchy of types. The same is true of the .NET Framework. There are similarities and differences between these two hierarchies, and one task that J# has to perform is provide access to both in as seamless a manner as possible. This task is complicated by name clashes . For instance, at the top of the JDK hierarchy is the generic Object type in the java.lang package. Likewise, there is an Object class at the top of the .NET Framework hierarchy in the System namespace. Also, both the JDK and .NET contain a String type, arrays, exceptions, and their own primitive types. The J# compiler must handle all of these potential conflicts.

J# Objects

In the Java language, the unqualified type Object is resolved as java.lang.Object . In the common language runtime, the unqualified type Object is resolved as System.Object . J# addresses this ambiguity by effectively making java.lang.Object an alias for System.Object , so they are both the same thing. When a J# object is compiled, any java.lang.Object methods called using it (such as toString , wait , getClass , and so on) that are not part of the System.Object class are silently redirected to the com.ms.vjsharp.lang.ObjectImpl class implemented in the vjslib.dll assembly. We won't get into the details of how this is done because they might change in the future, but suffice it to say that in J# java.lang.Object and System.Object are interchangeable. (You can even execute java.lang.Object methods against a variable explicitly declared as System.Object , and vice-versa.)

All the classes you create in J# are descended directly from System.Object , and you can assign any J# class object (as opposed to a primitive J# type) to a System.Object with impunity. Effectively, the types in Microsoft's version of the JDK are treated just like the types in any other assembly, and you can even access them from other languages. You add a reference to the Vsjlib.dll assembly, and then you can use the types in the java namespace (and any other namespaces defined in Vjslib.dll). Here's a fragment of C# that creates a Java Long variable:

 usingSystem; usingjava; classMyClass { publicstaticvoidMain(string[]args) { java.lang.Longtest=newjava.lang.Long(99); } } 
Primitive Types

The Java language distinguishes between object types (classes) and primitive types. In the JDK, all object types are instantiated using the new operator, created on the heap, and thereafter subject to the memory management implemented by the JVM. Primitive types (the numeric types, char , and boolean ) are created on the stack and come into existence automatically when execution reaches the point at which they are declared. Primitive types are removed as soon as they go out of scope and are not garbage collected in the same way that object types are.

The same is true in the common language runtime ”so far. However, in Java the primitive types are built into the JVM. In contrast, the .NET Framework Class Library defines the primitive types as structures in the System namespace. A structure is a bit like a class. It can have methods as well as data, but it is created on the stack. For example, the native integer type in the common language runtime is actually the structure System.Int32 . When you create a variable of type Integer in Visual Basic .NET, or int in C#, these are actually aliases for System.Int32 . In many ways, the primitive types of the common language runtime are more like the wrapper classes (such as java.lang.Integer ) of the JDK. In languages other than J#, the common language runtime further blurs the distinction between the primitive and object types by allowing you to directly assign a primitive to an object. For example, in C# you can use statements such as this:

 //C# intanInt=99; Objectthing=anInt; 

If you think about this from a Java perspective, it looks quite odd because in Java the primitive types and the object types do not mix. To do the equivalent in Java or J#, you would have to write this:

 //JavaandJ# intanInt=99; Objectthing=newInteger(anInt); 

What the preceding code does is use the Integer wrapper class to create an object that contains a copy of the integer value inside it. You can then assign the Integer to an Object instance. In essence, what you're doing is creating a "box" on the heap, sticking an integer value in that box, and then referencing it with the Object , as shown in Figure 3-7.

Figure 3-7. Using a wrapper

The common language runtime actually performs the same trick, but it does so silently, without you having to write any code to do it. This is called boxing . The common language runtime also performs unboxing , which allows you to assign an object to a primitive type:

 //C# intanInt=99; Objectthing=anInt;//boxing anInt=(int)thing;//unboxing 

Unboxing must use a cast, and if the object does not refer to a boxed value of the appropriate type, the common language runtime will raise an InvalidCastException .

The JVM does not perform boxing, so it should come as no surprise that it does not perform unboxing either. Instead, many of the wrapper classes that you use for boxing provide methods that allow you to gain access to the primitive data inside. For example, the Integer class has the intValue method:

 //JavaandJ# intanInt=99; Objectthing=newInteger(anInt); anInt=((Integer)thing).intValue(); 

What this all means is you cannot treat the Java primitive types in the same way as the .NET primitive types when you write J# code. Nevertheless, in the .NET world, the common language runtime performs the role of the JVM, and the J# compiler uses the same underlying Microsoft Intermediate Language (MSIL) data types used by the .NET primitive types. To see what we mean, look at this method implemented in J#:

 publicstaticdoubleCalculatePay(intnumHours,doubleratePerHour) { returnnumHours*ratePerHour; } 

The MSIL code for the CalculatePay method looks like this:

 .methodpublichidebysigstaticfloat64CalculatePay(int32numHours, float64ratePerHour)cilmanaged { }//endofmethodJavaCalcPay::CalculatePay 

Notice how the Java double type is compiled into an MSIL float64 , and a Java int becomes an int32 . Suppose you write the equivalent method in another language that uses the .NET types (Visual Basic .NET, for example):

 PublicFunctionCalculatePay(numHoursasInteger,_ ratePerHourasDouble)asDouble ReturnnumHours*ratePerHour EndFunction 

Here is the MSIL code for this version:

 .methodpublicstaticfloat64CalculatePay(int32numHours, float64ratePerHour)cilmanaged { }//endofmethodVBCalcPay::CalculatePay 

You can see that the Visual Basic Integer (which is actually a System.Int32 ) is also compiled into an int32 , and the Visual Basic Double ( System.Double ) becomes a float64 . So although the .NET and Java primitive types are different when you're writing code (J# retains the semantics of Java primitive types), by the time your code has been compiled, they are interchangeable. You can call a method written in J# from Visual Basic and pass parameters whose underlying types are the same in both languages.

Value Types

The primitive types in .NET are examples of value types . In .NET, a value type is a type that is created on the stack rather than the heap. The most common value types used by .NET are structures and enumerations. The .NET Framework Class Library contains numerous value types, and if you're writing programs in C#, Visual Basic .NET, or C++, you can also define your own. Java and J# do not have value types, and you cannot create them using J#. However, you can employ existing value types from J#. With the exception of the primitive types, the J# compiler performs automatic boxing and unboxing conversions when you access them. You just use them as if they were ordinary Java types.

One common example of a value type in .NET is the enumeration. An enumeration is a set of named constants, usually applied to a specific domain. For example, the DayOfWeek enumeration in the System namespace defines constants representing each day of the week. You can create a variable whose type is DayOfWeek , assign it one of the defined constant values, and read it back. You can also compare a DayOfWeek variable against the constant DayOfWeek values. The following code creates a variable called day , assigns it the value Monday , and then prints it out. This is all valid J# code.

 System.DayOfWeekday=newSystem.DayOfWeek(); day=System.DayOfWeek.Monday; System.out.println("Thedayis " +day.ToString()); 

Internally, enumeration constants are actually integer values, and you can cast an enumeration constant to and from a Java int . You can even perform arithmetic on them, but be careful not to go out of range ”if you subtract 1 from a variable holding DayOfWeek.Sunday (which has the value 0), you'll end up with -1 , which doesn't stand for any day of the week.

Note

Although you can create local variables using the value types defined in the .NET Framework Class Library, J# does not currently allow you to use value types such as System.Int32 or System.Int64 in any definitions that generate metadata (method signatures, fields, and properties). Instead, you should use the corresponding equivalent primitive types ( int , long , and so on).


Strings

The .NET Framework defines a String type in the System namespace. There is also a String type in the java.lang package in the JDK. The situation is similar to that of the Object types, and the solution to any ambiguity is equally similar. When you use J#, System.String and java.lang.String are interchangeable. You can assign a System.String to a java.lang.String , and you can execute System.String and java.lang.String methods against objects of either type.

Arrays

Arrays in Java are first-class objects that descend from java.lang.Object . As such, each array has a type, fields, and methods. The class for a given Java array is actually generated by the Java compiler. Programmers tend not to concern themselves with such details, but if you're curious , you can see the name of the class by printing this array:

 int[]data=newint[5];//Couldbeanysizeforthisdemo System.out.println(data); 

The results will vary. Using the J# compiler, the data array was called [I@1f . Using the JDK 1.1.4 compiler from Sun, the data array was called [I@273d3c . These names will vary.

Array Class Names in Java

The class name that Java generates for an array comprises a [ for each dimension, followed by a letter or a string indicating the type of the array elements, followed by an @ sign, and then a unique hexadecimal integer. The name [I@5b indicates that the array has one dimension and contains integers. An array of floats would start with the prefix [F@ , and a two-dimensional array of long s would start with the prefix [[J@ . Why J ? Well, L is used if the array contains nonprimitive types, and J is the next available letter. For example, a one-dimensional array of Object s has the prefix [Ljava.lang.Object;@ .

The .NET Framework also contains arrays. .NET has the Array class defined in the System namespace, and every non-J# .NET array inherits from System.Array . The .NET arrays have their own properties and methods, which are different from but sometimes similar to those of Java arrays. For example, Java arrays have a length field that you can examine to determine the length of the array, and .NET arrays have a Length property. (Note the uppercase L). What this means is that J# arrays and arrays used by the other .NET languages are not the same. Happily, this is only of minor concern. First, you cannot create System.Array objects directly in J#, so there is little scope for ambiguity. Second, the J# compiler will freely convert from Java arrays to .NET arrays and back again. You can pass arrays from your C# code as parameters to methods written in J#, and vice-versa.

Java allows you to create multidimensional arrays. In the following example, variable i is a two-dimensional array of int elements:

 int[][]i; 

Strictly speaking, variable i is an array of arrays of int elements. The Java language permits such arrays to be ragged ”the second dimension can vary in length. For example, in the data array shown below, the second dimension comprises five elements in the first row of the array and three elements in the second row:

 int[][]someData={{1,2,3,4,5},{6,7,8}}; 

The .NET Framework supports jagged arrays (notice the different nomenclature ” jagged rather than ragged ), which are essentially the same, and the J# compiler will convert Java ragged arrays into .NET jagged arrays and back again. However ”and this is a big however ”jagged arrays are not part of the Common Language Specification (CLS), and some languages, including Visual Basic .NET, do not have them! Instead, the CLS mandates rectangular arrays, where each dimension length is fixed and therefore cannot vary once it has been defined. In C#, a 5-by-3 two-dimensional rectangular array is declared using the following syntax:

 int[,]moreData=newint[5,3]; 

Rectangular arrays are different beasts from jagged arrays, and you cannot assign a two-dimensional jagged array to a two-dimensional rectangular array (not even in C#). This posed a slight problem for J#. The Java language has only ragged arrays, which meant that J# would not be able to use any of the methods in the .NET Framework Class Library that passed or returned multidimensional arrays (because they're all rectangular, for CLS compliance). So Microsoft bit the bullet and added rectangular arrays to J#.

Exceptions

Both Java and .NET allow you to define, throw, and catch exceptions. Java exceptions descend from the generic Exception class in the java.lang package, but .NET exceptions descend from System.Exception . Java exceptions and .NET exceptions are different types that expose a similar functionality. To make interoperability with Java possible, an unqualified Exception refers to the java.lang.Exception class:

 try { } catch(Exceptione)//defaultstojava.lang.Exception { } 

If you want to catch the .NET Exception class instead, you must qualify the Exception object:

 try { } catch(System.Exceptione) { } 

One result of the way in which the Exception classes are defined with J# is that System.Exception will mask and catch java.lang.Exception (and consequently any other Java exceptions). However, the converse is not true ” java.lang.Exception will not catch System.Exception or any other .NET exceptions. So if your application throws a System.Exception or any of the other .NET exceptions, using a catch(Exception e) statement will not actually catch them. (You'll most likely get an error when you compile your code, informing you that possible exceptions have not been caught.) This has significant implications if you're using the .NET Framework Class Library with J# because many of the methods raise .NET exceptions. You should be prepared to handle them.

Interfaces

Java has interfaces, and .NET has interfaces. A J# class can implement a .NET interface, and a class written using one of the other .NET languages can implement a J# interface. An example C# interface is shown here:

 //C# publicinterfaceCBase { voiddoProcessing(); } 

The J# JChild class that follows can implement this interface and even add a throws clause if the method can raise an exception:

 //J# publicclassJChildimplementsCBase { publicvoiddoProcessing()throwsException { thrownewException("Processingfailed"); } } 

The important point to remember when you use interfaces is that the specified methods must use types that are available in the implementing languages. (Trying to implement a C++ interface that uses pointer types in J# could be tricky, for example.) If you stick to the types in the CLS, you should not have any problems.

You might need to perform a bit of translation when you implement interfaces that define properties. For example, the following interface is valid in C#:

 //C# interfaceCSharpInterface { intdata { get; set; } } 

An implementation in J# would look like this:

 publicclassJSharpPropertiesimplementsCSharpInterface { /**@property*/ intget_data() { } /**@property*/ voidset_data(intvalue) { } } 

Other Issues

You should be aware of some other issues that will affect the way that your J# programs interact with the .NET Framework, as described in the following sections.

Methods with Variable Numbers of Arguments

The CLS allows you to create methods that take variable numbers of arguments (as opposed to creating overloaded methods). The following C# method allows the caller to pass in a variable number of int parameters. The params keyword indicates that the array parameter that follows can be interpreted as a list of int elements by the compiler. If you are a C or C++ developer, you might be familiar with varargs , which performs a similar function:

 //C# publicvoidUseParams(paramsint[]list) { for(inti=0;i<list.Length;i++) { //processelementi } } 

If you want to pass a selection of different types, you can create a params array comprising Object elements. You can invoke this method from C# using statements such as this one:

 //C# UseParams(99,100,120); UseParams(5); 

At run time, the actual parameters will be packaged into an int array and passed to the UseParams method as the formal parameter list. However, if you try calling the method from J# using the same syntax, you'll receive a compile-time error because the Java language does not permit methods that take variable parameter lists. Instead, you must manually package the parameters into an array yourself, as shown here:

 //J# int[]i={99,100,120}; UseParams(i); 
Console I/O

The JDK provides the System.out PrintStream object, which has the method println for displaying information on the console. The println method is overloaded, which allows you to output a single value of any type ”most often a java.lang.String . The .NET Framework contains the Console object in the System namespace, which fulfills a similar role. It has a method called WriteLine , which you can use to display information to the screen. You can print Java strings, objects, and primitive types, as shown here. (Objects will be rendered using the ToString method inherited from System.Object .)

 importSystem.*;//ConsoleisintheSystemnamespace Stringmessage= "Hello,World"; Console.WriteLine(message); 

You might find it more convenient to use Console.WriteLine rather than System.out.println when you develop J# programs.

Getting keyboard input is notoriously troublesome in Java. The System.in InputStream object supplies a read method that you can use to read individual bytes from the keyboard, but it is all very low-level and error-prone . In contrast, the System.Console class in the .NET Framework Class Library contains the ReadLine method, which reads a System.String from the keyboard, up to the next carriage return. This method is easier to use, as this code shows:

 importSystem.*; System.Stringinput=Console.ReadLine(); 
The ubyte Type

Microsoft has tried hard to not make too many additions to the Java language in J#. You've already seen that rectangular arrays were added to maintain compliance with the CLS. One data type in the CLS that is used by a number of methods in the .NET Framework Class Library is Byte , which can hold 8-bit unsigned values. Java also has a byte data type, but it is signed. (.NET has the SByte data type for signed 8-bit values.) Microsoft therefore created the data type ubyte for J#, for 8-bit unsigned data. Try not to get too confused ”a .NET SByte corresponds to a J# byte , and a .NET Byte corresponds to a J# ubyte !

Threads

The JDK defines the Thread class in the java.lang package. Classes that extend Thread and override the run method can be executed asynchronously. If an object instantiates a Thread object and calls the start method, the JVM will invoke the Thread object's run method and return to the calling object without waiting for run to finish. Both the calling object and the Thread object can then execute concurrently. The Thread class also provides the stop method, which can halt a Thread object in its tracks, and the join method, which causes the creating object to wait for the Thread object to complete. Classes can define methods as synchronized , which prevents two concurrent threads from executing the same code in the same object at the same time ”the second thread will have to wait for the first to exit the method.

The following classes illustrate a simple Thread class called WorkerThread . The run method prints the values 0 through 499. The ThreadRunner class creates a new WorkerThread and invokes its run method asynchronously by executing the start method. The ThreadRunner then prints a farewell message and finishes.

JDK Threads
 classWorkerThreadextendsThread { publicvoidrun() { for(inti=0;i<500;i++) System.out.print(i+ " "); } } classThreadRunner { publicstaticvoidmain(String[]args) { WorkerThreadwt=newWorkerThread(); wt.start(); System.out.println("ThreadRunnerfinished"); } } 

You can compile and run this program using the JDK or J# ”it will work with either. On our uniprocessor machine, both the JDK and the J# implementations cause the "ThreadRunner finished" message to be displayed before the numbers 0 to 499 are printed. The results you get when you run the program might not be the same, especially if you have a multiprocessor machine.

Bear in mind that when you use JDK threads, the JVM implements the threading model itself. Many operating systems, including Windows, have native support for threads, and the JVM might use this facility behind the scenes, but it will use its own data structures internally for managing them. The reason for this, once again, is portability. Any Java application can use threads whether the underlying platform supports them or not, and all implementations of the JDK must provide a consistent implementation of the Thread class.

Windows has its own multithreading capability, and the common language runtime permits managed access to it through the System.Threading namespace. This namespace is feature-rich compared to the JDK, and it exposes the System.Threading.Thread class, which you can use from J#. To some extent, common language runtime threads and JDK threads will interoperate ”they can be coordinated with each other using either synchronized methods or common language runtime Monitor objects. However, the System.Threading.Thread class is different from java.lang.Thread and they are not interchangeable. A common language runtime thread cannot be a member of a JDK ThreadGroup , for example. The main reason that the java.lang.Thread class exists is to support existing JDK code that you don't want to rewrite. If you're undertaking new development that involves threads, you should use the System.Threading namespace in preference to the java.lang.Thread .

You have probably gathered that threads are another area in which there's a namespace clash . By default, the J# compiler assumes that an unqualified Thread reference refers to a java.lang.Thread . If you want to use a common language runtime thread, you must qualify it as a System.Threading.Thread . Threads under the common language runtime will be explained in detail in Chapter 8.

Omissions from the JDK

Microsoft has implemented much of the JDK 1.1.4 functionality. The omissions include RMI, which we already mentioned, and there is also no support for the Java Native Interface (JNI) or for applets. However, .NET, together with Windows, does supply eminently suitable alternatives. Instead of RMI, you can use .NET Remoting or COM. JNI has been replaced by J/Direct (originally introduced with Visual J++) and the Platform Invoke service, and applets have been superseded by ASP.NET Web Forms.

Migrating to J#

Microsoft supplies some useful tools with the Visual Studio.NET IDE to help you migrate existing JDK and Visual J++ 6.0 applications to J#. You can migrate the source code, or you can convert class files. We'll look at these possibilities next.

Migrating Source Code

By now, you should appreciate that you can compile ordinary .java source code using the jc compiler to produce DLLs and EXEs. The compiler can take source code that conforms to the Java Language Specification version 2.0. If you have existing J++ 6.0 solutions, you can open them in Visual Studio .NET if you have Visual J# installed. Solution and project files will be upgraded automatically (the Visual J# Upgrade Wizard takes you through the process), but you should be aware of a few issues:

  • The CLASSPATH variable is no longer used. Instead, you must add references to each assembly that implements the packages your application uses. This means you must first have upgraded those packages and converted them into assemblies.

  • References to COM components are not preserved automatically. You must add them manually to the Visual J# project either during the upgrade process (the wizard will prompt you) or using the Add Reference command on the Project menu.

  • The project upgrade process ignores Microsoft Visual SourceSafe (VSS) settings. You must check out all source files from VSS before upgrading.

  • Not all deployment options are preserved, and neither are any prebuild or postbuild steps.

The compiler understands many of the directives Microsoft introduced with J++, such as @dll , @com , @conditional , and #if . This means you can quickly migrate much of your code that uses J/Direct for accessing native APIs and that implements COM components using JActiveX wrappers. (For more information about using J/Direct and migrating COM components, see Part IV of this book.) Also, alongside the JDK libraries, Microsoft has also implemented some of the extension packages that ship with J++, most notably com.ms.com, com.ms.dll, and com.ms.win32. The class libraries include an implementation of the JDBC-ODBC bridge driver in com.ms.jdbc.odbc. This driver requires an ODBC 3.0 or later driver. These packages are all held in the Vjslib.dll assembly. You'll also find an implementation of the WFC libraries in the Vjswfc.dll assembly.

The Profiler, Heap Monitoring, and Debug APIs that were provided with J++ have been omitted from J#. You should use the corresponding .NET Framework Class Library APIs instead.

Converting Class Files

If you do not have the source code for a given set of Java classes available, you can still migrate an application to .NET. Microsoft's Java Binary Converter tool, jbimp, converts .class files into MSIL assemblies ”DLLs and EXEs. The simplest way to use jbimp is to run it from the command line. You'll find jbimp.exe in the folder \Program Files\Microsoft Visual J# .NET\Framework\Bin. If you're migrating several class files, you can combine them into a single DLL using the /target:library switch, which is similar to that used by the vjc compiler. The name of the DLL is taken from the first class file supplied on the command line, or you can specify a name using the /out parameter.

Let's take the OvenBean, TemperatureEvent , TemperatureEventListener , and TemperatureWarning classes used by the components example earlier in this chapter. (These files are in the JDK OvenBean folder.) Pretend that you've lost the source code and have only the .class files created as a result of compiling using the JDK. You can convert them into a .NET DLL using the following command:

 jbimp/target:library/out:OvenBean.dllOvenBean.class TemperatureEvent.classTemperatureEventListener.classTemperatureWarning.class 

You can build an EXE using the /target:exe parameter (the default), as long as one of the classes has a main method. You can also use the /reference parameter to link to another .NET assembly. Continuing with the OvenBean example, you can convert the test client ( BeanClient.class ) and link it to the OvenBean.dll assembly by using the following command:

 jbimp/reference:OvenBean.dllBeanClient.class 

Running this command will result in a .NET executable called BeanClient.exe. (You can use the /out parameter to generate an executable with a different name.)

The jbimp tool has a number of other options available, including /recurse , which will recursively process .class files in subdirectories. This option is useful if you have an entire package that you need to convert.

I l @ ve RuBoard


Microsoft Visual J# .NET (Core Reference)
Microsoft Visual J# .NET (Core Reference) (Pro-Developer)
ISBN: 0735615500
EAN: 2147483647
Year: 2002
Pages: 128

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net