类方法
一、类方法
向日期类中添加判断"闰年” 的方法,即如下所示的两种方法:
1)判断任意年份:判断任意年份(如2017年)是否是闰年;
2)判断任意日期:判断日期类的实例年份(例如,设置为2017年1O月15日的日期的年份2017年)是否是闰年;
方法1不是针对特定实例设置的,它不属于特定的实例,这一点与类变量(静态字段)相同, 适合用来实现这种处理的就是被称为类方法的静态方法;与特定的实例无关,而是与类整体相关的处理,或者与属于类的各个实例的状态无关的处理,可以实现为静态方法。
创建这两个方法,1为类方法,2为实例方法;假定这两个方法的名称都为isLeap,执行重载;之所以能对这两个方法执行重载是因为存在如下规则:
重载是定义签名不同但名称相同的方法,可以对类方法和实例方法执行重载。
1、判断任意年份(类方法,即静态方法)
静态方法版的isLeap用于判断某一年是否是闰年;由于不是对日期类的实例进行调用,因此可以将其理解为接收int型的“普通方法”,这样的方法可以加上static实现为类方法,类方法版的isLeap的定义如下所示:
//类方法--y年是闰年吗?
public static boolean isLeap(int y) {
return y%4 == 0 && y%100 != 0 || y % 400 = 0;
}
它会判断形参中接收到的y年是否是闰年
2、判断任意日期(实例方法)
非静态方法,即实例方法版的isLeap 用于判断类实例的日期的年份是否是闰年;判断的对象是方法所属的实例字段year,该方法无需接收参数,其定义如下所示:
//实例方法--自身的日期是闰年吗?
public static isLeap(int y) {
return y % 4 == 0 && y%100 != 0 || y % 400 == 0;
}
【这里的year就是实例万法isLeap所属的实例中的字段year】
这里执行的判断本质上和类方法版是一样的,程序中充满相同的代码、不利于维护。使用类方法的程序如下所示:
//实例方法--自身的日期是闰年吗?
public boolean isLeap() {
return isLeap(year);
}
程序将判断year年是否是闰年的操作委托给了类方法版的isLeap。
【在这里,实例方法调用了类方法;但反过来,类方法不可以调用实例方法】
日期类Day.java
// 日期类Day
public class Day {
private int year = 1; // 年
private int month = 1; // 月
private int date = 1; // 日
//-- y年是闰年吗?[类方法(静态方法)] --//
public static boolean isLeap(int y) {
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
}
//-- 构造函数 --//
public Day() { }
public Day(int year) { this.year = year; }
public Day(int year, int month) { this(year); this.month = month; }
public Day(int year, int month, int date) { this(year, month); this.date = date; }
public Day(Day d) { this(d.year, d.month, d.date); }
//--- 获取年、月、日 ---//
public int getYear() { return year; } // 获取年
public int getMonth() { return month; } // 获取月
public int getDay() { return date; } // 获取日
//--- 设置年、月、日 ---//
public void setYear(int year) { this.year = year; } // 设置年
public void setMonth(int month) { this.month = month; } // 设置月
public void setDate(int date) { this.date = date; } // 设置日
public void set(int year, int month, int date) { // 年月日
this.year = year; // 年
this.month = month; // 月
this.date = date; // 日
}
//-- 是闰年吗?【实例方法】 --//
public boolean isLeap() { return isLeap(year); }
//--- 计算星期 ---//
public int dayOfWeek() {
int y = year; // 0 … 星期日
int m = month; // 1 … 星期一
if (m == 1 || m == 2) { // :
y--; // 5 … 星期五
m += 12; // 6 … 星期六
}
return (y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + date) % 7;
}
//--- 与日期d相等吗 ---//
public boolean equalTo(Day d) {
return year == d.year && month == d.month && date == d.date;
}
//--- 返回字符串的表示 ---//
public String toString() {
String[] wd = {"日", "一", "二", "三", "四", "五", "六"};
return String.format("%04d年%02d月%02d日(%s)",
year, month, date, wd[dayOfWeek()]);
}
}
测试代码:DayTester.java
import java.util.Scanner;
class DayTester {
public static void main(String[] args) {
Scanner stdIn = new Scanner(System.in);
int y, m, d;
System.out.print("公历年份:");
y = stdIn.nextInt();
System.out.println("该年" +
(Day.isLeap(y) ? "是闰年。" : "不是闰年。"));
System.out.println("请输入日期。");
System.out.print("年:"); y = stdIn.nextInt();
System.out.print("月:"); m = stdIn.nextInt();
System.out.print("日:"); d = stdIn.nextInt();
Day a = new Day(y, m, d); // 读入的日期
System.out.println(a.getYear() + "年" +
(a.isLeap() ? "是闰年。" : "不是闰年。"));
}
}
输出:
1)类方法(静态方法)的调用
System.out.println("该年" + (Day.isLeap(y) ? "是闰年。" : "不是闰年。"));
上述程序调用了判断y年是否是闰年的类方法版isLeap,类方法并不是针对特定实例启动的,因此其调用形式如下:
类名 .方法名()
2)实例方法的调用
System.out.println(a.getYear() + "年" + (a.isLeap() ? "是闰年。" : "不是闰年。"));
此处调用了判断日期a是否是闰年的实例方法版isLeap,实例方法的调用形式如下:
类类型变量名 .方法名()
二、类变量和类方法
根据是静态的还是非静态的,字段和方法互相访问的情况有所不同。例如,日期类的类方法isLeap无法访问实例变量year、month、date。这是因为无法确定访问哪一个实例的year、month、date (甚至有可能不创建任何实例)
通过代码来验证一下方法和字段的访问属性:Static.java
class Static {
private static int s; // ■静态字段(类变量)
private int a; // □非静态字段(实例变量)
public static void m1() { } // ●静态方法①(类方法)
public void f1() { } // ○非静态方法①(实例方法)
//-- ●静态方法②(类方法) --//
public static void m2(int x) {
s = x; // ■可以访问静态字段
// a = x; // □不可以访问非静态字段(错误)
m1(); // ●可以调用静态方法
// f1(); // ○不可以调用非静态方法(错误)
}
//-- ○非静态方法②(实例方法) --//
public void f2(int x) {
s = x; // ■可以访问静态字段
a = x; // □可以访问非静态字段
m1(); // ●可以调用静态方法
f1(); // ○可以调用非静态方法
System.out.println("s = " + s + " a = " + a);
}
}
public class StaticTester {
public static void main(String[] args) {
Static c1 = new Static();
Static c2 = new Static();
Static.m2(5);
c1.f2(10);
c2.f2(20);
}
}
输出:
在类Static中,分别存在静态字段和非静态字段各一个、静态方法和非静态方法各两个。【注释掉的地方的访问会发生错误(不注释掉会发生编译错误)】
各个字段和方法的关系如图所示非静态字段(变量)和非静态方法(实例方法)属于实例cl和c2而静态字段(实类量)和静态方法(类方法)是独立于实例的,只有一个。
1)实例方法(非静态方法)
可以访问所有的非静态字段、静态字段、非静态方法、静态方法(例如,方法f2可以访问所有的非静态字段a、静态字段s 、非静态方法fl、静态方法m1),实例方法既可以访问“自身持有的变量/方法” ,也可以访问“大家共享的变量/方法” 。
2)类方法(静态方法)
可以访问静态字段和静态方法,但不可以访问非静态字段和非静态方法;
这是因为访问字段a和方法fl时,无法判断a和fl是属于cl还是属于c2;
类方法不具有“自身持有的变量/方法” ,只可以访问“大家共享的变量/方法” 。
计算三个整数值中最大值的程序:
// 计算3个整数值中的最大值(方法版)
import java.util.Scanner;
class Max3Method {
//--- 返回a, b, c中的最大值 ---//
static int max(int a, int b, int c) {
int max = a;
if (b > max) max = b;
if (c > max) max = c;
return max;
}
public static void main(String[] args) {
Scanner stdIn = new Scanner(System.in);
System.out.print("整数a:"); int a = stdIn.nextInt();
System.out.print("整数b:"); int b = stdIn.nextInt();
System.out.print("整数c:"); int c = stdIn.nextInt();
System.out.println("最大值是" + max(a, b, c) + "。");
}
}
输出:
1)max 必须为类方法的原因
声明中加上static的main方法是类方法(静态方法),由于类方法无法调用同一个类中的实例方法,因此max也必须加上static,声明为类方法。如果max声明为不加static的实例方法(非静态方法),那么main方法就无法进行调用了(会发生编译错误)。
2)main方法中使用max(...)进行调用的原因
类方法的调用形式为"类名.方法名(...) " ,但main方法只使用了”方法名(...) " 进行调用,像这祥省略类名,只使用方法名进行调用的原因非常简单,因为main方法和max属于同—个类。