一.方法的返回值

  当我们在程序中调用方法时,虚拟机将会跳转到对应的方法中去执行。当以下几种情况发生时,虚拟机将会回到调用方法的语句并继续向下执行:

  • 执行完方法中所有的语句;
  • 遇到return语句;
  • 方法抛出一个异常(有关异常的内容将会在后面的文章中讨论)。

  这里我们重点介绍return语句。return语句用来返回一个值,当虚拟机遇到return语句时将会立刻结束当前方法并带着返回值回到调用此方法的地方。在声明方法时,返回值的类型要和return语句里返回的值的类型一致。如果方法没有需要返回的值,可以将返回值设置为void。在返回值类型为void的方法中也可以使用return语句,格式为:

return;

  它的作用仅仅是为了结束函数的执行。如果在返回值类型为void的方法中返回一个值,将会出现编译错误。

  返回值的类型既可以是基本数据类型,也可以是引用类型。当返回值类型是基本数据类型时,它返回的是基本数据类型的值;当返回值类型是引用类型时,它返回的是对这个对象的引用。

  当一个方法的返回值类型是一个类时,返回的值必须是对这个类或它的子类的对象的引用。当返回值类型是一个接口时,返回的值必须是对实现了这个接口的类的对象的引用。

二.this关键字

  在方法或构造器中,this用来引用当前对象,可以通过this来访问当前对象的所有成员。

  使用this关键字的最常见的原因是因为域会被方法中的参数或变量覆盖。此时,使用this关键字可以非常清晰的分辨它们而不是给变量或参数换一个名字。就像下面这样:

public class Point {
public int x = 0;
public int y = 0; public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

  this关键字还可以用来代表构造方法。下面是另一个Rectangle类的实现:

public class Rectangle {
private int x, y;
private int width, height; public Rectangle() {
this(0, 0, 1, 1);
}
public Rectangle(int width, int height) {
this(0, 0, width, height);
}
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
...
}

  这样可以提高代码的复用性而不必为每一个构造器都编写类似的代码。在一个构造器中调用另一个构造器时,必须将这行代码放在第一行。

三.控制对类成员的访问权限

  权限修饰符决定了其他类是否可以使用某个类或访问某个类的成员。有以下两种级别的权限控制。

1.类级别的访问权限

  类级别的访问权限通过加在class关键字之前的权限修饰符决定,它控制的是其他类对该类的访问权限,只有public或包私有(就是没有权限修饰符)两种。public权限意味着其他所有的类都可以使用该类,而包私有权限则意味着只有在同一个包(有关包的概念将会在下一篇教程中介绍)中的类才可以使用该类。你可能在在后面的有关内部类的文章中会看到class关键字前面可以使用private或protected,但这个类实际上已经是某个类的成员,它的访问权限属于成员级别的访问权限。

2.成员级别的访问权限

  成员级别的访问权限通过加在类的成员之前的权限修饰符来决定,它控制的是其他类对该成员的访问权限,分为public、protected、包私有和private。public意味着其他所有的类都可以访问该成员,protected意味着只有该类所在包中的其他类或该类的子类才可以访问该成员,包私有意味着只有该类所在包中的其他类可以访问该成员,private意味着该成员只能在该类中访问。下面的表格显示了它们的关系:



  例如,下面的几个类关系如下:



  下面的表格显示了这几个类对Alpha类中被不同权限修饰符修饰的成员的访问权限:



  一般来说应该尽可能的对域使用private,除非它是一个公共的常量,这种情况下应该使用public。

四.静态成员

1.静态域

  当创建一个类的许多对象时,每个对象都有属于自己的非静态域,这些变量存储在不同的内存位置。有时,我们希望某个域被所有对象共享,或者说这个域属于整个类而不是每个对象。使用static修饰符的域被称为静态域或类变量,该类的每一个实例共享这个变量,这个变量存在于内存中的一个固定的位置。该类的任何实例都可以更改静态域的值,也可以在不创建类实例的情况下访问静态域。

  例如,假设要创建多个Bicycle对象并为每个对象分配一个ID,第一个对象ID为1。此ID号对于每个对象都是唯一的,因此是一个实例变量。同时,您需要一个变量来表示Bicycle已创建的对象数,以便知道要分配给下一个对象的ID。这个域与任何单个对象无关,而与整个类有关,可以将这个域设置为静态域。例如:

public class Bicycle {

    private int cadence;
private int gear;
private int speed;
private int id;
private static int numberOfBicycles = 0; public Bicycle(int startCadence, int startSpeed, int startGear){
gear = startGear;
cadence = startCadence;
speed = startSpeed;
id = ++numberOfBicycles;
}
}

  每当创建一个Bicycle类的实例时,numberOfBicycles的值都会加1,然后将它的值作为ID分配给这个实例。

2.静态方法

  静态方法是指使用static修饰的方法。和静态域一样,它属于类,而不是某个实例。可以通过类名去调用它而不用先创建类的实例。使用下面的语法调用静态方法:

ClassName.methodName(args);

  尽管也可以通过创建实例去访问类的静态方法,但不鼓励这么做。通常使用静态方法去访问静态域,例如,可以通过一个静态方法去访问Bicycle类的静态域numberOfBicycles:

public static int getNumberOfBicycles() {
return numberOfBicycles;
}

  静态域、静态方法、非静态域和非静态方法之间的访问规则如下:

  • 非静态方法可以访问其他非静态方法和非静态域;
  • 非静态方法可以访问静态方法和静态域;
  • 静态方法可以访问其他静态方法和静态域;
  • 静态方法不能访问非静态方法和非静态域。

  总的来说就是非静态方法可以访问所有成员,而静态方法只能访问静态成员。

3.常量

  static关键字常和final关键字一起用来定义常量。final修饰符意味着域的值不能改变。例如,下面的变量定义了一个常量PI,它的值是圆周率:

static final double PI = 3.141592653589793;

  被final修饰的变量一旦赋值以后就不能再修改。常量的命名规范是大写每个字母并且使用下划线(_)分隔每个单词。

  如果使用fianl修饰引用类型的变量,并不是说这个变量引用的对象不能发生任何改变,而是说这个变量不能再去引用别的对象。

五.域的初始化

  现在回忆一下我们在《Java基础教程(5)--变量》一文中学习过的有关域的默认值的内容。在声明域时,如果不对它进行显式的初始化,编译器将赋予它一个默认值。如果是基本数据类型的域,根据它的类型,它的默认值将会是:



  如果是引用类型的域,它的默认值将会是null。

  除了这种默认的初始化行为,是否还有其他方式可以在使用对象前对域进行初始化呢?答案是肯定的。下面我们将分别介绍非静态域和静态域初始化行为。

1.初始化非静态域

(1)默认初始化

  正如上文所说,在声明域时,如果不对它进行赋值,编译器将赋予它一个默认值。例如:

public class Bag {
public int capacity;
}

  在创建完Bag类的对象以后,capacity域的值将会是0。

(2)显式初始化

  也可以在声明域的同时给它提供一个初始值,就像下面这样:

public class Bag {
public int capacity = 10;
}

(3)在构造方法中进行初始化

  构造方法也可以对域进行初始化:

public class Bag {
public int capacity; public Bag(int capacity) {
this.capacity = capacity;
}
}

(4)非静态代码块

  下面定义了一个非静态代码块对域进行初始化:

public class Bag {
public int capacity; {
capacity = 10;
}
}

  与非静态代码块对应的是静态代码块,马上会在下文中介绍。编译器会将初始化块拷贝到每个构造方法最前面的位置,因此,这种方法可以用来在多个构造方法中共享代码块。一个类中可以定义多个代码块,它们可以出现在类体中的任何位置,它们的执行顺序与源代码中的定义顺序相同。

(5)调用方法进行初始化

  下面调用了一个方法对域进行初始化:

public class Bag {
public int capacity = initializeCapacity(); private int initializeCapacity() {
return 10;
}
}

  当然实际编写代码过程中这个方法的逻辑肯定没有这么简单,这里只是为了举个例子。如果子类想要重用这个初始化方法可以将方法的访问权限设置为protected,但是同时应该使用final修饰符来禁止子类重写(有关重写的内容会在后续文章中进行介绍)这个方法(PS:Java Tutorial里说在实例初始化期间调用非final方法可能会导致问题,我暂时没想到会出现什么问题,如果有知道的大神还请点拨一二,下面是原文的截图)。



  在了解完这几种初始化的方式以后,我们就可以在编写代码时根据它们的性质灵活选择。但是如果这几种初始化方式同时出现的话,它们的顺序又是怎么样呢?由于默认初始化和显示初始化都是在声明域的同时完成的,这两种方式只能同时出现一种;又由于使用方法对域进行初始化的方式只是一种获取值的形式,并不属于初始化时机的讨论范围之内,它在显式初始化、构造方法和代码块中都可以使用,所以实际上可以将这五种方式总结为三种。它们的顺序如下(大于号代表左边的顺序早于右边的):

默认初始化、显示初始化>非静态代码块>构造方法

2.初始化静态域

  与非静态域相比,由于静态域的初始化与类是否实例化无关,所以不讨论在构造方法中对静态域赋值的方式(不代表不可以这么做,但是不建议将静态域与类的实例之间建立任何联系)。其余四种形式都有与之对应的初始化行为,默认初始化与显式初始化较为简单,这里不再赘述。

  与非静态域的非静态代码块对应的是静态代码块,一个类可以有任意数量的静态代码块,它们可以出现在类中的任何位置,编译器保证按照它们在源代码中出现的顺序调用静态代码块。

  也可以使用静态方法对静态域赋值,就像非静态域那样。

  静态域的初始化顺序如下:

默认初始化、显式初始化>静态代码块

3.初始化final域

  由于编译器不会对final域进行默认初始化,因此必须手动对其进行初始化,除此之外,其他的与上面提到的初始化方式相同,即对于非静态final域而言,可以使用显示初始化、非静态代码块和构造方法对其进行初始化,而对于静态final域来说,可以使用显示初始化和静态代码块来对其进行初始化。

Java基础教程(12)--深入理解类的更多相关文章

  1. Java基础教程(18)--继承

    一.继承的概念   继承是面向对象中一个非常重要的概念,使用继承可以从逻辑和层次上更好地组织代码,大大提高代码的复用性.在Java中,继承可以使得子类具有父类的属性和方法或者重新定义.追加属性和方法. ...

  2. Java基础教程:注解

    Java基础教程:注解 本篇文章参考的相关资料链接: 维基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3 注解基础与高级应用:http: ...

  3. Java基础教程(5)--变量

    一.变量 1.变量的定义   正如上一篇教程<Java基础教程(4)--面向对象概念>中介绍的那样,对象将它的状态存在域中.但是你可能仍然有一些疑问,例如:命名一个域的规则和惯例是什么?除 ...

  4. Java基础教程:泛型基础

    Java基础教程:泛型基础 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚 ...

  5. Java基础教程:面向对象编程[1]

    Java基础教程:面向对象编程 内容大纲 Java语言概述 Java语言特点 1.Java为纯面向对象的语言,它能够直接反映现实生活中的对象.总之,Everything is object! 2.平台 ...

  6. Java基础教程(25)--I/O

    一.I/O流   I/O流表示输入源或输出目标.流可以表示许多不同类型的源和目标,例如磁盘文件.设备.其他程序等.   流支持许多不同类型的数据,包括字节.原始数据类型.字符和对象等.有些流只传递数据 ...

  7. Java基础教程:Java内存区域

    Java基础教程:Java内存区域 运行时数据区域 Java虚拟机在执行Java程序的过程种会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟 ...

  8. Java基础教程:多线程基础(5)——倒计时器(CountDownLatch)

    Java基础教程:多线程基础(5)——倒计时器(CountDownLatch) 引入倒计时器 在多线程协作完成业务功能时,有时候需要等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能,在这种 ...

  9. Java基础-类加载机制与自定义类Java类加载器(ClassLoader)

    Java基础-类加载机制与自定义类Java类加载器(ClassLoader) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于类加载器的概念和分类我就不再废话了,因为我在之前的笔 ...

随机推荐

  1. eclipse创建Maven工程没有Maven Dependencies

    记一次eclipse创建Maven工程没有Maven Dependencies的坑 Eclipse版本:luna版 maven用的3.5.0 配置本文不提. 这辆天用Eclipse创建maven工程 ...

  2. IPv6 Scapy Samples

    IPv6 ICMP icmp ipv6 request i=IPv6() i.dst="2001:db8:dead::1" q=ICMPv6EchoRequest() p=(i/q ...

  3. leetcode 91. 解码方法 JAVA

    题目: 一条包含字母 A-Z 的消息通过以下方式进行了编码: 'A' -> 1 'B' -> 2 ... 'Z' -> 26 给定一个只包含数字的非空字符串,请计算解码方法的总数. ...

  4. python使用venv环境报Python.h : No such file or direc

    这个是因为域名 install python36的时候没有按照python36-devel ,只需要 yum install python36-devel 然后重新安装所需模块即可.

  5. jquery判断滚动到某个div显示底部按钮

    判读滚动某个div显示底部按钮 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta char ...

  6. 【xsy1144】选物品 主席树

    题目大意:$N$ 件物品摆成一排,给每个物品定义两个属性 $A$ 和$ B$,两件物品的 差异度 定义为它们两种属性的差的绝对值中较大的一个.如果要求出一些物品的差异度,我们先定义一个 理想物品,使它 ...

  7. XCode9的新变化

    XCode9已经随着ios11的发布发布了,那么在这个XCode9版本中有哪些变化呢? 1 折叠代码 焦点在方法的实现体的方法名上,按comman键,则整个函数会被框住.用来标志这个方法的起点和终点 ...

  8. 【Java并发编程】:加锁和volatile变量

    加锁和volatile变量两者之间的区别: 1.volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比syn ...

  9. grep常用用法

    grep常用用法 [root@www ~]# grep [-acinv] [--color=auto] '搜寻字符串' filename 选项与参数: -a :将 binary 文件以 text 文件 ...

  10. 【转】Entity framework 6 编写的通用数据类

    原文:http://blog.csdn.net/laokaizzz/article/details/25730813 准备参考