类初始化器

灵感

灵感来自

Jos在Java论坛中发贴 ,我整理了一篇有关类初始化器的文章。

实际上,大多数Java程序员并未充分利用初始化程序。 一旦Java程序员了解了构造函数,他/她就会感觉到他们找到了放置必须在创建对象之前运行的代码的好地方,并且有时不要尝试寻找放置对象创建前代码的更多地方。 放置对象创建前代码的其他地方是初始化程序。

在类中可以使用两种类型的初始化器。 静态初始值设定项和实例初始值设定项。

静态初始值设定项。

静态初始化程序是在加载类时运行一次的代码的第一位。 由于它们是静态的,因此它们不绑定到任何特定实例,而是在其类的所有对象之间共享。 因此,实例初始化程序块仅执行一次(在类加载期间)。 之后,在程序中创建对象不会再次触发静态初始化程序块的执行。 静态的另一个结果是,您只能使用静态初始化程序来初始化静态变量。 这是有道理的,因为这些是类加载期间仅有的可用选项。 chat不休,让我们看一个例子:

一个场景

该场景是一个Utility类,它提供有关数据库中产品的信息。 该类在数据库中所有产品的列表上工作,方法是在加载类时将它们加载到arraylist中一次,然后在随后的所有产品查询中引用该arraylist。 例如,exists方法不需要连接到数据库,而只需查询产品ArrayList。

免责声明

请注意,此示例仅用于说明目的。 该方法仅在只读数据库系统中有用,因为如果在加载ProductUtils类之后在程序执行期间将产品添加到数据库中,则该产品将不会在类加载时创建的产品列表中可用。 当然有解决方法,但我们离题了...

静态初始化程序块仅由static关键字开头的一对开合括号表示,初始化代码位于括号内

 static {
//initialization code goes here.
它在起作用

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
class ProductUtils {
    static List<Product> products = new ArrayList();
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost/test","r035198x ", "acomplexpassword");
            Statement st = con.createStatement();
            ResultSet rs = st.executeQuery("select * from Products");
            while(rs.next()) {
                products.add(new Product(rs.getInt(1), rs.getString(2)));
                System.out.println("product added");
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        } 
    } 
    public static void main(String[] args) { 
    } 
    boolean exists (Product product) {
        return products.contains(product);
    } 
}

主输出为空白

使用适当的配置(正确创建数据库并在类路径中正确设置连接器)运行此程序,将打印为每个产品添加到产品ArrayList的产品。 请注意,我故意将main方法保留为空白,但是程序连接到数据库,将对象加载到arraylist并将一些内容打印到控制台。 静态初始化程序块负责所有这一切。 主要方法是无辜的。

这应该清楚表明,执行静态初始化程序块不需要类的实例。 静态初始化器可以像您希望的那样复杂,只需记住它们是在类加载期间执行的,因此只能访问其他静态器。

多多益善?

您可以在类中拥有任意数量的静态初始化器。 它们将按照它们在您的代码中出现的顺序执行(从文件顶部到底部)。 为了使代码易于阅读,应将不同的任务放在不同的初始化器块中,以便每个块执行一个逻辑操作。

采用

使用静态初始化程序执行必须在创建类的所有对象之前执行一次的操作。 在静态初始化程序中执行这些操作可确保一次执行,因此可以加快程序的速度,因为不必在每次创建该类的对象时都再次执行这些操作。 更具体地说,对JVM加载该类的过程中必须执行的操作使用静态初始化程序。 上面的示例还包含静态初始化器的常见用法。

常用

驱动程序接口的规格说

加载Driver类时,它应该创建其自身的实例并在DriverManager中注册。 这意味着用户可以通过调用来加载和注册驱动程序
Class.forName(“ foo.bah.Driver”)
数据库驱动程序编写者在此处被告知,在加载其驱动程序时,必须创建一个对象并在驱动程序管理器中注册。

上例中用于连接数据库的MySQL驱动程序必须遵守此规则。 对DriverManager.registerDriver()的调用是在MySQL驱动程序类的静态初始化程序中完成的,该初始化程序类似于:

  static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (java.sql.SQLException E) {
            throw new RuntimeException("Can't register driver!");
        } 
        if (DEBUG) {
            Debug.trace("ALL");
        }
    }
马克·马修斯(Mark Matthews)撰写

这很有用,因为加载驱动程序现在仅转换为加载该驱动程序的驱动程序类。

实例初始化器

静态初始值设定项和实例初始值设定项的区别在于执行它们的时间。 加载类时,静态初始化程序执行一次,而实例初始化程序在创建对象之前运行一次。 这种变化很重要。 实例初始化程序现在可以访问类中的非静态变量,包括

这个对象。

命名类中的实例初始化器应用于初始化实例变量,这些实例变量不能使用一行代码来初始化,而对于该类中的所有构造函数都应以相同的方式初始化。 否则,如果应根据调用的构造函数对变量进行不同的初始化,则应在适当的构造函数中对其进行初始化。

 class Cat {
    String name;
    int numOfLegs;
    int numOfEars; 
    {
        numOfLegs = 2;
        numOfEars = 2;
    }
    Cat(String name) {
        this.name = name;
    }
    public String toString() {
        return name + " has " + numOfLegs + " legs and " + numOfEars + " ears"; 
    } 
    public static void main(String[] args) {
        System.out.println(new Cat("Mary").toString()); 
    }
}
在这里,每只猫都用两只腿和两只耳朵初始化。 在构造函数之前调用

实例初始化程序总是在调用构造函数之前被调用(请注意,编译器实际上为类中的每个构造函数生成<init>方法,而这些是在调用构造函数时调用的方法)。 与静态初始化程序一样,多个初始化程序以其文本顺序执行。

匿名类中的实例初始化器

与匿名类一起使用时,初始化程序可使繁琐的代码更加优雅。 通常,您想隔离执行不同操作的代码。 特别是,您不想将构建结构的代码(例如列表,地图或更多奇特的结构)与处理该结构的代码混合使用。 假设您要在整数和字符串之间建立一个映射,以便1映射到“ Sunday”,2映射到“ Monday”,依此类推。

您可以将地图构建为

 Map<Integer, String> days = new HashMap();
days.put(1, “Sunday”);
days.put(2, “Monday”);
//etc
之后,您就可以开始操作地图了。

如果使用匿名类和初始化程序,则可以通过以下方式完成

 Map<Integer, String> days = new HashMap() {
            {
                         put(1, “Sunday”);
                 put(2, “Monday”);
                         //etc
               }
}; 
结束} 上面的代码中的明显标记了地图创建的结束。 查看代码的任何人都可以轻松地在检查创建结构的代码和操作该结构的代码之间切换。 根据初始化的复杂程度,这可以节省几秒钟的调试时间。 请记住,HashMap只是结构的示例,可能需要几行初始化。 其他用户定义的结构也可以从同一方法中受益,甚至更多,这取决于完全初始化结构必须执行的操作。 初始化程序和异常

静态初始化器无法引发检查的异常(如果这样做,则是编译错误)。

如果在该类的每个构造函数的throws子句中显式声明了该异常或其父类,并且该类具有至少一个显式定义的构造函数,则该命名类中的实例变量初始化器只能引发该检查的异常。 8.3.2])。

 static {
    if(true) {
        throw new Exception();
    }
}
无法编译。

 public class Sorry {
    String sorry;
    public Sorry() throws Exception{}
        {
        if(true) {
                          throw new Exception();
                    }
    }
}
编译良好。

但是,允许匿名类中的实例变量初始化器引发任何检查的异常。

 import java.io.IOException;
public class Sorry {
    String sorry;
    public Sorry() {}
    public static void main(String[] args) throws IOException{
        System.out.print( new Sorry() {
            {
       if(true) {
                              throw new IOException("Sorry");
                      }
            }
        }.toString()); 
    }
    public String toString() {
        return sorry;
    } 
}
编译良好。

如果您想知道班级的名称,我的一个朋友写了一个对不起的班级,通过电子邮件向女友道歉了5 000次,我只是偷了那个班级,并做了些改动以适合更严肃的目的。

我希望您现在对初始化程序有所了解,并知道何时使用哪一个来提高程序的速度和可维护性。

From: https://bytes.com/topic/java/insights/744109-class-initializers

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值