Java Technology Home Page
Help PagesA-Z Index

Java Developer Connection(SM)
Technical Tips

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
Print Button

Members Only Requires login

Early Access Members Only

Downloads

Bug Database Members Only
Submit a Bug
View Database

Newsletters
Back Issues
Subscribe

Learning Centers
Articles
Bookshelf
Code Samples
New to Java
Question of the Week
Quizzes
Tech Tips
Tutorials

Forums

Technology Centers
Tech Tips archive

Tech Tips

October 9, 2001

WELCOME to the Java Developer ConnectionSM (JDC) Tech Tips, October 9, 2001. This issue covers:

These tips were developed using JavaTM 2 SDK, Standard Edition, v 1.3.

Pixel

HOW ARGUMENTS ARE PASSED TO JAVA METHODS

Suppose you're doing some Java programming, and you have a simple program like this:

    public class CallDemo1 {
        static void f(int arg1) {
            arg1 = 10;
        }
    
        public static void main(String args[]) {
            int arg1;
    
            arg1 = 5;
    
            f(arg1);
    
            System.out.println("arg1 = " + arg1);
        }
    }

In the main method, the variable arg1 is given the value 5, and then passed as an argument to the method f. This method declares a parameter of the same name, arg1, used to access the argument.

What happens when you run this simple program? The method f modifies the value of arg1, so what value gets printed, 5 or 10? It turns out that 5 is the right answer. This implies that setting arg1 to 10 in method f has no effect outside of the method.

Why does it work this way? The answer has to do with the distinction between the pass-by-value and pass-by-reference approaches to passing arguments to a method. The Java language uses pass-by-value exclusively. Before explaining what this means, let's look at an example of pass-by-reference, using C++ code:

    // CallDemo1.cpp
    
    #include <iostream>
    
    using namespace std;
    
    void f(int arg1, int& arg2)
    {
        arg1 = 10;
        arg2 = 10;
    }
    
    int main()
    {
        int arg1, arg2;
    
        arg1 = 5;
        arg2 = 5;
    
        f(arg1, arg2);
    
        cout << "arg1 = " << arg1 << " arg2 = " 
                                       << arg2 << endl;
    }

Function f has two parameters. The first parameter is a pass-by-value parameter, the second a pass-by-reference one. When you run this program, the first assignment in function f has no effect in the caller (the main function). But the second assignment does, in fact, change the value of arg2 in main. The & in int& says that arg2 is a pass-by-reference parameter. This particular example has no equivalent in Java programming.

So what does pass-by-value actually mean? To answer this, it's instructive to look at some JVM1 bytecodes that result from the following two commands:

    javac CallDemo1.java

    javap -c -classpath . CallDemo1

Here is an excerpt from the output:

    Method void f(int)
        0 bipush 10
        2 istore_0
        3 return

    Method void main(java.lang.String[])
        0 iconst_5
        1 istore_1
        2 iload_1
        3 invokestatic #2 <Method void f(int)>

In the main method, the instruction iconst_5 pushes the value 5 onto the operand stack of the Java virtual machine. This value is then stored in the second local variable (arg1, with args as the first local variable). iload_1 pushes the value of the second local variable onto the operand stack, where it will serve as an argument to the called method.

Then the method f is invoked using the invokestatic instruction. The argument value is popped from the stack, and is used to create a stack frame for the called method. This stack frame represents the local variables in f, with the method parameter (arg1) as the first of the local variables.

What this means is that the parameters in a method are copies of the argument values passed to the method. If you modify a parameter, it has no effect on the caller. You are simply changing the copy's value in the stack frame that is used to hold local variables. There is no way to "get back" at the arguments in the calling method. So assigning to arg1 in f does not change arg1 in main. Also, the arg1 variables in f and in main are unrelated to each other, except that arg1 in f starts with a copy of the value of arg1 in main. The variables occupy different locations in memory, and the fact that they have the same name is irrelevant.

By contrast, a pass-by-reference parameter is implemented by passing the memory address of the caller's argument to the called function. The argument address is copied into the parameter. The parameter contains an address that references the argument's memory location so that changes to the parameter actually change the argument value in the caller. In low-level terms, if you have the memory address of a variable, you can change the variable's value at will.

The discussion of argument passing is complicated by the fact that the term "reference" in pass-by-reference means something slightly different than the typical use of the term in Java programming. In Java, the term reference is used in the context of object references. When you pass an object reference to a method, you're not using pass-by-reference, but pass-by-value. In particular, a copy is made of the object reference argument value, and changes to the copy (through the parameter) have no effect in the caller. Let's look at a couple of examples to clarify this idea:

    class A {
        public int x;
    
        A(int x) {
            this.x = x;
        }
    
        public String toString() {
            return Integer.toString(x);
        }
    }
    
    public class CallDemo2 {
        static void f(A arg1) {
            arg1 = null;
        }
    
        public static void main(String args[]) {
            A arg1 = new A(5);
    
            f(arg1);
    
            System.out.println("arg1 = " + arg1);
        }
    }

In this example, a reference to an A object is passed to f. Setting arg1 to null in f has no effect on the caller, just as in the previous example. The value 5 gets printed. The caller passes a copy of the object reference value (arg1), not the memory address of arg1. So the called method cannot get back at arg1 and change it.

Here's another example:

    class A {
        public int x;
    
        public A(int x) {
            this.x = x;
        }
    
        public String toString() {
            return Integer.toString(x);
        }
    }
    
    public class CallDemo3 {
        static void f(A arg1) {
            arg1.x = 10;
        }
    
        public static void main(String args[]) {
            A arg1 = new A(5);
    
            f(arg1);
    
            System.out.println("arg1 = " + arg1);
        }
    }

What gets printed here is 10. How can that be? You've already seen that there's no way to change the caller's version of arg1 in the called method. But this code shows that the object referenced by arg1 can be changed. Here, the calling method and the called method have an object in common, and both methods can change the object. In this example, the object reference (arg1) is passed by value. Then a copy of it is made into the stack frame for f. But both the original and the copy are object references, and they point to a common object in memory that can be modified.

In Java programming, it's common to say things like "a String object is passed to method f" or "an array is passed to method g." Technically speaking, objects and arrays are not passed. Instead, references or addresses to them are passed. For example, if you have a Java object containing 25 integer fields, and each field is 4 bytes, then the object is approximately 100 bytes long. But when you pass this object as an argument to a method, there is no actual copy of 100 bytes. Instead, a pointer, reference, or address of the object is passed. The same object is referenced in the caller and the called method. By contrast, in a language like C++, it's possible to pass either an actual object or a pointer to the object.

What are the implications of pass-by-value? One is that when you pass objects or arrays, the calling method and the called method share the objects, and both can change the object. So you might want to employ defensive copying techniques, as described in the September 4, 2001 Tech Tip, "Making Defensive Copies of Objects"

You can fix the case above, where the called method modifies an object, by making the class immutable. An immutable class is one whose instances cannot be modified. Here's how you to do this:

    final class A {
        private final int x;
    
        public A(int x) {
            this.x = x;
        }
    
        public String toString() {
            return Integer.toString(x);
        }
    }
    
    public class CallDemo4 {
        static void f(A arg1) {
            //arg1.x = 10;
        }
    
        public static void main(String args[]) {
            A arg1 = new A(5);
    
            f(arg1);
    
            System.out.println("arg1 = " + arg1);
        }
    }

The printed result is 5. Now uncomment the modification of A in f and recompile the program. Notice that it results in a compile error. You have made A immutable, so it can't be legally modified by f.

Another implication of pass-by-value is that you can't use method parameters to return multiple values from a method, unless you pass in a mutable object reference or array, and let the method modify the object. There are other ways of returning multiple values, such as returning an array from the method, or creating a specialized class and returning an instance of it.

For more information about how arguments are passed to Java Methods, see Section 1.8.1, Invoking a Method, and section 2.6.4, Parameter Values, in "The JavaTM Programming Language Third Edition" by Arnold, Gosling, and Holmes. Also see item 13, Favor immutability, and item 24, Make defensive copies when needed, in "Effective Java Programming Language Guide" by Joshua Bloch

Pixel

CONVERTING C PROGRAMS TO THE JAVA PROGRAMMING LANGUAGES

Imagine that you have a large body of C code, and you're thinking about recoding it using Java features. What are some of the "big picture" issues that you need to consider when you do this? This tip presents a series of examples, and covers some of the issues in converting C code to the Java language. It focuses, in particular, on program and data structuring.

One preliminary note about this kind of conversion. The C and Java languages are very different. C is targeted toward systems programming and the ability to perform low-level manipulations. By comparison, the Java language takes a higher-level view, and has better facilities for writing large, well-structured, hardware-independent applications. So understand that some features in each language do not exist or are very different in the other.

Let's start by looking at a simple C program, one that contains a global variable whose value is set and then retrieved and printed:

    /* ConvDemo1.c */
    
    #include <stdio.h>
    
    int current_month;  
    /* current month as a value 1-12 */
    
    int main()
    {
        current_month = 9;
    
        printf("current month = %d\n", current_month);
    
        return 0;
    }

When you run the program, the result is:

current month = 9

There is no Java equivalent to a global variable, so how would you write a corresponding program? Here's one way of doing it:

    class Globals {
        private Globals() {}
    
        public static int current_month;
    }
    
    public class ConvDemo1 {
        public static void main(String args[]) {
            Globals.current_month = 9;
    
            System.out.println("current month = " +
                Globals.current_month);
        }
    }

The class Globals has a private constructor (so that no instances of the class can be created), and defines a public static field. Once this is done, Globals.current_month can be accessed throughout the application, just as a global variable would be in C. As is the case with other names, you might need to qualify Globals with a package name, depending on how you structure your program. And you might want to replace the 9 with a symbolic constant name like SEPTEMBER, or use an enumerated type for the month.

ConvDemo1 is a very simple program that works, but there is an immediate issue. The current_month field is public, and it's possible to say something like:

Globals.current_month = -37;

and cause havoc by doing so. Typically, when you do Java programming, you make fields like current_month private. You then control access to these private fields by means of constructors or accessor and mutator methods. Here's a program that takes this approach -- it makes the current_month field private, and defines two methods to set and get the field's value:

    class Globals {
        private Globals() {}
    
        private static int current_month;
    
        public static void setCurrentMonth(int m) {
            if (m < 1 || m > 12) {
                throw new IllegalArgumentException();
            }
            current_month = m;
        }
    
        public static int getCurrentMonth() {
            return current_month;
        }
    }
    
    public class ConvDemo2 {
        public static void main(String args[]) {
            Globals.setCurrentMonth(9);
    
            System.out.println("current month = " +
                Globals.getCurrentMonth());
        }
    }

The approach illustrated in ConvDemo2 raises a question: how far do you want to diverge from the original structure of your C program when you convert it to use Java features? There's more than one right answer.

There's yet another way to write this program. The ConvDemo2 program does not actually use Java objects. No instances of the Globals class are created. Here's an alternative, one that defines a Globals class and creates a Globals object:

    class Globals {
        private int current_month;
    
        private static final Globals globlist = 
                                         new Globals();
    
        private Globals() {
            current_month = 1;
        }
    
        public static Globals getInstance() {
            return globlist;
        }
    
        public void setCurrentMonth(int m) {
            if (m < 1 || m > 12) {
                throw new IllegalArgumentException();
            }
            current_month = m;
        }
  
        public int getCurrentMonth() {
            return current_month;
        }
    }
    
    public class ConvDemo3 {
        public static void main(String args[]) {
            Globals.getInstance().setCurrentMonth(9);
    
            System.out.println("current month = " +
              Globals.getInstance().getCurrentMonth());
        }
    }

In the ConvDemo3 example, Globals is a singleton class, that is, it's instantiated exactly once. The getInstance method is called a "static factory method." The program could have instead made globlist a public static final field, but doing it this way allows the singleton property to be broken if needed at some later time.

This approach is perhaps the most natural in Java terms. Here a set of globals is represented as an object that you manipulate in the usual object-oriented way, but it's the furthest from the original C code.

You can use a Java class to structure global functions in a similar way to what's been shown so far with global data. Classes can be used simply as packaging vehicles. Note that when you use singletons and static fields, there are some issues with serialization that you might need to consider.

Let's look at a totally different area: the Java equivalent of C unions. A union is something like a class structure, but the fields overlap in memory, and only one of a set of fields is active at a time. Usually there is a tag field that says which field is active. Here's a C program that uses a union:

    /* ConvDemo4.c */

    #include <stdio.h>
    
    enum Type {DAYS, HOURSMINUTES};
    
    struct Days {
        int num_days;
    };
    
    struct HoursMinutes {
        int num_hours;
        int num_minutes;
    };
    
    struct Time {
        enum Type tag;
        union {
            struct Days days;
            struct HoursMinutes hm;
        } u;
    };
    
    void print(struct Time ti)
    {
        int minutes;
    
        if (ti.tag == DAYS) {
            minutes = ti.u.days.num_days * 24 * 60;
        }
        else {
            minutes = ti.u.hm.num_hours * 
                              60 + ti.u.hm.num_minutes;
        }
    
        printf("number of minutes = %d\n", minutes);
    }
     
    
    int main()
    {
        struct Time ti;
    
        ti.tag = DAYS;
        ti.u.days.num_days = 10;
        print(ti);
    
        ti.tag = HOURSMINUTES;
        ti.u.hm.num_hours = 15;
        ti.u.hm.num_minutes = 59;
        print(ti);
    }

In this program, an interval of time is represented by either a number of days or by an hours/minutes object. The union within the Time structure contains an instance of each of these objects. However, the objects overlap, and only one of them is valid at any time.

When you run the program, the output is:

    number of minutes = 14400

    number of minutes = 959

How would you create an equivalent Java program? From studying the C program, it's clear that there are multiple data representations, and for each representation, you want to obtain the number of elapsed minutes from the corresponding object.

One Java approach is to define an abstract class called Time that specifies a getMinutes method. You then define concrete subclasses of Time called Days and HoursMinutes. Each subclass defines a specific data representation, and implements getMinutes accordingly.

Here's what the code looks like:

    abstract class Time {
        abstract int getMinutes();
    }
    
    class Days extends Time {
        private final int num_days;
    
        public Days(int num_days) {
            this.num_days = num_days;
        }
    
        public int getMinutes() {
            return num_days * 24 * 60;
        }
    }
    
    class HoursMinutes extends Time {
        private final int num_hours;
        private final int num_minutes;
    
        public HoursMinutes(int num_hours, int num_minutes) {
            this.num_hours = num_hours;
            this.num_minutes = num_minutes;
        }
    
        public int getMinutes() {
            return num_hours * 60 + num_minutes;
        }
    }
    
    public class ConvDemo4 {
        public static void main(String args[]) {
            Time t = new Days(10);
            System.out.println("minutes = " + t.getMinutes());
    
            t = new HoursMinutes(15, 59);
            System.out.println("minutes = " + t.getMinutes());
        }
    }

The ConvDemo4 program uses a variable of the abstract class type (Time) to hold a reference to either a Days or an HoursMinutes object. There's no tag field used with the Java code. Run-time type information is available, so that if you have a variable of type Time, you can find out what the real type is, for example, by using the instanceof operator.

So far the examples have used global variables and functions, and the Java equivalent of unions. Let's consider one final example, one where you have some type of global data object, using structures and arrays. Here's the C code:

    /* ConvDemo5.c */

    #include <stdio.h>
    
    struct A {
        int x;
        int y;
    };
    
    struct B {
        struct A a1;
        int b[5];
        struct A a2;
    };
    
    struct B b = {
        {37, 47},
        {1, 2, 3, 4, 5},
        {57, 67}
    };
    
    int main()
    {
        printf("%d %d\n", b.a2.y, b.b[2]);
    }

The ConvDemo5.c program defines a structure A, with two integer fields. It then defines a structure B that contains an A object, an array, and another A object. An instance of the B structure is initialized as global data. When you run the program, the output is:

    67 3

What's the Java equivalent to the ConvDemo5.c program? The Java language has no global variables, as mentioned earlier. Furthermore, objects and arrays are always dynamically allocated in the Java language. So the corresponding program looks like this:

    class A {
        public int x;
        public int y;
        A(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    
    class B {
        public A a1;
        public int b[];
        public A a2;
        B(A a1, int b[], A a2) {
            this.a1 = a1;
            this.b = b;
            this.a2 = a2;
        }
    }
    
    class Globals {
        private Globals() {}
        public static B b = new B(
            new A(37, 47),
            new int[]{1, 2, 3, 4, 5},
            new A(57, 67)
        );
    }
    
    public class ConvDemo5 {
        public static void main(String args[]) {
            System.out.println(Globals.b.a2.y + " " +
                Globals.b.b[2]);
        }
    }

This Java code is a simple translation of the C code. You might want to modify it as discussed earlier, for example, by making some of the fields private instead of public.

With C programming, if you say:

    struct B {
        struct A a1;
        int b[5];
        struct A a2;
    };

    struct B b;

then the b object really does contain two A objects and an array laid out together in memory. But the equivalent Java object contains three fields, with each field a reference; the actual A objects and the array are stored elsewhere in memory. A corollary to this point is that there's no Java equivalent to some of the low-level manipulations you can do with C objects. For example, there are no Java equivalents to obtaining the size of an object, figuring out the memory offset of a field, making assumptions that fields are together in memory, and so on.

The examples above illustrated several issues that you need to consider when converting C code to the Java language. There are some tradeoffs here. You really need to decide how closely the Java code should match the corresponding C, and how much you want to employ Java features to improve the robustness of your application.

For more information about Converting C Programs to the Java Language, see Section 2.2, Fields, in "The JavaTM Programming Language Third Edition" by Arnold, Gosling, and Holmes. Also see item 20, Replace unions with class hierarchies, in "Effective Java Programming Language Guide" by Joshua Bloch

Pixel

IMPORTANT: Please read our Terms of Use and Privacy policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/

FEEDBACK
Comments? Send your feedback on the JDC Tech Tips to:
[email protected]

SUBSCRIBE/UNSUBSCRIBE

- To subscribe, go to the subscriptions page, choose the newsletters you want to subscribe to and click "Update".
- To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Update".

- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://java.sun.com/jdc/TechTips/index.html

- COPYRIGHT
Copyright 2001 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright. For more information, see:

http://java.sun.com/jdc/copyright.html

This issue of the JDC Tech Tips is written by Glen McCluskey.

JDC Tech Tips
October 9, 2001

1 As used in this document, the terms "Java virtual machine" or "JVM" mean a virtual machine for the Java platform.

Sun, Sun Microsystems, Java, and Java Developer Connection are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.


Print Button
[ This page was updated: 5-Oct-2001 ]
Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary | Help Pages
For answers to common questions and further contact
information please see the java.sun.com Help Pages.
Sun Microsystems, Inc.
Copyright © 1995-2001 Sun Microsystems, Inc.
All Rights Reserved. Terms of Use. Privacy Policy.