java中字段和方法区别
Java教程Java 101:Java中的类和对象介绍了Java类和对象的基础知识,包括如何声明类,通过字段描述属性,通过方法描述行为,通过构造函数初始化对象以及从类实例化对象并访问其成员。 。
该Java技巧以该介绍为基础,介绍了在Java程序中使用字段和方法的七种高级技术。 如果您准备学习字段常量,字段访问规则,链接实例方法调用,传递值参数,递归和方法调用堆栈,调用方法的规则以及实用程序类,请继续阅读。
场常数
您可以通过在其声明中包含关键字final
创建一个只读字段。 结果称为常数 。 例如, final int DAYS_IN_MONTH = 30;
final static double NORMAL_BODY_TEMP = 98.6;
声明常量DAYS_IN_MONTH
和NORMAL_BODY_TEMP
。 按照惯例,常量的名称用大写字母表示。
实例和类常量不同。 每个对象的实例常量可以看到不同的值,但是类常量为所有对象提供相同的值。 参见清单1中的区别。
清单1.实例与类常量
class Month
{
final static int NUM_MONTHS = 12;
final int DAYS_IN_MONTH;
Month(int days_in_month)
{
DAYS_IN_MONTH = days_in_month;
}
public static void main(String[] args)
{
System.out.println(Month.NUM_MONTHS);
Month feb = new Month(28);
System.out.println(feb.DAYS_IN_MONTH);
Month jul = new Month(31);
System.out.println(jul.DAYS_IN_MONTH);
}
}
Month.java
首先声明一个NUM_MONTHS
类常量,该常量已初始化为12
。 从Month
类创建的所有对象的此常量将看到相同的值。 声明常量时,必须为类常量分配一个值,并且以后不能对其进行修改。
接下来,该类声明一个DAYS_IN_MONTH
实例常量。 尽管可以将该常量初始化为声明的一部分。 而是将初始化推迟到构造函数,该构造函数是唯一可以初始化的地方。 这样的常数称为空白最终值 。 实例常量不能随后进行修改。
编译代码如下:
javac Month.java
然后运行生成的应用程序:
java Month
您应该观察以下输出:
12
28
31
现场访问规则
根据字段的类型(实例或类)和上下文(从类内部或类外部的代码)以不同的方式访问字段。 这四个规则将帮助您避免在不同上下文中访问不同种类的字段时出错:
- 从同一类中的另一个实例字段,构造函数或实例方法访问该字段时,请指定一个没有前缀的实例字段名称。 示例:
author
。 - 从同一类中的另一个实例或类字段,构造函数或实例或类方法访问此字段时,请指定一个没有前缀的类字段名称。 示例:
counter
。 - 从对象的类外部或同一类中的类方法访问此字段(前提是可以访问)时,请指定对象引用,后跟成员访问运算符,然后是实例字段名称。 例如:
book.title
。 - 从类的外部访问该字段(假设可以访问)时,请指定一个类名称,后跟成员访问运算符,然后是类字段名称。 示例:
Book.counter
。
在某些情况下, 阴影 (其中的参数或局部变量隐藏或屏蔽实例字段)是一个问题。 您可以在this.
之前解决this.
实例字段名或类名,成员访问运算符为类字段名。 例如,如果必须将参数值分配给名称相同的实例字段,则还可以在this.
字段名称。
链接实例方法调用
可以通过成员访问运算符将两个或更多实例方法调用链接在一起,从而获得更紧凑的代码。 要完成实例方法调用链接,您需要重新构造实例方法。 关键是设计您的方法以返回对当前对象的引用,该引用通过this
关键字指示。
在清单2中,您可以看到我已将链接在一起的方法的返回类型更改为类类型。 我也用过return this;
以确保每个方法将始终返回当前对象引用。
清单2.设置实例方法调用链
public class TG
{
public static void main(String[] args)
{
Turtle turtle = new Turtle().penDown();
turtle.move(10).turnLeft().move(10).turnRight().move(10).penUp();
}
}
class Turtle
{
Turtle penUp()
{
System.out.println("pen up");
return this;
}
Turtle penDown()
{
System.out.println("pen down");
return this;
}
Turtle turnLeft()
{
System.out.println("turn left");
return this;
}
Turtle turnRight()
{
System.out.println("turn right");
return this;
}
Turtle move(int numUnits)
{
System.out.println("moving " + numUnits + " units");
return this;
}
}
清单2中的源代码来自我创建的Turtle Graphics应用程序,该应用程序演示了实例方法调用链。 该应用程序由TG
主类和Turtle
帮助器类组成。 主类实例化Turtle
并将各种实例方法调用链接到此引用。
您会注意到Turtle
不包含构造函数。 我选择不声明构造函数,因为没有可初始化的东西。 如果未声明任何构造函数,则编译器将生成一个不执行任何操作的默认无参数构造函数。 声明至少一个构造函数时,编译器不会生成此构造函数。
传递值参数
方法或构造函数调用包含零个或多个传递给该方法或构造函数的参数。 Java通过pass-by-value将参数传递给方法和构造函数,后者将变量的值或另一个表达式的值传递给该元素。 值传递参数如下所示:
Library library = new Library();
Book book = new Book("Moby Dick", 1851);
library.add(book);
使用值传递时,被调用的方法或构造函数无法更改其参数。 例如, Library
的void add(Book book)
方法无法更改传递给book
参数的参数。 所以你不能这样做:
class Library
{
void add(Book book)
{
book = new Book("...", 2015);
// ...
}
}
并预期调用者的book
局部变量(以前的Book book = new Book("Moby Dick", 1851);
表达式)中的值会发生变化。 如果您确实成功更改了该参数,则JVM可能首次尝试在library.add(null);
中将新值分配为null
时崩溃library.add(null);
方法调用。
递归和方法调用栈
一个方法通常执行可能包含对其他方法的调用的语句。 您可能已经在Java 101的“ Return语句”部分中看到了这一点:Java中的类和对象 ,其中的copy()
方法称为System.in.read()
和System.out.println()
。 但是,通常有一个方法调用本身很有用。 这种编程技术称为递归 。
例如,假设您需要编写一种方法来返回阶乘 ,即所有正整数(包括一个特定整数)的乘积。 知道 ! 是阶乘的数学符号,可以猜4! 等于4x3x2x1或24。编写此方法的第一种方法可能包含以下代码:
static int factorial(int n)
{
int product = 1;
for (int i = 2; i <= n; i++)
product *= i;
return product;
}
尽管此代码通过迭代完成其任务,但可以采用递归样式来更紧凑地编写factorial()
:
static int factorial(int n)
{
if (n == 1)
return 1; // base problem
else
return n * factorial(n - 1);
}
递归方法用一个简单的术语表达了一个问题。 根据此示例,最简单的问题(也称为基本问题 )为1! (1)。 当一个大于1的参数传递给factorial()
,此方法通过使用下一个较小的参数值调用自身,将该问题分为一个更简单的问题。 最终,将解决基本问题。 例如,调用factorial(4)
导致以下表达式堆栈:
4 * factorial(3)
3 * factorial(2)
2 * factorial(1)
最后一个表达式位于堆栈的顶部。 当factorial(1)
返回1时,当堆栈开始展开时,将按以下顺序评估这些表达式:
-
2 * factorial(1)
现在变为2 * 1(2) -
3 * factorial(2)
现在变为3 * 2(6) -
4 * factorial(3)
现在变为4 * 6(24)
递归提供了一种表达许多问题的绝妙方法。 其他示例包括在基于树的数据结构中搜索特定值,并在分层文件系统中查找并输出包含特定文本的所有文件的名称。
无限递归和堆栈空间耗尽
递归会占用堆栈空间,因此请确保递归最终以基本问题结尾; 否则,您将耗尽堆栈空间,并且应用程序将被迫终止。
方法调用栈
方法调用需要一个方法调用堆栈来跟踪必须返回执行的语句。 此外,堆栈在每个方法调用的基础上跟踪参数和局部变量。 可以将方法堆栈视为自助餐厅中的一堆干净的托盘-您从堆的顶部弹出一个干净的托盘,然后洗碗机会将下一个干净的托盘推到堆的顶部。
调用方法时,JVM将被调用的方法及其参数和要在该方法上执行的第一条语句的地址推入方法调用堆栈。 JVM还为方法的参数和/或局部变量分配堆栈空间。 当方法返回时,JVM删除参数/局部变量空间,将地址和参数弹出堆栈,然后将执行转移到给定地址的语句。
调用方法规则
根据方法的类型(实例或类)和上下文(从类内部或类外部的代码)以不同的方式调用方法。 这是在各种情况下调用不同类型方法的四个规则:
- 从同一类中的另一个实例方法或构造函数调用该方法时,请指定一个没有前缀的实例方法名称。 示例:
add(book)
。 - 从另一个实例或类方法或同一类中的构造函数调用时,指定不带前缀的类方法名称。 示例:
search(values, value)
。 - 从对象的类外部或同一类的类方法(前提是可访问)中调用方法时,请指定对象引用,后跟成员访问运算符,然后是实例方法名称。 示例:
book.getTitle()
。 - 从类的外部调用方法时(如果可以访问),请指定一个类名称,后跟成员访问运算符,然后再类方法名称。 示例:
Book.showCount()
。
不要忘记确保传递给方法的参数数量(以及它们传递的顺序和这些参数的类型)与被调用方法中的参数相对应。 否则,编译器将报告错误。
实用程序类
实用程序类由静态字段和/或静态方法组成。 标准类库包含实用程序类的示例,包括Math
。 清单3给出了实用程序类的另一个示例。
清单3. Java中的实用程序类
class Utilities
{
// Prevent Utilities from being instantiated by declaring a
// private no-argument constructor.
private Utilities()
{
}
static double average(double[] values)
{
double sum = 0.0;
for (int i = 0; i < values.length; i++)
sum += values[i];
return sum / values.length;
}
static void copy() throws java.io.IOException // I'll discuss throws and
{ // exceptions in a future
while (true) // article.
{
int _byte = System.in.read();
if (_byte == -1)
return;
System.out.print((char) _byte);
}
}
static int factorial(int n)
{
if (n == 1)
return 1; // base problem
else
return n * factorial(n - 1);
}
static int search(int[] values, int srchValue)
{
for (int i = 0; i < values.length; i++)
if (values[i] == srchValue)
return i; // return index of found value
return -1; // -1 is an invalid index, so it's useful for indicating
// "value not found".
}
}
清单3中声明的Utilities
类用作Java 101中引入的大多数类方法的占位符:Java中的类和对象 。 为了防止Utilities
被实例化,我声明了一个私有的,无参数(且为空)的构造函数。
清单4展示了一个小类,演示了许多Utilities
方法。
翻译自: https://www.infoworld.com/article/2996556/java-101-primer-fields-and-methods-in-java.html
java中字段和方法区别