Tutorialspoint Java 面向对象设计教程

来源：易百教程

Java面向对象设计™

本教程将通过示例了解Java中面向对象(OOP)的概念。讨论面向对象编程的特性，以及编写面向对象程序包括创建类，并从这些类创建对象，以及创建应用程序，这些应用程序是使用这些对象的独立可执行程序。

如果以前从未使用过面向对象的编程语言，在开始编写任何代码之前，您需要学习一些基本概念。本课将向您介绍对象，类，继承，接口和包。在这些概念如何与真实世界相关，同时提供Java编程语言的语法介绍。

对象是什么？

对象是相关状态和行为的软件包。软件对象通常用于模拟在日常生活中发现的真实世界对象。本课解释如何在对象中表示状态和行为，介绍数据封装的概念，并解释以这种方式设计软件的好处。
更多关于对象，可点击以下链接了解 -

http://www.yiibai.com/java_object_oriented_design/java_class_instance.html

类是什么？

类是创建对象的蓝图或原型。这个部分定义了一个模型化真实世界对象的状态和行为的类。 它有意地专注于基础，显示一个简单的类如何干净地建模状态和行为。
更多关于类的介绍，可点击以下链接了解 -

http://www.yiibai.com/java_object_oriented_design/java_class_base.html

继承是什么？

继承提供了一个强大和自然的机制来组织和结构化软件。本节介绍类如何从其父类继承状态和行为，并解释如何使用Java编程语言提供的简单语法从另一个类派生一个类。
更多关于继承的介绍，可点击以下链接了解 -

http://www.yiibai.com/java_object_oriented_design/java_inheritance.html

接口是什么？

接口是类和外部世界之间的契约。当一个类实现一个接口时，它承诺提供由该接口发布的行为。本节定义一个简单的接口，并解释实现它的任何类的必要的更改。
更多关于接口的介绍，可点击以下链接了解 -

http://www.yiibai.com/java_object_oriented_design/java_interface.html

包是什么？

包是以逻辑方式组织类和接口的命名空间。将代码放入包中使得大型软件项目更容易管理。 本节解释为什么包是这么有用，并介绍由Java平台提供的应用程序编程接口(API)。
更多关于包的介绍，可点击以下链接了解 -

http://www.yiibai.com/java_object_oriented_design/java_import.html

类是定义对象的数据字段和方法的模板，蓝图或约定。对象是类的实例。一个类可以创建多个实例。Java类使用变量来定义数据字段和方法来定义操作。此外，类提供了一种特殊类型的方法，称为构造函数，调用它们来创建一个新对象。构造函数可以执行任何操作，但构造函数一般设计为执行初始化操作，例如：初始化对象的数据字段。
参考以下图，类可被实例化为一个个的对象 -

对象由属性和方法组成。 属性是定义对象的特征; 属性中包含的值将同一个类的对象区分开来。为了更好地理解这一点，以手机(Mobile)作为一个对象。手机(Mobile)具有像模型，制造商，成本，操作系统等特性。因此，如果创建“华为”手机对象和“IPhone”手机对象，它们的特性是不一样的，因此每个对象是不一样的。对象的属性的值也称为对象的状态。

Java面向对象编程有三个主要特点。

封装

继承

多态性

我们对上面每个特性作更详细讨论和学习。

封装

封装意味着将所有变量(实例变量)和方法放在一个称为类的单元中，在对象内隐藏数据和方法。 封装提供了保证数据和方法免于被意外更改的安全性。程序员有时将封装称为使用“黑盒”，或者可以使用而不考虑内部实现机制。 程序员可以访问和使用黑盒中包含的方法和数据，但不能更改它们。下面示例显示了Mobile类和属性，在使用构造函数参数创建对象时设置一次对象信息。可以使用具有公共(public)访问修饰符的getXXX()方法来访问属性。

package oopsconcept;
public class Mobile {
    private String manufacturer;
    private String operating_system;
    public String model;
    private int cost;
    //Constructor to set properties/characteristics of object
    Mobile(String man, String o,String m, int c){
        this.manufacturer = man;
        this.operating_system=o;
        this.model=m;
        this.cost=c;
    }
    //Method to get access Model property of Object
    public String getModel(){
        return this.model;
    }
    // We can add other method to get access to other properties
}

继承

面向对象程序的一个重要特性是继承 - 创建一个新类时共享现有类的属性和方法。继承主要用于代码可重用性。所以利用已有的类并作进一步扩展即可。这就是提高代码的可重用性的一种办法。一般来说，一个定义可以告诉从已有的类派生一个新类，这种实现称为继承。可以查看下面的继承概念示例。这里已有上面的步中创建的Mobile类，它被其他特定类如：Android类和Blackberry类扩展。

Android.java 代码如下 -

package oopsconcept;
public class Android extends Mobile{
        //Constructor to set properties/characteristics of object
        Android(String man, String o,String m, int c){
                super(man, o, m, c);
            }
        //Method to get access Model property of Object
        public String getModel(){
            return "This is Android Mobile- " + model;
        }
}

Blackberry.java 代码如下 -

package oopsconcept;
public class Blackberry extends Mobile{
    //Constructor to set properties/characteristics of object
    Blackberry(String man, String o,String m, int c){
                    super(man, o, m, c);
                }
    public String getModel(){
        return "This is Blackberry-"+ model;
    }
}

多态性

在Java核心中，多态(Polymorphism)是一个容易理解的概念。 多态性定义是：Poly表示许多，morphos表示形式。它描述了允许在基于上下文的不同情况下正确解释相同字或符号的语言的特征。在Java中有两种类型的多态性。 例如，在英语中，动词“run”表示不同的东西，如果在“足迹”，“商业”或“电脑”这个东西关联。那么理解“run”的含义应该是基于与它使用的其他词。面向对象的程序可编写使用相同名称的方法，但在不同的上下文中执行不同的工作。 Java提供了两种实现多态的方法。

1. 静态多态性(编译时多态性/方法重载)：
通过改变方法名称和参数来执行不同方法实现的能力被称为方法重载。在下面的程序中，有三个打印(print())方法，每个方法有不同的参数。当要正确重载一个方法时，可以调用它提供不同的参数列表，并执行相应的方法版本。

package oopsconcept;
class Overloadsample {
    public void print(String s){
        System.out.println("First Method with only String- "+ s);
    }
    public void print (int i){
        System.out.println("Second Method with only int- "+ i);
    }
    public void print (String s, int i){
        System.out.println("Third Method with both- "+ s + "--" + i);
    }
}
public class PolymDemo {
    public static void main(String[] args) {
        Overloadsample obj = new Overloadsample();
        obj.print(10);
        obj.print("Amit");
        obj.print("Hello", 100);
    }
}

执行上面代码，输出结果如下 -

2. 动态多态性(运行时多态性/方法覆盖)

当通过扩展现有类创建子类时，新子类包含在原始超类中定义的数据和方法。换句话说，任何子类对象都具有其父对象的所有属性。然而，有时，超类数据字段和方法不完全适合于子类对象; 在这些情况下可考虑覆盖父类成员。下面举个例子说明继承。

package oopsconcept;
public class OverridingDemo {
    public static void main(String[] args) {
        //Creating Object of SuperClass and calling getModel Method
        Mobile m = new Mobile("Nokia", "Win8", "Lumia",10000);
        System.out.println(m.getModel());
        //Creating Object of Sublcass and calling getModel Method
        Android a = new Android("Samsung", "Android", "Grand",30000);
        System.out.println(a.getModel());
        //Creating Object of Sublcass and calling getModel Method
        Blackberry b = new Blackberry("BlackB", "RIM", "Curve",20000);
        System.out.println(b.getModel());
    }
}

抽象

所有编程语言都提供抽象。可以说，能够解决的问题的复杂性与抽象的种类和质量直接相关。面向对象编程的一个基本要素是抽象。人类通过抽象来管理复杂性。当你开车时，不必关心汽车的确切内部工作(除非你是一个技工)。关心的是通过其接口，如方向盘，制动踏板，加速踏板等与汽车交互。各种汽车制造商有不同的实施汽车工作，但其基本接口没有改变(即仍然使用方向盘，刹车踏板，加速踏板等与汽车互动)。 因此，对汽车的知识是抽象的。

管理抽象的强大方法是通过使用层次化分类。这允许分层复杂系统的语义，将它们分成更易于管理的部分。从外面，一辆汽车是一个单一的对象。汽车由几个子系统组成：转向，制动器，音响系统，安全带，暖气，蜂窝电话等。反过来，这些子系统中的每一个由更专门的单元组成。 例如，声音系统由无线电，CD播放器和/或磁带播放器组成。关键是通过使用分层抽象来管理汽车(或任何其他复杂系统)的复杂性。

抽象类是不完整的东西，不能创建抽象类的实例。如果想使用它，需要通过扩展它来完成或具体的实现。如果一个类不包含任何抽象方法，并且实现了所有抽象方法继承自已实现或扩展的抽象类或接口，那么它被称为具体类。顺便说一下，Java有抽象类的概念，抽象方法，但是变量不能在Java中抽象。

让我们举一个名为Vehicle的Java抽象类的例子。当创建一个类叫Vehicle，知道应该有像start()和Stop()的方法，但不知道每个车辆的启动和停止机制，因为可以有不同的启动和停止机制，例如：可以通过按按钮启动。

抽象的优点是：一个新类型的车辆只需要创建一个新类，扩展 VehicleAbstract 类并实现它的方法：启动-start()和停止-stop()方法的接口都是一样的。

package oopsconcept;
public abstract class VehicleAbstract {
    public abstract void start();
    public void stop(){
        System.out.println("Stopping Vehicle in abstract class");
    }
}
class TwoWheeler extends VehicleAbstract{
    @Override
    public void start() {
        System.out.println("Starting Two Wheeler");
    }
}
class FourWheeler extends VehicleAbstract{
    @Override
    public void start() {
        System.out.println("Starting Four Wheeler");
    }
}

package oopsconcept;
public class VehicleTesting {
    public static void main(String[] args) {
        VehicleAbstract my2Wheeler = new TwoWheeler();
        VehicleAbstract my4Wheeler = new FourWheeler();
        my2Wheeler.start();
        my2Wheeler.stop();
        my4Wheeler.start();
        my4Wheeler.stop();
    }
}

执行上面代码，输出结果如下 -

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java面向对象设计

Java类基础 - Java面向对象设计™

类是什么？

类在面向对象的编程范式的基本单位。它们用作模板来创建对象。
Java中的类可以包含五个组件：

字段（域）

方法

构造器（也叫构造函数）

静态初始化器

实例初始化程序

字段和方法也称为类的成员。类和接口也可以是一个类的成员。类可以有零个或多个类成员。
构造函数用于创建类的对象。所以必须至少有一个类的构造函数。

初始化器用于初始化类的字段。在一个类中可以有零个或多个静态或实例类型的初始化器。

声明一个类

在Java中声明类的一般语法如下 -

<modifiers> class <class name> {
    // Body of the class
}

是对类声明有特殊含义的关键字。类声明可以有零个或多个修饰符（modifiers）。

关键字 class 用于声明一个类。

<class name>是用户定义的类名称，它应该是有效的标识符组成。
每个类都有一个主体（body），它在一对大括号（{}）中指定。类的主体包含其不同的组件，例如字段，方法等。

下面的代码定义了一个名为Dog的类，它具有一个空主体。Dog类不使用任何修饰符。如下代码所示 -

class Dog  {
// 主体是空的，什么也没有定义
}

类中的字段

类的字段表示类的属性。

Dog类的每个对象都有两个属性：名称（name）和性别（gender）。Dog类应包括两个字段的声明：一个表示名称，一个表示性别。

这些字段在类的主体中声明。 在类中声明一个字段的一般语法是 -

<modifiers> class  className {
   <modifiers> <data type> <field name> = initialvalue;
   <modifiers> <data type> fieldName;
}

<modifiers> class  className {
   <modifiers> <data type> <field name> = initialvalue;
   <modifiers> <data type> fieldName;
}

字段声明可以使用零个或多个修饰符。字段的数据类型位于其名称之前。 或者还可以使用值来初始化每个字段的值。

以下代码显示如何向Dog类添加字段：

class Dog  {
    String  name;
    String gender;
}

在java中，一般是用大写字母开始一个类的名称（比较规范，推荐的用法），并大写后续的单词，例如：Table，FirstTime，OrderDetail等等。而使用 table，firstTime，order_detail 等这样的形式来命名类不是一个规范的作法。

字段和方法的名称应以小写字母开头，后面的单词的第一个字母应该大写，例如：name，firstName，orderList等等。

Dog类声明两个字段：name 和 gender。两个字段都是可存储字符串的String类型。
Dog类的每个实例都有这两个字段的副本。

Java中一个类有两种类型的字段：

类字段

实例字段

有时属性属于类本身，不属于该类的任何特定实例。
所有狗的数量应属于狗类本身。狗的数量不存在于狗类的任何特定实例。

只有一个类属性的副本存在，而不考虑实例数。类字段也称为类变量。实例字段也称为实例变量。

name 和 gender是Dog类的两个实例变量。而类的变量必须使用static关键字作为修饰符声明。

以下代码带中，Dog类的声明有一个类变量 - count。如下所示 -

class Dog  {
    String name;           // An  instance variable
    String gender;         // An  instance variable
    static long  count;    // A  class  variable  because of  the   static  modifier
}

类变量称为静态变量。 实例变量被称为非静态变量。

使用示例

在这个示例中，定义一个 Dog 类，并实例化三个 Dog 类的实例，每次实例化类的实例时，自动将类变量count的值加1，用来统计当前有多少 Dog 类被实例化。

class Dog  {
    String name;           // An  instance variable
    String gender;         // An  instance variable
    static long  count;    // A  class  variable  because of  the   static  modifier

    Dog(String name, String gender){
        this.name = name;
        this.gender = gender;
        // 每实例化一个对象， 类变量 count 的值加  1 。
        count = count + 1;
    }
}

class Main {
   public static void main(String[] args) {

       Dog dog1 = new Dog("旺财", "公");
       Dog dog2 = new Dog("阿贵", "公");
       Dog dog3 = new Dog("悠悠", "母");
       // 以下都是访问类变量的方法
       System.out.println("dog1.count=>"+dog1.count);
       System.out.println("dog2.count=>"+dog2.count);
       System.out.println("dog3.count=>"+dog3.count);
       System.out.println("Dog::count=>"+Dog.count);
   }
}

执行上面代码，得到如下结果 -

dog1.count=>3
dog2.count=>3
dog3.count=>3
Dog::count=>3

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java类基础

Java类实例化 - Java面向对象设计™

在本教程中，将学习如何将类实例化，读取类实例的字段等等。

创建类的实例的一般语法如下：

new <Class Constructor>;

new运算符后面是对构造函数的调用。new运算符通过分配堆上的内存来创建类的实例。以下语句创建Dog类的实例：

new Dog();

Dog（）是对Dog类的构造函数的调用。当不向类添加构造函数时，Java编译器会自动添加一个。Java编译器添加的构造函数称为默认构造函数。默认构造函数不接受参数。

类的构造函数的名称与类名称相同。new运算符为类的每个实例字段分配内存。在创建类的实例时不会为类静态变量分配内存。

要访问类的实例的实例变量，必须有它的引用。类的名称在Java中定义了一个新的引用类型。指定引用类型的变量可以存储相同引用类型的实例的引用。

声明一个引用变量，它用于存储Dog类的实例的引用。

Dog anInstance;

Dog是类名，它也是一个引用类型，并且anInstance是该类型的变量。
anInstance是Dog类型的引用变量。anInstance变量可用于存储Dog类的实例的引用。

new运算符为类的新实例分配内存，并返回对该实例的引用。

需要将由new运算符返回的引用存储在引用变量中，如下代码所示 -

Dog anInstance = null;
anInstance = new Dog();

null引用类型

可以为任何引用类型的变量分配一个空(null)值。null值意味着引用变量是指没有对象。

Dog  obj  = null;  // obj  is not  referring to any  object
obj  = new Dog();  // Now, obj  is referring to a  valid Dog  object

可以使用一个 null 文字与比较运算符来检查是否相等，如下代码所示 -

if  (obj == null)  {
    //obj is null
}

if  (obj !=  null)  {
    //obj is not null
}

Java不会混合引用类型和原始类型。所以不能给一个原始类型变量赋null值。

点表示法访问类字段

点（.）符号用于引用实例变量。点符号语法的一般形式是 -

<Reference Variable Name>.<Instance Variable Name>

obj.name引用obj引用实例的 name 实例变量。
要为实例变量 name 分配值，请使用参考以下代码 -

obj.name = "Rectangle";

以下语句将实例变量- name 的值分配给String类型变量-aName：

String aName = obj.name;

要引用类变量，请使用类的名称。

ClassName.ClassVariableName

例如，使用Dog.count来引用Dog类的类变量 - count 。
向类变量 - count 分配新值，如下代码 -

Dog.count  = 1;

要将类变量- count 的值读取到变量中，如下代码 -

long count = Dog.count;

以下代码显示如何使用类实例字段和类变量 -

class Dog {
    static int count = 0;
    String name;
    String gender;
}

public class Main {
    public static void main(String[] args) {
        Dog obj = new Dog();
        // Increase count by one
        Dog.count++;
        // 实例变量赋值
        obj.name = "旺财";
        obj.gender = "公";
        System.out.println("obj.name is :" + obj.name);
        obj.name = "XML";
        System.out.println("obj.name is :" + obj.name);
        String changedName = obj.name;
        System.out.println("changedName is :" + changedName);
    }
}

执行上面的示例代码，得到如下结果 -

obj.name is :旺财
obj.name is :XML
changedName is :XML

字段默认初始化

类的所有字段（静态以及非静态）都将将初始化为默认值。字段的默认值取决于其数据类型。

数字字段（byte, short, char, int, long, float和double）初始化为0。 布尔字段初始化为false。 引用类型字段初始化为null。

下面的代码演示了字段的默认初始化值。

public class Main {
    byte b;
    short s;
    int i;
    long l;
    float f;
    double d;
    boolean bool;
    String str; // 引用类型

    // from yiibai.com
    public static void main(String[] args) {
        Main obj = new Main();
        System.out.println("byte is initialized to " + obj.l);
        System.out.println("short is initialized to " + obj.s);
        System.out.println("int is initialized to " + obj.i);
        System.out.println("long is initialized to " + obj.l);
        System.out.println("float is initialized to " + obj.f);
        System.out.println("double is initialized to " + obj.d);
        System.out.println("boolean is initialized to " + obj.bool);
        System.out.println("String is initialized to " + obj.str);
    }
}

上面的代码生成以下结果 -

byte is initialized to 0
short is initialized to 0
int is initialized to 0
long is initialized to 0
float is initialized to 0.0
double is initialized to 0.0
boolean is initialized to false
String is initialized to null

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java类实例化

Java访问级别 - Java面向对象设计™

类的一个简单名称是在class关键字和{符号之间的名称。
当通过简单的类名称来引用一个类时，编译器在引用类所在的同一个包中查找该类声明。

当然，还可以使用全名来引用一个类，如下所示。

com.yiibai.Dog aDog;

指定类的访问级别的一般语法是

<access level modifier>class <class name> {
    // Body of the class
}

类声明中<access level modifier>只有两个有效值：

无值

public

无值则称为包级访问。具有包级别访问的类只能在声明它的包中访问。

具有公共访问（public）级别修改器的类可以从应用程序中的任何包访问。

package  com.yiibai;

public class Dog  {

}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java访问级别

Java导入(import语句的用法) - Java面向对象设计™

导入(import）声明用于将任何类型导入编译单元。导入(import）声明出现在包声明之后，第一个类型声明之前。

有两种类型的导入声明：

单类型导入声明

按需导入声明

单类型导入声明

单类型导入声明用于从包导入单个类型。 它的形式如下 -

import <fully qualified name of a type>;

以下导入声明从com.yiibai包导入Dog类：

import com.yiibai.Dog;

单一类型的导入声明只从包中导入一个类型（一个类）。要从包中导入多个类(或型类)，请为每个类型使用单独的导入声明。

以下导入声明从pkg1包导入ClassOne，从pkg2包导入ClassTwo和ClassThree，以及从pkg3包导入ClassFour，如以下代码：

import pkg1.ClassOne;
import pkg2.ClassTwo;
import pkg2.ClassThree;
import pkg3.ClassFour;

以下代码使用Dog类的完全限定名称。

public  class  Main{
   public static  void  main(String[]  args)  {
      com.yiibai.Dog jack;  // Uses  full qualified name for the   Dog  class
   }
}

修改后的Main类声明如下：

import com.yiibai.Dog; // Import   the   Dog  class

public  class  Main {
   public static  void  main(String[]  args)  {
      Dog  jack; // Use simple name of  the   Dog  class
   }
}

当编译器在语句中遇到Dog类的简单名称时，如以下声明 -

Dog  jack;

它将通过所有导入声明将简单名称解析为完全限定名称。当它尝试解析简单名称Dog时，它会找到导入声明import com.java2s.Dog 并将导入Dog类。

在上面的语句中使用简单名称Dog时，实际指定使用com.yiibai.Dog类。编译器用以下语句替换上面的语句：

com.yiibai.Dog jack;

导入声明允许在代码中使用类型的简单名称，从而使代码更具可读性。当编译代码时，编译器用其完全限定名替换类型的简单名称。它使用导入声明将类型的简单名称转换为其完全限定名称。

按需导入声明

按需导入声明使用一个导入声明从包导入多个类型。按需导入声明的语法是 -

import <package name>.*;

这里，包名称后面跟着一个点（.）和一个星号（*）。例如，以下按需导入声明从 com.yiibai 包导入所有类型：

import com.yiibai.*;

可以使用按需导入声明重写Main类的代码。

import com.yiibai.*;

public  class  Main {
    public static  void  main(String[]  args)  {
        Dog  jack; // Use simple name of  the   Dog  class
    }
}

静态导入声明

静态导入声明将类型的静态成员（变量/方法）导入到编译单元中。静态导入声明有两种风格：单静态导入和按需静态导入。
单静态导入声明从类型中导入一个静态成员。 按需静态导入声明导入类型的所有静态成员。
静态导入声明的一般语法如下：

单静态import语句：

import static <package  name>.<type name>.<static member name>;

按需静态导入声明-import语句：

import static <package  name>>.<type  name>.*;

System是java.lang包中的一个类，它有一个名为out的静态变量。
当使用System.out时，指的是System类中的静态变量。可以使用静态导入声明从System类导入out静态变量，如下所示：

import static  java.lang.System.out;

以下代码导入System类的out静态变量。

import static  java.lang.System.out;

public class  StaticImportTest  {
    public static  void  main(String[]  args)  {
        out.println("Hello static  import!");
    }
}

上面的代码生成以下结果。

Hello static  import!

java.lang包中的Math类有许多实用程序常量和静态方法。
例如，它有一个类变量名为PI，就是那个圆周率。要使用Math类中的任何静态变量或方法，则需要使用类名Math来限定它们。
例如，可以将PI静态变量称为Math.PI，将sqrt（）方法称为Math.sqrt（）。可以使用以下按需静态导入声明来导入Math类的所有静态成员：

import static  java.lang.Math.*;

实例

以下代码通过导入其静态成员来演示使用Math类。参考如下代码 -

import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.System.out;

public class Main {
  public static void main(String[] args) {
    double radius = 9.9;
    double area = PI * radius * radius;

    out.println("Value of  PI  is: " + PI);
    out.println("Radius of  circle: " + radius);
    out.println("Area of  circle: " + area);
    out.println("Square root of  8.0: " + sqrt(8.0));
  }
}

上面的代码生成以下结果。

Value of  PI  is: 3.141592653589793
Radius of  circle: 9.9
Area of  circle: 307.90749597833565
Square root of  8.0: 2.8284271247461903

以下是关于静态导入声明的一些重要规则。

如果导入两个具有相同简单名称的静态成员，一个使用单静态导入声明，另一个使用静态按需导入声明，则使用单静态import声明导入的静态成员优先。

使用单静态导入声明导入具有相同简单名称的两个静态成员是不允许的。

如果使用单静态导入声明导入静态成员，并且在同一类中存在具有相同名称的静态成员，则使用类中的静态成员。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java导入(import语句的用法)

Java类方法 - Java面向对象设计™

类中的方法定义对象的行为。方法是一个命名的代码块。调用方法的代码是方法的调用者。
可选地，方法可以接受来自调用者的输入值，并且它可以向调用者返回值。

输入值的列表称为参数。 方法可以具有零参数。 如果一个方法有零个参数，我们说该方法没有任何参数或方法不使用参数。

方法总是在类的主体内定义。方法声明的一般语法是形式 -

<modifiers> <return type> <method name> (<parameters list>) <throws clause>{
    // Body of  the   method  goes  here
}

<modifiers> 是修饰符的可选列表;

<return type> 是从方法返回的值的数据类型;

<method name> 是方法的名称；

方法参数

方法名称后面是一对闭合的小括号。或者，可以在括号内为方法指定一个或多个参数，多个参数用逗号分隔。
右括号可以后跟一个throws子句。最后，在大括号内指定代码的实现。方法声明中的四个部分是必需的：

返回类型；

方法名称；

一个小括号；

一个大括号；

以下是一个方法的示例：

方法的名称是：add；

使用两个 int 类型的参数： n1 和 n2 ；

返回这两个参数的和；

int add(int n1, int n2)  {
   int sum = n1  + n2;
   return  sum;
}

有时，方法不会向其调用者返回值。 如果方法不向调用者返回任何值，则使用关键字void作为返回类型。

方法名称必须是有效的Java标识符。
通常，Java方法从小写开始，随后使用字帽，也就是之后的单词的第一个字母大写。例如，getName，setName，getDogCount和createDog都是有效的方法名称。方法可以从其调用者获取输入值。参数用于从调用者获取输入值。

参数由两部分组成：数据类型和变量名称。 方法参数是变量声明。变量用于保存从方法的调用者传递的输入值。逗号分隔方法中的参数。

在以下示例中，add方法声明两个参数：n1和n2。 两个参数都是int数据类型。

int add(int n1, int n2)  {
   int sum = n1  + n2;
   return  sum;
}

当调用add方法时，调用者必须传递两个int值。从调用者传递的第一个值存储在n1中，从调用者传递的第二个值存储在n2中。
参数n1和n2也称为形式参数。

方法通过其在特定上下文中的签名唯一地标识。 方法的签名是其名称及其参数的数量，类型和顺序的组合。

局部变量

在方法，构造函数或方法块中声明的变量称为局部变量。在方法中声明的局部变量仅在执行方法的持续时间内存在。因为局部变量只存在一个临时持续时间，所以不能在方法，构造函数或声明它的块之外使用。

方法的形式参数被视为局部变量。 当调用方法时，在方法的正文执行之前，它们用实际的参数值初始化。

使用局部变量需要遵守以下规则 -

默认情况下不会初始化局部变量。

在程序向局部变量分配值之前，不能在程序中访问局部变量。

局部变量可以在方法体中的任何地方声明。 但是，它必须在使用之前声明。

当类变量或实例变量和局部的名称相同时，局部变量隐藏（覆盖）实例变量和类变量。

以下是一个简单的示例：

public class Main {
  public static int sum(int a , int b){
    int sum = 0; // 局部变量
    sum = a + b;
    return sum; // 返回后，sum变量被回收销毁
  }
  public static void main(String[] args) {
    int a = 100;
    int b = 200;
    int sum = 0;
    sum = sum(a, b);
    out.println("The sum of a and b is: " + sum);
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java类方法

Java方法重载 - Java面向对象设计™

在同一类中具有多个具有相同名称的方法称为方法重载。类中具有相同名称的方法可以是声明的方法，继承的方法或两者的组合。
重载方法必须具有不同数量的参数，不同类型的参数或两者。方法的返回类型，访问级别和throws子句对使其成为重载方法没有任何影响。参考以下示例代码，其中有几个名称为 m1 方法。

import java.io.IOException;

class MyClass {
  public void m1(int a) {
    // Code goes here
  }

  public void m1(int a, int b) {
    // Code goes here
  }

  public int m1(String a) {
    // Code goes here
    return 0;
  }

  public int m1(String a, int b) throws IOException {
    // Code goes here
    return 0;
  }
}

示例-1

下面的代码显示了如何使用重载。参考以下代码 -

public class Main {

  public double add(int a, int b) {
    System.out.println("Inside add(int a, int b)");
    double s = a + b;
    return s;//from  ww  w. ja  v a 2  s . c  om
  }

  public double add(double a, double b) {
    System.out.println("Inside add(double a,  double   b)");
    double s = a + b;
    return s;
  }

  public static void main(String[] args) {
    Main ot = new Main();
    int i = 1;
    int j = 1;
    double d1 = 10.42;
    float f1 = 22.3F;
    float f2 = 24.5F;
    short s1 = 22;
    short s2 = 26;

    ot.add(i, j);
    ot.add(d1, j);
    ot.add(i, s1);
    ot.add(s1, s2);
    ot.add(f1, f2);
    ot.add(f1, s2);
  }
}

上面的代码生成以下结果。

Inside add(int a, int b)
Inside add(double a,  double   b)
Inside add(int a, int b)
Inside add(int a, int b)
Inside add(double a,  double   b)
Inside add(double a,  double   b)

实例-2

有时，重载方法和自动类型扩展可能会混淆编译器，导致编译器错误。

class Adder {
  public double add(int a, double b) {
    return a + b;
  }

  public double add(double a, int b) {
    return a + b;
  }
}

public class Main {
  public static void main(String[] args) {
    Adder a = new Adder();
    // double d = a.add(2, 3); // A compile-time error
    double d1 = a.add((double) 2, 3); // OK. Will use add(double, int)
    double d2 = a.add(2, (double) 3); // OK. Will use add(int, double)

  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java方法重载

Java实例和静态方法 - Java面向对象设计™

类可以有两种类型的方法：实例方法和类方法。 实例方法和类方法也分别称为类的非静态方法和静态方法。

实例方法用于实现类的实例的行为。 实例方法只能在类的实例的上下文中调用。类方法用于实现类本身的行为。类方法可在类的上下文中执行。

static 修饰符用于定义类方法。 方法声明中不使用static修饰符，那么该方法则是一个实例方法。

示例

以下是声明一些静态和非静态方法的示例：

// A  static or  class method， 使用了 `static` 修饰符
static void  aClassMethod()  {

}

// A  non-static or  instance method  ， 未使用 `static` 修饰符
void  anInstanceMethod()  {

}

注意

当调用类的静态方法时，类的实例可能不存在。 因此，不允许从静态方法内部引用实例变量。

类定义一加载到内存中，类变量就存在。 类定义在创建类的第一个实例之前就加载到内存中。类方法或静态方法只能引用类的变量或类的静态变量。 实例方法或非静态方法可以引用类变量以及类的实例变量。

以下代码演示了在方法中访问的类字段的类型。

public class Main {
  static int m = 100; // A static variable
  int n = 200; // An instance variable

  // Declare a static method
  static void printM() {

    /*
     * We can refer to only static variable m in this method because you are
     * inside a static method
     */
    System.out.println("printM() - m   = " + m);
    // 注意这里边不能访问 实例变量 - n
  }

  // Declare an instance method
  void printMN() {
    /* We can refer to both static and instance variables m and n in this method */
    System.out.println("printMN() - m   = " + m);
    System.out.println("printMN() - n  = " + n);
  }
}

调用方法

在方法的主体中执行代码称为调用方法。实例方法和类方法是以不同方式调用。使用点表示法在类的实例上调用实例方法。

<instance reference>.<instance method name>(<actual parameters>)

在调用类的实例方法之前，必须先引用一个类的实例（或创建一个类实例）。

以下代码显示如何调用Main类的printMN（）实例方法：

// Create an  instance of  Main class  and
// store its  reference in mt reference variable
Main mt = new Main();

// Invoke  the   printMN() instance  method  using the   mt reference variable
mt.printMN();

要调用类方法，请使用带有名称的点(.)表示法。下面的代码调用Main类的printM（）类方法：

// Invoke  the   printM() class  method
Main.printM();

属于一个类的属性也属于该类的所有实例。因此也可以使用该类的实例的引用来调用类方法。

Main mt = new Main();
mt.printM(); // Call the   class method  using an  instance mt

使用类名调用类方法比使用实例引用更直观。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java实例和静态方法

Java main()方法 - Java面向对象设计™

现在来了解一下一个类中的main（）方法。main（）方法声明如下：

public static  void  main(String[]  args)  {

}

在main（）方法的声明中使用了两个修饰符public和static。public修饰符使得它可以从应用程序中的任何地方访问，只要声明的类是可访问的。

static修饰符使它成为一个类方法，因此可以使用类名来调用它。它的返回类型是void，这意味着它不返回一个值给它的调用者。

它的名字是main，接受一个类型为String array（String []）的参数。

main（）方法是Java应用程序的入口方法。 例如，可使用以下命令运行Main类：

java  com.yiibai.Main

在上面的代码中，当运行一个类时，JVM调用main（）方法。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java main()方法

Java参数传递 - Java面向对象设计™

Java支持两种数据类型：基本数据类型和引用数据类型。
原始数据类型是一个简单的数据结构，它只有一个与之相关的值。 引用数据类型是一个复杂的数据结构，它表示一个对象。

原始数据类型的变量将该值直接存储在其存储器地址处。使用对象和引用变量时会有所不同。Java中的所有参数都通过值传递。

当参数是原始数据类型时，实际参数的值将复制到参数。
在方法主体中对参数值进行的任何更改只会更改形式参数的副本，而不会更改实际参数的值。当参数通过参考值传递时，存储在实际参数中的参考被复制到形式参数。 实际参数和形式参数都指向内存中的相同对象。

可以将另一个对象的引用分配给方法主体中的形式参数。

示例

下面的代码演示了Java中的引用传递机制。

class Phone {
  public String model = "Unknown";
  public int year = 2016;
  public double price = 0.0;
}

public class Main {
  public static void main(String[] args) {
    Phone myPhone = new Phone();

    myPhone.model = "iPhone";
    myPhone.year = 2018;
    myPhone.price = 16000.0;

    System.out.println("#1: model  = " + myPhone.model + ", year   = "
        + myPhone.year + ", price = " + myPhone.price);

    Main.test(myPhone);

    System.out.println("#4: model  = " + myPhone.model + ", year   = "
        + myPhone.year + ", price = " + myPhone.price);
  }

  public static void test(Phone xPhone) {
    System.out.println("#2: model  = " + xPhone.model + ", year   = "
        + xPhone.year + ",  price = " + xPhone.price);

    // Let's make xyCar refer to a new object
    xPhone = new Phone();

    System.out.println("#3: model  = " + xPhone.model + ", year   = "
        + xPhone.year + ", price = " + xPhone.price);
  }
}

上面的代码生成以下结果。

#1: model  = iPhone, year   = 2018, price = 16000.0
#2: model  = iPhone, year   = 2018,  price = 16000.0
#3: model  = Unknown, year   = 2016, price = 0.0
#4: model  = iPhone, year   = 2018, price = 16000.0

注意
当引用类型参数传递给方法时，形式参数可以访问对象，实际参数也可以访问该对象。

形式参数可以通过直接更改实例变量的值或通过调用对象上的方法来修改对象。通过形式参数对对象进行的任何修改都可以通过实际参数立即可见，因为它们都保存对内存中同一对象的引用。形式参数本身可以被修改以引用方法内的另一个对象。

要禁用将引用类型形式参数更改为引用不同对象的方法，请在引用类型形式参数声明中使用关键字final。

public class Main {
  public static void main(String[] args) {
    Phone myPhone = new Phone();

    myPhone.model = "iPhone";
    myPhone.year = 2017;
    myPhone.price = 16000.0;

    Main.test(myPhone);
  }

  public static void test(final Phone xPhone) {
    System.out.println("#2: model  = " + xPhone.model + ", year   = "
        + xPhone.year + ",  price = " + xPhone.price);

    // Let's make xyCar refer to a new object
    //xPhone = new Phone();
  }
}
class Phone {
  public String model = "Unknown";
  public int year = 2014;
  public double price = 0.0;
}

上面的代码生成以下结果。

#2: model  = iPhone, year   = 2017,  price = 16000.0

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java参数传递

Java可变长度参数 - Java面向对象设计™

术语“varargs”是”variable-length arguments”(可变长度参数)的缩写。
可变长度参数声明一个接受可变数量的参数（或参数）的方法或构造函数。

声明可变长度参数方法

要声明可变长度参数，在方法参数的数据类型之后添加一个省略号...。下面的代码显示了一个带有一个可变长度参数num 的 max（）方法声明，它是int数据类型。

public static  int max(int... num) {

}

省略号...之前和之后添加空格是可选的。可变长度参数方法可以有多个参数。 下面的代码显示aMethod（）接受三个参数，其中一个是可变长度参数：

public static int aMethod(String str, double   d1,   int...num)  {

}

方法最多可以有一个可变长度参数。可变长度参数方法的可变长度参数必须是参数列表中的最后一个参数。

void  m2(String str, int...n1) {

}

示例

重写上面的 max（）方法，使其成为可变长度参数方法：

public class Main {
  public static int max(int... num) {
    int max = Integer.MIN_VALUE;
    for (int i = 0; i < num.length; i++) {
      if (num[i] > max) {
        max = num[i];
      }
    }
    return max;
  }
}

使用可变长度参数方法

可以使用for循环来处理可变长度参数的参数列表。length属性给出了可变长度参数传递的值的数量。

要获取可变长度参数中的第n个值，需要使用varArgsName [n-1]。可以使用foreach循环来处理可变长度参数。

public class Main {
  // 求参数列表中的最大值...
  public static int max2(int... num) {
    int max = Integer.MIN_VALUE;
    for (int currentNumber : num) {
      if (currentNumber > max) {
        max = currentNumber;
      }
    }
    return max;
  }
}

可以调用Main.max（）方法如下：

int max1 = Main.max(1, 8);
int max2 = Main.max(1, 1, 3);

对于方法中的可变长度参数，可以使用零个或多个参数。 以下代码是对max（）方法的有效调用：

int max = Main.max(); // Passing no  argument  is ok

max（）方法的以下声明将强制其调用者至少传递两个整数：

// Argumenets  n1  and  n2  are   mandatory
public static int max(int n1,  int n2,   int... num) {

}

编译器会将前两个参数n1和n2视为强制性参数，将第三个参数num作为可选参数。

public class Main {
  public static int max(int n1, int n2, int... num) {
    // Initialize max to the maximum of n1 and n2
    int max = (n1 > n2 ? n1 : n2);

    for (int i = 0; i < num.length; i++) {
      if (num[i] > max) {
        max = num[i];
      }
    }
    return max;
  }

  public static void main(String[] args) {
    System.out.println(max(77, 99));
    System.out.println(max(77, 99, 1010));
    System.out.println(max(77, 99, 1010, 1313));
  }
}

上面的代码生成以下结果。

重载可变长度参数方法

方法重载规则适用于可变长度参数方法。可以使用可变长度参数重载一个方法，只要方法的参数在类型，顺序或数字上不同。

例如，以下是一个重载的max（）方法的有效示例：

public  class  Main {
    public static int max(int x, int  y)  {
    }

    public static int max(int...num)  {
    }
}

考虑下面的代码 -

int max = Main.max(12, 13);  // which  max()  will be  called?

Java将调用max（int x，int y）。 Java首先尝试使用对参数数量的精确匹配来找到方法声明。 如果没有找到完全匹配，它将使用可变长度参数查找匹配。
如果可变长度参数方法重载，Java使用该方法的其它特定版本，而不使用可变长度参数方法。 java使用可变长度参数方法作为解决方法调用的最后手段。
方法本身的重载可能是有效的。 但是，调用它可能会导致问题。

public class Main {
  public static int max(int... num) {
    return 0;
  }

  public static int max(double... num) {
    return 0;
  }
}

下面的代码调用哪个max（）方法？

int max = Main.max(); // Which max()  to call?

上面的语句将生成一个编译时错误。

可变长度参数方法和main（）方法

main（）方法的签名必须是main（String [] args）。Main类的main（）方法的以下声明是有效的。

public class Main {
  public static void main(String... args) {
    System.out.println("Hello from  varargs main()...");
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java可变长度参数

Java类成员访问级别 - Java面向对象设计™

类可以是public或default（或包级别）。在本教程中主要介绍 public，private，protected 这几个访问级别修辞符的使用。
类成员的访问级别确定程序的哪个区域可以访问它。 以下四个访问级别修饰符之一可以用于类成员：

公开（public）

私有（private）

保护（protected）

默认或包级别访问

前三种类型的访问级别使用以下三个关键字之一来指定：public，private或protected。第四种类型称为默认访问级别（或包级别），并且通过不使用访问修饰符来指定。

如果类成员使用public关键字声明为公开，如果类本身可访问的话，则可以从Java代码中的任何位置访问它。

如果使用private关键字将类成员声明为私有，则只能在声明类的主体中访问，而在其他任何地方都不能访问。

如果使用protected关键字将类成员声明为保护，则可以从同一包中的类或该类的子类来访问此类成员，即使子类位于不同的包中也是可以访问的。

如果不为类成员使用任何访问级别修辞符，则它具有包级别访问权限。 具有包级别访问权限的类成员可以从同一个包中访问。

类成员的访问级别可以从最严格到最不严格，即：private，package-level，protected和public。

示例

以下代码显示如何使用不同的访问级别：

public class Main {
  private int num1; // private access level
  int num2; // package-level access
  protected int num3; // protected access level
  public int num4; // public access level

  public static int count = 1; // public access level

  // private access level
  private void m1() {
  }

  // package-level access
  void m2() {
  }

  // protected access level
  protected void m3() {
  }

  // public access level
  public void m4() {
  }

  // private access level
  private static void doSometing() {

  }
}

注意：可以为类的实例和静态成员指定访问级别。

它是一个约定，指定访问级别修改器作为声明中的第一个修饰符。要声明静态公共字段，请首先使用public修饰符，然后使用static修饰符作为约定。必须要考虑类及其成员的访问级别，以确定类成员是否可访问。

以下代码显示了如何在创建Java bean时使用访问级别修饰符。

class Account {
  private double balance;

  public double getBalance() {
    return this.balance;
  }

  public int save(double amount) {
    if (amount < 0.0 || Double.isNaN(amount) || Double.isInfinite(amount)) {
      System.out.println("Invalid credit amount:  " + amount);
      return -1;
    }
    this.balance = this.balance + amount;
    return 1;
  }

  public int spend(double amount) {
    if (amount < 0.0 || Double.isNaN(amount) || Double.isInfinite(amount)) {
      System.out.println("Invalid debit amount:  " + amount);
      return -1;
    }
    if (this.balance < amount) {
      System.out.println("Insufficient  fund. Debit   attempted: " + amount);
      return -1;
    }
    this.balance = this.balance - amount;
    return 1;
  }
}

public class Main {
  public static void main(String[] args) {
    Account ac = new Account();
    double balance = ac.getBalance();
    System.out.println("Balance = " + balance);

    ac.save(2);
    ac.spend(1);

    balance = ac.getBalance();
    System.out.println("Balance = " + balance);

    // Attempt to credit and debit invalid amounts
    ac.save(-2);
    ac.spend(Double.POSITIVE_INFINITY);

    balance = ac.getBalance();
    System.out.println("Balance = " + balance);

    // Attempt to debit more than the balance
    ac.spend(200.00);

    balance = ac.getBalance();
    System.out.println("Balance = " + balance);
  }
}

上面的代码生成以下结果。

Balance = 0.0
Balance = 1.0
Invalid credit amount:  -2.0
Invalid debit amount:  Infinity
Balance = 1.0
Insufficient  fund. Debit   attempted: 200.0
Balance = 1.0

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java类成员访问级别

Java this关键字 - Java面向对象设计™

this关键字是什么?

Java有一个关键字叫this。 它是对类的当前实例的引用。它只能在实例的上下文中使用。以下代码显示如何使用this关键字。

public class Main {
  int varA = 1;
  int varB = varA; // Assign value of varA to varB
  int varC = this.varA; // Assign value of varA to varC
}

当实例变量或类变量被具有相同名称的另一个变量覆盖时，需要使用关键字this限定一个实例变量，并使用类名称定义一个类变量。

实例

下面的代码显示了如何使用this关键字来引用一个实例变量，它的名字被一个局部变量覆盖隐藏。

public class Main {
  int num = 2018; // 这是一个实例变量

  void printNum(int num) { // 这个参数的名称也叫 num ，与上面实例变量同名
    System.out.println("Parameter num: " + num); // 打印参数 num 的值
    System.out.println("Instance variable num: " + this.num); // 打印实例变量 num 的值
  }

  public static void main(String[] args) {
    Main tt6 = new Main();
    tt6.printNum(2017);
  }
}

上面的代码生成以下结果。

Parameter num: 2017
Instance variable num:2018

注意：有时，需要保持变量名相同，因为它们代表相同的事情。

例如，以下代码非常常见：
Student类声明了一个实例变量id。 在其setId()方法中，它还命名参数id，并使用this.id引用实例变量。
它还使用this.id在其getId()方法中引用实例变量id。

public class Student {
  private int id; // An instance variable
  public void setId(int id) {
    this.id = id;
  }

  public int getId() {

    return this.id;
  }
}

可以使用关键字this来限定实例方法名。 以下代码显示使用关键字this调用m2()方法的m1()方法。

public class Main {
  void m1() {
    // Invoke the m2() method
    this.m2(); // same as "m2();"
    // 或者不要 this 直接 m2();
    // m2();
  }

  void m2() {
    // do something
    System.out.println("This m2() method.");
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java this关键字

Java final关键字 - Java面向对象设计™

final关键字不允许修改或替换其原始值或定义。final关键字可以在以下三个上下文中使用：

变量声明

类声明

方法声明

final变量

如果一个变量被声明为final，它只能被赋值一次。final变量的值在设置后不能修改。变量声明包括局部变量的声明，方法/构造函数的形式参数，实例变量和类变量。要将变量声明为final，请在变量声明中使用final关键字。

final int YES  = 1;

final变量的值只设置一次，它不能被重新赋值。有两种方法来初始化final变量的值：

在声明时给予初始化。

将其初始化延迟到稍后的时间。

但是，必须要在第一次读取 final 变量之前初始化它。

final局部变量 - 可以声明一个局部变量为final。如果将局部变量声明为空的final变量，则必须在使用前初始化它。

final参数 - 可以声明一个参数final。 当调用方法或构造函数时，参数将使用实际参数的值自动初始化。因此，不能在方法或构造函数主体内更改final参数的值。

final实例变量 - 可以声明一个实例变量final和空的final。空的final实例变量必须初始化一次，并且只有在调用类的任何构造函数时才初始化一次。

final类变量 - 可以声明一个类变量final和空的final。必须在其中一个静态初始化器中初始化一个空的最终类变量。

final参考变量 - 引用变量存储对象的引用。final引用变量意味着，一旦引用一个对象（或null），它就不能被修改以引用另一个对象。

以下代码显示了test2()方法的最终形式参数x：

public void  test2(final int x)  {

如果有一个类的多个静态初始化器，必须在一个静态初始化器中初始化所有空的final类变量一次。

public class Main {
  public static final int YES = 1;
  public static final int NO = 2;
  public static final String MSG;

  static {
    MSG = "final static variable";
  }
}

final类

如果一个类被声明为final，它不能被扩展（或子类化）。

final方法

如果一个方法声明为final，它不能在包含该方法的类的子类中重新定义（覆盖或隐藏）。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java final关键字

Java构造函数(构造器) - Java面向对象设计™

构造函数是用于在对象创建后立即初始化对象的代码块。构造函数的结构看起来类似于一个方法。

声明构造函数

构造函数声明的一般语法是：

<Modifiers> <Constructor Name>(<parameters list>) throws <Exceptions list> {

}

构造函数的声明以修饰符开头。
构造函数可以将其访问修饰符为：public，private，protected或package-level（无修饰符）。

构造函数名称与类的名称相同。构造函数名称后面是一对括号，可能包括参数。可选地，右括号后面可以是关键字throws，其后面是逗号分隔异常列表。

以下代码显示了声明类Test的构造函数的示例。 类的名称和构造函数的名称必须匹配。

public class Test {

  public Test() {
    // Code goes here

  }
}

与方法不同，构造函数没有返回类型。

使用构造函数

使用一个带有new操作符的构造函数来在创建新实例之后初始化类的实例。使用new运算符来创建一个对象，构造函数初始化该对象。
以下语句使用Test类的构造函数来初始化Test类的对象：

Test t = new Test();

以下代码显示了如何使用构造函数 -

class Cat {
  public Cat() {
    System.out.println("in constructor...");
  }
}

public class Main {
  public static void main(String[] args) {
    // Create a Cat object and ignore its reference
    new Cat();
    // Create another Cat object and store its reference in c
    Cat c = new Cat();
  }
}

上面的代码生成以下结果。

in constructor...
in constructor...

重载构造函数

一个类可以有多个构造函数。 它们称为重载构造函数。如果一个类有多个构造函数，它们的顺序或参数类型都必须与其他构造函数不同。
下面的代码声明两个构造函数。 一个构造函数不接受参数，另一个接受一个String类型的参数。

class Car {
  // Constructor #1
  public Car() {
    System.out.println("A car  is created.");
  }

  // Constructor #2
  public Car(String name) {
    System.out.println("A car  named " + name + "  is created.");
  }
}

public class Main {
  public static void main(String[] args) {
    Car d1 = new Car(); // Uses Constructor #1
    Car d2 = new Car("My Car"); // Uses Constructor #2
  }
}

上面的代码生成以下结果。

每个对象创建表达式调用一次构造函数。可以在对象创建的过程中只执行一个构造函数的代码一次。

从另一个构造函数调用构造函数

构造函数可以调用同一类中的另一个构造函数。考虑下面的Test类。 它声明两个构造函数; 一个不接受参数，一个接受一个int参数。
必须使用关键字this从另一个构造函数调用另外一个构造函数。下面的代码使用语句“this（1）;”从没有参数的构造函数中调用具有int参数的构造函数。

class Test {
  Test() {
    this(1); // OK. Note the use of the keyword this.
  }

  Test(int x) {
  }
}

如果构造函数调用另一个构造函数，它必须是构造函数体中的第一个可执行语句。构造函数不能调用自身，因为它将导致递归调用。

从构造函数返回

构造函数在其声明中不能有返回类型。但可以在构造函数体中使用没有返回表达式的return语句。当一个构造函数中的返回语句被执行时，控制返回给调用者，忽略构造函数的其余代码。
以下代码显示了在构造函数中使用return语句的示例。

class Test {
  public Test(int x) {
    if (x < 0) {
      return;
    }

    System.out.println("here");
  }
}

构造函数的访问级别修饰符

构造函数的访问级别决定了可以在对象创建表达式中使用该构造函数的程序。可以为构造函数指定四个访问级别之一：public，private，protected和package-level（不写的情况）。下面的代码声明了Test类的四个构造函数。

// Class Test   has  public access level
public class Test {
  // Constructor #1 - Package-level access
  Test() {
  }

  // Constructor #2 - public access level
  public Test(int x) {
  }

  // Constructor #3 - private access level
  private Test(int x, int y) {
  }

  // Constructor #4 - protected access level
  protected Test(int x, int y, int z) {
  }
}

具有公共（public）访问级别的构造函数可以在程序的任何部分中使用。具有私有（private）访问级别的构造函数只能在声明它的同一类中使用。具有受保护（protected）访问级别的构造函数可以在具有声明类的相同包的程序中以及在任何包中的任何子类中使用。具有包级访问权限的构造函数可以在声明其类的同一个包中使用。

默认构造函数

如果在定义类时，一个构造函数都不声明，Java编译器将向类添加一个构造函数。编译器添加的构造函数称为默认构造函数。默认构造函数没有任何参数。默认构造函数也称为无参数构造函数。如果类已经有一个构造函数，编译器就不会添加任何构造函数。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java构造函数(构造器)

Java初始化块 - Java面向对象设计™

实例初始化块

实例初始化块用于初始化类的对象。一个实例初始化程序只是一个类的代码块，但在任何方法或构造函数之外。实例初始值设定程序没有名称。它的代码只是放置在一个开放的大括号和闭包中。

示例

下面的代码展示了如何为Test类声明一个实例初始化器。注意，实例初始化程序在实例上下文中执行，并且关键字this在实例初始化程序中可用。

class Test {
  private int num;
  // An instance initializer
  {
    this.num = 101;

    /* Other code for the instance initializer*/
  }

  /* Other code for Test class*/
}

多重实例初始化

一个类可以有多个实例初始化器。对于创建的每个对象，它们都以文本顺序自动执行。所有实例初始值的代码在任何构造函数之前执行。
下面的代码演示了构造函数和实例初始化函数的执行顺序。

public class Main {
  {
    System.out.println("Inside instance initializer 1.");
  }

  {
    System.out.println("Inside instance initializer 2.");
  }

  public Main() {
    System.out.println("Inside  no-args constructor.");
  }

  public static void main(String[] args) {
    Main m = new Main();
  }
}

上面的代码生成以下结果。

Inside instance initializer 1.
Inside instance initializer 2.
Inside  no-args constructor.

实例初始化程序中不能有return语句。

静态初始化块

静态初始化块也称为静态初始化器。它类似于实例初始化块。

它用于初始化一个类。 每个对象执行一个实例初始化器，而当类定义被加载到JVM中时，只对一个类执行一次静态初始化器。需要在其声明的开头使用static关键字。

在一个类中可以有多个静态初始化器。 所有静态初始化器都按文本出现的顺序执行，并在任何实例初始化器之前执行。

以下代码演示了何时执行静态初始化程序。

public class Main {
  private static int num;
  {// An instance initializer
    System.out.println("Inside instance initializer.");
  }
  // A static initializer. Note the use of the keyword static below.
  static {
    num = 2018;
    System.out.println("Inside static initializer.");
  }

  // Constructor
  public Main() {
    System.out.println("Inside constructor.");
  }

  public static void main(String[] args) {
    System.out.println("Inside  main() #1.   num: " + num);
    // Declare a reference variable of the class
    Main si;
    System.out.println("Inside  main() #2.   num: " + num);
    new Main();    // Create an object

    System.out.println("Inside  main() #3.   num: " + num);
    new Main();// Create another object
  }
}

上面的代码生成以下结果。

Inside static initializer.
Inside  main() #1.   num: 2018
Inside  main() #2.   num: 2018
Inside instance initializer.
Inside constructor.
Inside  main() #3.   num: 2018
Inside instance initializer.
Inside constructor.

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java初始化块

Java Object类 - Java面向对象设计™

Java在java.lang包中有一个Object类。所有Java类都直接或间接扩展Object类。所有Java类都是Object类的子类，Object类是所有类的超类。
Object类本身没有超类。Object类的引用变量可以保存任何类的对象的引用。
以下代码声明对象类型的引用变量obj：

Object obj;

Object类有九个方法，可以在Java中的所有类中使用。有关 Object类的所有方法及描述 -

方法名称 | 描述
public String toString() | 它已经在Object类中实现了，也可以自定义它。它返回对象的字符串表示形式。通常，它用于调试目的。
public boolean equals(Object obj) | 它已经在Object类中实现，可以自定义它，它用于比较两个对象的相等性。
public int hashCode() | 它已经在Object类中实现，可以自定义它。它返回对象的哈希码（整数）值。
protected Object clone() throws CloneNotSupportedException | 它不在Object类中实现，可以通过覆盖克隆方法来自定义它。它用于创建对象的副本。
protected void finalize() throws Throwable | 它不是在Object类中实现的，可以自定义它。它在对象被销毁之前被垃圾收集器调用。
public final Class getClass() | 它在Object类中实现，不能自定义它。它返回对对象的Class对象的引用。
public final void notify() | 它在Object类中实现，不能自定义它。此方法通知对象的等待队列中的一个线程。
public final void notifyAll() | 它在Object类中实现，不能自定义它。此方法通知对象的等待队列中的所有线程。
public final void wait() throws InterruptedException ; public final void wait(long timeout) throws InterruptedException ; public final void wait (long timeout, int nanos) throws InterruptedException | 它在Object类中实现，不能自定义它。使对象的等待队列中的线程等待不管是否超时。

示例

以下代码显示如何重新实现Object类的toString()方法。

public class Test   {
    public String toString()  {
        return "Here  is a  string";
    }
}

Object类是什么？

Java中的每个对象都属于一个类。Object类的getClass()方法返回Class对象的引用。
以下代码显示了如何获取Cat对象的Class对象的引用：

Cat  c  = new Cat();
Class catClass  = c.getClass();

Class类是通用的，其形式类型参数是由其对象表示类的名称。

可以使用泛型重写上面的语句。如下语句所示 -

Class<Cat>   catClass = c.getClass();

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java Object类

Java哈希编码 - Java面向对象设计™

对象的哈希代码

哈希码(也叫散列码)是一个整数值。计算整数的算法称为散列函数。Java使用散列码从基于散列的集合中有效地检索数据。Object类有一个返回int的hashCode()方法，它是对象的哈希码。
该方法的默认实现通过将对象的内存地址转换为整数来计算对象的哈希码。下面是我们在类中重写hashCode()方法时必须遵循的规则。
假设有两个对象引用，x和y。

如果x.equals(y)返回true，x.hashCode()必须返回一个整数，它等于y.hashCode()。
如果两个对象使用equals()方法相等，则它们必须具有相同的哈希码。
如果x.hashCode()等于y.hashCode()，则x.equals(y)不必返回true。
如果对同一个对象多次调用hashCode()方法，则该方法必须返回相同的整数值。

如果一个类覆盖这两个方法中的任何一个，它必须基于散列集合覆盖该类的对象以保证能正确工作。Java 7添加了一个实用程序类java.lang.Objects。 它包含一个hash()方法，用于计算任意数量值的哈希码。从java 7，使用Objects.hash()方法来计算对象的哈希码。

实例

以下代码显示如何计算哈希值。

class Book {
  private String title;
  private String author;

  public int hashCode() {
    int hash = 37;
    int code = 0;

    // Use title
    code = (title == null ? 0 : title.hashCode());
    hash = hash * 59 + code;

    // Use author
    code = (author == null ? 0 : author.hashCode());
    hash = hash * 59 + code;

    return hash;
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java哈希编码

Java对象相等比较(Equals) - Java面向对象设计™

以下代码显示如何实现equals()和hashCode()方法

class Point {
  private int x;
  private int y;

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

  /* implement the equals() method */
  public boolean equals(Object otherObject) {
    // Are the same?
    if (this == otherObject) {
      return true;
    }

    // Is otherObject a null reference?
    if (otherObject == null) {
      return false;
    }

    // Do they belong to the same class?
    if (this.getClass() != otherObject.getClass()) {
      return false;
    }

    // Get the reference of otherObject in a SmartPoint variable
    Point otherPoint = (Point) otherObject;

    // Do they have the same x and y co-ordinates
    boolean isSamePoint = (this.x == otherPoint.x && this.y == otherPoint.y);

    return isSamePoint;
  }

  /*
   * implement hashCode() method of the Object class, which is a requirement
   * when you implement equals() method
   */
  public int hashCode() {
    return (this.x + this.y);
  }
}

public class Main {
  public static void main(String[] args) {
    Point pt1 = new Point(10, 10);
    Point pt2 = new Point(10, 10);
    Point pt3 = new Point(12, 19);
    Point pt4 = pt1;

    System.out.println("pt1 == pt1: " + (pt1 == pt1));
    System.out.println("pt1.equals(pt1): " + pt1.equals(pt1));

    System.out.println("pt1 == pt2: " + (pt1 == pt2));
    System.out.println("pt1.equals(pt2): " + pt1.equals(pt2));

    System.out.println("pt1 == pt3: " + (pt1 == pt3));
    System.out.println("pt1.equals(pt3): " + pt1.equals(pt3));

    System.out.println("pt1 == pt4: " + (pt1 == pt4));
    System.out.println("pt1.equals(pt4): " + pt1.equals(pt4));
  }
}

上面的代码生成以下结果。

pt1 == pt1: true
pt1.equals(pt1): true
pt1 == pt2: false
pt1.equals(pt2): true
pt1 == pt3: false
pt1.equals(pt3): false
pt1 == pt4: true
pt1.equals(pt4): true

这里是equals()方法实现的规范。 假设x，y和z是三个对象的非空引用。

自反性。 表达式x.equals(x)应该返回true。

对称性。 如果x.equals(y)返回true，那么y.equals(x)也必须返回true。

传递性。如果x.equals(y)返回true，那么y.equals(z)也返回true，则x.equals(z)必须返回true。

一致性。如果x.equals(y)返回true，它应该保持返回true，直到x或y的状态被修改。 如果x.equals(y)返回false，它应该保持返回false，直到x或y的状态被修改。

与null引用的比较：任何类的对象不应等于null引用。 表达式x.equals(null)应始终返回false。

与hashCode()方法的关系：如果x.equals(y)返回true，x.hashCode()必须返回与y.hashCode()相同的值。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java对象相等比较(Equals)

Java对象toString()方法 - Java面向对象设计™

对象的字符串表示以可读格式包含有关对象状态的信息。
Object类的toString()方法表示字符串中类的对象。Object类提供了toString()方法的默认实现。 它返回一个以下格式的字符串：

<fully qualified class name>@<hash code of object in hexadecimal>

示例

考虑下面的代码及其输出。如是亲自执行代码可能会得到不同的输出。

public class Main{
  public static void main(String[] argv){
    Object obj  = new Object();
    String objStr = obj.toString();
    System.out.println(objStr);
  }
}

上面的代码生成以下结果。

java.lang.Object@2a139a55

实例

以下代码显示了如何创建自己的toString方法。

public class Main{
  public static void main(String[] argv){
    MyClass obj  = new MyClass(123);
    String objStr = obj.toString();
    System.out.println(objStr);
  }
}
class MyClass {
  private int value;

  public MyClass(int value) {
    this.value = value;
  }

  public void setValue(int value) {
    this.value = value;
  }

  public int getValue() {
    return value;
  }

  /* override toString() method of the Object class */
  public String toString() {
    // Return the stored value as a string
    String str = String.valueOf(this.value);
    return str;
  }
}

上面的代码生成以下结果。

123

注：需要确保它被声明为public，它的返回类型是String，并且它不接受任何参数。

类的toString()方法非常重要。 当需要对象的字符串表示时，Java会自动调用toString()方法。

有两种情况值得一提：

当连接一个字符串和一个对象时，实例调用的就是toString()方法。

String str = "Hello" + new Point(10, 20);

Java在Point对象上调用toString()方法，并将返回的值并连接“Hello”字符串。上述语句与以下语句相同：

String str = "Hello" + new Point(10, 20).toString();

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java对象toString()方法

Java对象克隆 - Java面向对象设计™

Java不提供克隆(复制)对象的自动机制。克隆对象意味着逐位复制对象的内容。要支持克隆操作，请在类中实现clone()方法。Object类中的clone()方法的声明如下：

protected  Object clone()  throws   CloneNotSupportedException

clone()方法声明为protected。 因此，不能从客户端代码调用它。以下代码无效：

Object obj  = new Object();
Object clone = obj.clone(); // Error. Cannot  access protected clone() method

需要在类中声明clone()方法为public来克隆类的对象。
它的返回类型是Object。 这意味着将需要使用clone()方法转换返回值。
假设MyClass是可克隆的。 克隆代码将如下所示

MyClass mc  = new MyClass();
MyClass clone = (MyClass)mc.clone(); // Need to use  a  cast

Object类中的clone()方法会抛出CloneNotSupportedException。要调用clone()方法，需要将调用放在try-catch块中，或者重新抛出异常。

示例

以下代码显示了如何实现克隆方法。

class MyClass implements Cloneable {
  private double value;
  public MyClass(double value) {
    this.value = value;
  }

  public void setValue(double value) {
    this.value = value;
  }

  public double getValue() {
    return this.value;
  }

  public Object clone() {
    MyClass copy = null;
    try {
      copy = (MyClass) super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return copy;
  }
}

public class Main {
  public static void main(String[] args) {
    MyClass dh = new MyClass(100.00);

    MyClass dhClone = (MyClass) dh.clone();

    System.out.println("Original:" + dh.getValue());
    System.out.println("Clone :" + dhClone.getValue());

    dh.setValue(100.00);
    dhClone.setValue(200.00);

    System.out.println("Original:" + dh.getValue());
    System.out.println("Clone :" + dhClone.getValue());
  }
}

上面的代码生成以下结果。

Original:100.0
Clone :100.0
Original:100.0
Clone :200.0

实例-2

以下代码不从clone()方法返回对象类型，该方法仅在Java5或更高版本中编译。

class MyClass  implements Cloneable  {
    public MyClass clone()  {
       Object copy  = null;
       return  (MyClass)copy;
    }
}

下面的代码展示了如何做浅克隆。

class MyClass implements Cloneable {
  private double value;

  public MyClass(double value) {
    this.value = value;
  }

  public void setValue(double value) {
    this.value = value;
  }

  public double getValue() {
    return this.value;
  }

  public Object clone() {
    MyClass copy = null;
    try {
      copy = (MyClass) super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return copy;
  }
}

class ShallowClone implements Cloneable {
  private MyClass holder = new MyClass(0.0);

  public ShallowClone(double value) {
    this.holder.setValue(value);
  }

  public void setValue(double value) {
    this.holder.setValue(value);
  }

  public double getValue() {
    return this.holder.getValue();
  }

  public Object clone() {
    ShallowClone copy = null;
    try {
      copy = (ShallowClone) super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return copy;
  }
}

public class Main {
  public static void main(String[] args) {
    ShallowClone sc = new ShallowClone(100.00);
    ShallowClone scClone = (ShallowClone) sc.clone();

    System.out.println("Original:" + sc.getValue());
    System.out.println("Clone :" + scClone.getValue());

    sc.setValue(200.00);

    System.out.println("Original:" + sc.getValue());
    System.out.println("Clone :" + scClone.getValue());
  }
}

上面的代码生成以下结果。

Original:100.0
Clone :100.0
Original:200.0
Clone :200.0

实例-3

ShallowClone类的clone()方法中的代码与MyClass类的clone()方法相同。当ShallowClone类使用super.clone()调用Object类的clone()方法时，它会接收自身的浅拷贝。也就是说，它与其克隆共享其实例变量中使用的DoubleHolder对象。

在深克隆中，需要克隆对象的所有引用实例变量。

class MyClass implements Cloneable {
  private double value;

  public MyClass(double value) {
    this.value = value;
  }

  public void setValue(double value) {
    this.value = value;
  }

  public double getValue() {
    return this.value;
  }

  public Object clone() {
    MyClass copy = null;
    try {
      copy = (MyClass) super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return copy;
  }
}

class DeepClone implements Cloneable {
  private MyClass holder = new MyClass(0.0);

  public DeepClone(double value) {
    this.holder.setValue(value);
  }

  public void setValue(double value) {
    this.holder.setValue(value);
  }

  public double getValue() {
    return this.holder.getValue();
  }
  public Object clone() {
    DeepClone copy = null;
    try {
      copy = (DeepClone) super.clone();
      copy.holder = (MyClass) this.holder.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return copy;
  }
}

public class Main {
  public static void main(String[] args) {
    DeepClone sc = new DeepClone(100.00);
    DeepClone scClone = (DeepClone) sc.clone();

    System.out.println("Original:" + sc.getValue());
    System.out.println("Clone :" + scClone.getValue());

    sc.setValue(200.00);

    System.out.println("Original:" + sc.getValue());
    System.out.println("Clone :" + scClone.getValue());
  }
}

执行上面的代码，将生成以下结果 -

Original:100.0
Clone :100.0
Original:200.0
Clone :100.0

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java对象克隆

Java对象finalize()方法 - Java面向对象设计™

Java提供了一种在对象即将被销毁时执行资源释放的方法。在Java中创建对象，但是不能销毁对象。JVM运行一个称为垃圾收集器的低优先级特殊任务来销毁不再引用的所有对象。

垃圾回收器给我们一个机会，在对象被销毁之前执行清理代码。Object类有一个finalize()方法就是用于在对象被销毁之前执行清理代码，声明如下：

protected void  finalize() throws   Throwable  {  }

Object类中的finalize()方法不会做任何事情。所以需要覆盖类中的finalize()方法。类的finalize()方法将在类的对象销毁之前由垃圾回收器调用。

示例

以下代码显示了如何创建一个Finalize类覆盖对象类的finalize()方法。

class Finalize {
  private int x;
  public Finalize(int x) {
    this.x = x;
  }
  public void finalize() {
    System.out.println("Finalizing " + this.x);
  }
}
public class Main {
  public static void main(String[] args) {
    for (int i = 0; i < 20; i++) {
        Finalize f= new Finalize(i);
        f.finalize();// 可显示的调用
    }
  }
}

上面的代码生成以下结果。

Finalizing 0
Finalizing 1
Finalizing 2
Finalizing 3
Finalizing 4
Finalizing 5
Finalizing 6
Finalizing 7
Finalizing 8
Finalizing 9
Finalizing 10
Finalizing 11
Finalizing 12
Finalizing 13
Finalizing 14
Finalizing 15
Finalizing 16
Finalizing 17
Finalizing 18
Finalizing 19

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java对象finalize()方法

Java不可变对象 - Java面向对象设计™

在创建状态后无法更改其状态的对象称为不可变对象。一个对象不可变的类称为不可变类。不变的对象可以由程序的不同区域共享而不用担心其状态改变。

不可变对象本质上是线程安全的。

示例

以下代码创建了不可变类的示例。

public  class  IntWrapper {
    private  final  int  value;
    public IntWrapper(int value) {
        this.value = value;
    }
    public int  getValue() {
        return value;
    }
}

下面显示如何创建IntWrapper类的对象：

IntWrapper wrapper  = new IntWrapper(101);

在这一点上，wrapper对象保存101值，并且没有办法改变它。因此，IntWrapper类是一个不可变的类，它的对象是不可变的对象。
但是，最好将所有实例变量声明为final，以便Java编译器在编译期间强制执行不可变性。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java不可变对象

Java内部类 - Java面向对象设计™

内部类是什么？

作为包的成员的类被称为顶级类。一个类可以在另一个类中声明。 这种类型的类称为内部类。
如果在另一个类中声明的类被显式或隐式声明为static，它被称为嵌套类，而不是内部类。
包含内部类的类称为封闭类或外部类。

示例

下面的代码声明一个内部类。

class Outer {
  public class Inner {
    // Members of the Inner class go here
  }
  // Other members of the Outer class go here
}

Outer类是一个顶级类。Inner类是一个内部类。 它是外类的成员。Outer类是Inner类的封闭(外部)类。内部类可以是另一个内部类的封闭类。 内部类的嵌套层次没有限制。

内部类的实例只能存在于其封闭类的实例中。

使用内部类的优点

以下是内部类的一些优点：

在使用它们的其他类中定义类。

提供了一个额外的命名空间来管理类结构。

一些设计模式使用内部类更容易实现。

实现回调机制使用内部类是优雅和方便的。它有助于在Java中实现闭包。

访问局部变量的限制

下面的代码演示了访问局部内部类中的局部变量的规则。
main()方法声明两个局部变量：x和y。 这两个变量都是被声明为 final。变量x在被初始化之后不能改变，变量y也不能被改变，因为它被声明为final。

public class Main {
  public static void main(String... args) {
    final int x = 1;
    final int y = 2;

    class LocalInner {
      void print() {
        System.out.println("x = " + x);
        System.out.println("y = " + y);
      }
    }
    /*
     * Uncomment the following statement will make the variable x no longer
     * an effectively final variable and the LocalIneer class will not compile.
     */
    // x = 100;

    LocalInner li = new LocalInner();
    li.print();
  }
}

上面的代码生成以下结果。

x = 1
y = 2

内部类和继承

内部类可以继承另一个内部类，顶级类或其封闭类。

class A {
  public class B {
  }

  public class C extends B {
  }

  public class D extends A {
  }
}

class E extends A {
  public class F extends B {
  }
}

内部类中没有静态成员

Java中的关键字static使一个构造成为一个顶层结构。因此，不能为内部类声明任何静态成员(字段，方法或初始化器)。
允许在内部类中有作为编译时常量的静态字段。

class A {
  public class B {
    public final static int DAYS_IN_A_WEEK = 7; // OK
    public final String str = new String("Hello");
  }
}

生成的内部类的类文件

每个内部类都被编译成一个单独的类文件。成员内部类和静态内部类的类文件名格式如下：

<outer-class-name>$<member-or-static-inner-class-name>

本地内部类的类文件名的格式如下：

<outer-class-name>$<a-number><local-inner-class-name>

匿名类的类文件名的格式如下：

<outer-class-name>$<a-number>

类文件名中的<a-number>是从1开始顺序生成的数字，以避免任何名称冲突。

静态上下文中的内部类

可以在静态上下文中定义一个内部类，例如静态方法或静态初始化器。所有静态字段成员都可以访问这样的内部类。

class Outer {
  static int k = 1;
  int m = 2;

  public static void staticMethod() {
    // Class Inner is defined in a static context
    class Inner {
      int j = k; // OK. Referencing static field k
      // int n = m; // An error. Referencing non-static field m
    }
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java内部类

Java内部类类型 - Java面向对象设计™

可以在类中的任何位置定义内部类，并在其中编写Java语句。有三种类型的内部类。 内部类的类型取决于位置和声明的方式。

成员内部类

局部内部类

匿名内部类

成员内部类

成员内部类在类中声明的方式与声明成员字段或成员方法相同。它可以声明为public，private，protected或package-level。成员内部类的实例可以仅存在于其封闭类的实例内。

以下代码创建了一个成员内部类。

class Car {
  private int year;

  // A member inner class named Tire public
  class Tire {
    private double radius;
    public Tire(double radius) {
      this.radius = radius;
    }
    public double getRadius() {
      return radius;
    }
  } // Member inner class declaration ends here

  // A constructor for the Car class
  public Car(int year) {
    this.year = year;
  }
  public int getYear() {
    return year;
  }
}

局部内在类

一个局部内部类在块中声明。 其范围仅限于声明它的块。由于其范围限于其封闭块，因此其声明不能使用任何访问修饰符，例如public，private或protected。
通常，在方法内定义局部内部类。 但是，它也可以在静态初始化器，非静态初始化器和构造器中定义。下面的代码显示了一个局部内部类的例子。

import java.util.ArrayList;
import java.util.Iterator;

public class Main {
  public static void main(String[] args) {
    StringList tl = new StringList();
    tl.addTitle("A");
    tl.addTitle("B");

    Iterator iterator = tl.titleIterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
class StringList {
  private ArrayList<String> titleList = new ArrayList<>();

  public void addTitle(String title) {
    titleList.add(title);
  }

  public void removeTitle(String title) {
    titleList.remove(title);
  }

  public Iterator<String> titleIterator() {
    // A local inner class - TitleIterator
    class TitleIterator implements Iterator<String> {
      int count = 0;
      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }

      @Override
      public String next() {
        return titleList.get(count++);
      }
    }

    TitleIterator titleIterator = new TitleIterator();
    return titleIterator;
  }
}

上面的代码生成以下结果。

A
B

示例

下面的代码有一个局部内部类继承自另一个公共类。

import java.util.Random;

abstract class IntGenerator {
  public abstract int getValue() ;
}
class LocalGen {
  public IntGenerator getRandomInteger() {
    class RandomIntegerLocal extends IntGenerator {
      @Override
      public int getValue() {
        Random rand = new Random();
        long n1 = rand.nextInt();
        long n2 = rand.nextInt();
        int value = (int) ((n1 + n2) / 2);
        return value;
      }
    }

    return new RandomIntegerLocal();
  } // End of getRandomInteger() method
}

public class Main {
  public static void main(String[] args) {
    LocalGen local = new LocalGen();
    IntGenerator rLocal = local.getRandomInteger();
    System.out.println(rLocal.getValue());
    System.out.println(rLocal.getValue());
  }
}

上面的代码生成以下结果(每次的结果可能不太一样)。

453673065
1036946998

匿名内部类

匿名内部类没有名称。 因为它没有名称，所以它不能有构造函数。匿名类是一次性类。定义一个匿名类并同时创建它的对象。
创建匿名类及其对象的一般语法如下：

new Interface()  {
// Anonymous  class body  goes  here
}

或者 -

new Superclass(<argument-list-for-a-superclass-constructor>)  {
// Anonymous  class body  goes  here
}

new运算符用于创建匿名类的实例。它后面是现有的接口名称或现有的类名称。接口名称或类名称不是新创建的匿名类的名称。如果使用接口名称，则匿名类实现接口。如果使用类名，则匿名类继承自其它类。
仅当新运算符后面跟有类名时，才使用<argument-list>。 如果新运算符后跟接口名称，则它为空。
如果<argument-list>存在，它包含要调用的现有类的构造函数的实际参数列表。

匿名类主体像往常一样在大括号中。匿名类主体应该简短，以便更好的可读性。下面的代码包含一个简单的匿名类，它在标准输出上打印一条消息。

public class Main {
  public static void main(String[] args) {
    new Object() {
      // An instance initializer

        System.out.println("Hello from  an  anonymous class.");
      }
    }; // A semi-colon is necessary to end the statement
  }
}

上面的代码生成以下结果。

Hello from  an  anonymous class.

实例-2

以下代码使用匿名类来创建迭代器(Iterator)。

import java.util.ArrayList;
import java.util.Iterator;

public class Main {
  private ArrayList<String> titleList = new ArrayList<>();

  public void addTitle(String title) {
    titleList.add(title);
  }

  public void removeTitle(String title) {
    titleList.remove(title);
  }

  public Iterator<String> titleIterator() {
    // An anonymous class
    Iterator<String> iterator = new Iterator<String>() {
      int count = 0;

      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }

      @Override
      public String next() {
        return titleList.get(count++);
      }
    }; // Anonymous inner class ends here

    return iterator;
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java内部类类型

Java静态内部类 - Java面向对象设计™

静态成员类不是内部类在另一个类的主体中定义的成员类可以声明为静态。

示例

以下代码声明了顶级类A和静态成员类B：

class A {
  // Static member class
  public static class B {
    // Body for class B goes here
  }
}

静态成员类不是内部类。 它被认为是一个顶级类。静态成员类也称为嵌套顶级类。A类的实例和B类的实例可以独立存在，因为它们都是顶级类。

静态成员类可以声明为public，protected，package-level或private，以限制其在其封闭类之外的可访问性。

使用静态成员类有两个好处：

静态成员类可以访问其包含类的静态成员，包括私有静态成员。

一个包通过提供一个命名空间，就像一个顶级类的容器。 具有静态成员类的顶级类提供了额外的命名空间层。

静态成员类是其封闭顶级类的直接成员，而不是包的成员。
静态成员类的对象的创建方式与使用new运算符创建顶级类的对象的方式相同。 要创建一个B类的对象，如下所示 -

A.B bReference = new A.B();

由于类B的名称在类A中的范围内，因此可以使用其简单名称在类A中创建其对象。

B  bReference2 = new B(); // This  statement appears inside class  A  code

还可以通过导入com.yiibai.innerclasses.A.B类，使用A类之外的简单名称B.

实例-2

下面的代码显示了如何使用静态内部类。

public class Main {
  public static void main(String[] args) {
    Car.Tire m = new Car.Tire(17);
    Car.Tire m2 = new Car.Tire(19);

    Car.Keyboard k = new Car.Keyboard(122);
    Car.Keyboard k1 = new Car.Keyboard(142);

    System.out.println(m);
    System.out.println(m2);
    System.out.println(k);
    System.out.println(k1);
  }
}
class Car {
  // Static member class - Monitor
  public static class Tire {
    private int size;

    public Tire(int size) {
      this.size = size;
    }

    public String toString() {
      return "Monitor   - Size:" + this.size + "  inch";
    }
  }

  // Static member class - Keyboard
  public static class Keyboard {
    private int keys;

    public Keyboard(int keys) {
      this.keys = keys;
    }

    public String toString() {
      return "Keyboard  - Keys:" + this.keys;
    }
  }
}

上面的代码生成以下结果。

Monitor   - Size:17  inch
Monitor   - Size:19  inch
Keyboard  - Keys:122
Keyboard  - Keys:142

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java静态内部类

Java内部类成员 - Java面向对象设计™

内部类可以访问其所有实例成员，实例字段和其封闭类的实例方法。
参考如下实例 -

class Outer {
  private int value = 2017;
  public class Inner {
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here

  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}

public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();

    out.printValue();
    in.printValue();

    out.setValue(2018);

    out.printValue();
    in.printValue();
  }
}

上面的代码生成以下结果。

Outer: Value  = 2017
Inner: Value  = 2017
Outer: Value  = 2018
Inner: Value  = 2018

示例-1

以下代码显示如何访问内部类的内部变量。

public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();

    out.printValue();
    in.printValue();

    out.setValue(3);

    out.printValue();
    in.printValue();
  }
}
class Outer {
  private int value = 1;
  public class Inner {
    private int value = 2;
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here
  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}

上面的代码生成以下结果。

Outer: Value  = 1
Inner: Value  = 2
Outer: Value  = 3
Inner: Value  = 2

Inner类中的this关键字

以下代码显示如何在内部类中使用关键字 - this。

class Outer {
  private int value = 1;
  class QualifiedThis {
    private int value = 2;

    public void printValue() {
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);
      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }

    public void printHiddenValue() {
      int value = 2;
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);

      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }
  }
  public void printValue() {
    System.out.println("value=" + value);
    System.out.println("this.value=" + this.value);
  }
}

public class Main {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.QualifiedThis qt = outer.new QualifiedThis();
    System.out.println("printValue():");
    qt.printValue();

    System.out.println("printHiddenValue():");
    qt.printHiddenValue();
    outer.printValue();
  }
}

上面的代码生成以下结果。

printValue():
value=2
this.value=2
QualifiedThis.this.value=2
printHiddenValue():
value=2
this.value=2
QualifiedThis.this.value=2
value=1
this.value=1

隐藏变量

如果实例变量名称被隐藏，必须使用关键字this或类名称以及关键字this限定其名称。

class TopLevelOuter {
  private int v1 = 100;

  // Here, only v1 is in scope
  public class InnerLevelOne {
    private int v2 = 200;

    // Here, only v1 and v2 are in scope
    public class InnerLevelTwo {
      private int v3 = 300;

      // Here, only v1, v2, and v3 are in scope
      public class InnerLevelThree {
        private int v4 = 400;

        // Here, all v1, v2, v3, and v4 are in scope

      }
    }
  }
}

从外部类

以下代码显示如何从外部类引用变量。

public class Test{
  private int value = 1;
  public class Inner {
    private int value = 2;

    public void printValue() {
      System.out.println("Inner: Value  = " + value);
      System.out.println("Outer: Value  = " + Test.this.value);
    }
  } // Inner class ends here

  public void printValue() {
    System.out.println("\nOuter - printValue()...");
    System.out.println("Outer: Value  = " + value);
  }

  public void setValue(int newValue) {
    System.out.println("\nSetting  Outer's value to " + newValue);
    this.value = newValue;
  }

}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java内部类成员

Java异常类 - Java面向对象设计™

异常类层次结构

异常类层次结构从java.lang.Throwable类开始。当抛出异常时，它必须是Throwable类的对象或其任何子类。

catch块的参数必须是Throwable类型或其子类，例如：Exception，ArithmeticException，IOException等。

可以通过继承一个异常类类来创建自己定义的异常类。

组织排列多个catch块

Java运行时选择适当的catch块，并从第一个catch块开始顺序寻找合适的catch块。
try块的多个catch块必须从最具体的异常类型排列为最通用的异常类型。

以下代码使用多个catch块的有效序列。

ArithmeticException类是RuntimeException类的子类。

如果这两个异常都在同一个try块的catch块中处理，那么最具体的类型，即 ArithmeticException，必须出现在最通用的类型之前，即放在RuntimeException异常类之前。

try  {
// Do  something, which  might  throw  Exception
}
catch(ArithmeticException e1)  {
// Handle  ArithmeticException first
}
catch(RuntimeException e2)  {
// Handle  RuntimeException after  ArithmeticException
}

已检查和未检查异常

有三种类型的异常：

有一些异常，有更高的发生可能。可以在try-catch块中处理它们。或者在调用方法/构造函数声明中指定抛出异常。

错误是可能发生的异常，并且几乎不能处理它。 例如，java.lang.OutOfMemeoryError。 在程序中也不能做任何事情从内存不足错误中恢复。异常类是层次结构中的类，它们是Error类的子类和Error类，属于此类别的异常。
编译器不对这类代码采取处理。如果在运行时抛出此类异常，运行时将通过显示详细的错误消息并暂停应用程序来处理它。

在运行时可能会发生异常，可从异常条件中恢复。
异常类是层次结构中的类，它们是RuntimeException类的子类和RuntimeException类本身，属于此类别。

第一种异常分类称为检查异常，因为编译器检查它们是否在代码中有处理了。Throwable类，Exception类和Exception类的子类(不包括RuntimeException类及其子类)称为检查异常。

所有未检查异常的异常称为未检查异常，因为编译器不检查它们是否在代码中有处理。Error类，Error类的所有子类，RuntimeException类及其所有子类都是未检查的异常。

当然也可以处理未检查的异常，但是这不是编译器强迫要求的。用于处理已检查或未检查异常的程序结构是相同的。

以下代码显示如何处理已检查的异常

import java.io.IOException;

public class Main {
  public static void main(String[] argv) {
    try {
      int input = System.in.read();
      if (input != -1) {
        System.out.println(input);
      }
    } catch (IOException e) {
      System.out.print("IOException occurred.");
    }
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java异常类

Java继承教程 - Java面向对象设计™

子类可以从超类继承。 超类也称为基类或父类。子类也称为派生类或子类。从另一个类继承一个类非常简单。在子类的类声明中使用关键字extends，后跟超类名称。
Java不支持多重继承的实现。Java中的类不能有多个超类。

继承语法

继承的一般语法是 -

<class modifiers>class <SubclassName> extends <SuperclassName> {
    // Code for the   Subclass
}

示例

以下代码显示如何使用从Employee类继承并创建Manager类，Employee类称为基类，而Manager称为子类。

class Employee {
  private String name = "Unknown";
  public void setName(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }
}
class Manager extends Employee {
}
public class Main {
  public static void main(String[] args) {
    // Create an object of the Manager class
    Manager mgr = new Manager();

    // Set the name of the manager
    mgr.setName("张经理");

    // Get the name of the manager
    String mgrName = mgr.getName();

    // Display the manager name
    System.out.println("Manager Name: " + mgrName);
  }
}

上面的代码生成以下结果。

Manager Name: 张经理

d

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java继承教程

Java方法覆盖教程 - Java面向对象设计™

方法覆盖

重新定义来自超类(父类)继承的类中的实例方法称为方法覆盖。

示例

现在来看看，类A和类B的以下声明，覆盖了 print() 方法 ：

public class A  {
    public void  print() {
        System.out.println("A");
    }
}

public class B  extends A  {
    public void  print() {
        System.out.println("B");
    }
}

类B是类A的子类。类B从其超类继承print()方法并重新定义它。类B中的print()方法覆盖类A的print()方法。

如果一个类覆盖了一个方法，它会影响覆盖的类及其子类。 考虑下面的类C的声明：

public class C  extends B  {
   // Inherits B.print()
}

类C不声明任何方法。但它继承类B中的print()方法。看看下面的代码，猜想它输出结果应该是什么？

class A {
    public void print() {
        System.out.println("A");
    }
}

class B extends A {
    public void print() {
        System.out.println("B");
    }
}

class C extends B {
    public void print() {
        System.out.println("B");
    }
}

public class Main {
    public static void main(String[] args) {
        // Create an object of the Manager class
        A aObj = new A();
        B bObj = new B();
        C cObj = new C();
        aObj.print();
        bObj.print();
        cObj.print();
    }
}

执行上面的代码，得到以下输出结果 -

A
B
B

注意, 类总是继承它的直接超类的方法。

方法必须是实例方法。 覆盖不适用于静态方法。重写方法必须具有与重写方法相同的名称。重写方法必须具有与重写方法相同顺序的相同类型和相同数量的参数。

当方法的参数使用通用类型时，与其他方法比较时考虑将泛型类型参数的擦除。参数的名称是无关紧要的，不会影响方法的覆盖。

访问级别

覆盖方法的访问级别必须至少与被覆盖方法的访问级别相同或更宽松。下表列出了覆盖方法允许的访问级别 -

覆盖方法访问级别 | 允许覆盖方法访问级别
public | public
protected | public, protected
package-level | public, protected, package-level

方法可以在其throws子句中包括检查异常的列表。覆盖方法无法向覆盖方法中的异常列表添加新的异常。

它可以删除一个或所有异常，或者可以用另一个异常替换异常。

访问覆盖方法

从子类访问重写的方法。在子类中可以使用关键字super作为限定符来调用超类的重写方法。

class MySuperClass {
  public void print() {
    System.out.println("Inside MySuperClass");
  }
}

class MySubClass extends MySuperClass {
  public void print() {
    // Call print() method of MySuperClass class
    super.print();
    // Print a message
    System.out.println("Inside MySubClass.print()");
  }

  public void callOverridenPrint() {
    // Call print() method of MySuperClass class
    super.print();
  }
}

public class Main {
  public static void main(String[] args) {
    MySubClass aoSub = new MySubClass();
    aoSub.print();
    aoSub.callOverridenPrint();
  }
}

上面的代码生成以下结果。

Inside MySuperClass
Inside MySubClass.print()
Inside MySuperClass

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java方法覆盖教程

Java继承和构造函数 - Java面向对象设计™

构造函数不是类的成员，它们不是由子类继承的。它们用于初始化实例变量。

class CSuper {
  public CSuper() {
    System.out.println("Inside CSuper() constructor.");
  }
}

class CSub extends CSuper {
  public CSub() {
    System.out.println("Inside CSub()  constructor.");
  }
}

public class Main {
  public static void main(String[] args) {
    CSub cs = new CSub();
  }
}

上面的代码生成以下结果。

Inside CSuper() constructor.
Inside CSub()  constructor.

示例

下面显示了编译器如何注入一个super()调用来调用父类的无参数构造函数

class CSuper {
  public CSuper() {
    super(); // Injected by the compiler
    System.out.println("Inside CSuper() constructor.");
  }
}

class CSub extends CSuper {
  public CSub() {
    super(); // Injected by the compiler
    System.out.println("Inside CSub()  constructor.");
  }
}

public class Main {
  public static void main(String[] args) {
    CSub cs = new CSub();
  }
}

上面的代码生成以下结果。

关键字super指的是类的直接父类(超类)。可以调用超类构造函数，使用super关键字必须要放在构造函数中的第一个语句。

无参数构造函数

可以将超类的无参数构造函数或任何其他构造函数显式地调用在类的构造函数中的第一个语句。只有在没有明确添加的情况下，编译器才会注入无参数构造函数调用。

class Employee {
  private String name = "Unknown";

  public Employee(String name) {
    this.name = name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

class Manager extends Employee {
  public Manager(String name) {
    super(name);
  }
}

public class Main {
  public static void main(String[] args) {
    Manager mgr = new Manager("李经理");
    String name = mgr.getName();
    System.out.println("Manager name:  " + name);
  }
}

上面的代码生成以下结果。

Manager name:  李经理

每个类都必须直接或间接地从其构造函数中调用其超类的构造函数。如果超类没有无参数构造函数，那么必须显式地调用超类的其他构造函数。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java继承和构造函数

Java继承方法隐藏(覆盖) - Java面向对象设计™

方法隐藏

一个类从其超类继承所有非私有静态方法。在子类中重新定义继承的静态方法称为方法隐藏。子类中的重定义静态方法隐藏其超类的静态方法。在类中重定义非静态方法称为方法覆盖。
关于方法隐藏的重定义方法(名称，访问级别，返回类型和异常)的所有规则与方法覆盖相同。

class MySuper {
  public static void print() {
    System.out.println("Inside MySuper.print()");
  }
}

class MySubclass extends MySuper {
  public static void print() {
    System.out.println("Inside MySubclass.print()");
  }
}

public class Main {
  public static void main(String[] args) {
    MySuper mhSuper = new MySubclass();
    MySubclass mhSub = new MySubclass();
    MySuper.print();
    MySubclass.print();
    ((MySuper) mhSub).print();
    mhSuper = mhSub;
    mhSuper.print();
    ((MySubclass) mhSuper).print();
  }
}

上面的代码生成以下结果。

Inside MySuper.print()
Inside MySubclass.print()
Inside MySuper.print()
Inside MySuper.print()
Inside MySubclass.print()

字段隐藏

子类中的字段声明(静态或非静态)，在其父类中会隐藏具有相同名称的继承字段，也就是说在字段名称相同，子类读取字段时，只能读取子类中的字段无法读取到父类中的字段。
在字段隐藏的情况下，不考虑字段的类型及其访问级别。字段隐藏仅基于字段名称。

class MySuper {
  protected int num = 100;
  protected String name = "Tom";
}

class MySub extends MySuper {
  public void print() {
    System.out.println("num: " + num);
    System.out.println("name: " + name);
  }
}

class MySub2 extends MySuper {
  // Hides num field in MySuper class
  private int num = 200;

  // Hides name field in MySuper class
  private String name = "Jack";

  public void print() {
    System.out.println("num: " + num);
    System.out.println("name: " + name);
  }
}

public class Main {
  public static void main(String[] args) {
    MySub fhSub = new MySub();
    fhSub.print();
    MySub2 fhSub2 = new MySub2();
    fhSub2.print();
  }
}

执行上面的代码，将会得到如下结果 -

num: 100
name: Tom
num: 200
name: Jack

示例

以下代码显示了如何使用 super 关键字访问超类中的隐藏字段。

class MySuper {
  protected int num = 100;
  protected String name = "Tom";
}

class MySub extends MySuper {
  // Hides the num field in MySuper class
  private int num = 200;

  // Hides the name field in MySuper class
  private String name = "Jack";

  public void print() {
    System.out.println("num: " + num);
    System.out.println("super.num: " + super.num);
    System.out.println("name: " + name);
    System.out.println("super.name: " + super.name);
  }
}

public class Main {
  public static void main(String[] args) {
    MySub s = new MySub();
    s.print();
  }
}

上面的代码生成以下结果。

num: 200
super.num: 100
name: Jack
super.name: Tom

字段隐藏发生在一个类声明一个变量与来自其继承超类中的变量的名称相同时。字段隐藏仅基于字段的名称。子类可使用关键字super来访问超类的隐藏字段。子类可以使用简单的名称来访问其主体中的重定义的字段。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java继承方法隐藏(覆盖)

Java抽象类和方法 - Java面向对象设计™

Java可以定义这样的一个类：这个类不能创建对象。它的目的只是表示一个想法或是一个框架，提供给其他类的对象共用。这样的类称为抽象类。

语法

在类声明中需要使用abstract关键字来声明一个抽象类。
例如，下面的代码声明一个Shape的抽象类：

public abstract class Shape  {

}

下面的代码为Shape类添加了一个draw()方法。

public abstract class Shape  {
    public  Shape() {
    }

    public abstract  void  draw();
}

抽象类不一定要求它具有至少一个抽象方法。如果一个类有一个被声明或继承的抽象方法，它必须被声明为抽象类。抽象方法的声明方式与任何其他方法相同，只是在方法的最后加上分号表示，并且不会有任何执行代码语句。

示例

下面的Shape类有抽象和非抽象方法。

abstract class Shape {
  private String name;

  public Shape() {
    this.name = "Unknown  shape";
  }

  public Shape(String name) {
    this.name = name;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  // 带有分号，表示为抽象方法
  public abstract void draw();

  public abstract double getArea();

  public abstract double getPerimeter();
}

下面的代码展示了如何创建一个Rectangle类，它继承自Shape类。

class Rectangle extends Shape {
  private double width;
  private double height;

  public Rectangle(double width, double height) {
    // Set the shape name as "Rectangle"
    super("Rectangle");
    this.width = width;
    this.height = height;
  }

  // Provide an implementation for inherited abstract draw() method
  public void draw() {
    System.out.println("Drawing a  rectangle...");
  }

  // Provide an implementation for inherited abstract getArea() method
  public double getArea() {
    return width * height;
  }

  // Provide an implementation for inherited abstract getPerimeter() method
  public double getPerimeter() {
    return 2.0 * (width + height);
  }
}

这个 Rectangle类要实现父类Shape类中的所有抽象方法，否则会有错误出现。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java抽象类和方法

Java泛型类 - Java面向对象设计™

使用泛型(也叫通用)，可以在不知道代码操作的对象的类型的情况下编写代码。 它允许创建通用类，构造函数和方法。泛型类是使用形式类型参数定义的。
形式类型参数是一个逗号分隔的变量名列表，放在类声明中类名后的尖括号<>中。

语法

下面的代码声明一个类Wrapper，它接受一个形式类型参数：

public class Wrapper<T>  {

}

上代码中，参数已命名为T。T是一个类型变量，它可以是Java中的任何引用类型，例如：String，Integer，Double等。

当使用Wrapper类时，要指定形式类型参数值。

实际参数

采用形式类型参数的类也称为参数化类。可以声明Wrapper类的变量，将String类型指定为其形式类型参数的值，如下所示。

Wrapper<String> stringWrapper;

这里，String是实际的类型参数。
Java允许使用一个泛型类不指定形式类型参数。

用法

这是为了向后兼容。还可以声明Wrapper <T>类的变量，如下所示：

Wrapper  aRawWrapper;

当使用通用类而不指定实际的类型参数时，它被称为原始类型。 上面的声明使用Wrapper <T>类作为原始类型，因为它没有指定T类型的值。

通用类的实际类型参数必须是引用类型。原始类型不允许作为通用类的实际类型参数。
类可以采用多个正式类型参数。 下面的代码声明一个Mapper类，它接受两个形式参数T和R：

public class Mapper<T, R>    {

}

可以声明Mapper <T，R>类的变量如下：

Mapper<String, Integer>  mapper;

这里，实际的类型参数是String和Integer。形式类型参数在类体中可用作类型。

public class Wrapper<T> {
  private T obj;

  public Wrapper(T obj) {
    this.obj = obj;
  }

  public T get() {
    return obj;
  }

  public void set(T obj) {
    this.obj = obj;
  }
}

Wrapper <T>类使用形式类型参数来声明实例变量obj，以声明其构造函数和set()方法的形式参数，以及作为get()方法的返回类型。

可以通过为构造函数指定实际的类型参数来创建泛型类型的对象，如下所示：

Wrapper<String> w1  = new Wrapper<String>("Hello");

也可以省略实际的类型参数。 在下面的代码中，省略指定String类型，编译器会将构造函数的实际类型参数推断为String：

Wrapper<String> w1  = new Wrapper<>("Hello");

当声明了泛型类的一个变量，可以把形式类型参数看作是指定的实际类型参数。现在，可以认为，对于w1，Wrapper类的get()方法返回一个String。

String s1=  w1.get();

示例

以下代码显示如何使用Wrapper类。

public class Main {
  public static void main(String[] args) {
    Wrapper<String> w1 = new Wrapper<>("Hello");
    String s1 = w1.get();
    System.out.println("s1=" + s1);

    w1.set("Testing generics");
    String s2 = w1.get();
    System.out.println("s2=" + s2);

    w1.set(null);
    String s3 = w1.get();
    System.out.println("s3=" + s3);
  }
}

class Wrapper<T> {
  private T obj;

  public Wrapper(T obj) {
    this.obj = obj;
  }

  public T get() {
    return obj;
  }

  public void set(T obj) {
    this.obj = obj;
  }
}

执行上面代码，输出结果如下 -

s1=Hello
s2=Testing generics
s3=null

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java泛型类

Java泛型约束 - Java面向对象设计™

无限通配符

通配符类型由问号表示，如 <?> 中所示。对于通用类型，通配符类型为对象类型用于原始类型。
可以将任何已知类型的泛型分配为通配符类型。
参考以下示例代码：

// MyBag  of  String type
MyBag<String> stringMyBag  = new MyBag<String>("Hi");

// You can  assign a  MyBag<String> to  MyBag<?> type
MyBag<?> wildCardMyBag  = stringMyBag;

通配符通配类型中的问号(例如，<?>)表示未知类型。当使用通配符声明参数化类型作为参数类型时，这意味着不知道它的类型。

MyBag<?> unknownMyBag = new MyBag<String>("Hello");

上边界通配符

上边界通配符表示通配符的上限，如下语法

<? extends T>

这里，T是一种类型。 <? extends T>表示任何类型为T或其子类是可接受的。
例如，上限可以是数字类型。

如果传递任何其他类型，是Number类型或它的子类，没有问题。 但是，如果不是Number类型或其子类型的任何东西都会在编译时被拒绝。

使用上限作为数字值(Number)，可以将方法定义为 -

class MyBag<T> {
  private T ref;

  public MyBag(T ref) {
    this.ref = ref;
  }

  public T get() {
    return ref;
  }

  public void set(T a) {
    this.ref = a;
  }
}

public class Main {
  public static double sum(MyBag<? extends Number> n1,
      MyBag<? extends Number> n2) {
    Number num1 = n1.get();
    Number num2 = n2.get();
    double sum = num1.doubleValue() + num2.doubleValue();
    return sum;
  }

}

不管为n1和n2传递的是什么，它们将始终与Number的赋值兼容，因为编译器确保传递给sum()方法的参数遵循其声明中指定的规则 <? extends Number> 。

下限通配符

指定下限通配符与指定上限通配符相反。使用下限通配符的语法是<? super T>，这表示“任何T的超类型”。

class MyBag<T> {
  private T ref;/*from w  w w. j ava 2  s  .co  m*/

  public MyBag(T ref) {
    this.ref = ref;
  }

  public T get() {
    return ref;
  }

  public void set(T a) {
    this.ref = a;
  }
}
public class Main {
  public static <T> void copy(MyBag<T> source, MyBag<? super T> dest) {
    T value = source.get();
    dest.set(value);
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java泛型约束

Java泛型方法和构造函数 - Java面向对象设计™

泛型方法

可以在方法声明中定义类型参数，它们在方法的返回类型之前的尖括号中指定。包含泛型方法声明的类型不必是通用类型。可以在非静态方法声明中使用为泛型类型指定的类型参数。

示例

以下代码显示如何为方法m1()定义新的类型参数V。新类型参数V强制将方法m1()的第一个和第二个参数必须为相同类型。

第三个参数必须与类型T相同，这是类实例化的类型。

class MyBag<T> {
  private T ref;

  public MyBag(T ref) {
    this.ref = ref;
  }

  public T get() {
    return ref;
  }

  public void set(T a) {
    this.ref = a;
  }
}
class Test<T>  {
  public <V>  void  m1(MyBag<V>  a, MyBag<V>  b, T  c)  {

  }
}

使用通用方法

要传递方法的形式类型参数的实际类型参数，必须在方法调用中的点和方法名之间的尖括号<>中指定它。

class MyBag<T> {
  private T ref;

  public MyBag(T ref) {
    this.ref = ref;
  }

  public T get() {
    return ref;
  }

  public void set(T a) {
    this.ref = a;
  }
}
class Test<T> {
  public <V> void m1(MyBag<V> a, MyBag<V> b, T c) {
  }
}
public class Main {

  public static void main(String[] argv) {
    Test<String> t = new Test<String>();
    MyBag<Integer> iw1 = new MyBag<Integer>(new Integer(201));
    MyBag<Integer> iw2 = new MyBag<Integer>(new Integer(202));

    // Specify that Integer is the actual type for the type parameter for m1()
    t.<Integer>m1(iw1, iw2, "hello");

    t.m1(iw1, iw2, "hello");
  }
}

实例-2

以下代码显示了如何声明通用静态方法。不能在静态方法中引用包含类的类型参数。
静态方法只能引用它自己声明的类型参数。以下静态通用类型定义了类型参数T，用于约束参数source和dest的类型。

class MyBag<T> {
  private T ref;

  public MyBag(T ref) {
    this.ref = ref;
  }

  public T get() {
    return ref;
  }

  public void set(T a) {
    this.ref = a;
  }
}

public class Main {
  public static <T> void copy(MyBag<T> source, MyBag<? super T> dest) {
    T value = source.get();
    dest.set(value);
  }

  public static void main(String[] argv) {
  }
}

要为静态方法调用指定实际的类型参数，可以这样做：

Main.<Integer>copy(iw1, iw2);

通用构造函数

可以为构造函数定义类型参数。下面的代码定义了类Test的构造函数的类型参数U。它放置一个约束，构造函数的类型参数U必须是相同的，或者它的类类型参数T类型的子类型。

public class Test<T> {
  public <U extends T> Test(U k) {
  }
}

要为构造函数指定实际的类型参数值，请在 new 运算符和构造函数名称之间的尖括号中指定它，如以下代码段所示：

class Test<T> {
  public <U extends T> Test(U k) {
  }
}

public class Main {
  public static void main(String[] argv) {
    // Specify the actual type parameter for the constructor as Double
    Test<Number> t1 = new<Double> Test<Number>(new Double(1.9));

    // Let the compiler figure out, Integer is
    // the actual type parameter for the constructor
    Test<Number> t2 = new Test<Number>(new Integer(1));

  }
}

通用对象在创建中的类型推断

Java7在通用类型的对象创建表达式中增加了对类型推断的一些有限支持。
对于以下语句：

List<String> list = new ArrayList<String>();

在Java7中，可以在上面的语句中指定空尖括号，称为菱形操作符或简单的菱形<>作为ArrayList的类型参数。

List<String> list = new ArrayList<>();

如果不在对象创建表达式中为通用类型指定类型参数，那么类型是原始类型，编译器生成未检查的警告。
例如，以下语句将编译产生未选中的警告：

List<String> list = new ArrayList(); // Generates an  unchecked  warning

不能创建通用的异常类。 并且没有通用的匿名类。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java泛型方法和构造函数

Java异常处理教程 - Java面向对象设计™

异常是在没有定义正常执行路径时在Java程序的执行期间可能出现的条件。Java通过将执行操作的代码与处理错误的代码分离来处理错误。

当发生异常时，Java会创建一个包含有关异常的所有信息的对象，并将其传递给相应的异常处理代码。有关异常的信息包括异常的类型，发生异常的代码中的行号等。

try-catch块

要处理异常，请将代码放在try块中。 try块如下所示：

try  {
    // Code for the try block
    // 可能有异常的代码
}

try块以关键字try开头，后面是开括号和结束括号。try块的代码放在开口和关闭大括号内。try块本身不能使用。

它必须后跟一个或多个catch块，或一个finally块，或两者的组合。要处理可能在try块中抛出的异常，请使用catch块。一个catch块可以用于处理多种类型的异常。catch块的语法与方法的语法相似。

catch (ExceptionClassName parameterName)   {
    // Exception handling  code
}

catch块的声明就像一个方法声明。它从关键字catch开始，后面跟一对括号。在括号中，它声明了一个参数。

参数类型是应该捕获的异常类的名称。parameterName是用户给定的名称。括号后面是开口括号和结束括号。异常处理代码放在大括号中。当抛出异常时，异常对象的引用将复制到parameterName。可以使用parameterName从异常对象中获取信息。将一个或多个catch块与try块关联。
try-catch块的一般语法如下。

try  {
    // Your code  that may throw  an  exception
}
catch  (ExceptionClass1 e1){
    // Handle  exception of  ExceptionClass1 type
}
catch  (ExceptionClass2 e2){
    // Handle  exception of  ExceptionClass2 type
}
catch  (ExceptionClass3 e3){
    // Handle  exception of  ExceptionClass3 type
}

示例

下面的代码显示了如何处理除零异常。

public class Main {
  public static void main(String[] args) {
    int x = 10, y = 0, z;
    try {
      z = x / y;
      System.out.println("z = " + z);
    } catch (ArithmeticException e) {
      String msg = e.getMessage();
      e.printStackTrace();
      System.out.println("The error is: " + msg);
    }

    System.out.println("The end.");
  }
}

上面的代码生成以下结果。

java.lang.ArithmeticException: / by zero
    at Main.main(Main.java:5)
The error is: / by zero
The end.

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java异常处理教程

Java异常抛出 - Java面向对象设计™

如果要在一段代码中抛出一个已检查的异常，有两个选择：

使用try-catch块处理已检查的异常。

在方法/构造函数声明中用throws子句指定。

语法

throws子句的一般语法是：

<modifiers> <return type> <method name>(<params>) throws<List of Exceptions>{

}

关键字throws用于指定throws子句。throws子句放在方法参数列表的右括号之后。throws关键字后面是以逗号分隔异常类型的列表。

示例-1

以下代码显示如何在方法声明中使用throws子句

import java.io.IOException;

public class Main {
  public static void readChar() throws IOException {
    int input = System.in.read();

  }
}

这里是显示如何使用它的代码。

实例-2

在调用方法中捕捉抛出的异常 -

import java.io.IOException;

public class Main {
  public static void readChar() throws IOException {
    int input = System.in.read();
    System.out.println(input);
  }

  public static void main(String[] args) {
    try {
      readChar();
    } catch (IOException e) {
      System.out.println("Error occurred.");
    }
  }

}

上面的代码生成以下结果(输入-1回车，得到以下结果)。

-1
45

实例-3

继续 实例-2 直接抛出异常。

import java.io.IOException;

public class Main {
  public static void readChar() throws IOException {
    int input = System.in.read();
    System.out.println(input);
  }

  public static void main(String[] args) throws IOException {
    readChar();
  }
}

上面的代码生成以下结果(输入-1回车，得到以下结果)。

-1
45

抛出异常

可以使用throw语句在代码中抛出异常。throw语法的语法是 -

throw <A throwable object reference>;

throw是一个关键字，后面跟着一个可抛出对象的引用。throwable对象是一个类的实例，它是Throwable类的子类，或Throwable类本身。

以下是throw语句的示例，它抛出一个IOException：

// Create an  object of  IOException
IOException e1  = new IOException("File not  found");
// Throw the   IOException
throw  e1;

可以创建一个throwable对象并将其放在一个语句中。

// Throw an  IOException
throw  new IOException("File not  found");

如果抛出一个被检查的异常，必须使用try-catch块来处理它，或者在方法或构造函数声明中使用throws子句。

如果抛出未经检查的异常，上面的这些规则不适用。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java异常抛出

Java自定义异常 - Java面向对象设计™

我们可以创建自己(自定义)的异常类。它们必须扩展现有的异常类。

<Class Modifiers> class <Class Name> extends <Exception Class Name> {

}

<Class Name>是异常类名。在接下来的示例中，将创建一个MyException类，它扩展了java.lang.Exception类。

语法

自定义一个异常类的语法如下：

public class MyException  extends  Exception  {
}

异常类与Java中的任何其他类一样。 通常不向异常类中添加任何方法。许多可用于查询异常对象状态的有用方法已经在Throwable类中声明了。

自定义异常类构造函数

通常，异常类包括四个构造函数。所有构造函数将使用super关键字调用其超类的相应构造函数。

class MyException extends Exception {
  public MyException() {
    super();
  }

  public MyException(String message) {
    super(message);
  }

  public MyException(String message, Throwable cause) {
    super(message, cause);
  }

  public MyException(Throwable cause) {
    super(cause);
  }
}

第一个构造函数创建一个具有null的异常作为其详细消息。
第二个构造函数创建一个具有详细消息的异常。
第三和第四个构造函数允许通过包装/不包含详细消息的另一个异常来创建异常。

自定义异常类之后，就可以抛出MyException类型的异常。

throw new MyException("Your  message  goes  here");

可以在方法/构造函数声明中的throws子句中使用MyException类，或者在catch块中使用参数类型。

public void  m1()  throws   MyException  {
}

或捕获异常类

try  {

}catch(MyException e)  {

}

Throwable类

下面的列表显示了Throwable类的一些常用方法。
Throwable类是Java中所有异常类的超类。此表中显示的所有方法在所有异常类中都可用。

方法 | 方法描述
Throwable getCause() | 返回异常的原因。如果未设置异常的原因，则返回null。
String getMessage() | 返回异常的详细消息。
StackTraceElement[] getStackTrace() | 返回堆栈跟踪元素的数组。
Throwable initCause(Throwable cause) | 设置异常的原因。有两种方法可以将异常设置为异常的原因。 其他方法是使用构造函数，它接受原因作为参数。
void printStackTrace() | 在标准错误流上打印堆栈跟踪。
void printStackTrace(PrintStream s) | 将堆栈跟踪打印到指定的PrintStream对象。
void printStackTrace(PrintWriter s) | 将堆栈跟踪打印到指定的PrintWriter对象。
String toString() | 返回异常对象的简短描述。

实例-1

以下代码演示了使用异常类的printStackTrace()方法。

public class Main {
  public static void main(String[] args) {
    try {
      m1();
    } catch (MyException e) {
      e.printStackTrace(); // Print the stack trace
    }
  }

  public static void m1() throws MyException {
    m2();
  }

  public static void m2() throws MyException {
    throw new MyException("有异常或错误发生，鬼知道发生了什么，先抛出来再说！");
  }
}
class MyException extends Exception {
  public MyException() {
    super();
  }

  public MyException(String message) {
    super(message);
  }

  public MyException(String message, Throwable cause) {
    super(message, cause);
  }

  public MyException(Throwable cause) {
    super(cause);
  }
}

上面的代码生成以下结果。

实例-2

以下代码显示了如何将异常的堆栈跟踪写入字符串中。

import java.io.PrintWriter;
import java.io.StringWriter;

public class Main {
  public static void main(String[] args) {
    try {
      m1();
    } catch (MyException e) {
      String str = getStackTrace(e);
      System.out.println(str);
    }
  }
  public static void m1() throws MyException {
    m2();
  }

  public static void m2() throws MyException {
    throw new MyException("有异常或错误发生，鬼知道发生了什么，先抛出来再说！");
  }

  public static String getStackTrace(Throwable e) {
    StringWriter strWriter = new StringWriter();
    PrintWriter printWriter = new PrintWriter(strWriter);
    e.printStackTrace(printWriter);

    // Get the stack trace as a string
    String str = strWriter.toString();

    return str;
  }
}

class MyException extends Exception {
  public MyException() {
    super();
  }

  public MyException(String message) {
    super(message);
  }

  public MyException(String message, Throwable cause) {
    super(message, cause);
  }

  public MyException(Throwable cause) {
    super(cause);
  }
}

上面的代码生成以下结果。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java自定义异常

Java finally块 - Java面向对象设计™

try块也可以有零个或一个finally块。 finally块总是与try块一起使用。

语法

finally块的语法是：

finally  {
    // Code for finally block
}

finally块以关键字finally开始，后面紧跟一个大括号和一个大括号。finally块的代码放在大括号内。

try，catch和finally块有两种可能的组合：try-catch-finally或try-finally。try块可以后跟零个或多个catch块。try块最多可以有一个finally块。try块必须有一个catch块和一个finally块或两者都有。try-catch-finally块的语法是：

try  {
    // Code for try block
}
catch(Exception1 e1)  {
    // Code for catch block
}
finally  {
    // Code for finally block
}

try-finally块的语法是：

try  {
    // Code for try block
}
finally  {
    // Code for finally block
}

无论在相关联的try块和/或catch块中发生什么，finally块中的代码都保证执行。所以，一般使用finally块来写清理代码。
例如，可能用来释放一些资源，如数据库连接的关闭，文件的关闭等，当完成它们时，必须释放。

在try-finally块允许实现这个逻辑。代码结构将如下所示：

try  {
    // Obtain   and  use  some resources here
}
finally  {
    // Release the   resources that were  obtained in the   try  block
}

示例-1

下面的代码演示了finally块的使用。

public class Main {
  public static void main(String[] args) {
    int x = 10, y = 0, z = 0;
    try {
      System.out.println("Before dividing x  by  y.");
      z = x / y;
      System.out.println("After dividing x  by  y.");
    } catch (ArithmeticException e) {
      System.out.println("Inside  catch block a.");
    } finally {
      System.out.println("Inside finally  block a.");
    }

    try {
      System.out.println("Before setting  z  to 2.");
      z = 2;
      System.out.println("After setting  z  to 2.");
    }
    catch (Exception e) {
      System.out.println("Inside  catch block b.");
    } finally {
      System.out.println("Inside finally  block b.");
    }
    try {
      System.out.println("Inside try block c.");
    }
    finally {
      System.out.println("Inside finally  block c.");
    }

    try {
      System.out.println("Before  executing System.exit().");
      System.exit(0);
      System.out.println("After  executing System.exit().");

    } finally {
      // This finally block will not be executed
      // because application exits in try block
      System.out.println("Inside finally  block d.");
    }
  }
}

上面的代码生成以下结果。

Before dividing x  by  y.
Inside  catch block a.
Inside finally  block a.
Before setting  z  to 2.
After setting  z  to 2.
Inside finally  block b.
Inside try block c.
Inside finally  block c.
Before  executing System.exit().

重新抛出一个异常

捕获的异常可以被重新抛出。

public class Main {
  public static void main(String[] args) {
    try {
      m1();
    } catch (MyException e) {
      // Print the stack trace
      e.printStackTrace();
    }
  }

  public static void m1() throws MyException {
    try {
      m2();
    } catch (MyException e) {
      e.fillInStackTrace();
      throw e;
    }
  }

  public static void m2() throws MyException {
    throw new MyException("An  error has  occurred.");
  }
}

class MyException extends Exception {
  public MyException() {
    super();
  }

  public MyException(String message) {
    super(message);
  }

  public MyException(String message, Throwable cause) {
    super(message, cause);
  }

  public MyException(Throwable cause) {
    super(cause);
  }
}

上面的代码生成以下结果。

MyException: An  error has  occurred.
    at Main.m1(Main.java:14)
    at Main.main(Main.java:4)

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java finally块

Java异常用法 - Java面向对象设计™

访问线程的堆栈

以下代码显示了如何获取线程的堆栈帧。Throwable对象在创建线程的点处捕获线程的堆栈。参考以下代码 -

public class Main {
  public static void main(String[] args) {
    m1();
  }

  public static void m1() {
    m2();
  }

  public static void m2() {
    m3();
  }
  public static void m3() {
    Throwable t = new Throwable();
    StackTraceElement[] frames = t.getStackTrace();
    printStackDetails(frames);
  }
  public static void printStackDetails(StackTraceElement[] frames) {
    System.out.println("Frame count: " + frames.length);
    for (int i = 0; i < frames.length; i++) {
      int frameIndex = i; // i = 0 means top frame
      System.out.println("Frame Index: " + frameIndex);
      System.out.println("File Name: " + frames[i].getFileName());
      System.out.println("Class Name: " + frames[i].getClassName());
      System.out.println("Method Name: " + frames[i].getMethodName());
      System.out.println("Line Number: " + frames[i].getLineNumber());
    }
  }
}

上面的代码生成以下结果。

Frame count: 4
Frame Index: 0
File Name: Main.java
Class Name: Main
Method Name: m3
Line Number: 12
Frame Index: 1
File Name: Main.java
Class Name: Main
Method Name: m2
Line Number: 9
Frame Index: 2
File Name: Main.java
Class Name: Main
Method Name: m1
Line Number: 6
Frame Index: 3
File Name: Main.java
Class Name: Main
Method Name: main
Line Number: 3

try-with-resources块

Java 7添加了一个名为try-with-resources的新结构。使用Java 7中的新的try-with-resources构造，上面的代码可以写成 -

try (AnyResource aRes = create the resource...) {
    // Work with the resource here.
    // The resource will be  closed automatically.
}

当程序退出构造时，try-with-resources构造自动关闭资源。资源尝试构造可以具有一个或多个catch块和/或finally块。可以在try-with-resources块中指定多个资源。两个资源必须用分号分隔。
最后一个资源不能后跟分号。以下代码显示了try-with-resources使用一个和多个资源的一些用法(语法)：

try (AnyResource  aRes1  = getResource1())  {
    // Use aRes1  here
}

try (AnyResource  aRes1  = getResource1(); AnyResource  aRes2  = getResource2())  {
    // Use aRes1  and  aRes2  here
}

在try-with-resources中指定的资源是隐式最终的。在try-with-resources中的资源必须是java.lang.AutoCloseable类型。Java7添加了AutoCloseable接口，它有一个close()方法。当程序退出try-with-resources块时，将自动调用所有资源的close()方法。

在多个资源的情况下，按照指定资源的相反顺序调用close()方法。

class MyResource implements AutoCloseable {

  public MyResource() {
    System.out.println("Creating MyResource.");
  }
  @Override
  public void close() {

    System.out.println("Closing  MyResource...");
  }
}

public class Main {
  public static void main(String[] args) {
    try (MyResource mr = new MyResource();
        MyResource mr2 = new MyResource()) {

    }
  }
}

上面的代码生成以下结果。

Creating MyResource.
Creating MyResource.
Closing  MyResource...
Closing  MyResource...

多catch块

Java 7增加了对多catch块的支持，以便在catch块中处理多种类型的异常。可以在multi-catch块中指定多个异常类型。每个异常之间使用竖线(|)分隔。

捕获三个异常：Exception1，Exception2和Exception3。

try  {
    // May  throw  Exception1, Exception2, or  Exception3
}
catch (Exception1 | Exception2 | Exception3  e)  {
    // Handle  Exception1, Exception2, and  Exception3
}

在多catch块中，不允许有通过子类化相关的替代异常。
例如，不允许以下多catch块，因为Exception1和Exception2是Throwable的子类：

try  {
    // May  throw  Exception1, Exception2, or  Exception3
}
catch (Exception1 | Exception2 | Throwable    e)  {
    // Handle  Exceptions here
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java异常用法

Java接口教程 - Java面向对象设计™

Java接口教程

　接口是什么？

Java中的接口定义了一个引用类型来创建抽象概念。 接口由类实现，它是用来提供概念的实现。

在Java 8之前，一个接口只能包含抽象方法。 Java 8允许接口具有实现的静态和默认方法。

接口通过抽象概念定义不相关类之间的关系。例如，可以创建一个Person类来表示一个人，也可以创建一个Dog类来表示一只狗。

人和狗都可以走路。 这里的走路是一个抽象的概念。 狗可以走路，人也是走路。这里可以创建一个名为Walkable的接口来表示走路这个概念。然后在Person类和Dog类中各自实现Walkable概念(如：实现怎么走路)。 Person类实现了Walkable接口以人的方式走路(如：用两条腿走路)。 Dog类可以实现Walkable接口以狗的方式走路(如：用四条腿走路)。

示例-1

下面将使用一个例子来说明为什么需要接口。
假设Person类有一个walk()方法。
在相同的包下创建一个接口文件 - Walkable.java 并使用以下代码 -

public interface Walkable {
  void walk(); // 接口中只有一个方法
}

在相同的包下创建一个主类 -Main.java 并使用以下代码 -

class Person implements Walkable {
    public Person() {
    }

    public void walk() {
        System.out.println("人走路：使用两条腿足路.");
    }
}

class Dog implements Walkable {
    public Dog() {
    }

    public void walk() {
        System.out.println("狗走路：使用四条腿足路.");
    }
}

public class Main {
    public static void main(String[] args) {
        Walkable[] w = new Walkable[2];
        w[0] = new Person();
        w[1] = new Dog();
        w[0].walk();
        w[1].walk();
        // 或者
        // Person p = new Person();
        // p.walk();
    }
}

上面的代码输出结果如下 -

人走路：使用两条腿足路.
狗走路：使用四条腿足路.

类可以在其声明中使用关键字implements实现一个或多个接口。通过实现一个接口，类保证它将为接口中声明的所有方法提供一个实现，或者类将自己声明为抽象。
如果一个类实现了Walkable接口，它必须提供walk()方法的具体实现。

像类一样，接口定义了一个新的引用类型。当定义一个新的接口(例如，Walkable)时，定义一个新的参考接口类型。
以下声明有效：

Walkable  w;  // w  is a  reference variable of  type   Walkable

但是不能创建接口类型的对象，因为接口是定义抽象概念。 以下代码无效，编译会出错：

new Walkable(); // A  compile-time error

只能为类类型创建对象，但是可以使用一个接口类型变量来引用那些类实现该接口的任何对象。
如：Person和Dog类都实现了Walkable接口，所以Walkable类型的引用变量可以引用这两个类的对象。如下代码是没有问题的：

Walkable  w1  = new Person(); // OK
Walkable  w2  = new Dog();     // OK

可以使用它的引用类型变量访问接口的任何成员。 由于Walkable接口只有一个成员，也就是walk()方法，所以这里可以编写如下代码：

// Let  the   person  walk
w1.walk();
// Let  the   dog walk
w2.walk();

当调用w1的walk()方法时，它调用Person对象的walk()方法，因为w1指的是Person对象。当调用w2的walk()方法时，它调用Dog对象的walk()方法，因为w2指的是Dog对象。

当使用接口类型的引用变量调用方法时，它将调用它所引用的对象的方法。

实例-2

下面的代码创建了一个方法来使用接口参数类型。

public class Main{
  public static void main(String[] args) {
    Walkable[] w = new Walkable[2];
    w[0] = new Person();
    w[1] = new Dog();

    Walkables.letThemWalk(w);
  }
}

class Walkables {
  public static void letThemWalk(Walkable[] list) {
    for (Walkable w : list) {
      w.walk();
    }
  }
}

声明接口

声明接口的一般语法是 -

<modifiers> interface <interface-name>  {
    Constant-Declaration
    Method-Declaration
    Nested-Type-Declaration
}

接口声明以修饰符列表开头，也可以为空(不写)。像类一样，一个接口可以有一个公共或包级别的范围。
关键字public用于指示接口具有公共范围。缺少范围(不使用)修饰符表示接口具有包级别作用域。 具有包级别作用域的接口只能在其包的成员内引用。
关键字interface用于声明接口，后面是接口的名称。

接口的名称必须是有效的Java标识符。接口主体跟在其名称后面并放在大括号内。接口的主体可以为空。 以下是最简单的接口声明：

package com.yiibai;

interface Updatable  {
    // The interface body  is empty
}

像类一样，一个接口有一个简单的名称和一个完全限定名。关键字interface后面的标识符是其简单名称。接口的完全限定名称通过使用其包名称和用点分隔的简单名称组成。

在上面的示例中，Updatable是简单的名称，com.yiibai.Updatable是完全限定名称。使用接口的简单和完全限定名的规则与类的规则相同。
下面的代码声明一个名为ReadOnly的接口。 它有一个公共(public)范围。

package  com.yiibai;

public interface  ReadOnly {
    // The interface body  is empty
}

接口声明总是抽象的，无论是否明确声明它为抽象。

标记接口

标记接口是没有成员的接口。标记接口用来标记类具有特殊含义。

interface  Shape {
}
class Circle implements Shape{
}

Shape c = new Circle();

if (c instanceof Shape)  {
   System.out.println("Using a  Shape object");
}

Java API有许多标记接口。如：java.lang.Cloneable，java.io.Serializable和java.rmi.Remote都是标记接口。

功能接口

仅具有一个抽象方法的接口被称为功能接口。

多态性 - 一个对象，许多视图

多态性是指对象具有多种形式的能力。多态性是对象提供其不同视图的能力。使用接口使得我们可以创建一个多态对象。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口教程

Java接口字段 - Java面向对象设计™

一个接口可以有三种类型的成员：

常量字段

抽象，静态和默认方法

静态类型作为嵌套接口和类

一个接口不能有可变的实例和类变量。与类不同，接口不能被实例化。 接口的所有成员都是隐式公开的。

常量字段声明

可以在接口中声明常量字段，如下所示。 它声明一个名为Choices的接口，它有两个字段的声明：YES和NO。 两者都是int数据类型。

public interface  Choices   {
    public static final int YES  = 1;
    public static final int NO  = 2;
}

接口中的所有字段都是隐式的public，static和final。Choices接口可以声明如下，而不改变其含义：

public interface  Choices   {
    int YES  = 1;
    int NO  = 2;
}

可以使用点形式的形式访问接口中的字段，如下 -

<interface-name>.<field-name>

可以使用Choices.YES和Choices.NO访问Choices接口中的YES和NO字段的值。下面的代码演示了如何使用点符号来访问接口的字段。

public class ChoicesTest {
  public static void main(String[] args) {
    System.out.println("Choices.YES = " + Choices.YES);
    System.out.println("Choices.NO = " + Choices.NO);
  }
}

接口中的字段总是最终的(final)，无论关键字final是否在其声明中使用。必须在声明时初始化一个字段。
可以使用编译时或运行时常量表达式来初始化字段。 由于final字段只赋值一次，因此除了声明之外，不能设置接口字段的值。

以下代码显示了接口的一些有效和无效字段声明：

public interface ValidFields {
  int X = 10;
  int Y = X;
  double N = X + 10.5;

  boolean YES = true;
  boolean NO = false;

  //Test TEST = new Test();
}

使用接口中字段名称中的所有大写字母表示它们是常量是一种约定。接口的字段总是公共的(public)。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口字段

Java接口方法 - Java面向对象设计™

接口方法声明

可以在接口中声明三种类型的方法：

抽象方法

静态方法

默认方法

在Java 8之前，只能在接口中声明抽象方法。 修饰符static和default用于分别声明静态和默认方法。
不使用static和default修饰符就是方法抽象。

以下是具有所有三种类型方法的接口的示例：

interface AnInterface {
  // An abstract method
  int m1();

  // A static method
  static int m2()  {
    // The method  implementation goes  here
  }

  // A default method
  default int m3() {
    // The method implementation goes here
  }
}

抽象方法声明

接口中的所有方法声明都是隐式抽象和公开的，除非它们声明为static或default。接口中的抽象方法没有实现。抽象方法的主体总是由分号表示，而不是一对大括号。
下面的代码声明一个名为Player的接口：

public interface Player {
  public abstract void play();

  public abstract void stop();

  public abstract void forward();

  public abstract void rewind();
}

Player 接口是音频/视频播放器的规范。真实的播放器，例如DVD播放器，将通过实现 Player 接口的所有四个方法来提供规范的具体实现。
在接口中使用 abstract 和 public 关键字声明方法中是多余的。上面的Player接口的声明可以改写如下，而不改变其含义：

public interface  Player {
    void  play();
    void  stop();
    void  forward();
    void  rewind();
}

接口中的抽象方法声明可以包括参数，返回类型和throws子句。

public interface NewPlayer {
  boolean play(int account) throws AccountNotFoundException;

  boolean stop(double amount);

  boolean forward(double amount) throws InsufficientBalanceException;

  double rewind();
}

接口的抽象方法由实现接口的类来实现，类重写它们以提供方法并实现。接口中的抽象方法不能声明为final。
类可以重写声明接口的方法为final，指示子类不能覆盖该方法。

静态方法声明

从Java 8，我们可以在接口中创建静态方法。静态方法包含静态修饰符- static ，并且是隐式公开的。可以重新定义Walkable接口以包括letThemWalk()方法。

interface Walkable {
  // An abstract method
  void walk();

  // A static convenience method
  public static void letThemWalk(Walkable[] list) {
    for (int i = 0; i < list.length; i++) {
      list[i].walk();
    }
  }
}

可以使用点表示法使用接口的静态方法。如下代码 -

<interface-name>.<static-method>

与类中的静态方法不同，接口中的静态方法不能通过实现类或子接口来继承。从另一个接口继承的接口称为子接口。 只有一种方法来调用接口的静态方法：使用接口名称。例如：
必须使用MyInterface.myStaticMethod()调用接口MyInterface的静态方法myStaticMethod()。

可以使用方法的非限定名称myStaticMethod()来调用它，这仅在接口的主体中，或者当使用静态import语句导入方法时可这样使用。

默认方法声明

接口中的默认方法使用修辞符-default来声明。默认方法是在Java 8中添加新功能。默认方法为实现接口的类提供了一个默认实现，但不覆盖默认方法。
假设，有以下的一个接口。

interface Shape{
  void setX(double x);
  void setY(double y);
  double getX();
  double getY();
}

下面的代码显示了 Circle 类实现了 Shape 接口。

class Circle implements Movable {
  private double x;
  private double y;

  public Circle() {
  }

  public Circle(double x, double y) {
    this.x = x;
    this.y = y;
  }

  public void setX(double x) {
    this.x = x;
  }

  public void setY(double y) {
    this.y = y;
  }

  public double getX() {
    return x;
  }

  public double getY() {
    return y;
  }

  public String toString() {
    return "Circle(" + x + ", " + y + ")";
  }
}

如果向Shape添加一个新方法如下。

interface Shape {
  void setX(double x);

  void setY(double y);

  double getX();

  double getY();

  void move(double deltaX, double deltaY);
}

在Java 8之前，新方法move()是一个抽象方法。 所有实现Shape接口的类都必须提供这个新方法的实现。
实现Shape接口的Pen类将不能通过编译，除非将新方法添加实现到这些类中。在Java 8之前，在将接口分配给公共接口之后，在中断实现接口方法代码的情况下，向接口添加方法是不可能的。
所以，引入了Java接口默认方法这个解决方案。可以将默认方法添加到现有接口，并为该方法提供默认实现而不用在实现接口的类中实现这个默认方法。

所有实现接口的类都将继承默认实现。类可以选择覆盖默认实现或使用方法的默认实现。

默认方法使用关键字default声明。 默认方法不能声明为abstract或static。 它必须提供一个实现。 否则将在编译时发生错误。
以下代码使用默认方法更改Shape接口。

interface Movable {
  void setX(double x);

  void setY(double y);

  double getX();

  double getY();

  // 一个默认的方法
  default void move(double deltaX, double deltaY) {
    double newX = getX() + deltaX;
    double newY = getY() + deltaY;
    setX(newX);
    setY(newY);
  }
}

以下项目列出了类方法和接口默认方法之间的相似点和差异。

两者都可以以相同的方式访问关键字this。 关键字this是调用方法的对象的引用。

类一个具体方法可以访问类的实例变量。

默认方法不能访问实现接口的类的变量的实例。

默认方法可以访问接口的其他成员。

两种类型的方法都可以使用它们的参数。

两个方法都可以有一个throws子句。

接口中的嵌套类型声明定义了一个新的引用类型。

可以将一个类，接口，枚举和注释声明为嵌套类型。

在接口内声明的接口/类称为嵌套接口/类。

接口和类定义新的引用类型，因此做嵌套接口和嵌套类。

嵌套接口始终通过其封装接口访问。

可以在接口中声明一个嵌套类。

参考以下一个具有嵌套类和常量字段的Task接口。

interface Task {
  class EmptyTask implements Task {
    private EmptyTask() {
    }
    public void runJob() {
      System.out.println("Empty...");
    }
  }
  // A constant field
  Task EMPTY_JOB = new EmptyTask();
  void runJob();
}

public class Main {
  public static void main(String[] args) {
    submitJob(Task.EMPTY_JOB);
  }
  public static void submitJob(Task job) {
    job.runJob();
  }
}

Java接口方法 # Java_interface_Methods

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口方法

Java接口作为类型 - Java面向对象设计™

接口定义了一个新的引用类型。可以使用接口类型来声明变量，在方法中声明参数类型，作为方法的返回类型等。

interface  Shape {
    void  draw();
}
public class Main {
  // interface type as instance variable
  private Shape myShape;

  // interface type as parameter type for a constructor
  public Main(Shape s) {
    this.myShape = s;
  }

  // interface type as return type of a method
  public Shape getShape() {
    return this.myShape;
  }

  // interface type as parameter type for a method
  public void setShape(Shape s) {
    this.myShape = s;
  }

  public void letItSwim() {
    // interface type as a local variable
    Shape locaShape = null;

    locaShape = this.myShape;

    // interface variable can invoke methods
    // declared in the interface and the Object class
    locaShape.draw();
  }
}

接口类型的变量是指其类实现该接口的内存中的对象。使用接口类型的变量或直接使用接口名称来访问接口中声明的任何常量字段。

最好使用接口名访问接口的常量。使用接口类型的变量来调用接口中声明的任何方法。接口类型的变量可以调用java.lang.Object类的任何方法。

默认情况下，接口类型的实例或静态变量将初始化为null。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口作为类型

Java接口实现 - Java面向对象设计™

实现接口

接口指定对象必须提供的协议。类可以提供接口的抽象方法的部分实现，并且在这种情况下，类必须将自身声明为抽象。

实现接口的类使用“implements”子句来指定接口的名称。“implements”子句由关键字implements，后跟逗号分隔的接口类型列表组成。

一个类可以实现多个接口。实现接口的类声明的一般语法如下：

<modifiers> class  <class-Name>  implements <comma-separated-list-of-interfaces>  {
    // Class body  goes  here
}

假设有一个Circle类。

public class Circle implements Shape {
   void  draw(){
      System.out.println("draw circle");
   }
}

实现接口的类必须重写以实现接口中声明的所有抽象方法。 否则，类必须声明为abstract。接口的默认方法也由实现类继承。植入类可以选择不需要重写默认方法。接口中的静态方法不会被实现类继承。
下面的代码定义了两种引用类型，一种来自Circle类，另一种来自接口类型。

Circle c = new Circle();
Shape shape = new Circle();

变量c是Circle类型。它指的是Circle对象。第二个赋值也是有效的，因为Circle类实现了Shape接口，而Circle类的每个对象也都是Shape类型。

实现接口方法

当一个类完全实现了一个接口时，它为所实现的接口的所有抽象方法提供一个实现。接口中的方法声明包括方法的约束。例如，方法声明中的throws子句是方法的约束。

import java.io.IOException;
interface Shape {
  void draw(double amount) throws IOException;
}
class Main implements Shape{

  @Override
  public void draw(double amount) {
    // TODO Auto-generated method stub
  }
}

Main的代码是有效的，即使它丢弃了throws子句。当类覆盖接口方法时，允许删除约束异常。如果使用Shape类型，必须处理IOException。

import java.io.IOException;

interface Shape {
  void draw(double amount) throws IOException;
}
class Main implements Shape{

  @Override
  public void draw(double amount) {
    // TODO Auto-generated method stub

  }
  public void anotherMethod(){
    Shape s = new Main();
    try {
      s.draw(0);
    } catch (IOException e) {
      e.printStackTrace();
    }
    draw(0);
  }
}

实现多个接口

一个类可以实现多个接口。类实现的所有接口都在类声明中的关键字implements之后列出。
通过实现多个接口，类为所有接口中的所有抽象方法提供实现。

interface Adder {
  int add(int n1, int n2);
}
interface Subtractor {
  int subtract(int n1, int n2);
}
class Main implements Adder, Subtractor {
  public int add(int n1, int n2) {
    return n1 + n2;
  }
  public int subtract(int n1, int n2) {
    return n1 - n2;
  }
}

部分实现接口

类不必为所有方法提供实现。如果一个类不提供接口的完全实现，它必须声明为abstract。

interface Calculator {
  int add(int n1, int n2);

  int subtract(int n1, int n2);
}
abstract class Main implements Calculator{
  public int add(int n1, int n2) {
    return n1 + n2;
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口实现

Java接口继承 - Java面向对象设计™

接口可以从另一个接口继承。与类不同，接口可以继承多个接口。如下代码所示 -

interface Singer {
  void sing();
  void setRate(double rate);
  double getRate();
}
interface Writer {
  void write();
  void setRate(double rate);
  double getRate();
}
interface Player {
  void play();
  void setRate(double rate);
  default double getRate() {
    return 300.0;
  }
}

一个接口使用关键字extends来继承自其他接口。 关键字extends之后是以逗号分隔的继承接口名称列表。继承的接口称为超级接口，继承接口的接口称为子接口。

接口继承其超级接口的以下成员：

抽象和默认方法

常量字段

嵌套类型

接口不从其超级接口继承静态方法。接口可以重写它从其超级接口继承的继承的抽象和默认方法。如果超级接口和子接口具有相同名称的字段和嵌套类型，则子接口会覆盖父接口对应字段。

interface A {
  String s = "A";
}
interface B extends A {
  String s = "B";
}
public class Main {
  public static void main(String[] argv){
    System.out.println(B.s);
  }
}

以下代码显示如何覆盖父接口的默认方法。

interface A {
  default String getValue(){
    return "A-Interface";
  }
}
interface B extends A {
  default String getValue(){
    return "B-Interface";
  }
}

class MyClass implements B{
}

public class Main {
  public static void main(String[] argv){
    System.out.println(new MyClass().getValue());
  }
}

上面的代码生成以下结果。

B-Interface

继承冲突实现

引入默认方法使得类可以从其超类和超级接口继承冲突的实现。Java使用三个简单的规则为了解决冲突。如下 -

总是以超接口的为准

最具体的以超接口为准

类必须覆盖冲突的方法

instanceof运算符

可以使用instanceof运算符来评估计算引用类型变量是指特定类的对象，还是其类实现特定接口。instanceof运算符的一般语法是 -

referenceVariable instanceof  ReferenceType

参考以下实现代码 -

interface A {//  ww  w. j  a  va 2  s.  co  m
  default String getValue(){
    return "A";
  }
}
interface B {
  default String getValue(){
    return "B";
  }
}

class MyClass implements B,A{
  public String getValue(){
    return "B";
  }
}

public class Main {
  public static void main(String[] argv){
    MyClass myClass = new MyClass();
    System.out.println(myClass instanceof MyClass);
    System.out.println(myClass instanceof A);
    System.out.println(myClass instanceof B);
  }
}

上面的代码生成以下结果。

true
true
true

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java接口继承

Java枚举类型 - Java面向对象设计™

枚举类型是什么？

枚举类型创建常量的有序列表作为类型。它以特定顺序指定常量。在枚举类型中定义的常量是该枚举类型的实例。

语法

使用关键字enum定义枚举类型，语法以下：

<access-modifier> enum <enum-type-name>  {
    // List of  comma  separated names of  enum constants
}

<access-modifiers>与类的访问修饰符相同：public，private，protected或package-level。

<enum-type-name>是有效的Java标识符。枚举类型的主体放在大括号中，名称后面。主体可以有逗号分隔的常数和其他元素的列表，例如实例变量，方法等。
在大多数情况下，枚举体只包括常量。

示例

以下代码声明了一个名为Gender的枚举类型，它声明了两个常量：MALE和FEMALE：

public enum Gender  {
    MALE,  FEMALE;  // The semi-colon is optional
}

将枚举常量命名为大写是一种惯例。以下代码声明了一个名为Level的公共枚举类型，它具有四个枚举常量：LOW，MEDIUM，HIGH和URGENT。

public enum Level {
    LOW,  MEDIUM,  HIGH, URGENT;
}

可以从应用程序中的任何位置访问公共枚举类型。公共枚举类型保留在具有枚举类型名称的文件中。 级别枚举将保存在名为Level.java的文件中。
需要在包中放置枚举类型，使用import语句来导入枚举类型。

枚举变量

声明枚举类型的变量，就像声明类的变量一样。以下代码声明Level枚举类型的errorLevel变量，如下代码所示 -

Level errorLevel;

可以为枚举类型变量分配null值，如下所示：

Level defectLevel = null;

在 switch 语句中使用枚举类型

可以在switch语句中使用枚举类型。当switch表达式是枚举类型时，所有case标签必须是同一枚举类型的枚举常量。

enum Direction {
  East, South, West, North
}

public class Main {
  public static void main(String args[]) {
    Direction dir = Direction.South;
    switch (dir) {
    case South:
      System.out.println("south");
      break;
    case East:
      System.out.println("East");
      break;
    case West:
      System.out.println("West");
      break;
    case North:
      System.out.println("North.");
      break;
    }
  }
}

上面的代码生成以下结果。

south

嵌套枚举类型

可以在类，接口或另一个枚举类型中有一个嵌套的枚举类型声明。嵌套枚举类型是隐式静态的。 因为枚举类型总是静态的，所以不能在方法体内声明一个局部枚举类型。
对于嵌套枚举类型，可以使用任何访问修饰符(public，private，protected 或 package)级别。

以下代码显示了如何在Person类中声明名为Gender的嵌套公共枚举类型。

class Person {
  public enum Gender {
    MALE, FEMALE
  }
}

public class Main {
  public static void main(String[] args) {
    Person.Gender m = Person.Gender.MALE;
    Person.Gender f = Person.Gender.FEMALE;
    System.out.println(m);
    System.out.println(f);
  }
}

Person.Gender枚举类型可以从应用程序中的任何位置访问，因为它已被声明为public。也可以通过使用静态导入导入枚举常量来使用枚举常量的简单名称。

import static  com.java2s.enums.Person.Gender.*;

枚举类型实现接口

枚举类型可以实现接口。枚举类型实现接口的规则与实现接口的类的规则相同。
下面的代码演示了如何让枚举类型实现一个接口。

interface Command {
  void execute();
}

enum Level implements Command {
  LOW {
    public void execute() {
      System.out.println("Low...");
    }
  },
  HIGH {
    public void execute() {
      System.out.println("HIGH...");
    }
  };
  public abstract void execute();
}

public class Main {
  public static void main(String... args) {
    for (Command cmd : Level.values()) {
      cmd.execute();
    }
  }
}

上面的代码生成以下结果。

Low...
HIGH...

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java枚举类型

Java枚举值 - Java面向对象设计™

枚举类型定义了两件事：

枚举常量名称

常量的顺序

以下代码声明了一个名为Level的公共枚举类型，它具有四个枚举常量：LOW，MEDIUM，HIGH 和 URGENT。

public enum Level {
    LOW,  MEDIUM,  HIGH, URGENT;
}

Level枚举类型定义了四个枚举常量。
级别枚举类型的变量只能是以下四个值之一 - LOW，MEDIUM，HIGH 和 URGENT 或 null。可以通过使用枚举类型名称作为限定符和点符号来引用枚举常量。
以下代码将Level枚举类型常量的值分配给一些局部变量：

Level low  = Level.LOW;
Level medium = Level.MEDIUM;
Level high  = Level.HIGH;
Level urgent = Level.URGENT;

但是不能实例化枚举类型。例如，以下代码导致编译时错误：

Level badAttempt   = new Level(); // A  compile-time error

枚举顺序号 枚举类型为其所有常量分配一个顺序号，称为ordinal。顺序值从零开始，在常数列表中从第一个到最后一个依次递增1。第一个枚举常量赋值为0，第二个为1，第三个为2，依此类推。
分配给Level枚举类型中声明的常量的序数值是：将0分配给LOW，1分配给MEDIUM，2到HIGH，以及3到URGENT。

每个枚举常量都有一个与常量的标识符相同的名称。例如，级别枚举类型中的LOW常数的名称为“LOW”。可以分别使用name()和ordinal()方法获取枚举常量的名称和序号值。每个枚举类型都有一个名为values()的静态方法，它按在枚举主体中声明的顺序返回一个常量数组。
以下代码打印在Level枚举类型中声明的所有枚举常量的名称和序号。

enum Level {
  LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
  public static void main(String[] args) {
    for (Level s : Level.values()) {
      String name = s.name();
      int ordinal = s.ordinal();
      System.out.println(name + "(" + ordinal + ")");
    }
  }
}

上面的代码生成以下结果。

LOW(0)
MEDIUM(1)
HIGH(2)
URGENT(3)

枚举常量的反向查找

可以从名称或位置获得枚举常量的引用。使用valueOf()方法就可以做到这点。

枚举常量的反向查找区分大小写。可以使用values()方法返回的数组，通过顺序执行反向查找。values()方法返回的数组中的值的顺序与枚举常量的声明顺序相同。枚举常量的序数从零开始。
以下代码演示了如何反转查找枚举常量：

enum Level {
  LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
  public static void main(String[] args) {
    Level low1 = Level.valueOf("LOW");
    // A reverse lookup using a name
    Level low2 = Level.values()[0];
    // A reverse lookup using an ordinal
    System.out.println(low1);
    System.out.println(low2);
    System.out.println(low1 == low2);

  }
}

执行上面的示例代码，得到如下结果 -

LOW
LOW
true

枚举常量的范围

java.util.EnumSet集合类与枚举类型的枚举常量列表一起使用。
下面的代码演示了如何使用EnumSet类来处理枚举常量的范围。

import java.util.EnumSet;

enum Level {
    LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
    public static void main(String[] args) {
        EnumSet<Level> allLevels = EnumSet.allOf(Level.class);
        print(allLevels);

        EnumSet<Level> l = EnumSet.range(Level.LOW, Level.URGENT);
        print(l);

    }

    public static void print(EnumSet<Level> levels) {
        for (Level d : levels) {
            System.out.println(d + "  ");
        }
    }
}

执行上面的示例代码，得到如下结果 -

LOW
MEDIUM
HIGH
URGENT
LOW
MEDIUM
HIGH
URGENT

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java枚举值

Java Enum类 - Java面向对象设计™

枚举类型的超类

编译枚举类型时，编译器会创建一个类。枚举类型具有构造函数，字段和方法。 枚举类型仅在编译器生成的代码中实例化。

每个枚举类型都隐式地扩展java.lang.Enum类。 Enum类中定义的所有方法都可以与所有枚举类型一起使用。

Enum类

下表列出了在Enum类中定义的方法。

方法名称 | 方法描述
public final String name() | 返回枚举常量的名称
public final int ordinal() | 返回枚举常量的顺序值
public final boolean equals(Object other) | 如果指定的对象等于枚举常量，则返回true。 否则，它返回false。当它们用于两个枚举常量，==运算符和equals()方法返回相同的结果。
public final int hashCode() | 返回枚举常量的哈希码值。
public final int compareTo(E o) | 将此枚举常量与指定的枚举常量进行比较。 它返回此枚举常量和指定枚举常量的序数值的差异。
public final Class getDeclaringClass() | 返回声明枚举常量的类的对象。
public String toString() | 默认情况下，它返回枚举常量的名称，与name()方法相同。
public static valueOf(Class enumType, String name) | 返回指定的枚举类型和名称的枚举常量。

示例

Level  lowLevel = Enum.valueOf(Level.class, "LOW")

以下代码显示了如何比较两个枚举常量：

enum Level {
  LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
  public static void main(String[] args) {
    Level s1 = Level.LOW;
    Level s2 = Level.HIGH;

    // s1.compareTo(s2) returns s1.ordinal() - s2.ordinal()
    int diff = s1.compareTo(s2);
    if (diff > 0) {
      System.out.println(s1 + "  occurs after  " + s2);
    } else {
      System.out.println(s1 + "  occurs before " + s2);
    }

  }
}

上面的代码生成以下结果。

LOW  occurs before HIGH

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java Enum类

Java枚举方法 - Java面向对象设计™

因为枚举类型实际上是一个类，所以可在枚举类型体中声明一切，也可在类体中声明它。
以下代码使用字段，构造函数和方法定义了一个名为 Level 的枚举。

public enum Level {
  LOW(30), MEDIUM(15), HIGH(7), URGENT(1);

  // Declare an instance variable
  private int levelValue;

  // Declare a private constructor
  private Level(int levelValue) {
    this.levelValue = levelValue;
  }

  public int getLevelValue() {
    return levelValue;
  }
}

上面的代码声明了一个实例变量levelValue，它将为每个枚举常量存储一个值。它还定义了一个私有构造函数，它接受一个int参数。 它将其参数的值存储在实例变量中。
可以向枚举类型添加多个构造函数。但不能向枚举类型添加公共或受保护的构造函数。

Level枚举中声明一个公共方法getLevelValue()。枚举常量声明已更改为 -

LOW(30), MEDIUM(15),  HIGH(7),  URGENT(1);

现在每个枚举常量名称后面都跟着括号中的整数值。LOW(30)是调用int参数类型的构造函数的缩写。当创建枚举常量时，括号中的值将被传递给添加的构造函数。LOW调用默认的无参数构造函数，而LOW(30)调用有参数的构造函数。

示例

以下代码测试Level枚举类型。它打印常量的名称，序数值和它们的下划线值。

enum Level {
  LOW(30), MEDIUM(15), HIGH(7), URGENT(1);

  // Declare an instance variable
  private int levelValue;

  // Declare a private constructor
  private Level(int levelValue) {
    this.levelValue = levelValue;
  }

  public int getLevelValue() {
    return levelValue;
  }
}

public class Main {
  public static void main(String[] args) {
    for (Level s : Level.values()) {
      String name = s.name();
      int ordinal = s.ordinal();
      int underLine = s.getLevelValue();
      System.out.println("name=" + name + ",  ordinal=" + ordinal + ", underLine="
          + underLine);
    }
  }
}

上面的代码生成以下结果。

name=LOW,  ordinal=0, underLine=30
name=MEDIUM,  ordinal=1, underLine=15
name=HIGH,  ordinal=2, underLine=7
name=URGENT,  ordinal=3, underLine=1

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java枚举方法

Java枚举体 - Java面向对象设计™

将主体添加到枚举常量

可以为每个枚举常量添加一个不同的主体。主体可以有字段和方法。枚举常量的主体放在其名称后的大括号中。如果枚举常量接受参数，其主体将遵循其参数列表。将主体与枚举常量相关联的语法如下：

<access-modifier> enum <enum-type-name>  {
   ENUM_VALUE1  {
      // Body for ENUM_VALUE1  goes  here
   },
   ENUM_VALUE2  {
      // Body for ENUM_VALUE2  goes  here
   },
   ENUM_VALUE3(arguments-list)  {
      // Body of  ENUM_VALUE3  goes  here
   };

   // Other  code  goes  here
}

示例

以下代码创建了Level枚举类型和它的主体。

enum Level {
  LOW("Low Level", 30) {
    public double getDistance() {
      return 30.0;
    }
  },
  MEDIUM("Medium Level", 15) {
    public double getDistance() {
      return 15.0;
    }
  },
  HIGH("High Level", 7) {
    public double getDistance() {
      return 7.0;
    }
  },
  URGENT("Urgent Level", 1) {
    public double getDistance() {
      return 1.0;
    }
  };

  private int levelValue;
  private String description;

  private Level(String description, int levelValue) {
    this.description = description;
    this.levelValue = levelValue;
  }

  public int getLevelValue() {
    return levelValue;
  }

  @Override
  public String toString() {
    return this.description;
  }

  public abstract double getDistance();
}

public class Main {
  public static void main(String[] args) {
    for (Level s : Level.values()) {
      String name = s.name();
      String desc = s.toString();
      int ordinal = s.ordinal();
      int levelValue = s.getLevelValue();
      double distance = s.getDistance();
      System.out.println("name=" + name + ",  description=" + desc
          + ",  ordinal=" + ordinal + ", levelValue=" + levelValue
          + ", distance=" + distance);
    }
  }
}

Level枚举有一个抽象方法getDistance()。每个实例常量都有一个实体为getDistance()方法提供实现。它覆盖了Enum类中的toString()方法。
执行上面的代码，得到如下结果 -

name=LOW,  description=Low Level,  ordinal=0, levelValue=30, distance=30.0
name=MEDIUM,  description=Medium Level,  ordinal=1, levelValue=15, distance=15.0
name=HIGH,  description=High Level,  ordinal=2, levelValue=7, distance=7.0
name=URGENT,  description=Urgent Level,  ordinal=3, levelValue=1, distance=1.0

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java枚举体

Java枚举比较 - Java面向对象设计™

可以通过三种方式比较两个枚举常量：

使用Enum类的compareTo()方法

使用Enum类的equals()方法

使用==运算符

Enum类的compareTo()方法比较同一枚举类型的两个枚举常量。它返回两个枚举常量的序数差。如果两个枚举常量相同，则返回零。

示例

以下代码将打印-3，因为LOW(ordinal = 0)和URGENT(ordinal = 3)的序数的差为-3。

负值表示正在比较的常数发生在比较对象之前。

enum Level {
  LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
  public static void main(String[] args) {
    Level s1 = Level.LOW;
    Level s2 = Level.URGENT;

    // s1.compareTo(s2) returns s1.ordinal() - s2.ordinal()
    int diff = s1.compareTo(s2);
    System.out.println(diff);
  }
}

上面的代码生成以下结果。

-3

实例-2

Enum类的equals()方法比较两个枚举常量的相等性。枚举常量只等于自身。equals()方法可以在不同类型的两个枚举常量上调用。

enum Level {
  LOW, MEDIUM, HIGH, URGENT;
}

public class Main {
  public static void main(String[] args) {
    Level s1  = Level.LOW;
    Level s2  = Level.URGENT;
    System.out.println(s1.equals(s1));
  }
}

上面的代码生成以下结果。

true

可以使用等于(==)运算符来比较两个枚举常量的相等性。等于(==)运算符的两个操作数必须是相同的枚举类型。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java枚举比较

Java注解 - Java面向对象设计™

Java注解是什么？

在Java中的注解允许将元数据与程序元素相关联。
程序元素可以是包，类，接口，类的字段，局部变量，方法，方法的参数，枚举，注解，通用类型/方法声明中的类型参数等等。

可以在Java程序中注解任何声明或类型。注解在任何其他修饰符(public，private，final，static等)的程序元素的声明中用作修饰符。与修饰符不同，注解不会修改程序元素的含义。它是程序元素的注解。

声明注解类型

声明注解类型类似于声明接口类型。注解类型是一种特殊类型的接口类型。使用前面带有@符号的interface关键字来声明注解类型(注意与声明接口的区别)。
以下是声明注解类型的一般语法：

<modifiers> @interface <annotation-type-name>  {
    // Annotation type body
}

注解声明的<modifiers>修辞符与接口声明的相同。可以将注解类型声明为public或package级别。@符号与interface关键字之间可以用空格分隔，也可以连接在一起。按照惯例，它们一般是连接放在一起，如：@interface。interface关键字后面是注解类型名称。 它应该是有效的Java标识符。

注解类型正文放在大括号中。以下代码创建注解以使用版本信息注解程序元素。

public  @interface  Version {
    int  major();
    int  minor();
}

在上面代码中的Version注解类型中声明了两个抽象方法：major()和minor()。注解类型中的抽象方法称为元素。注解可以声明零个或多个元素，它们被声明为抽象方法。抽象方法名称是注解类型的元素的名称。
在上面的代码中，Version注解类型声明了两个元素：major和minor。 两个元素的数据类型都是int。

如果要导入一个注解类型，那么它应该就像导入任何其他类型一样。

注解实例

要创建注解类型的实例并使用它来注解程序元素，使用以下语法：

@annotationType(name1=value1,  name2=value2, names3=values3...)

注解类型前面带有一个@符号，后面是用括号括起来的逗号分隔的name = value对列表。name = value对中的名称是在注解类型中声明的元素的名称和值，它由用户提供。name = value对不必按照在注释类型中声明的顺序出现。
以下代码使用Version类型的注解，主要元素值设置为1(major=1)，次要元素值设置为0(minor=0)。

@Version(major=1, minor=0)

也可以使用注释类型的完全限定名，如下代码 -

@com.java2s.annotation.Version(major=0,  minor=1)

可以将类声明注释为 -

@Version(major=1, minor=0)
public  class  VersionTest {

}

可以将程序元素的注释与其他修饰符混合使用。

@Version(major=1, minor=0)  public  class  VersionTest {
}

public  @Version(major=1, minor=0)  class  VersionTest {
}

完整的源代码

@interface Version {
  int major();

  int minor();
}

@Version(major = 1, minor = 0)
public class Main {
  @Version(major = 1, minor = 1)
  private int xyz = 110;

  @Version(major = 1, minor = 0)
  public Main() {
  }

  @Version(major = 1, minor = 1)
  public Main(int xyz) {
    this.xyz = xyz;
  }

  @Version(major = 1, minor = 0)
  public void printData() {
  }

  @Version(major = 1, minor = 1)
  public void setXyz(int xyz) {

    @Version(major = 1, minor = 2)
    int newValue = xyz;

    this.xyz = xyz;
  }
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解

Java注解类型的限制 - Java面向对象设计™

注解类型的限制

注解类型不能从另一个注释类型继承。每个注释类型都隐式地继承java.lang.annotation.Annotation接口，其声明如下：

package  java.lang.annotation;

public interface  Annotation  {
   boolean equals(Object  obj);
   int  hashCode();
   String toString();
   Class<? extends  Annotation> annotationType();
}

注解类型中的方法声明不能指定任何参数。注解类型中的方法声明不能有throws子句。在注解类型中声明的方法的返回类型必须是以下类型之一：

任何原始类型： byte, short, int, long, float, double, boolean, 和 char 等等

java.lang.String

java.lang.Class

枚举类型

注解类型

任何上述类型的数组，例如，String[], int[]等等

返回类型不能是嵌套数组。 例如，返回类型不能是String [][]或int [][]的二维或多维数组。

可以使用以下方法声明注释，如下所示：

public  @interface MyAnnotation {
    Class element1();  // Any Class type
    Class<Test> element2();  // Only  Test   class  type
    Class<? extends  Test>  element3(); // Test   or  its  subclass type
}

注释类型不能是通用的。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解类型的限制

Java注解默认值 - Java面向对象设计™

可以为注解中的元素定义默认值。不需要为带有默认值的注解元素提供值。
默认值可以使用以下一般语法定义：

<modifiers> @interface <annotation type name> {
    <data-type> <element-name>() default <default-value>;
}

关键字default指定默认值。默认值必须是与元素的数据类型兼容的类型。
以下代码通过将minor元素的默认值指定为0，来创建Version注释类型，如下所示：

public  @interface  Version {
    int major();
    int minor() default 0; // zero as default value for minor
}

示例

以下代码显示注解如何使用默认值。

@Version(major=1) // minor is zero, which  is its  default value
@Version(major=2, minor=1)  // minor  is 1, which  is the   specified  value

以下代码显示如何为数组和其他数据类型指定默认值：

public @interface Version {
  double d() default 1.89;

  int num() default 1;

  int[] x() default { 1, 2 };

  String s() default "Hello";

  String[] s2() default { "abc", "xyz" };

  Class c() default Exception.class;

  Class[] c2() default { Exception.class, java.io.IOException.class };
}

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解默认值

Java注解用法 - Java面向对象设计™

Java注解用法

注解元素的提供值必须是编译时常量表达式，不能使用null作为注解中任何类型元素的值。

基本类型

注解类型中的元素的数据类型可以是任何基本数据类型：byte，short，int，long，float，double，boolean 和 char。

Version注释类型声明两个元素：major和minor，并且都是int数据类型。

以下代码声明了注解类型：

public @interface MyAnnotation {
  byte a();

  short b();

  int c();

  long d();

  float e();

  double f();

  boolean g();

  char h();
}

@MyAnnotation(a=1, b=2,  c=3,  d=4,  e=12.34F, f=1.89, g=true, h='Y')

可以使用编译时常量表达式来指定注解元素的值。以下两个版本注释实例有效的：

@Version(major=2+1, minor=(int)13.2)
@Version(major=3, minor=13)

字符串类型

可以在注释类型中使用String类型的元素。以下代码定义了名为Name的注解类型。 它有两个元素：first和last，它们都是String类型。

public @interface Name  {
   String first();
   String last();
}

@Name(first="Tom", last="Smith")
public class NameTest {
    @Name(first="Jack", last="Iaan")
    public void  aMethod()   {
    }
}

在String类型的元素的值表达式中使用字符串连接+运算符是有效的。

@Name(first="Ja" + "ck", last="Ia" + "an")

Class类型

以下代码显示如何使用Class类型作为注解值。

import java.io.IOException;

@interface MyAnnotation {
  Class<? extends Throwable> willThrow() default java.lang.Throwable.class;
}

public class Main {
  @MyAnnotation(willThrow = IOException.class)
  public static void testCase1() {
    // Code goes here
  }

  @MyAnnotation()
  public static void testCase2() {
  }
}

枚举类型

注解可以具有枚举类型的元素。

enum Level {
  PENDING, FAILED, PASSED;
}

@interface Review {
  Level status() default Level.PENDING;

  String comments() default "";
}

@Review(status = Level.PASSED)
public class Main {

}

注解类型

可以使用注解类型作为另一个注解类型的声明内的元素的类型。要为注解类型的元素提供值，请使用用于创建注解类型实例的语法。

@interface Name {
  String first();

  String last();
}

@interface Version {
  int major();

  int minor() default 0; // zero as default value for minor
}

@interface Description {
  Name name();

  Version version();

  String comments() default "";
}

@Description(name = @Name(first = "Tom", last = "Smith"), version = @Version(major = 1, minor = 2), comments = "Just a  test class")
public class Main {

}

注解可以具有数组类型的元素。数组类型可以是以下类型之一：

原始类型

java.lang.String类型

java.lang.Class类型

枚举类型

注解类型

需要在大括号中指定数组元素的值。数组的元素由逗号分隔。

@interface ItemList {
  String[] items();
}

@ItemList(items = { "A", "B" })
public class Main {

}

如果数组中只有一个元素，则允许省略括号。

@ToDo(items={"A"})
@ToDo(items="A")

传递一个空数组

@ToDo(items={})

速记注解语法

假设有一个注释类型如下。

public  @interface Enabled  {
    boolean status() default true;
}

要使用带有默认值的Enabled注解类型注释程序元素，可以使用@Enabled()语法。
不需要为status元素指定值，因为它具有默认值。可以进一步省略括号。

@Enabled
public class Main {

}

@Enabled()
public class Main {

}

只有一个元素的注解类型有速记语法。如果注释类型只有一个具有命名值的元素，可以省略name = value对中的名称。以下代码声明了 Company 注解类型，它只有一个名为value的元素：

public  @interface Company  {
    String value();
}

当使用 Company 注释时，可以省略name = value对的名称。

@Company(value="Inc.")
public class Test   {
}

变成 -

@Company("Inc.")
public class Test   {
}

以下代码显示了如果元素数据类型是数组，如何使用缩写。

public  @interface Item   {
    String[] value();
}

@Item({"A", "B"})
public class Test   {
}

如果在数组注释类型中只指定一个元素，可以进一步省略括号。

@Item("A")
public class Test   {
}

如果在使用注释时只提供一个值，则元素的名称为假设那个值。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解用法

Java注解类型 - Java面向对象设计™

标记注解类型

标记注解类型是没有元素的注解类型，甚至没有默认值。标记注解由注解处理工具使用。

public  @interface Marker  {
}
@Marker
public class Main{

}

元注解类型

元注解类型是注解类型，用于注解其他注解类型。元注解类型是Java类库的一部分。它们在包java.lang.annotation中声明。

以下注解类型是元注解类型：

目标

保留

继承

文档记录

可重复的

本地

目标注释类型

目标注释类型注释注释类型以设置上下文以使用注释类型。它只有一个名为value的元素。 其值元素是java.lang.annotation.ElementType枚举类型的数组。下表列出了ElementType枚举中的所有常量。

常量名称 | 描述
ANNOTATION_TYPE | 注释另一个注释类型声明，使注释类型为元注释。
CONSTRUCTOR | 注释构造函数。
FIELD | 注释字段和枚举常量。
LOCAL_VARIABLE | 注释局部变量。
METHOD | 注释方法。
PACKAGE | 注释包声明。
PARAMETER | 注释参数。
TYPE | 注释类，接口(包括注释类型)或枚举声明。
TYPE_PARAMETER | 在通用类，接口，方法等中注释类型参数。
TYPE_USE | 注释所有类型的使用。

以下Version注释类型具有目标元注释，它指定Version注释类型可以与只有三种类型的程序元素一起使用：任何类型(类，接口，枚举和注释类型)，构造函数和方法。

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;

@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD })
public @interface Version {
  int major();

  int minor();
}

Version注释不能用于除其Target注释中指定的三种类型之外的任何程序元素。

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE_USE })
@interface MyAnno {
}

@Target({ ElementType.TYPE_USE })
@interface MyAnno2 {
}

public class Main {
  public void processData() throws @MyAnno Exception {

    int roundedValue = (@MyAnno2 int) .02;

    Main t = new @MyAnno Main();
  }

}

如果不使用Target注释类型注释注释类型，则注释类型可以用作任何声明的修饰符，但类型参数声明除外。

保留(Retention)注释

保留(Retention)注释设置注释类型的保留策略。注释可以在三个级别保留 -

仅源代码

仅类文件，默认行为。

类文件和运行时

保留(Retention)元注释类型指定Java应如何保留注释。如果注释类型具有“仅限源代码”保留策略，则在编译到类文件中时将删除其实例。如果保留策略为“仅类文件”，则其实例将保留在类文件中，但不能在运行时读取。
如果保留策略为“类文件和运行时”，则注释实例保留在类文件中，并且它们可在运行时读取。保留元注释类型声明一个名为value的元素，它是java.lang.annotation.RetentionPolicy枚举类型。

RetentionPolicy枚举有三个常量：SOURCE，CLASS 和 RUNTIME，它们分别用于指定仅源，仅类和类运行时的保留策略。

以下代码在版本注释类型上使用保留元注释。它指定版本注释应该在运行时可用。

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@interface Version {
  int major();

  int minor();
}

如果不对注释类型使用保留元注释，则其保留策略仅默认为类文件。则将无法在运行时读取这些注释。在类文件中或在运行时，局部变量声明上的注释永远不可用，而不考虑注释类型的保留策略。

继承注释类型

继承注释类型是标记元注释类型。如果注释类型使用Inherited元注释注释，则其实例将由子类声明继承。
如果使用注释类型注释除类声明以外的任何元素，它没有任何效果。以下代码显示了@Inherited元注释类型的效果。

import java.lang.annotation.Inherited;

@interface Ann2 {
  int id();
}

@Inherited
@interface Ann3 {
  int id();
}

@Ann2(id = 1)
@Ann3(id = 2)
class A {
}

// Class B inherits Ann3(id=2) annotation from the class A
class B extends A {

}

文档注释

文档化注释类型是标记元注释类型。如果注释类型用Documented注解，Javadoc工具将为其所有实例生成文档。

import java.lang.annotation.Documented;

@Documented
@interface Version {
  int major();

  int minor();
}

@Version(major = 1, minor = 0)
public class Main {
}

当使用Javadoc工具为Main类生成文档时，Main类声明上的Version注释也会作为文档的一部分生成。

可重复注释

Java 8添加了可重复元注释类型。如果在一个单独的代码元素上重复使用它，注释类型声明必须用@Repeatable来注解。

Repeatable注释类型只有一个名为value的元素，其类型是另一个注释类型的类类型。

import java.lang.annotation.Repeatable;

@interface LogHistory {
  Log[] value();
}

@Repeatable(LogHistory.class)
@interface Log {
  String date();

  String comments();
}

@Log(date = "01/01/2018", comments = "B")
@Log(date = "01/21/2018", comments = "A")
public class Main {
  public static void process() {

  }
}

本地注释

本地(Native)注释类型是元注释，用于注释可以从本地代码引用的字段。它是一个标记注释。

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解类型

Java标准注解 - Java面向对象设计™

Java API定义了许多标准注解类型。

以下部分将讨论在java.lang包中定义的四种最常用的标准注解。

Deprecated

Override

SuppressWarnings

FunctionalInterface

已弃用的注解类型 - Deprecated

弃用的注解类型是标记注解类型。 它标记了已弃用的代码元素。使用已弃用的程序元素将导致编译器生成警告。

@Deprecated
class MyClass {
  private MyClass() {
  }

  public static MyClass getInstance() {
    MyClass dt = new MyClass();
    return dt;
  }
}

public class Main {
  public static void main(String[] args) {
    MyClass dt; // Generates a compile-time note
  }
}

覆盖注解类型 - Override

覆盖注解类型是只能在方法上使用的标记注解类型。它表示方法覆盖在其超类中声明的方法。
如果要覆盖超类中的一个方法，建议使用@Override注解覆盖的方法。编译器将确保注解的方法真正覆盖超类中的一个方法。

如果注解的方法不覆盖超类中的方法，编译器将生成错误。

class A {
  public void m1() {
  }
}

class B extends A {
  @Override
  public void m1() {
  }
}

抑制警告注解类型 - SuppressWarnings

抑制警告(SuppressWarnings)注解类型用于抑制命名编译器警告。它声明一个名为value的元素，其数据类型是一个String数组。

import java.util.ArrayList;

public class Main {
  @SuppressWarnings("unchecked")
  public void test() {
    @SuppressWarnings("rawtypes")
    ArrayList list = new ArrayList();
    list.add("Hello"); // The compiler issues an unchecked warning
  }
}

功能接口注解类型 - FunctionalInterface

具有一个抽象方法声明的接口被称为功能接口( - FunctionalInterface)。如果使用此注解的接口不是函数接口，则会生成编译时错误。

@FunctionalInterface
interface  MyThread{
    void  run();
}

只有抽象方法的接口是一个功能接口，不管它是否用@FunctionalInterface注解。

注解Java包

要注解一个Java包，创建一个名为package-info.java的文件，并将注解包声明放在这个文件中。以下代码显示了package-info.java文件的内容。

// package-info.java
@Version(major=1, minor=0)
package  com.yiibai.annotation;

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java标准注解

Java注解反射 - Java面向对象设计™

程序元素上的注解是Java对象。允许访问其注解的程序元素实现java.lang.reflect.AnnotatedElement接口。以下类实现了AnnotatedElement接口：
AnnotatedElement接口的方法用于访问以下列出的对象类型的注解。

java.lang.Class
java.lang.reflect.Executable
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Parameter
java.lang.Package
java.lang.reflect.AccessibleObject

注解类型必须使用运行时的保留策略来保留元注解，以便在运行时访问它。

示例 - 1

假设有一个Test类，并且想打印它的所有注解。参考以下代码片段，它将打印Test类的类声明上的所有注解：

import java.lang.annotation.Annotation;

@SuppressWarnings("unchecked")
@Deprecated
public class Main {

  public static void main(String[] argv) {
    // Get the class object reference
    Class<Main> c = Main.class;
    // Get all annotations on the class declaration
    Annotation[] allAnns = c.getAnnotations();
    System.out.println("Annotation count: " + allAnns.length);

    // Print all annotations
    for (Annotation ann : allAnns) {
      System.out.println(ann);
    }
  }
}

执行上面的代码得到如下结果 -

Annotation count: 1
@java.lang.Deprecated()

如上面的结果所示，Annotation接口的toString()方法返回注解的字符串表示形式。

实例-2

以下代码显示了如何获取指定的注解，请参考以下代码 -

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface Version {
  int major();

  int minor();
}
@Version(major=1,minor=2)
public class Main {
  public static void main(String[] argv) {
    Class<Main> c = Main.class;

    Version v = c.getAnnotation(Version.class);
    if (v == null) {
      System.out.println("Version annotation is  not  present.");
    } else {
      int major = v.major();
      int minor = v.minor();
      System.out.println("Version: major=" + major + ", minor=" + minor);
    }

  }
}

上面的代码生成以下结果。

Version: major=1, minor=2

实例-3

以下代码显示了如何访问方法的注解，请参考以下代码 -

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
@interface Version {
  int major();
  int minor();
}

@Version(major = 1, minor = 0)
class AccessAnnotation {
  @Version(major = 1, minor = 1)
  public void testMethod1() {
  }

  @Version(major = 1, minor = 2)
  @Deprecated
  public void testMethod2() {
  }
}

public class Main {
  public static void main(String[] args) {
    Class<AccessAnnotation> c = AccessAnnotation.class;
    System.out.println("Annotations for class:" + c.getName());
    printAnnotations(c);

    System.out.println("Method annotations:");
    Method[] m = c.getDeclaredMethods();
    for (int i = 0; i < m.length; i++) {
      System.out.println("Annotations for method:" + m[i].getName());
      printAnnotations(m[i]);
    }
  }

  public static void printAnnotations(AnnotatedElement programElement) {
    Annotation[] annList = programElement.getAnnotations();
    for (int i = 0; i < annList.length; i++) {
      System.out.println(annList[i]);
      if (annList[i] instanceof Version) {
        Version v = (Version) annList[i];
        int major = v.major();
        int minor = v.minor();
        System.out.println("Found  Version annotation:  " + "major  =" + major
            + ", minor=" + minor);
      }
    }
  }
}

上面的代码生成以下结果。

Annotations for class:AccessAnnotation
@Version(major=1, minor=0)
Found  Version annotation:  major  =1, minor=0
Method annotations:
Annotations for method:testMethod1
@Version(major=1, minor=1)
Found  Version annotation:  major  =1, minor=1
Annotations for method:testMethod2
@Version(major=1, minor=2)
Found  Version annotation:  major  =1, minor=2
@java.lang.Deprecated()

实例-4

以下代码显示了如何在运行时访问可重复注解的实例，请参考以下代码 -

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface LogHistory {
  Log[] value();
}
@Repeatable(LogHistory.class)
@interface Log {
  String date();
  String comments();
}

@Log(date = "02/01/2018", comments = "A")
@Log(date = "01/22/2018", comments = "B")
public class Main {
  public static void main(String[] args) {
    Class<Main> mainClass = Main.class;

    Log[] annList = mainClass.getAnnotationsByType(Log.class);
    for (Log log : annList) {
      System.out.println("Date=" + log.date() + ", Comments=" + log.comments());
    }

    Class<LogHistory> containingAnnClass = LogHistory.class;
    LogHistory logs = mainClass.getAnnotation(containingAnnClass);

    for (Log log : logs.value()) {
      System.out.println("Date=" + log.date() + ", Comments=" + log.comments());
    }
  }
}

上面的代码生成以下结果。

Date=02/01/2018, Comments=A
Date=01/22/2018, Comments=B
Date=02/01/2018, Comments=A
Date=01/22/2018, Comments=B

本站代码下载：http://www.yiibai.com/siteinfo/download.html

本文属作者原创，转载请注明出处：易百教程 » Java注解反射