|
|
|
||||||||||||||||||
|
Tech Tips archive
Tech TipsOctober 9, 2001WELCOME 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. HOW ARGUMENTS ARE PASSED TO JAVA METHODSSuppose 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
What happens when you run this simple program? 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 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
Then the method
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 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 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
In Java programming, it's common to say things like "a String
object is passed to method 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 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 CONVERTING C PROGRAMS TO THE JAVA PROGRAMMING LANGUAGESImagine 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.current_month = -37;
and cause havoc by doing so. Typically, when you do Java
programming, you make fields like
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
There's yet another way to write this program. The
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 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 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 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 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
67 3
What's the Java equivalent to the
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 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
IMPORTANT: Please read our Terms of Use and Privacy policies:
FEEDBACK SUBSCRIBE/UNSUBSCRIBE
- To subscribe, go to the subscriptions page, choose the newsletters you want to subscribe to and click "Update".
- ARCHIVES http://java.sun.com/jdc/TechTips/index.html
- COPYRIGHT 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 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. |