Java 面向对象设计一口气讲完![]~( ̄▽ ̄)~*(上)

news2024/11/27 22:27:57

目录

Java 类实例

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

null引用类型

访问类的字段的点表示法

字段的默认初始化

Java 访问级别 

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

Java 导入

Java面向对象设计 - Java导入

单类型导入声明

按需导入声明

静态导入声明

例子

Java 方法

Java面向对象设计 - Java方法

方法参数

局部变量

Java 方法返回

Java面向对象设计 - Java方法返回

返回

Java 方法重载

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

例子

例2

Java 实例/静态方法

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

例子

注意

调用方法

Java 主方法

Java面向对象设计 - Java主要方法

Java 参数传递

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

例子

注意

Java varargs方法

Java面向对象设计 - Java varargs方法

声明varargs方法

例子

使用varargs方法

重载Varargs方法

Varargs方法和main()方法

Java this关键字

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

什么是 this?

使用 this 来区分当前对象。

说明:

例子

注意

Java 访问级别成员

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

例子

注意

Java final关键字

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

final 变量

final 类

final 方法

Java 构造函数

Java面向对象设计 - Java构造函数

声明构造函数

使用构造函数

重载构造函数

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

从构造函数返回

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

默认构造函数

Java 初始化块

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

实例初始化块

例子

多重实例初始化

静态初始化块

Java Object类

Java面向对象设计 -  Java Object类

方法

例子

什么是对象类?

Java HashCode(哈希码)

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

Object的哈希码

例子

Java Object.Equals方法

Java面向对象设计 - Java Object.Equals方法

注意

Java Object.toString方法

Java面向对象设计 - Java Object.toString方法

例子

例2

注意

Java Object.Clone方法

Java面向对象设计 - Java Object.Clone方法

例子

例2

例3

Java Object.Finalize方法

Java面向对象设计 - Java Object.Finalize方法

例子

Java Immutable(不可变)对象

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

例子

注意

Java Objects类

Java面向对象设计 - Java Objects类

HashCode

equals

toString

requireNonNull


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运算符为类的新实例分配内存,并返回对该实例的引用。

我们需要将由新运算符返回的引用存储在引用变量中。

anInstance = new Dog();

null引用类型

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

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

您可以使用一个空文字与比较运算符来检查是否相等和不等。

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

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

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


 

访问类的字段的点表示法

点符号用于引用实例变量。

点符号语法的一般形式是

<Reference Variable Name>.<Instance Variable Name>

obj.name引用obj引用变量引用的实例的名称实例变量。

要为名称实例变量分配值,请使用

obj.name = "Rectangle";

以下语句将name实例变量的值分配给String变量aName:

String aName = obj.name;

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

ClassName.ClassVariableName

例如,我们可以使用Dog.count来引用Dog类的计数类变量。

向计数类变量分配新值

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 = "Java";
    obj.gender = "Male";

    obj.name = "XML";

    String changedName = obj.name;
  }
}

字段的默认初始化

类的所有字段(静态以及非静态)都将初始化为默认值。

字段的默认值取决于其数据类型。

数字字段(字节,短,char,int,long,float和double)初始化为零。布尔字段初始化为false。引用类型字段初始化为null。

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

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

  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);
  }
}

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

Java 访问级别 

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

类简单名称是 class 关键字和 {)之间的名称。

当我们通过简单的名称引用一个类时,编译器在引用类所在的同一个包中查找该类声明。

我们可以使用全名来引用一个类如下。

com.w3cschool.Dog aDog;

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

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

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

  • no value
  • public

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

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

package  com.w3cschool;

public class Dog  {

}

Java 导入

Java面向对象设计 - Java导入

导入声明用于将任何类型导入编译单元。

导入声明出现在包声明之后,第一个类型声明之前。

有两种类型的导入声明:

  • 单类型导入声明
  • 按需导入声明

单类型导入声明

单类型导入声明用于从包导入单个类型。它是形式。

import <fully qualified name of a type>;

以下导入声明从com.w3cschool包导入Dog类:

import com.w3cschool.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.w3cschool.Dog jack;  // Uses  full qualified name for the   Dog  class
   }
}

以下代码显示如何使用单类型import语句将com.w3cschool.Dog类导入到其简单名称。

修改后的Main类声明如下:

import com.w3cschool.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.java2s.Dog类。

编译器用以下语句替换上面的语句:

com.w3cschool.Dog jack;

导入声明允许您在代码中使用类型的简单名称,从而使您的代码更具可读性。

当编译代码时,编译器用其完全限定名替换类型的简单名称。

它使用导入声明将类型的简单名称转换为其完全限定名称。

按需导入声明

按需导入声明使用一个导入声明从包导入多个类型。

按需导入声明的语法是

import <package name>.*;

这里,包名称后面跟着一个点和一个星号(*)。

例如,以下需要的import-on-demand声明从com.java2s包导入所有类型:

import com.w3cschool.*;

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

import com.w3cschool.*;

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

静态导入声明

静态导入声明将类型的静态成员(变量/方法)导入到编译单元中。

静态导入声明有两种风格:单静态导入和静态 - 按需导入。

单静态导入声明从类型中导入一个静态成员。静态导入请求声明导入类型的所有静态成员。

静态导入声明的一般语法如下:

单静态导入语句:

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

静态导入需求声明:

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

系统是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!");
    }
}

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

java.lang包中的Math类有许多实用程序常量和静态方法。

例如,它有一个类变量名为PI。

要使用Math类的任何静态变量或方法,我们需要使用类名Math来限定它们。

例如,您可以将PI静态变量称为Math.PI,将sqrt()方法称为Math.sqrt()。

我们可以使用以下static-import-on-demand声明来导入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 = 6.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  2.0: " + sqrt(2.0));
  }
}

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

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

  • 如果导入具有相同简单名称的两个静态成员,一个使用单静态导入声明,另一个使用静态import-on-demand声明,则使用单静态import声明导入的静态成员优先。
  • 使用单静态导入声明来导入具有相同简单名称的两个静态成员是不允许的。
  • 如果使用单静态导入声明导入静态成员,并且在同一类中存在具有相同名称的静态成员,则使用类中的静态成员。

Java 方法

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也称为形式参数。

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

局部变量

在方法,构造函数或块中声明的变量称为局部变量。

在方法中声明的局部变量仅在执行方法的持续时间内存在。

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

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

您需要遵守关于局部变量的使用的以下规则。

  • 默认情况下不会初始化局部变量。
  • 在向程序分配值之前,不能在程序中访问局部变量。
  • 局部变量可以在方法体中的任何地方声明。但是,它必须在使用之前声明。
  • 局部变量隐藏实例变量的名称和具有相同名称的类变量。

Java 方法返回

Java面向对象设计 - Java方法返回

修饰符,返回类型和参数名称不是签名的一部分。

方法的签名唯一地标识类中的方法。不允许在具有相同签名的类中有多个方法。

方法的代码在方法的主体中指定,方法的主体用大括号括起来。

使用其名称及其参数的值(如果有)在括号中调用方法。

要调用add方法,请使用以下语句:

add(10, 12);

上述对add方法的调用分别将10和12作为参数n1和n2的值。

用于调用add方法的两个值(10和12)称为实际参数。

Java将实际参数复制到形式参数,然后再在方法体内执行代码。

return语句用于从方法返回值。它以return关键字开始。

如果方法返回一个值,则return关键字后面必须跟有一个表达式,该表达式将计算返回的值。

如果方法不返回值,则其返回类型指定为void。如果方法的返回类型为void,则该方法不必包括return语句。

如果一个带有void返回类型的方法想要包括一个return语句,return关键字后面不能跟任何表达式; return关键字后紧跟一个分号,以标记语句的结尾。

返回

return语句将控制权返回给方法的调用者。返回语句是在方法的主体中执行的最后一个语句。

要捕获方法调用的值,请在可以使用值的任何位置使用方法调用表达式。

例如,以下代码将从add方法返回的值分配给变量调用sum:

int sum = add(10, 12); // sum variable will be  assigned 22

以下方法声明为方法printMessage;

void  printMessage()  {
      System.out.println("test"); 
}

printMessage方法将void指定为其返回类型,这意味着它不会向其调用者返回值。

它不指定任何参数,这意味着它不接受来自其调用者的任何输入值。

要调用printMessage方法,请编写以下语句:

printMessage();

由于printMessage()方法不返回任何值,因此您不能将该方法的调用用作任何需要值的表达式的一部分。

当方法的返回类型为void时,没有必要使用return语句,因为我们没有从方法返回的值。

Java 方法重载

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

在同一类中具有多个具有相同名称的方法称为方法重载。

类中具有相同名称的方法可以是声明的方法,继承的方法或两者的组合。

重载方法必须具有不同数量的参数,不同类型的参数或两者。

方法的返回类型,访问级别和throws子句对使其成为重载方法没有任何影响。

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;
  }
}

例子

下面的代码显示了如何使用重载。

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;
  }

  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);
  }
}

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

例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)

  }
}

Java 实例/静态方法

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

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

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

类方法用于实现类本身的行为。类方法总是在类的上下文中执行。

静态修饰符用于定义类方法。方法声明中缺少静态修饰符,使得​​该方法成为一个实例方法。

例子

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

// A  static or  class method 
static void  aClassMethod()  {
    
}

// A  non-static or  instance method 
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);

  }

  // 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

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

Java 主方法

Java面向对象设计 - Java主要方法

让我们讨论我们用来运行我们的类的main()方法。

main()方法声明如下:

public static  void  main(String[]  args)  {

}

在main()方法的声明中使用了两个修饰符public和static。

public修饰符使得它可以从应用程序中的任何地方访问,只要它被声明的类是可访问的。

静态修饰符使它成为一个类方法,因此可以使用类名来调用它。

它的返回类型是void,这意味着它不返回一个值给它的调用者。

它的名称是main,它接受一个类型为String array(String [])的参数。

main()方法是Java应用程序的入口方法。例如,您可以使用以下命令运行Main类:

java  com.w3cschool.Main

当你运行一个类时,JVM调用main()方法。

Java 参数传递

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

Java支持两种数据类型:基本数据类型和引用数据类型。

原始数据类型是一个简单的数据结构,它只有一个与之相关的值。引用数据类型是一个复杂的数据结构,它表示一个对象。

原始数据类型的变量将该值直接存储在其存储器地址处。

使用对象和引用变量时,事情会有所不同。

Java中的所有参数都通过值传递。

当参数是原始数据类型时,实际参数的值将复制到参数中。

对方法主体中的参数值进行的任何更改只会更改形式参数的副本,而不会更改实际参数的值。

当参数通过参考值传递时,存储在实际参数中的参考被复制到形式参数。实际参数和形式参数都指向内存中的相同对象。

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

例子

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

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

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

    myPhone.model = "iPhone";
    myPhone.year = 2009;
    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);
  }
}

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

注意

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

形式参数可以通过直接更改实例变量的值或通过调用对象上的方法来修改对象。

通过形式参数对对象进行的任何修改都可以通过实际参数立即可见,因为它们都保存对内存中同一对象的引用。

形式参数本身可以被修改以引用方法内的另一个对象。

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

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

    myPhone.model = "iPhone";
    myPhone.year = 2009;
    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;
}

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

Java varargs方法

Java面向对象设计 - Java varargs方法

术语“varargs"是“可变长度参数"的缩写。

varargs声明一个接受可变数量的参数(或参数)的方法或构造函数。

声明varargs方法

要声明varargs,在方法参数的数据类型之后添加一个省略号 ... 

下面的代码显示了一个带有一个可变长度参数num的max()方法声明,它是int数据类型。

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

}

省略号之前和之后添加空格是可选的。

varargs方法可以有多个参数。下面的代码显示aMethod()接受三个参数,其中一个是可变长度参数:

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

}

varargs方法最多可以有一个可变长度参数。

varargs方法的variable-length参数必须是参数列表中的最后一个参数。

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

}

例子

让我们重写max()方法,使其成为varargs方法:

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;
  }
}

使用varargs方法

我们可以使用for循环来处理可变长度参数的参数列表。

length属性给出了为variable-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(7, 9));
    System.out.println(max(7, 9, 10));
    System.out.println(max(7, 9, 10, 13));
  }
}

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

重载Varargs方法

方法的相同重载规则适用于varargs方法。

我们可以使用可变长度参数重载一个方法,只要方法的参数在类型,顺序或数字上不同。

例如,以下是一个重载的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首先尝试使用对参数数量的精确匹配来找到方法声明。如果没有找到完全匹配,它将使用可变长度参数查找匹配。

如果varargs方法重载,Java使用该方法的更特定版本,而不使用varargs方法。 java使用varargs方法作为解决方法调用的最后手段。

方法本身的重载可能是有效的。但是,调用它可能会导致问题。

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?

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

Varargs方法和main()方法

main()方法的签名必须是main(String [] args)。

Main类的main()方法的以下声明是有效的。

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

Java this关键字

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

什么是 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 来区分当前对象。

Java 中为解决变量的命名冲突和不确定性问题,引入关键字 this 代表其所在方法的当前对象的引用:

  1. 构造方法中指该构造器所创建的新对象;
  2. 方法中指调用该方法的对象;
  3. 在类本身的方法或构造器中引用该类的实例变量(全局变量)和方法。

this 只能用在构造器或者方法中,用于获得调用当前的构造器方法的对象引用。可以和任何的对象引用一样来处理这个this对象。

说明:
  1. 当实例变量和局部变量重名,JAVA 平台会按照先局部变量、后实例变量的顺序寻找。即,方法中使用到的变量的寻找规律是先找局部变量,再找实例变量。如果没用找到,将会有一个编译错误而无法通过编译。
  2. 如果使用 this.a,则不会在方法(局部变量)中寻找变量 a ,而是直接去实例变量中去寻找,如果寻找不到,则会有一个编译错误。
  3. 在一个方法内,如果没有出现局部变量和实例变量重名的情况下,是否使用 this 关键字是没有区别的。
  4. 在同一个类中,Java 普通方法的互相调用可以省略 ​this. ​,而直接使用方法名 + 参数。因为 Java 编译器会帮我们加上。

例子

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

public class Main {
  int num = 2014; // An instance variable

  void printNum(int num) {
    System.out.println("Parameter num: " + num);
    System.out.println("Instance variable num: " + this.num);
  }

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

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

注意

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

例如,以下代码非常常见:

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();"
  }

  void m2() {
    // do something
  }
}

Java 访问级别成员

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

类可以是public或default(或包级别)。

类成员的访问级别确定程序的哪个区域可以访问它。以下四个访问级别修饰符之一可以用于类成员:

  • public
  • private
  • protected
  • Default 或者 package-level访问

前三种类型的访问级别使用以下三个关键字之一指定:public,private或protected。

第四种类型称为默认访问级别(或包级别),并且通过不使用访问修饰符来指定。

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

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

如果使用 protected 关键字将类成员声明为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);
  }
}

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

Java final关键字

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

final关键字不允许修改或替换其原始值或定义。

final关键字可以在以下三个上下文中使用:

  • 变量声明
  • 类声明
  • 方法声明

final 变量

如果一个变量被声明为final,它只能被赋值一次。最终变量的值在设置后不能修改。

变量声明包括局部变量的声明,方法/构造函数的形式参数,实例变量和类变量。

变量声明包括局部变量的声明,方法/构造函数的形式参数,实例变量和类变量。

final int YES  = 1;

我们可以只设置一次final变量的值。

有两种方法来初始化final变量:

  • 在声明时予以初始化。
  • 将其初始化延迟到稍后的时间。

但是,我们必须在第一次读取最终变量之前初始化它。

  • final局部变量

    你可以声明一个局部变量fi​​nal。如果将局部变量声明为空的最终变量,则必须在使用前初始化它。

  • final参数

    我们可以声明一个参数final。当调用方法或构造函数时,参数将使用实际参数的值自动初始化。

    因此,您不能更改方法或构造函数体内的最终形式参数的值。

  • final实例变量

    我们可以声明一个实例变量final和blank final。

    空白最终实例变量必须初始化一次,并且只有在调用类的任何构造函数时才初始化一次。

  • final类变量

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

  • final引用变量

    引用变量存储对象的引用。最终引用变量意味着,一旦引用一个对象(或null),它就不能被修改以引用另一个对象。

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

public void  test2(final int x)  {

如果我们有一个类的多个静态初始化器,我们必须在一个静态初始化器中初始化所有空的最终类变量一次。

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,它不能在包含该方法的类的子类中重新定义(覆盖或隐藏)。

Java 构造函数

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();
  }
}

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

重载构造函数

一个类可以有多个构造函数。它们称为重载构造函数。

如果一个类有多个构造函数,它们的数量,顺序或参数类型都必须与其他构造函数不同。

下面的代码声明两个构造函数。一个构造函数不接受参数,另一个接受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) {
  }
}

具有公共访问级别的构造函数可以在程序的任何部分中使用。

具有私有访问级别的构造函数只能在声明它的同一类中使用。

具有受保护访问级别的构造函数可以在具有在其中声明类的相同包的程序中以及在任何包中的任何后代类内使用。

具有包级访问权限的构造函数可以在声明其类的同一个包中使用。

默认构造函数

具有包级访问权限的构造函数可以在声明其类的同一个包中使用。

编译器添加的构造函数称为默认构造函数。

默认构造函数没有任何参数。

默认构造函数也称为无参数构造函数。

如果类已经有一个构造函数,编译器不会添加任何构造函数。

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();
  }
}

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

实例初始化程序不能有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 = 2014;
    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
  }
}

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

静态初始化器不能抛出检查的异常,它不能有一个return语句。

Java Object类

Java面向对象设计 -  Java Object类

Java在java.lang包中有一个Object类。

所有Java类都直接或间接扩展Object类。

所有Java类都是Object类的子类Object类是所有类的超类。

Object类本身没有超类。

Object类的引用变量可以保存任何类的对象的引用。

以下代码声明对象类型的引用变量obj:

Object obj;

方法

Object类有九个方法,可以在Java中的所有类中使用。

  • 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";
    }
}

什么是对象类?

Java中的每个对象都属于一个类。

Object类的getClass()方法返回Class对象的引用。

以下代码显示了如何获取Cat对象的Class对象的引用:

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

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

我们可以使用泛型重写上面的语句。

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

Java HashCode(哈希码)

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

Object的哈希码

哈希码是一个整数值。计算整数的算法称为散列函数。

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;
  }
}

Java Object.Equals方法

Java面向对象设计 - Java Object.Equals方法

以下代码显示如何实现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));
  }
}

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

注意

这里是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的状态被修改。
  • 与空引用的比较:任何类的对象不应等于空引用。表达式x.equals(null)应始终返回false。
  • 与hashCode()方法的关系:如果x.equals(y)返回true,x.hashCode()必须返回与y.hashCode()相同的值。

Java Object.toString方法

Java面向对象设计 - Java Object.toString方法

对象的字符串表示应以可读格式包含有关对象状态的足够信息。

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);
  }
}

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

例2

以下代码显示了如何创建自己的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;
  }
}

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

注意

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

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

有两种情况值得一提:

当你连接一个字符串和一个对象

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

Java在Point对象上调用toString()方法,并将返回的值连接到“Hello"字符串。

上述语句与以下语句相同:

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

Java Object.Clone方法

Java面向对象设计 - Java Object.Clone方法

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(200.00);
    dhClone.setValue(400.00);

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

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

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

例2

以下代码不从clone方法返回对象类型,该方法仅在Java 5或更高版本中编译。

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());
  }
}

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

例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());
  }
}

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

Java Object.Finalize方法

Java面向对象设计 - Java Object.Finalize方法

Java提供了一种在对象即将被销毁时执行资源释放的方法。

在Java中,我们创建对象,但是我们不能销毁对象。

JVM运行一个称为垃圾收集器的低优先级特殊任务来销毁不再引用的所有对象。

垃圾回收器给我们一个机会,在对象被销毁之前执行清理代码。

Object类有一个finalize()方法,声明如下:

protected void  finalize() throws   Throwable  {  }

Object类中的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 < 20000; i++) {
      new Finalize(i);
    }
  }
}

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

Java Immutable(不可变)对象

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);

在这一点上,包装器对象保持101,并且没有办法改变它。

因此,IntWrapper类是一个不可变的类,它的对象是不可变的对象。

最好将所有实例变量声明为final,这样Java编译器将在编译期间强制实现不可变性。

Java Objects类

Java面向对象设计 - Java Objects类

Java在java.util包中有一个实用程序类Objects用于处理对象。

它由所有静态方法组成。 Objects类中的大多数方法都会优雅地处理空值。

以下是类中的方法列表。他们的描述遵循列表。

  • int compare(T a, T b, Comparator c)
    如果参数相同,则返回0,否则返回c.compare(a,b)。因此,如果两个参数都为null,则返回0。
  • boolean deepEquals(Object a, Object b)
    检查两个对象是否相等。如果两个参数都相等,则返回true。否则,它返回false。如果两个参数都为null,则返回true。
  • boolean equals(Object a, Object b)
    比较两个对象是否相等。如果两个参数相等,则返回true。否则,它返回false。如果两个参数都为null,则返回true。
  • int hash(Object... values)
    为所有指定的对象生成哈希码。它可以用于计算对象的哈希码,该哈希码基于多个实例字段。
  • int hashCode(Object o)
    返回指定对象的哈希码值。如果参数为null,则返回0。
  • boolean isNull(Object obj)
    如果指定的对象为null,isNull()方法返回true。否则,它返回false。您还可以使用比较运算符==检查对象是否为null,例如,obj == null返回obj的true为null。
  • boolean nonNull(Object obj)
    执行与isNull()方法相反的检查。
  • T requireNonNull(T obj)
    T requireNonNull(T obj, String message)
    T requireNonNull(T obj, Supplier messageSupplier)
    检查参数是否为null。如果参数为null,它会抛出一个NullPointerException异常。此方法设计用于验证方法和构造函数的参数。
    第二个版本可以指定当参数为null时抛出的NullPointerException的消息。
    第三个版本的方法将一个Supplier作为第二个参数。
  • String toString(Object o)
    String toString(Object o, String nullDefault)
    如果参数为null,则toString()方法返回一个“null”字符串。对于非空参数,它返回通过调用参数的toString()方法返回的值。

HashCode

下面的代码演示了如何使用来自Objects类的方法来计算哈希码。

import java.util.Objects;

public class Main {
  public static void main(String[] args) {
    // Compute hash code for two integers, a char, and a string
    int hash = Objects.hash(10, 800, "\u20b9", "Hello");
    System.out.println("Hash Code is " + hash);

  }
}

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

equals

以下代码显示了如何使用Objects类中的equals方法来比较两个对象。

import java.util.Objects;

public class Main {
  public static void main(String[] args) {
    // Test for equality
    boolean isEqual = Objects.equals(null, null);
    System.out.println("null is  equal to null:  " + isEqual);

    isEqual = Objects.equals(null, "XYZ");
    System.out.println("null is  equal to XYZ: " + isEqual);
  }
}

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

toString

以下代码显示如何使用toString方法从对象将对象转换为字符串。

import java.util.Objects;

public class Main {
  public static void main(String[] args) {
    // toString() method test
    System.out.println("toString(null) is  " + Objects.toString(null));
    System.out.println("toString(null, \"XXX\")  is "
        + Objects.toString(null, "XXX"));
  }
}

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

requireNonNull

以下代码显示如何使用Objects类中的requireNonNull。

import java.time.Instant;
import java.util.Objects;
import java.util.function.Supplier;

public class Main {
  public static void main(String[] args) {
    try {
      printName("A");
      printName(null);
    } catch (NullPointerException e) {
      System.out.println(e.getMessage());
    }
    try {
      Supplier<String> messageSupplier = () -> "Name is  required. Error generated on  "
          + Instant.now();
      printNameWithSuplier("asdf", messageSupplier);
      printNameWithSuplier(null, messageSupplier);
    } catch (NullPointerException e) {
      System.out.println(e.getMessage());
    }
  }

  public static void printName(String name) {
    Objects.requireNonNull(name, "Name is required.");
    System.out.println("Name is " + name);
  }

  public static void printNameWithSuplier(String name,
      Supplier<String> messageSupplier) {
    Objects.requireNonNull(name, messageSupplier);
  }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2192562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

decltype推导规则

decltype推导规则 当用decltype(e)来获取类型时&#xff0c;编译器将依序判断以下四规则&#xff1a; 1.如果e是一个没有带括号的标记符表达式(id-expression)或者类成员访问表达式&#xff0c;那么decltype(e)就是e所命名的实体的类型。此外&#xff0c;如果e是一个被重载的函…

k8s 之安装metrics-server

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/01/29 18:25 metrics-server可帮助我们查看pod的cpu和内存占用情况 kubectl top po nginx-deploy-56696fbb5-mzsgg # 报错&#xff0c;需要Metrics API 下载 Metrics 解决 wget https://github.com/kubernetes-sigs/metri…

基于auth2的单点登录原理理解

创作背景&#xff1a;基于auth2实现企业门户与业务系统的单点登录跳转。 架构组成&#xff1a;4A统一认证中心&#xff0c;门户系统&#xff0c;业务系统&#xff0c;用户&#xff1b; 实现目标&#xff1a;用户登录门户系统后&#xff0c;可通过点击业务系统菜单&#xff0c…

字符串数学专题

粗心的小可 题目描述 小可非常粗心&#xff0c;打字的时候将手放到了比正确位置偏右的一个位置&#xff0c;因此&#xff0c;Q打成了W&#xff0c;E打成了R&#xff0c;H打成了J等等。键盘如下所示 现在给你若干行小可打字的结果&#xff0c;请你还原成正确的文本。 输入描述…

嵌入式面试八股文(五)·一文带你详细了解程序内存分区中的堆与栈的区别

目录 1. 栈的工作原理 1.1 内存分配 1.2 地址生长方向 1.3 生命周期 2. 堆的工作原理 2.1 动态内存分配 2.1.1 malloc函数 2.1.2 calloc函数 2.1.3 realloc函数 2.1.4 free函数 2.2 生命周期管理 2.3 地址生长方向 3. 堆与栈区别 3.1 管理方式不同…

海南聚广众达电子商务咨询有限公司助力商家业绩飙升

在这个短视频与直播风靡的时代&#xff0c;抖音电商无疑成为了众多商家竞相追逐的新风口。作为电商服务领域的佼佼者&#xff0c;海南聚广众达电子商务咨询有限公司凭借其专业的团队、创新的策略与丰富的实战经验&#xff0c;正引领着一批又一批商家在抖音平台上破浪前行&#…

顺序表及其代码实现

目录 前言1.顺序表1.1 顺序表介绍1.2 顺序表基本操作代码实现 总结 前言 顺序表一般不会用来单独存储数据&#xff0c;但自身的优势&#xff0c;很多时候不得不使用顺序表。 1.顺序表 1.1 顺序表介绍 顺序表是物理结构连续的线性表&#xff0c;支持随机存取&#xff08;底层…

Leetcode—139. 单词拆分【中等】

2024每日刷题&#xff08;173&#xff09; Leetcode—139. 单词拆分 dp实现代码 class Solution { public:bool wordBreak(string s, vector<string>& wordDict) {int n s.size();unordered_set<string> ust(wordDict.begin(), wordDict.end());vector<b…

探索基于基于人工智能进行的漏洞评估的前景

根据2023年的一份报告 网络安全企业据估计&#xff0c;到 10.5 年&#xff0c;网络犯罪每年将给世界造成 2025 万亿美元的损失。每年记录在案的网络犯罪数量都会创下新高。这要求对传统的安全测试流程进行重大改变。这就是漏洞评估发挥作用的地方。 漏洞评估对于识别系统中的弱…

双指针_有效三角形个数三数之和四数之和

有效三角形个数 思路&#xff1a; 我们可以通过暴力枚举&#xff0c;三重for循环来算但&#xff0c;时间复杂度过高。 有没有效率更高的算法呢&#xff1f; 我们知道如果两条较短的边小于最长的一条边&#xff0c;那么就可以构成三角形。 如果这个数组是升序的&#xff0c;两…

负压DC-DC开关电源设计

负压DC-DC开关电源设计 与常见的正压输出BUCK电路对比&#xff0c;区别就在于将 原芯片接GND的网络接到了负压输出。 电感一接sW引脚&#xff0c;另外一接到了OV-GND。 注意几点如下: 芯片耐压选择 EN引脚耐压 输入滤波电容的选择 拓扑结构 BOOST模式&#xff1a;当NMO…

NXP i.MX8系列平台开发讲解 - 4.2.3 摄像头篇(三) - 摄像头MIPI 接口

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 文章目录 关注星号公众号&#xff0c;不容错过精彩 作者&#xff1a;HywelStar 1. 概述 MIPI是Mobile Industry Pr…

论文阅读:InternVL v1.5| How Far Are We to GPT-4V? 通过开源模型缩小与商业多模式模型的差距

论文地址&#xff1a;https://arxiv.org/abs/2404.16821 Demo&#xff1a; https://internvl.opengvlab.com Model&#xff1a;https://huggingface.co/OpenGVLab/InternVL-Chat-V1-5 公开时间&#xff1a;2024年4月29日 InternVL1.5&#xff0c;是一个开源的多模态大型语言模…

【无人机设计与控制】基于matlab的无人机FMCW(频率调制连续波)毫米波高度计雷达仿真

摘要 本文介绍了一种基于FMCW&#xff08;频率调制连续波&#xff09;雷达技术的无人机毫米波高度计的仿真。FMCW雷达通过测量发射信号与回波信号之间的频差来确定目标的距离和速度。在本项目中&#xff0c;我们使用MATLAB仿真无人机毫米波雷达的性能&#xff0c;展示其在不同…

TS1 order set分析

如下图&#xff0c;所示为TS1 order序列。该序列有16个symbol组成。 常见的symbol有&#xff0c;PAD和COM等。PAD是K symbol&#xff0c;还有D symbol。下文先给出COM symbol的解读。读协议文档可知COM常被称为K28.5。K是symbol的类型&#xff0c;注意symbol是编码过的数据。K…

六、Java 基础语法(下)

一、变量 1、变量的定义与使用 变量就是内存中的存储空间&#xff0c;空间中存储着经常发生改变的数据变量定义格式&#xff1a; 数据类型 变量名 数据值使用时根据变量名使用举例如下&#xff0c;上面是代码&#xff0c;下面是输出 2、变量的注意事项 变量名不允许重复…

Dyna-slam复现(保姆级详细图文版,百分百成功)

因最近论文要和这些算法做对比,故配置了一下,在此记录 因为是老的算法,cuda版本现在的显卡都不能使用,所以笔者找的电脑是华硕飞行堡垒17年的电脑,1080的显卡 深度学习及maskrcnn配置 先将dyna-slam git下来,终端执行 git clone https://github.com/BertaBescos/Dyna…

Arduino UNO R3自学笔记21 之 Arduino电机的闭环控制

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;上篇写了电机速度测定&#xff0c;这篇主要是讲测定出的速度用于反馈&#xff0c;使得实际速度快速响应到需要的速度。 1.控制系统介绍 分2大类&#x…

ECML PKDD 2024 | 时空数据(Spatial-Temporal)和时间序列(Time series)论文总结

ECML PKDD 2024于9月9号-9月13号在立陶宛维尔纽斯举行&#xff08;Vilnius&#xff09; 本文总结了ECML PKDD 2024有关时空数据&#xff08;spatial-temporal data&#xff09;的相关论文&#xff0c;主要包含交通预测&#xff0c;预训练&#xff0c;迁移学习等内容&#xff0…

latex本地运行(MiKTeX+VScode)-20241006

1、安装 LaTex 主流的分发版本应该就是 TeXLive 和 MikTeX 了,这里使用 MikTex(只有几百M)—— TeXLive 太大了、默认安装全部包,可选自选部分安装单实在有些许麻烦,MikTeX 则方便得多,需要的时候可以自动安装全部包 点击跳转到 MiKTeX 官网,直接下载即可:不用担心什…