java如何初始化类对象_Java中的类和对象初始化

java如何初始化类对象

Java中的类和对象必须在使用前进行初始化。 您之前已经了解到 ,在加载类时,将类字段初始化为默认值,并且通过构造函数初始化了对象,但是还有更多要初始化的内容。 本文介绍了Java的所有用于初始化类和对象的功能。

下载
下载本教程中示例应用程序的源代码。 由Jeff Friesen为JavaWorld创建。

如何初始化Java类

在探讨Java对类初始化的支持之前,让我们回顾一下初始化Java类的步骤。 考虑清单1。

清单1.将类字段初始化为默认值
class SomeClass
{
   static boolean b;
   static byte by;
   static char c;
   static double d;
   static float f;
   static int i;
   static long l;
   static short s;
   static String st;
}

清单1声明类SomeClass 。 此类声明九个类型为booleanbytechardoublefloatintlongshortString字段。 加载SomeClass ,每个字段的位都设置为零,您可以按如下解释:

false
0
\u0000
0.0
0.0
0
0
0
null

先前的类字段隐式初始化为零。 但是,您还可以通过直接为它们分配值来显式初始化类字段,如清单2所示。

清单2.将类字段初始化为显式值
class SomeClass
{
   static boolean b = true;
   static byte by = 1;
   static char c = 'A';
   static double d = 2.0;
   static float f = 3.0f;
   static int i = 4;
   static long l = 5000000000L;
   static short s = 20000;
   static String st = "abc";
}

每个分配的值必须与类字段的类型兼容。 每个变量直接存储值, st除外。 变量st存储对包含abcString对象的引用。

引用类字段

初始化类字段时,将其初始化为先前初始化的类字段的值是合法的。 例如,清单3将y初始化为x的值。 两个字段都初始化为2

清单3.引用先前声明的字段
class SomeClass
{
   static int x = 2;
   static int y = x;

   public static void main(String[] args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}

但是,相反的做法是不合法的:您无法将类字段初始化为随后声明的类字段的值。 Java编译器在遇到这种情况时会输出illegal forward reference 。 考虑清单4。

清单4.尝试引用随后声明的字段
class SomeClass
{
   static int x = y;
   static int y = 2;

   public static void main(String[] args)
   {
      System.out.println(x);
      System.out.println(y);
   }
}

当编译器遇到static int x = y;时,它将报告illegal forward reference static int x = y; 。 这是因为源代码是从上到下编译的,并且编译器尚未看到y 。 (如果未明确初始化y ,它也会输出此消息。)

类初始化块

在某些情况下,您可能需要执行复杂的基于类的初始化。 您将在加载类之后,从该类创建任何对象之前(假定该类不是实用程序类)执行此操作。 您可以将类初始化块用于此任务。

类初始化块是语句块,其前面是引入到类主体中的static关键字。 当类加载时,将执行这些语句。 考虑清单5。

清单5.初始化正弦和余弦值的数组
class Graphics
{
   static double[] sines, cosines;
   static
   {
      sines = new double[360];
      cosines = new double[360];
      for (int i = 0; i < sines.length; i++)
      {
         sines[i] = Math.sin(Math.toRadians(i));
         cosines[i] = Math.cos(Math.toRadians(i));
      }
   }
}

清单5声明了一个Graphics类,该类声明了sinescosines数组变量。 它还声明了一个类初始化块,该块创建了360个元素的数组,这些数组的引用分配给sinescosines 。 然后,通过调用Math类的sin()cos()方法,使用for语句将这些数组元素初始化为适当的正弦和余弦值。 ( Math是Java标准类库的一部分。我将在以后的文章中讨论此类和这些方法。)

性能技巧

因为性能对图形应用程序很重要,并且访问数组元素比调用方法要快,所以开发人员诉诸性能技巧,例如创建和初始化正弦和余弦数组。

组合类字段初始化器和类初始化块

您可以在应用程序中组合多个类字段初始化器和类初始化块。 清单6提供了一个示例。

清单6.以自上而下的顺序执行类初始化
class MCFICIB
{
   static int x = 10;

   static double temp = 98.6;

   static
   {
      System.out.println("x = " + x);
      temp = (temp - 32) * 5.0/9.0; // convert to Celsius
      System.out.println("temp = " + temp);
   }

   static int y = x + 5;

   static
   {
      System.out.println("y = " + y);
   }

   public static void main(String[] args)
   {
   }
}

清单6声明并初始化一对类字段( xy ),并声明一对static初始化器。 如下所示编译此清单:

javac MCFICIB.java

然后运行生成的应用程序:

java MCFICIB

您应该观察以下输出:

x = 10
temp = 37.0
y = 15

此输出显示以自顶向下的顺序执行类初始化。

<clinit>()方法

编译类初始化程序和类初始化块时,Java编译器将编译后的字节码(按自上而下的顺序)存储在名为<clinit>()的特殊方法中。 尖括号可防止名称冲突 :您不能在源代码中声明<clinit>()方法,因为<>字符在标识符上下文中是非法的。

加载类后,JVM会在调用main()之前main()存在main()情况下)先调用此方法。

让我们看一下MCFICIB.class 。 以下部分反汇编显示了xtempy字段的存储信息:

Field #1

00000290        Access Flags                          ACC_STATIC
00000292        Name                                  x
00000294        Descriptor                            I
00000296        Attributes Count                      0

Field #2

00000298        Access Flags                          ACC_STATIC
0000029a        Name                                  temp
0000029c        Descriptor                            D
0000029e        Attributes Count                      0

Field #3

000002a0        Access Flags                          ACC_STATIC
000002a2        Name                                  y
000002a4        Descriptor                            I
000002a6        Attributes Count                      0

Descriptor行标识该字段的JVM 类型描述符 。 该类型用单个字母表示: I表示intD表示double

以下部分反汇编揭示了<clinit>()方法的字节码指令序列。 每行以一个十进制数字开头,该数字标识后续指令的从零开始的偏移地址:

  0        bipush 10
  2        putstatic MCFICIB/x I
  5        ldc2_w #98.6
  8        putstatic MCFICIB/temp D
 11        getstatic java/lang/System/out Ljava/io/PrintStream;
 14        new java/lang/StringBuilder
 17        dup
 18        invokespecial java/lang/StringBuilder/<init>()V
 21        ldc "x = "
 23        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 26        getstatic MCFICIB/x I
 29        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
 32        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 35        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 38        getstatic MCFICIB/temp D
 41        ldc2_w #32
 44        dsub
 45        ldc2_w #5
 48        dmul
 49        ldc2_w #9
 52        ddiv
 53        putstatic MCFICIB/temp D
 56        getstatic java/lang/System/out Ljava/io/PrintStream;
 59        new java/lang/StringBuilder
 62        dup
 63        invokespecial java/lang/StringBuilder/<init>()V
 66        ldc "temp = "
 68        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 71        getstatic MCFICIB/temp D
 74        invokevirtual java/lang/StringBuilder/append(D)Ljava/lang/StringBuilder;
 77        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
 80        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
 83        getstatic MCFICIB/x I
 86        iconst_5
 87        iadd
 88        putstatic MCFICIB/y I
 91        getstatic java/lang/System/out Ljava/io/PrintStream;
 94        new java/lang/StringBuilder
 97        dup
 98        invokespecial java/lang/StringBuilder/<init>()V
101        ldc "y = "
103        invokevirtual java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
106        getstatic MCFICIB/y I
109        invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
112        invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
115        invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
118        return

从偏移量0到偏移量2的指令序列等效于以下类字段初始化器:

static int x = 10;

从偏移量5到偏移量8的指令序列等效于以下类字段初始化器:

static double temp = 98.6;

从偏移量11到偏移量80的指令序列等效于以下类初始化块:

static
{
   System.out.println("x = " + x);
   temp = (temp - 32) * 5.0/9.0; // convert to Celsius
   System.out.println("temp = " + temp);
}

从偏移量83到偏移量88的指令序列等效于以下类字段初始化程序:

static int y = x + 5;

从偏移量91到偏移量115的指令序列等效于以下类初始化块:

static
{
   System.out.println("y = " + y);
}

最后,偏移量为118的return指令将执行从<clinit>()返回到JVM的调用此方法的部分。

不用担心字节码的含义

此练习的收获是看到清单6的类字段初始化器和类初始化块中的所有代码都位于<clinit>()方法中,并以自上而下的顺序执行。

如何初始化对象

加载并初始化一个类后,您通常会希望从该类创建对象。 正如您在我最近对使用类和对象进行编程的介绍中所了解的那样,您可以通过放置在类的构造函数中的代码来初始化对象。 考虑清单7。

清单7.使用构造函数初始化一个对象
class City
{
   private String name;
   int population;

   City(String name, int population)
   {
      this.name = name;
      this.population = population;
   }

   @Override
   public String toString()
   {
      return name + ": " + population;
   }

   public static void main(String[] args)
   {
      City newYork = new City("New York", 8491079);
      System.out.println(newYork); // Output: New York: 8491079
   }
}

清单7声明了一个带有namepopulation字段的City类。 创建City对象时,将调用City(String name, int population)构造函数,以将这些字段初始化为被调用构造函数的参数。 (我还重写了Objectpublic String toString()方法,以方便地以字符串形式返回城市名称和人口值System.out.println()最终调用此方法以返回对象的字符串表示形式,并由它输出。 )

在调用构造函数之前, namepopulation包含哪些值? 您可以通过插入System.out.println(this.name); System.out.println(this.population);来查找System.out.println(this.name); System.out.println(this.population); System.out.println(this.name); System.out.println(this.population); 在构造函数的开头。 在编译了源代码( javac City.java )并运行了应用程序( java City )之后,您将观察到null表示name0表示populationnew运算符在执行构造函数之前将对象的object(实例)字段清零。

与类字段一样,您可以显式初始化对象字段。 例如,您可以指定String name = "New York";int population = 8491079; 。 但是,这样做通常无济于事,因为这些字段将在构造函数中初始化。 我唯一想到的好处是为对象字段分配了默认值。 当您调用未初始化字段的构造函数时,将使用此值:

int numDoors = 4; // default value assigned to numDoors

Car(String make, String model, int year)
{
   this(make, model, year, numDoors);
}

Car(String make, String model, int year, int numDoors)
{
   this.make = make;
   this.model = model;
   this.year = year;
   this.numDoors = numDoors;
}

对象初始化反映了类的初始化

翻译自: https://www.infoworld.com/article/3040564/java-101-class-and-object-initialization-in-java.html

java如何初始化类对象

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值