92、Java 中的正则表达式、反射、RMI 及日期时间格式化

Java 中的正则表达式、反射、RMI 及日期时间格式化

1. 正则表达式

正则表达式在文本解析、操作和标记化中非常重要。在 Java 里,有两种模式匹配的选择:
- Pattern.matches() 方法 :若只进行一次性模式匹配,可使用 Pattern 类的 matches() 方法,其定义如下:

static boolean matches(String pattern, CharSequence str)

该方法会自动编译模式并查找匹配项,若模式匹配字符串则返回 true ,否则返回 false 。不过,若要重复使用同一模式,此方法效率不如先编译模式再使用 Matcher 的模式匹配方法。
- String.matches() 方法 String 类也实现了 matches() 方法,定义如下:

boolean matches(String pattern)

若调用该方法的字符串与模式中的正则表达式匹配,则返回 true ,否则返回 false

建议大家探索正则表达式的功能,尝试不同类型的模式和输入序列,掌握其模式匹配的工作原理后,会发现它在编程中很有用。

2. 反射

反射是软件自我分析的能力,由 java.lang.reflect 包和 Class 中的元素提供。它在使用 Java Beans 等组件时尤为重要,能在运行时动态分析软件组件并描述其功能,而非在编译时。例如,可通过反射确定类支持的方法、构造函数和字段。

java.lang.reflect 包包含多个接口和十个类,如下表所示:
| 类名 | 主要功能 |
| ---- | ---- |
| AccessibleObject | 允许绕过默认的访问控制检查 |
| Array | 允许动态创建和操作数组 |
| Constructor | 提供构造函数的信息 |
| Executable | 由 Method Constructor 扩展的抽象超类(JDK 8 添加) |
| Field | 提供字段的信息 |
| Method | 提供方法的信息 |
| Modifier | 提供类和成员访问修饰符的信息 |
| Parameter | 提供参数的信息(JDK 8 添加) |
| Proxy | 支持动态代理类 |
| ReflectPermission | 允许反射类的私有或受保护成员 |

以下是一个简单的反射应用示例,用于打印 java.awt.Dimension 类的构造函数、字段和方法:

import java.lang.reflect.*;
public class ReflectionDemo1 {
  public static void main(String args[]) {
    try {
      Class<?> c = Class.forName("java.awt.Dimension");
      System.out.println("Constructors:");
      Constructor<?> constructors[] = c.getConstructors();
      for(int i = 0; i < constructors.length; i++) {
        System.out.println(" " + constructors[i]);
      }

      System.out.println("Fields:");
      Field fields[] = c.getFields();
      for(int i = 0; i < fields.length; i++) {
        System.out.println(" " + fields[i]);
      }

      System.out.println("Methods:");
      Method methods[] = c.getMethods();
      for(int i = 0; i < methods.length; i++) {
        System.out.println(" " + methods[i]);
      }
    }
    catch(Exception e) {
      System.out.println("Exception: " + e);
    }
  }
}

输出示例:

Constructors:
 public java.awt.Dimension(int,int)
 public java.awt.Dimension()
 public java.awt.Dimension(java.awt.Dimension)
Fields:
 public int java.awt.Dimension.width
 public int java.awt.Dimension.height
Methods:
 public int java.awt.Dimension.hashCode()
 public boolean java.awt.Dimension.equals(java.lang.Object)
 public java.lang.String java.awt.Dimension.toString()
 public java.awt.Dimension java.awt.Dimension.getSize()
 public void java.awt.Dimension.setSize(double,double)
 public void java.awt.Dimension.setSize(java.awt.Dimension)
 public void java.awt.Dimension.setSize(int,int)
 public double java.awt.Dimension.getHeight()
 public double java.awt.Dimension.getWidth()
 public java.lang.Object java.awt.geom.Dimension2D.clone()
 public void java.awt.geom.Dimension2D.setSize(java.awt.geom.Dimension2D)
 public final native java.lang.Class java.lang.Object.getClass()
 public final native void java.lang.Object.wait(long)
   throws java.lang.InterruptedException
 public final void java.lang.Object.wait()
   throws java.lang.InterruptedException
 public final void java.lang.Object.wait(long,int)
   throws java.lang.InterruptedException
 public final native void java.lang.Object.notify()
 public final native void java.lang.Object.notifyAll()

另一个示例展示了如何获取类的公共方法:

import java.lang.reflect.*;
class A {
  public void a1() {
  }
  public void a2() {
  }
  protected void a3() {
  }
  private void a4() {
  }
}
public class ReflectionDemo2 {
  public static void main(String args[]) {
    try {
      A a = new A();
      Class<?> c = a.getClass();
      System.out.println("Public Methods:");
      Method methods[] = c.getDeclaredMethods();
      for(int i = 0; i < methods.length; i++) {
        int modifiers = methods[i].getModifiers();
        if(Modifier.isPublic(modifiers)) {
          System.out.println(" " + methods[i].getName());
        }
      }
    }
    catch(Exception e) {
      System.out.println("Exception: " + e);
    }
  }
}

输出示例:

Public Methods:
 a1
 a2

Modifier 类还提供了一组静态方法,用于返回可应用于特定程序元素类型的修饰符类型,例如:

static int classModifiers()
static int constructorModifiers()
static int fieldModifiers()
static int interfaceModifiers()
static int methodModifiers()
static int parameterModifiers() // JDK 8 添加
3. 远程方法调用(RMI)

RMI 允许一台机器上执行的 Java 对象调用另一台机器上 Java 对象的方法,这对构建分布式应用很重要。下面介绍如何使用 RMI 构建一个简单的客户端/服务器应用:
- 步骤一:输入并编译源代码
- AddServerIntf.java :定义服务器提供的远程接口,包含一个接受两个 double 参数并返回它们之和的方法。

import java.rmi.*;
public interface AddServerIntf extends Remote {
  double add(double d1, double d2) throws RemoteException;
}
  • AddServerImpl.java :实现远程接口, add() 方法的实现很简单。
import java.rmi.*;
import java.rmi.server.*;
public class AddServerImpl extends UnicastRemoteObject implements AddServerIntf {
  public AddServerImpl() throws RemoteException {
  }
  public double add(double d1, double d2) throws RemoteException {
    return d1 + d2;
  }
}
  • AddServer.java :服务器端的主程序,使用 Naming 类的 rebind() 方法更新 RMI 注册表。
import java.net.*;
import java.rmi.*;
public class AddServer {
  public static void main(String args[]) {
    try {
      AddServerImpl addServerImpl = new AddServerImpl();
      Naming.rebind("AddServer", addServerImpl);
    }
    catch(Exception e) {
      System.out.println("Exception: " + e);
    }
  }
}
  • AddClient.java :实现分布式应用的客户端,需要三个命令行参数:服务器机器的 IP 地址或名称以及两个要相加的数字。
import java.rmi.*;
public class AddClient {
  public static void main(String args[]) {
    try {
      String addServerURL = "rmi://" + args[0] + "/AddServer";
      AddServerIntf addServerIntf = (AddServerIntf)Naming.lookup(addServerURL);
      System.out.println("The first number is: " + args[1]);
      double d1 = Double.valueOf(args[1]).doubleValue();
      System.out.println("The second number is: " + args[2]);
      double d2 = Double.valueOf(args[2]).doubleValue();
      System.out.println("The sum is: " + addServerIntf.add(d1, d2));
    }
    catch(Exception e) {
      System.out.println("Exception: " + e);
    }
  }
}

输入所有代码后,使用 javac 编译这四个源文件。

  • 步骤二:必要时手动生成存根
    在 Java 5 之前,需要使用 rmic 手动构建存根。若处于旧环境,可使用以下命令生成存根:
rmic AddServerImpl

此命令会生成 AddServerImpl_Stub.class 文件,使用 rmic 时,要确保 CLASSPATH 包含当前目录。

  • 步骤三:在客户端和服务器机器上安装文件
  • 客户端机器:复制 AddClient.class AddServerImpl_Stub.class (若需要)和 AddServerIntf.class 到一个目录。
  • 服务器机器:复制 AddServerIntf.class AddServerImpl.class AddServerImpl_Stub.class (若需要)和 AddServer.class 到一个目录。

  • 步骤四:在服务器机器上启动 RMI 注册表
    JDK 提供了 rmiregistry 程序,先检查 CLASSPATH 环境变量是否包含文件所在目录,然后从命令行启动 RMI 注册表:

start rmiregistry

启动后会创建一个新窗口,需保持该窗口打开,直到完成 RMI 示例的实验。

  • 步骤五:启动服务器
    从命令行启动服务器代码:
java AddServer

该代码会实例化 AddServerImpl 并将该对象以 “AddServer” 名称注册。

  • 步骤六:启动客户端
    AddClient 软件需要三个参数,可从命令行以以下两种格式调用:
java AddClient server1 8 9
java AddClient 11.12.13.14 8 9

也可在同一台机器上测试,使用回环地址:

java AddClient 127.0.0.1 8 9

示例输出如下:

The first number is: 8
The second number is: 9
The sum is: 17.0

实际使用 RMI 时,服务器可能需要安装安全管理器。

4. 使用 java.text 格式化日期和时间

java.text 包可用于格式化、解析、搜索和操作文本,这里介绍其常用的用于格式化日期和时间信息的类。

4.1 DateFormat 类

DateFormat 是一个抽象类,可用于格式化和解析日期和时间。 getDateInstance() 方法可返回能格式化日期信息的 DateFormat 实例,有以下几种形式:

static final DateFormat getDateInstance()
static final DateFormat getDateInstance(int style)
static final DateFormat getDateInstance(int style, Locale locale)

style 参数可以是 DEFAULT SHORT MEDIUM LONG FULL ,这些是 DateFormat 定义的 int 常量,会使日期呈现不同的细节。 locale 参数是 Locale 定义的静态引用之一。若未指定 style 和/或 locale ,则使用默认值。

format() 方法是该类常用的方法之一,其中一种重载形式如下:

final String format(Date d)

该方法接受一个 Date 对象并返回包含格式化信息的字符串。

以下示例展示了如何格式化日期信息:

import java.text.*;
import java.util.*;
public class DateFormatDemo {
  public static void main(String args[]) {
    Date date = new Date();
    DateFormat df;
    df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.JAPAN);
    System.out.println("Japan: " + df.format(date));
    df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.KOREA);
    System.out.println("Korea: " + df.format(date));
    df = DateFormat.getDateInstance(DateFormat.LONG, Locale.UK);
    System.out.println("United Kingdom: " + df.format(date));
    df = DateFormat.getDateInstance(DateFormat.FULL, Locale.US);
    System.out.println("United States: " + df.format(date));
  }
}

示例输出:

Japan: 14/01/01
Korea: 2014. 1. 1
United Kingdom: 01 January 2014
United States: Wednesday, January 1, 2014

getTimeInstance() 方法可返回能格式化时间信息的 DateFormat 实例,也有几种不同的形式,使用方式与 getDateInstance() 类似。以下示例展示了如何格式化时间信息:

import java.text.*;
import java.util.*;

后续可根据 getTimeInstance() 方法的不同形式编写代码来输出不同风格和地区的时间信息。

以上就是 Java 中关于正则表达式、反射、RMI 以及日期时间格式化的相关内容,希望对大家有所帮助。大家可以按照上述步骤进行实践,深入理解这些知识在 Java 编程中的应用。

Java 中的正则表达式、反射、RMI 及日期时间格式化(续)

5. 正则表达式、反射、RMI 和日期时间格式化的总结与应用场景分析
5.1 正则表达式

正则表达式是一种强大的文本处理工具,它的应用场景广泛。在数据验证方面,比如验证用户输入的邮箱地址、手机号码等是否符合格式要求,就可以使用正则表达式。在数据提取方面,当需要从一大段文本中提取特定格式的信息时,正则表达式能发挥重要作用。例如,从网页源代码中提取所有的链接地址。

从性能角度来看,若只进行一次性的模式匹配,使用 Pattern.matches() 方法较为方便,但如果需要多次使用同一模式,先编译模式再使用 Matcher 的模式匹配方法会更高效。这是因为 Pattern.matches() 每次都会自动编译模式,而编译过程会消耗一定的性能。

5.2 反射

反射机制在框架开发中有着重要的应用。例如,在 Spring 框架中,反射被用于实现依赖注入和 AOP(面向切面编程)。通过反射,框架可以在运行时动态地创建对象、调用方法和访问字段。在插件开发中,反射也能让程序在运行时加载和使用外部插件。

不过,反射也有一些缺点。由于反射是在运行时进行操作,会带来一定的性能开销。而且,使用反射可能会破坏类的封装性,因为它可以绕过访问控制检查来访问类的私有成员。

5.3 远程方法调用(RMI)

RMI 主要用于构建分布式应用程序,它使得不同机器上的 Java 对象可以相互调用方法,就像在本地调用一样。在大型企业级应用中,不同的业务模块可能部署在不同的服务器上,通过 RMI 可以实现模块之间的通信和协作。

在实际使用 RMI 时,需要注意网络问题和安全问题。网络不稳定可能会导致方法调用失败,而安全问题则需要通过安装安全管理器等方式来解决。

5.4 日期时间格式化

在开发 Web 应用时,日期时间格式化非常重要。不同地区的用户对日期时间的显示格式有不同的需求,使用 DateFormat 类可以根据用户的地区和需求来格式化日期和时间。例如,在一个国际化的电商网站中,需要根据用户所在地区显示不同格式的订单日期和时间。

6. 综合示例:结合正则表达式、反射和日期时间格式化

以下是一个综合示例,展示了如何结合正则表达式、反射和日期时间格式化来实现一个简单的功能。假设我们有一个类 MyClass ,其中包含一些方法,我们要使用反射获取这些方法,并使用正则表达式验证方法名是否符合特定格式,最后将当前日期时间按照特定格式输出。

import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;

class MyClass {
    public void method1() {
        System.out.println("This is method 1");
    }

    public void method2() {
        System.out.println("This is method 2");
    }

    private void privateMethod() {
        System.out.println("This is a private method");
    }
}

public class ComprehensiveExample {
    public static void main(String[] args) {
        // 使用反射获取 MyClass 的方法
        MyClass myObject = new MyClass();
        Class<?> clazz = myObject.getClass();
        Method[] methods = clazz.getMethods();

        // 定义正则表达式模式
        Pattern pattern = Pattern.compile("^method\\d+$");

        for (Method method : methods) {
            String methodName = method.getName();
            // 使用正则表达式验证方法名
            if (pattern.matcher(methodName).matches()) {
                System.out.println("Valid method: " + methodName);
                try {
                    method.invoke(myObject);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        // 格式化当前日期时间
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date currentDate = new Date();
        String formattedDate = dateFormat.format(currentDate);
        System.out.println("Current date and time: " + formattedDate);
    }
}

在这个示例中,首先使用反射获取 MyClass 的所有公共方法,然后使用正则表达式验证方法名是否以 method 开头并后跟一个或多个数字。如果方法名符合格式要求,则调用该方法。最后,使用 SimpleDateFormat 类将当前日期时间格式化为 yyyy-MM-dd HH:mm:ss 的形式并输出。

7. 总结

通过对正则表达式、反射、RMI 和日期时间格式化的学习,我们了解了这些技术的原理、应用场景和使用方法。正则表达式能高效地处理文本匹配和提取问题;反射机制为程序的动态性提供了支持;RMI 使得分布式应用的开发成为可能;日期时间格式化则满足了不同地区用户对日期时间显示的需求。

在实际开发中,我们应根据具体的需求和场景选择合适的技术。同时,要注意这些技术的优缺点,合理使用它们,以提高程序的性能和可维护性。希望大家通过本文的介绍,能更好地掌握这些 Java 技术,并在实际项目中灵活运用。

下面是一个简单的 mermaid 流程图,展示了使用 RMI 构建简单客户端/服务器应用的步骤:

graph LR
    A[输入并编译源代码] --> B[必要时手动生成存根]
    B --> C[在客户端和服务器机器上安装文件]
    C --> D[在服务器机器上启动 RMI 注册表]
    D --> E[启动服务器]
    E --> F[启动客户端]

这个流程图清晰地展示了使用 RMI 构建简单客户端/服务器应用的主要步骤,从源代码的输入编译到最终客户端的启动,每个步骤都有明确的先后顺序。通过这个流程图,我们可以更直观地理解整个过程。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值