58、高级 Java 编程综合指南

高级 Java 编程综合指南

1. 受限属性与属性变更事件

在 Java 编程中,属性变更事件和受限属性是重要的概念。当一个属性发生变化时,我们可能需要通知相关的监听器。以下是一个展示属性变更事件处理的代码示例:

import java.beans.*;

public class DemoBean {
    private int length = 10;
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private VetoableChangeSupport vcs = new VetoableChangeSupport(this);

    public int getLength() {
        return length;
    }

    public void setLength(int l) throws PropertyVetoException {
        int oldLength = length;
        vcs.fireVetoableChange("length", oldLength, l);
        length = l;
        pcs.firePropertyChange("length", oldLength, l);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    public void addVetoableChangeListener(VetoableChangeListener listener) {
        vcs.addVetoableChangeListener(listener);
    }

    public void removeVetoableChangeListener(VetoableChangeListener listener) {
        vcs.removeVetoableChangeListener(listener);
    }
}

上述代码中, DemoBean 类包含了属性变更和受限属性的处理逻辑。 PropertyChangeSupport 用于处理普通的属性变更事件,而 VetoableChangeSupport 用于处理受限属性的变更。当调用 setLength 方法时,会先触发 fireVetoableChange 方法,若没有监听器否决该变更,才会更新属性值并触发普通的属性变更事件。

2. 通过 JSP 使用 Java Bean

在 JSP 页面中,我们可以使用 jsp:useBean 动作来加载 Java Bean。以下是使用该标签的基本语法:

<jsp:useBean id="name" class="package.Class" />

这个动作会实例化 class 属性指定的类的对象,并将其与 id 属性指定的名称绑定。例如,若要使用 beans 目录下的 CalculateBean.java ,可以这样写:

<jsp:useBean id="calc" class="beans.CalculateBean" >

这等价于以下脚本代码:

<% beans.CalculateBean calc = new beans.CalculateBean; %>

需要注意的是,所有类都应该属于某个包,这样才能在其他类中访问。创建 Bean 类时,要确保它属于某个包,编译后将其放置在 /WEB-INF/classes/beans/CalculateBean.class 路径下,然后就可以在 JSP 中引用它。

2.1 访问 Bean 属性

在 JSP 中,可以使用 jsp:getProperty 标签来访问 Bean 的属性。该标签有两个属性: name property name 属性的值应与 jsp:useBean id 属性相同, property 属性的值应与 Bean 类中指定的实例变量匹配。例如,对于 CalculateBean 类的 name 属性,可以通过以下两种方式访问:

<jsp:getProperty name="calc" property="name" />

或者

<%= calc.getName() %>
2.2 设置 Bean 属性

可以使用 jsp:setProperty 标签来修改 Bean 的属性。该标签有三个属性: name (应与 jsp:useBean 中指定的 id 完全匹配)、 property (要更改的属性名称)和 value (属性的新值)。例如,若要更改 name 属性的值,可以这样做:

<jsp:setProperty name="calc" property="name" value="Peter" />

或者

<% calc.setName("Peter"); %> 

当 Bean 的属性是数值类型,而传递的值是字符串时,JSP 容器可以自动处理类型转换。只需指定:

<jsp:useBean id = "calc" class = "beans.CalculateBean" />
<jsp:setProperty name = "calc" property = "*"/>

这种设置还可用于将 HTML 表单元素与 Java Bean 同步,减轻了重复编码的负担。

2.3 CalculateBean 示例

以下是一个完整的示例,展示了如何在 JSP 中使用 Java Bean:
JavaBeans.jsp

<Html>
<Head>
<title> Using Java Beans in JSP </title>
</Head>
<body>
Hello <br>
<jsp:useBean id="calc" class="beans.CalculateBean" >
After Instantiating Bean <br>
<jsp:setProperty name="calc" property="*" />
<jsp:setProperty name="calc" property="name" value="sachin" />
After Setting property: name <br>
<jsp:setProperty name="calc" property="var1" value="10" />
After Setting property: variable 1 <br>
<jsp:setProperty name="calc" property="var2" value="10" />
After Setting property: variable 2 <br>
Name: <jsp:getProperty name="calc" property="name" /><br>
Variable 1: <jsp:getProperty name="calc" property="var1" /><br>
Variable 2: <jsp:getProperty name="calc" property="var2" /><br>
<%= "Result: "+ calc.sum() %>
</jsp:useBean>
</body>
</Html>

CalculateBean.java

package beans;
import java.io.*;

public class CalculateBean implements Serializable {
    private int var1;
    private int var2;
    private String name = new String();

    public int getVar1() {
        return var1;
    }

    public int getVar2() {
        return var2;
    }

    public String getName() {
        return name;
    }

    public void setName(String x) {
        name = x;
    }

    public void setVar1(int x) {
        var1 = x;
    }

    public void setVar2(int y) {
        var2 = y;
    }

    public int sum() {
        return var1 + var2;
    }
}

这个示例中, CalculateBean 类有两个整数变量 var1 var2 ,以及一个字符串变量 name sum 方法用于计算 var1 var2 的和。在 JSP 页面中,通过 jsp:useBean 实例化 Bean,使用 jsp:setProperty 设置属性值,使用 jsp:getProperty 访问属性值,并调用 sum 方法计算结果。

3. JAR 文件操作

JAR(Java Archive)文件类似于 ZIP 文件,Java 开发工具包(JDK)提供了 jar 工具来处理 JAR 文件。

3.1 创建 JAR 文件

创建 JAR 文件的基本命令如下:

jar -cvmf jar-file-name manifest-file-name input-file-names

各选项和参数的含义如下:
| 选项 | 含义 |
| ---- | ---- |
| -c | 创建 JAR 文件 |
| -v | 在标准输出上显示详细信息 |
| -f | 指定输出到文件而不是标准输出 |
| -m | 指定要添加到 JAR 中的清单文件 |
| jar-file-name | 生成的 JAR 文件的名称,扩展名为 .jar |
| input-file-names | 要包含在 JAR 文件中的文件或目录,多个文件或目录用空格分隔 |

例如,创建 Calculator.jar 文件:

jar -cvfm Calculator.jar manifest.txt beans
3.2 查看 JAR 文件内容

使用以下命令查看 JAR 文件的内容:

jar -tf jar-file

其中, -t 选项表示查看内容, -f 选项指定要查看的 JAR 文件。

3.3 提取 JAR 文件内容

使用 -xf 命令提取 JAR 文件的内容:

jar -xf jar-file-name

-x 选项表示提取内容, -f 选项指定要提取的 JAR 文件。提取的内容将被放置在当前目录下。

3.4 清单文件

清单文件( .mf )是一个特殊的文件,可以包含 JAR 文件中文件的相关信息。创建 JAR 文件时,如果没有提供清单文件,会自动创建一个默认的清单文件,路径为 META-INF/MANIFEST.MF 。默认清单文件包含以下内容:

Manifest-Version: 1.0
Created-By: 1.7.0_09 (Oracle Corporation)

若要修改清单文件,可以使用 -m 选项。首先创建一个包含要添加信息的文本文件,然后使用以下命令:

jar cfm jar-file manifest-file input-file(s)

需要注意的是,清单文件末尾要添加换行符,并且 -m -f 选项及其参数的顺序必须匹配。

如果要指定 JAR 包中应用程序的入口类,可以在清单文件中添加以下头部信息:

Main-Class: classname

添加该清单文件后,可以使用以下命令运行 JAR 包中的应用程序:

java -jar JAR-name

另外,也可以使用 -e 选项来指定应用程序的入口点,而无需编辑或创建清单文件。例如:

jar cfe x.jar MyMain MyMain.class

如果入口类在某个包中,可以这样调用:

jar cfe x.jar App.MyMain App/MyMain.class
4. 远程方法调用(RMI)

远程方法调用(RMI)是 Java 实现的用于分布式计算的远程过程调用(RPC)机制。它基于客户端/服务器概念,允许运行在一个机器上的对象被运行在不同机器上的客户端访问。

4.1 RMI 网络模型

RMI 客户端/服务器应用程序基于 TCP/IP 网络模型。在 TCP/IP 中,应用层负责处理表示层和会话层的问题。RMI 为这些层提供了相应的功能,客户端和服务器的表示层功能分别由存根(Stub)和骨架(Skeleton)处理,远程引用层(RRL)负责管理客户端和服务器之间的会话。

4.2 存根和骨架

存根是客户端的代理类,骨架是服务器端的代理类,它们由特殊的编译器 rmic 生成。客户端与存根交互,存根将客户端的请求传递给服务器,骨架将服务器的响应传递给客户端。存根的主要功能包括:
- 编组参数和解组返回值
- 通知 RRL 应在服务器上调用某个方法
- 通知 RRL 调用已完成

骨架的主要功能包括:
- 编组返回值和解组参数
- 调用实际的远程对象实现

参数的编组和解组处理了如何将参数发送到服务器以及如何将响应返回给客户端的问题。由于网络具有异构性,不同机器可能使用不同的架构和协议,因此需要进行参数的转换和处理。

以下是 RMI 工作流程的 mermaid 流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([客户端发起调用]):::startend --> B(存根编组参数):::process
    B --> C(通过 JRMP 协议发送请求):::process
    C --> D(服务器接收请求):::process
    D --> E(骨架解组参数):::process
    E --> F(调用实际远程对象方法):::process
    F --> G(骨架编组返回值):::process
    G --> H(通过 JRMP 协议发送响应):::process
    H --> I(存根解组返回值):::process
    I --> J([返回结果给客户端]):::startend

通过本文的介绍,我们了解了高级 Java 编程中的受限属性、通过 JSP 使用 Java Bean、JAR 文件操作以及远程方法调用等重要概念和技术。这些知识对于开发分布式、可维护的 Java 应用程序具有重要意义。在实际开发中,可以根据具体需求灵活运用这些技术,提高开发效率和应用程序的性能。

5. 实际应用中的注意事项
5.1 类路径和包管理

在使用 Java Bean 和 JAR 文件时,类路径和包管理至关重要。所有类都应放在特定的包中,以确保在其他类中可访问。例如,创建的 CalculateBean 类放在 beans 包中,编译后需将其放置在 /WEB-INF/classes/beans/CalculateBean.class 路径下。如果使用 JAR 文件替代单个类文件,需将 JAR 文件放在 /WEB-INF/lib/ 目录下。

以下是一个简单的步骤列表,用于确保类路径和包管理正确:
1. 创建类时,明确指定包名,如 package beans;
2. 编译类文件,确保生成的 .class 文件位于正确的包路径下。
3. 如果使用 JAR 文件,将其放置在 /WEB-INF/lib/ 目录下。
4. 在 JSP 或其他 Java 代码中,使用完整的包名引用类,如 beans.CalculateBean

5.2 异常处理

在处理受限属性和 RMI 时,异常处理是必不可少的。在 DemoBean 类的 setLength 方法中,调用 vcs.fireVetoableChange 时可能会抛出 PropertyVetoException ,因此需要在方法签名中声明该异常。在 RMI 中,网络通信可能会出现各种异常,如连接失败、超时等,需要在代码中进行适当的异常处理。

以下是 DemoBean 类中异常处理的示例:

public void setLength(int l) throws PropertyVetoException {
    int oldLength = length;
    vcs.fireVetoableChange("length", oldLength, l);
    length = l;
    pcs.firePropertyChange("length", oldLength, l);
}
5.3 性能优化

在实际应用中,性能优化是一个重要的考虑因素。对于 JAR 文件,合理组织文件结构可以减少加载时间。例如,将相关的类和资源放在同一个 JAR 文件中,避免不必要的文件查找。在 RMI 中,减少网络通信次数和数据传输量可以提高性能。可以通过批量调用远程方法或优化数据传输格式来实现。

6. 高级应用场景
6.1 分布式系统中的 RMI

RMI 在分布式系统中有着广泛的应用。例如,一个企业级的电子商务系统,可能有多个服务器负责不同的业务逻辑,如商品管理、订单处理、用户认证等。客户端可以通过 RMI 远程调用这些服务器上的方法,实现业务逻辑的分布式处理。

以下是一个简单的 RMI 分布式系统示例:
1. 定义远程接口:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteService extends Remote {
    String sayHello() throws RemoteException;
}
  1. 实现远程接口:
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteServiceImpl extends UnicastRemoteObject implements RemoteService {
    protected RemoteServiceImpl() throws RemoteException {
        super();
    }

    @Override
    public String sayHello() throws RemoteException {
        return "Hello from the remote server!";
    }
}
  1. 服务器端代码:
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RMIServer {
    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
            RemoteService service = new RemoteServiceImpl();
            Naming.rebind("rmi://localhost:1099/RemoteService", service);
            System.out.println("Server is ready.");
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }
    }
}
  1. 客户端代码:
import java.rmi.Naming;

public class RMIClient {
    public static void main(String[] args) {
        try {
            RemoteService service = (RemoteService) Naming.lookup("rmi://localhost:1099/RemoteService");
            String result = service.sayHello();
            System.out.println(result);
        } catch (Exception e) {
            System.err.println("Client exception: " + e.toString());
            e.printStackTrace();
        }
    }
}
6.2 JSP 和 Java Bean 的结合应用

在 Web 开发中,JSP 和 Java Bean 的结合可以实现动态网页的开发。例如,一个用户注册页面,用户输入的信息可以通过 JSP 页面收集,然后传递给 Java Bean 进行处理,如数据验证、存储到数据库等。

以下是一个简单的用户注册示例:
1. 创建 Java Bean:

package beans;

import java.io.Serializable;

public class UserBean implements Serializable {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
  1. 创建 JSP 页面:
<Html>
<Head>
<title> User Registration </title>
</Head>
<body>
<jsp:useBean id="user" class="beans.UserBean" />
<form action="register.jsp" method="post">
    Username: <input type="text" name="username" /><br>
    Password: <input type="password" name="password" /><br>
    <input type="submit" value="Register" />
</form>
<jsp:setProperty name="user" property="*" />
</body>
</Html>
  1. 创建处理注册的 JSP 页面:
<Html>
<Head>
<title> Registration Result </title>
</Head>
<body>
<jsp:useBean id="user" class="beans.UserBean" />
<jsp:setProperty name="user" property="*" />
<%
    // 这里可以添加数据验证和存储到数据库的代码
    out.println("User " + user.getUsername() + " registered successfully!");
%>
</body>
</Html>
6. 总结

通过本文的介绍,我们全面了解了高级 Java 编程中的多个重要方面,包括受限属性、通过 JSP 使用 Java Bean、JAR 文件操作以及远程方法调用等。这些技术在开发分布式、可维护的 Java 应用程序中起着关键作用。

在实际开发中,需要注意类路径和包管理、异常处理和性能优化等问题。同时,要根据具体的应用场景,灵活运用这些技术,如在分布式系统中使用 RMI,在 Web 开发中结合 JSP 和 Java Bean。通过不断学习和实践,我们可以更好地掌握这些技术,提高 Java 开发的效率和应用程序的质量。

技术点 关键信息
受限属性 使用 VetoableChangeSupport 处理受限属性变更,变更前咨询监听器
JSP 使用 Java Bean 通过 jsp:useBean 实例化 Bean, jsp:setProperty 设置属性, jsp:getProperty 访问属性
JAR 文件操作 使用 jar 工具创建、查看、提取 JAR 文件,可自定义清单文件
远程方法调用(RMI) 基于客户端/服务器模型,通过存根和骨架实现远程对象访问,使用 JRMP 协议通信

希望这些知识能帮助你在 Java 开发的道路上更进一步,不断探索和创新,开发出更加优秀的 Java 应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值