学习目标:类属性和方法的定义和访问。
成员变量
在定义类时,经常需要抽象出它的属性,并定义在类的主体中,在类中定义的属性也称为类的成员变量。
成员变量是抽象出来的类属性,成员变量一般不能直接被外部访问或修改,需要通过get和set方法来访问或修改变量,成员变量一般用private关键字修饰,表明为私有成员,禁止外部直接访问。private关键字是权限修饰符(关于权限修饰符的说明后面的课会介绍),声明成员变量时,如果不添加权限修饰符,默认访问权限是default权限。
成员变量的作用范围为整个类文件,作用范围就是成员变量的有效工作范围,在整个类文件中,成员变量都可以被访问和使用。
例如:
public class Fruits {
//水果类汁液含量属性
private String water;
//水果类糖分含量属性
private String sugar;
//水果类芳香度属性
private String fragrance;
//水果类的构造方法
Fruits(String inwater,String sugar,String fragrance)
{
this.water = inwater;
this.sugar = sugar;
this.fragrance = fragrance;
}
// 水果类的开花行为
public void flower()
{
System.out.println("开花");
}
// 水果类的成熟行为
public void ripe()
{
System.out.println("成熟");
}
// 水果类的落果行为
public void drop ()
{
System.out.println("落果");
}
}
Fruits类有三个成员变量,分别是water(汁液含量)、sugar(糖分含量)、fragrance(芳香度),这三个变量被修饰为private,禁止外部访问和修改。外部如果要访问和修改类属性,需要调用类提供的get和set方法,如getWater()方法可以获取water变量的值,setWater(String water)方法可以设置water变量的值,另外两个变量,依次类推。
对于成员变量,Java提供了三种初始化方式。
(1)使用默认值初始化
Java为声明的成员变量提供了默认初始化机制,当声明成员变量时,即使没有显式赋值,Java也会为以下类型的成员变量提供默认值:
(2)使用显式值初始化
声明成员变量的同时,进行赋值。
例如:
public class Circle {
//定义半径属性
private double r = 1.5;
}
(3)使用类构造方法初始化
通过类构造方法来初始化成员变量(类构造方法后面介绍)。
类成员变量的get和set方法可以由IDEA自动创建,不需要程序员手动编码。具体方法如下:
(1)在代码编辑窗口,单击鼠标右键,在弹出的菜单中选择【Generate…】命令或按下组合键Alt+Insert;
(2)在弹出的对话框中选择【Getter】或【Setter】命令,【Getter】命令创建获取属性的方法,【Setter】命令创建为属性赋值的方法;
(3)在弹出的创建属性方法对话框中,选择全部属性或需要创建get和set方法的属性,单击【OK】按钮即可。

下面的代码已经添加了类成员变量的get和set方法:
package unit;
public class Fruits {
//水果类汁液含量属性
private String water;
//水果类糖分含量属性
private String sugar;
//水果类芳香度属性
private String fragrance;
//水果类的构造方法
Fruits(String inwater,String sugar,String fragrance)
{
this.water = inwater;
this.sugar = sugar;
this.fragrance = fragrance;
}
// 水果类的开花行为
public void flower()
{
System.out.println("开花");
}
// 水果类的成熟行为
public void ripe()
{
System.out.println("成熟");
}
// 水果类的落果行为
public void drop ()
{
System.out.println("落果");
}
public String getWater() {
return water;
}
public String getSugar() {
return sugar;
}
public void setWater(String water) {
this.water = water;
}
public void setSugar(String sugar) {
this.sugar = sugar;
}
public void setFragrance(String fragrance) {
this.fragrance = fragrance;
}
public String getFragrance() {
return fragrance;
}
}
成员方法
类方法是类行为的抽象,如同类属性是类特征的抽象。类方法是由多条语句组成的语句块,并完成类的某一项功能,语句块用{}括起来。
案例2:创建一个Person类。
public class Person {
//声明名称属性,并被初始化为"张三"
private String name = "张三";
//声明年龄属性,并被初始化为22
private int age = 22;
//定义一个方法,public是修饰符,void表示没有返回值
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
案例代码见PUni5项目unit包下Person类。
代码中的tell()方法输出人的名称和年龄属性。
从上面的代码可以看出,方法的定义由权限修饰符、方法返回值类型、方法名称、参数、方法体组成。
方法的权限修饰符可以省略,默认的是default修饰符,该修饰符的作用是只要其他类和该类在一个包里,就可以用它的实例调用该方法。
方法返回值类型是方法返回值的数据类型,例如例子中的getAge()方法返回数据类型为int的年龄属性。tell()方法没有返回值,因此用void表示无返回值。
方法名称是方法的名字,类实例对象调用该方法时,需要使用该名称。
例如:
Person p = new Person();
p.tell();
方法名称要遵守命名规范,方法名称首单词全部小写,如果一个方法由多个单词构成,那么从第二个单词开始首字母大写,不要使用连接符。
定义方法时,方法名后面括号内的参数,称为形式参数(简称形参),参数之间用逗号隔开。方法的参数是方法执行时用来接收数据的,调用方法的语句负责传入数据。
参数就像是占位符。当方法被调用时,调用方向参数传递一个值,这个值被称为实际参数。参数列表包括方法参数的类型、顺序、数量。参数是可选的,也就是说,方法可能不包含参数
根据方法是否包含参数,方法可以分为有参方法和无参方法。
无参方法定义的语法为:
public 返回值类型 方法名() {}
例如例子中的tell()方法。
有参方法定义的语法为:
public 返回值类型 方法名(参数列表) {}
例如例子中的setName(String name)方法和setAge(int age)方法。
在实际编程中,不但自己定义类方法,也大量使用Java类库或第三方类库提供的类方法。
自定义的类方法是在类中为了解决某个问题而编写的一段功能代码片段,例如上面例子的tell()以及类属性的get和set方法都属于自定义方法。
Java类库或第三方类库提供的类方法,可以帮助我们完成很多程序功能,提高开发速度和质量,例如上面例子的System.out.println(“”)方法就为我们提供了输出字符串到控制台的功能。
通过查阅JDK API,使用import语句将类引入到程序中并实例化类,最后通过对象名.方法名.(参数)的形式来使用
静态变量和静态方法
Java虚拟机运行Java程序时,会加载类到内存(类并没有被实例化),此时因为类没有被实例化,还不能访问类的成员变量和成员方法,但在类中声明的静态变量和静态方法是可以访问的,因为静态变量和静态方法在类加载时就完成了内存的分配。
在类中声明静态变量和静态方法,需要使用static关键字,被static关键字修饰的变量和方法称为静态成员。
声明类静态变量的语法如下:
static datatype variable;
其中,static是静态关键字,datatype是数据类型,variable是变量名称。
声明类静态方法的语法如下:
static datatype method();
其中,static是静态关键字,datatype是方法返回数据类型,method是方法名称。
案例3:在类中声明一个静态变量和一个静态方法。
在Punit5项目unit包下创建StaticSample类。代码如下:
package unit;
public class StaticSample {
// 声明一个静态变量
static double PI = 3.14;
// 声明一个静态方法
static double getArea(double r)
{
return PI * r * r;
}
}
程序声明了两个静态成员:PI是静态变量,并赋值为3.14;getArea()方法是静态方法,形参r是圆的半径,该方法用于计算并返回圆的面积。
在Punit5项目unit包下创建StaticSampleTest类。代码如下:
package unit;
public class StaticSampleTest {
public static void main(String[] args) {
// 计算圆的面积
double dArea = StaticSample.getArea(5.0);
System.out.println("圆的面积:" + dArea);
//输出StaticSample类的静态变量PI
System.out.println("PI = " + StaticSample.PI);
}
}
程序使用StaticSample类的静态方法getArea来计算圆的面积。因为StaticSample类和StaticSampleTest类在同一个包内,因此StaticSampleTest类不需要使用import语句来导入StaticSample类。
StaticSample类的getArea()方法是静态方法,无需实例化StaticSample类,直接使用StaticSample类名就可以调用getArea()方法。
StaticSample类的PI是静态变量,可以使用StaticSample类名来访问静态变量PI。
权限修饰符
权限修饰符主要是用于限定外部访问类、类属性和类方法的访问权限。权限修饰符有default、public、private、protect,这些修饰符的访问权限如下表所示。
在上面的访问权限规则表中。
default是默认访问权限,当我们在类中声明成员变量或成员方法时,如果不添加访问权限修饰符,默认的就是default权限。default权限允许同一个类、以及同一个包中的类、同包子类访问成员变量和方法。
public权限是公有权限,它允许同一个类、同一包下的类、同包子类、其它包的类都可以访问成员变量和方法。
private权限是私有权限,它只允许同一个类来访问成员变量和方法。
protect权限主要用于子类访问权限的控制,它允许同一个类、同一个包的类和同包子类访问成员变量和方法。
构造方法
类被实例化时会自动调用类的构造方法,构造方法需满足如下要求:
(1)方法名必须与类名称完全相匹配;
(2)构造方法不需要返回类型;
(3)构造方法不能被static、final等关键字修饰,且不能有return返回语句。
下面的代码使用new运算符实例化Fruits类,apple是Fruits类的实例化对象。
Fruits apple = new Fruits("80%”, “60%”, ”30%”);
使用new运算符实例化Fruits对象时,系统首先为要创建的对象分配内存空间,然后调用类的构造方法初始化类成员属性或类成员变量,最后返回对象实例的引用给apple。
构造方法也分为有参和无参,Fruits类的构造方法属于有参构造方法,调用有参构造方法时,需要传入相对应的参数。无参构造方法则无需传入参数。下面分别讨论无参和有参构造方法。
(1)无参构造方法
无参构造方法又分为隐式无参构造方法和显式无参构造方法。
隐式无参构造方法
在定义类的时候,如果没有给类定义一个构造方法,Java编译器在编译时会默认提供一个隐式的构造方法,它没有任何参数,并且有一个空的方法体。
例如,PersonSample类中隐含一个无参构造方法,在main方法中可以通过new关键字来调用此默认的构造方法,PersonSample类代码如下:
package unit;
public class PersonSample {
//声明名称属性,并被初始化为"张三"
private String name = "张三";
//声明年龄属性,并被初始化为22
private int age = 22;
//定义一个方法,public是修饰符,void表示没有返回值
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
PersonSample代码见Punit5项目unit包下PersonSample类。
虽然PersonSample类没有提供显示的构造方法,使用new运算符仍然可以实例化PersonSample类为person对象,这是因为Java编译器在编译时会默认提供一个隐式的构造方法。
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建person对象,调用系统提供的一个隐式无参构造方法
PersonSample person = new PersonSample ();
person.tell();
}
显式无参构造方法
隐式无参构造方法是由系统提供的,无法修改其内容。但有时需要在调用这个构造方法时初始化类成员属性或执行一些操作,要实现这个需求,就需要为它定义一个显式无参构造方法,修改上述代码如下:
ModifyPersonSample代码见Punit5项目unit包下ModifyPersonSample类。
该类提供了显示的构造方法,在构造方法中对变量成员name和age进行初始化操作。使用new运算符实例化类为person对象时,构造方法被调用,person对象的成员变量被初始化。
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建person对象,调用系统提供的一个隐式无参构造方法
ModifyPersonSample person = new ModifyPersonSample ();
person.tell();
}
}
(2)显示有参构造方法
构造并初始化对象是构造方法的作用,所以有时需要给这个方法传入一些参数,定义一个显示的有参构造方法。例如:
//定义一个显示有参构造方法
public ModifyPersonSample (String instrName,int innAge)
{
this.name = instrName;
this.age = innAge;
}
下面为ModifyPersonSample类增加有两个参数的显式构造方法,为查阅案例代码方便起见,在Punit5项目unit包下新建NewPerson类。修改后的代码如下:
package unit;
public class NewPerson {
//声明名称属性,并被初始化为"张三"
private String name;
//声明年龄属性,并被初始化为22
private int age;
//定义一个显示有参构造方法
public NewPerson(String instrName,int innAge)
{
this.name = instrName;
this.age = innAge;
}
//定义一个方法,public是修饰符,void表示没有返回值
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
NewPerson提供了显示的带参数的构造方法,变量成员name和age由构造方法传入的参数进行初始化。使用new运算符实例化ModifyPersonSample类为person对象时,调用构造方法时需要传入相应的参数。
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建person对象,调用系统提供的一个隐式无参构造方法
NewPerson person = new NewPerson ("张三",22);
person.tell();
}
}
构造方法和方法的类似之处在于它们都包含可执行代码,区别在于,只有当Java类实例化时才执行构造方法。构造方法通常包含类属性初始化代码,从严格意义上说,构造方法并不是方法,因为它不会返回任何值。
this关键字
this关键字是类当前实例对象的引用(对象的引用在后面的课会讲述),使用this关键字可以访问类当前实例对象的成员变量和成员方法。
例如:
public class Circle {
/**
* @Fields π属性
*/
private double PI = 3.14;
/**
* @Fields 半径属性
*/
double radius = 5;
/**
* @return the radius
*/
public double getRadius() {
return radius;
}
/**
* @param radius the radius to set
*/
public void setRadius(double radius) {
this.radius = radius;
}
}
Circle类的setRadius()方法用于设置Circle类的成员变量radius,成员变量radius与setRadius()方法中的形参名称相同,为了不引起成员变量和形参局部变量的混淆。在这种情况下,可以使用this关键字来指定成员变量radius。
this.radius指定的是Circle类的成员变量radius,而this.radius = radius语句中的第二个radius则指定的是setRadius()方法的形参。该方法的作用就是将形参radius的值赋值给成员变量radius。
this关键字除了能够访问当前实例对象的成员变量和成员方法外,还可以作为成员方法的返回值,返回当前实例对象的引用。
类的主方法
一个完整的Java程序一般由多个类构成,每个类完成不同的功能。在这些类中会有一个主类,这个主类有一个main()方法,这个main()方法就是程序的入口,它定义了程序从何处开始,以及对程序流向的控制,Java 虚拟机通过主方法来执行程序。
主方法的语法如下:
public static void main(String[] args) {
// 方法体
}
主方法是静态方法,没有返回值,权限修饰符是public。主方法的形参是数组类型,其中args[0]~ args[n]分别代表程序的第一个到第n个参数,可以使用args的length属性获取参数的个数。
类方法的重载
类的构造方法有无参构造方法和有参构造方法,无参构造方法在调用时没有参数,有参构造方法在调用时需要传入参数。在实际编程应用中,无参构造方法和有参构造方法可能都会用到,因此在类中会同时声明无参构造方法和有参构造方法。例如下面的PersonA类既声明了无参构造方法,也声明了有参构造方法。
public class PersonA {
//定义一个显式无参构造方法
public PersonA()
{
this.name = "张三";
this.age = 22;
}
//定义一个显式有参构造方法
public PersonA(String instrName,int innAge)
{
this.name = instrName;
this.age = innAge;
}
}
public static void main(String[] args) {
//创建person对象,调用系统提供的一个显式有参构造方法
PersonA personone = new PersonA("李四",36);
//创建person对象,调用系统提供的一个显式无参构造方法
PersonA persontwo = new PersonA();
}
PersonA类声明了两个构造方法,一个无参,一个有参。在main方法中,创建了两个PersonA类对象,其中创建personone对象时调用有参构造方法,传入了姓名“李四”和年龄36两个参数;创建persontwo对象时调用无参构造方法,无参构造方法将persontwo对象的姓名和年龄分别初始化为“张三”和22。
PersonA类提供了两个构造方法,两个构造方法的名称都相同,不同的是一个没有参数,一个需要传入两个参数。这种方法名称相同,但传入参数不同的声明方式称为类方法的重载,重载不仅用于类的构造方法,同样也适用于类的其它方法。
Java类方法的重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的代码实现。调用时通过传递给它们的不同参数个数或参数类型来决定具体使用哪个方法,这也是Java的多态性。
一个类可以有多个重载方法 ,可根据不同的需求来设计类中不同的重载方法。其中,参数个数,类型,甚至不同类型参数顺序的不同均为对同一个方法的不同重载。例如经常用到的String类就是一个很典型的例子:

上图是String类的方法及属性列表,从图中可以看出,String类有多个构造方法,方便程序创建String对象时,可以传入不同类型的数据。
案例4:设计一个计算两数和的类,分别支持整数、单精度浮点、双精度浮点、长整型数据类型的两数相加,可以采用重载方法实现用同一方法名称完成不同类型数据的相加功能。
在Punit4项目unit包下创建Addition类。代码如下:
package unit;
public class Addition {
//两个整型数据相加
public double add(int a, int b) {
int sum = a + b;
return (double) sum;
}
//两个单精度浮点数据相加
public double add(float a, float b) {
float sum = a + b;
return (double) sum;
}
//两个双精度浮点数据相加
public double add(double a, double b) {
double sum = a + b;
return sum;
}
//两个长整型数据相加
public double add(long a, long b) {
long sum = a + b;
return (double) sum;
}
}
ddition类声明了四个add重载方法,分别实现整数类型、单精度浮点类型、双精度浮点类型、长整型数据的相加。
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建addition对象,调用系统提供的一个显式有参构造方法
Addition addition = new Addition();
//两个整数相加
double sum1 = addition.add(20,102);
//两个单精度浮点数相加
double sum2 = addition.add(168.5f,31.5f);
//两个双精度浮点数相加
double sum3 = addition.add(268.15,31.36);
//两个长整型数据相加
double sum4 = addition.add(18906l,219867l);
}
}
在main方法中,调用add方法时,传入不同的数据类型,Java编译器会根据传入的不同参数匹配不同的add方法。
在Java类中,要实现方法重载,方法名须要相同,并且每个重载方法同其它重载方法相比,需满足参数个数不同或者参数数据类型不同,但方法的返回类型不能用于区分不同的重载方法。