Java面向对象(上)

秒速五厘米 2022-12-27 07:57 246阅读 0赞

Java面向对象(上)

面向对象(Object Oriented)是一种新兴的程序设计方法,或者是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、多态等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。

要理解面向对象的编程语言,首先要理解类与对象这两个概念,首先要理解类与对象这两个概念,类和对象是面向对象编程中最基本、也是最重要的特征之一。类的成员不但可以是变量,还可以是函数;通过类定义出来的变量也有特定的称呼,叫做“对象”。类的实例化可生成对象。对象与实体是一一对应的,也就是说现实世界中每一个实体都是一个对象。面向对象最小的程序单元是类。

类(Class)是一个模板,它描述一类对象的行为和状态,也可以说类(Class)是用来描述具有相同的属性和方法的对象的集合。软件对象的状态就是属性,属性值区分对象,行为通过方法体现。

对象(Object)是指客观存在的事物(可以是看得见摸得着的,也可以是看不见摸不着的),类的实例(instance)。由一组属性和方法构成。可以简单地描述为:对象 = 属性 + 方法

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70

【类和对象的关系,简单地说,类是对象之上的抽象,是对象的模板;对象是类的具体化,称为类的实例。面向对象编程中,对象和实例,意思是一样的。】

属性(attribute或property)是用来描述对象的外部特征。

属性的引用方法为:

对象名.属性名 = 属性值 或 变量名 = 对象名.属性名

方法(Method)是对某对象接收消息后所采取的操作的描述,它表明了一个对象所具有的行为能力。

调用对象的方法为:

对象名.方法名[参数列表]

Java是一门面向对象的编程语言,首先给出一个示例:

public class Demo {

  1. public static void main(String\[\] args)\{
  2. // 定义类Student
  3. class Student\{ // 通过class关键字类定义类
  4. // 类包含的变量
  5. String name;
  6. int age;
  7. float score;
  8. // 类包含的函数
  9. void say()\{
  10. System.out.println( name + "的年龄是 " + age + ",成绩是 " + score );
  11. \}
  12. \}
  13. // 通过类来定义变量,即创建对象——类的实例化
  14. Student stu1 = new Student(); // 必须使用new关键字
  15. // 操作类的成员
  16. stu1.name = "小明";
  17. stu1.age = 15;
  18. stu1.score = 92.5f;
  19. stu1.say();
  20. \}

}

运行结果:

小明的年龄是 15,成绩是 92.5

建立了类后,如何创建对象?使用 new 关键字创建对象,这是常用的创建对象的方法,语法格式如下:

类名 对象名 = new 类名();

参见上例。Java创建对象还有其它方式,后续介绍。

Java类的定义

Java类与对象知识思维导图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 1

类是描述实体的“模板”,它定义了属于这个类的对象所应该具有的状态(属性)和行为(方法)。

类是描述实体的“模板”,它定义了属于这个类的对象所应该具有的状态(属性)和行为(方法)。换句话说,类是数据和对数据进行操作的方法的集合体。类中定义的数据成员表示了类的对象的一种状态,可认为是对象的属性;类中定义的方法表示对类的对象的操作。类是对象的一种模板。从一个类中可创建任意多个对象,它们具有相同的行为,但各自拥有自身的不同状态,依次可以将多个对象区别开。一个程序运行时总会产生许多个对象,这些对象在本质上具有天生的潜在并发性,它们之间总是要发生联系的,那么该如何通信呢?对象之间是通过事件进行通信的。事件是一种特殊的对象。

Java中类定义的基本格式是:

[类修饰符] class <类名> {

<类体>

}

其中,关键字class表示定义一个类,类修饰符可以用public或默认,<类名>是Java合法的标识符名。按Java编码约定,类名的英文单词第一个字母要大写,若由多个单词组成,则每个单词的首字母都要大写,<类体>可缺省,<类体>由变量定义和方法定义组成。

类的成员变量的声明格式是:

[修饰符] <类型> <变量名>[ = 初始值];

如:private String name = “LiMing”;

类的成员方法的声明格式是:

[修饰符] <返回值类型> <方法名>(形式参数列表) {

  1. <方法体>

}

更具体描述如下:

类定义的基本格式:

[public][abstract|final] class className [extends superclassName] [implements interfaceNameList]{……}

其中,修饰符public,abstract,final 说明了类的属性,className为类名,superclassName为类的父类的名字,interfaceNameList为类所实现的接口列表。

在{ }中的…… 表示是“类体”部分,下面介绍。

类体格式:

class className{

  1. \[public | protected | private \] \[static\] \[final\] \[transient\] \[volatile\] type variableName;//成员变量
  2. \[public | protected | private \] \[static\] \[final | abstract\] \[native\] \[synchronized\] returnType methodName(\[paramList\]) \[throws exceptionList\]\{
  3. statements
  4. \}//成员方法

}

成员变量(Member variable),官方文档称为field【注】。

【注:在面向对象理论中,表示“与对象或类相关联的变量”的用语是属性(attribute);在面向对象的程序语言设计中,称为成员变量(member variable),有时称为成员字段(member field)或字段(field)。特别提示, Java 语言官方文档中用语就是field, Property有另外的含义,而多数程序员及书籍网络习惯使用成员变量,在此从众。】

Java修饰符

Java语言提供了很多修饰符,主要分为两类:访问修饰符非访问修饰符

访问修饰符:可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限:default (即缺省、默认,什么也不写)、private 、public、protected。访问修饰符是一组限定类、属性或方法是否可以被程序里的其他部分访问和调用的修饰符。

☆public:公有的

被声明为public的类、方法、构造方法和接口能够被任何其他类访问。如果几个相互访问的public类分布在不用的包中,则需要导入相应public类所在的包。由于类的继承性,类所有的公有方法和变量都能被其子类继承。

☆protected:受保护的

被声明为protected的变量、方法和构造方法能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。

protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected。

子类能访问protected修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。

☆private:私有的

私有访问修饰符是最严格的访问级别,所以被声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。

声明为私有访问类型的变量只能通过类中公共的Getter/Setter方法被外部类访问。

private访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。

☆default(默认,缺省):也称为 friendly(友好的),不写访问修饰符。

不使用任何修饰符声明的属性和方法,对同一个包内的类是可见的。接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。

访问控制修饰符的权限如下表:







































                                     修饰符

访问范围

private

default

protected

public

同一个类

可访问

可访问

可访问

可访问

同一包中的其他类

不可访问

可访问

可访问

可访问

不同包中的子类

不可访问

不可访问

可访问

可访问

不同包中的非子类

不可访问

不可访问

不可访问

可访问

如何使用访问控制符

访问控制符可以让我们很方便的控制代码的权限:

当需要让自己编写的类被所有的其他类访问时,就可以将类的访问控制符声明为 public。

当需要让自己的类只能被自己的包中的类访问时,就可以省略访问控制符。

当需要控制一个类中的成员数据时,可以将这个类中的成员数据访问控制符设置为 public、protected,或者省略。

访问控制和继承

请注意以下方法继承(Java继承和多态后续介绍)的规则:

父类中声明为public的方法在子类中也必须为public。

父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。

父类中默认修饰符声明的方法,能够在子类中声明为private。

父类中声明为private的方法,不能够被继承。

非访问修饰符

static 修饰符,用来修饰类方法和类变量。

final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。

abstract 修饰符,用来创建抽象类和抽象方法。

synchronized 和 volatile 修饰符,主要用于线程的编程。

Java中的类(class)可以包含以下类型变量:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 2

局部变量( lacal variable):在方法、构造方法或者语句块中定义的变量被称为局部变量。生命周期是从声明位置开始到”}”为止。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。【对于局部变量,Java 虚拟机并不会给它自行初始化,也就是并不会给它赋上该类型的默认值,局部变量需要自己去初始化。】

成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。从属于对象,生命周期伴随对象始终。如果不自行初始化,他会自动初始化成该类型的默认初始值(数值型变量初始化成 0 或 0.0,字符型变量的初始化值是 \u0000,布尔型默认是 false)。

类变量(静态变量 static variable):类变量也声明在类中,方法体之外,但必须声明为static类型,从属于类,生命周期伴随类始终,从类加载到卸载。如果不自行初始化,他会自动初始化成该类型的默认初始值(数值型变量初始化成 0 或 0.0,字符型变量的初始化值是 \u0000,布尔型默认是 false)。

成员变量、局部变量、静态变量的区别














































 

成员变量

局部变量

静态变量

定义的位置

类中,方法外

方法中,或者方法的形式参数

在类中,方法外

初始化值

有默认初始化值

,先定义,赋值后才能使用

有默认初始化值

调用方式

对象调用

 

对象调用,类名调用

存储位置

堆中

栈中

方法区

生命周期

与对象共存亡

与方法共存亡

与类共存亡

别名

实例变量

 

类变量

例、一个简单的 Person 类:

public class Person {
private String name; // 姓名 成员变量(属性)
private int age; // 年龄 成员变量(属性)
public void tell() { // 定义说话的方法 成员方法
System.out.println(name+”今年”+age+”岁!”);
}
}

Java变量作用域(适用范围)

在Java中,变量的作用域分为四个级别:类级、对象实例级、方法级、块级。

☆类级变量又称全局级变量或静态变量,需要使用static关键字修饰。类级变量在类定义后就已经存在,占用内存空间,可以通过类名来访问,不需要实例化。

☆对象实例级变量就是在类中定义的变量,是成员变量,实例化后才会分配内存空间,才能访问。

☆方法级变量就是在方法内部定义的变量,以及方法的形参,是局部变量。

☆块级变量就是代码块中定义的变量,也是局部变量,块是指由大括号包围的代码,出了这个块就消失了。例如:

{

  1. int age = 3;
  2. String name = "www.weixueyuan.net";
  3. // 正确,在块内部可以访问 age 和 name 变量
  4. System.out.println( name + "已经" + age + "岁了");

}

// 错误,在块外部无法访问 age 和 name 变量

System.out.println( name + “已经” + age + “岁了”);

Java变量的作用域需要明确几点:

方法内部除了能访问方法级的变量,还可以访问类级和实例级的变量。

块内部能够访问类级、实例级变量,如果块被包含在方法内部,它还可以访问方法级的变量。

方法级和块级的变量必须被显示地初始化,否则不能访问。

示例

public class DemoScope{

  1. public static String name = "张小明"; // 类级变量
  2. public int i; // 对象实例级变量
  3. // 属性块,在类初始化属性时候运行
  4. \{
  5. int j = 2;// 块级变量
  6. \}
  7. public void test1() \{
  8. int j = 3; // 方法级变量
  9. if(j == 3) \{
  10. int k = 5; // 块级变量
  11. \}
  12. // 这里不能访问块级变量,块级变量只能在块内部访问
  13. System.out.println("name=" + name + ", i=" + i + ", j=" + j);
  14. \}
  15. public static void main(String\[\] args) \{
  16. // 不创建对象,直接通过类名访问类级变量
  17. System.out.println(DemoScope.name);
  18. // 创建对象并访问它的方法
  19. DemoScope t = new DemoScope();
  20. t.test1();
  21. \}

}

运行结果:

张小明

name=张小明, i=0, j=3

Java成员方法进一步说明

方法(method)的实现也包括两部分内容:方法声明和方法体。

方法声明包括方法名、返回类型和外部参数。其中参数的类型可以是简单数据类型,也可以是复合数据类型(又称引用数据类型)。

方法体是对方法的实现,它包括局部变量的声明以及所有合法的Java指令(语句)。方法体中声明的局部变量的作用域在该方法内部。若局部变量与类的成员变量同名,则类的成员变量被隐藏。

为了区别参数和类的成员变量,我们必须使用this。this用在一个方法中引用当前对象,它的值是调用该方法的对象。返回值须与返回类型一致,或者完全相同,或是其子类。当返回类型是接口时,返回值必须实现该接口。

成员方法的例子如下,前面Person 类中已提及

  1. public void tell() \{ // 定义说话的方法 成员方法
  2. System.out.println(name+"今年"+age+"岁!");

}

关于成员方法的两点说明:

1) 成员方法的返回值

若方法有返回值,则在方法体中用 return 语句指明要返回的值,其格式如下所示。

return 表达式

或者

return (表达式)

其中,表达式可以是常量、变量、对象等。表达式的数据类型必须与声明成员方法时给出的返回值类型一致。

2) 形参、实参及成员方法的调用

一般来说,可以通过以下方式来调用成员方法:

methodName({paramList})

关于方法的参数,经常会提到形参与实参,形参是定义方法时参数列表中出现的参数,实参是调用方法时为方法传递的参数。

方法的形参和实参具有以下特点:

☆形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在方法内部有效,方法调用结束返回主调方法后则不能再使用该形参变量。

☆实参可以是常量、变量、表达式、方法等,无论实参是何种类型的量,在进行方法调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值、输入等办法使实参获得确定值。

☆实参和形参在数量、类型和顺序上应严格一致,否则会发生“类型不匹配” 的错误。

☆方法调用中发生的数据传送是单向的,即只能把实参的值传送绐形参,而不能把形参的值反向地传送给实参。因此在方法调用过程中,形参的值发生改变,而实参中的值不会变化。

下面的示例演示了调用 add() 方法前后形参 x 的变化。

class TestAdd{

  1. public int add(int x) \{
  2. x += 30;
  3. System.out.println("形参 x 的值:"+x);
  4. return x;
  5. \}
  6. public static void main(String\[\] args) \{
  7. int x = 150;
  8. System.out.println("调用 add() 方法之前 x 的值:"+x);
  9. Test t = new Test();
  10. int i = t.add(x);
  11. System.out.println("实参 x 的值:"+x);
  12. System.out.println("调用 add() 方法的返回值:"+i);
  13. \}

}

运行上述程序,输出结果如下:

调用 add() 方法之前 x 的值:150

形参 x 的值:180

实参 x 的值:150

调用 add() 方法的返回值:180

从输出结果可以看出,形参 x 值的改变,并没有影响实参 x。

在调用成员方法时应注意以下 4 点:

1)对无参成员方法来说,是没有实际参数列表的(即没有 paramList),但方法名后的括号不能省略。

2)对带参数的成员方法来说,实参的个数、顺序以及它们的数据类型必须与形式参数的个数、顺序以及它们的数据类型保持一致,各个实参间用逗号分隔。实参名与形参名可以相同,也可以不同。

3)实参也可以是表达式,此时一定要注意使表达式的数据类型与形参的数据类型相同,或者使表达式的类型按 Java 类型转换规则达到形参指明的数据类型。

4)实参变量对形参变量的数据传递是“值传递”,即只能由实参传递给形参,而不能由形参传递给实参。程序中执行到调用成员方法时,Java 把实参值复制到一个临时的存储区(栈)中,形参的任何修改都在栈中进行,当退出该成员方法时,Java 自动清除栈中的内容。

注意:在java中参数的传递只有按值传递(并没有所谓的按引用传递)。下面简要介绍一下。

Java数据类型可以分为两大类:基本类型(primitive types)和引用类型(reference types)。(参见“Java数据类型”一章)

a、基本数据类型的按值传递

public class Swap {

  1. public static void main(String args\[\]) \{
  2. int x = 10;
  3. int y = 20;
  4. swap(x, y);
  5. System.out.println("x(2) = " + x);
  6. System.out.println("y(2) = " + y);
  7. \}
  8. public static void swap(int x, int y) \{
  9. int temp = x;
  10. x = y;
  11. y = temp;
  12. System.out.println("x(1) = " + x);
  13. System.out.println("y(1) = " + y);
  14. \}

}

main函数调用swap函数来交换 x,y的值,调用函数之后发现main中x,y的值并未交换。main函数中的x,y和swap函数中的x,y分别存放在不同的区域,在main中调用swap函数的时候,会将main中的x,y的值赋给swap中的x,y。当swap函数中对x,y交换时只是对swap帧中的x,y做交换,并不会改变main中的x,y。所以当函数返回时main中的x,y并不会改变输出如下:

x(1) = 20

y(1) = 10

x(2) = 10

y(2) = 20

b、引用数据类型的按值传递

引用数据数据类型分为三种:①接口 ②类 ③数组

public class Swap2 {

  1. public static void main(String args\[\]) \{
  2. int \[\]a=\{10,20\};
  3. System.out.println("a\[0\]=" + a\[0\] + " a\[1\]=" + a\[1\]); //a\[0\]=10,a\[1\]=20
  4. swap(a, 0, 1);
  5. System.out.println("a\[0\]=" + a\[0\] + " a\[1\]=" + a\[1\]); //a\[0\]=20,a\[1\]=10
  6. \}
  7. public static void swap(int \[\]a,int i,int j)\{
  8. int temp=a\[i\];
  9. a\[i\]=a\[j\];
  10. a\[j\]=temp;
  11. System.out.println("a\[0\]=" + a\[0\] + " a\[1\]=" + a\[1\]); //a\[0\]=20,a\[1\]=10
  12. \}

}

输出

a[0]=10 a[1]=20

a[0]=20 a[1]=10

a[0]=20 a[1]=10

运行程序后发现,swap函数对a[0] ,a[1]的操作竟然影响到了main函数中的a[0] ,a[1]的值,为什么会产生如此之结果?原来对于引用类型参数,实参并没有被复制一份给形参,还是按值传递的,只不过传递的是对象的引用,即实参和形参的引用指向同一对象,对象的引用不会改变,地址指向的对象的内容可以在被调用的方法中改变。【“引用”是对象的别名,实质是对象占据内存空间的地址——对象的首地址】

Java中的方法调用,参数按值传递:对于基本数据类型,形参和实参使用两块不同的内存空间,因此方法中对形参的改变不会影响实参的值;对于引用数据类型,形参和实参的引用指向同一对象使用的内存空间,在被调用的方法中对对象内容的改变,实参会反应出来。

值传递是会将实际参数复制一份,而引用传递是没有复制的,所以,值传递和引用传递的区别并不是传递的内容,而是实参到底有没有被复制一份给形参。

20201219072747714.png

【值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

Java中只有按值传递,并没有所谓的按引用传递。参见 为什么说java只有按值传递https://www.codenong.com/cs109706152/ 】

构造方法(构造器constructor)

构造方法(构造器constructor)是一个特殊的方法。Java 中的每个类都有构造方法,用来初始化该类的一个对象。

构造方法具有和类名相同的名称,不返回任何数据类型,而且不能被static、final、synchronized、abstract和native修饰。

实例在创建时通过new操作符会调用其对应的构造方法,构造方法用于初始化实例。

Java每个类都有构造方法,如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认的无参数构造方法。它没有参数,也没有执行语句,类似这样:

class Person {

  1. public Person() \{
  2. \}

}

如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法了。

public class Main {

  1. public static void main(String\[\] args) \{
  2. Person p = new Person("Xiao Ming", 15);
  3. System.out.println(p.getName());
  4. System.out.println(p.getAge());
  5. \}

}

class Person {

  1. private String name;
  2. private int age;
  3. // 构造方法
  4. public Person(String name, int age) \{
  5. this.name = name;
  6. this.age = age;
  7. \}
  8. public String getName() \{
  9. return this.name;
  10. \}
  11. public int getAge() \{
  12. return this.age;
  13. \}

}

输出:

Xiao Ming

15

如果既要能使用带参数的构造方法,又想保留不带参数的构造方法,那么只能把两个构造方法都定义出来:

public class Main {

  1. public static void main(String\[\] args) \{
  2. Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
  3. Person p2 = new Person(); // 也可以调用无参数构造方法
  4. System.out.println(p1.getName());
  5. System.out.println(p1.getAge());
  6. System.out.println(p2.getName());
  7. System.out.println(p2.getAge());
  8. \}

}

class Person {

  1. private String name;
  2. private int age;
  3. // 构造方法
  4. public Person() \{
  5. \}
  6. // 构造方法
  7. public Person(String name, int age) \{
  8. this.name = name;
  9. this.age = age;
  10. \}
  11. public String getName() \{
  12. return this.name;
  13. \}
  14. public int getAge() \{
  15. return this.age;
  16. \}

}

输出:

Xiao Ming

15

null

0

没有在构造方法中初始化字段时,引用类型的字段默认是null,int类型默认值是0,布尔类型默认值是false。请你结合上面输出后两行理解之。

可以定义多个构造方法,编译器根据参数自动判断;可以在一个构造方法内部调用另一个构造方法,便于代码复用。构造方法支持重载。

多构造方法

可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分:

class Person {

  1. private String name;
  2. private int age;
  3. public Person(String name, int age) \{
  4. this.name = name;
  5. this.age = age;
  6. \}
  7. public Person(String name) \{
  8. this.name = name;
  9. this.age = 12;
  10. \}
  11. public Person() \{
  12. \}

}

如果调用new Person(“Xiao Ming”, 20);,会自动匹配到构造方法public Person(String, int)。

如果调用new Person(“Xiao Ming”);,会自动匹配到构造方法public Person(String)。

如果调用new Person();,会自动匹配到构造方法public Person()。

一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…):

class Person {

  1. private String name;
  2. private int age;
  3. public Person(String name, int age) \{
  4. this.name = name;
  5. this.age = age;
  6. \}
  7. public Person(String name) \{
  8. this(name, 18); // 调用另一个构造方法Person(String, int)
  9. \}
  10. public Person() \{
  11. this("Unnamed"); // 调用另一个构造方法Person(String)
  12. \}

}

this关键字

this关键字可用于区分成员变量和局部变量,代表当前对象,调用本类中的其他构造方法等。this关键字指向的是当前对象的引用:

调用类中的属性:this.属性名称,指的是访问类中的成员变量,用来区分成员变量和局部变量(重名问题)

调用类中的方法:this.方法名称,用来访问本类的成员方法。

调用类构造方法:this();访问本类的构造方法,()中可以有参数的 如果有参数 就是调用指定的有参构造。对于this() 方法注意两点:1)this() 不能使用在普通方法中,只能写在构造方法中;2)必须是构造方法中的第一条语句。

下面举几个应用场景。

应用一:引用成员变量

Public Class Student {

  1. String name; //定义一个成员变量name
  2. private void SetName(String name) \{ //定义一个参数(局部变量)name
  3. this.name=name; //将局部变量的值传递给成员变量

}

}

如上面这段代码中,有一个成员变量name,同时在方法中有一个形式参数,名字也是name,然后在方法中将形式参数name的值传递给成员变量name,虽然我们可以看明白这个代码的含义,但是作为Java编译器它是怎么判断的呢?到底是将形式参数name的值传递给成员变量name,还是反过来将成员变量name的值传递给形式参数name呢?也就是说,两个变量名字如果相同的话,那么Java如何判断使用哪个变量?此时this这个关键字就起到作用了。this这个关键字其代表的就是对象中的成员变量或者方法。也就是说,如果在某个变量前面加上一个this关键字,其指的就是这个对象的成员变量或者方法,而不是指成员方法的形式参数或者局部变量。为此在上面这个代码中,this.name代表的就是对象中的成员变量,又叫做对象的属性,而后面的name则是方法的形式参数,代码this.name=name就是将形式参数的值传递给成员变量。这就是上面这个代码的具体含义。

一般情况下,在Java语言中引用成员变量或者成员方法都是以对象名.成员变量或者对象名.成员方法的形式。不过有些程序员即使在没有相同变量的时候,也喜欢使用this.成员变量的形式来引用变量,这主要是从便于代码的阅读考虑的。一看到这个this关键字就知道现在引用的变量是成员变量或者成员方法,而不是局部变量。这无形中就提高了代码的阅读性。不过话说回来,这是this关键字在Java语言中的最简单的应用。从这个应用中,我们可以看出this关键字其代表的就是对象的名字。

其实如果是局部变量的话,也是相同的道理。如在上面的代码中,name不是形式参数,而是一个局部变量。此时Java也会遇到相同的疑惑,即变量名name代表的到底是局部变量还是形式参数?name=name到底代表的是什么含义?根据局部变量的作用域,在方法内部,如果局部变量与成员变量同名的话,那么是以局部变量为准。可是在name=name这个赋值语句中,将局部变量的值赋值给自己,显然并不是很合适。根据代码的含义,本来的意思应该是将局部变量赋值给成员变量。为了更清晰的表达这个含义,为此最好采用如下的书写格式this.name=name。这里的this关键字含义就是对象名student,为此this.name就表示student.name。

应用二:调用类的构造方法

public class Student { //定义一个类,类的名字为student。

  1. public Student() \{ //定义一个方法,名字与类相同故为构造方法
  2. this(“Hello!”);

}

  1. public Student(String name) \{ //定义一个带形式参数的构造方法

}

}

this关键字除了可以调用成员变量之外,还可以调用构造方法。在一个Java类中,其方法可以分为成员方法和构造方法两种。构造方法是一个与类同名的方法,在Java类中必须存在一个构造方法。如果在代码中没有显示的体现构造方法的话,那么编译器在编译的时候会自动添加一个没有形式参数的构造方法。这个构造方法跟普通的成员方法还是有很多不同的地方。如构造方法一律是没有返回值的,而且也不用void关键字来说明这个构造方法没有返回值。而普通的方法可以有返回值、也可以没有返回值,程序员可以根据自己的需要来定义。不过如果普通的方法没有返回值的话,那么一定要在方法定义的时候采用void关键字来进行说明。其次构造方法的名字有严格的要求,即必须与类的名字相同。也就是说,Java编译器发现有个方法与类的名字相同才把其当作构造方法来对待。而对于普通方法的话,则要求不能够与类的名字相同,而且多个成员方法不能够采用相同的名字。在一个类中可以存在多个构造方法,这些构造方法都采用相同的名字,只是形式参数不同。Java语言就凭形式参数不同来判断调用那个构造方法。

在上面这段代码中,定义了两个构造方法,一个带参数,另一个没有带参数。构造方法都不会有返回值,不过由于构造方法的特殊性,为此不必要在构造方法定义时带上void关键字来说明这个问题。在第一个没有带参数的构造方法中,使用了this(“Hello!”)这句代码,这句代码表示什么含义呢?在构造方法中使this关键字表示调用类中的构造方法。如果一个类中有多个构造方法,因为其名字都相同,跟类名一致,那么这个this到底是调用哪个构造方法呢?其实,这跟采用其他方法引用构造方法一样,都是通过形式参数来调用构造方法的。如上例中,this关键字后面加上了一个参数,那么就表示其引用的是带参数的构造方法。如果现在有三个构造方法,分别为不带参数、带一个参数、带两个参数。那么Java编译器会根据所传递的参数数量的不同,来判断该调用哪个构造方法。从上面示例中可以看出,this关键字不仅可以用来引用成员变量,而且还可以用来引用构造方法。

不过如果要使用这种方式来调用构造方法的话,有一个语法上的限制。一般来说,利用this关键字来调用构造方法,只有在无参数构造方法中第一句使用this调用有参数的构造方法。否则的话,翻译的时候,就会有错误信息。这跟引用成员变量不同。如果引用成员变量的话,this关键字是没有位置上的限制的。如果不熟悉这个限制的话,那么还是老老实实的采用传统的构造方法调用方式为好。虽然比较麻烦,但是至少不会出错。

应用三:返回对象的值

this关键字除了可以引用变量或者成员方法之外,还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this,来返回某个类的引用。此时这个this关键字就代表类的名称。如代码在上面student类中,那么代码代表的含义就是return student。可见,这个this关键字除了可以引用变量或者成员方法之外,还可以作为类的返回值,这才是this关键字最引人注意的地方。

下面给出一个简单但完整的例子

在该例子中,我们创建两个类:Employee 和 EmployeeTest。

Employee.java 文件代码如下

public class Employee{

String name;

int age;

String designation;

double salary;

// Employee 类的构造器

public Employee(String name){

  1. this.name = name;

}

// 设置age的值

public void empAge(int empAge){

  1. age = empAge;

}

/* 设置designation的值*/

public void empDesignation(String empDesig){

  1. designation = empDesig;

}

/* 设置salary的值*/

public void empSalary(double empSalary){

  1. salary = empSalary;

}

/* 打印信息 */

public void printEmployee(){

  1. System.out.println("名字:"+ name );
  2. System.out.println("年龄:" + age );
  3. System.out.println("职位:" + designation );
  4. System.out.println("薪水:" + salary);

}

}

Employee 类有四个成员变量:name、age、designation 和 salary。该类显式声明了一个构造方法,该方法只有一个参数。

下面给出EmployeeTest类,该类实例化2个 Employee 类的实例,并调用方法设置变量的值。

EmployeeTest.java 文件代码如下:

public class EmployeeTest{

public static void main(String[] args){

  1. /\* 使用构造器创建两个对象 \*/
  2. Employee empOne = new Employee("Li Ming");
  3. Employee empTwo = new Employee("Wang Fang");
  4. // 调用这两个对象的成员方法
  5. empOne.empAge(26);
  6. empOne.empDesignation("高级程序员");
  7. empOne.empSalary(10000);
  8. empOne.printEmployee();
  9. empTwo.empAge(22);
  10. empTwo.empDesignation("程序员");
  11. empTwo.empSalary(7500);
  12. empTwo.printEmployee();

}

}

编译这两个文件并且运行 EmployeeTest 类,得到如下输出结果:

名字:Li Ming

年龄:26

职位:高级程序员

薪水:10000.0

名字:Wang Fang

年龄:22

职位:程序员

薪水:7500.0

Java方法的可变参数

在具体实际开发过程中,有时方法中参数的个数是不确定的,就可以使用可变参数。

声明可变参数的语法格式如下:

methodName({paramList},paramType…paramName)

其中,methodName 表示方法名称;paramList 表示方法的固定参数列表;paramType 表示可变参数的类型;… 是声明可变参数的标识;paramName 表示可变参数名称。

注意:可变参数必须定义在参数列表的最后。

例 、每次参加考试的人数是不固定的,但是每次考试完之后都需要打印出本次考试的总人数以及参加考试的学生名单。下面编写程序,使用方法的可变参数实现该功能,具体的代码如下:

public class StudentTestMethod {

  1. // 定义输出考试学生的人数及姓名的方法
  2. public void print(String...names) \{
  3. int count = names.length; // 获取总个数
  4. System.out.println("本次参加考试的有"+count+"人,名单如下:");
  5. for(int i = 0;i < names.length;i++) \{
  6. System.out.println(names\[i\]);
  7. \}
  8. \}
  9. public static void main(String\[\] args) \{
  10. // TODO Auto-generated method stub
  11. StudentTestMethod student = new StudentTestMethod();
  12. student.print("李洁","张辉","宋雨荷"); // 传入3个值
  13. student.print("赵小萌","孙一");
  14. \}

}

在 Student TestMethod 类中定义了 print() 方法和 main() 方法。print() 方法声明了一个 String 类型的可变参数,方法体打印可变参数的总个数以及参数值。在 main() 方法中创建了 StudentTestMethod 类的实例,然后分别传入不同个数的参数调用 print() 方法。

运行 StudentTestMethod 类,输出结果如下:

本次参加考试的有3人,名单如下:

李洁

张辉

宋雨荷

本次参加考试的有2人,名单如下:

赵小萌

孙一

Java创建对象

对象是对类的实例化。对象具有状态和行为,变量用来表明对象的状态,方法表明对象所具有的行为。现在详细介绍对象的创建,在 Java 语言中创建对象分显式创建与隐含创建两种情况。

隐含创建对象

除了显式创建对象以外,在 Java 程序中还可以隐含地创建对象,例如下面几种情况。

1)String strName = “strValue”,其中的“strValue”就是一个 String 对象,由 Java 虚拟机隐含地创建。

2)字符串的“+”运算符运算的结果为一个新的 String 对象,示例如下:

String str1 = “Hello”;

String str2 = “Java”;

String str3 = str1+str2; // str3引用一个新的String对象

3)当 Java 虚拟机加载一个类时,会隐含地创建描述这个类的 Class 实例。

提示:类的加载是指把类的 .class 文件中的二进制数据读入内存中,把它存放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。

显式创建对象

有多种方式:

new关键字

Class.newInstance

Constructor.newInstance

Clone方法

反序列化

使用关键字 new 来创建对象,这是常用的创建对象的方法,有人给出语法格式如下:

类名 对象名 = new 类名([参数]);

特别提示,实际上应是或应理解为:

类名 对象名 = new 构造方法名([参数列表]);

上句和如下两句:

类名 对象名;

对象名 = new 构造方法名([参数列表]);

等价。

赋值号右边的new是为新建对象开辟内存空间的运算符,用new运算符开辟新建对象的内存之后,系统自动调用构造方法初始化该对象。

New后是构造方法名,只不过类的构造方法名和类名一样。Java每个类都有构造方法,如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认的无参数构造方法。构造方法定义了当创建一个对象时要进行的操作。关于构造方法前面已介绍。

其中的对象名,也称为对象引用名;[参数列表]是可选项,多个参数用英文逗号分隔。

下面给出一个简明的例子:

public class Puppy{

public Puppy(String name){

  1. //这个构造器仅有一个参数:name
  2. System.out.println("名字是 : " + name );

}

public static void main(String[] args){

  1. // 下面的语句将创建一个Puppy对象
  2. Puppy myPuppy = new Puppy( "tommy" );

}

}

编译并运行上面的程序输出

名字是 : tommy

new 操作符的返回值是一个对象的引用。下面对这句话详细解释之。

何谓对象?

要理解什么是对象,需要跟类一起结合起来理解。下面这段话引自《Java编程思想》中的一段原话:

“按照通俗的说法,每个对象都是某个类(class)的一个实例(instance),这里,‘类’就是‘类型’的同义词。”

从这一句话就可以理解到对象的本质,简而言之,它就是类的实例,比如所有的人统称为“人类”,这里的“人类”就是一个类(物种的一种类型),而具体到每个人,比如张三这个人,它就是对象,就是“人类”的实例。

何谓对象引用?

《Java编程思想》一段话: “每种编程语言都有自己的数据处理方式。有些时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示(例如C/C++里的指针)来操作对象。所有这些在 Java 里都得到了简化,一切都被视为对象。因此,我们可采用一种统一的语法。尽管将一切都“看作”对象,但操纵的标识符实际是指向一个对象的“引用”(reference)。”  

基本数据类型和引用数据类型。

Java的其中基本类型变量有四类8种:byte short int long float double char boolean,除了8种基本数据类型变量,其他变量都是引用数据类型,如类、接口、数组等。

基本数据类型,只有一块存储空间, 在栈中,存放的是具体数据值。

引用数据类型,有两块存储空间一个在栈(Stack)中,一个在堆(heap)中。堆中存放对象实体(使用new关键字,即表示在堆中开辟一块新的存储空间),栈中存放对象在堆中所在位置的首地址。new 操作符的返回值是一个对象的引用。

一个引用类型变量(栈中的一块内存空间)保存了一个该类型对象在堆中所在位置的首地址,也称作一个引用类型变量指向了一个该类型的对象,通过这个变量就可以操作对象中的数据。

下面借助一个简单的例子解释之

// Person类部分

class Person {

  1. String name;
  2. int age;

}

//PersonTest 类部分

public class PersonTest {

  1. public static void main(String\[\] args) \{
  2. Person p1 = new Person();
  3. p1.name = "张伟";
  4. p1.age = 17;
  5. System.out.println("姓名:" + p1.name + ",年龄:" + p1.age);
  6. Person p2 = new Person();
  7. p2.name = "李丽";
  8. p2.age = 12;
  9. System.out.println("姓名:" + p2.name + ",年龄:" + p2.age);
  10. p1 = p2;
  11. p1.age = 13;
  12. System.out.println("姓名:" + p1.name + ",年龄:" + p1.age);
  13. System.out.println("姓名:" + p2.name + ",年龄:" + p2.age);
  14. \}

}

下面使用bluej编译运行情况

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 3

运行结果如下

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 4

解释如下

内存分配示意图如下:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 5

当多个对象的引用指向堆同一块内存空间(p1 = p2; 即将一个对象的引用赋值给另一个变量,两个变量所记录的地址值是一样的,此时p1指向 p2所指向的对象,不再指向 p1原本指向的对象,如上图所示),只要通过任何一个对象引用修改了对象中的数据,随后,无论使用哪一个对象引用获取数据,得到的都是修改后的值,因此,最后两行打印语句的结果是一样的。

官网对new 关键字介绍见 Creating Objects https://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html

关于Java中创建对象的其它方式可参见:

Java创建对象的几种方式 https://juejin.cn/post/6844904054775087117 在此不深入介绍了。

static关键字

类的成员变量有两种:一种是被static修饰的变量,称为类变量或静态变量;另一种是没有被static修饰的变量,称为实例变量。它们两者的区别如下。

(1)静态变量在内存中占用一份备份,运行时Java虚拟机只为静态变量分配一次内存,在加载类的过程中完成内存空间的分配。可以直接通过类名访问静态变量。

(2)对于实例变量,每创建一个实例,就会为实例变量分配一次内存。实例变量可在内存中有多份备份,互不影响。

//TestStaticVar.java

class Incrementable {

  1. public int num;
  2. public static int count = 0; // 静态变量count
  3. public Incrementable() \{
  4. count++; //或 Incrementable. count++; 或 this.count++;
  5. num = count;
  6. \}
  7. public Incrementable(int k) \{
  8. count = num;
  9. count++;
  10. \}

}

public class TestStaticVar {

  1. public static void main(String\[\] args) \{
  2. Incrementable\[\] increment = new Incrementable\[5\]; // 创建引用类型的数组
  3. for (int i = 0; i < increment.length; i++) \{
  4. increment\[i\] = new Incrementable();
  5. System.out.println("increment\[" + i + "\].count = " + Incrementable.count
  6. + ",increment\[" + i + "\].num = " + increment\[i\].num);
  7. \}
  8. for (int i = 0; i < increment.length; i++) \{
  9. increment\[i\] = new Incrementable(0);
  10. System.out.println("increment\[" + i + "\].count = " + Incrementable.count
  11. + ",increment\[" + i + "\].num = " + increment\[i\].num);
  12. \}
  13. \}

}

程序运行结果:

increment[0].count = 1,increment[0].num = 1

increment[1].count = 2,increment[1].num = 2

increment[2].count = 3,increment[2].num = 3

increment[3].count = 4,increment[3].num = 4

increment[4].count = 5,increment[4].num = 5

increment[0].count = 1,increment[0].num = 0

increment[1].count = 1,increment[1].num = 0

increment[2].count = 1,increment[2].num = 0

increment[3].count = 1,increment[3].num = 0

increment[4].count = 1,increment[4].num = 0

类中的方法(非构造方法)可分为实例方法和类方法。方法声明时,方法类型前面不加关键字static修饰的是实例方法,用static修饰的方法是静态方法(类方法)。访问静态方法不需要创建类的实例,可以直接通过类名来访问。若已创建了对象,也可通过对象引用来访问。

//TestStaticMethod.java

class Citizen{

  1. private static String country = "china";
  2. private String name = "Tom";
  3. public static void f1()\{
  4. System.out.println(country);
  5. // System.out.println(name); // 不可直接访问非静态成员,只能间接访问
  6. \}
  7. public void f2()\{ // 可访问静态成员
  8. f1(); // 或Citizen.f1(); 或 this.f1();
  9. System.out.println(country);
  10. System.out.println(name);
  11. \}

}

public class TestStaticMethod {

  1. public static void main(String\[\] args)\{
  2. new TestStaticMethod();
  3. Citizen.f1(); // 直接通过类名访问f1()
  4. Citizen citizen = new Citizen();
  5. citizen.f2();
  6. citizen.f1(); // 可以通过引用访问f1()
  7. \}

}

程序运行结果:

china

china

china

Tom

china

在使用类的静态方法时,需要注意以下三点。

(1)在静态方法里只能直接访问类中其他的静态成员(包括变量和方法),不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例后才可使用。

(2)静态方法不能以任何方式引用this和super关键字。因为静态方法在使用前是不需要创建任何对象的,当静态方法被调用时,this所引用的对象也许根本还没有产生。

(3)子类只能继承、重载、隐藏父类的静态方法,不能重写父类的静态方法,也不能把父类不是静态的方法重写成静态方法。

除此之外,需要明确一下几点:

类方法独立于该类的任何对象,其他类不用实例化即可调用它们。

类方法可以调用其它的类方法

类方法只能访问static变量

类方法不能以任何形式引用this 和super。

对成员变量的操作只能放在方法中,方法可以对成员变量和该方法体中声明的局部变量进行操作。在声明类的成员变量时可以同时赋予初值,如:

class A {

  1. int a=12;
  2. float b=12.56f;

}

但是不可以这样做:

class A {

  1. int a;
  2. float b;
  3. a = 12; //非法,这是赋值语句(语句只能出现方法体中),不是变量的声明
  4. b = 12.56f; //非法

}

实例方法既能对类变量操作也能对实例变量操作,而类方法只能对类变量进行操作。

实例方法可以调用该类中的实例或类方法;类中的类方法只能调用该类的类方法,不能调用实例方法。为加深认识,再给出如下例子:

不正确的引用:

class StaticError {

  1. String mystring = hello”;
  2. public static void main(String args\[\]) \{
  3. System.out.println(mystring);
  4. \}

}

编译时错误信息: nonstatic variable mystring cannot be referenced from a static context

为什么不正确:只有对象的方法可以访问对象的变量。

解决的办法有二:

  1. 将变量改成类变量

class StaticError

{

  1. static String mystring = hello”;
  2. public static void main(String args\[\])
  3. \{
  4. System.out.println(mystring);
  5. \}

}

  1. 先创建一个类的实例,再通过该对象访问该变量。

class NoStaticError

{

  1. String mystring = hello”;
  2. public static void main(String args\[\])
  3. \{
  4. NoStaticError noError;
  5. noError = new NoStaticError();
  6. System.out.println(noError.mystring);
  7. \}

}

[java]static关键字的四种用法 https://www.cnblogs.com/dotgua/p/6354151.html

final关键字

final关键字有最终、不变的意思,可以修饰成员变量,也可以修饰方法和类,而且会对类的继承产生很大的影响。通过final关键字的修饰可以改变其特性。在后续的继承部分介绍。

super关键字

super指一个对象的直接父类对象。super可以用来引用父类中的(被覆盖的)方法、(被隐藏的)变量及构造方法。在后续的继承部分介绍。

abstract关键字

abstract关键字是表示抽象的意思。关键字abstract和final的含义完全是相反的。所谓抽象,就好比在日常生活中人们设计的图纸,而这个图纸就好比是一个抽象的房子似的,需要把房子盖起来实现这个图纸。在Java里,抽象类里最少要含有一个抽象方法,让它的子类去实现这个抽象方法,抽象类里也可以定义一些方法。在后续的继承部分介绍。

Java的包(package)

在Java中,为了组织代码的方便,可以将功能相似的类放到一个文件夹内,这个文件夹,就叫做包(package)。

包不但可以包含类,还可以包含接口和其他的包。

我们可以编写自己的包,也可以使用由系统提供的包(类库)。

【系统提供的包如java.lang,此包中包含了各种定义java语言时必须的类,这些类能够以其他类不能使用的方式访问java的内部。其中的类包括:
Object类:java中最原始、最重要的类,每个java类都是它的子类,它实现了每个类都必须具有的基本方法。
基本类型包装器:Boolean, Character, Number, Double, Float, Integer, Long。
String类:字符串类。
Math类:数学函数的集合。
执行线程:类Thread, ThreadGroup, 接口Runable。
异常和错误:类Exception, Error, 接口Throwable。
运行环境类:可以通过类Runtime和System访问外部系统环境。System类的两个常用功能就是访问标准输入/输出流和错误流、退出程序。】

当在一个源文件中定义多个类,并且还有import语句和package语句时,要特别注意这些规则:

☆一个源文件中只能有一个public类。

☆一个源文件可以有多个非public类。

☆源文件的名称应该和public类的类名保持一致。例如:源文件中public类的类名是Employee,那么源文件应该命名为Employee.java。

☆如果一个类定义在某个包中,那么package语句应该在源文件的首行。

☆如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。

☆import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

【Java中类名,方法,变量,包名等大小写规范

类名:所有单词首字母大写 例如: ClassPerson 中的P需要大写

方法名: 一般用动词开头 采用驼峰方式 首字母小写,后面的单词首字母大写, 例如: int driveCar();

变量名: 一般首字母必须小写,如果该变量名有多个单词组成,后面的单词首字母大写,单词与单词之间不要使用”_“做连接,例如: int count; int age; 如果使用关键字static final定义的单词全部大写,并用_(下划线)连接,如:

static final int VALUE_ONE = 11;

包名:由小写英文字母组成,一般采用公司域名倒过来写 例如:Package com. china.jx 】

包以”.”来表示层级关系,例如Package com. china.jx 表示的目录为..\com\china\jx

又如package p1.p2 表示的目录为 ..\p1\p2。

package 关键字可以声明一个包,例如:

package p1.p2;

必须将 package 语句放在所有语句的前面,例如:

package p1.p2;

public class Test {

  1. public Test()\{
  2. System.out.println("我是Test类的构造方法");
  3. \}

}

表明 Test 类位于 p1.p2 包中。

Java中包、公共类与Java源码文件的关系

一个项目(Project)可以有几个包,包是为了更好的规划整理你写的各个类,将相关的类放在一个包中。包是为了更好的规划整理你写的各个类。

在Java源文件中,如果使用package语句,则package语句应作为第一条可执行语句(它的前面只能有注释或空行)。package语句的一般格式为:

package 包名;

包名可以是一个合法的标识符,也可以是若干个标识符加“.”分割而成,包的名字有层次关系,各层之间以点分隔。包层次必须与Java开发系统的文件系统结构相同。例如 package aaa.bb.image

则此文件必须存放在aaa\bb\image目录下。

如果源程序中省略了package语句,源文件中所定义命名的类被隐含地认为是无名包,即源文件中定义命名的类在同一个包中,但该包没有名字。

包(package)分为无名包和有名包,无名包的好处是无需对包作任何声明,可以拿来方便快捷使用,但也因为无名包没有名字,所以其它包无法用import语句导入访问无名包中的类,所以说无名包只适合在局部应用。而有名包就可以被其它包所访问,先用import语句导入,再用“包名.类名”方式访问指定包中指定的类。

包(package)是一个为了方便管理组织java文件的组织和管理方式。包内包含有一组类。可以使用import关键字来导入一个包。例如使用import java.util.* 就可以导入java.util包里面的所有类。所谓导入这个包里面的所有类,就是在import声明这个包名以后,在接下来的程序中可以直接使用该包中的类。

一个Java源代码文件必须有一个后缀名.java,此文件通常被称为编译单元。在编译单元内有且仅有一个public类,否则编译器就不会接受。该public类的名称必须与文件的名称相同(包括大小写,但不包括后缀名.java)。

当编译一个.java文件(即一个编译单元)时,在.java文件中的每个类都会有一个输出文件,而该输出文件的名称与.java文件中每个类的名称相同,只是多了一个后缀名.class。因此在编译少量.java文件之后,会得到大量的.class文件。每个.java文件都是一个构件,如果希望许许多多的这样的构件从属于同一个群组,就可以在每一个.java文件中使用关键字package。而这个群组就是一个类库。

例如,假设文件的名称是Apple.java,这就意味着在该文件中有且仅有一个public类,该类的名称必须是Apple(注意大小写):

package fruit;

public class Apple

{

  1. //...

}

上面的代码已经将Apple类包含在了fruit包中,现在如果有人想使用Apple或者是fruit中的任何其他public类,就必须使用关键字import来使fruit中的名称可用。

import fruit.*;

public class ImportApple

{

  1. public static void main(String\[\] args)
  2. \{
  3. Apple a=new Apple();
  4. \}

}

或者使用带包名的限定名称:

public class QualifiedApple

{

  1. public static void main(String\[\] args)
  2. \{
  3. fruit.Apple a=new fruit.Apple();
  4. \}

}

显然使用关键字import使代码更加简洁。

小结(这个小结有点长)

Java的类分为两大部分:系统预先定义的类和用户自定义类。Java的类库就是系统定义的类,它是系统提供的已实现的标准类的集合。本文重点介绍后者。

类的基本定义包括两部分:类声明和类体。基本格式为:

class 类名 {

  1. 类体的内容

}

class 是关键字,用来定义类。“class 类名”是类的声明部分,类名必须是合法的Java标识符。两个大括号以及之间的内容是类体。如:

class People {

}

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NuZHMxMjM_size_16_color_FFFFFF_t_70 6

其中

☆关键字extends用于直接继承父类,本文未介绍。

☆关键字implements用于要实现的接口,本文未介绍。

☆类修饰符是下列之一:

[public | abstract | final]

public 该关键字声明的类可以在其他的任何类中使用。默认时,该类只能被同一个程序包中其他的类使用。

abstract — 抽象类,没有具体对象的概念类,没有具体实现功能,只用于扩展子类。例如:“鸟”,由此可以派生出“鸽子”、“燕子”等具体类。

final — 最终类,表示该类已经非常具体,没有子类可扩展。

如果类名使用字母,建议名字的首字母使用大写字母。类名最好容易识别、见名知意。当类名由几个“单词”复合而成时,每个单词的首字母使用大写,如HelloGame。

类的关键是抓住事物的两个方面:属性和行为,即数据以及在数据上所进行的操作,因此类体的内容由如下所述的两部分构成:

变量的声明:用来存储属性的值(体现对象的属性)。

方法的定义:方法可以对类中声明的变量进行操作,即给出算法(体现对象所具有的行为功能)。

(成员)变量的定义格式:

[变量修饰符] 变量数据类型 变量名1,变量名2[=变量初值]…;

其中

变量修饰符:

[public |protected |private |package] [static] [final]

public 、protected 、private为可访问性修饰符:

  1. public: 任何其它类、对象只要可以看到这个类的话,那么它就可以存取变量的数据,或使用方法。
  2. protected:同一类,同一包可以使用。不同包的类要使用,必须是该类的子类。
  3. private:不允许任何其他类存取和调用。
  4. default:(前边没有修饰字的情况)在同一包中出现的类才可以直接使用它的数据和方法.

static — 说明该成员变量是类变量。

final — 说明为常量使用。

变量的类型可以是Java中任意的数据类型,包括简单类型,类,接口,数组。

变量的分类:

变量声明部分所声明的变量被称作类的成员变量。在方法体中声明的变量和方法的参数被称作局部变量。

成员变量在整个类内都有效,局部变量只在声明它的方法内有效。方法参数在整个方法内有效,方法内的局部变量从声明它的位置之后开始有效。

成员变量又分为实例变量和类变量。在声明成员变量时,用关键字static给予修饰的称作类变量,否则称作实例变量(类变量也称为static变量,静态变量),例如:

class Dog {

  1. float x; //实例变量
  2. static int y; //类变量

}

如果局部变量的名字与成员变量的名字相同,则成员变量被隐藏,即这个成员变量在这个方法内暂时失效。例如:

class Tom {

  1. int x=98,y;
  2. void f() \{
  3. int x = 3; //局部变量x和成员变量x名字相同
  4. y = x; //y得到的值是3,不是98。如果方法f 中没有 “int x=3;”,y的值将是98
  5. \}

}

变量的名字除了符合标识符规定外,建议首单词的首字母使用小写;如果变量的名字由多个单词组成,从第2个单词开始的其它单词的首字母使用大写。

方法

方法定义的完整形式:

[方法修饰符] 返回类型 方法名称(参数1,参数2,…) [throws exceptionList]

{ … statements; //方法体:方法的内容

}

其中

方法修饰符

[public |protected | private ][static][final |abstract][native][synchronized]

返回类型可以是任意的Java数据类型,当一个方法不需要返回值时,返回类型为void。

参数的类型可以是简单数据类型,也可以是引用数据类型(数组、类或接口),参数传递方式是值传递。

方法体是对方法的实现。它包括局部变量的声明以及所有合法的Java指令。局部变量的作用域只在该方法内部。

一个方法必须声明其返回类型,如果无返回值,则必须声明其返回类型为void。

当return语句带有返回值时,它与方法定义的返回类型的关系必须符合如下几种情况之一:

☆当方法声明的返回类型是基本数据类型时,返回值的数据类型必须与返回类型一致。

☆当方法声明的返回类型是一个类时,返回对象的数据类型必须是与方法声明的返回类相同的类或其子类。

☆当方法声明的返回类型是一个接口类型时,返回的对象所属的类必须实现这个接口。

Java支持方法名重载,即多个方法可以共享一个名字。本文未介绍,后续介绍。

方法的参数传递

Java的参数传递方式是传递值,也称为“值传递”。

参数有两种类型:

基本类型的变量名是变量本身;

引用类型变量的名字是变量的存储地点。

对于这种值传递方式要区分如下两种情况:

(1)当参数变元是一个简单类型时,值传递意味着这个方法不能改变参数变元的值。

(2)当参数变元是一个引用类型时,值传递意味着这个方法不能改动这个对象的引用,但是方法可以调用该对象的方法来修改该对象中可访问的变量。

构造方法是一种特殊方法。如果类中没有编写构造方法,系统会默认该类只有一个构造方法,该默认的构造方法是无参数的,且方法体中没有语句。如果定义了一个或多个构造方法,那么Java不提供默认的构造方法。

构造方法的名字必须与它所在的类的名字完全相同,而且没有类型,构造方法也可以重载。如:

class trapezoid {

float 上底,下底,高;

  1. trapezoid() \{ //构造方法
  2. upperBottom=60;
  3. lowerBottom=100;
  4. height=20;
  5. \}
  6. trapezoid(float x,int y,float h) \{ //构造方法
  7. upperBottom=x;
  8. lowerBottom=y;
  9. height=h;
  10. \}

}

需要特别注意的是,构造方法没有类型,下列Point类中只有一个构造方法,其中的void Point(int a,int b)和int Point()都不是构造方法。

class Point {

  1. int x,y;
  2. Point() \{ //是构造方法
  3. x = 1;
  4. y = 1;
  5. \}
  6. void Point(int a,int b) \{ //不是构造方法(该方法的类型是void)
  7. x = a;
  8. y = b;
  9. \}
  10. int Point() \{ //不是构造方法(该方法的类型是int)
  11. return 12;
  12. \}

}

类中的方法(非构造方法)可分为实例方法和类方法。方法声明时,方法类型前面不加关键字static修饰的是实例方法、加static修饰的是类方法(静态方法)。例如:

class A {

  1. int a;
  2. float max(float x,float y) \{ //实例方法
  3. \}
  4. static float jerry() \{ //类方法
  5. \}
  6. static void speak(String s) \{ //类方法
  7. \}

}

可以使用new运算符创建对象

类名 对象名 = new 构造方法名([参数列表]);

上句和如下两句:

类名 对象名;

对象名 = new 构造方法名([参数列表]);

等价。

赋值号右边的new是为新建对象开辟内存空间的运算符,用new运算符开辟新建对象的内存之后,系统自动调用构造方法初始化该对象。

若类中没有定义构造方法,系统会调用默认的构造方法。

对于下列Point类:

class Point {

  1. int x,y;
  2. Point(int a,int b) \{
  3. x=a;
  4. y=b;
  5. \}

}

如果创建2个对象:

Point p1 = new Point(5,15);

Point p2 = new Point(8,18);

p1和p2的引用不同

参见下图:

20201225174410456.png

假如在程序中使用了如下的赋值语句:p1 = p2;

把p2中的引用赋给了p1,p1和p2的引用相同,参见下图:

20201225174410446.png

对于两个同类型的引用型变量,如果具有同样的引用,就会用同样的实体,因此,如果改变参数变量所引用的实体,就会导致原变量的实体发生同样的变化。

this关键字

this指对象自身,this可以出现在实例方法和构造方法中,但不可以出现在类方法中。

this关键字出现在类的构造方法中时,代表使用该构造方法所创建的对象。

实例方法必须只能通过对象来调用,不能用类名来调用。当this出现实例方法中时,代表正在调用该方法的当前对象。

实例方法可以操作类的成员变量,当实例成员变量在实例方法中出现时,默认的格式是:

this.成员变量

编写代码时可以省略this。但是,当实例成员变量的名字和局部变量的名字相同时,成员变量前面的“this.” 就不可以省略。

静态成员、静态方法中不能用this和super关键字。

包(package)

由于Java编译器为每个类生成一个字节码文件,且文件名与类名相同,因此同名的类有可能发生冲突。为了解决这一问题,Java提供包来管理类名空间。

package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。

声明包的格式为:

package pkg1[.pkg2[.pkg3…]];

Java编译器把包对应于文件系统的目录管理,package语句中,用 . 来指明目录的层次。例如:

package myclass.graphics;

这条语句指定这个包中的类生成的类文件存储在目录 myclass/graphics 下。

包的使用

通过以下几种方法之一进行:

1)装载整个包

  1. 利用import 语句载入整个包。例如:
  2. import graphicPackage.\*;

该语句必须位于源程序中的任何类和接口定义之前。

2) 装载一个类或接口

  1. 有时只需要某个包中的一个类或接口,无需装载整个包。如:
  2. import graphicPackage.Circle;

3) 直接使用包名作类名的前缀

如果没有使用import语句装载某个包,可以直接在所需要的类名前加上包名作为前缀。例如: graplicPackage.Rectangle rectG;

发表评论

表情:
评论列表 (有 0 条评论,246人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java面向对象编程·

    本文章对面向对象编程中的包和继承做了深度讲解,并且附上了思考习题,希望各位读者在学习完本文章后能对包和继承有一个更深入的理解,此文章有任何的错误都可以在评论区指正,作者会...